diff --git a/package.json b/package.json index c9e83b0bf29ec7..0277832b2c6004 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "babel-plugin-syntax-hermes-parser": "0.25.1", "babel-plugin-transform-define": "^2.1.4", "babel-plugin-transform-flow-enums": "^0.0.2", - "chalk": "^4.0.0", + "chalk": "^4.1.2", "clang-format": "^1.8.0", "connect": "^3.6.5", "deep-equal": "1.1.1", diff --git a/packages/community-cli-plugin/package.json b/packages/community-cli-plugin/package.json index c6939930dcd692..f11c7547ffad3a 100644 --- a/packages/community-cli-plugin/package.json +++ b/packages/community-cli-plugin/package.json @@ -25,14 +25,14 @@ "dependencies": { "@react-native/dev-middleware": "workspace:*", "@react-native/metro-babel-transformer": "workspace:*", - "chalk": "^4.0.0", + "chalk": "^4.1.2", "debug": "^2.2.0", "invariant": "^2.2.4", "metro": "^0.81.0", "metro-config": "^0.81.0", "metro-core": "^0.81.0", "readline": "^1.3.0", - "semver": "^7.1.3" + "semver": "^7.7.2" }, "devDependencies": { "metro-resolver": "^0.81.0" diff --git a/packages/helloworld/package.json b/packages/helloworld/package.json index bb582ef4e4021d..f25dd0e066196d 100644 --- a/packages/helloworld/package.json +++ b/packages/helloworld/package.json @@ -19,10 +19,10 @@ "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.3", "@babel/runtime": "^7.25.0", - "@react-native/babel-preset": "0.77.0-main", - "@react-native/core-cli-utils": "0.77.0-main", - "@react-native/eslint-config": "0.77.0-main", - "@react-native/metro-config": "0.77.0-main", + "@react-native/babel-preset": "workspace:*", + "@react-native/core-cli-utils": "workspace:*", + "@react-native/eslint-config": "workspace:*", + "@react-native/metro-config": "workspace:*", "chalk": "^4.1.2", "commander": "^12.0.0", "eslint": "^8.19.0", diff --git a/packages/react-native-codegen/package.json b/packages/react-native-codegen/package.json index a27d9433b9c0a6..530c721e72967d 100644 --- a/packages/react-native-codegen/package.json +++ b/packages/react-native-codegen/package.json @@ -49,7 +49,7 @@ "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/preset-env": "^7.25.3", - "chalk": "^4.0.0", + "chalk": "^4.1.2", "hermes-estree": "0.25.1", "micromatch": "^4.0.4", "prettier": "2.8.8", diff --git a/packages/react-native-macos-init/package.json b/packages/react-native-macos-init/package.json index 934a1a1529b43d..d3fac36f4f5c56 100644 --- a/packages/react-native-macos-init/package.json +++ b/packages/react-native-macos-init/package.json @@ -18,12 +18,12 @@ }, "bin": "./bin.js", "dependencies": { - "chalk": "^3", + "chalk": "^4.1.2", "find-up": "^4.1.0", "npm-registry-fetch": "^14.0.0", "prompts": "^2.3.0", - "semver": "^7.5.2", - "yargs": "^15.1.0" + "semver": "^7.7.2", + "yargs": "^17.6.2" }, "devDependencies": { "@rnx-kit/tsconfig": "^2.0.0", diff --git a/packages/react-native-popup-menu-android/package.json b/packages/react-native-popup-menu-android/package.json index 377fea0f7b7bda..30254822f710a5 100644 --- a/packages/react-native-popup-menu-android/package.json +++ b/packages/react-native-popup-menu-android/package.json @@ -1,5 +1,5 @@ { - "name": "@react-native/popup-menu-android", + "name": "@react-native-macos/popup-menu-android", "version": "0.77.0-main", "private": true, "description": "PopupMenu for the Android platform", @@ -23,7 +23,7 @@ "peerDependencies": { "@types/react": "^19.0.0", "react": "*", - "react-native": "*" + "react-native-macos": "workspace:*" }, "peerDependenciesMeta": { "@types/react": { diff --git a/packages/react-native-test-library/package.json b/packages/react-native-test-library/package.json index 702bf7a2f075f7..ae5512bf9ecf67 100644 --- a/packages/react-native-test-library/package.json +++ b/packages/react-native-test-library/package.json @@ -27,11 +27,12 @@ "devDependencies": { "@babel/core": "^7.25.2", "@react-native/babel-preset": "workspace:*", + "react": "19.0.0", "react-native-macos": "workspace:*" }, "peerDependencies": { "react": "*", - "react-native-macos": "*" + "react-native-macos": "workspace:*" }, "codegenConfig": { "name": "OSSLibraryExampleSpec", diff --git a/packages/react-native/package.json b/packages/react-native/package.json index e2f4ee99175103..9e20ca5455ba21 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -110,6 +110,7 @@ } }, "dependencies": { + "@babel/core": "^7.25.2", "@jest/create-cache-key-function": "^29.6.3", "@react-native-macos/virtualized-lists": "workspace:*", "@react-native/assets-registry": "workspace:*", @@ -124,7 +125,7 @@ "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.25.1", "base64-js": "^1.5.1", - "chalk": "^4.0.0", + "chalk": "^4.1.2", "commander": "^12.0.0", "event-target-shim": "^5.0.1", "flow-enums-runtime": "^0.0.6", @@ -141,7 +142,7 @@ "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.25.0", - "semver": "^7.1.3", + "semver": "^7.7.2", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", @@ -164,5 +165,8 @@ "jsSrcsDir": "src" } ] + }, + "devDependencies": { + "react": "19.0.0" } } diff --git a/packages/rn-tester/package.json b/packages/rn-tester/package.json index 426bfb7f9f4375..947c8bccf9d741 100644 --- a/packages/rn-tester/package.json +++ b/packages/rn-tester/package.json @@ -27,8 +27,8 @@ "e2e-test-ios": "./scripts/maestro-test-ios.sh" }, "dependencies": { + "@react-native-macos/popup-menu-android": "workspace:*", "@react-native/oss-library-example": "workspace:*", - "@react-native/popup-menu-android": "workspace:*", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "nullthrows": "^1.1.1" @@ -54,6 +54,7 @@ "@react-native-community/cli": "15.0.0-alpha.2", "@react-native-community/cli-platform-android": "15.0.0-alpha.2", "@react-native-community/cli-platform-ios": "15.0.0-alpha.2", + "react": "19.0.0", "react-native-macos": "workspace:*" } } diff --git a/packages/virtualized-lists/package.json b/packages/virtualized-lists/package.json index 9f9090c661d8c1..8518b84107a75f 100644 --- a/packages/virtualized-lists/package.json +++ b/packages/virtualized-lists/package.json @@ -25,16 +25,21 @@ "nullthrows": "^1.1.1" }, "devDependencies": { + "react": "19.0.0", "react-test-renderer": "19.0.0" }, "peerDependencies": { "@types/react": "^19.0.0", "react": "*", - "react-native": "*" + "react-native": "*", + "react-native-macos": "workspace:*" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "react-native": { + "optional": true } } } diff --git a/tools/api/package.json b/tools/api/package.json index 8f26475f3d3ad3..20b4f8d52ea018 100644 --- a/tools/api/package.json +++ b/tools/api/package.json @@ -12,7 +12,7 @@ "license": "MIT", "private": true, "dependencies": { - "chalk": "^4.0.0", + "chalk": "^4.1.2", "glob": "^7.1.1", "ini": "^5.0.0" } diff --git a/yarn.config.cjs b/yarn.config.cjs index 3f63e794a0a237..12f56276ef467a 100644 --- a/yarn.config.cjs +++ b/yarn.config.cjs @@ -9,6 +9,157 @@ const {defineConfig} = require('@yarnpkg/types'); * @typedef {import('@yarnpkg/types').Yarn.Constraints.Dependency} Dependency */ +const IGNORE_CONSISTENT_DEPENDENCIES_FOR = new Set([ + `.`, + `packages/docusaurus`, +]); + +/** + * This rule will enforce that a workspace MUST depend on the same version of a dependency as the one used by the other workspaces + * We allow Docusaurus to have different dependencies for now; will be addressed later (when we remove Gatsby) + * @param {Context} context + */ +function enforceConsistentDependenciesAcrossTheProject({Yarn}) { + for (const dependency of Yarn.dependencies()) { + if (IGNORE_CONSISTENT_DEPENDENCIES_FOR.has(dependency.workspace.cwd)) + continue; + + if (dependency.type === `peerDependencies`) + continue; + + for (const otherDependency of Yarn.dependencies({ident: dependency.ident})) { + if (IGNORE_CONSISTENT_DEPENDENCIES_FOR.has(otherDependency.workspace.cwd)) + continue; + + if (otherDependency.type === `peerDependencies`) + continue; + + if ((dependency.type === `devDependencies` || otherDependency.type === `devDependencies`) && Yarn.workspace({ident: otherDependency.ident})) + continue; + + dependency.update(otherDependency.range); + } + } +} + +/** + * This rule will enforce that a workspace MUST depend on the same version of a dependency as the one used by the other workspaces + * We allow Docusaurus to have different dependencies for now; will be addressed later (when we remove Gatsby) + * @param {Context} context + */ +function enforceWorkspaceDependenciesWhenPossible({Yarn}) { + for (const dependency of Yarn.dependencies()) { + if (!Yarn.workspace({ident: dependency.ident})) + continue; + + dependency.update(`workspace:*`); + } +} + +/** + * @param {Context} context + * @param {string} ident + * @param {string} explanation + */ +function forbidDependency({Yarn}, ident, explanation) { + for (const dependency of Yarn.dependencies({ident})) { + dependency.error(explanation); + } +} + +/** + * @param {Context} context + * @param {Record any) | string>} fields + */ +function enforceFieldsOnAllWorkspaces({Yarn}, fields) { + for (const workspace of Yarn.workspaces()) { + for (const [field, value] of Object.entries(fields)) { + workspace.set(field, typeof value === `function` ? value(workspace) : value); + } + } +} + +/** + * @param {Context} context + * @param {string} ident + * @param {string} otherIdent + * @param {boolean} mustExist + */ +function enforceDependencyRelationship({Yarn}, ident, otherIdent, mustExist) { + for (const dependency of Yarn.dependencies({ident})) { + if (dependency.type === `peerDependencies`) + continue; + + const hasOtherDependency = Yarn.dependency({ + workspace: dependency.workspace, + ident: otherIdent, + }); + + if (mustExist) { + if (hasOtherDependency) + continue; + + dependency.error(`The presence of ${ident} in ${dependency.type} mandates the presence of ${otherIdent}`); + } else { + if (!hasOtherDependency) + continue; + + dependency.error(`The presence of ${ident} in ${dependency.type} forbids the presence of ${otherIdent}`); + } + } +} + + +/** + * Validate that all peer dependencies are provided. If one isn't, the + * constraint will try to fix it by looking at what's used in the other + * workspaces of the project. If it doesn't find any way to satisfy the + * dependency, it will generate an error. + * + * @param {Context} context + */ +function enforcePeerDependencyPresence({Yarn}) { + for (const workspace of Yarn.workspaces()) { + // The Gatsby website is pretty much deprecated anyway + if (workspace.cwd === `packages/gatsby`) + continue; + + for (const dependency of Yarn.dependencies({workspace})) { + if (dependency.type === `peerDependencies`) + continue; + + if (!dependency.resolution) + continue; + + for (const peerName of dependency.resolution.peerDependencies.keys()) { + // Webpack plugins have peer dependencies but don't often need it; weird + if (peerName === `webpack`) + continue; + + if (dependency.resolution.dependencies.has(peerName)) + continue; + + const otherDeps = Yarn.dependencies({ident: peerName}) + .filter(otherDep => otherDep.type !== `peerDependencies`); + + if (otherDeps.length === 0) + workspace.error(`Missing dependency on ${peerName} (required by ${dependency.ident})`); + + // If the workspace has itself a peer dependency of the same name, then + // we assume that it'll be fulfilled by its ancestors in the dependency + // tree, so we only need to add the dependency to devDependencies. + const autofixTarget = Yarn.dependency({workspace, ident: peerName, type: `peerDependencies`}) + ? `devDependencies` + : `dependencies`; + + for (const otherDep of otherDeps) { + workspace.set([autofixTarget, peerName], otherDep.range); + } + } + } + } +} + /** * Enforce that react-native-macos declares a peer dependency on react-native on release branches, * except on the main branch, where there is no published version of React Native to align to. @@ -109,8 +260,26 @@ function enforceReactNativeMacosVersionConsistency({Yarn}) { module.exports = defineConfig({ constraints: async ctx => { + // Constraints copied from the Yarn monorepo + enforceConsistentDependenciesAcrossTheProject(ctx); + enforceWorkspaceDependenciesWhenPossible(ctx); + enforcePeerDependencyPresence(ctx); + // enforceFieldsOnAllWorkspaces(ctx, { + // license: `MIT`, + // // When changing the engines.node value check https://node.green/ for + // // which ECMAScript version is fully supported and update the following files as needed: + // // - tsconfig.json + // // - packages/eslint-config/index.js + // // - packages/yarnpkg-builder/sources/commands/new/plugin.ts + // [`engines.node`]: `>=18.12.0`, + // [`repository.type`]: `git`, + // [`repository.url`]: `git+https://github.com/yarnpkg/berry.git`, + // [`repository.directory`]: workspace => workspace.cwd, + // }); + + // React Native macOS specific constraints expectReactNativePeerDependency(ctx); enforceReactNativeVersionConsistency(ctx); enforceReactNativeMacosVersionConsistency(ctx); }, -}); \ No newline at end of file +}); diff --git a/yarn.lock b/yarn.lock index f866afe5195b04..7c05b8e5b436fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2776,7 +2776,7 @@ __metadata: babel-plugin-syntax-hermes-parser: "npm:0.25.1" babel-plugin-transform-define: "npm:^2.1.4" babel-plugin-transform-flow-enums: "npm:^0.0.2" - chalk: "npm:^4.0.0" + chalk: "npm:^4.1.2" clang-format: "npm:^1.8.0" connect: "npm:^3.6.5" deep-equal: "npm:1.1.1" @@ -2835,20 +2835,40 @@ __metadata: languageName: unknown linkType: soft +"@react-native-macos/popup-menu-android@workspace:*, @react-native-macos/popup-menu-android@workspace:packages/react-native-popup-menu-android": + version: 0.0.0-use.local + resolution: "@react-native-macos/popup-menu-android@workspace:packages/react-native-popup-menu-android" + dependencies: + "@react-native/codegen": "workspace:*" + nullthrows: "npm:^1.1.1" + peerDependencies: + "@types/react": ^19.0.0 + react: "*" + react-native-macos: "workspace:*" + peerDependenciesMeta: + "@types/react": + optional: true + languageName: unknown + linkType: soft + "@react-native-macos/virtualized-lists@workspace:*, @react-native-macos/virtualized-lists@workspace:packages/virtualized-lists": version: 0.0.0-use.local resolution: "@react-native-macos/virtualized-lists@workspace:packages/virtualized-lists" dependencies: invariant: "npm:^2.2.4" nullthrows: "npm:^1.1.1" + react: "npm:19.0.0" react-test-renderer: "npm:19.0.0" peerDependencies: "@types/react": ^19.0.0 react: "*" react-native: "*" + react-native-macos: "workspace:*" peerDependenciesMeta: "@types/react": optional: true + react-native: + optional: true languageName: unknown linkType: soft @@ -2960,7 +2980,7 @@ __metadata: "@babel/plugin-transform-object-rest-spread": "npm:^7.24.7" "@babel/plugin-transform-optional-chaining": "npm:^7.24.8" "@babel/preset-env": "npm:^7.25.3" - chalk: "npm:^4.0.0" + chalk: "npm:^4.1.2" glob: "npm:^7.1.1" hermes-estree: "npm:0.25.1" hermes-parser: "npm:0.25.1" @@ -2982,7 +3002,7 @@ __metadata: dependencies: "@react-native/dev-middleware": "workspace:*" "@react-native/metro-babel-transformer": "workspace:*" - chalk: "npm:^4.0.0" + chalk: "npm:^4.1.2" debug: "npm:^2.2.0" invariant: "npm:^2.2.4" metro: "npm:^0.81.0" @@ -2990,7 +3010,7 @@ __metadata: metro-core: "npm:^0.81.0" metro-resolver: "npm:^0.81.0" readline: "npm:^1.3.0" - semver: "npm:^7.1.3" + semver: "npm:^7.7.2" peerDependencies: "@react-native-community/cli-server-api": "*" peerDependenciesMeta: @@ -3145,26 +3165,11 @@ __metadata: dependencies: "@babel/core": "npm:^7.25.2" "@react-native/babel-preset": "workspace:*" + react: "npm:19.0.0" react-native-macos: "workspace:*" peerDependencies: react: "*" - react-native-macos: "*" - languageName: unknown - linkType: soft - -"@react-native/popup-menu-android@workspace:*, @react-native/popup-menu-android@workspace:packages/react-native-popup-menu-android": - version: 0.0.0-use.local - resolution: "@react-native/popup-menu-android@workspace:packages/react-native-popup-menu-android" - dependencies: - "@react-native/codegen": "workspace:*" - nullthrows: "npm:^1.1.1" - peerDependencies: - "@types/react": ^19.0.0 - react: "*" - react-native: "*" - peerDependenciesMeta: - "@types/react": - optional: true + react-native-macos: "workspace:*" languageName: unknown linkType: soft @@ -3175,11 +3180,12 @@ __metadata: "@react-native-community/cli": "npm:15.0.0-alpha.2" "@react-native-community/cli-platform-android": "npm:15.0.0-alpha.2" "@react-native-community/cli-platform-ios": "npm:15.0.0-alpha.2" + "@react-native-macos/popup-menu-android": "workspace:*" "@react-native/oss-library-example": "workspace:*" - "@react-native/popup-menu-android": "workspace:*" flow-enums-runtime: "npm:^0.0.6" invariant: "npm:^2.2.4" nullthrows: "npm:^1.1.1" + react: "npm:19.0.0" react-native-macos: "workspace:*" peerDependencies: react: 19.0.0 @@ -5038,16 +5044,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^3": - version: 3.0.0 - resolution: "chalk@npm:3.0.0" - dependencies: - ansi-styles: "npm:^4.1.0" - supports-color: "npm:^7.1.0" - checksum: 10c0/ee650b0a065b3d7a6fda258e75d3a86fc8e4effa55871da730a9e42ccb035bf5fd203525e5a1ef45ec2582ecc4f65b47eb11357c526b84dd29a14fb162c414d2 - languageName: node - linkType: hard - "chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -10817,7 +10813,7 @@ __metadata: version: 0.0.0-use.local resolution: "public-api@workspace:tools/api" dependencies: - chalk: "npm:^4.0.0" + chalk: "npm:^4.1.2" glob: "npm:^7.1.1" ini: "npm:^5.0.0" languageName: unknown @@ -10918,14 +10914,14 @@ __metadata: "@types/semver": "npm:^7.1.0" "@types/valid-url": "npm:^1.0.2" "@types/yargs": "npm:^15.0.3" - chalk: "npm:^3" + chalk: "npm:^4.1.2" find-up: "npm:^4.1.0" just-scripts: "npm:^2.3.2" npm-registry-fetch: "npm:^14.0.0" prompts: "npm:^2.3.0" - semver: "npm:^7.5.2" + semver: "npm:^7.7.2" typescript: "npm:^5.6.3" - yargs: "npm:^15.1.0" + yargs: "npm:^17.6.2" bin: react-native-macos-init: ./bin.js languageName: unknown @@ -10935,6 +10931,7 @@ __metadata: version: 0.0.0-use.local resolution: "react-native-macos@workspace:packages/react-native" dependencies: + "@babel/core": "npm:^7.25.2" "@jest/create-cache-key-function": "npm:^29.6.3" "@react-native-macos/virtualized-lists": "workspace:*" "@react-native/assets-registry": "workspace:*" @@ -10949,7 +10946,7 @@ __metadata: babel-jest: "npm:^29.7.0" babel-plugin-syntax-hermes-parser: "npm:0.25.1" base64-js: "npm:^1.5.1" - chalk: "npm:^4.0.0" + chalk: "npm:^4.1.2" commander: "npm:^12.0.0" event-target-shim: "npm:^5.0.1" flow-enums-runtime: "npm:^0.0.6" @@ -10962,11 +10959,12 @@ __metadata: nullthrows: "npm:^1.1.1" pretty-format: "npm:^29.7.0" promise: "npm:^8.3.0" + react: "npm:19.0.0" react-devtools-core: "npm:^6.0.1" react-refresh: "npm:^0.14.0" regenerator-runtime: "npm:^0.13.2" scheduler: "npm:0.25.0" - semver: "npm:^7.1.3" + semver: "npm:^7.7.2" stacktrace-parser: "npm:^0.1.10" whatwg-fetch: "npm:^3.0.0" ws: "npm:^6.2.3" @@ -11478,7 +11476,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:2 >=2.2.1 || 3.x || 4 || 5 || 7, semver@npm:^7.0.0, semver@npm:^7.1.3, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": +"semver@npm:2 >=2.2.1 || 3.x || 4 || 5 || 7, semver@npm:^7.0.0, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": version: 7.6.3 resolution: "semver@npm:7.6.3" bin: @@ -11505,6 +11503,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.7.2": + version: 7.7.2 + resolution: "semver@npm:7.7.2" + bin: + semver: bin/semver.js + checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea + languageName: node + linkType: hard + "semver@npm:~7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4"