diff --git a/.changeset/slow-poets-itch.md b/.changeset/slow-poets-itch.md new file mode 100644 index 000000000..a29a23b97 --- /dev/null +++ b/.changeset/slow-poets-itch.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/yarn-plugin-align-deps": minor +--- + +Added experimental Yarn plugin to simplify dependency alignment diff --git a/incubator/yarn-plugin-align-deps/README.md b/incubator/yarn-plugin-align-deps/README.md new file mode 100644 index 000000000..778e95ce9 --- /dev/null +++ b/incubator/yarn-plugin-align-deps/README.md @@ -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 diff --git a/incubator/yarn-plugin-align-deps/eslint.config.js b/incubator/yarn-plugin-align-deps/eslint.config.js new file mode 100644 index 000000000..40c96d3f5 --- /dev/null +++ b/incubator/yarn-plugin-align-deps/eslint.config.js @@ -0,0 +1,4 @@ +import config from "@rnx-kit/eslint-config"; + +// eslint-disable-next-line no-restricted-exports +export default config; diff --git a/incubator/yarn-plugin-align-deps/index.js b/incubator/yarn-plugin-align-deps/index.js new file mode 100644 index 000000000..6907ebeff --- /dev/null +++ b/incubator/yarn-plugin-align-deps/index.js @@ -0,0 +1,108 @@ +// @ts-check + +/** + * @import { Hooks, Manifest, PackageExtensionData, Plugin } from "@yarnpkg/core"; + */ + +const PLUGIN_NAME = "@rnx-kit/yarn-plugin-align-deps"; + +/** + * @param {Record} config + * @param {string} projectRoot + * @param {NodeJS.Require} require + * @returns {Promise<((manifest: Manifest["raw"]) => PackageExtensionData) | undefined>} + */ +async function loadUserProfiles( + { "rnx-kit": rnxconfig }, + projectRoot, + require +) { + if ( + typeof rnxconfig !== "object" || + !rnxconfig || + !("profiles" in rnxconfig) + ) { + return; + } + + const { profiles } = rnxconfig; + if (!profiles) { + return; + } + + switch (typeof profiles) { + case "object": + return ({ "rnx-kit": rnxconfig }) => { + if (!rnxconfig || !("profile" in rnxconfig)) { + return; + } + + return profiles[rnxconfig.profile]; + }; + + case "string": { + const path = require("node:path"); + + const profilesPath = path.resolve(projectRoot, profiles); + const external = await import(profilesPath); + return external?.default ?? external; + } + + default: + console.warn( + `${PLUGIN_NAME}: invalid configuration: 'profiles' must be an object or a string` + ); + break; + } +} + +// TODO: This module *must* be CommonJS because `actions/setup-node` does not +// support ESM. Yarn itself does. +exports.name = PLUGIN_NAME; + +/** @type {(require: NodeJS.Require) => Plugin} */ +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(({ manifest }) => { + const { name, version, raw } = manifest; + if (!name || !version) { + return; + } + + const profile = getUserProfile(raw); + if (!profile) { + return; + } + + const descriptor = structUtils.makeDescriptor(name, version); + registerPackageExtension(descriptor, profile); + }); + }, + }, +}); diff --git a/incubator/yarn-plugin-align-deps/package.json b/incubator/yarn-plugin-align-deps/package.json new file mode 100644 index 000000000..a5a8e4036 --- /dev/null +++ b/incubator/yarn-plugin-align-deps/package.json @@ -0,0 +1,41 @@ +{ + "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": "microsoftopensource@users.noreply.github.com" + }, + "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": { + "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" + }, + "experimental": true +} diff --git a/packages/template/package.json b/packages/template/package.json index b10b30438..e7a1c921c 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -21,7 +21,7 @@ "directory": "packages/template" }, "engines": { - "node": ">=16.17" + "node": ">=18.12" }, "scripts": { "build": "rnx-kit-scripts build", diff --git a/yarn.lock b/yarn.lock index f543588fd..d2bbe7b0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4838,6 +4838,19 @@ __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" + languageName: unknown + linkType: soft + "@shikijs/engine-oniguruma@npm:^1.25.1": version: 1.26.1 resolution: "@shikijs/engine-oniguruma@npm:1.26.1" @@ -5564,7 +5577,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: