Replace glob@^7.0.0 with tinyglobby@^0.2.15 (#54737)

Summary:
This replaces `glob@^7.0.0` with `tinyglobby@^0.2.15`. `glob@7` has been deprecated for a while and some versions after had security notices released for them. The plan is to backport this PR to `0.81.x` and onwards.

> [!NOTE]
> This is a stopgap solution until `fs.glob` becomes generally available with the EOL of Node v20

Succeeds:
- https://github.com/facebook/react-native/issues/54669
- https://github.com/facebook/react-native/issues/48875

## Changelog:

[GENERAL] [SECURITY] - Replace `glob@^7.0.0` with `tinyglobby@^0.2.15`

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

Test Plan:
- Ran all modified commands manually and `pod install in `rn-tester`
- NOTE: `ios-prebuild`-related scripts haven't been run manually yet

Reviewed By: robhogan

Differential Revision: D88069145

Pulled By: huntie

fbshipit-source-id: 0c455342a4c6d1d6605fd09fe47b418e5d751491
This commit is contained in:
Phil Pluckthun
2025-12-03 05:45:48 -08:00
committed by meta-codesync[bot]
parent b447d267a8
commit 41eace0093
17 changed files with 159 additions and 160 deletions
-79
View File
@@ -1,79 +0,0 @@
// flow-typed signature: d2a519d7d007e9ba3e5bf2ac3ff76eca
// flow-typed version: f243e51ed7/glob_v7.x.x/flow_>=v0.104.x
declare module "glob" {
declare type MinimatchOptions = {|
debug?: boolean,
nobrace?: boolean,
noglobstar?: boolean,
dot?: boolean,
noext?: boolean,
nocase?: boolean,
nonull?: boolean,
matchBase?: boolean,
nocomment?: boolean,
nonegate?: boolean,
flipNegate?: boolean
|};
declare type Options = {|
...MinimatchOptions,
cwd?: string,
root?: string,
nomount?: boolean,
mark?: boolean,
nosort?: boolean,
stat?: boolean,
silent?: boolean,
strict?: boolean,
cache?: { [path: string]: boolean | "DIR" | "FILE" | $ReadOnlyArray<string>, ... },
statCache?: { [path: string]: boolean | { isDirectory(): boolean, ... } | void, ... },
symlinks?: { [path: string]: boolean | void, ... },
realpathCache?: { [path: string]: string, ... },
sync?: boolean,
nounique?: boolean,
nodir?: boolean,
ignore?: string | $ReadOnlyArray<string>,
follow?: boolean,
realpath?: boolean,
absolute?: boolean
|};
/**
* Called when an error occurs, or matches are found
* err
* matches: filenames found matching the pattern
*/
declare type CallBack = (err: ?Error, matches: Array<string>) => void;
declare class Glob extends events$EventEmitter {
constructor(pattern: string): this;
constructor(pattern: string, callback: CallBack): this;
constructor(pattern: string, options: Options, callback: CallBack): this;
minimatch: {...};
options: Options;
aborted: boolean;
cache: { [path: string]: boolean | "DIR" | "FILE" | $ReadOnlyArray<string>, ... };
statCache: { [path: string]: boolean | { isDirectory(): boolean, ... } | void, ... };
symlinks: { [path: string]: boolean | void, ... };
realpathCache: { [path: string]: string, ... };
found: Array<string>;
pause(): void;
resume(): void;
abort(): void;
}
declare class GlobModule {
Glob: Class<Glob>;
(pattern: string, callback: CallBack): void;
(pattern: string, options: Options, callback: CallBack): void;
hasMagic(pattern: string, options?: Options): boolean;
sync(pattern: string, options?: Options): Array<string>;
}
declare module.exports: GlobModule;
}
+34
View File
@@ -0,0 +1,34 @@
declare module 'tinyglobby' {
import typeof FSModule from 'fs';
declare type FileSystemAdapter = Partial<FSModule>;
declare type GlobOptions = {
absolute?: boolean,
braceExpansion?: boolean,
caseSensitiveMatch?: boolean,
cwd?: string | URL,
debug?: boolean,
deep?: number,
dot?: boolean,
expandDirectories?: boolean,
extglob?: boolean,
followSymbolicLinks?: boolean,
fs?: FileSystemAdapter,
globstar?: boolean,
ignore?: string | $ReadOnlyArray<string>,
onlyDirectories?: boolean,
onlyFiles?: boolean,
signal?: AbortSignal,
};
declare type GlobModule = {
convertPathToPattern(path: string): string;
escapePath(path: string): string;
isDynamicPattern(pattern: string, options?: { caseSensitiveMatch: boolean }): boolean;
glob(patterns: string | $ReadOnlyArray<string>, options?: GlobOptions): Promise<string[]>;
globSync(patterns: string | $ReadOnlyArray<string>, options?: GlobOptions): string[];
};
declare module.exports: GlobModule;
}
+1 -1
View File
@@ -87,7 +87,6 @@
"fb-dotslash": "0.5.8",
"flow-api-translator": "0.32.0",
"flow-bin": "^0.291.0",
"glob": "^7.1.1",
"hermes-eslint": "0.32.0",
"hermes-transform": "0.32.0",
"ini": "^5.0.0",
@@ -116,6 +115,7 @@
"supports-color": "^7.1.0",
"temp-dir": "^2.0.0",
"tinybench": "^4.1.0",
"tinyglobby": "^0.2.15",
"typescript": "5.8.3",
"ws": "^7.5.10"
},
+1 -1
View File
@@ -31,10 +31,10 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/parser": "^7.25.3",
"glob": "^7.1.1",
"hermes-parser": "0.32.0",
"invariant": "^2.2.4",
"nullthrows": "^1.1.1",
"tinyglobby": "^0.2.15",
"yargs": "^17.6.2"
},
"devDependencies": {
+6 -3
View File
@@ -25,10 +25,10 @@
const babel = require('@babel/core');
const fs = require('fs');
const glob = require('glob');
const micromatch = require('micromatch');
const path = require('path');
const prettier = require('prettier');
const {globSync} = require('tinyglobby');
const {styleText} = require('util');
const prettierConfig = JSON.parse(
@@ -106,8 +106,11 @@ async function buildFile(file, silent) {
}
const srcDir = path.resolve(__dirname, '..', SRC_DIR);
const pattern = path.resolve(srcDir, '**/*');
const files = glob.sync(pattern, {nodir: true});
const files = globSync('**/*', {
cwd: srcDir,
absolute: true,
onlyFiles: true,
});
process.stdout.write(fixedWidth(`${path.basename(PACKAGE_DIR)}\n`));
@@ -15,8 +15,8 @@ const {FlowParser} = require('../../parsers/flow/parser');
const {TypeScriptParser} = require('../../parsers/typescript/parser');
const {filterJSFile} = require('./combine-utils');
const fs = require('fs');
const glob = require('glob');
const path = require('path');
const {globSync} = require('tinyglobby');
const flowParser = new FlowParser();
const typescriptParser = new TypeScriptParser();
@@ -66,13 +66,11 @@ function expandDirectoriesIntoFiles(
if (!fs.lstatSync(file).isDirectory()) {
return [file];
}
const filePattern = path.sep === '\\' ? file.replace(/\\/g, '/') : file;
return glob.sync(`${filePattern}/**/*{,.fb}.{js,ts,tsx}`, {
nodir: true,
// TODO: This will remove the need of slash substitution above for Windows,
// but it requires glob@v9+; with the package currenlty relying on
// glob@7.1.1; and flow-typed repo not having definitions for glob@9+.
// windowsPathsNoEscape: true,
return globSync('**/*{,.fb}.{js,ts,tsx}', {
expandDirectories: false,
onlyFiles: true,
absolute: true,
cwd: file,
});
})
.filter(element => filterJSFile(element, platform, exclude));
+1 -1
View File
@@ -175,7 +175,6 @@
"base64-js": "^1.5.1",
"commander": "^12.0.0",
"flow-enums-runtime": "^0.0.6",
"glob": "^7.1.1",
"hermes-compiler": "0.0.0",
"invariant": "^2.2.4",
"jest-environment-node": "^29.7.0",
@@ -191,6 +190,7 @@
"scheduler": "0.27.0",
"semver": "^7.1.3",
"stacktrace-parser": "^0.1.10",
"tinyglobby": "^0.2.15",
"whatwg-fetch": "^3.0.0",
"ws": "^7.5.10",
"yargs": "^17.6.2"
@@ -13,8 +13,8 @@
const CodegenUtils = require('../codegen-utils');
const {codegenLog} = require('./utils');
const fs = require('fs');
const glob = require('glob');
const path = require('path');
const {globSync} = require('tinyglobby');
function generateSchemaInfos(
libraries /*: $ReadOnlyArray<$FlowFixMe> */,
@@ -57,17 +57,18 @@ function extractSupportedApplePlatforms(
dependencyPath /*: string */,
) /*: ?{[string]: boolean} */ {
codegenLog('Searching for podspec in the project dependencies.', true);
const podspecs = glob.sync('*.podspec', {cwd: dependencyPath});
const podspecs = globSync('*.podspec', {
cwd: dependencyPath,
onlyFiles: true,
absolute: true,
});
if (podspecs.length === 0) {
return;
}
// Take the first podspec found
const podspec = fs.readFileSync(
path.join(dependencyPath, podspecs[0]),
'utf8',
);
const podspec = fs.readFileSync(podspecs[0], 'utf8');
/**
* Podspec can have platforms defined in two ways:
+9 -2
View File
@@ -15,8 +15,8 @@ import type {BuildFlavor, Destination} from './types';
const {createLogger} = require('./utils');
const {execSync} = require('child_process');
const fs = require('fs');
const glob = require('glob');
const path = require('path');
const {globSync} = require('tinyglobby');
const buildLog = createLogger('SPM');
@@ -28,9 +28,16 @@ function computeFrameworkPaths(
productsFolder /*: string */,
) /*: Array<string> */ {
// The frameworks are in the products folder under a platform/buildType folder and are directories ending with .framework
const frameworks = glob.sync('**/*.framework', {
const frameworks = globSync('**/*.framework', {
cwd: productsFolder,
expandDirectories: false,
onlyDirectories: true,
absolute: true,
}).map(framework => {
// NOTE: tinyglobby outputs a trailing slash for directories
return framework[framework.length - 1] === '/'
? framework.slice(0, -1)
: framework;
});
if (frameworks.length === 0) {
+5 -3
View File
@@ -9,8 +9,8 @@
*/
const fs = require('fs');
const glob = require('glob');
const path = require('path');
const {globSync} = require('tinyglobby');
/**
* This regular expression is designed to match function calls to `podspec_sources` within a podspec file.
@@ -32,9 +32,10 @@ function getHeaderFilesFromPodspecs(
rootFolder /*:string*/,
) /*: { [key: string]: string[] }*/ {
// Find podspec files
const podSpecFiles = glob.sync('**/*.podspec', {
const podSpecFiles = globSync('**/*.podspec', {
cwd: rootFolder,
absolute: true,
onlyFiles: true,
});
const headers /*: { [key: string]: string[] }*/ = {};
@@ -66,9 +67,10 @@ function getHeaderFilesFromPodspecs(
const p = path.resolve(process.cwd(), path.dirname(podspec));
const results = globPatterns
.map(g => {
return glob.sync(g.replace('{h}', 'h'), {
return globSync(g.replace('{h}', 'h'), {
cwd: p,
absolute: true,
expandDirectories: false,
});
})
.flat();
+15 -13
View File
@@ -20,10 +20,10 @@ const {
const babel = require('@babel/core');
const translate = require('flow-api-translator');
const {promises: fs} = require('fs');
const glob = require('glob');
const micromatch = require('micromatch');
const path = require('path');
const prettier = require('prettier');
const {globSync} = require('tinyglobby');
const ts = require('typescript');
const {parseArgs, styleText} = require('util');
@@ -103,15 +103,15 @@ async function buildPackage(packageName /*: string */) {
const {emitTypeScriptDefs} = getBuildOptions(packageName);
const entryPoints = await getEntryPoints(packageName);
const files = glob
.sync(path.resolve(PACKAGES_DIR, packageName, SRC_DIR, '**/*'), {
nodir: true,
})
.filter(
file =>
!entryPoints.has(file) &&
!entryPoints.has(file.replace(/\.js$/, '.flow.js')),
);
const files = globSync('**/*', {
cwd: path.resolve(PACKAGES_DIR, packageName, SRC_DIR),
onlyFiles: true,
absolute: true,
}).filter(
file =>
!entryPoints.has(file) &&
!entryPoints.has(file.replace(/\.js$/, '.flow.js')),
);
process.stdout.write(
`${packageName} ${styleText('dim', '.').repeat(72 - packageName.length)} `,
@@ -429,9 +429,11 @@ function normalizeExportsTarget(target /*: string */) /*: string */ {
}
function validateTypeScriptDefs(packageName /*: string */) {
const files = glob.sync(
path.resolve(PACKAGES_DIR, packageName, BUILD_DIR, '**/*.d.ts'),
);
const files = globSync('**/*.d.ts', {
cwd: path.resolve(PACKAGES_DIR, packageName, BUILD_DIR),
absolute: true,
onlyFiles: true,
});
const compilerOptions = {
...getTypeScriptCompilerOptions(packageName),
noEmit: true,
+8 -7
View File
@@ -13,13 +13,14 @@ packages/react-native/**/*.h
[exclude]
; Collection of paths we're sure aren't part of the public API. These
; will always override anything in [include].
packages/react-native/ReactCommon/jsinspector-modern/tests/*
packages/react-native/ReactCommon/react/Nativemodule/samples/*
packages/react-native/ReactCommon/react/test_utils/*
packages/react-native/ReactCommon/react/utils/*
packages/react-native/ReactCommon/react/featureflags/*
packages/react-native/Libraries/WebSocket/*
packages/react-native/Libraries/Wrapper/Example/*
; Patterns end in *.h to only match exact files and never directories
packages/react-native/ReactCommon/jsinspector-modern/tests/*.h
packages/react-native/ReactCommon/react/Nativemodule/samples/*.h
packages/react-native/ReactCommon/react/test_utils/*.h
packages/react-native/ReactCommon/react/utils/*.h
packages/react-native/ReactCommon/react/featureflags/*.h
packages/react-native/Libraries/WebSocket/*.h
packages/react-native/Libraries/Wrapper/Example/*.h
[settings]
output=scripts/cxx-api/ReactNativeCPP.api
+8 -12
View File
@@ -10,9 +10,9 @@
const {execSync} = require('child_process');
const fs = require('fs');
const glob = require('glob');
const ini = require('ini');
const path = require('path');
const {globSync} = require('tinyglobby');
const {styleText} = require('util');
const CONFIG_PATH = path.join(__dirname, './public-api.conf');
@@ -179,17 +179,13 @@ function main() {
let start = performance.now();
let files /*: string[]*/ = [];
for (const searchGlob of config.include) {
// glob 7 doesn't support searchGlob as a string[]
files = files.concat(
glob.sync(searchGlob, {
ignore: config.exclude,
root: GLOB_PROJECT_ROOT,
}),
);
}
files = Array.from(new Set(files));
const files = globSync(config.include, {
// WARN: Unlike glob, this will exclude directories to be traversed too right now
// This means that glob patterns in here must match exact files only
ignore: config.exclude,
cwd: GLOB_PROJECT_ROOT,
expandDirectories: false,
});
// Sort the files to make the output deterministic
files.sort();
+16 -9
View File
@@ -23,11 +23,11 @@ const {
// $FlowFixMe[cannot-resolve-module]
} = require('@microsoft/api-extractor');
const {promises: fs} = require('fs');
const glob = require('glob');
const {diff} = require('jest-diff');
const path = require('path');
const prettier = require('prettier');
const osTempDir = require('temp-dir');
const {globSync} = require('tinyglobby');
const {styleText} = require('util');
const inputFilesPostTransforms: $ReadOnlyArray<PluginObj<mixed>> = [
@@ -163,12 +163,11 @@ async function validateSnapshots(
}
async function findPackagesWithTypedef() {
const packagesWithGeneratedTypes = glob
.sync(`${PACKAGES_DIR}/**/types_generated`, {nodir: false})
.map(typesPath =>
path.relative(PACKAGES_DIR, typesPath).split('/').slice(0, -1).join('/'),
);
const packagesWithGeneratedTypes = globSync('**/types_generated', {
cwd: PACKAGES_DIR,
onlyDirectories: true,
expandDirectories: false,
}).map(typesPath => path.dirname(typesPath));
const packagesWithNames = await Promise.all(
packagesWithGeneratedTypes.map(async pkg => {
const packageJsonContent = await fs.readFile(
@@ -202,7 +201,11 @@ async function preparePackagesInTempDir(
}),
);
const typeDefs = glob.sync(`${tempDirectory}/**/*.d.ts`);
const typeDefs = globSync('**/*.d.ts', {
cwd: tempDirectory,
onlyFiles: true,
absolute: true,
});
await Promise.all(
typeDefs.map(async file => {
const source = await fs.readFile(file, 'utf-8');
@@ -223,7 +226,11 @@ async function rewriteLocalImports(
tempDirectory: string,
packages: $ReadOnlyArray<{directory: string, name: string}>,
) {
const definitions = glob.sync(`${tempDirectory}/**/*.d.ts`);
const definitions = globSync('**/*.d.ts', {
cwd: tempDirectory,
onlyFiles: true,
absolute: true,
});
await Promise.all(
definitions.map(async file => {
@@ -17,8 +17,8 @@ const {
} = require('./constants');
const {execSync} = require('child_process');
const fs = require('fs');
const glob = require('glob');
const path = require('path');
const {globSync} = require('tinyglobby');
const util = require('util');
/*::
@@ -142,7 +142,11 @@ async function createBuildStructure(
// Now let's use glob to get the final list of files
const sources = dependency.files.sources;
sources.forEach(source => {
const sourceFiles = glob.sync(source, {cwd: sourceFolder});
const sourceFiles = globSync(source, {
cwd: sourceFolder,
expandDirectories: false,
onlyFiles: false,
});
sourceFiles.forEach(sourceFile => {
const sourcePath = path.join(sourceFolder, sourceFile);
const targetPath = path.join(targetFolder, sourceFile);
@@ -180,7 +184,11 @@ async function createHeaderStructure(
// Now let's use glob to get the final list of files
const headers = dependency.files.headers;
headers.forEach(source => {
const sourceFiles = glob.sync(source, {cwd: sourceFolder});
const sourceFiles = globSync(source, {
cwd: sourceFolder,
expandDirectories: false,
onlyFiles: false,
});
sourceFiles.forEach(sourceFile => {
// get source path
const sourcePath = path.join(sourceFolder, sourceFile);
@@ -234,7 +242,11 @@ async function copyResources(
// Copy all resources
resources.forEach(source => {
const sourceFiles = glob.sync(source, {cwd: rootFolder});
const sourceFiles = globSync(source, {
cwd: rootFolder,
expandDirectories: false,
onlyFiles: false,
});
sourceFiles.forEach(sourceFile => {
const sourcePath = path.resolve(rootFolder, sourceFile);
const targetPath = path.join(targetFolder, path.basename(sourceFile));
+7 -10
View File
@@ -10,8 +10,8 @@
const {REACT_NATIVE_PACKAGE_DIR, REPO_ROOT} = require('./consts');
const {promises: fs} = require('fs');
const glob = require('glob');
const path = require('path');
const {globSync} = require('tinyglobby');
const WORKSPACES_CONFIG = '{packages,private}/*';
@@ -71,15 +71,12 @@ async function getPackages(
} = filter;
const packagesEntries = await Promise.all(
glob
.sync(`${WORKSPACES_CONFIG}/package.json`, {
cwd: REPO_ROOT,
absolute: true,
ignore: includeReactNative
? []
: ['packages/react-native/package.json'],
})
.map(parsePackageInfo),
globSync(`${WORKSPACES_CONFIG}/package.json`, {
cwd: REPO_ROOT,
absolute: true,
ignore: includeReactNative ? [] : ['packages/react-native/package.json'],
expandDirectories: false,
}).map(parsePackageInfo),
);
return Object.fromEntries(
+19 -1
View File
@@ -4652,6 +4652,11 @@ fd-slicer@~1.1.0:
dependencies:
pend "~1.2.0"
fdir@^6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
figures@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
@@ -4982,7 +4987,7 @@ glob-to-regex.js@^1.0.1:
resolved "https://registry.yarnpkg.com/glob-to-regex.js/-/glob-to-regex.js-1.0.1.tgz#f71cc9cb8441471a9318626160bc8a35e1306b21"
integrity sha512-CG/iEvgQqfzoVsMUbxSJcwbG2JwyZ3naEqPkeltwl0BSS8Bp83k3xlGms+0QdWFUAwV+uvo80wNswKF6FWEkKg==
glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
glob@^7.0.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -7724,6 +7729,11 @@ picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
picomatch@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
pify@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -8865,6 +8875,14 @@ tinybench@^4.1.0:
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-4.1.0.tgz#090118e51159eb105f3cc2ef5cf371f3f8adc7bf"
integrity sha512-8JZoQRJgWWEIIeAmpiNmMHIREmUY3oGX8GRmlmNapLr/qtgMe+K76vM2qabh85hNScnE2lqTVTajVETjuD9Ixg==
tinyglobby@^0.2.15:
version "0.2.15"
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2"
integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==
dependencies:
fdir "^6.5.0"
picomatch "^4.0.3"
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"