Reorganise shared script utils (#52473)

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

Shared utils that were located in the root of `scripts/` are now colocated closer to their dependencies or moved to `scripts/shared/` — simplifying the root directory layout.

Changelog: [Internal]

Reviewed By: robhogan

Differential Revision: D77873875

fbshipit-source-id: e04dba41a1ef811d32793931033fdfa93afad0cd
This commit is contained in:
Alex Hunt
2025-07-08 06:10:36 -07:00
committed by Facebook GitHub Bot
parent 6dfe59e1df
commit fc5e33b582
45 changed files with 141 additions and 153 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ if (process.env.FBSOURCE_ENV === '1') {
require('@fb-tools/babel-register');
} else {
// Register Babel to allow local packages to be loaded from source
require('../scripts/babel-register').registerForMonorepo();
require('../scripts/shared/babelRegister').registerForMonorepo();
}
const transformer = require('@react-native/metro-babel-transformer');
+1 -1
View File
@@ -13,7 +13,7 @@ export type * from './index.flow';
*/
if (!process.env.BUILD_EXCLUDE_BABEL_REGISTER) {
require('../../../scripts/babel-register').registerForMonorepo();
require('../../../scripts/shared/babelRegister').registerForMonorepo();
}
module.exports = require('./index.flow');
+1 -1
View File
@@ -13,7 +13,7 @@ export type * from './index.flow';
*/
if (process.env.BUILD_EXCLUDE_BABEL_REGISTER == null) {
require('../../../scripts/babel-register').registerForMonorepo();
require('../../../scripts/shared/babelRegister').registerForMonorepo();
}
module.exports = require('./index.flow');
@@ -13,7 +13,7 @@ export type * from './version.flow';
*/
if (process.env.BUILD_EXCLUDE_BABEL_REGISTER == null) {
require('../../../../scripts/babel-register').registerForMonorepo();
require('../../../../scripts/shared/babelRegister').registerForMonorepo();
}
module.exports = require('./version.flow');
@@ -13,7 +13,7 @@ export type * from './index.flow';
*/
if (!Boolean(process.env.BUILD_EXCLUDE_BABEL_REGISTER)) {
require('../../../../scripts/babel-register').registerForMonorepo();
require('../../../../scripts/shared/babelRegister').registerForMonorepo();
}
module.exports = require('./index.flow');
+1 -1
View File
@@ -13,7 +13,7 @@ export type * from './index.flow';
*/
if (!process.env.BUILD_EXCLUDE_BABEL_REGISTER) {
require('../../../../scripts/babel-register').registerForMonorepo();
require('../../../../scripts/shared/babelRegister').registerForMonorepo();
}
export * from './index.flow';
+1 -1
View File
@@ -13,7 +13,7 @@ export type * from './index.flow';
*/
if (!process.env.BUILD_EXCLUDE_BABEL_REGISTER) {
require('../../../scripts/babel-register').registerForMonorepo();
require('../../../scripts/shared/babelRegister').registerForMonorepo();
}
export * from './index.flow';
+1 -1
View File
@@ -13,7 +13,7 @@ export type * from './index.flow';
*/
if (!process.env.BUILD_EXCLUDE_BABEL_REGISTER) {
require('../../../scripts/babel-register').registerForMonorepo();
require('../../../scripts/shared/babelRegister').registerForMonorepo();
}
module.exports = require('./index.flow');
+1 -1
View File
@@ -9,7 +9,7 @@
*/
if (require.main === module) {
require('../../../../scripts/babel-register').registerForMonorepo();
require('../../../../scripts/shared/babelRegister').registerForMonorepo();
let command;
+1 -1
View File
@@ -31,7 +31,7 @@ function injectCoreCLIUtilsRuntimePatch() {
if (process.env.BUILD_EXCLUDE_BABEL_REGISTER == null) {
// $FlowFixMe[cannot-resolve-module]
require('../../scripts/babel-register').registerForMonorepo();
require('../../scripts/shared/babelRegister').registerForMonorepo();
}
injectCoreCLIUtilsRuntimePatch();
+1 -1
View File
@@ -31,7 +31,7 @@ function injectCoreCLIUtilsRuntimePatch() {
if (process.env.BUILD_EXCLUDE_BABEL_REGISTER == null) {
// $FlowFixMe[cannot-resolve-module]
require('../../scripts/babel-register').registerForMonorepo();
require('../../scripts/shared/babelRegister').registerForMonorepo();
}
injectCoreCLIUtilsRuntimePatch();
@@ -10,5 +10,5 @@
'use strict';
require('../../../scripts/babel-register').registerForMonorepo();
require('../../../scripts/shared/babelRegister').registerForMonorepo();
module.exports = require('@react-native/metro-babel-transformer');
+1 -1
View File
@@ -8,6 +8,6 @@
* @format
*/
require('../../../scripts/babel-register').registerForMonorepo();
require('../../../scripts/shared/babelRegister').registerForMonorepo();
module.exports = require('./runner');
+1 -1
View File
@@ -8,6 +8,6 @@
* @format
*/
require('../../../../scripts/babel-register').registerForMonorepo();
require('../../../../scripts/shared/babelRegister').registerForMonorepo();
module.exports = require('./warmup');
+2 -2
View File
@@ -8,9 +8,9 @@
* @format
*/
require('../babel-register').registerForScript();
require('../shared/babelRegister').registerForScript();
const {PACKAGES_DIR, REPO_ROOT} = require('../consts');
const {PACKAGES_DIR, REPO_ROOT} = require('../shared/consts');
const {
buildConfig,
getBabelConfig,
+1 -1
View File
@@ -8,7 +8,7 @@
* @format
*/
require('../babel-register').registerForScript();
require('../shared/babelRegister').registerForScript();
const {BUILD_DIR, PACKAGES_DIR} = require('./build');
const {buildConfig} = require('./config');
+1 -1
View File
@@ -8,7 +8,7 @@
* @format
*/
const {PACKAGES_DIR} = require('../consts');
const {PACKAGES_DIR} = require('../shared/consts');
// $FlowFixMe[untyped-import]: TODO type ansi-styles
const ansiStyles = require('ansi-styles');
const {execSync, spawnSync} = require('child_process');
+3 -3
View File
@@ -10,10 +10,10 @@
'use strict';
/*:: import type {ProjectInfo} from '../utils/monorepo'; */
/*:: import type {ProjectInfo} from '../shared/monorepoUtils'; */
const {PRIVATE_DIR, REPO_ROOT} = require('../consts');
const {getPackages} = require('../utils/monorepo');
const {PRIVATE_DIR, REPO_ROOT} = require('../shared/consts');
const {getPackages} = require('../shared/monorepoUtils');
const {retry} = require('./utils/retry');
const {
VERDACCIO_SERVER_URL,
+1 -1
View File
@@ -10,7 +10,7 @@
'use strict';
const {REPO_ROOT} = require('../../consts');
const {REPO_ROOT} = require('../../shared/consts');
const {execSync, spawn} = require('child_process');
const fs = require('fs');
const path = require('path');
@@ -11,8 +11,8 @@
import type {PluginObj} from '@babel/core';
const {PACKAGES_DIR, REACT_NATIVE_PACKAGE_DIR} = require('../../consts');
const {isGitRepo} = require('../../scm-utils');
const {PACKAGES_DIR, REACT_NATIVE_PACKAGE_DIR} = require('../../shared/consts');
const isGitRepo = require('../../shared/isGitRepo');
const {API_EXTRACTOR_CONFIG_FILE, TYPES_OUTPUT_DIR} = require('../config');
const apiSnapshotTemplate = require('./templates/ReactNativeApi.d.ts-template.js');
const applyBabelTransformsSeq = require('./utils/applyBabelTransformsSeq');
@@ -8,7 +8,7 @@
* @format
*/
const {PACKAGES_DIR, REPO_ROOT} = require('../../consts');
const {PACKAGES_DIR, REPO_ROOT} = require('../../shared/consts');
const {ENTRY_POINT, IGNORE_PATTERNS, TYPES_OUTPUT_DIR} = require('../config');
const getRequireStack = require('./resolution/getRequireStack');
const translatedModuleTemplate = require('./templates/translatedModule.d.ts-template');
+1 -1
View File
@@ -8,7 +8,7 @@
* @format
*/
require('../../babel-register').registerForScript();
require('../../shared/babelRegister').registerForScript();
const buildApiSnapshot = require('./buildApiSnapshot');
const buildGeneratedTypes = require('./buildGeneratedTypes');
@@ -8,7 +8,7 @@
* @format
*/
const {REPO_ROOT} = require('../../../consts');
const {REPO_ROOT} = require('../../../shared/consts');
const debug = require('debug')('build-types:resolution');
const fs = require('fs');
const path = require('path');
@@ -8,8 +8,8 @@
* @format
*/
const {PACKAGES_DIR} = require('../../../consts');
const {getPackages} = require('../../../utils/monorepo');
const {PACKAGES_DIR} = require('../../../shared/consts');
const {getPackages} = require('../../../shared/monorepoUtils');
const {existsSync} = require('fs');
const path = require('path');
+1 -1
View File
@@ -9,7 +9,7 @@
* @oncall react_native
*/
require('../../babel-register').registerForScript();
require('../../shared/babelRegister').registerForScript();
const {diffApiSnapshot} = require('./diffApiSnapshot');
const fs = require('fs');
+47 -1
View File
@@ -8,7 +8,6 @@
* @noflow
*/
const {getVersionsBySpec} = require('../../npm-utils');
const {getPackages} = require('../../utils/monorepo');
const {exit} = require('shelljs');
const yargs = require('yargs');
@@ -42,6 +41,53 @@ function reversePatchComp(semverA, semverB) {
return patchB - patchA;
}
/**
* `packageName`: name of npm package
* `spec`: spec range ex. '^0.72.0'
*
* Return an array of versions of the specified spec range or throw an error
*/
function getVersionsBySpec(
packageName /*: string */,
spec /*: string */,
) /*: Array<string> */ {
const npmString = `npm view ${packageName}@'${spec}' version --json`;
const result = exec(npmString, {silent: true});
if (result.code) {
// Special handling if no such package spec exists
if (result.stderr.includes('npm ERR! code E404')) {
/**
* npm ERR! code E404
* npm ERR! 404 No match found for version ^0.72.0
* npm ERR! 404
* npm ERR! 404 '@react-native/community-cli-plugin@^0.72.0' is not in this registry.
* npm ERR! 404
* npm ERR! 404 Note that you can also install from a
* npm ERR! 404 tarball, folder, http url, or git url.
* {
* "error": {
* "code": "E404",
* "summary": "No match found for version ^0.72.0",
* "detail": "\n '@react-native/community-cli-plugin@^0.72.0' is not in this registry.\n\nNote that you can also install from a\ntarball, folder, http url, or git url."
* }
* }
*/
const error = JSON.parse(
result.stderr
.split('\n')
.filter(line => !line.includes('npm ERR'))
.join(''),
).error;
throw new Error(error.summary);
} else {
throw new Error(`Failed: ${npmString}`);
}
}
const versions = JSON.parse(result.stdout.trim());
return !Array.isArray(versions) ? [versions] : versions;
}
async function main() {
const data = [];
const packages = await getPackages({
+1 -1
View File
@@ -17,8 +17,8 @@
* and to make it more accessible for other devs to play around with.
*/
const {REPO_ROOT} = require('../consts');
const {initNewProjectFromSource} = require('../e2e/init-project-e2e');
const {REPO_ROOT} = require('../shared/consts');
const {
checkPackagerRunning,
launchPackagerInSeparateWindow,
@@ -21,7 +21,7 @@ const getNpmInfoMock = jest.fn();
const generateAndroidArtifactsMock = jest.fn();
const getPackagesMock = jest.fn();
const {REPO_ROOT} = require('../../consts');
const {REPO_ROOT} = require('../../shared/consts');
const {publishNpm} = require('../publish-npm');
const path = require('path');
@@ -33,7 +33,7 @@ describe('publish-npm', () => {
.mock('shelljs', () => ({
exec: execMock,
}))
.mock('./../../scm-utils', () => ({
.mock('../../releases/utils/scm-utils', () => ({
exitIfNotOnGit: command => command(),
getCurrentCommit: () => 'currentco_mmit',
isTaggedLatest: isTaggedLatestMock,
@@ -49,8 +49,8 @@ describe('publish-npm', () => {
.mock('../../releases/set-rn-artifacts-version', () => ({
updateReactNativeArtifacts: updateReactNativeArtifactsMock,
}))
.mock('../../npm-utils', () => ({
...jest.requireActual('../../npm-utils'),
.mock('../../releases/utils/npm-utils', () => ({
...jest.requireActual('../../releases/utils/npm-utils'),
publishPackage: publishPackageMock,
getNpmInfo: getNpmInfoMock,
}));
@@ -78,7 +78,7 @@ describe('publish-npm', () => {
it('should fail when invalid build type is passed', async () => {
// Call actual function
// $FlowExpectedError[underconstrained-implicit-instantiation]
const npmUtils = jest.requireActual('../../npm-utils');
const npmUtils = jest.requireActual('../../releases/utils/npm-utils');
getNpmInfoMock.mockImplementation(npmUtils.getNpmInfo);
await expect(async () => {
@@ -117,14 +117,14 @@ describe('publish-npm', () => {
describe("publishNpm('nightly')", () => {
beforeAll(() => {
jest.mock('../../utils/monorepo', () => ({
...jest.requireActual('../../utils/monorepo'),
jest.mock('../../shared/monorepoUtils', () => ({
...jest.requireActual('../../shared/monorepoUtils'),
getPackages: getPackagesMock,
}));
});
afterAll(() => {
jest.unmock('../../utils/monorepo');
jest.unmock('../../shared/monorepoUtils');
});
it('should publish', async () => {
@@ -20,7 +20,7 @@ const fetchMock = jest.fn();
jest.mock('child_process', () => ({execSync}));
jest.mock('shelljs', () => ({exec: execMock}));
jest.mock('../../utils/monorepo', () => ({
jest.mock('../../shared/monorepoUtils', () => ({
getPackages: getPackagesMock,
}));
// $FlowIgnore[cannot-write]
+3 -3
View File
@@ -14,17 +14,17 @@
import type {BuildType} from '../releases/utils/version-utils';
*/
const {REPO_ROOT} = require('../consts');
const {getNpmInfo, publishPackage} = require('../npm-utils');
const {
updateReactNativeArtifacts,
} = require('../releases/set-rn-artifacts-version');
const {setVersion} = require('../releases/set-version');
const {getNpmInfo, publishPackage} = require('../releases/utils/npm-utils');
const {
publishAndroidArtifactsToMaven,
publishExternalArtifactsToMaven,
} = require('../releases/utils/release-utils');
const {getPackages} = require('../utils/monorepo');
const {REPO_ROOT} = require('../shared/consts');
const {getPackages} = require('../shared/monorepoUtils');
const path = require('path');
const yargs = require('yargs');
@@ -8,8 +8,8 @@
* @format
*/
const {publishPackage} = require('../npm-utils');
const {getPackages} = require('../utils/monorepo');
const {publishPackage} = require('../releases/utils/npm-utils');
const {getPackages} = require('../shared/monorepoUtils');
const {execSync} = require('child_process');
const {parseArgs} = require('util');
@@ -20,7 +20,7 @@ jest.mock('fs', () => ({
},
}));
const {REPO_ROOT} = require('../../consts');
const {REPO_ROOT} = require('../../shared/consts');
const {updateReactNativeArtifacts} = require('../set-rn-artifacts-version');
const path = require('path');
@@ -11,7 +11,7 @@
const {setVersion} = require('../set-version');
const path = require('path');
jest.mock('../../consts', () => ({
jest.mock('../../shared/consts', () => ({
REPO_ROOT: path.join(__dirname, '__fixtures__', 'set-version'),
PACKAGES_DIR: path.join(__dirname, '__fixtures__', 'set-version', 'packages'),
REACT_NATIVE_PACKAGE_DIR: path.join(
+1 -1
View File
@@ -9,7 +9,7 @@
*/
const {setVersion} = require('../releases/set-version');
const {getBranchName} = require('../scm-utils');
const {getBranchName} = require('../releases/utils/scm-utils');
const {parseVersion} = require('./utils/version-utils');
const {execSync} = require('child_process');
const yargs = require('yargs');
+1 -1
View File
@@ -16,7 +16,7 @@ const {setupDependencies} = require('./ios-prebuild/setupDependencies');
const {createSwiftPackageFile} = require('./ios-prebuild/swift-package');
const path = require('path');
require('../babel-register').registerForScript();
require('../shared/babelRegister').registerForScript();
const THIRD_PARTY_PATH = 'packages/react-native/third-party';
const BUILD_DESTINATION = '.build';
+2 -2
View File
@@ -12,8 +12,8 @@
import type {BuildType, Version} from './utils/version-utils';
*/
const {REPO_ROOT} = require('../consts');
const {getNpmInfo} = require('../npm-utils');
const {getNpmInfo} = require('../releases/utils/npm-utils');
const {REPO_ROOT} = require('../shared/consts');
const {parseVersion, validateBuildType} = require('./utils/version-utils');
const {promises: fs} = require('fs');
const path = require('path');
+2 -2
View File
@@ -11,14 +11,14 @@
'use strict';
/*::
import type {PackageJson} from '../utils/monorepo';
import type {PackageJson} from '../shared/monorepoUtils';
*/
const {
getPackages,
getWorkspaceRoot,
updatePackageJson,
} = require('../utils/monorepo');
} = require('../shared/monorepoUtils');
const {updateReactNativeArtifacts} = require('./set-rn-artifacts-version');
const {parseArgs} = require('util');
@@ -8,7 +8,7 @@
* @format
*/
const {getNpmInfo, getVersionsBySpec, publishPackage} = require('../npm-utils');
const {getNpmInfo, publishPackage} = require('../npm-utils');
const execMock = jest.fn();
const getCurrentCommitMock = jest.fn();
@@ -98,49 +98,4 @@ describe('npm-utils', () => {
});
});
});
describe('getVersionsBySpec', () => {
it('should return array when single version returned', () => {
execMock.mockImplementationOnce(() => ({code: 0, stdout: '"0.72.0" \n'}));
const versions = getVersionsBySpec('mypackage', '^0.72.0');
expect(versions).toEqual(['0.72.0']);
});
it('should return array of versions', () => {
execMock.mockImplementationOnce(() => ({
code: 0,
stdout: '[\n"0.73.0",\n"0.73.1"\n]\n',
}));
const versions = getVersionsBySpec('mypackage', '^0.73.0');
expect(versions).toEqual(['0.73.0', '0.73.1']);
});
it('should return error summary if E404', () => {
const error =
`npm ERR! code E404\n` +
`npm ERR! 404 No match found for version ^0.72.0\n` +
`npm ERR! 404\n` +
`npm ERR! 404 '@react-native/community-cli-plugin@^0.72.0' is not in this registry.\n` +
`npm ERR! 404\n` +
`npm ERR! 404 Note that you can also install from a\n` +
`npm ERR! 404 tarball, folder, http url, or git url.\n` +
`{\n` +
` "error": {\n` +
` "code": "E404",\n` +
` "summary": "No match found for version ^0.72.0",\n` +
` "detail": "\n '@react-native/community-cli-plugin@^0.72.0' is not in this registry.\n\nNote that you can also install from a\ntarball, folder, http url, or git url."\n` +
` }\n` +
`}\n`;
execMock.mockImplementationOnce(() => ({
code: 1,
stderr: error,
}));
expect(() => {
getVersionsBySpec('mypackage', '^0.72.0');
}).toThrow('No match found for version ^0.72.0');
});
});
});
@@ -10,12 +10,12 @@
'use strict';
const {parseVersion} = require('./releases/utils/version-utils');
const {
exitIfNotOnGit,
getCurrentCommit,
isTaggedLatest,
} = require('./scm-utils');
const {parseVersion} = require('./version-utils');
const {exec} = require('shelljs');
/*::
@@ -173,55 +173,7 @@ function getPackageVersionStrByTag(
return result.stdout.trim();
}
/**
* `packageName`: name of npm package
* `spec`: spec range ex. '^0.72.0'
*
* Return an array of versions of the specified spec range or throw an error
*/
function getVersionsBySpec(
packageName /*: string */,
spec /*: string */,
) /*: Array<string> */ {
const npmString = `npm view ${packageName}@'${spec}' version --json`;
const result = exec(npmString, {silent: true});
if (result.code) {
// Special handling if no such package spec exists
if (result.stderr.includes('npm ERR! code E404')) {
/**
* npm ERR! code E404
* npm ERR! 404 No match found for version ^0.72.0
* npm ERR! 404
* npm ERR! 404 '@react-native/community-cli-plugin@^0.72.0' is not in this registry.
* npm ERR! 404
* npm ERR! 404 Note that you can also install from a
* npm ERR! 404 tarball, folder, http url, or git url.
* {
* "error": {
* "code": "E404",
* "summary": "No match found for version ^0.72.0",
* "detail": "\n '@react-native/community-cli-plugin@^0.72.0' is not in this registry.\n\nNote that you can also install from a\ntarball, folder, http url, or git url."
* }
* }
*/
const error = JSON.parse(
result.stderr
.split('\n')
.filter(line => !line.includes('npm ERR'))
.join(''),
).error;
throw new Error(error.summary);
} else {
throw new Error(`Failed: ${npmString}`);
}
}
const versions = JSON.parse(result.stdout.trim());
return !Array.isArray(versions) ? [versions] : versions;
}
module.exports = {
getNpmInfo,
getVersionsBySpec,
publishPackage,
};
@@ -28,7 +28,7 @@ let isRegisteredForMonorepo = false;
* ```js
* // Place in a package entry point
* if (!process.env.BUILD_EXCLUDE_BABEL_REGISTER) {
* require('../../../scripts/babel-register').registerForMonorepo();
* require('../../../scripts/shared/babelRegister').registerForMonorepo();
* }
* ```
*/
@@ -57,7 +57,7 @@ function registerForMonorepo() {
*
* ```js
* // Place in a script entry point
* require('../babel-register').registerForScript();
* require('../shared/babelRegister').registerForScript();
* ```
*/
function registerForScript() {
@@ -13,7 +13,7 @@ const path = require('path');
/**
* The absolute path to the repo root.
*/
const REPO_ROOT /*: string */ = path.resolve(__dirname, '..');
const REPO_ROOT /*: string */ = path.resolve(__dirname, '../..');
/**
* The absolute path to the packages directory (note: this directory alone may
+35
View File
@@ -0,0 +1,35 @@
/**
* 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
*/
'use strict';
const childProcess = require('child_process');
/*::
type Commit = string;
*/
function isGitRepo() /*: boolean */ {
try {
const result = childProcess.execSync(
'git rev-parse --is-inside-work-tree',
{
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
},
);
return result.trim() === 'true';
} catch (error) {
console.error(`Failed to check git repository status: ${error}`);
}
return false;
}
module.exports = isGitRepo;
@@ -8,7 +8,7 @@
* @format
*/
const {REPO_ROOT} = require('../consts');
const {REPO_ROOT} = require('./consts');
const {promises: fs} = require('fs');
const glob = require('glob');
const path = require('path');