Skip to content

Commit

Permalink
feat(align-deps): add Yarn plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 committed Feb 25, 2025
1 parent bda2ac4 commit 8432597
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/slow-poets-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rnx-kit/yarn-plugin-align-deps": minor
---

Added experimental Yarn plugin to simplify dependency alignment
33 changes: 33 additions & 0 deletions incubator/yarn-plugin-align-deps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# @rnx-kit/yarn-plugin-align-deps

[![Build](https://github.com/microsoft/rnx-kit/actions/workflows/build.yml/badge.svg)](https://github.com/microsoft/rnx-kit/actions/workflows/build.yml)
[![npm version](https://img.shields.io/npm/v/@rnx-kit/yarn-plugin-align-deps)](https://www.npmjs.com/package/@rnx-kit/yarn-plugin-align-deps)

🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧

### THIS TOOL IS EXPERIMENTAL — USE WITH CAUTION

🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧

This is a sample folder to use as base for generating new packages for
`rnx-kit`.

## Motivation

We want new packages to follow an existing set of patterns and guidelines; via
this package, we can enforce easily allow new folders to stick to at least a
common starting point.

## Installation

```sh
yarn add @rnx-kit/yarn-plugin-align-deps --dev
```

or if you're using npm

```sh
npm add --save-dev @rnx-kit/yarn-plugin-align-deps
```

## Usage
1 change: 1 addition & 0 deletions incubator/yarn-plugin-align-deps/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("@rnx-kit/eslint-config");
131 changes: 131 additions & 0 deletions incubator/yarn-plugin-align-deps/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// @ts-check

/**
* @import { Hooks, Manifest, PackageExtensionData, Plugin } from "@yarnpkg/core";
* @typedef {{ [key: string]: string | number | boolean | JSONObject | JSONObject[] | null }} JSONObject;
* @typedef {Manifest["raw"]} RawManifest;
*/

const PLUGIN_NAME = "@rnx-kit/yarn-plugin-align-deps";

/**
* @param {RawManifest} manifest
* @returns {JSONObject | undefined}
*/
function getKitConfig({ "rnx-kit": rnxconfig }) {
if (!rnxconfig || typeof rnxconfig !== "object") {
return undefined;
}

return /** @type {JSONObject} */ (rnxconfig);
}

/**
* @param {RawManifest} manifest
* @param {string} projectRoot
* @param {NodeJS.Require} require
* @returns {Promise<((cwd: string, manifest: RawManifest) => PackageExtensionData | undefined) | void>}
*/
async function loadUserProfiles(
manifest,
projectRoot,
// @ts-expect-error `tsc` thinks `require` is unused
require
) {
const rnxconfig = getKitConfig(manifest);
if (!rnxconfig || !("profiles" in rnxconfig)) {
return;
}

const { profiles } = rnxconfig;
if (!profiles) {
return;
}

if (typeof profiles === "string") {
const path = require("node:path");

// On Windows, import paths must include the `file:` protocol and we must
// manually prefix it. `URL.pathToFileURL` incorrectly adds the disk station
// so the URL becomes `file://C:/C:/...`.
const profilesPath = path.posix.resolve(projectRoot, profiles);
const external = await import(`file://${encodeURI(profilesPath)}`);
return external?.default ?? external;
}

if (typeof profiles === "object" && !Array.isArray(profiles)) {
return (_cwd, manifest) => {
const rnxconfig = getKitConfig(manifest);
if (!rnxconfig || !("profile" in rnxconfig)) {
return;
}

const { profile } = rnxconfig;
if (typeof profile !== "string") {
return;
}

const p = profiles[profile];
if (!p || typeof p !== "object" || Array.isArray(p)) {
return;
}

return /** @type {PackageExtensionData} */ (p);
};
}

console.warn(
`${PLUGIN_NAME}: invalid configuration: 'profiles' must be an object or a string`
);
}

// This module *must* be CommonJS because `actions/setup-node` (and probably
// others) does not support ESM. Yarn itself does.
exports.name = PLUGIN_NAME;

/** @type {(require: NodeJS.Require) => Plugin<Hooks>} */
exports.factory = (require) => ({
hooks: {
registerPackageExtensions: async (
configuration,
registerPackageExtension
) => {
const { projectCwd } = configuration;
if (!projectCwd) {
return;
}

const { Project, structUtils } = require("@yarnpkg/core");

const { workspace } = await Project.find(configuration, projectCwd);
if (!workspace) {
return;
}

const { manifest, project } = workspace;
const getUserProfile = await loadUserProfiles(
manifest.raw,
projectCwd,
require
);
if (!getUserProfile) {
return;
}

project.workspacesByCwd.forEach(({ cwd, manifest }) => {
const { name, version, raw } = manifest;
if (!name || !version) {
return;
}

const profile = getUserProfile(cwd, raw);
if (!profile) {
return;
}

const descriptor = structUtils.makeDescriptor(name, version);
registerPackageExtension(descriptor, profile);
});
},
},
});
43 changes: 43 additions & 0 deletions incubator/yarn-plugin-align-deps/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"private": true,
"name": "@rnx-kit/yarn-plugin-align-deps",
"version": "0.0.1",
"description": "EXPERIMENTAL - USE WITH CAUTION - yarn-plugin-align-deps",
"homepage": "https://github.com/microsoft/rnx-kit/tree/main/incubator/yarn-plugin-align-deps#readme",
"license": "MIT",
"author": {
"name": "Microsoft Open Source",
"email": "[email protected]"
},
"files": [
"index.js"
],
"main": "index.js",
"exports": {
".": "./index.js"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/rnx-kit",
"directory": "incubator/yarn-plugin-align-deps"
},
"engines": {
"node": ">=18.12"
},
"scripts": {
"build": "rnx-kit-scripts build",
"format": "rnx-kit-scripts format",
"lint": "rnx-kit-scripts lint",
"test": "rnx-kit-scripts test"
},
"devDependencies": {
"@rnx-kit/eslint-config": "*",
"@rnx-kit/scripts": "*",
"@rnx-kit/tsconfig": "*",
"@yarnpkg/core": "^4.0.0",
"eslint": "^9.0.0",
"prettier": "^3.0.0",
"typescript": "^5.0.0"
},
"experimental": true
}
7 changes: 7 additions & 0 deletions incubator/yarn-plugin-align-deps/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "@rnx-kit/tsconfig/tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"include": ["index.js"]
}
2 changes: 1 addition & 1 deletion packages/template/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"directory": "packages/template"
},
"engines": {
"node": ">=16.17"
"node": ">=18.12"
},
"scripts": {
"build": "rnx-kit-scripts build",
Expand Down
16 changes: 15 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4838,6 +4838,20 @@ __metadata:
languageName: unknown
linkType: soft

"@rnx-kit/yarn-plugin-align-deps@workspace:incubator/yarn-plugin-align-deps":
version: 0.0.0-use.local
resolution: "@rnx-kit/yarn-plugin-align-deps@workspace:incubator/yarn-plugin-align-deps"
dependencies:
"@rnx-kit/eslint-config": "npm:*"
"@rnx-kit/scripts": "npm:*"
"@rnx-kit/tsconfig": "npm:*"
"@yarnpkg/core": "npm:^4.0.0"
eslint: "npm:^9.0.0"
prettier: "npm:^3.0.0"
typescript: "npm:^5.0.0"
languageName: unknown
linkType: soft

"@shikijs/engine-oniguruma@npm:^1.25.1":
version: 1.26.1
resolution: "@shikijs/engine-oniguruma@npm:1.26.1"
Expand Down Expand Up @@ -5564,7 +5578,7 @@ __metadata:
languageName: node
linkType: hard

"@yarnpkg/core@npm:^4.1.6, @yarnpkg/core@npm:^4.2.0":
"@yarnpkg/core@npm:^4.0.0, @yarnpkg/core@npm:^4.1.6, @yarnpkg/core@npm:^4.2.0":
version: 4.2.0
resolution: "@yarnpkg/core@npm:4.2.0"
dependencies:
Expand Down

0 comments on commit 8432597

Please sign in to comment.