set unified version (#42776)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/42776

Changelog: [Internal] set all monorepo packages (including react-native) to one version and update all inter-dependencies (including the template)

Reviewed By: huntie

Differential Revision: D53251917

fbshipit-source-id: 95330ca66dcb7234a3f09752ecc3ed9087ced4bf
This commit is contained in:
Luna Wei
2024-01-31 19:32:08 -08:00
committed by Facebook GitHub Bot
parent 6a8a28b80f
commit ebc2831d5b
11 changed files with 335 additions and 23 deletions
+1
View File
@@ -48,6 +48,7 @@ module.exports = {
'<rootDir>/packages/react-native/jest/ReactNativeInternalFeatureFlagsMock.js',
},
moduleFileExtensions: ['fb.js'].concat(defaults.moduleFileExtensions),
modulePathIgnorePatterns: ['scripts/.*/__fixtures__/'],
unmockedModulePathPatterns: [
'node_modules/react/',
'packages/react-native/Libraries/Renderer',
+28 -22
View File
@@ -4,48 +4,54 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
const {readdirSync, readFileSync} = require('fs');
const path = require('path');
const ROOT_LOCATION = path.join(__dirname, '..', '..');
const PACKAGES_LOCATION = path.join(ROOT_LOCATION, 'packages');
const REPO_ROOT = path.join(path.dirname(__filename), '..', '..');
const PACKAGES_LOCATION = path.join(REPO_ROOT, 'packages');
const DEFAULT_OPTIONS = {includeReactNative: false};
const DEFAULT_OPTIONS /*: Options */ = {includeReactNative: false};
/*::
type PackageJSON = {
name: string,
private?: ?boolean,
version: string,
dependencies: {[string]: string},
devDependencies: {[string]: string},
...
};
type Options = {
includeReactNative?: ?boolean,
};
*/
/**
* Function, which returns an array of all directories inside specified location
*
* @param {string} source Path to directory, where this should be executed
* @returns {string[]} List of directories names
*/
const getDirectories = source =>
const getDirectories = (source /*: string */) /*: Array<string> */ =>
readdirSync(source, {withFileTypes: true})
.filter(file => file.isDirectory())
.map(directory => directory.name);
/**
* @callback forEachPackageCallback
* @param {string} packageAbsolutePath
* @param {string} packageRelativePathFromRoot
* @param {Object} packageManifest
*/
.map(directory => directory.name.toString());
/**
* Iterate through every package inside /packages (ignoring react-native) and call provided callback for each of them
*
* @param {forEachPackageCallback} callback The callback which will be called for each package
* @param {{includeReactNative: (boolean|undefined)}} [options={}] description
*/
const forEachPackage = (callback, options = DEFAULT_OPTIONS) => {
const forEachPackage = (
callback /*: (string, string, PackageJSON) => void */,
options /*: Options */ = DEFAULT_OPTIONS,
) => {
const {includeReactNative} = options;
// We filter react-native package on purpose, so that no CI's script will be executed for this package in future
// Unless includeReactNative options is provided
const packagesDirectories = getDirectories(PACKAGES_LOCATION).filter(
directoryName => directoryName !== 'react-native' || includeReactNative,
directoryName =>
directoryName !== 'react-native' || includeReactNative === true,
);
packagesDirectories.forEach(packageDirectory => {
@@ -53,7 +59,7 @@ const forEachPackage = (callback, options = DEFAULT_OPTIONS) => {
const packageRelativePathFromRoot = path.join('packages', packageDirectory);
const packageManifest = JSON.parse(
readFileSync(path.join(packageAbsolutePath, 'package.json')),
readFileSync(path.join(packageAbsolutePath, 'package.json')).toString(),
);
callback(packageAbsolutePath, packageRelativePathFromRoot, packageManifest);
+1 -1
View File
@@ -54,7 +54,7 @@ type PackageMetadata = {|
* is an object that contains the absolute path to the package and the packageJson.
*/
function getPackagesToPublish() /*: PackageMap */ {
let packages = {};
let packages /*: PackageMap */ = {};
forEachPackage(
(packageAbsolutePath, packageRelativePathFromRoot, packageManifest) => {
@@ -0,0 +1,12 @@
{
"name": "@monorepo/pkg-a",
"version": "0.0.1",
"description": "@monorepo/pkg-a",
"dependencies": {
"@monorepo/pkg-b": "0.0.1",
"@monorepo/other": "0.0.1"
},
"devDependencies": {
"@monorepo/pkg-c": "0.0.1"
}
}
@@ -0,0 +1,10 @@
{
"name": "@monorepo/pkg-b",
"version": "0.0.2",
"description": "@monorepo/pkg-b",
"dependencies": {
"@monorepo/pkg-c": "0.0.1",
"metro-config": "^0.80.3",
"metro-runtime": "^0.80.3"
}
}
@@ -0,0 +1,9 @@
{
"name": "@monorepo/pkg-c",
"version": "0.0.3",
"description": "@monorepo/pkg-c",
"dependencies": {
"metro-config": "^0.80.3",
"metro-runtime": "^0.80.3"
}
}
@@ -0,0 +1,12 @@
{
"name": "react-native",
"version": "0.0.0",
"description": "fake react native package",
"dependencies": {
"@monorepo/pkg-a": "0.0.1",
"@monorepo/pkg-b": "0.0.1",
"@monorepo/pkg-c": "0.0.1",
"metro-config": "^0.80.3",
"metro-runtime": "^0.80.3"
}
}
@@ -0,0 +1,15 @@
{
"name": "react-native-test-template",
"version": "0.0.1",
"private": true,
"dependencies": {
"react": "18.2.0",
"react-native": "0.0.0"
},
"devDependencies": {
"@monorepo/pkg-a": "0.0.1",
"@monorepo/pkg-c": "0.0.0",
"@types/react": "^18.2.6",
"@types/react-test-renderer": "^18.0.0"
}
}
@@ -0,0 +1,79 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`setVersion updates all public packages to version: monorepo-pkg-a 1`] = `
"{
\\"name\\": \\"@monorepo/pkg-a\\",
\\"version\\": \\"0.80.0\\",
\\"description\\": \\"@monorepo/pkg-a\\",
\\"dependencies\\": {
\\"@monorepo/pkg-b\\": \\"0.80.0\\",
\\"@monorepo/other\\": \\"0.0.1\\"
},
\\"devDependencies\\": {
\\"@monorepo/pkg-c\\": \\"0.80.0\\"
}
}
"
`;
exports[`setVersion updates all public packages to version: monorepo-pkg-b 1`] = `
"{
\\"name\\": \\"@monorepo/pkg-b\\",
\\"version\\": \\"0.80.0\\",
\\"description\\": \\"@monorepo/pkg-b\\",
\\"dependencies\\": {
\\"@monorepo/pkg-c\\": \\"0.80.0\\",
\\"metro-config\\": \\"^0.80.3\\",
\\"metro-runtime\\": \\"^0.80.3\\"
}
}
"
`;
exports[`setVersion updates all public packages to version: monorepo-pkg-c 1`] = `
"{
\\"name\\": \\"@monorepo/pkg-c\\",
\\"version\\": \\"0.80.0\\",
\\"description\\": \\"@monorepo/pkg-c\\",
\\"dependencies\\": {
\\"metro-config\\": \\"^0.80.3\\",
\\"metro-runtime\\": \\"^0.80.3\\"
}
}
"
`;
exports[`setVersion updates all public packages to version: react-native 1`] = `
"{
\\"name\\": \\"react-native\\",
\\"version\\": \\"0.80.0\\",
\\"description\\": \\"fake react native package\\",
\\"dependencies\\": {
\\"@monorepo/pkg-a\\": \\"0.80.0\\",
\\"@monorepo/pkg-b\\": \\"0.80.0\\",
\\"@monorepo/pkg-c\\": \\"0.80.0\\",
\\"metro-config\\": \\"^0.80.3\\",
\\"metro-runtime\\": \\"^0.80.3\\"
}
}
"
`;
exports[`setVersion updates all public packages to version: template 1`] = `
"{
\\"name\\": \\"react-native-test-template\\",
\\"version\\": \\"0.0.1\\",
\\"private\\": true,
\\"dependencies\\": {
\\"react\\": \\"18.2.0\\",
\\"react-native\\": \\"0.80.0\\"
},
\\"devDependencies\\": {
\\"@monorepo/pkg-a\\": \\"0.80.0\\",
\\"@monorepo/pkg-c\\": \\"0.80.0\\",
\\"@types/react\\": \\"^18.2.6\\",
\\"@types/react-test-renderer\\": \\"^18.0.0\\"
}
}
"
`;
@@ -0,0 +1,46 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
const setVersion = require('../index');
const path = require('path');
describe('setVersion', () => {
beforeAll(() => {
jest.mock('path', () => {
// $FlowIgnore[underconstrained-implicit-instantiation]
const originalPath = jest.requireActual('path');
return {
...originalPath,
dirname: () => originalPath.join(__dirname, '__fixtures__/two/levels'),
};
});
jest.mock('fs', () => {
// $FlowIgnore[underconstrained-implicit-instantiation]
const originalFs = jest.requireActual('fs');
return {
...originalFs,
writeFileSync: (packagePath, content) => {
expect(content).toMatchSnapshot(
path.basename(path.join(packagePath, '..')),
);
},
};
});
});
test('updates all public packages to version', () => {
setVersion('0.80.0');
});
afterAll(() => {
jest.unmock('path');
jest.unmock('fs');
});
});
+122
View File
@@ -0,0 +1,122 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react-native
*/
'use strict';
const forEachPackage = require('../../monorepo/for-each-package');
const {readFileSync, writeFileSync} = require('fs');
const path = require('path');
const yargs = require('yargs');
function getPublicPackages() {
// eslint-disable-next-line func-call-spacing
const packages = new Set /*::<string>*/();
forEachPackage(
(_, __, packageJson) => {
if (packageJson.private !== true) {
packages.add(packageJson.name);
}
},
{includeReactNative: true},
);
return packages;
}
function setVersion(version /*: string */) {
const publicPackages = getPublicPackages();
forEachPackage(
(packageAbsolutePath, _, packageJson) => {
if (packageJson.private === true) {
return;
}
packageJson.version = version;
if (packageJson.dependencies != null) {
for (const dependency of Object.keys(packageJson.dependencies)) {
if (publicPackages.has(dependency)) {
packageJson.dependencies[dependency] = version;
}
}
}
if (packageJson.devDependencies != null) {
for (const devDependency of Object.keys(packageJson.devDependencies)) {
if (publicPackages.has(devDependency)) {
packageJson.devDependencies[devDependency] = version;
}
}
}
writeFileSync(
path.join(packageAbsolutePath, 'package.json'),
JSON.stringify(packageJson, null, 2) + '\n',
'utf-8',
);
// Update template package.json
if (packageJson.name === 'react-native') {
const templatePackageJsonPath = path.join(
packageAbsolutePath,
'template',
'package.json',
);
const templatePackageJson = JSON.parse(
readFileSync(templatePackageJsonPath).toString(),
);
if (templatePackageJson.dependencies != null) {
for (const dependency of Object.keys(
templatePackageJson.dependencies,
)) {
if (publicPackages.has(dependency)) {
templatePackageJson.dependencies[dependency] = version;
}
}
}
if (templatePackageJson.devDependencies != null) {
for (const devDependency of Object.keys(
templatePackageJson.devDependencies,
)) {
if (publicPackages.has(devDependency)) {
templatePackageJson.devDependencies[devDependency] = version;
}
}
}
writeFileSync(
templatePackageJsonPath,
JSON.stringify(templatePackageJson, null, 2) + '\n',
'utf-8',
);
}
},
{includeReactNative: true},
);
}
module.exports = setVersion;
if (require.main === module) {
const {toVersion} = yargs(process.argv.slice(2))
.command(
'$0 <to-version>',
'Update all monorepo packages to <to-version>',
args =>
args.positional('to-version', {
type: 'string',
description: 'Set the version of all packages to this value',
required: true,
}),
)
.parseSync();
setVersion(toVersion);
}