diff --git a/.eslintrc.js b/.eslintrc.js index a14016bf0e..54373f38e6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -62,8 +62,8 @@ module.exports = { }, { files: [ - // Uses FileMapPlugin as input type - 'packages/metro-file-map/types/index.d.ts', + // flow-api-translator doesn't support translating `empty` + 'packages/metro-file-map/types/flow-types.d.ts', ], rules: { '@typescript-eslint/no-explicit-any': 'off', diff --git a/.flowconfig b/.flowconfig index e4a52aa8ae..cc67a8382a 100644 --- a/.flowconfig +++ b/.flowconfig @@ -38,4 +38,4 @@ untyped-import untyped-type-import [version] -^0.302.0 +^0.309.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eaf3f2fe9e..de118b5c1f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,6 +26,18 @@ jobs: node-version: ${{ inputs.node-version }} no-lockfile: ${{ inputs.no-lockfile }} - name: Run Jest Tests - run: yarn jest --ci --maxWorkers 4 --reporters=default --reporters=jest-junit --rootdir='./' env: NIGHTLY_TESTS_NO_LOCKFILE: ${{ inputs.no-lockfile }} + shell: bash + run: | + max_attempts=3 + attempt=1 + until yarn jest --ci --maxWorkers 4 --reporters=default --reporters=jest-junit --rootdir='./'; do + if [ $attempt -ge $max_attempts ]; then + echo "Tests failed after $max_attempts attempts" + exit 1 + fi + echo "Attempt $attempt failed, retrying..." + attempt=$((attempt + 1)) + sleep 5 + done diff --git a/flow-typed/environment/node.js b/flow-typed/environment/node.js index 8bd1534e17..1a99ec0dcb 100644 --- a/flow-typed/environment/node.js +++ b/flow-typed/environment/node.js @@ -21,7 +21,7 @@ interface ErrnoError extends Error { syscall?: string; } -type Node$Conditional = T extends true +type Node$Conditional = T extends true ? IfTrue : T extends false ? IfFalse @@ -282,7 +282,7 @@ type child_process$execFileOpts = Readonly<{ signal?: AbortSignal, }>; -type child_process$execFileCallback = +type child_process$execFileCallback = child_process$execCallback; type child_process$execFileSyncOpts = Readonly<{ @@ -414,7 +414,7 @@ type child_process$spawnOpts = Readonly<{ serialization?: 'json' | 'advanced', }>; -type child_process$spawnSyncRet = Readonly<{ +type child_process$spawnSyncRet = Readonly<{ pid: number, output: Array, // TODO: subprocess.stdout may be null in case of error @@ -454,9 +454,9 @@ type child_process$Serializable = type child_process$SendHandle = net$Server | net$Socket; declare class child_process$ChildProcessTyped< - TStdin: stream$Writable | null, - TStdout: stream$Readable | null, - TStderr: stream$Readable | null, + TStdin extends stream$Writable | null, + TStdout extends stream$Readable | null, + TStderr extends stream$Readable | null, > extends events$EventEmitter { +stdin: TStdin; @@ -515,7 +515,7 @@ declare module 'child_process' { stream$Readable, >; - type StringOrBuffer = + type StringOrBuffer = Opts extends Readonly<{encoding: infer E, ...}> ? E extends buffer$NonBufferEncoding ? string @@ -524,11 +524,15 @@ declare module 'child_process' { : string | Buffer : Default; - type StreamForChannel = Channel extends 0 + type StreamForChannel = Channel extends 0 ? stream$Writable : stream$Readable; - type MaybeStream = + type MaybeStream< + Opts, + FD extends 0 | 1 | 2, + PipeByDefault extends true | false = true, + > = Opts extends Readonly<{stdio: infer E, ...}> ? E extends child_process$StdioPipe ? StreamForChannel @@ -552,7 +556,7 @@ declare module 'child_process' { stream$Readable, >; - declare function exec( + declare function exec( command: string, options: Opts, callback?: child_process$execCallback>, @@ -562,11 +566,11 @@ declare module 'child_process' { stream$Readable, >; - declare function execSync( + declare function execSync( command: string, ): Buffer; - declare function execSync( + declare function execSync( command: string, options: Opts, ): StringOrBuffer; @@ -583,7 +587,7 @@ declare module 'child_process' { stream$Readable, >; - declare function execFile( + declare function execFile( file: string, args: ReadonlyArray, options: Opts, @@ -594,7 +598,7 @@ declare module 'child_process' { stream$Readable, >; - declare function execFile( + declare function execFile( file: string, options: Opts, callback?: child_process$execFileCallback>, @@ -609,13 +613,13 @@ declare module 'child_process' { args?: ReadonlyArray, ): Buffer; - declare function execFileSync( + declare function execFileSync( command: string, args: ReadonlyArray, options: Opts, ): StringOrBuffer; - declare function execFileSync( + declare function execFileSync( command: string, options: Opts, ): StringOrBuffer; @@ -625,7 +629,7 @@ declare module 'child_process' { args?: ReadonlyArray, ): child_process$ChildProcessTyped; - declare function fork( + declare function fork( modulePath: string, args: ReadonlyArray, options: Opts, @@ -635,7 +639,7 @@ declare module 'child_process' { MaybeStream, >; - declare function fork( + declare function fork( modulePath: string, options: Opts, ): child_process$ChildProcessTyped< @@ -653,7 +657,7 @@ declare module 'child_process' { stream$Readable, >; - declare function spawn( + declare function spawn( command: string, args: ReadonlyArray, options: Opts, @@ -663,7 +667,7 @@ declare module 'child_process' { MaybeStream, >; - declare function spawn( + declare function spawn( command: string, options: Opts, ): child_process$ChildProcessTyped< @@ -677,13 +681,13 @@ declare module 'child_process' { args?: ReadonlyArray, ): child_process$spawnSyncRet; - declare function spawnSync( + declare function spawnSync( command: string, args: ReadonlyArray, options: Opts, ): child_process$spawnSyncRet>; - declare function spawnSync( + declare function spawnSync( command: string, options: Opts, ): child_process$spawnSyncRet>; @@ -2112,7 +2116,7 @@ declare module 'fs' { }>, ): void; - declare type GlobOptions = Readonly<{ + declare type GlobOptions = Readonly<{ /** * Current working directory. * @default process.cwd() @@ -2154,7 +2158,7 @@ declare module 'fs' { callback: (err: ?ErrnoError, matches: Array) => void, ): void; - declare function glob( + declare function glob( pattern: string | ReadonlyArray, options: GlobOptions, callback: ( @@ -2172,7 +2176,7 @@ declare module 'fs' { * @since v22.0.0 * @returns paths of files that match the pattern. */ - declare function globSync( + declare function globSync( pattern: string | ReadonlyArray, options?: GlobOptions, ): Node$Conditional, Array>; @@ -2289,7 +2293,7 @@ declare module 'fs' { ): WriteStream; datasync(): Promise; fd: number; - read( + read( buffer: T, offset: number, length: number, @@ -2314,7 +2318,7 @@ declare module 'fs' { highWaterMark?: number, }>, ): readline$Interface; - readv | Array | Array>( + readv | Array | Array>( buffers: T, position?: number | null, ): Promise<{buffers: T, bytesRead: number}>; @@ -2340,7 +2344,7 @@ declare module 'fs' { }>, ): Promise; writeFile: AppendOrWriteToFileHandle; - writev | Array | Array>( + writev | Array | Array>( buffers: T, position?: number | null, ): Promise<{buffers: T, bytesWritten: number}>; @@ -2386,7 +2390,7 @@ declare module 'fs' { atime: number | string | Date, mtime: number | string | Date, ): Promise, - glob( + glob( pattern: string | ReadonlyArray, options?: GlobOptions, ): Node$Conditional< @@ -2422,7 +2426,7 @@ declare module 'fs' { recursive?: boolean, }>, ): Promise, - read( + read( filehandle: FileHandle, buffer: T, offset: number, @@ -2503,7 +2507,7 @@ declare module 'fs' { overflow?: 'ignore' | 'throw', }>, ): AsyncIterator<{eventType: string, filename: ?string}>, - write( + write( filehandle: FileHandle, buffer: T, offset: number, @@ -3314,7 +3318,7 @@ declare module 'perf_hooks' { now(): number; setResourceTimingBufferSize(maxSize: number): void; +timeOrigin: number; - timerify, TReturn>( + timerify, TReturn>( fn: (...TArgs) => TReturn, options?: Readonly<{histogram?: RecordableHistogram}>, ): (...TArgs) => TReturn; @@ -3606,14 +3610,22 @@ declare class stream$Readable extends stream$Stream { static toWeb(streamReadable: stream$Readable): ReadableStream; constructor(options?: readableStreamOptions): void; + closed: boolean; destroy(error?: Error): this; + destroyed: boolean; + errored: ?Error; isPaused(): boolean; pause(): this; - pipe(dest: T, options?: {end?: boolean, ...}): T; + pipe(dest: T, options?: {end?: boolean, ...}): T; read(size?: number): ?(string | Buffer); readable: boolean; + readableAborted: boolean; + readableDidRead: boolean; + readableEnded: boolean; + readableFlowing: boolean | null; readableHighWaterMark: number; readableLength: number; + readableObjectMode: boolean; resume(): this; setEncoding(encoding: string): this; unpipe(dest?: stream$Writable): this; @@ -3658,8 +3670,10 @@ declare class stream$Writable extends stream$Stream { static toWeb(streamWritable: stream$Writable): WritableStream; constructor(options?: writableStreamOptions): void; + closed: boolean; cork(): void; destroy(error?: Error): this; + destroyed: boolean; end(callback?: () => void): this; end(chunk?: string | Buffer | Uint8Array, callback?: () => void): this; end( @@ -3667,11 +3681,17 @@ declare class stream$Writable extends stream$Stream { encoding?: string, callback?: () => void, ): this; + errored: ?Error; setDefaultEncoding(encoding: string): this; uncork(): void; writable: boolean; + writableAborted: boolean; + writableEnded: boolean; + writableFinished: boolean; writableHighWaterMark: number; writableLength: number; + writableNeedDrain: boolean; + writableObjectMode: boolean; write( chunk: string | Buffer | Uint8Array, callback?: (error?: Error) => void, @@ -3773,25 +3793,25 @@ declare module 'stream' { }, callback: (error?: Error) => void, ): () => void; - declare function pipeline( + declare function pipeline( s1: stream$Readable, last: T, cb: (error?: Error) => void, ): T; - declare function pipeline( + declare function pipeline( s1: stream$Readable, s2: stream$Duplex, last: T, cb: (error?: Error) => void, ): T; - declare function pipeline( + declare function pipeline( s1: stream$Readable, s2: stream$Duplex, s3: stream$Duplex, last: T, cb: (error?: Error) => void, ): T; - declare function pipeline( + declare function pipeline( s1: stream$Readable, s2: stream$Duplex, s3: stream$Duplex, @@ -3799,7 +3819,7 @@ declare module 'stream' { last: T, cb: (error?: Error) => void, ): T; - declare function pipeline( + declare function pipeline( s1: stream$Readable, s2: stream$Duplex, s3: stream$Duplex, @@ -3808,7 +3828,7 @@ declare module 'stream' { last: T, cb: (error?: Error) => void, ): T; - declare function pipeline( + declare function pipeline( s1: stream$Readable, s2: stream$Duplex, s3: stream$Duplex, @@ -4191,19 +4211,19 @@ declare module 'timers' { // [key: $SymbolDispose]: () => void; } - declare export function setTimeout>( + declare export function setTimeout>( callback: (...args: TArgs) => unknown, delay?: number, ...args: TArgs ): Timeout; - declare export function setInterval>( + declare export function setInterval>( callback: (...args: TArgs) => unknown, delay?: number, ...args: TArgs ): Timeout; - declare export function setImmediate>( + declare export function setImmediate>( callback: (...args: TArgs) => unknown, ...args: TArgs ): Immediate; @@ -4500,7 +4520,7 @@ declare module 'util' { declare function stripVTControlCharacters(str: string): string; declare function parseArgs< - TOptions: {+[string]: util$ParseArgsOption} = {}, + TOptions extends {+[string]: util$ParseArgsOption} = {}, >(config: { args?: Array, options?: TOptions, @@ -4514,7 +4534,7 @@ declare module 'util' { }; declare function parseArgs< - TOptions: {[string]: util$ParseArgsOption} = {}, + TOptions extends {[string]: util$ParseArgsOption} = {}, >(config: { args?: Array, options?: TOptions, @@ -5658,7 +5678,7 @@ declare class Process extends events$EventEmitter { setegid?: (id: number | string) => void; seteuid?: (id: number | string) => void; setgid?: (id: number | string) => void; - setgroups?: (groups: Array) => void; + setgroups?: (groups: Array) => void; setuid?: (id: number | string) => void; stderr: stream$Writable | tty$WriteStream; stdin: stream$Readable | tty$ReadStream; diff --git a/flow-typed/environment/utility-types.js b/flow-typed/environment/utility-types.js index 1a0fa3988e..db973a2e19 100644 --- a/flow-typed/environment/utility-types.js +++ b/flow-typed/environment/utility-types.js @@ -21,6 +21,6 @@ * can be safely spread, forcing handling of potentially present but undefined * props. */ -declare type SafeOptionalProps = { +declare type SafeOptionalProps = { [K in keyof T]: T[K] extends void ? void | T[K] : T[K], }; diff --git a/flow-typed/jest-worker.js b/flow-typed/jest-worker.js index 8abf619bd8..73f2aba33a 100644 --- a/flow-typed/jest-worker.js +++ b/flow-typed/jest-worker.js @@ -237,7 +237,7 @@ declare module 'jest-worker' { dequeue(workerId: number): QueueChildMessage | null; } - declare export type FarmOptions> = + declare export type FarmOptions> = Readonly<{ computeWorkerKey?: ( method: string, @@ -259,7 +259,7 @@ declare module 'jest-worker' { workerSchedulingPolicy?: 'round-robin' | 'in-order', }>; - declare export type IJestWorker = Readonly<{ + declare export type IJestWorker = Readonly<{ // dynamically exposed methods from the worker // $FlowFixMe[incompatible-exact] ...TExposed, @@ -270,10 +270,10 @@ declare module 'jest-worker' { }>; declare export class Worker< - TExposed: Readonly<{ + TExposed extends Readonly<{ [string]: (...Array<$FlowFixMe>) => Promise<$FlowFixMe>, }> = {}, - TSetupArgs: ReadonlyArray = ReadonlyArray, + TSetupArgs extends ReadonlyArray = ReadonlyArray, > { constructor( workerPath: string, diff --git a/flow-typed/jest.js b/flow-typed/jest.js index 24639c0388..eb6b3294da 100644 --- a/flow-typed/jest.js +++ b/flow-typed/jest.js @@ -15,7 +15,7 @@ // MODIFIED: Added ESLint suppression comment - no-unused-vars doesn't understand declaration files /* eslint-disable no-unused-vars */ -type JestMockFn, TReturn> = { +type JestMockFn, TReturn> = { (...args: TArguments): TReturn, /** * An object for introspecting mock calls @@ -873,7 +873,10 @@ type JestObjectType = { * implementation. */ // MODIFIED: Added defaults to type arguments. - fn = ReadonlyArray, TReturn = any>( + fn< + TArguments extends ReadonlyArray = ReadonlyArray, + TReturn = any, + >( implementation?: (...args: TArguments) => TReturn, ): JestMockFn, /** diff --git a/flow-typed/npm/babel-traverse_v7.x.x.js b/flow-typed/npm/babel-traverse_v7.x.x.js index 3abf30bc3c..e28bb4921f 100644 --- a/flow-typed/npm/babel-traverse_v7.x.x.js +++ b/flow-typed/npm/babel-traverse_v7.x.x.js @@ -25,7 +25,7 @@ declare module '@babel/traverse' { getCode(): ?string; getScope(): ?Scope; addHelper(name: string): {}; - buildError( + buildError( node: BabelNode, msg: string, Error: Class, @@ -37,7 +37,7 @@ declare module '@babel/traverse' { getCode(): ?string; getScope(): ?Scope; addHelper(name: string): {}; - buildError( + buildError( node: BabelNode, msg: string, Error: Class, @@ -294,7 +294,7 @@ declare module '@babel/traverse' { declare type Opts = {...}; - declare export class NodePath<+TNode: BabelNode = BabelNode> { + declare export class NodePath<+TNode extends BabelNode = BabelNode> { parent: BabelNode; hub: HubInterface; contexts: Array; @@ -340,7 +340,7 @@ declare module '@babel/traverse' { setData(key: string, val: TVal): TVal; getData(key: string, def?: TVal): TVal; - buildCodeFrameError( + buildCodeFrameError( msg: string, Error?: Class, ): TError; @@ -724,7 +724,7 @@ declare module '@babel/traverse' { getAllPrevSiblings(): Array>; - get( + get( key: TKey, context?: boolean | TraversalContext, ): TNode[TKey] extends BabelNode ? NodePath<> : Array>; @@ -1432,17 +1432,20 @@ declare module '@babel/traverse' { // END GENERATED NODE PATH METHODS } - declare export type VisitNodeFunction<-TNode: BabelNode, TState> = ( + declare export type VisitNodeFunction<-TNode extends BabelNode, TState> = ( path: NodePath, state: TState, ) => void; - declare export type VisitNodeObject<-TNode: BabelNode, TState> = Partial<{ + declare export type VisitNodeObject< + -TNode extends BabelNode, + TState, + > = Partial<{ enter(path: NodePath, state: TState): void, exit(path: NodePath, state: TState): void, }>; - declare export type VisitNode<-TNode: BabelNode, TState> = + declare export type VisitNode<-TNode extends BabelNode, TState> = | VisitNodeFunction | VisitNodeObject; diff --git a/flow-typed/npm/babel_v7.x.x.js b/flow-typed/npm/babel_v7.x.x.js index f9d293c16c..4da72d6b7d 100644 --- a/flow-typed/npm/babel_v7.x.x.js +++ b/flow-typed/npm/babel_v7.x.x.js @@ -353,7 +353,7 @@ declare module '@babel/core' { raw: BabelNodeArrayExpression, ): BabelNodeIdentifier; - buildCodeFrameError( + buildCodeFrameError( node: BabelNode, msg: string, Class, @@ -1083,6 +1083,8 @@ declare module '@babel/core' { +babelrc: string | void; +babelignore: string | void; +config: string | void; + +files: ReadonlySet; + +fileHandling: 'ignored' | 'transpile' | 'unsupported'; constructor(options: ValidatedOptions): PartialConfig; diff --git a/flow-typed/prettier.js b/flow-typed/prettier.js index 4c0ab7cccf..13f0b5eb34 100644 --- a/flow-typed/prettier.js +++ b/flow-typed/prettier.js @@ -405,7 +405,7 @@ declare module 'prettier' { declare export type SupportOptionType = 'int' | 'boolean' | 'choice' | 'path'; - declare export type BaseSupportOption = { + declare export type BaseSupportOption = { +name?: string | void, since: string, /** diff --git a/package.json b/package.json index 3d1d4805db..67a9846271 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "@typescript-eslint/parser": "^8.36.0", "acorn": "^8.7.1", "babel-jest": "^29.7.0", - "babel-plugin-syntax-hermes-parser": "0.33.3", + "babel-plugin-syntax-hermes-parser": "0.35.0", "babel-plugin-transform-flow-enums": "^0.0.2", "chalk": "^4.0.0", "debug": "^4.4.0", @@ -29,9 +29,9 @@ "eslint-plugin-lint": "^1.0.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-relay": "^1.8.3", - "flow-api-translator": "0.33.3", - "flow-bin": "^0.302.0", - "hermes-eslint": "0.33.3", + "flow-api-translator": "0.35.0", + "flow-bin": "^0.309.0", + "hermes-eslint": "0.35.0", "invariant": "^2.2.4", "istanbul-api": "3.0.0", "istanbul-lib-coverage": "3.0.0", @@ -44,6 +44,7 @@ "prettier": "3.6.2", "prettier-plugin-hermes-parser": "0.34.1", "progress": "^2.0.0", + "signedsource": "^2.0.0", "tinyglobby": "^0.2.15", "typescript": "5.8.3" }, diff --git a/packages/buck-worker-tool/package.json b/packages/buck-worker-tool/package.json index c3ccd30b61..aa73d201cb 100644 --- a/packages/buck-worker-tool/package.json +++ b/packages/buck-worker-tool/package.json @@ -1,6 +1,6 @@ { "name": "buck-worker-tool", - "version": "0.83.5", + "version": "0.83.6", "description": "Implementation of the Buck worker protocol for Node.js.", "license": "MIT", "repository": { diff --git a/packages/buck-worker-tool/src/worker-tool.js b/packages/buck-worker-tool/src/worker-tool.js index 8432acc25b..f15c40e8ba 100644 --- a/packages/buck-worker-tool/src/worker-tool.js +++ b/packages/buck-worker-tool/src/worker-tool.js @@ -25,7 +25,7 @@ export type Command = ( ) => Promise | void; export type Commands = {[key: string]: Command, ...}; -type Message = Data & { +type Message = Data & { id: number, type: Type, ... diff --git a/packages/buck-worker-tool/types/CommandFailedError.d.ts b/packages/buck-worker-tool/types/CommandFailedError.d.ts index 94e98be980..ca018bc82e 100644 --- a/packages/buck-worker-tool/types/CommandFailedError.d.ts +++ b/packages/buck-worker-tool/types/CommandFailedError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/buck-worker-tool/src/CommandFailedError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/buck-worker-tool/types/profiling.d.ts b/packages/buck-worker-tool/types/profiling.d.ts index 7eb90d32d0..b2d545e7f7 100644 --- a/packages/buck-worker-tool/types/profiling.d.ts +++ b/packages/buck-worker-tool/types/profiling.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/buck-worker-tool/src/profiling.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export declare function startProfiling(): void; diff --git a/packages/buck-worker-tool/types/worker-tool.d.ts b/packages/buck-worker-tool/types/worker-tool.d.ts index fdfe065d79..d2b23f96f7 100644 --- a/packages/buck-worker-tool/types/worker-tool.d.ts +++ b/packages/buck-worker-tool/types/worker-tool.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<1155614c83550da2ee3328fd45cb59b5>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/buck-worker-tool/src/worker-tool.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Duplex} from 'stream'; diff --git a/packages/metro-babel-register/package.json b/packages/metro-babel-register/package.json index 1616d110d7..9e46f73a80 100644 --- a/packages/metro-babel-register/package.json +++ b/packages/metro-babel-register/package.json @@ -1,6 +1,6 @@ { "name": "metro-babel-register", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 babel/register configuration for Metro.", "main": "src/babel-register.js", "exports": { @@ -21,7 +21,7 @@ "@babel/preset-typescript": "^7.24.7", "@babel/register": "^7.24.6", "babel-plugin-replace-ts-export-assignment": "^0.0.2", - "babel-plugin-syntax-hermes-parser": "0.33.3", + "babel-plugin-syntax-hermes-parser": "0.35.0", "babel-plugin-transform-flow-enums": "^0.0.2", "escape-string-regexp": "^1.0.5", "flow-enums-runtime": "^0.0.6" diff --git a/packages/metro-babel-transformer/package.json b/packages/metro-babel-transformer/package.json index e415b99a49..d82c99482c 100644 --- a/packages/metro-babel-transformer/package.json +++ b/packages/metro-babel-transformer/package.json @@ -1,6 +1,6 @@ { "name": "metro-babel-transformer", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Base Babel transformer for Metro.", "main": "src/index.js", "exports": { @@ -25,7 +25,8 @@ "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", - "hermes-parser": "0.33.3", + "hermes-parser": "0.35.0", + "metro-cache-key": "0.83.6", "nullthrows": "^1.1.1" }, "engines": { diff --git a/packages/metro-babel-transformer/src/index.js b/packages/metro-babel-transformer/src/index.js index 505d74ef0f..0e0a292980 100644 --- a/packages/metro-babel-transformer/src/index.js +++ b/packages/metro-babel-transformer/src/index.js @@ -12,7 +12,12 @@ import type {BabelFileMetadata} from '@babel/core'; import type {File as BabelNodeFile} from '@babel/types'; -import {parseSync, transformFromAstSync} from '@babel/core'; +import { + loadPartialConfigSync, + parseSync, + transformFromAstSync, +} from '@babel/core'; +import {getCacheKey as getFileCacheKey} from 'metro-cache-key'; import nullthrows from 'nullthrows'; type BabelTransformOptions = NonNullable< @@ -69,6 +74,11 @@ export type MetroBabelFileMetadata = { ... }; +export type BabelTransformerCacheKeyOptions = Readonly<{ + projectRoot?: string, + enableBabelRCLookup?: boolean, +}>; + export type BabelTransformer = Readonly<{ transform: BabelTransformerArgs => Readonly<{ ast: BabelNodeFile, @@ -79,7 +89,7 @@ export type BabelTransformer = Readonly<{ metadata?: MetroBabelFileMetadata, ... }>, - getCacheKey?: () => string, + getCacheKey?: (options?: BabelTransformerCacheKeyOptions) => string, }>; function transform({ @@ -143,12 +153,41 @@ function transform({ } } +/** + * Generates a cache key component based on the user's Babel configuration files. + * This uses Babel's loadPartialConfigSync to resolve which config files apply + * to a given file, and includes their contents in the cache key so that changes + * to babel.config.js or .babelrc will invalidate the transform cache. + * + * This is called once by the main thread (not on worker instances). + */ +function getCacheKey(options?: BabelTransformerCacheKeyOptions): string { + if (options == null) { + return ''; + } + // Load the partial babel config to get the resolved config file paths + const partialConfig = loadPartialConfigSync({ + cwd: options.projectRoot, + root: options.projectRoot, + babelrc: options.enableBabelRCLookup ?? true, + }); + + const files = partialConfig?.files; + + if (files == null || files.size === 0) { + return ''; + } + + // Hash the contents of all config files + return getFileCacheKey([...files].sort()); +} + // Type check exports /*:: -({transform}) as BabelTransformer; +({transform, getCacheKey}) as BabelTransformer; */ -export {transform}; +export {transform, getCacheKey}; /** * Backwards-compatibility with CommonJS consumers using interopRequireDefault. @@ -156,4 +195,4 @@ export {transform}; * * @deprecated Default import from 'metro-babel-transformer' is deprecated, use named exports. */ -export default {transform}; +export default {transform, getCacheKey}; diff --git a/packages/metro-babel-transformer/types/index.d.ts b/packages/metro-babel-transformer/types/index.d.ts index 28852c64da..b77f422ac3 100644 --- a/packages/metro-babel-transformer/types/index.d.ts +++ b/packages/metro-babel-transformer/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-babel-transformer/src/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {BabelFileMetadata} from '@babel/core'; @@ -73,25 +80,41 @@ export type MetroBabelFileMetadata = Omit< | BabelFileImportLocsMetadata; }; }; +export type BabelTransformerCacheKeyOptions = Readonly<{ + projectRoot?: string; + enableBabelRCLookup?: boolean; +}>; export type BabelTransformer = Readonly<{ transform: ($$PARAM_0$$: BabelTransformerArgs) => Readonly<{ ast: BabelNodeFile; functionMap?: BabelFileFunctionMapMetadata; metadata?: MetroBabelFileMetadata; }>; - getCacheKey?: () => string; + getCacheKey?: (options?: BabelTransformerCacheKeyOptions) => string; }>; declare function transform( $$PARAM_0$$: BabelTransformerArgs, ): ReturnType; -export {transform}; +/** + * Generates a cache key component based on the user's Babel configuration files. + * This uses Babel's loadPartialConfigSync to resolve which config files apply + * to a given file, and includes their contents in the cache key so that changes + * to babel.config.js or .babelrc will invalidate the transform cache. + * + * This is called once by the main thread (not on worker instances). + */ +declare function getCacheKey(options?: BabelTransformerCacheKeyOptions): string; +export {transform, getCacheKey}; /** * Backwards-compatibility with CommonJS consumers using interopRequireDefault. * Do not add to this list. * * @deprecated Default import from 'metro-babel-transformer' is deprecated, use named exports. */ -declare const $$EXPORT_DEFAULT_DECLARATION$$: {transform: typeof transform}; +declare const $$EXPORT_DEFAULT_DECLARATION$$: { + transform: typeof transform; + getCacheKey: typeof getCacheKey; +}; declare type $$EXPORT_DEFAULT_DECLARATION$$ = typeof $$EXPORT_DEFAULT_DECLARATION$$; export default $$EXPORT_DEFAULT_DECLARATION$$; diff --git a/packages/metro-cache-key/package.json b/packages/metro-cache-key/package.json index 8c6b97036b..fee2eb1f9f 100644 --- a/packages/metro-cache-key/package.json +++ b/packages/metro-cache-key/package.json @@ -1,6 +1,6 @@ { "name": "metro-cache-key", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Cache key utility.", "main": "src/index.js", "exports": { diff --git a/packages/metro-cache-key/src/index.js b/packages/metro-cache-key/src/index.js index e6329e74b5..6894391820 100644 --- a/packages/metro-cache-key/src/index.js +++ b/packages/metro-cache-key/src/index.js @@ -12,7 +12,7 @@ import crypto from 'crypto'; import fs from 'fs'; -export function getCacheKey(files: Array): string { +export function getCacheKey(files: ReadonlyArray): string { return files .reduce( (hash, file) => hash.update('\0', 'utf8').update(fs.readFileSync(file)), diff --git a/packages/metro-cache-key/types/index.d.ts b/packages/metro-cache-key/types/index.d.ts index 5e0cec574e..d84a09b4a3 100644 --- a/packages/metro-cache-key/types/index.d.ts +++ b/packages/metro-cache-key/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<48a23e4247062bad5de7d29f42741be4>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache-key/src/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ -export declare function getCacheKey(files: Array): string; +export declare function getCacheKey(files: ReadonlyArray): string; diff --git a/packages/metro-cache/package.json b/packages/metro-cache/package.json index d0a1ab2082..bd152a2e08 100644 --- a/packages/metro-cache/package.json +++ b/packages/metro-cache/package.json @@ -1,6 +1,6 @@ { "name": "metro-cache", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Cache layers for Metro.", "main": "src/index.js", "exports": { @@ -21,7 +21,7 @@ "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", - "metro-core": "0.83.5" + "metro-core": "0.83.6" }, "devDependencies": { "memfs": "^4.38.2" diff --git a/packages/metro-cache/types/Cache.d.ts b/packages/metro-cache/types/Cache.d.ts index f95aa554b3..318ba7c3f8 100644 --- a/packages/metro-cache/types/Cache.d.ts +++ b/packages/metro-cache/types/Cache.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<1fe6ca4aa9f1f410281edf1b8f9adea2>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache/src/Cache.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {CacheStore} from './types'; diff --git a/packages/metro-cache/types/index.d.ts b/packages/metro-cache/types/index.d.ts index f676d9adb4..3be3d5feb4 100644 --- a/packages/metro-cache/types/index.d.ts +++ b/packages/metro-cache/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<2bb72546f80164eb8c0068f9de3a1487>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache/src/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import Cache from './Cache'; diff --git a/packages/metro-cache/types/stableHash.d.ts b/packages/metro-cache/types/stableHash.d.ts index 6e9a72292f..870f430613 100644 --- a/packages/metro-cache/types/stableHash.d.ts +++ b/packages/metro-cache/types/stableHash.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<36e0de65be0930a61b8ff46232052ea7>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache/src/stableHash.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function stableHash(value: unknown): Buffer; diff --git a/packages/metro-cache/types/stores/AutoCleanFileStore.d.ts b/packages/metro-cache/types/stores/AutoCleanFileStore.d.ts index 22aa4ab6bd..f3cc4b3a81 100644 --- a/packages/metro-cache/types/stores/AutoCleanFileStore.d.ts +++ b/packages/metro-cache/types/stores/AutoCleanFileStore.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache/src/stores/AutoCleanFileStore.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Options} from './FileStore'; diff --git a/packages/metro-cache/types/stores/FileStore.d.ts b/packages/metro-cache/types/stores/FileStore.d.ts index 8a62ac3d7a..6b0ff799d0 100644 --- a/packages/metro-cache/types/stores/FileStore.d.ts +++ b/packages/metro-cache/types/stores/FileStore.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<7de501c6653d300c594fcf37ce3b56f4>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache/src/stores/FileStore.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export type Options = Readonly<{root: string}>; diff --git a/packages/metro-cache/types/stores/HttpError.d.ts b/packages/metro-cache/types/stores/HttpError.d.ts index 5837c66df6..2830a1c7fa 100644 --- a/packages/metro-cache/types/stores/HttpError.d.ts +++ b/packages/metro-cache/types/stores/HttpError.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<7a0b4b83fb44651820333ade6a980ef7>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache/src/stores/HttpError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare class HttpError extends Error { diff --git a/packages/metro-cache/types/stores/HttpGetStore.d.ts b/packages/metro-cache/types/stores/HttpGetStore.d.ts index 70eaa16e79..a3ba44ae9f 100644 --- a/packages/metro-cache/types/stores/HttpGetStore.d.ts +++ b/packages/metro-cache/types/stores/HttpGetStore.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<12106d5e641e2402d71f229ec168a8ec>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache/src/stores/HttpGetStore.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Options as HttpOptions} from './HttpStore'; diff --git a/packages/metro-cache/types/stores/HttpStore.d.ts b/packages/metro-cache/types/stores/HttpStore.d.ts index bfc235d597..52d1f74966 100644 --- a/packages/metro-cache/types/stores/HttpStore.d.ts +++ b/packages/metro-cache/types/stores/HttpStore.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<05ef02cd1f35e98055791237d573dc5a>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache/src/stores/HttpStore.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import HttpError from './HttpError'; diff --git a/packages/metro-cache/types/stores/NetworkError.d.ts b/packages/metro-cache/types/stores/NetworkError.d.ts index 3984acfb7f..35044d3b22 100644 --- a/packages/metro-cache/types/stores/NetworkError.d.ts +++ b/packages/metro-cache/types/stores/NetworkError.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<9a68fe7766e376b8525c589673853e54>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache/src/stores/NetworkError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare class NetworkError extends Error { diff --git a/packages/metro-cache/types/types.d.ts b/packages/metro-cache/types/types.d.ts index e747c9fd49..25f0d27d0d 100644 --- a/packages/metro-cache/types/types.d.ts +++ b/packages/metro-cache/types/types.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<80dd2674720fe89c7a90a649a922cb1d>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-cache/src/types.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export interface CacheStore { diff --git a/packages/metro-config/package.json b/packages/metro-config/package.json index 87ab49aac9..06708e34d2 100644 --- a/packages/metro-config/package.json +++ b/packages/metro-config/package.json @@ -1,6 +1,6 @@ { "name": "metro-config", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Config parser for Metro.", "main": "src/index.js", "exports": { @@ -22,15 +22,15 @@ "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", - "metro": "0.83.5", - "metro-cache": "0.83.5", - "metro-core": "0.83.5", - "metro-runtime": "0.83.5", + "metro": "0.83.6", + "metro-cache": "0.83.6", + "metro-core": "0.83.6", + "metro-runtime": "0.83.6", "yaml": "^2.6.1" }, "devDependencies": { "@types/connect": "^3.4.35", - "metro-babel-register": "0.83.5", + "metro-babel-register": "0.83.6", "pretty-format": "^29.7.0" }, "engines": { diff --git a/packages/metro-config/src/__flowtests__/types-flowtest.js b/packages/metro-config/src/__flowtests__/types-flowtest.js index 7d95f7ba52..7a93cb0abb 100644 --- a/packages/metro-config/src/__flowtests__/types-flowtest.js +++ b/packages/metro-config/src/__flowtests__/types-flowtest.js @@ -16,7 +16,7 @@ import {mergeConfig} from '../loadConfig'; declare var config: ConfigT; declare var inputConfig: InputConfigT; -declare function isMutableArray>( +declare function isMutableArray>( arr: T, ): T extends Array ? true : false; diff --git a/packages/metro-config/src/loadConfig.js b/packages/metro-config/src/loadConfig.js index 93721d249f..6ca4020bfe 100644 --- a/packages/metro-config/src/loadConfig.js +++ b/packages/metro-config/src/loadConfig.js @@ -105,7 +105,7 @@ async function resolveConfig( return await loadConfigFile(configPath); } -function mergeConfigObjects( +function mergeConfigObjects( base: T, overrides: InputConfigT, ): T { @@ -196,7 +196,7 @@ function mergeConfigObjects( }; } -async function mergeConfigAsync( +async function mergeConfigAsync( baseConfig: Promise, ...reversedConfigs: ReadonlyArray< InputConfigT | (T => InputConfigT) | (T => Promise), @@ -223,8 +223,8 @@ async function mergeConfigAsync( * Otherwise it will return synchronously. */ function mergeConfig< - T: InputConfigT, - R: ReadonlyArray< + T extends InputConfigT, + R extends ReadonlyArray< | InputConfigT | ((baseConfig: T) => InputConfigT) | ((baseConfig: T) => Promise), diff --git a/packages/metro-config/src/types.js b/packages/metro-config/src/types.js index 79141c4427..1ebd07bc23 100644 --- a/packages/metro-config/src/types.js +++ b/packages/metro-config/src/types.js @@ -11,7 +11,7 @@ import type {HandleFunction, Server} from 'connect'; import type {CacheStore, MetroCache} from 'metro-cache'; -import type {CacheManagerFactory} from 'metro-file-map'; +import type {CacheManagerFactory, InputFileMapPlugin} from 'metro-file-map'; import type {CustomResolver} from 'metro-resolver'; import type {JsTransformerConfig} from 'metro-transform-worker'; import type { @@ -163,6 +163,7 @@ type MetalConfigT = { fileMapCacheDirectory?: string, hasteMapCacheDirectory?: string, // Deprecated, alias of fileMapCacheDirectory unstable_fileMapCacheManagerFactory?: CacheManagerFactory, + unstable_fileMapPlugins?: ReadonlyArray, maxWorkers: number, unstable_perfLoggerFactory?: ?PerfLoggerFactory, projectRoot: string, diff --git a/packages/metro-config/types/defaults/createModuleIdFactory.d.ts b/packages/metro-config/types/defaults/createModuleIdFactory.d.ts index e9570aa491..0883cf3911 100644 --- a/packages/metro-config/types/defaults/createModuleIdFactory.d.ts +++ b/packages/metro-config/types/defaults/createModuleIdFactory.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-config/src/defaults/createModuleIdFactory.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function createModuleIdFactory(): (path: string) => number; diff --git a/packages/metro-config/types/defaults/defaults.d.ts b/packages/metro-config/types/defaults/defaults.d.ts index d1f90bafb2..dc393a3ef8 100644 --- a/packages/metro-config/types/defaults/defaults.d.ts +++ b/packages/metro-config/types/defaults/defaults.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-config/src/defaults/defaults.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {RootPerfLogger} from '../types'; diff --git a/packages/metro-config/types/defaults/exclusionList.d.ts b/packages/metro-config/types/defaults/exclusionList.d.ts index 8c9a30b664..445f5472cc 100644 --- a/packages/metro-config/types/defaults/exclusionList.d.ts +++ b/packages/metro-config/types/defaults/exclusionList.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<53b0e844540afad09974c4bcc9b0668c>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-config/src/defaults/exclusionList.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function exclusionList( diff --git a/packages/metro-config/types/defaults/getMaxWorkers.d.ts b/packages/metro-config/types/defaults/getMaxWorkers.d.ts index af248faded..b76ea9a47b 100644 --- a/packages/metro-config/types/defaults/getMaxWorkers.d.ts +++ b/packages/metro-config/types/defaults/getMaxWorkers.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-config/src/defaults/getMaxWorkers.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function getMaxWorkers(workers: null | undefined | number): number; diff --git a/packages/metro-config/types/defaults/index.d.ts b/packages/metro-config/types/defaults/index.d.ts index 972c1041da..c12a899b4b 100644 --- a/packages/metro-config/types/defaults/index.d.ts +++ b/packages/metro-config/types/defaults/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-config/src/defaults/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ConfigT} from '../types'; diff --git a/packages/metro-config/types/defaults/validConfig.d.ts b/packages/metro-config/types/defaults/validConfig.d.ts index e08113e571..e5cd63568b 100644 --- a/packages/metro-config/types/defaults/validConfig.d.ts +++ b/packages/metro-config/types/defaults/validConfig.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<4c43a8477da1248f0dfecc75159d2fe5>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-config/src/defaults/validConfig.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {InputConfigT} from '../types'; diff --git a/packages/metro-config/types/index.d.ts b/packages/metro-config/types/index.d.ts index b20564ec10..ff31cf9d9a 100644 --- a/packages/metro-config/types/index.d.ts +++ b/packages/metro-config/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-config/src/index.flow.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export type * from './types'; diff --git a/packages/metro-config/types/loadConfig.d.ts b/packages/metro-config/types/loadConfig.d.ts index 0157504d1e..1278b6a1f7 100644 --- a/packages/metro-config/types/loadConfig.d.ts +++ b/packages/metro-config/types/loadConfig.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<766965f89c595a34edf84abd019f9b92>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-config/src/loadConfig.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ConfigT, InputConfigT, YargArguments} from './types'; diff --git a/packages/metro-config/types/types.d.ts b/packages/metro-config/types/types.d.ts index 7b11efd0ee..3d7a2daa0b 100644 --- a/packages/metro-config/types/types.d.ts +++ b/packages/metro-config/types/types.d.ts @@ -4,13 +4,20 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<9c62bc2ca711f9693edc135a382a382a>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-config/src/types.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {HandleFunction, Server} from 'connect'; import type {CacheStore, MetroCache} from 'metro-cache'; -import type {CacheManagerFactory} from 'metro-file-map'; +import type {CacheManagerFactory, InputFileMapPlugin} from 'metro-file-map'; import type {CustomResolver} from 'metro-resolver'; import type {JsTransformerConfig} from 'metro-transform-worker'; import type { @@ -157,6 +164,7 @@ type MetalConfigT = { fileMapCacheDirectory?: string; hasteMapCacheDirectory?: string; unstable_fileMapCacheManagerFactory?: CacheManagerFactory; + unstable_fileMapPlugins?: ReadonlyArray; maxWorkers: number; unstable_perfLoggerFactory?: null | undefined | PerfLoggerFactory; projectRoot: string; diff --git a/packages/metro-core/package.json b/packages/metro-core/package.json index febb8180ea..a3ab613c35 100644 --- a/packages/metro-core/package.json +++ b/packages/metro-core/package.json @@ -1,6 +1,6 @@ { "name": "metro-core", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Metro's core package.", "main": "src/index.js", "exports": { @@ -20,7 +20,7 @@ "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", - "metro-resolver": "0.83.5" + "metro-resolver": "0.83.6" }, "license": "MIT", "engines": { diff --git a/packages/metro-core/types/Logger.d.ts b/packages/metro-core/types/Logger.d.ts index 6493ac4f88..1503ec7ed5 100644 --- a/packages/metro-core/types/Logger.d.ts +++ b/packages/metro-core/types/Logger.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<0395426536120e217377b6f3cde40ebc>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-core/src/Logger.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {BundleOptions} from 'metro/private/shared/types'; diff --git a/packages/metro-core/types/Terminal.d.ts b/packages/metro-core/types/Terminal.d.ts index bfece39731..462cee64a8 100644 --- a/packages/metro-core/types/Terminal.d.ts +++ b/packages/metro-core/types/Terminal.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<0b066ed2be97f8d1db5abb8f1a933cb0>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-core/src/Terminal.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Socket} from 'net'; diff --git a/packages/metro-core/types/canonicalize.d.ts b/packages/metro-core/types/canonicalize.d.ts index 6edfe16ad2..7c58460e5b 100644 --- a/packages/metro-core/types/canonicalize.d.ts +++ b/packages/metro-core/types/canonicalize.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-core/src/canonicalize.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function canonicalize(key: string, value: unknown): unknown; diff --git a/packages/metro-core/types/errors.d.ts b/packages/metro-core/types/errors.d.ts index 55b7f5f7df..ac88c01129 100644 --- a/packages/metro-core/types/errors.d.ts +++ b/packages/metro-core/types/errors.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<91251362bdbebafd413b723f863e29a9>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-core/src/errors.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import AmbiguousModuleResolutionError from './errors/AmbiguousModuleResolutionError'; diff --git a/packages/metro-core/types/errors/AmbiguousModuleResolutionError.d.ts b/packages/metro-core/types/errors/AmbiguousModuleResolutionError.d.ts index 1966412a61..00dd7b5622 100644 --- a/packages/metro-core/types/errors/AmbiguousModuleResolutionError.d.ts +++ b/packages/metro-core/types/errors/AmbiguousModuleResolutionError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<7a75db672d30c9ee9eb88666b881b3f6>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-core/src/errors/AmbiguousModuleResolutionError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {DuplicateHasteCandidatesError} from 'metro-file-map'; diff --git a/packages/metro-core/types/errors/PackageResolutionError.d.ts b/packages/metro-core/types/errors/PackageResolutionError.d.ts index 84d59f7417..4530faf3e7 100644 --- a/packages/metro-core/types/errors/PackageResolutionError.d.ts +++ b/packages/metro-core/types/errors/PackageResolutionError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<354fde3c81b3278d772c9279758d6b13>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-core/src/errors/PackageResolutionError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {InvalidPackageError} from 'metro-resolver'; diff --git a/packages/metro-core/types/index.d.ts b/packages/metro-core/types/index.d.ts index 73f3d39071..09b2ea446b 100644 --- a/packages/metro-core/types/index.d.ts +++ b/packages/metro-core/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<80e0670a74f3bf0ae7524193ec36bff9>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-core/src/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import AmbiguousModuleResolutionError from './errors/AmbiguousModuleResolutionError'; diff --git a/packages/metro-file-map/package.json b/packages/metro-file-map/package.json index 7730cdab56..79ebf97513 100644 --- a/packages/metro-file-map/package.json +++ b/packages/metro-file-map/package.json @@ -1,6 +1,6 @@ { "name": "metro-file-map", - "version": "0.83.5", + "version": "0.83.6", "description": "[Experimental] - 🚇 File crawling, watching and mapping for Metro", "main": "src/index.js", "exports": { diff --git a/packages/metro-file-map/src/Watcher.js b/packages/metro-file-map/src/Watcher.js index a48ea91a5d..99e10618a1 100644 --- a/packages/metro-file-map/src/Watcher.js +++ b/packages/metro-file-map/src/Watcher.js @@ -11,12 +11,11 @@ import type { Console, CrawlerOptions, - FileData, + CrawlResult, Path, PerfLogger, WatcherBackend, WatcherBackendChangeEvent, - WatchmanClocks, } from './flow-types'; import type {WatcherOptions as WatcherBackendOptions} from './watchers/common'; @@ -37,11 +36,12 @@ const debug = require('debug')('Metro:Watcher'); const MAX_WAIT_TIME = 240000; -type CrawlResult = { - changedFiles: FileData, - clocks?: WatchmanClocks, - removedFiles: Set, -}; +type InternalCrawlOptions = Readonly<{ + previousState: CrawlerOptions['previousState'], + roots: ReadonlyArray, + subpath?: string, + useWatchman: boolean, +}>; type WatcherOptions = { abortSignal: AbortSignal, @@ -86,12 +86,41 @@ export class Watcher extends EventEmitter { async crawl(): Promise { this.#options.perfLogger?.point('crawl_start'); + const options = this.#options; + + const result = await this.#crawl({ + previousState: options.previousState, + roots: options.roots, + useWatchman: options.useWatchman, + }); + this.#options.perfLogger?.point('crawl_end'); + return result; + } + + async recrawl( + subpath: string, + currentFileSystem: CrawlerOptions['previousState']['fileSystem'], + ): Promise { + return this.#crawl({ + previousState: { + clocks: new Map(), + fileSystem: currentFileSystem, + }, + roots: [path.join(this.#options.rootDir, subpath)], + subpath, + useWatchman: false, + }); + } + + async #crawl(crawlOptions: InternalCrawlOptions): Promise { const options = this.#options; + const {useWatchman, subpath} = crawlOptions; + const ignoreForCrawl = (filePath: string) => options.ignoreForCrawl(filePath) || path.basename(filePath).startsWith(this.#options.healthCheckFilePrefix); - const crawl = options.useWatchman ? watchmanCrawl : nodeCrawl; + const crawl = useWatchman ? watchmanCrawl : nodeCrawl; let crawler = crawl === watchmanCrawl ? 'watchman' : 'node'; options.abortSignal.throwIfAborted(); @@ -108,55 +137,50 @@ export class Watcher extends EventEmitter { this.emit('status', status); }, perfLogger: options.perfLogger, - previousState: options.previousState, + previousState: crawlOptions.previousState, rootDir: options.rootDir, - roots: options.roots, + roots: crawlOptions.roots, + subpath, }; - const retry = (error: Error): Promise => { - if (crawl === watchmanCrawl) { - crawler = 'node'; - options.console.warn( - 'metro-file-map: Watchman crawl failed. Retrying once with node ' + - 'crawler.\n' + - " Usually this happens when watchman isn't running. Create an " + - "empty `.watchmanconfig` file in your project's root folder or " + - 'initialize a git or hg repository in your project.\n' + - ' ' + - error.toString(), - ); - // $FlowFixMe[incompatible-type] Found when updating Promise type definition - return nodeCrawl(crawlerOptions).catch(e => { - throw new Error( - 'Crawler retry failed:\n' + - ` Original error: ${error.message}\n` + - ` Retry error: ${e.message}\n`, - ); - }); - } - - throw error; - }; - - const logEnd = (delta: CrawlResult): CrawlResult => { - debug( - 'Crawler "%s" returned %d added/modified, %d removed, %d clock(s).', - crawler, - delta.changedFiles.size, - delta.removedFiles.size, - delta.clocks?.size ?? 0, - ); - this.#options.perfLogger?.point('crawl_end'); - return delta; - }; + debug('Crawling roots: %s with %s crawler.', crawlOptions.roots, crawler); - debug('Beginning crawl with "%s".', crawler); + let delta: CrawlResult; try { - // $FlowFixMe[incompatible-type] Found when updating Promise type definition - return crawl(crawlerOptions).catch(retry).then(logEnd); - } catch (error) { - return retry(error).then(logEnd); + delta = await crawl(crawlerOptions); + } catch (firstError) { + if (crawl !== watchmanCrawl) { + throw firstError; + } + crawler = 'node'; + options.console.warn( + 'metro-file-map: Watchman crawl failed. Retrying once with node ' + + 'crawler.\n' + + " Usually this happens when watchman isn't running. Create an " + + "empty `.watchmanconfig` file in your project's root folder or " + + 'initialize a git or hg repository in your project.\n' + + ' ' + + firstError.toString(), + ); + try { + delta = await nodeCrawl(crawlerOptions); + } catch (retryError) { + throw new Error( + 'Crawler retry failed:\n' + + ` Original error: ${firstError.message}\n` + + ` Retry error: ${retryError.message}\n`, + ); + } } + + debug( + 'Crawler "%s" returned %d added/modified, %d removed, %d clock(s).', + crawler, + delta.changedFiles.size, + delta.removedFiles.size, + delta.clocks?.size ?? 0, + ); + return delta; } async watch(onChange: (change: WatcherBackendChangeEvent) => void) { @@ -214,6 +238,15 @@ export class Watcher extends EventEmitter { } return; } + // Watchman handles recrawls internally - receiving a recrawl event + // when using Watchman would indicate a bug. Log an error and ignore. + if (change.event === 'recrawl' && useWatchman) { + this.#options.console.error( + 'metro-file-map: Received unexpected recrawl event while using ' + + 'Watchman. Watchman recrawls are not implemented.', + ); + return; + } onChange(change); }); await watcher.startWatching(); diff --git a/packages/metro-file-map/src/__tests__/changes-between-starts-test.js b/packages/metro-file-map/src/__tests__/changes-between-starts-test.js new file mode 100644 index 0000000000..f9bfa3ec36 --- /dev/null +++ b/packages/metro-file-map/src/__tests__/changes-between-starts-test.js @@ -0,0 +1,344 @@ +/** + * 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 + */ + +import type {CacheData, FileData, FileMetadata} from '../flow-types'; +import type FileMapT from '../index'; + +import * as path from 'path'; + +jest.useRealTimers(); + +type MockCrawlResult = { + changedFiles: FileData, + removedFiles: Set, + clocks: Map, +}; + +let mockCrawlResult: MockCrawlResult; + +jest.mock('../crawlers/node', () => ({ + __esModule: true, + default: jest.fn(() => Promise.resolve(mockCrawlResult)), +})); + +let FileMap: Class; +let mockCacheContent: ?CacheData = null; +let mockCacheManager: { + read: JestMockFn, Promise>, + write: JestMockFn, Promise>, + end: JestMockFn, Promise>, +}; + +const ROOT_DIR = path.join('/', 'project'); +const FRUITS_DIR = path.join(ROOT_DIR, 'fruits'); + +const DEFAULT_HEALTH_CHECK_CONFIG = { + enabled: false, + interval: 10000, + timeout: 1000, + filePrefix: '.metro-file-map-health-check', +}; + +function createFileMetadata( + mtime: number = 32, + size: number = 42, +): FileMetadata { + return [ + mtime, // H.MTIME + size, // H.SIZE + 0, // H.VISITED + null, // H.SHA1 + 0, // H.SYMLINK + ]; +} + +describe('FileMap crawler backend integration', () => { + beforeEach(() => { + jest.resetModules(); + + mockCacheContent = null; + mockCacheManager = { + read: jest.fn().mockImplementation(async () => mockCacheContent), + write: jest.fn().mockImplementation(async getSnapshot => { + mockCacheContent = getSnapshot(); + }), + end: jest.fn(), + }; + + ({default: FileMap} = require('../')); + + mockCrawlResult = { + changedFiles: new Map(), + removedFiles: new Set(), + clocks: new Map([['fruits', 'c:clock:1']]), + }; + }); + + afterEach(async () => { + mockCacheContent = null; + }); + + describe('Cold cache and warm cache with changes', () => { + test('creates a file map on cold cache with all files new, then handles changes on rebuild', async () => { + mockCrawlResult = { + changedFiles: new Map([ + [path.join('fruits', 'Apple.js'), createFileMetadata()], + [path.join('fruits', 'Banana.js'), createFileMetadata()], + [path.join('fruits', 'Cherry.js'), createFileMetadata()], + ]), + removedFiles: new Set(), + clocks: new Map([['fruits', 'c:clock:1']]), + }; + + // Configure FileMap with no plugins and computeSha1: false + // so files don't need to be visited/read + const fileMap1 = new FileMap({ + extensions: ['js'], + rootDir: ROOT_DIR, + roots: [FRUITS_DIR], + cacheManagerFactory: () => mockCacheManager, + healthCheck: DEFAULT_HEALTH_CHECK_CONFIG, + maxWorkers: 1, + resetCache: false, + retainAllFiles: false, + useWatchman: false, + computeSha1: false, + plugins: [], + }); + + const {fileSystem: fileSystem1} = await fileMap1.build(); + + expect(fileSystem1.exists(path.join('fruits', 'Apple.js'))).toBe(true); + expect(fileSystem1.exists(path.join('fruits', 'Banana.js'))).toBe(true); + expect(fileSystem1.exists(path.join('fruits', 'Cherry.js'))).toBe(true); + + const allFiles1 = fileSystem1.getAllFiles(); + expect(allFiles1).toHaveLength(3); + expect(allFiles1).toContain(path.join(ROOT_DIR, 'fruits', 'Apple.js')); + expect(allFiles1).toContain(path.join(ROOT_DIR, 'fruits', 'Banana.js')); + expect(allFiles1).toContain(path.join(ROOT_DIR, 'fruits', 'Cherry.js')); + + expect(mockCacheManager.write).toHaveBeenCalledTimes(1); + + await fileMap1.end(); + + // Second build: crawler reports changes (modified, added, removed files) + mockCrawlResult = { + changedFiles: new Map([ + [path.join('fruits', 'Banana.js'), createFileMetadata(100, 50)], + [path.join('fruits', 'Date.js'), createFileMetadata(100, 30)], + ]), + removedFiles: new Set([path.join('fruits', 'Cherry.js')]), + clocks: new Map([['fruits', 'c:clock:2']]), + }; + + const fileMap2 = new FileMap({ + extensions: ['js'], + rootDir: ROOT_DIR, + roots: [FRUITS_DIR], + cacheManagerFactory: () => mockCacheManager, + healthCheck: DEFAULT_HEALTH_CHECK_CONFIG, + maxWorkers: 1, + resetCache: false, + retainAllFiles: false, + useWatchman: false, + computeSha1: false, + plugins: [], + }); + + const {fileSystem: fileSystem2} = await fileMap2.build(); + + expect(fileSystem2.exists(path.join('fruits', 'Apple.js'))).toBe(true); + expect(fileSystem2.exists(path.join('fruits', 'Banana.js'))).toBe(true); + expect(fileSystem2.exists(path.join('fruits', 'Cherry.js'))).toBe(false); + expect(fileSystem2.exists(path.join('fruits', 'Date.js'))).toBe(true); + + const allFiles2 = fileSystem2.getAllFiles(); + expect(allFiles2).toHaveLength(3); + expect(allFiles2).toContain(path.join(ROOT_DIR, 'fruits', 'Apple.js')); + expect(allFiles2).toContain(path.join(ROOT_DIR, 'fruits', 'Banana.js')); + expect(allFiles2).toContain(path.join(ROOT_DIR, 'fruits', 'Date.js')); + expect(allFiles2).not.toContain( + path.join(ROOT_DIR, 'fruits', 'Cherry.js'), + ); + + const bananaLookup = fileSystem2.lookup( + path.join(ROOT_DIR, 'fruits', 'Banana.js'), + ); + expect(bananaLookup.exists).toBe(true); + if (bananaLookup.exists && bananaLookup.type === 'f') { + expect(bananaLookup.metadata[0]).toBe(100); + expect(bananaLookup.metadata[1]).toBe(50); + } + + const dateLookup = fileSystem2.lookup( + path.join(ROOT_DIR, 'fruits', 'Date.js'), + ); + expect(dateLookup.exists).toBe(true); + if (dateLookup.exists && dateLookup.type === 'f') { + expect(dateLookup.metadata[0]).toBe(100); + expect(dateLookup.metadata[1]).toBe(30); + } + + expect(mockCacheManager.read).toHaveBeenCalledTimes(2); + expect(mockCacheManager.write).toHaveBeenCalledTimes(2); + + await fileMap2.end(); + }); + + test('handles multiple file additions and removals in a single rebuild', async () => { + mockCrawlResult = { + changedFiles: new Map([ + [path.join('fruits', 'File1.js'), createFileMetadata()], + [path.join('fruits', 'File2.js'), createFileMetadata()], + [path.join('fruits', 'File3.js'), createFileMetadata()], + [path.join('fruits', 'File4.js'), createFileMetadata()], + ]), + removedFiles: new Set(), + clocks: new Map([['fruits', 'c:clock:1']]), + }; + + const fileMap1 = new FileMap({ + extensions: ['js'], + rootDir: ROOT_DIR, + roots: [FRUITS_DIR], + cacheManagerFactory: () => mockCacheManager, + healthCheck: DEFAULT_HEALTH_CHECK_CONFIG, + maxWorkers: 1, + resetCache: false, + retainAllFiles: false, + useWatchman: false, + computeSha1: false, + plugins: [], + }); + + const {fileSystem: fileSystem1} = await fileMap1.build(); + expect(fileSystem1.getAllFiles()).toHaveLength(4); + await fileMap1.end(); + + // Second build: remove 3 files, modify 1, add 2 + mockCrawlResult = { + changedFiles: new Map([ + [path.join('fruits', 'File2.js'), createFileMetadata(200, 100)], + [path.join('fruits', 'File5.js'), createFileMetadata(200, 50)], + [path.join('fruits', 'File6.js'), createFileMetadata(200, 60)], + ]), + removedFiles: new Set([ + path.join('fruits', 'File1.js'), + path.join('fruits', 'File3.js'), + path.join('fruits', 'File4.js'), + ]), + clocks: new Map([['fruits', 'c:clock:2']]), + }; + + const fileMap2 = new FileMap({ + extensions: ['js'], + rootDir: ROOT_DIR, + roots: [FRUITS_DIR], + cacheManagerFactory: () => mockCacheManager, + healthCheck: DEFAULT_HEALTH_CHECK_CONFIG, + maxWorkers: 1, + resetCache: false, + retainAllFiles: false, + useWatchman: false, + computeSha1: false, + plugins: [], + }); + + const {fileSystem: fileSystem2} = await fileMap2.build(); + + expect(fileSystem2.exists(path.join('fruits', 'File1.js'))).toBe(false); + expect(fileSystem2.exists(path.join('fruits', 'File2.js'))).toBe(true); + expect(fileSystem2.exists(path.join('fruits', 'File3.js'))).toBe(false); + expect(fileSystem2.exists(path.join('fruits', 'File4.js'))).toBe(false); + expect(fileSystem2.exists(path.join('fruits', 'File5.js'))).toBe(true); + expect(fileSystem2.exists(path.join('fruits', 'File6.js'))).toBe(true); + + const allFiles2 = fileSystem2.getAllFiles(); + expect(allFiles2).toHaveLength(3); + + await fileMap2.end(); + }); + + test('correctly updates FileSystem lookup results after changes', async () => { + mockCrawlResult = { + changedFiles: new Map([ + [path.join('fruits', 'Original.js'), createFileMetadata(50, 100)], + ]), + removedFiles: new Set(), + clocks: new Map([['fruits', 'c:clock:1']]), + }; + + const fileMap1 = new FileMap({ + extensions: ['js'], + rootDir: ROOT_DIR, + roots: [FRUITS_DIR], + cacheManagerFactory: () => mockCacheManager, + healthCheck: DEFAULT_HEALTH_CHECK_CONFIG, + maxWorkers: 1, + resetCache: false, + retainAllFiles: false, + useWatchman: false, + computeSha1: false, + plugins: [], + }); + + const {fileSystem: fileSystem1} = await fileMap1.build(); + + const lookup1 = fileSystem1.lookup( + path.join(ROOT_DIR, 'fruits', 'Original.js'), + ); + expect(lookup1.exists).toBe(true); + if (lookup1.exists && lookup1.type === 'f') { + expect(lookup1.metadata[0]).toBe(50); + expect(lookup1.metadata[1]).toBe(100); + } + + await fileMap1.end(); + + // Second build: crawler reports the file was modified with new metadata + mockCrawlResult = { + changedFiles: new Map([ + [path.join('fruits', 'Original.js'), createFileMetadata(999, 500)], + ]), + removedFiles: new Set(), + clocks: new Map([['fruits', 'c:clock:2']]), + }; + + const fileMap2 = new FileMap({ + extensions: ['js'], + rootDir: ROOT_DIR, + roots: [FRUITS_DIR], + cacheManagerFactory: () => mockCacheManager, + healthCheck: DEFAULT_HEALTH_CHECK_CONFIG, + maxWorkers: 1, + resetCache: false, + retainAllFiles: false, + useWatchman: false, + computeSha1: false, + plugins: [], + }); + + const {fileSystem: fileSystem2} = await fileMap2.build(); + + const lookup2 = fileSystem2.lookup( + path.join(ROOT_DIR, 'fruits', 'Original.js'), + ); + expect(lookup2.exists).toBe(true); + if (lookup2.exists && lookup2.type === 'f') { + expect(lookup2.metadata[0]).toBe(999); + expect(lookup2.metadata[1]).toBe(500); + } + + await fileMap2.end(); + }); + }); +}); diff --git a/packages/metro-file-map/src/__tests__/index-test.js b/packages/metro-file-map/src/__tests__/index-test.js index f8ea09dea9..48da613f06 100644 --- a/packages/metro-file-map/src/__tests__/index-test.js +++ b/packages/metro-file-map/src/__tests__/index-test.js @@ -12,6 +12,8 @@ import type {InputOptions} from '..'; import type { BuildResult, + CanonicalPath, + ChangedFileMetadata, ChangeEvent, ChangeEventMetadata, FileData, @@ -19,6 +21,7 @@ import type { FileSystem, HasteMap, MockMap, + ReadonlyFileSystemChanges, WatcherBackendOptions, WorkerSetupArgs, } from '../flow-types'; @@ -61,7 +64,11 @@ jest.mock('jest-worker', () => ({ }), })); -jest.mock('../crawlers/node', () => ({__esModule: true, default: jest.fn()})); +const mockNodeCrawler = jest.fn(); +jest.mock('../crawlers/node', () => ({ + __esModule: true, + default: mockNodeCrawler, +})); jest.mock('../crawlers/watchman', () => ({ __esModule: true, default: jest.fn(options => { @@ -124,9 +131,14 @@ class MockWatcher extends AbstractWatcher { super(root, opts); mockEmitters[root] = this; } + + static isSupported(): boolean { + return true; + } } jest.mock('../watchers/FallbackWatcher', () => MockWatcher); +jest.mock('../watchers/NativeWatcher', () => MockWatcher); jest.mock('../watchers/WatchmanWatcher', () => MockWatcher); type MockFS = {[path: string]: ?string | {link: string}, __proto__: null}; @@ -221,7 +233,7 @@ const assertFileSystemEqual = (fileSystem: FileSystem, fileData: FileData) => { // Jest toEqual does not match Map instances from different contexts // This normalizes them for the uses cases in this test -const deepNormalize = (value: T): T => { +const deepNormalize = (value: T): T => { // $FlowFixMe[method-unbinding] const stringTag = Object.prototype.toString.call(value); switch (stringTag) { @@ -274,6 +286,7 @@ let cacheContent = null; describe('FileMap', () => { beforeEach(() => { jest.resetModules(); + mockNodeCrawler.mockClear(); mockEmitters = Object.create(null); mockFs = object({ @@ -1448,7 +1461,10 @@ describe('FileMap', () => { test('distributes work across workers', async () => { const jestWorker = require('jest-worker').Worker; const path = require('path'); - const dependencyExtractor = path.join(__dirname, 'dependencyExtractor.js'); + const dependencyExtractor = path.resolve( + __dirname, + '../plugins/dependencies/__tests__/mockDependencyExtractor.js', + ); await buildNewFileMap( { maxWorkers: 4, @@ -1634,14 +1650,59 @@ describe('FileMap', () => { }); describe('file system changes processing', () => { - function waitForItToChange( - fileMap: FileMap, - ): Promise<{eventsQueue: ChangeEvent}> { + function waitForItToChange(fileMap: FileMap): Promise { return new Promise(resolve => { fileMap.once('change', resolve); }); } + type ChangeEntry = [CanonicalPath, ChangedFileMetadata]; + + function expectChanges( + changes: ReadonlyFileSystemChanges, + expected: Readonly<{ + addedFiles?: ReadonlyArray, + modifiedFiles?: ReadonlyArray, + removedFiles?: ReadonlyArray, + addedDirectories?: ReadonlyArray, + removedDirectories?: ReadonlyArray, + }>, + ): void { + const sortByPath = (a: ChangeEntry, b: ChangeEntry): number => + a[0].localeCompare(b[0]); + + const toSortedArray = ( + iterable: Iterable>, + ): Array => + [...iterable].map(([p, m]): ChangeEntry => [p, m]).sort(sortByPath); + + expect(toSortedArray(changes.addedFiles)).toEqual( + (expected.addedFiles ?? []).slice().sort(sortByPath), + ); + expect(toSortedArray(changes.modifiedFiles)).toEqual( + (expected.modifiedFiles ?? []).slice().sort(sortByPath), + ); + expect(toSortedArray(changes.removedFiles)).toEqual( + (expected.removedFiles ?? []).slice().sort(sortByPath), + ); + expect([...changes.addedDirectories].sort()).toEqual( + (expected.addedDirectories ?? []).slice().sort(), + ); + expect([...changes.removedDirectories].sort()).toEqual( + (expected.removedDirectories ?? []).slice().sort(), + ); + } + + function countFileChanges( + changes: ReadonlyFileSystemChanges, + ): number { + return ( + [...changes.addedFiles].length + + [...changes.modifiedFiles].length + + [...changes.removedFiles].length + ); + } + function mockDeleteFile(root: string, relativePath: string) { const e = mockEmitters[root]; e.emitFileEvent({event: 'delete', relativePath}); @@ -1693,18 +1754,16 @@ describe('FileMap', () => { expect(hasteMap.getModule('Banana')).toBe(filePath); mockDeleteFile(path.join('/', 'project', 'fruits'), 'Banana.js'); mockDeleteFile(path.join('/', 'project', 'fruits'), 'Banana.js'); - const {eventsQueue} = await waitForItToChange(fileMap); - expect(eventsQueue).toHaveLength(1); - const deletedBanana = { - filePath, - metadata: { - modifiedTime: null, - size: null, - type: 'f', - }, - type: 'delete', - }; - expect(eventsQueue).toEqual([deletedBanana]); + const {changes} = await waitForItToChange(fileMap); + expect(countFileChanges(changes)).toBe(1); + expectChanges(changes, { + removedFiles: [ + [ + path.join('fruits', 'Banana.js'), + {isSymlink: false, modifiedTime: 32}, + ], + ], + }); // Verify that the initial result has been updated expect(fileSystem.exists(filePath)).toBe(false); expect(hasteMap.getModuleNameByPath(filePath)).toBeNull(); @@ -1718,24 +1777,12 @@ describe('FileMap', () => { size: 55, }; - const MOCK_DELETE_FILE: ChangeEventMetadata = { - type: 'f', - modifiedTime: null, - size: null, - }; - const MOCK_CHANGE_LINK: ChangeEventMetadata = { type: 'l', modifiedTime: 46, size: 5, }; - const MOCK_DELETE_LINK: ChangeEventMetadata = { - type: 'l', - modifiedTime: null, - size: null, - }; - const MOCK_CHANGE_FOLDER: ChangeEventMetadata = { type: 'd', modifiedTime: 45, @@ -1758,24 +1805,35 @@ describe('FileMap', () => { relativePath: 'Tomato.js', metadata: MOCK_CHANGE_FILE, }); + e.emitFileEvent({ + event: 'touch', + relativePath: 'Banana.js', + metadata: MOCK_CHANGE_FILE, + }); e.emitFileEvent({ event: 'touch', relativePath: 'Pear.js', metadata: MOCK_CHANGE_FILE, }); - const {eventsQueue} = await waitForItToChange(fileMap); - expect(eventsQueue).toEqual([ - { - filePath: path.join('/', 'project', 'fruits', 'Tomato.js'), - metadata: MOCK_CHANGE_FILE, - type: 'add', - }, - { - filePath: path.join('/', 'project', 'fruits', 'Pear.js'), - metadata: MOCK_CHANGE_FILE, - type: 'change', - }, - ]); + const {changes} = await waitForItToChange(fileMap); + expectChanges(changes, { + addedFiles: [ + [ + path.join('fruits', 'Tomato.js'), + {isSymlink: false, modifiedTime: 45}, + ], + ], + modifiedFiles: [ + [ + path.join('fruits', 'Banana.js'), + {isSymlink: false, modifiedTime: 45}, + ], + [ + path.join('fruits', 'Pear.js'), + {isSymlink: false, modifiedTime: 45}, + ], + ], + }); expect( fileSystem.exists(path.join('/', 'project', 'fruits', 'Tomato.js')), ).toBe(true); @@ -1801,8 +1859,8 @@ describe('FileMap', () => { relativePath: 'Tomato.js', metadata: MOCK_CHANGE_FILE, }); - const {eventsQueue} = await waitForItToChange(fileMap); - expect(eventsQueue).toHaveLength(1); + const {changes} = await waitForItToChange(fileMap); + expect(countFileChanges(changes)).toBe(1); }); fm_it( @@ -1847,8 +1905,8 @@ describe('FileMap', () => { expect(fileSystem.getSha1(bananaPath)).toBe(originalHash); expect(hasteMap.getModule('Banana')).toBe(bananaPath); - const {eventsQueue} = await waitForItToChange(fileMap); - expect(eventsQueue).toHaveLength(1); + const {changes} = await waitForItToChange(fileMap); + expect(countFileChanges(changes)).toBe(1); // After the 'change' event is emitted, we should have new data expect(fileSystem.linkStats(bananaPath)).toEqual({ @@ -1879,14 +1937,15 @@ describe('FileMap', () => { relativePath: 'LinkToStrawberry.js', metadata: MOCK_CHANGE_LINK, }); - const {eventsQueue} = await waitForItToChange(fileMap); - expect(eventsQueue).toEqual([ - { - filePath: path.join(fruitsRoot, 'Strawberry.js'), - metadata: MOCK_CHANGE_FILE, - type: 'change', - }, - ]); + const {changes} = await waitForItToChange(fileMap); + expectChanges(changes, { + modifiedFiles: [ + [ + path.join('fruits', 'Strawberry.js'), + {isSymlink: false, modifiedTime: 45}, + ], + ], + }); expect( fileSystem.linkStats(path.join(fruitsRoot, 'LinkToStrawberry.js')), ).toBeNull(); @@ -1909,19 +1968,19 @@ describe('FileMap', () => { relativePath: 'LinkToStrawberry.js', metadata: MOCK_CHANGE_LINK, }); - const {eventsQueue} = await waitForItToChange(fileMap); - expect(eventsQueue).toEqual([ - { - filePath: path.join(fruitsRoot, 'Strawberry.js'), - metadata: MOCK_CHANGE_FILE, - type: 'change', - }, - { - filePath: path.join(fruitsRoot, 'LinkToStrawberry.js'), - metadata: MOCK_CHANGE_LINK, - type: 'change', - }, - ]); + const {changes} = await waitForItToChange(fileMap); + expectChanges(changes, { + modifiedFiles: [ + [ + path.join('fruits', 'LinkToStrawberry.js'), + {isSymlink: true, modifiedTime: 46}, + ], + [ + path.join('fruits', 'Strawberry.js'), + {isSymlink: false, modifiedTime: 45}, + ], + ], + }); expect( fileSystem.linkStats(path.join(fruitsRoot, 'LinkToStrawberry.js')), ).toEqual({fileType: 'l', modifiedTime: 46, size: 5}); @@ -1934,12 +1993,15 @@ describe('FileMap', () => { async ({fileMap}) => { const {fileSystem} = await fileMap.build(); const e = mockEmitters[path.join('/', 'project', 'fruits')]; + mockFs[ + path.join('/', 'project', 'fruits', 'node_modules', 'apple.js') + ] = ''; e.emitFileEvent({ event: 'touch', relativePath: path.join('node_modules', 'apple.js'), metadata: MOCK_CHANGE_FILE, }); - const {eventsQueue} = await waitForItToChange(fileMap); + const {changes} = await waitForItToChange(fileMap); const filePath = path.join( '/', 'project', @@ -1947,14 +2009,47 @@ describe('FileMap', () => { 'node_modules', 'apple.js', ); - expect(eventsQueue).toHaveLength(1); - expect(eventsQueue).toEqual([ - {filePath, metadata: MOCK_CHANGE_FILE, type: 'add'}, - ]); + expect(countFileChanges(changes)).toBe(1); + expectChanges(changes, { + addedFiles: [ + [ + path.join('fruits', 'node_modules', 'apple.js'), + {isSymlink: false, modifiedTime: 45}, + ], + ], + addedDirectories: [path.join('fruits', 'node_modules')], + }); expect(fileSystem.exists(filePath)).toBe(true); }, ); + fm_it( + 'emits directory removed when removing the last file from a directory', + async ({fileMap}) => { + await fileMap.build(); + const e = mockEmitters[path.join('/', 'project', 'fruits')]; + e.emitFileEvent({ + event: 'delete', + relativePath: 'lonely.js', + }); + const {changes} = await waitForItToChange(fileMap); + expectChanges(changes, { + removedFiles: [ + [ + path.join('fruits', 'lonely.js'), + {isSymlink: false, modifiedTime: 32}, + ], + ], + removedDirectories: [path.join('fruits')], + }); + }, + { + mockFs: { + [path.join('/', 'project', 'fruits', 'lonely.js')]: '// lonely', + }, + }, + ); + fm_it( 'does not emit changes for regular files with unwatched extensions', async ({fileMap}) => { @@ -1972,12 +2067,17 @@ describe('FileMap', () => { relativePath: 'Banana.unwatched', metadata: MOCK_CHANGE_FILE, }); - const {eventsQueue} = await waitForItToChange(fileMap); + const {changes} = await waitForItToChange(fileMap); const filePath = path.join('/', 'project', 'fruits', 'Banana.js'); - expect(eventsQueue).toHaveLength(1); - expect(eventsQueue).toEqual([ - {filePath, metadata: MOCK_CHANGE_FILE, type: 'change'}, - ]); + expect(countFileChanges(changes)).toBe(1); + expectChanges(changes, { + modifiedFiles: [ + [ + path.join('fruits', 'Banana.js'), + {isSymlink: false, modifiedTime: 45}, + ], + ], + }); expect(fileSystem.exists(filePath)).toBe(true); }, ); @@ -1997,12 +2097,17 @@ describe('FileMap', () => { event: 'delete', relativePath: 'Unknown.ext', }); - const {eventsQueue} = await waitForItToChange(fileMap); + const {changes} = await waitForItToChange(fileMap); const filePath = path.join('/', 'project', 'fruits', 'Banana.js'); - expect(eventsQueue).toHaveLength(1); - expect(eventsQueue).toEqual([ - {filePath, metadata: MOCK_DELETE_FILE, type: 'delete'}, - ]); + expect(countFileChanges(changes)).toBe(1); + expectChanges(changes, { + removedFiles: [ + [ + path.join('fruits', 'Banana.js'), + {isSymlink: false, modifiedTime: 32}, + ], + ], + }); expect(fileSystem.exists(filePath)).toBe(false); expect(console.warn).not.toHaveBeenCalled(); expect(console.error).not.toHaveBeenCalled(); @@ -2022,17 +2127,22 @@ describe('FileMap', () => { relativePath: 'LinkToStrawberry.ext', metadata: MOCK_CHANGE_LINK, }); - const {eventsQueue} = await waitForItToChange(fileMap); + const {changes} = await waitForItToChange(fileMap); const filePath = path.join( '/', 'project', 'fruits', 'LinkToStrawberry.ext', ); - expect(eventsQueue).toHaveLength(1); - expect(eventsQueue).toEqual([ - {filePath, metadata: MOCK_CHANGE_LINK, type: 'add'}, - ]); + expect(countFileChanges(changes)).toBe(1); + expectChanges(changes, { + addedFiles: [ + [ + path.join('fruits', 'LinkToStrawberry.ext'), + {isSymlink: true, modifiedTime: 46}, + ], + ], + }); const linkStats = fileSystem.linkStats(filePath); expect(linkStats).toEqual({ fileType: 'l', @@ -2074,12 +2184,17 @@ describe('FileMap', () => { event: 'delete', relativePath: 'LinkToStrawberry.js', }); - const {eventsQueue} = await waitForItToChange(fileMap); + const {changes} = await waitForItToChange(fileMap); - expect(eventsQueue).toHaveLength(1); - expect(eventsQueue).toEqual([ - {filePath: symlinkPath, metadata: MOCK_DELETE_LINK, type: 'delete'}, - ]); + expect(countFileChanges(changes)).toBe(1); + expectChanges(changes, { + removedFiles: [ + [ + path.join('fruits', 'LinkToStrawberry.js'), + {isSymlink: true, modifiedTime: 32}, + ], + ], + }); // Symlink is deleted without affecting the Haste module or real file. expect(fileSystem.exists(symlinkPath)).toBe(false); @@ -2107,20 +2222,20 @@ describe('FileMap', () => { relativePath: 'Orange.android.js', metadata: MOCK_CHANGE_FILE, }); - const {eventsQueue} = await waitForItToChange(fileMap); - expect(eventsQueue).toHaveLength(2); - expect(eventsQueue).toEqual([ - { - filePath: path.join('/', 'project', 'fruits', 'Orange.ios.js'), - metadata: MOCK_CHANGE_FILE, - type: 'change', - }, - { - filePath: path.join('/', 'project', 'fruits', 'Orange.android.js'), - metadata: MOCK_CHANGE_FILE, - type: 'change', - }, - ]); + const {changes} = await waitForItToChange(fileMap); + expect(countFileChanges(changes)).toBe(2); + expectChanges(changes, { + modifiedFiles: [ + [ + path.join('fruits', 'Orange.android.js'), + {isSymlink: false, modifiedTime: 45}, + ], + [ + path.join('fruits', 'Orange.ios.js'), + {isSymlink: false, modifiedTime: 45}, + ], + ], + }); expect( hasteMap.getModuleNameByPath( path.join('/', 'project', 'fruits', 'Orange.ios.js'), @@ -2174,25 +2289,28 @@ describe('FileMap', () => { metadata: MOCK_CHANGE_FILE, }); - const {eventsQueue} = await waitForItToChange(fileMap); + const {changes} = await waitForItToChange(fileMap); // No duplicate warnings or errors should be printed. expect(console.warn).not.toHaveBeenCalled(); expect(console.error).not.toHaveBeenCalled(); - expect(eventsQueue).toHaveLength(2); - expect(eventsQueue).toEqual([ - { - filePath: path.join('/', 'project', 'vegetables', 'Melon.js'), - metadata: MOCK_DELETE_FILE, - type: 'delete', - }, - { - filePath: path.join('/', 'project', 'fruits', 'Melon.js'), - metadata: MOCK_CHANGE_FILE, - type: 'add', - }, - ]); + expect(countFileChanges(changes)).toBe(2); + expectChanges(changes, { + addedFiles: [ + [ + path.join('fruits', 'Melon.js'), + {isSymlink: false, modifiedTime: 45}, + ], + ], + removedFiles: [ + [ + path.join('vegetables', 'Melon.js'), + {isSymlink: false, modifiedTime: 32}, + ], + ], + removedDirectories: [path.join('vegetables')], + }); expect(hasteMap.getModule('Melon')).toEqual(newPath); }, ); @@ -2369,9 +2487,247 @@ describe('FileMap', () => { relativePath: path.join('tomato.js', 'index.js'), metadata: MOCK_CHANGE_FILE, }); - const {eventsQueue} = await waitForItToChange(fileMap); - expect(eventsQueue).toHaveLength(1); + const {changes} = await waitForItToChange(fileMap); + expect(countFileChanges(changes)).toBe(1); + }, + ); + }); + + describe('recrawl events', () => { + // Recrawl events only come from non-Watchman watchers (NativeWatcher, + // FallbackWatcher), because Watchman handles its own recrawls internally. + // These tests use useWatchman: false to simulate a non-Watchman watcher, + // so we need to mock nodeCrawl for the initial build. + beforeEach(() => { + mockNodeCrawler.mockImplementationOnce(async () => ({ + changedFiles: new Map([ + [path.join('fruits', 'Banana.js'), [32, 42, 0, null, 0, 'Banana']], + [path.join('fruits', 'Pear.js'), [32, 42, 0, null, 0, 'Pear']], + [ + path.join('fruits', 'Strawberry.js'), + [32, 42, 0, null, 0, 'Strawberry'], + ], + [ + path.join('fruits', '__mocks__', 'Pear.js'), + [32, 42, 0, null, 0, null], + ], + [ + path.join('vegetables', 'Melon.js'), + [32, 42, 0, null, 0, 'Melon'], + ], + ]), + removedFiles: new Set(), + })); + }); + + fm_it( + 'recrawl event triggers subdirectory crawl and detects added files', + async ({fileMap, hasteMap}) => { + const {fileSystem: _fileSystem} = await fileMap.build(); + const fruitsRoot = path.join('/', 'project', 'fruits'); + const e = mockEmitters[fruitsRoot]; + + // Simulate a directory move-in: a new subdirectory appears with files + const newDir = path.join(fruitsRoot, 'tropical'); + const newFile1 = path.join(newDir, 'Mango.js'); + const newFile2 = path.join(newDir, 'Papaya.js'); + + mockFs[newFile1] = `// Mango!`; + mockFs[newFile2] = `// Papaya!`; + + // Set up node crawler mock to return the new files + mockNodeCrawler.mockImplementationOnce( + async (options: $FlowFixMe) => { + const {rootDir} = options; + const changedFiles: Map = new Map(); + + // Return files found in the crawled subdirectory + changedFiles.set(path.relative(rootDir, newFile1), [ + 100, + 50, + 0, + null, + 0, + null, + ]); + changedFiles.set(path.relative(rootDir, newFile2), [ + 101, + 60, + 0, + null, + 0, + null, + ]); + + return { + changedFiles, + removedFiles: new Set(), + }; + }, + ); + + // Emit a recrawl event for the new directory + e.emitFileEvent({ + event: 'recrawl', + relativePath: 'tropical', + }); + + await waitForItToChange(fileMap); + + // Verify crawl was called with the correct directory + expect(mockNodeCrawler).toHaveBeenNthCalledWith( + 2, // Second call is the recrawl (first call is initial build) + expect.objectContaining({ + roots: [newDir], + }), + ); + }, + {config: {useWatchman: false}}, + ); + + fm_it( + 'recrawl event detects removed files from a moved-out directory', + async ({fileMap, hasteMap}) => { + const {fileSystem} = await fileMap.build(); + const fruitsRoot = path.join('/', 'project', 'fruits'); + const e = mockEmitters[fruitsRoot]; + + // Verify the file exists initially + const existingFile = path.join(fruitsRoot, 'Banana.js'); + expect(fileSystem.exists(existingFile)).toBe(true); + expect(hasteMap.getModule('Banana')).toBe(existingFile); + + // Set up node crawler mock to return the file as removed + mockNodeCrawler.mockImplementationOnce( + async (options: $FlowFixMe) => { + const {rootDir} = options; + const removedFiles: Set = new Set(); + removedFiles.add(path.relative(rootDir, existingFile)); + + return { + changedFiles: new Map(), + removedFiles, + }; + }, + ); + + // Emit a recrawl event (simulating directory being moved out) + e.emitFileEvent({ + event: 'recrawl', + relativePath: '', + }); + + const {changes} = await waitForItToChange(fileMap); + + // Verify deletion was emitted + expect(countFileChanges(changes)).toBe(1); + expect([...changes.removedFiles]).toHaveLength(1); + + // Verify file is no longer in the file system + expect(fileSystem.exists(existingFile)).toBe(false); + + // Verify haste map was updated + expect(hasteMap.getModule('Banana')).toBeNull(); + }, + {config: {useWatchman: false}}, + ); + + fm_it( + 'recrawl event detects both added and removed files', + async ({fileMap, hasteMap}) => { + const {fileSystem} = await fileMap.build(); + const fruitsRoot = path.join('/', 'project', 'fruits'); + const e = mockEmitters[fruitsRoot]; + + // Initial state + const existingFile = path.join(fruitsRoot, 'Pear.js'); + expect(fileSystem.exists(existingFile)).toBe(true); + + // New file to be added + const newFile = path.join(fruitsRoot, 'Kiwi.js'); + mockFs[newFile] = `// Kiwi!`; + + // Set up node crawler mock + mockNodeCrawler.mockImplementationOnce( + async (options: $FlowFixMe) => { + const {rootDir} = options; + const changedFiles: Map = new Map(); + const removedFiles: Set = new Set(); + + // Add new file + changedFiles.set(path.relative(rootDir, newFile), [ + 200, + 70, + 0, + null, + 0, + null, + ]); + + // Remove existing file + removedFiles.add(path.relative(rootDir, existingFile)); + + return { + changedFiles, + removedFiles, + }; + }, + ); + + e.emitFileEvent({ + event: 'recrawl', + relativePath: '', + }); + + const {changes} = await waitForItToChange(fileMap); + + // Verify both changes were emitted + expect(countFileChanges(changes)).toBe(2); + expect([...changes.addedFiles]).toHaveLength(1); + expect([...changes.removedFiles]).toHaveLength(1); + + // Verify file system state + expect(fileSystem.exists(newFile)).toBe(true); + expect(fileSystem.exists(existingFile)).toBe(false); + + // Verify haste map state + expect(hasteMap.getModule('Kiwi')).toBe(newFile); + expect(mockNodeCrawler).toHaveBeenCalled(); + }, + {config: {useWatchman: false}}, + ); + + fm_it( + 'recrawl event with no changes does not emit', + async ({fileMap}) => { + await fileMap.build(); + const fruitsRoot = path.join('/', 'project', 'fruits'); + const e = mockEmitters[fruitsRoot]; + + // Set up node crawler mock to return no changes + mockNodeCrawler.mockImplementationOnce(async () => ({ + changedFiles: new Map(), + removedFiles: new Set(), + })); + + const changeListener = jest.fn(); + fileMap.on('change', changeListener); + + e.emitFileEvent({ + event: 'recrawl', + relativePath: 'nonexistent', + }); + + // Wait for processing + await new Promise(resolve => setTimeout(resolve, 100)); + + // Verify crawl was called + expect(mockNodeCrawler).toHaveBeenCalled(); + + // Verify no change event was emitted (since no changes) + expect(changeListener).not.toHaveBeenCalled(); }, + {config: {useWatchman: false}}, ); }); }); diff --git a/packages/metro-file-map/src/cache/__tests__/DiskCacheManager-test.js b/packages/metro-file-map/src/cache/__tests__/DiskCacheManager-test.js index 7732a69a6c..a37b092593 100644 --- a/packages/metro-file-map/src/cache/__tests__/DiskCacheManager-test.js +++ b/packages/metro-file-map/src/cache/__tests__/DiskCacheManager-test.js @@ -105,7 +105,7 @@ describe('cacheManager', () => { let pluginCacheKey = 'foo'; const plugin: FileMapPlugin<> = { name: 'foo', - bulkUpdate() {}, + onChanged() {}, async initialize() {}, assertValid() {}, getSerializableSnapshot() { @@ -114,8 +114,6 @@ describe('cacheManager', () => { getWorker() { return null; }, - onNewOrModifiedFile() {}, - onRemovedFile() {}, getCacheKey() { return pluginCacheKey; }, diff --git a/packages/metro-file-map/src/crawlers/node/index.js b/packages/metro-file-map/src/crawlers/node/index.js index 2c087ba1af..e7aaabe7a7 100644 --- a/packages/metro-file-map/src/crawlers/node/index.js +++ b/packages/metro-file-map/src/crawlers/node/index.js @@ -10,9 +10,9 @@ */ import type { - CanonicalPath, Console, CrawlerOptions, + CrawlResult, FileData, IgnoreMatcher, } from '../../flow-types'; @@ -170,10 +170,9 @@ function findNative( }); } -export default async function nodeCrawl(options: CrawlerOptions): Promise<{ - removedFiles: Set, - changedFiles: FileData, -}> { +export default async function nodeCrawl( + options: CrawlerOptions, +): Promise { const { console, previousState, @@ -185,6 +184,7 @@ export default async function nodeCrawl(options: CrawlerOptions): Promise<{ perfLogger, roots, abortSignal, + subpath, } = options; abortSignal?.throwIfAborted(); @@ -199,7 +199,9 @@ export default async function nodeCrawl(options: CrawlerOptions): Promise<{ return new Promise((resolve, reject) => { const callback: Callback = fileData => { - const difference = previousState.fileSystem.getDifference(fileData); + const difference = previousState.fileSystem.getDifference(fileData, { + subpath, + }); perfLogger?.point('nodeCrawl_end'); diff --git a/packages/metro-file-map/src/crawlers/watchman/index.js b/packages/metro-file-map/src/crawlers/watchman/index.js index 4b011b6518..2e45eba592 100644 --- a/packages/metro-file-map/src/crawlers/watchman/index.js +++ b/packages/metro-file-map/src/crawlers/watchman/index.js @@ -13,10 +13,10 @@ import type {WatchmanClockSpec} from '../../flow-types'; import type { CanonicalPath, CrawlerOptions, + CrawlResult, FileData, FileMetadata, Path, - WatchmanClocks, } from '../../flow-types'; import type {WatchmanQueryResponse, WatchmanWatchResponse} from 'fb-watchman'; @@ -57,11 +57,7 @@ export default async function watchmanCrawl({ previousState, rootDir, roots, -}: CrawlerOptions): Promise<{ - changedFiles: FileData, - removedFiles: Set, - clocks: WatchmanClocks, -}> { +}: CrawlerOptions): Promise { abortSignal?.throwIfAborted(); const client = new watchman.Client(); diff --git a/packages/metro-file-map/src/flow-types.js b/packages/metro-file-map/src/flow-types.js index 7b57120045..44d05dea4d 100644 --- a/packages/metro-file-map/src/flow-types.js +++ b/packages/metro-file-map/src/flow-types.js @@ -21,7 +21,7 @@ export type BuildParameters = Readonly<{ extensions: ReadonlyArray, forceNodeFilesystemAPI: boolean, ignorePattern: RegExp, - plugins: ReadonlyArray>, + plugins: ReadonlyArray, retainAllFiles: boolean, rootDir: string, roots: ReadonlyArray, @@ -90,10 +90,16 @@ export type CacheManagerWriteOptions = Readonly<{ // - Real (no symlinks in path, though the path itself may be a symlink) export type CanonicalPath = string; -export type ChangeEvent = { +export type ChangedFileMetadata = Readonly<{ + isSymlink: boolean, + modifiedTime?: ?number, +}>; + +export type ChangeEvent = Readonly<{ logger: ?RootPerfLogger, - eventsQueue: EventsQueue, -}; + changes: ReadonlyFileSystemChanges>, + rootDir: string, +}>; export type ChangeEventMetadata = { modifiedTime: ?number, // Epoch ms @@ -119,8 +125,22 @@ export type CrawlerOptions = { rootDir: string, roots: ReadonlyArray, onStatus: (status: WatcherStatus) => void, + // Only consider files under this normalized subdirectory when computing + // removedFiles. If not provided, all files in the file system are considered. + subpath?: string, }; +export type CrawlResult = + | { + changedFiles: FileData, + removedFiles: Set, + clocks: WatchmanClocks, + } + | { + changedFiles: FileData, + removedFiles: Set, + }; + export type DependencyExtractor = { extract: ( content: string, @@ -150,20 +170,9 @@ export type WatcherStatus = export type DuplicatesSet = Map; export type DuplicatesIndex = Map>; -export type EventsQueue = Array<{ - filePath: Path, - metadata: ChangeEventMetadata, - type: string, -}>; - -export type FileMapDelta = Readonly<{ - removed: Iterable<[CanonicalPath, T]>, - addedOrModified: Iterable<[CanonicalPath, T]>, -}>; - export type FileMapPluginInitOptions< - SerializableState, - PerFileData = void, + +SerializableState, + +PerFileData = void, > = Readonly<{ files: Readonly<{ fileIterator( @@ -174,13 +183,13 @@ export type FileMapPluginInitOptions< ): Iterable<{ baseName: string, canonicalPath: string, - pluginData: ?PerFileData, + +pluginData: ?PerFileData, }>, lookup( mixedPath: string, ): | {exists: false} - | {exists: true, type: 'f', pluginData: PerFileData} + | {exists: true, type: 'f', +pluginData: PerFileData} | {exists: true, type: 'd'}, }>, pluginState: ?SerializableState, @@ -205,22 +214,22 @@ export type V8Serializable = | Readonly<{[key: string]: V8Serializable}>; export interface FileMapPlugin< - SerializableState: void | V8Serializable = void | V8Serializable, - PerFileData: void | V8Serializable = void | V8Serializable, + -SerializableState extends void | V8Serializable = void | V8Serializable, + -PerFileData extends void | V8Serializable = void | V8Serializable, > { +name: string; initialize( initOptions: FileMapPluginInitOptions, ): Promise; assertValid(): void; - bulkUpdate(delta: FileMapDelta): void; - getSerializableSnapshot(): SerializableState; - onRemovedFile(relativeFilePath: string, pluginData: ?PerFileData): void; - onNewOrModifiedFile(relativeFilePath: string, pluginData: ?PerFileData): void; + onChanged(changes: ReadonlyFileSystemChanges): void; + getSerializableSnapshot(): void | V8Serializable; getCacheKey(): string; getWorker(): ?FileMapPluginWorker; } +export type InputFileMapPlugin = FileMapPlugin; + export interface MetadataWorker { processFile( WorkerMessage, @@ -268,7 +277,23 @@ export type FileStats = Readonly<{ export interface FileSystem { exists(file: Path): boolean; getAllFiles(): Array; - getDifference(files: FileData): { + + /** + * Given a map of files, determine which of them are new or modified + * (changedFiles), and which of them are missing from the input + * (removedFiles), vs the current state of this instance of FileSystem. + */ + getDifference( + files: FileData, + options?: Readonly<{ + /** + * Only consider files under this subpath (which should be a directory) + * when computing removedFiles. If not provided, all files in the file + * system are considered. + */ + subpath?: string, + }>, + ): { changedFiles: FileData, removedFiles: Set, }; @@ -423,10 +448,39 @@ export type HasteMapItem = { }; export type HasteMapItemMetadata = [/* path */ string, /* type */ number]; +export interface FileSystemListener { + directoryAdded(canonicalPath: CanonicalPath): void; + directoryRemoved(canonicalPath: CanonicalPath): void; + + fileAdded(canonicalPath: CanonicalPath, data: FileMetadata): void; + fileModified( + canonicalPath: CanonicalPath, + oldData: FileMetadata, + newData: FileMetadata, + ): void; + fileRemoved(canonicalPath: CanonicalPath, data: FileMetadata): void; +} + +export interface ReadonlyFileSystemChanges<+T = FileMetadata> { + +addedDirectories: Iterable; + +removedDirectories: Iterable; + + +addedFiles: Iterable>; + +modifiedFiles: Iterable>; + +removedFiles: Iterable>; +} + export interface MutableFileSystem extends FileSystem { - remove(filePath: Path): ?FileMetadata; - addOrModify(filePath: Path, fileMetadata: FileMetadata): void; - bulkAddOrModify(addedOrModifiedFiles: FileData): void; + remove(filePath: Path, listener?: FileSystemListener): void; + addOrModify( + filePath: Path, + fileMetadata: FileMetadata, + listener?: FileSystemListener, + ): void; + bulkAddOrModify( + addedOrModifiedFiles: FileData, + listener?: FileSystemListener, + ): void; } export type Path = string; @@ -482,6 +536,12 @@ export type WatcherBackendChangeEvent = relativePath: string, root: string, metadata?: void, + }> + | Readonly<{ + event: 'recrawl', + clock?: ChangeEventClock, + relativePath: string, + root: string, }>; export type WatcherBackendOptions = Readonly<{ diff --git a/packages/metro-file-map/src/index.js b/packages/metro-file-map/src/index.js index 2f1c6507c5..113f85be1d 100644 --- a/packages/metro-file-map/src/index.js +++ b/packages/metro-file-map/src/index.js @@ -17,12 +17,13 @@ import type { CacheManagerFactory, CacheManagerFactoryOptions, CanonicalPath, + ChangedFileMetadata, ChangeEvent, ChangeEventClock, ChangeEventMetadata, Console, CrawlerOptions, - EventsQueue, + CrawlResult, FileData, FileMapPlugin, FileMapPluginWorker, @@ -31,6 +32,7 @@ import type { HasteMapData, HasteMapItem, HType, + InputFileMapPlugin, MutableFileSystem, Path, PerfLogger, @@ -44,6 +46,7 @@ import {DiskCacheManager} from './cache/DiskCacheManager'; import H from './constants'; import checkWatchmanCapabilities from './lib/checkWatchmanCapabilities'; import {FileProcessor} from './lib/FileProcessor'; +import {FileSystemChangeAggregator} from './lib/FileSystemChangeAggregator'; import normalizePathSeparatorsToPosix from './lib/normalizePathSeparatorsToPosix'; import normalizePathSeparatorsToSystem from './lib/normalizePathSeparatorsToSystem'; import {RootPathUtils} from './lib/RootPathUtils'; @@ -52,7 +55,6 @@ import {Watcher} from './Watcher'; import EventEmitter from 'events'; import {promises as fsPromises} from 'fs'; import invariant from 'invariant'; -import nullthrows from 'nullthrows'; import * as path from 'path'; import {performance} from 'perf_hooks'; @@ -69,6 +71,7 @@ export type { FileSystem, HasteMapData, HasteMapItem, + InputFileMapPlugin, }; export type InputOptions = Readonly<{ @@ -77,7 +80,7 @@ export type InputOptions = Readonly<{ extensions: ReadonlyArray, forceNodeFilesystemAPI?: ?boolean, ignorePattern?: ?RegExp, - plugins?: ReadonlyArray, + plugins?: ReadonlyArray, retainAllFiles: boolean, rootDir: string, roots: ReadonlyArray, @@ -111,12 +114,24 @@ type InternalOptions = Readonly<{ watchmanDeferStates: ReadonlyArray, }>; -// $FlowFixMe[unclear-type] Plugin types cannot be known statically -type AnyFileMapPlugin = FileMapPlugin; type IndexedPlugin = Readonly<{ - plugin: AnyFileMapPlugin, + // $FlowFixMe[unclear-type] Plugin types cannot be known statically + plugin: FileMapPlugin, dataIdx: ?number, }>; +type InternalEnqueuedEvent = Readonly< + | { + clock: ?ChangeEventClock, + relativeFilePath: string, + metadata: FileMetadata, + type: 'touch', + } + | { + clock: ?ChangeEventClock, + relativeFilePath: string, + type: 'delete', + }, +>; export {DiskCacheManager} from './cache/DiskCacheManager'; export {default as DependencyPlugin} from './plugins/DependencyPlugin'; @@ -421,7 +436,7 @@ export default class FileMap extends EventEmitter { }; }, fileIterator: opts => - mapIterator( + mapIterable( fileSystem.metadataIterator(opts), ({baseName, canonicalPath, metadata}) => ({ baseName, @@ -437,7 +452,13 @@ export default class FileMap extends EventEmitter { ]); // Update `fileSystem` and plugins based on the file delta. - await this.#applyFileDelta(fileSystem, plugins, fileDelta); + const actualChanges = await this.#applyFileDelta( + fileSystem, + plugins, + fileDelta, + ); + + const changeSize = actualChanges.getSize(); // Validate plugins before persisting them. plugins.forEach(({plugin}) => plugin.assertValid()); @@ -447,14 +468,9 @@ export default class FileMap extends EventEmitter { fileSystem, watchmanClocks, plugins, - fileDelta.changedFiles, - fileDelta.removedFiles, - ); - debug( - 'Finished mapping files (%d changes, %d removed).', - fileDelta.changedFiles.size, - fileDelta.removedFiles.size, + changeSize > 0, ); + debug('Finished mapping files (%d changes).', changeSize); await this.#watch(fileSystem, watchmanClocks, plugins); return {fileSystem}; @@ -492,11 +508,7 @@ export default class FileMap extends EventEmitter { */ async #buildFileDelta( previousState: CrawlerOptions['previousState'], - ): Promise<{ - removedFiles: Set, - changedFiles: FileData, - clocks?: WatchmanClocks, - }> { + ): Promise { this.#startupPerfLogger?.point('buildFileDelta_start'); const { @@ -540,10 +552,9 @@ export default class FileMap extends EventEmitter { watcher.on('status', status => this.emit('status', status)); - return watcher.crawl().then(result => { - this.#startupPerfLogger?.point('buildFileDelta_end'); - return result; - }); + const result = await watcher.crawl(); + this.#startupPerfLogger?.point('buildFileDelta_end'); + return result; } #maybeReadLink(normalPath: Path, fileMetadata: FileMetadata): ?Promise { @@ -568,25 +579,20 @@ export default class FileMap extends EventEmitter { removedFiles: ReadonlySet, clocks?: WatchmanClocks, }>, - ): Promise { + ): Promise { this.#startupPerfLogger?.point('applyFileDelta_start'); const {changedFiles, removedFiles} = delta; this.#startupPerfLogger?.point('applyFileDelta_preprocess_start'); - const missingFiles: Set = new Set(); - // Remove files first so that we don't mistake moved modules // modules as duplicates. this.#startupPerfLogger?.point('applyFileDelta_remove_start'); - const removed: Array<[string, FileMetadata]> = []; + const changeAggregator = new FileSystemChangeAggregator(); for (const relativeFilePath of removedFiles) { - const metadata = fileSystem.remove(relativeFilePath); - if (metadata) { - removed.push([relativeFilePath, metadata]); - } + fileSystem.remove(relativeFilePath, changeAggregator); } this.#startupPerfLogger?.point('applyFileDelta_remove_end'); - const readLinkPromises = []; + const readLinkPromises: Array> = []; const readLinkErrors: Array<{ normalFilePath: string, error: Error & {code?: string}, @@ -606,9 +612,9 @@ export default class FileMap extends EventEmitter { const maybeReadLink = this.#maybeReadLink(normalFilePath, fileData); if (maybeReadLink) { readLinkPromises.push( - maybeReadLink.catch(error => - readLinkErrors.push({normalFilePath, error}), - ), + maybeReadLink.catch(error => { + readLinkErrors.push({normalFilePath, error}); + }), ); } } @@ -647,38 +653,32 @@ export default class FileMap extends EventEmitter { /* $FlowFixMe[incompatible-type] Error exposed after improved typing of * Array.{includes,indexOf,lastIndexOf} */ if (['ENOENT', 'EACCESS'].includes(error.code)) { - missingFiles.add(normalFilePath); + delta.changedFiles.delete(normalFilePath); + fileSystem.remove(normalFilePath, changeAggregator); } else { // Anything else is fatal. throw error; } } - for (const relativeFilePath of missingFiles) { - changedFiles.delete(relativeFilePath); - const metadata = fileSystem.remove(relativeFilePath); - if (metadata) { - removed.push([relativeFilePath, metadata]); - } - } + this.#startupPerfLogger?.point('applyFileDelta_missing_end'); this.#startupPerfLogger?.point('applyFileDelta_add_start'); - fileSystem.bulkAddOrModify(changedFiles); + fileSystem.bulkAddOrModify(changedFiles, changeAggregator); this.#startupPerfLogger?.point('applyFileDelta_add_end'); this.#startupPerfLogger?.point('applyFileDelta_updatePlugins_start'); - plugins.forEach(({plugin, dataIdx}) => { - const mapFn: ([CanonicalPath, FileMetadata]) => [CanonicalPath, unknown] = - dataIdx != null - ? ([relativePath, fileData]) => [relativePath, fileData[dataIdx]] - : ([relativePath, fileData]) => [relativePath, null]; - plugin.bulkUpdate({ - addedOrModified: mapIterator(changedFiles.entries(), mapFn), - removed: mapIterator(removed.values(), mapFn), - }); + this.#plugins.forEach(({plugin, dataIdx}) => { + plugin.onChanged( + changeAggregator.getMappedView( + dataIdx != null ? metadata => metadata[dataIdx] : () => null, + ), + ); }); this.#startupPerfLogger?.point('applyFileDelta_updatePlugins_end'); this.#startupPerfLogger?.point('applyFileDelta_end'); + + return changeAggregator; } /** @@ -688,8 +688,7 @@ export default class FileMap extends EventEmitter { fileSystem: FileSystem, clocks: WatchmanClocks, plugins: ReadonlyArray, - changed: FileData, - removed: Set, + changedSinceCacheRead: boolean, ) { this.#startupPerfLogger?.point('persist_start'); await this.#cacheManager.write( @@ -704,7 +703,7 @@ export default class FileMap extends EventEmitter { ), }), { - changedSinceCacheRead: changed.size + removed.size > 0, + changedSinceCacheRead, eventSource: { onChange: cb => { // Inform the cache about changes to internal state, including: @@ -743,20 +742,68 @@ export default class FileMap extends EventEmitter { const hasWatchedExtension = (filePath: string) => this.#options.extensions.some(ext => filePath.endsWith(ext)); - let changeQueue: Promise = Promise.resolve(); let nextEmit: ?{ - eventsQueue: EventsQueue, + events: Array, firstEventTimestamp: number, firstEnqueuedTimestamp: number, } = null; const emitChange = () => { - if (nextEmit == null || nextEmit.eventsQueue.length === 0) { + if (nextEmit == null) { // Nothing to emit return; } - const {eventsQueue, firstEventTimestamp, firstEnqueuedTimestamp} = - nextEmit; + const {events, firstEventTimestamp, firstEnqueuedTimestamp} = nextEmit; + + const changeAggregator = new FileSystemChangeAggregator(); + + // Process a sequence of events. Note that preserving ordering is + // important here - a file may be both removed and added in the same + // batch. + // `changeAggregator` flattens this over time into the net change from + // this sequence. + for (const event of events) { + const {relativeFilePath, clock} = event; + if (event.type === 'delete') { + fileSystem.remove(relativeFilePath, changeAggregator); + } else { + fileSystem.addOrModify( + relativeFilePath, + event.metadata, + changeAggregator, + ); + } + this.#updateClock(clocks, clock); + } + + const changeSize = changeAggregator.getSize(); + + if (changeSize === 0) { + // We had events, but they've exactly cancelled each other out, reset + // so that timers are correct for the next change. + nextEmit = null; + return; + } + + const _netChange = changeAggregator.getView(); + this.#plugins.forEach(({plugin, dataIdx}) => { + plugin.onChanged( + changeAggregator.getMappedView( + dataIdx != null ? metadata => metadata[dataIdx] : () => null, + ), + ); + }); + + const toPublicMetadata = ( + metadata: Readonly, + ): ChangedFileMetadata => ({ + isSymlink: metadata[H.SYMLINK] !== 0, + modifiedTime: metadata[H.MTIME] ?? null, + }); + + const changesWithMetadata = + changeAggregator.getMappedView(toPublicMetadata); + const hmrPerfLogger = this.#options.perfLoggerFactory?.('HMR', { key: this.#getNextChangeID(), }); @@ -766,21 +813,24 @@ export default class FileMap extends EventEmitter { timestamp: firstEnqueuedTimestamp, }); hmrPerfLogger.point('waitingForChangeInterval_end'); - hmrPerfLogger.annotate({ - int: {eventsQueueLength: eventsQueue.length}, - }); + hmrPerfLogger.annotate({int: {changeSize}}); hmrPerfLogger.point('fileChange_start'); } const changeEvent: ChangeEvent = { - eventsQueue, + changes: changesWithMetadata, logger: hmrPerfLogger, + rootDir: this.#options.rootDir, }; this.emit('change', changeEvent); nextEmit = null; }; + let changeQueue: Promise = Promise.resolve(); + const onChange = (change: WatcherBackendChangeEvent) => { + // Recrawl events bypass normal filtering - they trigger a full subdirectory scan if ( + change.event !== 'recrawl' && change.metadata && // Ignore all directory events (change.metadata.type === 'd' || @@ -806,73 +856,38 @@ export default class FileMap extends EventEmitter { const relativeFilePath = this.#pathUtils.absoluteToNormal(absoluteFilePath); - const linkStats = fileSystem.linkStats(relativeFilePath); - - // The file has been accessed, not modified. If the modified time is - // null, then it is assumed that the watcher does not have capabilities - // to detect modified time, and change processing proceeds. - if ( - change.event === 'touch' && - linkStats != null && - change.metadata.modifiedTime != null && - linkStats.modifiedTime === change.metadata.modifiedTime - ) { - return; - } - - // Emitted events, unlike memoryless backend events, specify 'add' or - // 'change' instead of 'touch'. - const eventTypeToEmit = - change.event === 'touch' - ? linkStats == null - ? 'add' - : 'change' - : 'delete'; const onChangeStartTime = performance.timeOrigin + performance.now(); + const enqueueEvent = (event: InternalEnqueuedEvent) => { + nextEmit ??= { + events: [], + firstEnqueuedTimestamp: performance.timeOrigin + performance.now(), + firstEventTimestamp: onChangeStartTime, + }; + nextEmit.events.push(event); + }; + changeQueue = changeQueue .then(async () => { // If we get duplicate events for the same file, ignore them. if ( nextEmit != null && - nextEmit.eventsQueue.find( + nextEmit.events.find( event => - event.type === eventTypeToEmit && - event.filePath === absoluteFilePath && + event.type === change.event && + event.relativeFilePath === relativeFilePath && ((!event.metadata && !change.metadata) || (event.metadata && change.metadata && - event.metadata.modifiedTime != null && + event.metadata[H.MTIME] != null && change.metadata.modifiedTime != null && - event.metadata.modifiedTime === - change.metadata.modifiedTime)), + event.metadata[H.MTIME] === change.metadata.modifiedTime)), ) ) { return null; } - const linkStats = fileSystem.linkStats(relativeFilePath); - - const enqueueEvent = (metadata: ChangeEventMetadata) => { - const event = { - filePath: absoluteFilePath, - metadata, - type: eventTypeToEmit, - }; - if (nextEmit == null) { - nextEmit = { - eventsQueue: [event], - firstEnqueuedTimestamp: - performance.timeOrigin + performance.now(), - firstEventTimestamp: onChangeStartTime, - }; - } else { - nextEmit.eventsQueue.push(event); - } - return null; - }; - // If the file was added or modified, // parse it and update the file map. if (change.event === 'touch') { @@ -902,17 +917,12 @@ export default class FileMap extends EventEmitter { }, ); } - fileSystem.addOrModify(relativeFilePath, fileMetadata); - this.#updateClock(clocks, change.clock); - plugins.forEach(({plugin, dataIdx}) => - dataIdx != null - ? plugin.onNewOrModifiedFile( - relativeFilePath, - fileMetadata[dataIdx], - ) - : plugin.onNewOrModifiedFile(relativeFilePath), - ); - enqueueEvent(change.metadata); + enqueueEvent({ + clock: change.clock, + relativeFilePath, + metadata: fileMetadata, + type: change.event, + }); } catch (e) { if (!['ENOENT', 'EACCESS'].includes(e.code)) { throw e; @@ -925,26 +935,68 @@ export default class FileMap extends EventEmitter { // point. } } else if (change.event === 'delete') { - if (linkStats == null) { - // Don't emit deletion events for files we weren't retaining. - // This is expected for deletion of an ignored file. + enqueueEvent({ + clock: change.clock, + relativeFilePath, + type: 'delete', + }); + } else if (change.event === 'recrawl') { + // Recrawl event: flush pending changes and re-crawl the directory + emitChange(); + + // The relativePath is relative to the watcher root (change.root), + // but we need a path relative to rootDir for the recrawl. + const absoluteDirPath = path.join( + change.root, + normalizePathSeparatorsToSystem(change.relativePath), + ); + const subpath = this.#pathUtils.absoluteToNormal(absoluteDirPath); + + // Crawl the specific subdirectory + const watcher = this.#watcher; + invariant(watcher != null, 'Watcher must be initialized'); + const crawlResult = await watcher.recrawl(subpath, fileSystem); + + // Skip if no changes + if ( + crawlResult.changedFiles.size === 0 && + crawlResult.removedFiles.size === 0 + ) { return null; } - // We've already checked linkStats != null above, so the file - // exists in the file map and remove should always return metadata. - const metadata = nullthrows(fileSystem.remove(relativeFilePath)); - this.#updateClock(clocks, change.clock); - plugins.forEach(({plugin, dataIdx}) => - dataIdx != null - ? plugin.onRemovedFile(relativeFilePath, metadata[dataIdx]) - : plugin.onRemovedFile(relativeFilePath), + + // Reuse the same batch processing logic as build() + const recrawlChangeAggregator = await this.#applyFileDelta( + fileSystem, + this.#plugins, + crawlResult, ); - enqueueEvent({ - modifiedTime: null, - size: null, - type: linkStats.fileType, + // Update clock if provided + this.#updateClock(clocks, change.clock); + + // Skip emit if no changes after processing + if (recrawlChangeAggregator.getSize() === 0) { + return null; + } + + // Emit changes directly + const toPublicMetadata = ( + metadata: Readonly, + ): ChangedFileMetadata => ({ + isSymlink: metadata[H.SYMLINK] !== 0, + modifiedTime: metadata[H.MTIME] ?? null, }); + + const changesWithMetadata = + recrawlChangeAggregator.getMappedView(toPublicMetadata); + + const changeEvent: ChangeEvent = { + changes: changesWithMetadata, + logger: null, + rootDir: this.#options.rootDir, + }; + this.emit('change', changeEvent); } else { throw new Error( `metro-file-map: Unrecognized event type from watcher: ${change.event}`, @@ -1055,11 +1107,9 @@ export default class FileMap extends EventEmitter { } // TODO: Replace with it.map() from Node 22+ -const mapIterator: (Iterator, (T) => S) => Iterable = (it, fn) => - 'map' in it - ? it.map(fn) - : (function* mapped() { - for (const item of it) { - yield fn(item); - } - })(); +const mapIterable: (Iterable, (T) => S) => Iterator = (it, fn) => + (function* mapped() { + for (const item of it) { + yield fn(item); + } + })(); diff --git a/packages/metro-file-map/src/lib/FileSystemChangeAggregator.js b/packages/metro-file-map/src/lib/FileSystemChangeAggregator.js new file mode 100644 index 0000000000..0f00a49b7e --- /dev/null +++ b/packages/metro-file-map/src/lib/FileSystemChangeAggregator.js @@ -0,0 +1,143 @@ +/** + * 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 + */ + +import type { + CanonicalPath, + FileMetadata, + FileSystemListener, + ReadonlyFileSystemChanges, +} from '../flow-types'; + +export class FileSystemChangeAggregator implements FileSystemListener { + // Mutually exclusive with removedDirectories + +#addedDirectories: Set = new Set(); + // Mutually exclusive with addedDirectories + +#removedDirectories: Set = new Set(); + + // Mutually exclusive with modified and removed files + +#addedFiles: Map = new Map(); + // Mutually exclusive with added and removed files + +#modifiedFiles: Map = new Map(); + // Mutually exclusive with added and modified files + +#removedFiles: Map = new Map(); + + // Removed files must be paired with the file's metadata the last time it was + // observable by consumers - ie, immediately *before* this batch. To report + // this accurately with minimal overhead, we'll note the current metadata of + // a file the first time it is modified or removed within a batch. If it is + // re-added, modified and removed again, we still have the initial metadata. + // This is particularly important if, say, a regular file is replaced by a + // symlink, or vice-versa. + +#initialMetadata: Map = new Map(); + + directoryAdded(canonicalPath: CanonicalPath): void { + // Only add to newDirectories if this directory wasn't previously removed + // (i.e., it's truly new). If it was removed and re-added, the net effect + // is no directory change. + if (!this.#removedDirectories.delete(canonicalPath)) { + this.#addedDirectories.add(canonicalPath); + } + } + + directoryRemoved(canonicalPath: CanonicalPath): void { + if (!this.#addedDirectories.delete(canonicalPath)) { + this.#removedDirectories.add(canonicalPath); + } + } + + fileAdded(canonicalPath: CanonicalPath, data: FileMetadata): void { + if (this.#removedFiles.delete(canonicalPath)) { + // File was removed then re-added in the same batch - treat as modification + this.#modifiedFiles.set(canonicalPath, data); + } else { + // New file + this.#addedFiles.set(canonicalPath, data); + } + } + + fileModified( + canonicalPath: CanonicalPath, + oldData: FileMetadata, + newData: FileMetadata, + ): void { + if (this.#addedFiles.has(canonicalPath)) { + // File did not exist before this batch. Further modification only + // updates metadata + this.#addedFiles.set(canonicalPath, newData); + } else { + if (!this.#initialMetadata.has(canonicalPath)) { + this.#initialMetadata.set(canonicalPath, oldData); + } + this.#modifiedFiles.set(canonicalPath, newData); + } + } + + fileRemoved(canonicalPath: CanonicalPath, data: FileMetadata): void { + // Check if this file was added in the same batch + if (!this.#addedFiles.delete(canonicalPath)) { + let initialData = this.#initialMetadata.get(canonicalPath); + if (!initialData) { + initialData = data; + this.#initialMetadata.set(canonicalPath, initialData); + } + + // File was not added in this batch, so add to removed with last metadata + this.#modifiedFiles.delete(canonicalPath); + this.#removedFiles.set(canonicalPath, initialData); + } + // else: File was added then removed in the same batch - no net change + } + + getSize(): number { + return ( + this.#addedDirectories.size + + this.#removedDirectories.size + + this.#addedFiles.size + + this.#modifiedFiles.size + + this.#removedFiles.size + ); + } + + getView(): ReadonlyFileSystemChanges { + return { + addedDirectories: this.#addedDirectories, + removedDirectories: this.#removedDirectories, + addedFiles: this.#addedFiles, + modifiedFiles: this.#modifiedFiles, + removedFiles: this.#removedFiles, + }; + } + + getMappedView( + metadataMapFn: (metadata: FileMetadata) => T, + ): ReadonlyFileSystemChanges { + return { + addedDirectories: this.#addedDirectories, + removedDirectories: this.#removedDirectories, + addedFiles: mapIterable(this.#addedFiles, metadataMapFn), + modifiedFiles: mapIterable(this.#modifiedFiles, metadataMapFn), + removedFiles: mapIterable(this.#removedFiles, metadataMapFn), + }; + } +} + +function mapIterable( + map: Map, + metadataMapFn: (metadata: FileMetadata) => T, +): Iterable> { + return { + *[Symbol.iterator](): Iterator> { + for (const [path, metadata] of map) { + yield [path, metadataMapFn(metadata)]; + } + }, + }; +} diff --git a/packages/metro-file-map/src/lib/TreeFS.js b/packages/metro-file-map/src/lib/TreeFS.js index aa3b7a2718..7b1691ae2c 100644 --- a/packages/metro-file-map/src/lib/TreeFS.js +++ b/packages/metro-file-map/src/lib/TreeFS.js @@ -13,6 +13,7 @@ import type { FileData, FileMetadata, FileStats, + FileSystemListener, LookupResult, MutableFileSystem, Path, @@ -156,16 +157,45 @@ export default class TreeFS implements MutableFileSystem { return (fileMetadata && fileMetadata[H.SIZE]) ?? null; } - getDifference(files: FileData): { + getDifference( + files: FileData, + options?: Readonly<{ + // Only consider files under this normal subdirectory when computing + // removedFiles. If not provided, all files in the file system are + // considered. + subpath?: string, + }>, + ): { changedFiles: FileData, removedFiles: Set, } { const changedFiles: FileData = new Map(files); const removedFiles: Set = new Set(); - for (const {canonicalPath, metadata} of this.metadataIterator({ - includeNodeModules: true, - includeSymlinks: true, - })) { + const subpath = options?.subpath; + + // If a subpath is specified, start iteration from that node + let rootNode: DirectoryNode = this.#rootNode; + let prefix: string = ''; + if (subpath != null && subpath !== '') { + const lookupResult = this.#lookupByNormalPath(subpath, { + followLeaf: true, + }); + if (!lookupResult.exists || !isDirectory(lookupResult.node)) { + // Directory doesn't exist, nothing to compare - all files are new + return {changedFiles, removedFiles}; + } + rootNode = lookupResult.node; + prefix = lookupResult.canonicalPath; + } + + for (const {canonicalPath, metadata} of this.#metadataIterator( + rootNode, + { + includeNodeModules: true, + includeSymlinks: true, + }, + prefix, + )) { const newMetadata = files.get(canonicalPath); if (newMetadata) { if (isRegularFile(newMetadata) !== isRegularFile(metadata)) { @@ -378,11 +408,16 @@ export default class TreeFS implements MutableFileSystem { } } - addOrModify(mixedPath: Path, metadata: FileMetadata): void { + addOrModify( + mixedPath: Path, + metadata: FileMetadata, + changeListener?: FileSystemListener, + ): void { const normalPath = this.#normalizePath(mixedPath); // Walk the tree to find the *real* path of the parent node, creating // directories as we need. const parentDirNode = this.#lookupByNormalPath(path.dirname(normalPath), { + changeListener, makeDirectories: true, }); if (!parentDirNode.exists) { @@ -394,10 +429,13 @@ export default class TreeFS implements MutableFileSystem { const canonicalPath = this.#normalizePath( parentDirNode.canonicalPath + path.sep + path.basename(normalPath), ); - this.bulkAddOrModify(new Map([[canonicalPath, metadata]])); + this.bulkAddOrModify(new Map([[canonicalPath, metadata]]), changeListener); } - bulkAddOrModify(addedOrModifiedFiles: FileData): void { + bulkAddOrModify( + addedOrModifiedFiles: FileData, + changeListener?: FileSystemListener, + ): void { // Optimisation: Bulk FileData are typically clustered by directory, so we // optimise for that case by remembering the last directory we looked up. // Experiments with large result sets show this to be significantly (~30%) @@ -413,6 +451,7 @@ export default class TreeFS implements MutableFileSystem { if (directoryNode == null || dirname !== lastDir) { const lookup = this.#lookupByNormalPath(dirname, { + changeListener, followLeaf: false, makeDirectories: true, }); @@ -433,24 +472,48 @@ export default class TreeFS implements MutableFileSystem { lastDir = dirname; directoryNode = lookup.node; } + if (changeListener != null) { + const existingNode = directoryNode.get(basename); + if (existingNode != null) { + invariant( + !isDirectory(existingNode), + 'Detected addition or modification of file %s, but it is tracked as a non-empty directory', + normalPath, + ); + // File already exists - this is a modification + changeListener.fileModified(normalPath, existingNode, metadata); + } else { + // New file + changeListener.fileAdded(normalPath, metadata); + } + } directoryNode.set(basename, metadata); } } - remove(mixedPath: Path): ?FileMetadata { + remove(mixedPath: Path, changeListener?: FileSystemListener): void { const normalPath = this.#normalizePath(mixedPath); const result = this.#lookupByNormalPath(normalPath, {followLeaf: false}); if (!result.exists) { - return null; + return; } const {parentNode, canonicalPath, node} = result; if (isDirectory(node) && node.size > 0) { - throw new Error( - `TreeFS: remove called on a non-empty directory: ${mixedPath}`, - ); + for (const basename of node.keys()) { + this.remove(canonicalPath + path.sep + basename, changeListener); + } + // Removing the last file will delete this directory + return; } if (parentNode != null) { + if (changeListener != null) { + if (isDirectory(node)) { + changeListener.directoryRemoved(canonicalPath); + } else { + changeListener.fileRemoved(canonicalPath, node); + } + } parentNode.delete(path.basename(canonicalPath)); if (parentNode.size === 0 && parentNode !== this.#rootNode) { // NB: This isn't the most efficient algorithm - in the case of @@ -458,10 +521,9 @@ export default class TreeFS implements MutableFileSystem { // that's not expected to be a case common enough to justify // implementation complexity, or slowing down more common uses of // _lookupByNormalPath. - this.remove(path.dirname(canonicalPath)); + this.remove(path.dirname(canonicalPath), changeListener); } } - return isDirectory(node) ? null : node; } /** @@ -492,6 +554,10 @@ export default class TreeFS implements MutableFileSystem { // be added. Omit for performance if not needed. collectLinkPaths?: ?Set, + // Low-level callbacks called on mutations of TreeFS data. + // Omit for performance if not needed. + changeListener?: FileSystemListener, + // Like lstat vs stat, whether to follow a symlink at the basename of // the given path, or return the details of the symlink itself. followLeaf?: boolean, @@ -541,7 +607,8 @@ export default class TreeFS implements MutableFileSystem { // null. let ancestorOfRootIdx: ?number = opts.start?.ancestorOfRootIdx ?? 0; - const collectAncestors = opts.collectAncestors; + const {collectAncestors, changeListener} = opts; + // Used only when collecting ancestors, to avoid double-counting nodes and // paths when traversing a symlink takes us back to rootNode and out again. // This tracks the first character of the first segment not already @@ -583,6 +650,12 @@ export default class TreeFS implements MutableFileSystem { } segmentNode = new Map(); if (opts.makeDirectories === true) { + if (changeListener != null) { + const canonicalPath = isLastSegment + ? targetNormalPath + : targetNormalPath.slice(0, fromIdx - 1); + changeListener.directoryAdded(canonicalPath); + } parentNode.set(segmentName, segmentNode); } } diff --git a/packages/metro-file-map/src/lib/__tests__/FileSystemChangeAggregator-test.js b/packages/metro-file-map/src/lib/__tests__/FileSystemChangeAggregator-test.js new file mode 100644 index 0000000000..cb5656c676 --- /dev/null +++ b/packages/metro-file-map/src/lib/__tests__/FileSystemChangeAggregator-test.js @@ -0,0 +1,100 @@ +/** + * 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 + */ + +import type {FileMetadata} from '../../flow-types'; + +import {FileSystemChangeAggregator} from '../FileSystemChangeAggregator'; + +let aggregator: FileSystemChangeAggregator; + +beforeEach(() => { + aggregator = new FileSystemChangeAggregator(); +}); + +const FOO = 'foo.js'; + +test('removing, adding, modifying and removing a file records initial data', () => { + aggregator.fileRemoved(FOO, makeData(0)); + aggregator.fileAdded(FOO, makeData(1)); + aggregator.fileModified(FOO, makeData(1), makeData(2)); + aggregator.fileRemoved(FOO, makeData(2)); + const changes = getData(aggregator); + expect(changes.removedFiles.size).toBe(1); + expect(changes.removedFiles.get(FOO)).toEqual(makeData(0)); +}); + +test('modifying then removing a file records initial data', () => { + aggregator.fileModified(FOO, makeData(0), makeData(1)); + aggregator.fileRemoved(FOO, makeData(1)); + const changes = getData(aggregator); + expect(changes.removedFiles.size).toBe(1); + expect(changes.modifiedFiles.size).toBe(0); + expect(changes.removedFiles.get(FOO)).toEqual(makeData(0)); +}); + +test('adding, modifying then removing a file records empty changes', () => { + aggregator.fileAdded(FOO, makeData(0)); + aggregator.fileModified(FOO, makeData(0), makeData(1)); + aggregator.fileRemoved(FOO, makeData(1)); + const changes = getData(aggregator); + expect(changes.addedFiles.size).toBe(0); + expect(changes.modifiedFiles.size).toBe(0); + expect(changes.removedFiles.size).toBe(0); +}); + +afterEach(() => { + // assert mutual exclusivity + const changes = aggregator.getView(); + const addedDirectories = new Set(changes.addedDirectories); + const removedDirectories = new Set(changes.removedDirectories); + const addedFilePaths = new Set( + Array.from(changes.addedFiles, ([path]) => path), + ); + const modifiedFilePaths = new Set( + Array.from(changes.modifiedFiles, ([path]) => path), + ); + const removedFilePaths = new Set( + Array.from(changes.removedFiles, ([path]) => path), + ); + for (const dir of addedDirectories) { + expect(removedDirectories.has(dir)).toBe(false); + } + for (const dir of removedDirectories) { + expect(addedDirectories.has(dir)).toBe(false); + } + for (const path of addedFilePaths) { + expect(modifiedFilePaths.has(path)).toBe(false); + expect(removedFilePaths.has(path)).toBe(false); + } + for (const path of modifiedFilePaths) { + expect(addedFilePaths.has(path)).toBe(false); + expect(removedFilePaths.has(path)).toBe(false); + } + for (const path of removedFilePaths) { + expect(addedFilePaths.has(path)).toBe(false); + expect(modifiedFilePaths.has(path)).toBe(false); + } +}); + +function makeData(mtime: number = 0): FileMetadata { + return [mtime, 1, 0, null, 0]; +} + +function getData(aggregator: FileSystemChangeAggregator) { + const view = aggregator.getView(); + return { + addedDirectories: new Set(view.addedDirectories), + removedDirectories: new Set(view.removedDirectories), + addedFiles: new Map(Array.from(view.addedFiles, ([k, v]) => [k, v])), + modifiedFiles: new Map(Array.from(view.modifiedFiles, ([k, v]) => [k, v])), + removedFiles: new Map(Array.from(view.removedFiles, ([k, v]) => [k, v])), + }; +} diff --git a/packages/metro-file-map/src/lib/__tests__/TreeFS-test.js b/packages/metro-file-map/src/lib/__tests__/TreeFS-test.js index 2309a3092f..a65eab4293 100644 --- a/packages/metro-file-map/src/lib/__tests__/TreeFS-test.js +++ b/packages/metro-file-map/src/lib/__tests__/TreeFS-test.js @@ -9,7 +9,12 @@ * @oncall react_native */ -import type {CanonicalPath, FileData, FileMetadata} from '../../flow-types'; +import type { + CanonicalPath, + FileData, + FileMetadata, + FileSystemListener, +} from '../../flow-types'; import type TreeFSType from '../TreeFS'; import H from '../../constants'; @@ -300,6 +305,81 @@ describe.each([['win32'], ['posix']])('TreeFS on %s', platform => { ]), }); }); + + test('with subpath only considers files under that path', () => { + // Create a FileData that only includes files under 'foo' + const newFiles: FileData = new Map([ + [p('foo/another.js'), [124, 0, 0, null, 0, null]], // changed mtime + // foo/link-to-bar.js and others under foo are missing -> should be in removedFiles + ]); + + // Without subpath, all files not in newFiles would be removed + // But with subpath='foo', only files under foo are considered + expect(tfs.getDifference(newFiles, {subpath: p('foo')})).toEqual({ + changedFiles: new Map([ + [p('foo/another.js'), [124, 0, 0, null, 0, null]], + ]), + removedFiles: new Set([ + p('foo/owndir'), + p('foo/link-to-bar.js'), + p('foo/link-to-another.js'), + ]), + }); + }); + + test('with subpath detects new files under that path', () => { + // Verify that new files are detected and existing files (under the + // subdirectory) that are not in newFiles appear in removedFiles + const newFiles: FileData = new Map([ + [p('foo/another.js'), [123, 2, 0, null, 0, 'another']], // unchanged + [p('foo/new-file.js'), [456, 0, 0, null, 0, null]], // new file + ]); + + const result = tfs.getDifference(newFiles, { + subpath: p('foo'), + }); + + // New file should be in changedFiles + expect(result.changedFiles.has(p('foo/new-file.js'))).toBe(true); + + // Files not in newFiles should be in removedFiles + expect(result.removedFiles).toEqual( + new Set([ + p('foo/owndir'), + p('foo/link-to-bar.js'), + p('foo/link-to-another.js'), + ]), + ); + + // Files outside the subdirectory should NOT be affected + expect(result.removedFiles.has(p('bar.js'))).toBe(false); + expect(result.removedFiles.has(p('../outside/external.js'))).toBe(false); + }); + + test('with subpath for non-existent directory returns all as new', () => { + const newFiles: FileData = new Map([ + [p('nonexistent/file.js'), [123, 0, 0, null, 0, null]], + ]); + + // Directory doesn't exist, so nothing to compare - all files are new + expect(tfs.getDifference(newFiles, {subpath: p('nonexistent')})).toEqual({ + changedFiles: new Map([ + [p('nonexistent/file.js'), [123, 0, 0, null, 0, null]], + ]), + removedFiles: new Set(), + }); + }); + + test('with empty subpath behaves like no subdirectory specified', () => { + const newFiles: FileData = new Map([ + [p('foo/another.js'), [123, 0, 0, null, 0, null]], + ]); + + const withEmpty = tfs.getDifference(newFiles, {subpath: ''}); + const withUndefined = tfs.getDifference(newFiles); + + expect(withEmpty).toEqual(withUndefined); + }); }); describe('hierarchicalLookup', () => { @@ -771,16 +851,16 @@ describe.each([['win32'], ['posix']])('TreeFS on %s', platform => { [p('./bar.js')], [p('./link-to-foo/.././bar.js')], [p('/outside/../project/./bar.js')], - ])('removes a file and returns its metadata: %s', mixedPath => { + ])('removes a file: %s', mixedPath => { expect(tfs.linkStats(mixedPath)).not.toBeNull(); - expect(Array.isArray(tfs.remove(mixedPath))).toBe(true); + tfs.remove(mixedPath); expect(tfs.linkStats(mixedPath)).toBeNull(); }); test('deletes a symlink, not its target', () => { expect(tfs.linkStats(p('foo/link-to-bar.js'))).not.toBeNull(); expect(tfs.linkStats(p('bar.js'))).not.toBeNull(); - expect(Array.isArray(tfs.remove(p('foo/link-to-bar.js')))).toBe(true); + tfs.remove(p('foo/link-to-bar.js')); expect(tfs.linkStats(p('foo/link-to-bar.js'))).toBeNull(); expect(tfs.linkStats(p('bar.js'))).not.toBeNull(); }); @@ -801,6 +881,21 @@ describe.each([['win32'], ['posix']])('TreeFS on %s', platform => { expect(tfs.lookup(p('node_modules')).exists).toBe(false); }); + test('deleting a non-empty directory also removes its empty parent', () => { + // node_modules/pkg is the only child of node_modules + expect(tfs.lookup(p('node_modules/pkg')).exists).toBe(true); + expect(tfs.lookup(p('node_modules')).exists).toBe(true); + tfs.remove(p('node_modules/pkg')); + // Expect the directory and its contents to be deleted + expect(tfs.lookup(p('node_modules/pkg/a.js')).exists).toBe(false); + expect(tfs.lookup(p('node_modules/pkg/package.json')).exists).toBe( + false, + ); + expect(tfs.lookup(p('node_modules/pkg')).exists).toBe(false); + // And its parent, which is now empty + expect(tfs.lookup(p('node_modules')).exists).toBe(false); + }); + test('deleting all files leaves an empty map', () => { for (const {canonicalPath} of tfs.metadataIterator({ includeSymlinks: true, @@ -812,8 +907,8 @@ describe.each([['win32'], ['posix']])('TreeFS on %s', platform => { expect(tfs.lookup(p('foo')).exists).toBe(false); }); - test('returns null for a non-existent file', () => { - expect(tfs.remove('notexists.js')).toBeNull(); + test('no-op for a non-existent file', () => { + expect(() => tfs.remove('notexists.js')).not.toThrow(); }); }); }); @@ -970,4 +1065,174 @@ describe.each([['win32'], ['posix']])('TreeFS on %s', platform => { expect(mockProcessFile).toHaveBeenCalledTimes(2); }); }); + + describe('change listener', () => { + let simpleTfs: TreeFSType; + const logChange = jest.fn(); + const listener: FileSystemListener = { + fileAdded: (...args) => logChange('fileAdded', ...args), + fileModified: (...args) => logChange('fileModified', ...args), + fileRemoved: (...args) => logChange('fileRemoved', ...args), + directoryAdded: (...args) => logChange('directoryAdded', ...args), + directoryRemoved: (...args) => logChange('directoryRemoved', ...args), + }; + + beforeEach(() => { + logChange.mockClear(); + simpleTfs = new TreeFS({ + rootDir: p('/project'), + files: new Map([ + [p('existing.js'), [123, 0, 0, '', 0]], + [p('dir/nested.js'), [456, 0, 0, '', 0]], + [p('mylink'), [0, 0, 0, '', p('./dir')]], + ]), + processFile: () => { + throw new Error('Not implemented'); + }, + }); + }); + + describe('addOrModify with listener', () => { + test('tracks added files when adding a new file', () => { + simpleTfs.addOrModify(p('new.js'), [789, 0, 0, '', 0], listener); + + expect(logChange.mock.calls).toEqual([ + ['fileAdded', p('new.js'), [789, 0, 0, '', 0]], + ]); + }); + + test('tracks modified files when modifying an existing file', () => { + simpleTfs.addOrModify(p('existing.js'), [999, 0, 0, '', 0], listener); + + expect(logChange.mock.calls).toEqual([ + [ + 'fileModified', + p('existing.js'), + [123, 0, 0, '', 0], + [999, 0, 0, '', 0], + ], + ]); + }); + + test('tracks new directories when adding a file in a new directory', () => { + simpleTfs.addOrModify( + p('newdir/file.js'), + [123, 0, 0, '', '', 0, null], + listener, + ); + + expect(logChange.mock.calls).toEqual([ + ['directoryAdded', p('newdir')], + ['fileAdded', p('newdir/file.js'), [123, 0, 0, '', '', 0, null]], + ]); + }); + + test('tracks multiple new directories for deeply nested paths', () => { + simpleTfs.addOrModify( + p('a/b/c/file.js'), + [123, 0, 0, '', '', 0, null], + listener, + ); + expect(logChange.mock.calls).toEqual([ + ['directoryAdded', p('a')], + ['directoryAdded', p('a/b')], + ['directoryAdded', p('a/b/c')], + ['fileAdded', p('a/b/c/file.js'), [123, 0, 0, '', '', 0, null]], + ]); + }); + + test('does not track existing directories as new', () => { + simpleTfs.addOrModify( + p('dir/another.js'), + [789, 0, 0, '', '', 0, null], + listener, + ); + + expect(logChange.mock.calls).toEqual([ + ['fileAdded', p('dir/another.js'), [789, 0, 0, '', '', 0, null]], + ]); + }); + }); + + describe('bulkAddOrModify with listener', () => { + test('tracks multiple added files', () => { + simpleTfs.bulkAddOrModify( + new Map([ + [p('file1.js'), [1, 0, 0, '', '', 0, null]], + [p('file2.js'), [2, 0, 0, '', '', 0, null]], + [p('file3.js'), [3, 0, 0, '', '', 0, null]], + ]), + listener, + ); + + expect(logChange.mock.calls).toEqual([ + ['fileAdded', p('file1.js'), [1, 0, 0, '', '', 0, null]], + ['fileAdded', p('file2.js'), [2, 0, 0, '', '', 0, null]], + ['fileAdded', p('file3.js'), [3, 0, 0, '', '', 0, null]], + ]); + }); + }); + + test('accumulates changes across multiple operations', () => { + simpleTfs.addOrModify(p('new1.js'), [1, 0, 0, '', 0], listener); + simpleTfs.addOrModify(p('new2/file.js'), [2, 0, 0, '', 0], listener); + simpleTfs.addOrModify(p('new2/file.js'), [3, 0, 0, '', 0], listener); + simpleTfs.addOrModify( + p('new3/nested/file.js'), + [3, 0, 0, '', 0], + listener, + ); + simpleTfs.remove(p('existing.js'), listener); + simpleTfs.remove(p('new2/file.js'), listener); + + expect(logChange.mock.calls).toEqual([ + ['fileAdded', p('new1.js'), [1, 0, 0, '', 0]], + ['directoryAdded', p('new2')], + ['fileAdded', p('new2/file.js'), [2, 0, 0, '', 0]], + ['fileModified', p('new2/file.js'), [2, 0, 0, '', 0], [3, 0, 0, '', 0]], + ['directoryAdded', p('new3')], + ['directoryAdded', p('new3/nested')], + ['fileAdded', p('new3/nested/file.js'), [3, 0, 0, '', 0]], + ['fileRemoved', p('existing.js'), [123, 0, 0, '', 0]], + ['fileRemoved', p('new2/file.js'), [3, 0, 0, '', 0]], + ['directoryRemoved', p('new2')], + ]); + }); + + describe('remove with listener', () => { + test('tracks removed files and directories when deleting a non-empty directory', () => { + simpleTfs.remove(p('dir'), listener); + + expect(logChange.mock.calls).toEqual([ + ['fileRemoved', p('dir/nested.js'), [456, 0, 0, '', 0]], + ['directoryRemoved', p('dir')], + ]); + }); + }); + + describe('symlinks with listener', () => { + test('tracks added files when adding a symlink', () => { + simpleTfs.addOrModify( + p('link-to-existing'), + [0, 0, 0, '', p('./existing.js')], + listener, + ); + + expect(logChange.mock.calls).toEqual([ + [ + 'fileAdded', + p('link-to-existing'), + [0, 0, 0, '', p('./existing.js')], + ], + ]); + }); + + test('tracks removed symlinks with their metadata', () => { + simpleTfs.remove(p('mylink'), listener); + expect(logChange.mock.calls).toEqual([ + ['fileRemoved', p('mylink'), [0, 0, 0, '', p('./dir')]], + ]); + }); + }); + }); }); diff --git a/packages/metro-file-map/src/lib/__tests__/rootRelativeCacheKeys-test.js b/packages/metro-file-map/src/lib/__tests__/rootRelativeCacheKeys-test.js index 01137982b2..9d9ed06d75 100644 --- a/packages/metro-file-map/src/lib/__tests__/rootRelativeCacheKeys-test.js +++ b/packages/metro-file-map/src/lib/__tests__/rootRelativeCacheKeys-test.js @@ -63,7 +63,7 @@ jest.mock( test('returns a distinct cache key for any change', () => { const {rootDir: __, plugins: ___, ...simpleParameters} = buildParameters; - const varyDefault = ( + const varyDefault = ( key: T, newVal: BuildParameters[T], ): BuildParameters => { diff --git a/packages/metro-file-map/src/plugins/DependencyPlugin.js b/packages/metro-file-map/src/plugins/DependencyPlugin.js index 469624ce83..7ad2b57e22 100644 --- a/packages/metro-file-map/src/plugins/DependencyPlugin.js +++ b/packages/metro-file-map/src/plugins/DependencyPlugin.js @@ -10,7 +10,6 @@ */ import type { - FileMapDelta, FileMapPlugin, FileMapPluginInitOptions, FileMapPluginWorker, @@ -64,25 +63,11 @@ export default class DependencyPlugin return null; } - bulkUpdate(delta: FileMapDelta>): void { + onChanged(): void { // No-op: Worker already populated plugin data // Plugin data is write-only from worker } - onNewOrModifiedFile( - relativeFilePath: string, - pluginData: ?ReadonlyArray, - ): void { - // No-op: Dependencies already in plugin data - } - - onRemovedFile( - relativeFilePath: string, - pluginData: ?ReadonlyArray, - ): void { - // No-op - } - assertValid(): void { // No validation needed } diff --git a/packages/metro-file-map/src/plugins/HastePlugin.js b/packages/metro-file-map/src/plugins/HastePlugin.js index 89b9b56a0f..1aed548c3b 100644 --- a/packages/metro-file-map/src/plugins/HastePlugin.js +++ b/packages/metro-file-map/src/plugins/HastePlugin.js @@ -13,7 +13,6 @@ import type { Console, DuplicatesIndex, DuplicatesSet, - FileMapDelta, FileMapPlugin, FileMapPluginInitOptions, FileMapPluginWorker, @@ -24,6 +23,7 @@ import type { HTypeValue, Path, PerfLogger, + ReadonlyFileSystemChanges, } from '../flow-types'; import H from '../constants'; @@ -237,26 +237,26 @@ export default class HastePlugin ); } - bulkUpdate(delta: FileMapDelta): void { + onChanged(delta: ReadonlyFileSystemChanges): void { // Process removals first so that moves aren't treated as duplicates. - for (const [normalPath, maybeHasteId] of delta.removed) { - this.onRemovedFile(normalPath, maybeHasteId); + for (const [canonicalPath, maybeHasteId] of delta.removedFiles) { + this.#onRemovedFile(canonicalPath, maybeHasteId); } - for (const [normalPath, maybeHasteId] of delta.addedOrModified) { - this.onNewOrModifiedFile(normalPath, maybeHasteId); + for (const [canonicalPath, maybeHasteId] of delta.addedFiles) { + this.#onNewFile(canonicalPath, maybeHasteId); } } - onNewOrModifiedFile(relativeFilePath: string, id: ?string) { + #onNewFile(canonicalPath: string, id: ?string) { if (id == null) { // Not a Haste module or package return; } const module: HasteMapItemMetadata = [ - relativeFilePath, + canonicalPath, this.#enableHastePackages && - path.basename(relativeFilePath) === 'package.json' + path.basename(canonicalPath) === 'package.json' ? H.PACKAGE : H.MODULE, ]; @@ -324,14 +324,14 @@ export default class HastePlugin hasteMapItem[platform] = module; } - onRemovedFile(relativeFilePath: string, moduleName: ?string) { + #onRemovedFile(canonicalPath: string, moduleName: ?string) { if (moduleName == null) { // Not a Haste module or package return; } const platform = - getPlatformExtension(relativeFilePath, this.#platforms) || + getPlatformExtension(canonicalPath, this.#platforms) || H.GENERIC_PLATFORM; const hasteMapItem = this.#map.get(moduleName); @@ -344,7 +344,7 @@ export default class HastePlugin } } - this.#recoverDuplicates(moduleName, relativeFilePath); + this.#recoverDuplicates(moduleName, canonicalPath); } assertValid(): void { diff --git a/packages/metro-file-map/src/plugins/MockPlugin.js b/packages/metro-file-map/src/plugins/MockPlugin.js index bf0baaa995..305b317558 100644 --- a/packages/metro-file-map/src/plugins/MockPlugin.js +++ b/packages/metro-file-map/src/plugins/MockPlugin.js @@ -10,13 +10,13 @@ */ import type { - FileMapDelta, FileMapPlugin, FileMapPluginInitOptions, FileMapPluginWorker, MockMap as IMockMap, Path, RawMockMap, + ReadonlyFileSystemChanges, } from '../flow-types'; import normalizePathSeparatorsToPosix from '../lib/normalizePathSeparatorsToPosix'; @@ -79,15 +79,12 @@ export default class MockPlugin this.#raw = pluginState; } else { // Otherwise, traverse all files to rebuild - this.bulkUpdate({ - addedOrModified: [ - ...files.fileIterator({ - includeNodeModules: false, - includeSymlinks: false, - }), - ].map(({canonicalPath}) => [canonicalPath, null]), - removed: [], - }); + for (const {canonicalPath} of files.fileIterator({ + includeNodeModules: false, + includeSymlinks: false, + })) { + this.#onFileAdded(canonicalPath); + } } } @@ -102,24 +99,24 @@ export default class MockPlugin ); } - bulkUpdate(delta: FileMapDelta<>): void { + onChanged(delta: ReadonlyFileSystemChanges): void { // Process removals first so that moves aren't treated as duplicates. - for (const [relativeFilePath] of delta.removed) { - this.onRemovedFile(relativeFilePath); + for (const [canonicalPath] of delta.removedFiles) { + this.#onFileRemoved(canonicalPath); } - for (const [relativeFilePath] of delta.addedOrModified) { - this.onNewOrModifiedFile(relativeFilePath); + for (const [canonicalPath] of delta.addedFiles) { + this.#onFileAdded(canonicalPath); } } - onNewOrModifiedFile(relativeFilePath: Path): void { - const absoluteFilePath = this.#pathUtils.normalToAbsolute(relativeFilePath); + #onFileAdded(canonicalPath: Path): void { + const absoluteFilePath = this.#pathUtils.normalToAbsolute(canonicalPath); if (!this.#mocksPattern.test(absoluteFilePath)) { return; } const mockName = getMockName(absoluteFilePath); - const posixRelativePath = normalizePathSeparatorsToPosix(relativeFilePath); + const posixRelativePath = normalizePathSeparatorsToPosix(canonicalPath); const existingMockPosixPath = this.#raw.mocks.get(mockName); if (existingMockPosixPath != null) { @@ -141,16 +138,15 @@ export default class MockPlugin this.#raw.mocks.set(mockName, posixRelativePath); } - onRemovedFile(relativeFilePath: Path): void { - const absoluteFilePath = this.#pathUtils.normalToAbsolute(relativeFilePath); + #onFileRemoved(canonicalPath: Path): void { + const absoluteFilePath = this.#pathUtils.normalToAbsolute(canonicalPath); if (!this.#mocksPattern.test(absoluteFilePath)) { return; } const mockName = getMockName(absoluteFilePath); const duplicates = this.#raw.duplicates.get(mockName); if (duplicates != null) { - const posixRelativePath = - normalizePathSeparatorsToPosix(relativeFilePath); + const posixRelativePath = normalizePathSeparatorsToPosix(canonicalPath); duplicates.delete(posixRelativePath); if (duplicates.size === 1) { this.#raw.duplicates.delete(mockName); diff --git a/packages/metro-file-map/src/plugins/__tests__/DependencyPlugin-test.js b/packages/metro-file-map/src/plugins/dependencies/__tests__/DependencyPlugin-test.js similarity index 94% rename from packages/metro-file-map/src/plugins/__tests__/DependencyPlugin-test.js rename to packages/metro-file-map/src/plugins/dependencies/__tests__/DependencyPlugin-test.js index 0c8030bc09..bf00973127 100644 --- a/packages/metro-file-map/src/plugins/__tests__/DependencyPlugin-test.js +++ b/packages/metro-file-map/src/plugins/dependencies/__tests__/DependencyPlugin-test.js @@ -9,7 +9,7 @@ * @oncall react_native */ -import DependencyPlugin from '../DependencyPlugin'; +import DependencyPlugin from '../../DependencyPlugin'; import path from 'path'; describe('DependencyPlugin', () => { @@ -71,12 +71,9 @@ describe('DependencyPlugin', () => { }); test('returns different cache keys for different dependency extractors', () => { - const extractorPath = path.join( - __dirname, - '../../__tests__/dependencyExtractor.js', - ); + const extractorPath = path.join(__dirname, 'mockDependencyExtractor.js'); // $FlowFixMe[untyped-import] - const dependencyExtractor = require('../../__tests__/dependencyExtractor'); + const dependencyExtractor = require('./mockDependencyExtractor'); // Create plugin with cache key 'foo' dependencyExtractor.setCacheKey('foo'); @@ -103,10 +100,7 @@ describe('DependencyPlugin', () => { }); test('cache key includes extractor path', () => { - const extractorPath = path.join( - __dirname, - '../../__tests__/dependencyExtractor.js', - ); + const extractorPath = path.join(__dirname, 'mockDependencyExtractor.js'); plugin = new DependencyPlugin({ dependencyExtractor: extractorPath, computeDependencies: true, @@ -118,12 +112,9 @@ describe('DependencyPlugin', () => { }); test('handles extractor without getCacheKey method', () => { - const extractorPath = path.join( - __dirname, - '../../__tests__/dependencyExtractor.js', - ); + const extractorPath = path.join(__dirname, 'mockDependencyExtractor.js'); // $FlowFixMe[untyped-import] - const dependencyExtractor = require('../../__tests__/dependencyExtractor'); + const dependencyExtractor = require('./mockDependencyExtractor'); // Temporarily remove getCacheKey const originalGetCacheKey = dependencyExtractor.getCacheKey; diff --git a/packages/metro-file-map/src/__tests__/dependencyExtractor.js b/packages/metro-file-map/src/plugins/dependencies/__tests__/mockDependencyExtractor.js similarity index 100% rename from packages/metro-file-map/src/__tests__/dependencyExtractor.js rename to packages/metro-file-map/src/plugins/dependencies/__tests__/mockDependencyExtractor.js diff --git a/packages/metro-file-map/src/plugins/dependencies/dependencyExtractor.d.ts b/packages/metro-file-map/src/plugins/dependencies/dependencyExtractor.d.ts index 308464c9fe..b8cf6d95c9 100644 --- a/packages/metro-file-map/src/plugins/dependencies/dependencyExtractor.d.ts +++ b/packages/metro-file-map/src/plugins/dependencies/dependencyExtractor.d.ts @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @generated by js1 build metro-ts-defs / yarn run build-ts-defs */ declare const dependencyExtractor: { diff --git a/packages/metro-file-map/src/plugins/haste/__tests__/HastePlugin-test.js b/packages/metro-file-map/src/plugins/haste/__tests__/HastePlugin-test.js index b1d8fa8e28..c1b09c29f4 100644 --- a/packages/metro-file-map/src/plugins/haste/__tests__/HastePlugin-test.js +++ b/packages/metro-file-map/src/plugins/haste/__tests__/HastePlugin-test.js @@ -9,6 +9,10 @@ * @oncall react_native */ +import type { + CanonicalPath, + ReadonlyFileSystemChanges, +} from '../../../flow-types'; import type HasteMapType from '../../HastePlugin'; let mockPathModule; @@ -88,11 +92,16 @@ describe.each([['win32'], ['posix']])('HastePlugin on %s', platform => { expect(hasteMap.getModule('NameForFoo')).toEqual(p('/root/project/Foo.js')); }); - describe('onRemovedFile', () => { + describe('remove file', () => { let hasteMap: HasteMapType; + let removeFile: (path: CanonicalPath, name: ?string) => void; beforeEach(async () => { hasteMap = new HasteMap(opts); + removeFile = (canonicalPath, name) => + hasteMap.onChanged( + makeChanges({added: [], removed: [[canonicalPath, name]]}), + ); await hasteMap.initialize({ files: { fileIterator: jest.fn().mockReturnValue(INITIAL_FILES), @@ -104,7 +113,7 @@ describe.each([['win32'], ['posix']])('HastePlugin on %s', platform => { test('removes a module, without affecting others', () => { expect(hasteMap.getModule('NameForFoo')).not.toBeNull(); - hasteMap.onRemovedFile(p('project/Foo.js'), 'NameForFoo'); + removeFile(p('project/Foo.js'), 'NameForFoo'); expect(hasteMap.getModule('NameForFoo')).toBeNull(); expect(hasteMap.getModule('Bar')).not.toBeNull(); }); @@ -113,14 +122,14 @@ describe.each([['win32'], ['posix']])('HastePlugin on %s', platform => { expect(() => hasteMap.getModule('Duplicate')).toThrow( DuplicateHasteCandidatesError, ); - hasteMap.onRemovedFile(p('project/Duplicate.js'), 'Duplicate'); + removeFile(p('project/Duplicate.js'), 'Duplicate'); expect(hasteMap.getModule('Duplicate')).toBe( p('/root/project/other/Duplicate.js'), ); }); }); - describe('bulkUpdate', () => { + describe('onChanged', () => { let hasteMap: HasteMapType; beforeEach(async () => { @@ -134,27 +143,22 @@ describe.each([['win32'], ['posix']])('HastePlugin on %s', platform => { }); }); - test('removes a module, without affecting others', () => { - expect(hasteMap.getModule('NameForFoo')).not.toBeNull(); - hasteMap.onRemovedFile(p('project/Foo.js'), 'NameForFoo'); - expect(hasteMap.getModule('NameForFoo')).toBeNull(); - expect(hasteMap.getModule('Bar')).not.toBeNull(); - }); - test('fixes duplicates, adds and removes modules', () => { expect(() => hasteMap.getModule('Duplicate')).toThrow( DuplicateHasteCandidatesError, ); - hasteMap.bulkUpdate({ - removed: [ - [p('project/Duplicate.js'), 'Duplicate'], - [p('project/Foo.js'), 'NameForFoo'], - ], - addedOrModified: [ - [p('project/Baz.js'), 'Baz'], // New - [p('project/other/Bar.js'), 'Bar'], // New duplicate - ], - }); + hasteMap.onChanged( + makeChanges({ + added: [ + [p('project/Baz.js'), 'Baz'], // New + [p('project/other/Bar.js'), 'Bar'], // New duplicate + ], + removed: [ + [p('project/Duplicate.js'), 'Duplicate'], + [p('project/Foo.js'), 'NameForFoo'], + ], + }), + ); expect(hasteMap.getModule('Duplicate')).toBe( p('/root/project/other/Duplicate.js'), ); @@ -205,3 +209,19 @@ describe.each([['win32'], ['posix']])('HastePlugin on %s', platform => { }); }); }); + +function makeChanges({ + added, + removed, +}: Readonly<{ + added: ReadonlyArray<[CanonicalPath, ?string]>, + removed: ReadonlyArray<[CanonicalPath, ?string]>, +}>): ReadonlyFileSystemChanges { + return { + addedFiles: new Map(added), + removedFiles: new Map(removed), + modifiedFiles: new Map(), + addedDirectories: new Set(), + removedDirectories: new Set(), + }; +} diff --git a/packages/metro-file-map/src/plugins/mocks/__tests__/MockPlugin-test.js b/packages/metro-file-map/src/plugins/mocks/__tests__/MockPlugin-test.js index 6796c29430..43f2835646 100644 --- a/packages/metro-file-map/src/plugins/mocks/__tests__/MockPlugin-test.js +++ b/packages/metro-file-map/src/plugins/mocks/__tests__/MockPlugin-test.js @@ -21,6 +21,8 @@ describe.each([['win32'], ['posix']])('MockPlugin on %s', platform => { : filePath; let MockMap: Class; + let mockMap: MockMapType; + let onFileAdded: (filePath: string) => void; const opts = { console, @@ -33,20 +35,27 @@ describe.each([['win32'], ['posix']])('MockPlugin on %s', platform => { jest.resetModules(); mockPathModule = jest.requireActual<{}>('path')[platform]; MockMap = require('../../MockPlugin').default; + mockMap = new MockMap(opts); + onFileAdded = canonicalPath => + mockMap.onChanged({ + addedFiles: new Map([[canonicalPath, null]]), + modifiedFiles: new Map(), + removedFiles: new Map(), + addedDirectories: new Set(), + removedDirectories: new Set(), + }); jest.spyOn(console, 'warn').mockImplementation(() => {}); jest.clearAllMocks(); }); test('set and get a mock module', () => { - const mockMap = new MockMap(opts); - mockMap.onNewOrModifiedFile(p('__mocks__/foo.js')); + onFileAdded(p('__mocks__/foo.js')); expect(mockMap.getMockModule('foo')).toBe(p('/root/__mocks__/foo.js')); }); test('assertValid throws on duplicates', () => { - const mockMap = new MockMap(opts); - mockMap.onNewOrModifiedFile(p('__mocks__/foo.js')); - mockMap.onNewOrModifiedFile(p('other/__mocks__/foo.js')); + onFileAdded(p('__mocks__/foo.js')); + onFileAdded(p('other/__mocks__/foo.js')); expect(console.warn).toHaveBeenCalledTimes(1); expect(() => mockMap.assertValid()).toThrowError( @@ -59,9 +68,8 @@ Duplicate manual mock found for \`foo\`: }); test('recovers from duplicates', () => { - const mockMap = new MockMap(opts); - mockMap.onNewOrModifiedFile(p('__mocks__/foo.js')); - mockMap.onNewOrModifiedFile(p('other/__mocks__/foo.js')); + onFileAdded(p('__mocks__/foo.js')); + onFileAdded(p('other/__mocks__/foo.js')); expect(() => mockMap.assertValid()).toThrow(); @@ -79,7 +87,13 @@ Duplicate manual mock found for \`foo\`: version: 2, }); - mockMap.onRemovedFile(p('other/__mocks__/foo.js')); + mockMap.onChanged({ + addedFiles: new Map(), + modifiedFiles: new Map(), + removedFiles: new Map([[p('other/__mocks__/foo.js'), null]]), + addedDirectories: new Set(), + removedDirectories: new Set(), + }); expect(() => mockMap.assertValid()).not.toThrow(); @@ -94,7 +108,6 @@ Duplicate manual mock found for \`foo\`: }); test('loads from a snapshot', async () => { - const mockMap = new MockMap(opts); await mockMap.initialize({ files: { fileIterator: () => { @@ -130,17 +143,18 @@ Duplicate manual mock found for \`foo\`: ['foo', 'other/__mocks__/foo.js'], ]), duplicates: new Map([ - ['foo', new Set(['other/__mocks__/foo.js', '__mocks__/foo.js'])], + [ + 'foo', + new Set(['other/__mocks__/foo.js', '__mocks__/foo.js']), + ], ]), version: 2, }; - /* $FlowFixMe[incompatible-type] Natural Inference rollout. See - * https://fburl.com/workplace/6291gfvu */ - const mockMap = new MockMap({...opts, rawMockMap}); - expect(mockMap.getMockModule('bar')).toEqual( + const loadedMockMap = new MockMap({...opts, rawMockMap}); + expect(loadedMockMap.getMockModule('bar')).toEqual( p('/root/some/__mocks__/bar.js'), ); - expect(mockMap.getMockModule('foo')).toEqual( + expect(loadedMockMap.getMockModule('foo')).toEqual( p('/root/other/__mocks__/foo.js'), ); }); diff --git a/packages/metro-file-map/src/watchers/FallbackWatcher.js b/packages/metro-file-map/src/watchers/FallbackWatcher.js index 5f87b07d7e..a9b2d4b8d9 100644 --- a/packages/metro-file-map/src/watchers/FallbackWatcher.js +++ b/packages/metro-file-map/src/watchers/FallbackWatcher.js @@ -124,12 +124,27 @@ export default class FallbackWatcher extends AbstractWatcher { } /** - * Removes a dir from the registry. + * Removes a dir from the registry, returning all files that were registered + * under it (recursively). */ - #unregisterDir(dirpath: string): void { - if (this.#dirRegistry[dirpath]) { - delete this.#dirRegistry[dirpath]; + #unregisterDir(dirpath: string): Array { + const removedFiles: Array = []; + + // Find and remove all entries under this directory + for (const registeredDir of Object.keys(this.#dirRegistry)) { + if ( + registeredDir === dirpath || + registeredDir.startsWith(dirpath + path.sep) + ) { + // Collect all files in this directory + for (const filename of Object.keys(this.#dirRegistry[registeredDir])) { + removedFiles.push(path.join(registeredDir, filename)); + } + delete this.#dirRegistry[registeredDir]; + } } + + return removedFiles; } /** @@ -350,7 +365,15 @@ export default class FallbackWatcher extends AbstractWatcher { return; } this.#unregister(fullPath); - this.#unregisterDir(fullPath); + // When a directory is deleted, emit delete events for all files we + // knew about under that directory + const removedFiles = this.#unregisterDir(fullPath); + for (const removedFile of removedFiles) { + this.#emitEvent({ + event: DELETE_EVENT, + relativePath: path.relative(this.root, removedFile), + }); + } if (registered) { this.#emitEvent({event: DELETE_EVENT, relativePath}); } diff --git a/packages/metro-file-map/src/watchers/NativeWatcher.js b/packages/metro-file-map/src/watchers/NativeWatcher.js index b191e60df7..3c11a384fe 100644 --- a/packages/metro-file-map/src/watchers/NativeWatcher.js +++ b/packages/metro-file-map/src/watchers/NativeWatcher.js @@ -21,6 +21,7 @@ const debug = require('debug')('Metro:NativeWatcher'); const TOUCH_EVENT = 'touch'; const DELETE_EVENT = 'delete'; +const RECRAWL_EVENT = 'recrawl'; /** * NativeWatcher uses Node's native fs.watch API with recursive: true. @@ -74,9 +75,8 @@ export default class NativeWatcher extends AbstractWatcher { // ~instant on macOS or Windows. recursive: true, }, - (_event, relativePath) => { - // _event is always 'rename' on macOS, so we don't use it. - this._handleEvent(relativePath).catch(error => { + (event, relativePath) => { + this._handleEvent(event, relativePath).catch(error => { this.emitError(error); }); }, @@ -95,13 +95,23 @@ export default class NativeWatcher extends AbstractWatcher { } } - async _handleEvent(relativePath: string) { + async _handleEvent(event: string, relativePath: string) { const absolutePath = path.resolve(this.root, relativePath); if (this.doIgnore(relativePath)) { - debug('Ignoring event on %s (root: %s)', relativePath, this.root); + debug( + 'Ignoring event "%s" on %s (root: %s)', + event, + relativePath, + this.root, + ); return; } - debug('Handling event on %s (root: %s)', relativePath, this.root); + debug( + 'Handling event "%s" on %s (root: %s)', + event, + relativePath, + this.root, + ); try { const stat = await fsPromises.lstat(absolutePath); @@ -116,6 +126,23 @@ export default class NativeWatcher extends AbstractWatcher { return; } + // For directory "rename" events, notify that we need a recrawl since we + // wont' receive events for unmodified files underneath a moved (or + // cloned) directory. Renames are fired by the OS on moves, clones, and + // creations. We ignore "change" events because they indiciate a change + // to directory metadata, rather than its path or existence. + if (type === 'd' && event === 'rename') { + debug( + 'Directory rename detected on %s, requesting recrawl', + relativePath, + ); + this.emitFileEvent({ + event: RECRAWL_EVENT, + relativePath, + }); + return; + } + this.emitFileEvent({ event: TOUCH_EVENT, relativePath, diff --git a/packages/metro-file-map/src/watchers/__tests__/helpers.js b/packages/metro-file-map/src/watchers/__tests__/helpers.js index 70f3e30681..c6334662ae 100644 --- a/packages/metro-file-map/src/watchers/__tests__/helpers.js +++ b/packages/metro-file-map/src/watchers/__tests__/helpers.js @@ -42,7 +42,7 @@ const isWatchmanOnPath = () => { // `null` Watchers will be marked as skipped tests. export const WATCHERS: Readonly<{ - [key: string]: + [key: 'Watchman' | 'Native' | 'Fallback']: | Class | Class | Class @@ -53,6 +53,8 @@ export const WATCHERS: Readonly<{ Fallback: FallbackWatcher, }; +export type WatcherName = keyof typeof WATCHERS; + export type EventHelpers = { nextEvent: (afterFn: () => Promise) => Promise<{ eventType: string, @@ -62,17 +64,17 @@ export type EventHelpers = { untilEvent: ( afterFn: () => Promise, expectedPath: string, - expectedEvent: 'touch' | 'delete', + expectedEvent: 'touch' | 'delete' | 'recrawl', ) => Promise, allEvents: ( afterFn: () => Promise, - events: ReadonlyArray<[string, 'touch' | 'delete']>, + events: ReadonlyArray<[string, 'touch' | 'delete' | 'recrawl']>, opts?: {rejectUnexpected: boolean}, ) => Promise, }; export const createTempWatchRoot = async ( - watcherName: string, + watcherName: WatcherName, watchmanConfig: {[key: string]: unknown} | false = {}, ): Promise => { const tmpDir = await mkdtemp( @@ -94,7 +96,7 @@ export const createTempWatchRoot = async ( }; export const startWatching = async ( - watcherName: string, + watcherName: WatcherName, watchRoot: string, opts: WatcherOptions, ): (Promise<{ diff --git a/packages/metro-file-map/src/watchers/__tests__/integration-test.js b/packages/metro-file-map/src/watchers/__tests__/integration-test.js index 9bf5b5193c..798b6a4bf3 100644 --- a/packages/metro-file-map/src/watchers/__tests__/integration-test.js +++ b/packages/metro-file-map/src/watchers/__tests__/integration-test.js @@ -10,7 +10,7 @@ */ import type {WatcherOptions} from '../common'; -import type {EventHelpers} from './helpers'; +import type {EventHelpers, WatcherName} from './helpers'; import NativeWatcher from '../NativeWatcher'; import {WATCHERS, createTempWatchRoot, startWatching} from './helpers'; @@ -28,7 +28,7 @@ test('NativeWatcher is supported if and only if darwin', () => { describe.each(Object.keys(WATCHERS))( 'Watcher integration tests: %s', - watcherName => { + (watcherName: WatcherName) => { let appRoot; let cookieCount = 1; let watchRoot; @@ -42,6 +42,9 @@ describe.each(Object.keys(WATCHERS))( ? test : test.skip; + // NativeWatcher emits 'recrawl' for directories, others emit 'touch' + const expectedDirEventType = watcherName === 'Native' ? 'recrawl' : 'touch'; + beforeAll(async () => { watchRoot = await createTempWatchRoot(watcherName); @@ -53,9 +56,11 @@ describe.each(Object.keys(WATCHERS))( // the watcher was started. Tests should touch only distinct subsets of // these files to ensure that tests remain isolated. await mkdir(join(watchRoot, 'existing')); + await mkdir(join(watchRoot, 'existing', 'to-move-out')); await Promise.all([ writeFile(join(watchRoot, 'existing', 'file-to-delete.js'), ''), writeFile(join(watchRoot, 'existing', 'file-to-modify.js'), ''), + writeFile(join(watchRoot, 'existing', 'to-move-out', 'file.js'), ''), symlink('target', join(watchRoot, 'existing', 'symlink-to-delete')), ]); @@ -82,11 +87,16 @@ describe.each(Object.keys(WATCHERS))( }); beforeEach(async () => { - expect(await eventHelpers.nextEvent(() => mkdir(appRoot))).toStrictEqual({ + // NativeWatcher emits 'recrawl' for directories, others emit 'touch' + const event = await eventHelpers.nextEvent(() => mkdir(appRoot)); + expect(event).toMatchObject({ path: 'app', - eventType: 'touch', - metadata: expect.any(Object), + eventType: expectedDirEventType, }); + // For non-recrawl events, also check metadata + if (event.eventType === 'touch') { + expect(event.metadata).toEqual(expect.any(Object)); + } }); afterEach(async () => { @@ -186,6 +196,66 @@ describe.each(Object.keys(WATCHERS))( }); }); + maybeTest( + 'detects all files when a preexisting directory is moved in from outside a watched root', + async () => { + // Create a directory with a file in it outside the watch root, then move it in and check that both the directory and the file are reported as new. + const outsideDir = await fsPromises.mkdtemp( + join(os.tmpdir(), 'metro-file-map-unwatched-'), + ); + const outsideFile = join(outsideDir, 'file.js'); + await writeFile(outsideFile, ''); + + // NativeWatcher emits 'recrawl' for the directory, which triggers a + // full crawl that finds the file. Other watchers emit individual 'touch' + // events for both directory and file. + if (watcherName === 'Native') { + // NativeWatcher: expect recrawl event for the directory only + await eventHelpers.allEvents( + () => fsPromises.rename(outsideDir, join(appRoot, 'moved-in')), + [[join('app', 'moved-in'), 'recrawl']], + {rejectUnexpected: true}, + ); + } else { + // Other watchers: expect touch events for both directory and file + await eventHelpers.allEvents( + () => fsPromises.rename(outsideDir, join(appRoot, 'moved-in')), + [ + [join('app', 'moved-in'), 'touch'], + [join('app', 'moved-in', 'file.js'), 'touch'], + ], + {rejectUnexpected: true}, + ); + } + }, + ); + + maybeTest( + 'reports directory as deleted when it is moved from a watched root to outside', + async () => { + // Create a directory with a file in it inside the watch root, then move it out and check that both the directory and the file are reported as deleted. + const outsideDir = await fsPromises.mkdtemp( + join(os.tmpdir(), 'metro-file-map-unwatched-'), + ); + + await eventHelpers.allEvents( + () => + fsPromises.rename( + join(watchRoot, 'existing', 'to-move-out'), + join(outsideDir, 'moved-out'), + ), + watcherName === 'Native' + ? // NativeWatcher only emits an event for the directory, not contents + [[join('existing', 'to-move-out'), 'delete']] + : [ + [join('existing', 'to-move-out'), 'delete'], + [join('existing', 'to-move-out', 'file.js'), 'delete'], + ], + {rejectUnexpected: true}, + ); + }, + ); + maybeTest('detects deletion of a pre-existing symlink', async () => { expect( await eventHelpers.nextEvent(() => @@ -214,17 +284,21 @@ describe.each(Object.keys(WATCHERS))( }); maybeTest('detects changes to files in a new directory', async () => { - expect( - await eventHelpers.nextEvent(() => mkdir(join(watchRoot, 'newdir'))), - ).toStrictEqual({ + const dirEvent = await eventHelpers.nextEvent(() => + mkdir(join(watchRoot, 'newdir')), + ); + expect(dirEvent).toMatchObject({ path: join('newdir'), - eventType: 'touch', - metadata: { + eventType: expectedDirEventType, + }); + // For non-recrawl events, also check metadata + if (dirEvent.eventType === 'touch') { + expect(dirEvent.metadata).toStrictEqual({ modifiedTime: expect.any(Number), size: expect.any(Number), type: 'd', - }, - }); + }); + } expect( await eventHelpers.nextEvent(() => writeFile(join(watchRoot, 'newdir', 'file-in-new-dir.js'), 'code'), @@ -245,6 +319,9 @@ describe.each(Object.keys(WATCHERS))( maybeTestOn('darwin')( 'emits deletion for all files when a directory is deleted', async () => { + // For NativeWatcher, the directory events will be 'recrawl', not 'touch' + const dirEventType = expectedDirEventType; + await eventHelpers.allEvents( async () => { await mkdir(join(appRoot, 'subdir', 'subdir2'), {recursive: true}); @@ -255,8 +332,8 @@ describe.each(Object.keys(WATCHERS))( ]); }, [ - [join('app', 'subdir'), 'touch'], - [join('app', 'subdir', 'subdir2'), 'touch'], + [join('app', 'subdir'), dirEventType], + [join('app', 'subdir', 'subdir2'), dirEventType], [join('app', 'subdir', 'deep.js'), 'touch'], [join('app', 'subdir', 'subdir2', 'deeper.js'), 'touch'], ], diff --git a/packages/metro-file-map/src/watchers/common.js b/packages/metro-file-map/src/watchers/common.js index 2a431174d6..16f4c705bb 100644 --- a/packages/metro-file-map/src/watchers/common.js +++ b/packages/metro-file-map/src/watchers/common.js @@ -26,6 +26,7 @@ import path from 'path'; */ export const DELETE_EVENT = 'delete'; export const TOUCH_EVENT = 'touch'; +export const RECRAWL_EVENT = 'recrawl'; export const ALL_EVENT = 'all'; export type WatcherOptions = Readonly<{ diff --git a/packages/metro-file-map/types/Watcher.d.ts b/packages/metro-file-map/types/Watcher.d.ts index 14e6ecbf7e..9dff3ba096 100644 --- a/packages/metro-file-map/types/Watcher.d.ts +++ b/packages/metro-file-map/types/Watcher.d.ts @@ -4,26 +4,26 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<25fee66c7d26ad53cdd5bbab454fe50b>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/Watcher.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { Console, CrawlerOptions, - FileData, - Path, + CrawlResult, PerfLogger, WatcherBackendChangeEvent, - WatchmanClocks, } from './flow-types'; import EventEmitter from 'events'; -type CrawlResult = { - changedFiles: FileData; - clocks?: WatchmanClocks; - removedFiles: Set; -}; type WatcherOptions = { abortSignal: AbortSignal; computeSha1: boolean; @@ -64,6 +64,10 @@ export type HealthCheckResult = export declare class Watcher extends EventEmitter { constructor(options: WatcherOptions); crawl(): Promise; + recrawl( + subpath: string, + currentFileSystem: CrawlerOptions['previousState']['fileSystem'], + ): Promise; watch(onChange: (change: WatcherBackendChangeEvent) => void): void; close(): void; checkHealth(timeout: number): Promise; diff --git a/packages/metro-file-map/types/cache/DiskCacheManager.d.ts b/packages/metro-file-map/types/cache/DiskCacheManager.d.ts index 8d1e601eeb..464e1daa5d 100644 --- a/packages/metro-file-map/types/cache/DiskCacheManager.d.ts +++ b/packages/metro-file-map/types/cache/DiskCacheManager.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<9cdec2a3b7a46f0a893dd5dc392a5294>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/cache/DiskCacheManager.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro-file-map/types/constants.d.ts b/packages/metro-file-map/types/constants.d.ts index ea17d09d6c..772b0fe38a 100644 --- a/packages/metro-file-map/types/constants.d.ts +++ b/packages/metro-file-map/types/constants.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<733fae11203b79438dfb1ee2bbb6473d>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/constants.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {HType} from './flow-types'; diff --git a/packages/metro-file-map/types/crawlers/node/hasNativeFindSupport.d.ts b/packages/metro-file-map/types/crawlers/node/hasNativeFindSupport.d.ts index c4a13dde62..e91d9a6978 100644 --- a/packages/metro-file-map/types/crawlers/node/hasNativeFindSupport.d.ts +++ b/packages/metro-file-map/types/crawlers/node/hasNativeFindSupport.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<8b6ff8a24f9156cd7991006c72edd296>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/crawlers/node/hasNativeFindSupport.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function hasNativeFindSupport(): Promise; diff --git a/packages/metro-file-map/types/crawlers/node/index.d.ts b/packages/metro-file-map/types/crawlers/node/index.d.ts index de9c996564..38ddc03ecf 100644 --- a/packages/metro-file-map/types/crawlers/node/index.d.ts +++ b/packages/metro-file-map/types/crawlers/node/index.d.ts @@ -4,13 +4,18 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<27109494e4956802ba89ac6fd22aa277>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/crawlers/node/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ -import type {CanonicalPath, CrawlerOptions, FileData} from '../../flow-types'; +import type {CrawlerOptions, CrawlResult} from '../../flow-types'; -declare function nodeCrawl( - options: CrawlerOptions, -): Promise<{removedFiles: Set; changedFiles: FileData}>; +declare function nodeCrawl(options: CrawlerOptions): Promise; export default nodeCrawl; diff --git a/packages/metro-file-map/types/crawlers/watchman/index.d.ts b/packages/metro-file-map/types/crawlers/watchman/index.d.ts index d171458659..9952e52c5d 100644 --- a/packages/metro-file-map/types/crawlers/watchman/index.d.ts +++ b/packages/metro-file-map/types/crawlers/watchman/index.d.ts @@ -4,20 +4,20 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/crawlers/watchman/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ -import type { - CanonicalPath, - CrawlerOptions, - FileData, - WatchmanClocks, -} from '../../flow-types'; +import type {CrawlerOptions, CrawlResult} from '../../flow-types'; -declare function watchmanCrawl($$PARAM_0$$: CrawlerOptions): Promise<{ - changedFiles: FileData; - removedFiles: Set; - clocks: WatchmanClocks; -}>; +declare function watchmanCrawl( + $$PARAM_0$$: CrawlerOptions, +): Promise; export default watchmanCrawl; diff --git a/packages/metro-file-map/types/crawlers/watchman/planQuery.d.ts b/packages/metro-file-map/types/crawlers/watchman/planQuery.d.ts index 26bbb423fc..b90fad8c44 100644 --- a/packages/metro-file-map/types/crawlers/watchman/planQuery.d.ts +++ b/packages/metro-file-map/types/crawlers/watchman/planQuery.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<542d8499f7c1ed111b466dbea5bc98db>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/crawlers/watchman/planQuery.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ type WatchmanQuery = {[key: string]: unknown}; diff --git a/packages/metro-file-map/types/flow-types.d.ts b/packages/metro-file-map/types/flow-types.d.ts index 342a9a7fc1..16ca47efd9 100644 --- a/packages/metro-file-map/types/flow-types.d.ts +++ b/packages/metro-file-map/types/flow-types.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/flow-types.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {PerfLogger, PerfLoggerFactory, RootPerfLogger} from 'metro-config'; @@ -17,7 +24,7 @@ export type BuildParameters = Readonly<{ extensions: ReadonlyArray; forceNodeFilesystemAPI: boolean; ignorePattern: RegExp; - plugins: ReadonlyArray; + plugins: ReadonlyArray; retainAllFiles: boolean; rootDir: string; roots: ReadonlyArray; @@ -68,10 +75,15 @@ export type CacheManagerWriteOptions = Readonly<{ onWriteError: (error: Error) => void; }>; export type CanonicalPath = string; -export type ChangeEvent = { +export type ChangedFileMetadata = Readonly<{ + isSymlink: boolean; + modifiedTime?: null | undefined | number; +}>; +export type ChangeEvent = Readonly<{ logger: null | undefined | RootPerfLogger; - eventsQueue: EventsQueue; -}; + changes: ReadonlyFileSystemChanges>; + rootDir: string; +}>; export type ChangeEventMetadata = { modifiedTime: null | undefined | number; size: null | undefined | number; @@ -94,7 +106,11 @@ export type CrawlerOptions = { rootDir: string; roots: ReadonlyArray; onStatus: (status: WatcherStatus) => void; + subpath?: string; }; +export type CrawlResult = + | {changedFiles: FileData; removedFiles: Set; clocks: WatchmanClocks} + | {changedFiles: FileData; removedFiles: Set}; export type DependencyExtractor = { extract: ( content: string, @@ -121,15 +137,6 @@ export type WatcherStatus = }; export type DuplicatesSet = Map; export type DuplicatesIndex = Map>; -export type EventsQueue = Array<{ - filePath: Path; - metadata: ChangeEventMetadata; - type: string; -}>; -export type FileMapDelta = Readonly<{ - removed: Iterable<[CanonicalPath, T]>; - addedOrModified: Iterable<[CanonicalPath, T]>; -}>; export type FileMapPluginInitOptions< SerializableState, PerFileData = void, @@ -140,13 +147,13 @@ export type FileMapPluginInitOptions< ): Iterable<{ baseName: string; canonicalPath: string; - pluginData: null | undefined | PerFileData; + readonly pluginData: null | undefined | PerFileData; }>; lookup( mixedPath: string, ): | {exists: false} - | {exists: true; type: 'f'; pluginData: PerFileData} + | {exists: true; type: 'f'; readonly pluginData: PerFileData} | {exists: true; type: 'd'}; }>; pluginState: null | undefined | SerializableState; @@ -176,19 +183,25 @@ export interface FileMapPlugin< initOptions: FileMapPluginInitOptions, ): Promise; assertValid(): void; - bulkUpdate(delta: FileMapDelta): void; - getSerializableSnapshot(): SerializableState; - onRemovedFile( - relativeFilePath: string, - pluginData: null | undefined | PerFileData, - ): void; - onNewOrModifiedFile( - relativeFilePath: string, - pluginData: null | undefined | PerFileData, + onChanged( + changes: ReadonlyFileSystemChanges, ): void; + getSerializableSnapshot(): void | V8Serializable; getCacheKey(): string; getWorker(): null | undefined | FileMapPluginWorker; } +export type InputFileMapPlugin = FileMapPlugin< + /** + * > 235 | export type InputFileMapPlugin = FileMapPlugin; + * | ^^^^^ Unsupported feature: Translating "empty type" is currently not supported. + **/ + any, + /** + * > 235 | export type InputFileMapPlugin = FileMapPlugin; + * | ^^^^^ Unsupported feature: Translating "empty type" is currently not supported. + **/ + any +>; export interface MetadataWorker { processFile( $$PARAM_0$$: WorkerMessage, @@ -228,10 +241,22 @@ export type FileStats = Readonly<{ export interface FileSystem { exists(file: Path): boolean; getAllFiles(): Array; - getDifference(files: FileData): { - changedFiles: FileData; - removedFiles: Set; - }; + /** + * Given a map of files, determine which of them are new or modified + * (changedFiles), and which of them are missing from the input + * (removedFiles), vs the current state of this instance of FileSystem. + */ + getDifference( + files: FileData, + options?: Readonly<{ + /** + * Only consider files under this subpath (which should be a directory) + * when computing removedFiles. If not provided, all files in the file + * system are considered. + */ + subpath?: string; + }>, + ): {changedFiles: FileData; removedFiles: Set}; getSerializableSnapshot(): CacheData['fileSystemData']; getSha1(file: Path): null | undefined | string; getOrComputeSha1( @@ -335,10 +360,35 @@ export type HasteMapItem = { [platform: string]: HasteMapItemMetadata; }; export type HasteMapItemMetadata = [string, number]; +export interface FileSystemListener { + directoryAdded(canonicalPath: CanonicalPath): void; + directoryRemoved(canonicalPath: CanonicalPath): void; + fileAdded(canonicalPath: CanonicalPath, data: FileMetadata): void; + fileModified( + canonicalPath: CanonicalPath, + oldData: FileMetadata, + newData: FileMetadata, + ): void; + fileRemoved(canonicalPath: CanonicalPath, data: FileMetadata): void; +} +export interface ReadonlyFileSystemChanges { + readonly addedDirectories: Iterable; + readonly removedDirectories: Iterable; + readonly addedFiles: Iterable>; + readonly modifiedFiles: Iterable>; + readonly removedFiles: Iterable>; +} export interface MutableFileSystem extends FileSystem { - remove(filePath: Path): null | undefined | FileMetadata; - addOrModify(filePath: Path, fileMetadata: FileMetadata): void; - bulkAddOrModify(addedOrModifiedFiles: FileData): void; + remove(filePath: Path, listener?: FileSystemListener): void; + addOrModify( + filePath: Path, + fileMetadata: FileMetadata, + listener?: FileSystemListener, + ): void; + bulkAddOrModify( + addedOrModifiedFiles: FileData, + listener?: FileSystemListener, + ): void; } export type Path = string; export type ProcessFileFunction = ( @@ -378,6 +428,12 @@ export type WatcherBackendChangeEvent = relativePath: string; root: string; metadata?: void; + }> + | Readonly<{ + event: 'recrawl'; + clock?: ChangeEventClock; + relativePath: string; + root: string; }>; export type WatcherBackendOptions = Readonly<{ ignored: null | undefined | RegExp; diff --git a/packages/metro-file-map/types/index.d.ts b/packages/metro-file-map/types/index.d.ts index c083a8f29b..4f3fa28c50 100644 --- a/packages/metro-file-map/types/index.d.ts +++ b/packages/metro-file-map/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<806d228988308075b7a911c3dfb513d3>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { @@ -16,11 +23,11 @@ import type { ChangeEventMetadata, Console, FileData, - FileMapPlugin, FileSystem, HasteMapData, HasteMapItem, HType, + InputFileMapPlugin, PerfLoggerFactory, } from './flow-types'; @@ -36,6 +43,7 @@ export type { FileSystem, HasteMapData, HasteMapItem, + InputFileMapPlugin, }; export type InputOptions = Readonly<{ computeSha1?: null | undefined | boolean; @@ -43,7 +51,7 @@ export type InputOptions = Readonly<{ extensions: ReadonlyArray; forceNodeFilesystemAPI?: null | undefined | boolean; ignorePattern?: null | undefined | RegExp; - plugins?: ReadonlyArray; + plugins?: ReadonlyArray; retainAllFiles: boolean; rootDir: string; roots: ReadonlyArray; @@ -64,7 +72,6 @@ type HealthCheckOptions = Readonly<{ timeout: number; filePrefix: string; }>; -type AnyFileMapPlugin = FileMapPlugin; export {DiskCacheManager} from './cache/DiskCacheManager'; export {default as DependencyPlugin} from './plugins/DependencyPlugin'; export type {DependencyPluginOptions} from './plugins/DependencyPlugin'; diff --git a/packages/metro-file-map/types/lib/FileProcessor.d.ts b/packages/metro-file-map/types/lib/FileProcessor.d.ts index f64da6db5a..55469d524a 100644 --- a/packages/metro-file-map/types/lib/FileProcessor.d.ts +++ b/packages/metro-file-map/types/lib/FileProcessor.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<2ea213f753eef5de14cb8a27f68b9fa2>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/lib/FileProcessor.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro-file-map/types/lib/FileSystemChangeAggregator.d.ts b/packages/metro-file-map/types/lib/FileSystemChangeAggregator.d.ts new file mode 100644 index 0000000000..0a4b3ecd92 --- /dev/null +++ b/packages/metro-file-map/types/lib/FileSystemChangeAggregator.d.ts @@ -0,0 +1,40 @@ +/** + * 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. + * + * @noformat + * @oncall react_native + * @generated SignedSource<<5feda1b197530a9a5fdbc57200633ac5>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/lib/FileSystemChangeAggregator.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) + */ + +import type { + CanonicalPath, + FileMetadata, + FileSystemListener, + ReadonlyFileSystemChanges, +} from '../flow-types'; + +export declare class FileSystemChangeAggregator implements FileSystemListener { + directoryAdded(canonicalPath: CanonicalPath): void; + directoryRemoved(canonicalPath: CanonicalPath): void; + fileAdded(canonicalPath: CanonicalPath, data: FileMetadata): void; + fileModified( + canonicalPath: CanonicalPath, + oldData: FileMetadata, + newData: FileMetadata, + ): void; + fileRemoved(canonicalPath: CanonicalPath, data: FileMetadata): void; + getSize(): number; + getView(): ReadonlyFileSystemChanges; + getMappedView( + metadataMapFn: (metadata: FileMetadata) => T, + ): ReadonlyFileSystemChanges; +} diff --git a/packages/metro-file-map/types/lib/RootPathUtils.d.ts b/packages/metro-file-map/types/lib/RootPathUtils.d.ts index 090e68fabe..79d65e0262 100644 --- a/packages/metro-file-map/types/lib/RootPathUtils.d.ts +++ b/packages/metro-file-map/types/lib/RootPathUtils.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<5ecdb559fce5f5c6ed50df6e4eaebf20>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/lib/RootPathUtils.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export declare class RootPathUtils { diff --git a/packages/metro-file-map/types/lib/TreeFS.d.ts b/packages/metro-file-map/types/lib/TreeFS.d.ts index c7dcd95474..bf1293ee38 100644 --- a/packages/metro-file-map/types/lib/TreeFS.d.ts +++ b/packages/metro-file-map/types/lib/TreeFS.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<65a3c4140d459a56b8c949e52b32ea1b>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/lib/TreeFS.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { @@ -12,6 +19,7 @@ import type { FileData, FileMetadata, FileStats, + FileSystemListener, LookupResult, MutableFileSystem, Path, @@ -97,10 +105,10 @@ declare class TreeFS implements MutableFileSystem { getSerializableSnapshot(): CacheData['fileSystemData']; static fromDeserializedSnapshot(args: DeserializedSnapshotInput): TreeFS; getSize(mixedPath: Path): null | undefined | number; - getDifference(files: FileData): { - changedFiles: FileData; - removedFiles: Set; - }; + getDifference( + files: FileData, + options?: Readonly<{subpath?: string}>, + ): {changedFiles: FileData; removedFiles: Set}; getSha1(mixedPath: Path): null | undefined | string; getOrComputeSha1( mixedPath: Path, @@ -115,9 +123,16 @@ declare class TreeFS implements MutableFileSystem { * for example: `a/b.js` -> `./a/b.js` */ matchFiles(opts: MatchFilesOptions): Iterable; - addOrModify(mixedPath: Path, metadata: FileMetadata): void; - bulkAddOrModify(addedOrModifiedFiles: FileData): void; - remove(mixedPath: Path): null | undefined | FileMetadata; + addOrModify( + mixedPath: Path, + metadata: FileMetadata, + changeListener?: FileSystemListener, + ): void; + bulkAddOrModify( + addedOrModifiedFiles: FileData, + changeListener?: FileSystemListener, + ): void; + remove(mixedPath: Path, changeListener?: FileSystemListener): void; /** * Given a start path (which need not exist), a subpath and type, and * optionally a 'breakOnSegment', performs the following: diff --git a/packages/metro-file-map/types/lib/checkWatchmanCapabilities.d.ts b/packages/metro-file-map/types/lib/checkWatchmanCapabilities.d.ts index 55816411c5..6fcff5b748 100644 --- a/packages/metro-file-map/types/lib/checkWatchmanCapabilities.d.ts +++ b/packages/metro-file-map/types/lib/checkWatchmanCapabilities.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/lib/checkWatchmanCapabilities.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function checkWatchmanCapabilities( diff --git a/packages/metro-file-map/types/lib/normalizePathSeparatorsToPosix.d.ts b/packages/metro-file-map/types/lib/normalizePathSeparatorsToPosix.d.ts index c15bda1448..c5d39a99c2 100644 --- a/packages/metro-file-map/types/lib/normalizePathSeparatorsToPosix.d.ts +++ b/packages/metro-file-map/types/lib/normalizePathSeparatorsToPosix.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<30b5e6d2308dde108c136f95a59e3740>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/lib/normalizePathSeparatorsToPosix.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare const $$EXPORT_DEFAULT_DECLARATION$$: (filePath: string) => string; diff --git a/packages/metro-file-map/types/lib/normalizePathSeparatorsToSystem.d.ts b/packages/metro-file-map/types/lib/normalizePathSeparatorsToSystem.d.ts index c15bda1448..d45ff6b967 100644 --- a/packages/metro-file-map/types/lib/normalizePathSeparatorsToSystem.d.ts +++ b/packages/metro-file-map/types/lib/normalizePathSeparatorsToSystem.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<719a82b7670f09ecb97e007293fddfc6>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/lib/normalizePathSeparatorsToSystem.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare const $$EXPORT_DEFAULT_DECLARATION$$: (filePath: string) => string; diff --git a/packages/metro-file-map/types/lib/rootRelativeCacheKeys.d.ts b/packages/metro-file-map/types/lib/rootRelativeCacheKeys.d.ts index 8f82daada7..7ac8306df8 100644 --- a/packages/metro-file-map/types/lib/rootRelativeCacheKeys.d.ts +++ b/packages/metro-file-map/types/lib/rootRelativeCacheKeys.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/lib/rootRelativeCacheKeys.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {BuildParameters} from '../flow-types'; diff --git a/packages/metro-file-map/types/lib/sorting.d.ts b/packages/metro-file-map/types/lib/sorting.d.ts index e503c9aba4..75e4b84cbb 100644 --- a/packages/metro-file-map/types/lib/sorting.d.ts +++ b/packages/metro-file-map/types/lib/sorting.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<8805bc71542c6b43e940f8c5761ff187>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/lib/sorting.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export declare function compareStrings( diff --git a/packages/metro-file-map/types/plugins/DependencyPlugin.d.ts b/packages/metro-file-map/types/plugins/DependencyPlugin.d.ts index 051c254179..0fa9a41778 100644 --- a/packages/metro-file-map/types/plugins/DependencyPlugin.d.ts +++ b/packages/metro-file-map/types/plugins/DependencyPlugin.d.ts @@ -4,12 +4,18 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/DependencyPlugin.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { - FileMapDelta, FileMapPlugin, FileMapPluginInitOptions, FileMapPluginWorker, @@ -32,17 +38,7 @@ declare class DependencyPlugin initOptions: FileMapPluginInitOptions | null>, ): Promise; getSerializableSnapshot(): null; - bulkUpdate( - delta: FileMapDelta>, - ): void; - onNewOrModifiedFile( - relativeFilePath: string, - pluginData: null | undefined | ReadonlyArray, - ): void; - onRemovedFile( - relativeFilePath: string, - pluginData: null | undefined | ReadonlyArray, - ): void; + onChanged(): void; assertValid(): void; getCacheKey(): string; getWorker(): FileMapPluginWorker; diff --git a/packages/metro-file-map/types/plugins/HastePlugin.d.ts b/packages/metro-file-map/types/plugins/HastePlugin.d.ts index a41575a696..71d6cbc263 100644 --- a/packages/metro-file-map/types/plugins/HastePlugin.d.ts +++ b/packages/metro-file-map/types/plugins/HastePlugin.d.ts @@ -4,13 +4,19 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<3d1462ab2325a09553e02b69b5de84eb>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/HastePlugin.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { Console, - FileMapDelta, FileMapPlugin, FileMapPluginInitOptions, FileMapPluginWorker, @@ -20,6 +26,7 @@ import type { HTypeValue, Path, PerfLogger, + ReadonlyFileSystemChanges, } from '../flow-types'; export type HasteMapOptions = Readonly<{ @@ -52,16 +59,8 @@ declare class HastePlugin platform: null | undefined | string, _supportsNativePlatform?: null | undefined | boolean, ): null | undefined | Path; - bulkUpdate(delta: FileMapDelta): void; - onNewOrModifiedFile( - relativeFilePath: string, - id: null | undefined | string, - ): void; + onChanged(delta: ReadonlyFileSystemChanges): void; setModule(id: string, module: HasteMapItemMetadata): void; - onRemovedFile( - relativeFilePath: string, - moduleName: null | undefined | string, - ): void; assertValid(): void; computeConflicts(): Array; getCacheKey(): string; diff --git a/packages/metro-file-map/types/plugins/MockPlugin.d.ts b/packages/metro-file-map/types/plugins/MockPlugin.d.ts index a43cf0a56e..9d5abf4521 100644 --- a/packages/metro-file-map/types/plugins/MockPlugin.d.ts +++ b/packages/metro-file-map/types/plugins/MockPlugin.d.ts @@ -4,18 +4,25 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/MockPlugin.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { - FileMapDelta, FileMapPlugin, FileMapPluginInitOptions, FileMapPluginWorker, MockMap as IMockMap, Path, RawMockMap, + ReadonlyFileSystemChanges, } from '../flow-types'; export declare const CACHE_VERSION: 2; @@ -32,9 +39,7 @@ declare class MockPlugin implements FileMapPlugin, IMockMap { constructor($$PARAM_0$$: MockMapOptions); initialize($$PARAM_0$$: FileMapPluginInitOptions): Promise; getMockModule(name: string): null | undefined | Path; - bulkUpdate(delta: FileMapDelta): void; - onNewOrModifiedFile(relativeFilePath: Path): void; - onRemovedFile(relativeFilePath: Path): void; + onChanged(delta: ReadonlyFileSystemChanges): void; getSerializableSnapshot(): RawMockMap; assertValid(): void; getCacheKey(): string; diff --git a/packages/metro-file-map/types/plugins/dependencies/dependencyExtractor.d.ts b/packages/metro-file-map/types/plugins/dependencies/dependencyExtractor.d.ts index 308464c9fe..34ab913df3 100644 --- a/packages/metro-file-map/types/plugins/dependencies/dependencyExtractor.d.ts +++ b/packages/metro-file-map/types/plugins/dependencies/dependencyExtractor.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @generated by js1 build metro-ts-defs / yarn run build-ts-defs + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/dependencies/dependencyExtractor.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare const dependencyExtractor: { diff --git a/packages/metro-file-map/types/plugins/dependencies/worker.d.ts b/packages/metro-file-map/types/plugins/dependencies/worker.d.ts index c7b79538f0..cfa4d620f4 100644 --- a/packages/metro-file-map/types/plugins/dependencies/worker.d.ts +++ b/packages/metro-file-map/types/plugins/dependencies/worker.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<2e845e8720ef0522a5d4c30c30402f20>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/dependencies/worker.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro-file-map/types/plugins/haste/DuplicateHasteCandidatesError.d.ts b/packages/metro-file-map/types/plugins/haste/DuplicateHasteCandidatesError.d.ts index f66d95da0e..90f0c6a167 100644 --- a/packages/metro-file-map/types/plugins/haste/DuplicateHasteCandidatesError.d.ts +++ b/packages/metro-file-map/types/plugins/haste/DuplicateHasteCandidatesError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<2c991103bc4a71a81ef04de0884de576>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/haste/DuplicateHasteCandidatesError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {DuplicatesSet} from '../../flow-types'; diff --git a/packages/metro-file-map/types/plugins/haste/HasteConflictsError.d.ts b/packages/metro-file-map/types/plugins/haste/HasteConflictsError.d.ts index fe78a34b60..3f6c62d76b 100644 --- a/packages/metro-file-map/types/plugins/haste/HasteConflictsError.d.ts +++ b/packages/metro-file-map/types/plugins/haste/HasteConflictsError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<53c103ffe2115282c4d72593f47018aa>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/haste/HasteConflictsError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {HasteConflict} from '../../flow-types'; diff --git a/packages/metro-file-map/types/plugins/haste/computeConflicts.d.ts b/packages/metro-file-map/types/plugins/haste/computeConflicts.d.ts index fcd4195fb3..af0bc94823 100644 --- a/packages/metro-file-map/types/plugins/haste/computeConflicts.d.ts +++ b/packages/metro-file-map/types/plugins/haste/computeConflicts.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/haste/computeConflicts.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {HasteMapItem} from '../../flow-types'; diff --git a/packages/metro-file-map/types/plugins/haste/getPlatformExtension.d.ts b/packages/metro-file-map/types/plugins/haste/getPlatformExtension.d.ts index 23fbf107ab..8a4c14e963 100644 --- a/packages/metro-file-map/types/plugins/haste/getPlatformExtension.d.ts +++ b/packages/metro-file-map/types/plugins/haste/getPlatformExtension.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<3d628d7c2b6149348fcdc5782fc24bb7>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/haste/getPlatformExtension.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function getPlatformExtension( diff --git a/packages/metro-file-map/types/plugins/haste/worker.d.ts b/packages/metro-file-map/types/plugins/haste/worker.d.ts index eec44e52da..99566527d7 100644 --- a/packages/metro-file-map/types/plugins/haste/worker.d.ts +++ b/packages/metro-file-map/types/plugins/haste/worker.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<150098cafadeebb35978352da302d211>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/haste/worker.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro-file-map/types/plugins/mocks/getMockName.d.ts b/packages/metro-file-map/types/plugins/mocks/getMockName.d.ts index c15bda1448..f99b59655c 100644 --- a/packages/metro-file-map/types/plugins/mocks/getMockName.d.ts +++ b/packages/metro-file-map/types/plugins/mocks/getMockName.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<1c1794b89fa69eff13b6cd80bf0ab42d>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/plugins/mocks/getMockName.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare const $$EXPORT_DEFAULT_DECLARATION$$: (filePath: string) => string; diff --git a/packages/metro-file-map/types/watchers/AbstractWatcher.d.ts b/packages/metro-file-map/types/watchers/AbstractWatcher.d.ts index 2e73a75ab4..d5c6f47462 100644 --- a/packages/metro-file-map/types/watchers/AbstractWatcher.d.ts +++ b/packages/metro-file-map/types/watchers/AbstractWatcher.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/watchers/AbstractWatcher.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro-file-map/types/watchers/FallbackWatcher.d.ts b/packages/metro-file-map/types/watchers/FallbackWatcher.d.ts index 728c11131f..8ee35f2919 100644 --- a/packages/metro-file-map/types/watchers/FallbackWatcher.d.ts +++ b/packages/metro-file-map/types/watchers/FallbackWatcher.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<5152d1919d3373e4df611e0fca805e1c>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/watchers/FallbackWatcher.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import {AbstractWatcher} from './AbstractWatcher'; diff --git a/packages/metro-file-map/types/watchers/NativeWatcher.d.ts b/packages/metro-file-map/types/watchers/NativeWatcher.d.ts index f48047dcca..b118598f7e 100644 --- a/packages/metro-file-map/types/watchers/NativeWatcher.d.ts +++ b/packages/metro-file-map/types/watchers/NativeWatcher.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/watchers/NativeWatcher.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import {AbstractWatcher} from './AbstractWatcher'; @@ -43,6 +50,6 @@ declare class NativeWatcher extends AbstractWatcher { * End watching. */ stopWatching(): Promise; - _handleEvent(relativePath: string): void; + _handleEvent(event: string, relativePath: string): void; } export default NativeWatcher; diff --git a/packages/metro-file-map/types/watchers/RecrawlWarning.d.ts b/packages/metro-file-map/types/watchers/RecrawlWarning.d.ts index a106b22b43..51226bb96f 100644 --- a/packages/metro-file-map/types/watchers/RecrawlWarning.d.ts +++ b/packages/metro-file-map/types/watchers/RecrawlWarning.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/watchers/RecrawlWarning.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-file-map/types/watchers/WatchmanWatcher.d.ts b/packages/metro-file-map/types/watchers/WatchmanWatcher.d.ts index 90bb495ced..d558b256dc 100644 --- a/packages/metro-file-map/types/watchers/WatchmanWatcher.d.ts +++ b/packages/metro-file-map/types/watchers/WatchmanWatcher.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/watchers/WatchmanWatcher.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {WatcherOptions} from './common'; diff --git a/packages/metro-file-map/types/watchers/common.d.ts b/packages/metro-file-map/types/watchers/common.d.ts index bdfa8d83df..a9612932ba 100644 --- a/packages/metro-file-map/types/watchers/common.d.ts +++ b/packages/metro-file-map/types/watchers/common.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/watchers/common.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** @@ -22,6 +29,8 @@ export declare const DELETE_EVENT: 'delete'; export declare type DELETE_EVENT = typeof DELETE_EVENT; export declare const TOUCH_EVENT: 'touch'; export declare type TOUCH_EVENT = typeof TOUCH_EVENT; +export declare const RECRAWL_EVENT: 'recrawl'; +export declare type RECRAWL_EVENT = typeof RECRAWL_EVENT; export declare const ALL_EVENT: 'all'; export declare type ALL_EVENT = typeof ALL_EVENT; export type WatcherOptions = Readonly<{ diff --git a/packages/metro-file-map/types/worker.d.ts b/packages/metro-file-map/types/worker.d.ts index 1c1e3d94c2..34933fb3bb 100644 --- a/packages/metro-file-map/types/worker.d.ts +++ b/packages/metro-file-map/types/worker.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<40f8f3a5c3f7effaaada900336673157>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/worker.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro-file-map/types/workerExclusionList.d.ts b/packages/metro-file-map/types/workerExclusionList.d.ts index ac3fba8b76..3bc5ca75b8 100644 --- a/packages/metro-file-map/types/workerExclusionList.d.ts +++ b/packages/metro-file-map/types/workerExclusionList.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<532e56f03246c9dc2d9607e3b3c25058>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-file-map/src/workerExclusionList.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare const exclusionList: ReadonlySet; diff --git a/packages/metro-minify-terser/package.json b/packages/metro-minify-terser/package.json index 0c28bb0821..3428260f7f 100644 --- a/packages/metro-minify-terser/package.json +++ b/packages/metro-minify-terser/package.json @@ -1,6 +1,6 @@ { "name": "metro-minify-terser", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Minifier for Metro based on Terser.", "main": "src/index.js", "exports": { diff --git a/packages/metro-minify-terser/types/index.d.ts b/packages/metro-minify-terser/types/index.d.ts index 39ef5fc46f..caaa15cde1 100644 --- a/packages/metro-minify-terser/types/index.d.ts +++ b/packages/metro-minify-terser/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<0389df6d8e7a843e3d441820cfd4350b>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-minify-terser/src/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import minifier from './minifier'; diff --git a/packages/metro-minify-terser/types/minifier.d.ts b/packages/metro-minify-terser/types/minifier.d.ts index 8d94a82345..565cb29283 100644 --- a/packages/metro-minify-terser/types/minifier.d.ts +++ b/packages/metro-minify-terser/types/minifier.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-minify-terser/src/minifier.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {MinifierOptions, MinifierResult} from 'metro-transform-worker'; diff --git a/packages/metro-resolver/package.json b/packages/metro-resolver/package.json index e0e06c87bd..be1081f410 100644 --- a/packages/metro-resolver/package.json +++ b/packages/metro-resolver/package.json @@ -1,6 +1,6 @@ { "name": "metro-resolver", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Implementation of Metro's resolution logic.", "main": "src/index.js", "exports": { @@ -22,7 +22,7 @@ "node": ">=20.19.4" }, "devDependencies": { - "metro": "0.83.5" + "metro": "0.83.6" }, "dependencies": { "flow-enums-runtime": "^0.0.6" diff --git a/packages/metro-resolver/types/PackageExportsResolve.d.ts b/packages/metro-resolver/types/PackageExportsResolve.d.ts index 033ec0be26..4738f179ad 100644 --- a/packages/metro-resolver/types/PackageExportsResolve.d.ts +++ b/packages/metro-resolver/types/PackageExportsResolve.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<7490133a41b70c6a0855b73fd990a5e3>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/PackageExportsResolve.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ExportsField, FileResolution, ResolutionContext} from './types'; diff --git a/packages/metro-resolver/types/PackageImportsResolve.d.ts b/packages/metro-resolver/types/PackageImportsResolve.d.ts index 124f531778..e16d6cf3a0 100644 --- a/packages/metro-resolver/types/PackageImportsResolve.d.ts +++ b/packages/metro-resolver/types/PackageImportsResolve.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/PackageImportsResolve.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ExportsLikeMap, FileResolution, ResolutionContext} from './types'; diff --git a/packages/metro-resolver/types/PackageResolve.d.ts b/packages/metro-resolver/types/PackageResolve.d.ts index c4e674ccd4..00327004b0 100644 --- a/packages/metro-resolver/types/PackageResolve.d.ts +++ b/packages/metro-resolver/types/PackageResolve.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<7e880beb8b73f4a072dfd248ff41d7b0>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/PackageResolve.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {PackageInfo, ResolutionContext} from './types'; diff --git a/packages/metro-resolver/types/createDefaultContext.d.ts b/packages/metro-resolver/types/createDefaultContext.d.ts index 86096cec2b..9c9f7340d8 100644 --- a/packages/metro-resolver/types/createDefaultContext.d.ts +++ b/packages/metro-resolver/types/createDefaultContext.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/createDefaultContext.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ResolutionContext} from './types'; diff --git a/packages/metro-resolver/types/errors/FailedToResolveNameError.d.ts b/packages/metro-resolver/types/errors/FailedToResolveNameError.d.ts index e114242cbc..9203735216 100644 --- a/packages/metro-resolver/types/errors/FailedToResolveNameError.d.ts +++ b/packages/metro-resolver/types/errors/FailedToResolveNameError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/errors/FailedToResolveNameError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare class FailedToResolveNameError extends Error { diff --git a/packages/metro-resolver/types/errors/FailedToResolvePathError.d.ts b/packages/metro-resolver/types/errors/FailedToResolvePathError.d.ts index ec865d28be..380b61f889 100644 --- a/packages/metro-resolver/types/errors/FailedToResolvePathError.d.ts +++ b/packages/metro-resolver/types/errors/FailedToResolvePathError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<1e0fa2d1bab2971504a4c271d453dc29>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/errors/FailedToResolvePathError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {FileAndDirCandidates} from '../types'; diff --git a/packages/metro-resolver/types/errors/FailedToResolveUnsupportedError.d.ts b/packages/metro-resolver/types/errors/FailedToResolveUnsupportedError.d.ts index 2eec91a8da..d233efdd97 100644 --- a/packages/metro-resolver/types/errors/FailedToResolveUnsupportedError.d.ts +++ b/packages/metro-resolver/types/errors/FailedToResolveUnsupportedError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/errors/FailedToResolveUnsupportedError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare class FailedToResolveUnsupportedError extends Error { diff --git a/packages/metro-resolver/types/errors/InvalidPackageConfigurationError.d.ts b/packages/metro-resolver/types/errors/InvalidPackageConfigurationError.d.ts index a5e6995a60..100ab25dce 100644 --- a/packages/metro-resolver/types/errors/InvalidPackageConfigurationError.d.ts +++ b/packages/metro-resolver/types/errors/InvalidPackageConfigurationError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<109d7323b70ba3a4582f5868df075ffc>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/errors/InvalidPackageConfigurationError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-resolver/types/errors/InvalidPackageError.d.ts b/packages/metro-resolver/types/errors/InvalidPackageError.d.ts index 51b42b178b..0d7ec65cd3 100644 --- a/packages/metro-resolver/types/errors/InvalidPackageError.d.ts +++ b/packages/metro-resolver/types/errors/InvalidPackageError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<47b272bf4a763f5c68eed66346ee74e9>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/errors/InvalidPackageError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {FileCandidates} from '../types'; diff --git a/packages/metro-resolver/types/errors/PackageImportNotResolvedError.d.ts b/packages/metro-resolver/types/errors/PackageImportNotResolvedError.d.ts index 8ac2c5b863..200615e508 100644 --- a/packages/metro-resolver/types/errors/PackageImportNotResolvedError.d.ts +++ b/packages/metro-resolver/types/errors/PackageImportNotResolvedError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<5d012a93c58cbef8b5b315d70cb4fd5a>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/errors/PackageImportNotResolvedError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-resolver/types/errors/PackagePathNotExportedError.d.ts b/packages/metro-resolver/types/errors/PackagePathNotExportedError.d.ts index 591e4297db..29a35d48ae 100644 --- a/packages/metro-resolver/types/errors/PackagePathNotExportedError.d.ts +++ b/packages/metro-resolver/types/errors/PackagePathNotExportedError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/errors/PackagePathNotExportedError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-resolver/types/errors/formatFileCandidates.d.ts b/packages/metro-resolver/types/errors/formatFileCandidates.d.ts index 48d518d0e6..0223f5b5ef 100644 --- a/packages/metro-resolver/types/errors/formatFileCandidates.d.ts +++ b/packages/metro-resolver/types/errors/formatFileCandidates.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<648dc80101f337456690a8a403891952>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/errors/formatFileCandidates.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {FileCandidates} from '../types'; diff --git a/packages/metro-resolver/types/index.d.ts b/packages/metro-resolver/types/index.d.ts index d363312715..7e1e13f67a 100644 --- a/packages/metro-resolver/types/index.d.ts +++ b/packages/metro-resolver/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export type { diff --git a/packages/metro-resolver/types/resolve.d.ts b/packages/metro-resolver/types/resolve.d.ts index 574056539d..fc1775ca1b 100644 --- a/packages/metro-resolver/types/resolve.d.ts +++ b/packages/metro-resolver/types/resolve.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<56f6e00225ee5ece6142bb2b9e4c608d>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/resolve.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Resolution, ResolutionContext} from './types'; diff --git a/packages/metro-resolver/types/resolveAsset.d.ts b/packages/metro-resolver/types/resolveAsset.d.ts index e3b3ff4f0d..a211fb16fd 100644 --- a/packages/metro-resolver/types/resolveAsset.d.ts +++ b/packages/metro-resolver/types/resolveAsset.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/resolveAsset.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {AssetResolution, ResolutionContext} from './types'; diff --git a/packages/metro-resolver/types/types.d.ts b/packages/metro-resolver/types/types.d.ts index d6e11a5bfa..5adec82e58 100644 --- a/packages/metro-resolver/types/types.d.ts +++ b/packages/metro-resolver/types/types.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/types.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {TransformResultDependency} from 'metro/private/DeltaBundler/types'; diff --git a/packages/metro-resolver/types/utils/isAssetFile.d.ts b/packages/metro-resolver/types/utils/isAssetFile.d.ts index f09d7bd8d8..b0358eee2b 100644 --- a/packages/metro-resolver/types/utils/isAssetFile.d.ts +++ b/packages/metro-resolver/types/utils/isAssetFile.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/utils/isAssetFile.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-resolver/types/utils/isSubpathDefinedInExportsLike.d.ts b/packages/metro-resolver/types/utils/isSubpathDefinedInExportsLike.d.ts index 15f71e28f7..80f07222ed 100644 --- a/packages/metro-resolver/types/utils/isSubpathDefinedInExportsLike.d.ts +++ b/packages/metro-resolver/types/utils/isSubpathDefinedInExportsLike.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/utils/isSubpathDefinedInExportsLike.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-resolver/types/utils/matchSubpathFromExportsLike.d.ts b/packages/metro-resolver/types/utils/matchSubpathFromExportsLike.d.ts index 8e3e95a8b8..5470ca4188 100644 --- a/packages/metro-resolver/types/utils/matchSubpathFromExportsLike.d.ts +++ b/packages/metro-resolver/types/utils/matchSubpathFromExportsLike.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<2de88d23d3cf26217cd8ae8ada6c79b7>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/utils/matchSubpathFromExportsLike.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {NormalizedExportsLikeMap, ResolutionContext} from '../types'; diff --git a/packages/metro-resolver/types/utils/matchSubpathPattern.d.ts b/packages/metro-resolver/types/utils/matchSubpathPattern.d.ts index b4d2e774e3..5d114079aa 100644 --- a/packages/metro-resolver/types/utils/matchSubpathPattern.d.ts +++ b/packages/metro-resolver/types/utils/matchSubpathPattern.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<40375a98e9c03360973e0cf65b702ced>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/utils/matchSubpathPattern.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-resolver/types/utils/reduceExportsLikeMap.d.ts b/packages/metro-resolver/types/utils/reduceExportsLikeMap.d.ts index 4add966aa9..c64fa52459 100644 --- a/packages/metro-resolver/types/utils/reduceExportsLikeMap.d.ts +++ b/packages/metro-resolver/types/utils/reduceExportsLikeMap.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<0bb41edd0d0c6f257cb95ca9b7b00aa9>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/utils/reduceExportsLikeMap.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-resolver/types/utils/toPosixPath.d.ts b/packages/metro-resolver/types/utils/toPosixPath.d.ts index 2f87e35ad7..c787f3163a 100644 --- a/packages/metro-resolver/types/utils/toPosixPath.d.ts +++ b/packages/metro-resolver/types/utils/toPosixPath.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<091df9100cc8f841af449036a548f6aa>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-resolver/src/utils/toPosixPath.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-runtime/package.json b/packages/metro-runtime/package.json index 578f47ddbb..5bb0ca7537 100644 --- a/packages/metro-runtime/package.json +++ b/packages/metro-runtime/package.json @@ -1,6 +1,6 @@ { "name": "metro-runtime", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Module required for evaluating Metro bundles.", "exports": { "./package.json": "./package.json", diff --git a/packages/metro-runtime/src/modules/HMRClient.js b/packages/metro-runtime/src/modules/HMRClient.js index ef6c214c5e..6a1d1916d7 100644 --- a/packages/metro-runtime/src/modules/HMRClient.js +++ b/packages/metro-runtime/src/modules/HMRClient.js @@ -15,6 +15,8 @@ import type {HmrMessage, HmrUpdate} from './types'; const EventEmitter = require('./vendor/eventemitter3'); +const HEARTBEAT_INTERVAL_MS = 20_000; + type SocketState = 'opening' | 'open' | 'closed'; const inject = ({module: [id, code], sourceURL}: HmrModule) => { @@ -39,6 +41,7 @@ class HMRClient extends EventEmitter { _queue: Array = []; _state: SocketState = 'opening'; _ws: WebSocket; + _heartbeatTimer: ?IntervalID = null; constructor(url: string) { super(); @@ -48,6 +51,7 @@ class HMRClient extends EventEmitter { this._ws = new global.WebSocket(url); this._ws.onopen = () => { this._state = 'open'; + this._startHeartbeat(); this.emit('open'); this._flushQueue(); }; @@ -56,12 +60,17 @@ class HMRClient extends EventEmitter { }; this._ws.onclose = closeEvent => { this._state = 'closed'; + this._stopHeartbeat(); this.emit('close', closeEvent); }; this._ws.onmessage = message => { const data: HmrMessage = JSON.parse(String(message.data)); switch (data.type) { + case 'heartbeat': + // Not exposed to consumers + break; + case 'bundle-registered': this.emit('bundle-registered'); break; @@ -75,7 +84,7 @@ class HMRClient extends EventEmitter { break; case 'update-done': - this.emit('update-done'); + this.emit('update-done', data.body); break; case 'error': @@ -123,6 +132,22 @@ class HMRClient extends EventEmitter { this._queue.length = 0; } + _startHeartbeat(): void { + this._stopHeartbeat(); + this._heartbeatTimer = setInterval(() => { + if (this._state === 'open') { + this._ws.send('{"type":"heartbeat"}'); + } + }, HEARTBEAT_INTERVAL_MS); + } + + _stopHeartbeat(): void { + if (this._heartbeatTimer != null) { + clearInterval(this._heartbeatTimer); + this._heartbeatTimer = null; + } + } + enable() { this._isEnabled = true; const update = this._pendingUpdate; diff --git a/packages/metro-runtime/src/modules/types.js b/packages/metro-runtime/src/modules/types.js index 5b72cc69d3..91df116db3 100644 --- a/packages/metro-runtime/src/modules/types.js +++ b/packages/metro-runtime/src/modules/types.js @@ -83,6 +83,9 @@ export type HmrClientMessage = } | { +type: 'log-opt-in', + } + | { + +type: 'heartbeat', }; export type HmrMessage = @@ -97,6 +100,10 @@ export type HmrMessage = } | { +type: 'update-done', + +body?: {+changeId?: string}, } | HmrUpdateMessage - | HmrErrorMessage; + | HmrErrorMessage + | { + +type: 'heartbeat', + }; diff --git a/packages/metro-runtime/src/polyfills/__tests__/MetroFastRefreshMockRuntime.js b/packages/metro-runtime/src/polyfills/__tests__/MetroFastRefreshMockRuntime.js index 97e102bf3c..d24841019f 100644 --- a/packages/metro-runtime/src/polyfills/__tests__/MetroFastRefreshMockRuntime.js +++ b/packages/metro-runtime/src/polyfills/__tests__/MetroFastRefreshMockRuntime.js @@ -10,7 +10,7 @@ */ import type {DefineFn, RequireFn} from '../require'; -import typeof React from 'react'; +import type * as ReactModule from 'react'; import typeof ReactRefreshRuntime from 'react-refresh/runtime'; import typeof ReactTestRenderer from 'react-test-renderer'; @@ -47,7 +47,7 @@ export class Runtime { * The instance of React running in this runtime. Conceptually equivalent to * require('react'). */ - React: React; + React: typeof ReactModule; /** * The React renderer running in this runtime. Conceptually equivalent to @@ -86,8 +86,8 @@ export class Runtime { // Set up Fast Refresh. Adapted from `setUpReactRefresh.js` in React Native. jest.isolateModules(() => { - // $FlowFixMe[incompatible-type] Not sure why Flow doesn't approve - // $FlowFixMe[prop-missing] + // Configure the act environment for React 19 + global.IS_REACT_ACT_ENVIRONMENT = true; this.React = require('react'); this.#reactRefreshRuntime = require('react-refresh/runtime'); @@ -121,7 +121,9 @@ export class Runtime { this.events.onFullReload('Fast Refresh - Unrecoverable'); return; } - this.#reactRefreshRuntime.performReactRefresh(); + this.renderer.act(() => { + this.#reactRefreshRuntime.performReactRefresh(); + }); this.events.onFastRefresh(); }, }; diff --git a/packages/metro-runtime/types/modules/types.d.ts b/packages/metro-runtime/types/modules/types.d.ts index 3e23c41444..ea0cf8075b 100644 --- a/packages/metro-runtime/types/modules/types.d.ts +++ b/packages/metro-runtime/types/modules/types.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<117ae8d35a498c8c16f22a36d6ee14ef>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-runtime/src/modules/types.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export type ModuleMap = ReadonlyArray<[number, string]>; @@ -80,13 +87,18 @@ export type HmrClientMessage = | 'debug'; readonly data: Array; } - | {readonly type: 'log-opt-in'}; + | {readonly type: 'log-opt-in'} + | {readonly type: 'heartbeat'}; export type HmrMessage = | {readonly type: 'bundle-registered'} | { readonly type: 'update-start'; readonly body: {readonly isInitialUpdate: boolean}; } - | {readonly type: 'update-done'} + | { + readonly type: 'update-done'; + readonly body?: {readonly changeId?: string}; + } | HmrUpdateMessage - | HmrErrorMessage; + | HmrErrorMessage + | {readonly type: 'heartbeat'}; diff --git a/packages/metro-source-map/package.json b/packages/metro-source-map/package.json index 35d79b332a..2d849eca4c 100644 --- a/packages/metro-source-map/package.json +++ b/packages/metro-source-map/package.json @@ -1,6 +1,6 @@ { "name": "metro-source-map", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Source map generator for Metro.", "main": "src/source-map.js", "exports": { @@ -22,9 +22,9 @@ "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-symbolicate": "0.83.5", + "metro-symbolicate": "0.83.6", "nullthrows": "^1.1.1", - "ob1": "0.83.5", + "ob1": "0.83.6", "source-map": "^0.5.6", "vlq": "^1.0.0" }, diff --git a/packages/metro-source-map/src/Consumer/positionMath.js b/packages/metro-source-map/src/Consumer/positionMath.js index 51dfe19f68..9e488f302d 100644 --- a/packages/metro-source-map/src/Consumer/positionMath.js +++ b/packages/metro-source-map/src/Consumer/positionMath.js @@ -15,7 +15,7 @@ import type {Number0, Number1} from 'ob1'; import {add, add0, add1, neg} from 'ob1'; export function shiftPositionByOffset< - T: { + T extends { +line: ?Number1, +column: ?Number0, ... @@ -29,7 +29,7 @@ export function shiftPositionByOffset< } export function subtractOffsetFromPosition< - T: { + T extends { +line: ?Number1, +column: ?Number0, ... diff --git a/packages/metro-source-map/types/B64Builder.d.ts b/packages/metro-source-map/types/B64Builder.d.ts index b6402525ee..e84d86df1a 100644 --- a/packages/metro-source-map/types/B64Builder.d.ts +++ b/packages/metro-source-map/types/B64Builder.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<26fabb3db2058dda9c2a6e56de4728ce>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/B64Builder.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-source-map/types/BundleBuilder.d.ts b/packages/metro-source-map/types/BundleBuilder.d.ts index d63cc0ad89..db2f1d2069 100644 --- a/packages/metro-source-map/types/BundleBuilder.d.ts +++ b/packages/metro-source-map/types/BundleBuilder.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/BundleBuilder.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {IndexMap, IndexMapSection, MixedSourceMap} from './source-map'; diff --git a/packages/metro-source-map/types/Consumer/AbstractConsumer.d.ts b/packages/metro-source-map/types/Consumer/AbstractConsumer.d.ts index c51c71a7d1..ed977fdc92 100644 --- a/packages/metro-source-map/types/Consumer/AbstractConsumer.d.ts +++ b/packages/metro-source-map/types/Consumer/AbstractConsumer.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<22565876862bd94effefabfc09cf8933>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/AbstractConsumer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro-source-map/types/Consumer/DelegatingConsumer.d.ts b/packages/metro-source-map/types/Consumer/DelegatingConsumer.d.ts index 40381a996f..886974c20a 100644 --- a/packages/metro-source-map/types/Consumer/DelegatingConsumer.d.ts +++ b/packages/metro-source-map/types/Consumer/DelegatingConsumer.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<25b3906d78ba99d86fb91390016332ff>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/DelegatingConsumer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {MixedSourceMap} from '../source-map'; diff --git a/packages/metro-source-map/types/Consumer/MappingsConsumer.d.ts b/packages/metro-source-map/types/Consumer/MappingsConsumer.d.ts index dfadbaa5a8..00a33ff444 100644 --- a/packages/metro-source-map/types/Consumer/MappingsConsumer.d.ts +++ b/packages/metro-source-map/types/Consumer/MappingsConsumer.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/MappingsConsumer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {BasicSourceMap} from '../source-map'; diff --git a/packages/metro-source-map/types/Consumer/SectionsConsumer.d.ts b/packages/metro-source-map/types/Consumer/SectionsConsumer.d.ts index 865199acd4..4d0250b3cd 100644 --- a/packages/metro-source-map/types/Consumer/SectionsConsumer.d.ts +++ b/packages/metro-source-map/types/Consumer/SectionsConsumer.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<19e73dfc942bfc06b0a44f0488b16947>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/SectionsConsumer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {IndexMap} from '../source-map'; diff --git a/packages/metro-source-map/types/Consumer/constants.d.ts b/packages/metro-source-map/types/Consumer/constants.d.ts index 145c66c6c8..b6fc38a487 100644 --- a/packages/metro-source-map/types/Consumer/constants.d.ts +++ b/packages/metro-source-map/types/Consumer/constants.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/constants.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Number0, Number1} from 'ob1'; diff --git a/packages/metro-source-map/types/Consumer/createConsumer.d.ts b/packages/metro-source-map/types/Consumer/createConsumer.d.ts index 47e9856133..6500b7314b 100644 --- a/packages/metro-source-map/types/Consumer/createConsumer.d.ts +++ b/packages/metro-source-map/types/Consumer/createConsumer.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<3628df6a457f3d3d7c15f9e248338e4e>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/createConsumer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {MixedSourceMap} from '../source-map'; diff --git a/packages/metro-source-map/types/Consumer/index.d.ts b/packages/metro-source-map/types/Consumer/index.d.ts index bb85e48e1a..aa6164ee96 100644 --- a/packages/metro-source-map/types/Consumer/index.d.ts +++ b/packages/metro-source-map/types/Consumer/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<62d8c1b54e47245bd0ef831be2b7049d>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import DelegatingConsumer from './DelegatingConsumer'; diff --git a/packages/metro-source-map/types/Consumer/normalizeSourcePath.d.ts b/packages/metro-source-map/types/Consumer/normalizeSourcePath.d.ts index cd6deb84e4..9dc94efa22 100644 --- a/packages/metro-source-map/types/Consumer/normalizeSourcePath.d.ts +++ b/packages/metro-source-map/types/Consumer/normalizeSourcePath.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/normalizeSourcePath.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function normalizeSourcePath( diff --git a/packages/metro-source-map/types/Consumer/positionMath.d.ts b/packages/metro-source-map/types/Consumer/positionMath.d.ts index 6af98d4c34..9a479f8e1f 100644 --- a/packages/metro-source-map/types/Consumer/positionMath.d.ts +++ b/packages/metro-source-map/types/Consumer/positionMath.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<6db8c7c1cbb86a47de92e1b9565dd624>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/positionMath.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {GeneratedOffset} from './types'; diff --git a/packages/metro-source-map/types/Consumer/search.d.ts b/packages/metro-source-map/types/Consumer/search.d.ts index c23db5c75c..cbecf90b30 100644 --- a/packages/metro-source-map/types/Consumer/search.d.ts +++ b/packages/metro-source-map/types/Consumer/search.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/search.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export declare function greatestLowerBound( diff --git a/packages/metro-source-map/types/Consumer/types.d.ts b/packages/metro-source-map/types/Consumer/types.d.ts index 88132dab6f..310c547167 100644 --- a/packages/metro-source-map/types/Consumer/types.d.ts +++ b/packages/metro-source-map/types/Consumer/types.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<5fbef54d757c6130889a1889f7d71255>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Consumer/types.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {IterationOrder, LookupBias} from './constants'; diff --git a/packages/metro-source-map/types/Generator.d.ts b/packages/metro-source-map/types/Generator.d.ts index 2aa73e7761..beb1f43c3b 100644 --- a/packages/metro-source-map/types/Generator.d.ts +++ b/packages/metro-source-map/types/Generator.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/Generator.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro-source-map/types/composeSourceMaps.d.ts b/packages/metro-source-map/types/composeSourceMaps.d.ts index bf5002f055..fcba0d0155 100644 --- a/packages/metro-source-map/types/composeSourceMaps.d.ts +++ b/packages/metro-source-map/types/composeSourceMaps.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/composeSourceMaps.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {MixedSourceMap} from './source-map'; diff --git a/packages/metro-source-map/types/encode.d.ts b/packages/metro-source-map/types/encode.d.ts index 0f81355b8d..8e1562af73 100644 --- a/packages/metro-source-map/types/encode.d.ts +++ b/packages/metro-source-map/types/encode.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/encode.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-source-map/types/generateFunctionMap.d.ts b/packages/metro-source-map/types/generateFunctionMap.d.ts index 6adf227681..6fc3126f61 100644 --- a/packages/metro-source-map/types/generateFunctionMap.d.ts +++ b/packages/metro-source-map/types/generateFunctionMap.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<6f37d9027ca5bd3db3a9752a9498ab11>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/generateFunctionMap.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {FBSourceFunctionMap} from './source-map'; diff --git a/packages/metro-source-map/types/source-map.d.ts b/packages/metro-source-map/types/source-map.d.ts index b1ab54c477..cadc3b0109 100644 --- a/packages/metro-source-map/types/source-map.d.ts +++ b/packages/metro-source-map/types/source-map.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<7303fe7149cb12d764c6106cdf4f49ee>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-source-map/src/source-map.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {IConsumer} from './Consumer/types'; diff --git a/packages/metro-symbolicate/package.json b/packages/metro-symbolicate/package.json index 268bc22b17..f53c8a99a5 100644 --- a/packages/metro-symbolicate/package.json +++ b/packages/metro-symbolicate/package.json @@ -1,6 +1,6 @@ { "name": "metro-symbolicate", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 A tool to find the source location from JS bundles and stack traces.", "license": "MIT", "main": "./src/index.js", @@ -25,7 +25,7 @@ "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-source-map": "0.83.5", + "metro-source-map": "0.83.6", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" diff --git a/packages/metro-symbolicate/types/ChromeHeapSnapshot.d.ts b/packages/metro-symbolicate/types/ChromeHeapSnapshot.d.ts index 57fd1d03ef..a03ec6195e 100644 --- a/packages/metro-symbolicate/types/ChromeHeapSnapshot.d.ts +++ b/packages/metro-symbolicate/types/ChromeHeapSnapshot.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-symbolicate/src/ChromeHeapSnapshot.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ type RawBuffer = Array; diff --git a/packages/metro-symbolicate/types/GoogleIgnoreListConsumer.d.ts b/packages/metro-symbolicate/types/GoogleIgnoreListConsumer.d.ts index 84c52c25c8..1515754fee 100644 --- a/packages/metro-symbolicate/types/GoogleIgnoreListConsumer.d.ts +++ b/packages/metro-symbolicate/types/GoogleIgnoreListConsumer.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<37cd1bfec704014c5260f0fd26c787dc>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-symbolicate/src/GoogleIgnoreListConsumer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {MixedSourceMap} from 'metro-source-map'; diff --git a/packages/metro-symbolicate/types/SourceMetadataMapConsumer.d.ts b/packages/metro-symbolicate/types/SourceMetadataMapConsumer.d.ts index 1231a1fc61..0d2fcd0962 100644 --- a/packages/metro-symbolicate/types/SourceMetadataMapConsumer.d.ts +++ b/packages/metro-symbolicate/types/SourceMetadataMapConsumer.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<89d74f0d1fa133d80231a50f47338e1b>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-symbolicate/src/SourceMetadataMapConsumer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro-symbolicate/types/Symbolication.d.ts b/packages/metro-symbolicate/types/Symbolication.d.ts index 9982917d8e..3bd32fc30c 100644 --- a/packages/metro-symbolicate/types/Symbolication.d.ts +++ b/packages/metro-symbolicate/types/Symbolication.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-symbolicate/src/Symbolication.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ChromeHeapSnapshot} from './ChromeHeapSnapshot'; diff --git a/packages/metro-symbolicate/types/index.d.ts b/packages/metro-symbolicate/types/index.d.ts index 84510675f4..882d13b554 100644 --- a/packages/metro-symbolicate/types/index.d.ts +++ b/packages/metro-symbolicate/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<50f7c87b6ec9b7a4297b3ee8b2be2e91>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-symbolicate/src/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ // No exports diff --git a/packages/metro-symbolicate/types/symbolicate.d.ts b/packages/metro-symbolicate/types/symbolicate.d.ts index 75672fd816..acae617310 100644 --- a/packages/metro-symbolicate/types/symbolicate.d.ts +++ b/packages/metro-symbolicate/types/symbolicate.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<514225e55c0b3bbdbef6f68fb16f4085>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-symbolicate/src/symbolicate.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Readable, Writable} from 'stream'; diff --git a/packages/metro-transform-plugins/package.json b/packages/metro-transform-plugins/package.json index 7daeeb59e1..96d6ea080a 100644 --- a/packages/metro-transform-plugins/package.json +++ b/packages/metro-transform-plugins/package.json @@ -1,6 +1,6 @@ { "name": "metro-transform-plugins", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Transform plugins for Metro.", "main": "src/index.js", "exports": { @@ -33,7 +33,7 @@ "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/types": "^7.29.0", "babel-plugin-tester": "^6.0.1", - "metro": "0.83.5" + "metro": "0.83.6" }, "engines": { "node": ">=20.19.4" diff --git a/packages/metro-transform-plugins/src/__mocks__/test-helpers.js b/packages/metro-transform-plugins/src/__mocks__/test-helpers.js index 145a2bb6a1..785dbdf50d 100644 --- a/packages/metro-transform-plugins/src/__mocks__/test-helpers.js +++ b/packages/metro-transform-plugins/src/__mocks__/test-helpers.js @@ -18,7 +18,7 @@ const generate = require('@babel/generator').default; const t = require('@babel/types'); const nullthrows = require('nullthrows'); -function makeTransformOptions( +function makeTransformOptions( plugins: ReadonlyArray, options: OptionsT, ): BabelCoreOptions { @@ -51,7 +51,7 @@ function validateOutputAst(ast: BabelNode) { }); } -function transformToAst( +function transformToAst( plugins: ReadonlyArray, code: string, options: T, diff --git a/packages/metro-transform-plugins/src/import-export-plugin.js b/packages/metro-transform-plugins/src/import-export-plugin.js index b2f427e2ec..f93e04aea6 100644 --- a/packages/metro-transform-plugins/src/import-export-plugin.js +++ b/packages/metro-transform-plugins/src/import-export-plugin.js @@ -115,7 +115,7 @@ const resolveTemplate = template.expression(` /** * Enforces the resolution of a path to a fully-qualified one, if set. */ -function resolvePath( +function resolvePath( node: TNode, resolve: boolean, ): Expression | TNode { @@ -128,13 +128,13 @@ function resolvePath( }); } -declare function withLocation( +declare function withLocation( node: TNode, loc: ?SourceLocation, ): TNode; // eslint-disable-next-line no-redeclare -declare function withLocation( +declare function withLocation( node: ReadonlyArray, loc: ?SourceLocation, ): Array; diff --git a/packages/metro-transform-plugins/types/addParamsToDefineCall.d.ts b/packages/metro-transform-plugins/types/addParamsToDefineCall.d.ts index 91e8aecf6a..1621306aec 100644 --- a/packages/metro-transform-plugins/types/addParamsToDefineCall.d.ts +++ b/packages/metro-transform-plugins/types/addParamsToDefineCall.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<7b35f4001b105ee1f3612e8a0027a482>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-plugins/src/addParamsToDefineCall.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro-transform-plugins/types/constant-folding-plugin.d.ts b/packages/metro-transform-plugins/types/constant-folding-plugin.d.ts index bf6f926239..37200d5ead 100644 --- a/packages/metro-transform-plugins/types/constant-folding-plugin.d.ts +++ b/packages/metro-transform-plugins/types/constant-folding-plugin.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<126e200dfee829750f4424e550c34190>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-plugins/src/constant-folding-plugin.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {PluginObj} from '@babel/core'; diff --git a/packages/metro-transform-plugins/types/import-export-plugin.d.ts b/packages/metro-transform-plugins/types/import-export-plugin.d.ts index b833cf153f..869c88655c 100644 --- a/packages/metro-transform-plugins/types/import-export-plugin.d.ts +++ b/packages/metro-transform-plugins/types/import-export-plugin.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-plugins/src/import-export-plugin.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {PluginObj} from '@babel/core'; diff --git a/packages/metro-transform-plugins/types/index.d.ts b/packages/metro-transform-plugins/types/index.d.ts index 1a88c773da..0fd5073199 100644 --- a/packages/metro-transform-plugins/types/index.d.ts +++ b/packages/metro-transform-plugins/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<4831d14939e3956402eac933b0d81f6c>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-plugins/src/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type constantFoldingPlugin from './constant-folding-plugin'; diff --git a/packages/metro-transform-plugins/types/inline-plugin.d.ts b/packages/metro-transform-plugins/types/inline-plugin.d.ts index a8a8df9eaa..30caa0ae52 100644 --- a/packages/metro-transform-plugins/types/inline-plugin.d.ts +++ b/packages/metro-transform-plugins/types/inline-plugin.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<0a0f52c4e23d8cd25d04b2d46a09e480>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-plugins/src/inline-plugin.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {PluginObj} from '@babel/core'; diff --git a/packages/metro-transform-plugins/types/inline-requires-plugin.d.ts b/packages/metro-transform-plugins/types/inline-requires-plugin.d.ts index 952cd3efd7..a6aec652c0 100644 --- a/packages/metro-transform-plugins/types/inline-requires-plugin.d.ts +++ b/packages/metro-transform-plugins/types/inline-requires-plugin.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<1f73acbbf5a206de52478c57c058ccb8>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-plugins/src/inline-requires-plugin.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type * as $$IMPORT_TYPEOF_1$$ from '@babel/core'; diff --git a/packages/metro-transform-plugins/types/normalizePseudoGlobals.d.ts b/packages/metro-transform-plugins/types/normalizePseudoGlobals.d.ts index 01824f7239..16a530f8de 100644 --- a/packages/metro-transform-plugins/types/normalizePseudoGlobals.d.ts +++ b/packages/metro-transform-plugins/types/normalizePseudoGlobals.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<318e20b6680fabe0b8524213e38e0277>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-plugins/src/normalizePseudoGlobals.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Node as BabelNode} from '@babel/types'; diff --git a/packages/metro-transform-plugins/types/utils/createInlinePlatformChecks.d.ts b/packages/metro-transform-plugins/types/utils/createInlinePlatformChecks.d.ts index d1ff74c481..819cc274c5 100644 --- a/packages/metro-transform-plugins/types/utils/createInlinePlatformChecks.d.ts +++ b/packages/metro-transform-plugins/types/utils/createInlinePlatformChecks.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<13269e5dcf93e0b31428517812e3bb88>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-plugins/src/utils/createInlinePlatformChecks.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Scope} from '@babel/traverse'; diff --git a/packages/metro-transform-worker/package.json b/packages/metro-transform-worker/package.json index 9cb9a868fd..4368269c74 100644 --- a/packages/metro-transform-worker/package.json +++ b/packages/metro-transform-worker/package.json @@ -1,6 +1,6 @@ { "name": "metro-transform-worker", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 Transform worker for Metro.", "main": "src/index.js", "exports": { @@ -24,13 +24,13 @@ "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", - "metro": "0.83.5", - "metro-babel-transformer": "0.83.5", - "metro-cache": "0.83.5", - "metro-cache-key": "0.83.5", - "metro-minify-terser": "0.83.5", - "metro-source-map": "0.83.5", - "metro-transform-plugins": "0.83.5", + "metro": "0.83.6", + "metro-babel-transformer": "0.83.6", + "metro-cache": "0.83.6", + "metro-cache-key": "0.83.6", + "metro-minify-terser": "0.83.6", + "metro-source-map": "0.83.6", + "metro-transform-plugins": "0.83.6", "nullthrows": "^1.1.1" }, "devDependencies": { diff --git a/packages/metro-transform-worker/src/index.js b/packages/metro-transform-worker/src/index.js index 416a04cf7d..033a194a94 100644 --- a/packages/metro-transform-worker/src/index.js +++ b/packages/metro-transform-worker/src/index.js @@ -215,7 +215,7 @@ const minifyCode = async ( code, // functionMap is overridden by the serializer functionMap: null, - // isIgnored is overriden by the serializer + // isIgnored is overridden by the serializer isIgnored: false, map, path: filename, @@ -376,7 +376,7 @@ async function transformJS( let wrappedAst; // If the module to transform is a script (meaning that is not part of the - // dependency graph and it code will just be prepended to the bundle modules), + // dependency graph and its code will just be prepended to the bundle modules), // we need to wrap it differently than a commonJS module (also, scripts do // not have dependencies). if (file.type === 'js/script') { @@ -718,7 +718,10 @@ export const transform = async ( return await transformJSWithBabel(file, context); }; -export const getCacheKey = (config: JsTransformerConfig): string => { +export const getCacheKey = ( + config: JsTransformerConfig, + opts?: Readonly<{projectRoot: string, ...}>, +): string => { const {babelTransformerPath, minifierPath, ...remainingConfig} = config; const filesKey = metroGetCacheKey([ @@ -733,11 +736,20 @@ export const getCacheKey = (config: JsTransformerConfig): string => { ]); // $FlowFixMe[unsupported-syntax] - const babelTransformer = require(babelTransformerPath); + const babelTransformer = require(babelTransformerPath) as BabelTransformer; + + // Get cache key from babel transformer, which may include user's babel config files + const babelTransformerCacheKey = babelTransformer.getCacheKey + ? babelTransformer.getCacheKey({ + projectRoot: opts?.projectRoot, + enableBabelRCLookup: config.enableBabelRCLookup, + }) + : ''; + return [ filesKey, stableHash(remainingConfig).toString('hex'), - babelTransformer.getCacheKey ? babelTransformer.getCacheKey() : '', + babelTransformerCacheKey, ].join('$'); }; diff --git a/packages/metro-transform-worker/types/index.d.ts b/packages/metro-transform-worker/types/index.d.ts index f15d5c146c..ce18c8f5a5 100644 --- a/packages/metro-transform-worker/types/index.d.ts +++ b/packages/metro-transform-worker/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<165882da0b131608da36b1cbd00ecf28>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-worker/src/index.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { @@ -100,7 +107,10 @@ export declare const transform: ( options: JsTransformOptions, ) => Promise; export declare type transform = typeof transform; -export declare const getCacheKey: (config: JsTransformerConfig) => string; +export declare const getCacheKey: ( + config: JsTransformerConfig, + opts?: Readonly<{projectRoot: string}>, +) => string; export declare type getCacheKey = typeof getCacheKey; /** * Backwards-compatibility with CommonJS consumers using interopRequireDefault. diff --git a/packages/metro-transform-worker/types/utils/assetTransformer.d.ts b/packages/metro-transform-worker/types/utils/assetTransformer.d.ts index 7e90848ecd..6f6e5fd4b0 100644 --- a/packages/metro-transform-worker/types/utils/assetTransformer.d.ts +++ b/packages/metro-transform-worker/types/utils/assetTransformer.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-worker/src/utils/assetTransformer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {File} from '@babel/types'; diff --git a/packages/metro-transform-worker/types/utils/getMinifier.d.ts b/packages/metro-transform-worker/types/utils/getMinifier.d.ts index 45b1cbc46d..f3cbb816c1 100644 --- a/packages/metro-transform-worker/types/utils/getMinifier.d.ts +++ b/packages/metro-transform-worker/types/utils/getMinifier.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<410ba17e82af72676f3993ebd1d0f60f>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro-transform-worker/src/utils/getMinifier.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Minifier} from '../index.js'; diff --git a/packages/metro/package.json b/packages/metro/package.json index 1e255acabb..853338fedd 100644 --- a/packages/metro/package.json +++ b/packages/metro/package.json @@ -1,6 +1,6 @@ { "name": "metro", - "version": "0.83.5", + "version": "0.83.6", "description": "🚇 The JavaScript bundler for React Native.", "main": "src/index.js", "bin": "src/cli.js", @@ -34,24 +34,24 @@ "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", - "hermes-parser": "0.33.3", + "hermes-parser": "0.35.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.83.5", - "metro-cache": "0.83.5", - "metro-cache-key": "0.83.5", - "metro-config": "0.83.5", - "metro-core": "0.83.5", - "metro-file-map": "0.83.5", - "metro-resolver": "0.83.5", - "metro-runtime": "0.83.5", - "metro-source-map": "0.83.5", - "metro-symbolicate": "0.83.5", - "metro-transform-plugins": "0.83.5", - "metro-transform-worker": "0.83.5", + "metro-babel-transformer": "0.83.6", + "metro-cache": "0.83.6", + "metro-cache-key": "0.83.6", + "metro-config": "0.83.6", + "metro-core": "0.83.6", + "metro-file-map": "0.83.6", + "metro-resolver": "0.83.6", + "metro-runtime": "0.83.6", + "metro-source-map": "0.83.6", + "metro-symbolicate": "0.83.6", + "metro-transform-plugins": "0.83.6", + "metro-transform-worker": "0.83.6", "mime-types": "^3.0.1", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", @@ -72,7 +72,7 @@ "dedent": "^0.7.0", "jest-snapshot": "^29.7.0", "jest-snapshot-serializer-raw": "^1.2.0", - "metro-babel-register": "0.83.5", + "metro-babel-register": "0.83.6", "metro-memory-fs": "*", "mock-req": "^0.2.0", "mock-res": "^0.6.0", diff --git a/packages/metro/src/Bundler/util.js b/packages/metro/src/Bundler/util.js index fbcebb6f80..aa9a984315 100644 --- a/packages/metro/src/Bundler/util.js +++ b/packages/metro/src/Bundler/util.js @@ -17,7 +17,7 @@ import * as babylon from '@babel/parser'; import template from '@babel/template'; import * as babelTypes from '@babel/types'; -type SubTree = ( +type SubTree = ( moduleTransport: T, moduleTransportsByPath: Map, ) => Iterable; @@ -66,7 +66,7 @@ function filterObject( return copied; } -export function createRamBundleGroups( +export function createRamBundleGroups( ramGroups: ReadonlyArray, groupableModules: ReadonlyArray, subtree: SubTree, @@ -123,7 +123,7 @@ export function createRamBundleGroups( return result; } -function* filter( +function* filter( iterator: ArrayMap, predicate: ([A, Array]) => boolean, ): Generator<[A, Array], void, void> { diff --git a/packages/metro/src/DeltaBundler/DeltaCalculator.js b/packages/metro/src/DeltaBundler/DeltaCalculator.js index fe8b64ba4d..f8f0013691 100644 --- a/packages/metro/src/DeltaBundler/DeltaCalculator.js +++ b/packages/metro/src/DeltaBundler/DeltaCalculator.js @@ -10,16 +10,24 @@ */ import type {DeltaResult, Options} from './types'; -import type {RootPerfLogger} from 'metro-config'; import type {ChangeEvent} from 'metro-file-map'; import {Graph} from './Graph'; +import crypto from 'crypto'; import EventEmitter from 'events'; import path from 'path'; // eslint-disable-next-line import/no-commonjs const debug = require('debug')('Metro:DeltaCalculator'); +/** + * Assigns a unique, stable `changeId` to each `ChangeEvent` from the file + * watcher. Since all `DeltaCalculator` instances share the same + * `ChangeEvent` object reference per file system change, the `WeakMap` + * ensures each gets the same `changeId`. + */ +const changeEventIds: WeakMap = new WeakMap(); + /** * This class is in charge of calculating the delta of changed modules that * happen between calls. To do so, it subscribes to file changes, so it can @@ -172,76 +180,79 @@ export default class DeltaCalculator extends EventEmitter { return this._graph; } - _handleMultipleFileChanges = (changeEvent: ChangeEvent) => { - changeEvent.eventsQueue.forEach(eventInfo => { - this._handleFileChange(eventInfo, changeEvent.logger); - }); - }; + #shouldReset( + canonicalPath: string, + metadata: {+isSymlink: boolean, ...}, + ): boolean { + if (metadata.isSymlink) { + return true; + } - /** - * Handles a single file change. To avoid doing any work before it's needed, - * the listener only stores the modified file, which will then be used later - * when the delta needs to be calculated. - */ - _handleFileChange = ( - {type, filePath, metadata}: ChangeEvent['eventsQueue'][number], - logger: ?RootPerfLogger, - ): unknown => { - debug('Handling %s: %s (type: %s)', type, filePath, metadata.type); if ( - metadata.type === 'l' || - (this._options.unstable_enablePackageExports && - filePath.endsWith(path.sep + 'package.json')) + this._options.unstable_enablePackageExports && + (canonicalPath === 'package.json' || + canonicalPath.endsWith(path.sep + 'package.json')) ) { - this._requiresReset = true; - this.emit('change', {logger}); + return true; } - let state: void | 'deleted' | 'modified' | 'added'; - if (this._deletedFiles.has(filePath)) { - state = 'deleted'; - } else if (this._modifiedFiles.has(filePath)) { - state = 'modified'; - } else if (this._addedFiles.has(filePath)) { - state = 'added'; + + return false; + } + + _handleMultipleFileChanges = (changeEvent: ChangeEvent) => { + const {changes, logger, rootDir} = changeEvent; + + // Process added files: deleted+added = modified, otherwise added + for (const [canonicalPath, metadata] of changes.addedFiles) { + debug('Handling add: %s', canonicalPath); + if (this.#shouldReset(canonicalPath, metadata)) { + this._requiresReset = true; + } + const absolutePath = path.join(rootDir, canonicalPath); + if (this._deletedFiles.has(absolutePath)) { + this._deletedFiles.delete(absolutePath); + this._modifiedFiles.add(absolutePath); + } else { + this._addedFiles.add(absolutePath); + this._modifiedFiles.delete(absolutePath); + } } - let nextState: 'deleted' | 'modified' | 'added'; - if (type === 'delete') { - nextState = 'deleted'; - } else if (type === 'add') { - // A deleted+added file is modified - nextState = state === 'deleted' ? 'modified' : 'added'; - } else { - // type === 'change' - // An added+modified file is added - nextState = state === 'added' ? 'added' : 'modified'; + // Process modified files: added+modified stays added, otherwise modified + for (const [canonicalPath, metadata] of changes.modifiedFiles) { + debug('Handling change: %s', canonicalPath); + if (this.#shouldReset(canonicalPath, metadata)) { + this._requiresReset = true; + } + const absolutePath = path.join(rootDir, canonicalPath); + if (!this._addedFiles.has(absolutePath)) { + this._modifiedFiles.add(absolutePath); + } + this._deletedFiles.delete(absolutePath); } - switch (nextState) { - case 'deleted': - this._deletedFiles.add(filePath); - this._modifiedFiles.delete(filePath); - this._addedFiles.delete(filePath); - break; - case 'added': - this._addedFiles.add(filePath); - this._deletedFiles.delete(filePath); - this._modifiedFiles.delete(filePath); - break; - case 'modified': - this._modifiedFiles.add(filePath); - this._deletedFiles.delete(filePath); - this._addedFiles.delete(filePath); - break; - default: - nextState as empty; + // Process removed files: added+deleted = no change, otherwise deleted + for (const [canonicalPath, metadata] of changes.removedFiles) { + debug('Handling delete: %s', canonicalPath); + if (this.#shouldReset(canonicalPath, metadata)) { + this._requiresReset = true; + } + const absolutePath = path.resolve(rootDir, canonicalPath); + if (this._addedFiles.has(absolutePath)) { + this._addedFiles.delete(absolutePath); + } else { + this._deletedFiles.add(absolutePath); + this._modifiedFiles.delete(absolutePath); + } } - // Notify users that there is a change in some of the bundle files. This - // way the client can choose to refetch the bundle. - this.emit('change', { - logger, - }); + let changeId = changeEventIds.get(changeEvent); + if (changeId == null) { + changeId = crypto.randomUUID(); + changeEventIds.set(changeEvent, changeId); + } + + this.emit('change', {logger, changeId}); }; async _getChangedDependencies( diff --git a/packages/metro/src/DeltaBundler/__tests__/DeltaCalculator-context-test.js b/packages/metro/src/DeltaBundler/__tests__/DeltaCalculator-context-test.js index ab277e5731..5749b22494 100644 --- a/packages/metro/src/DeltaBundler/__tests__/DeltaCalculator-context-test.js +++ b/packages/metro/src/DeltaBundler/__tests__/DeltaCalculator-context-test.js @@ -17,6 +17,8 @@ import type {Options, TransformResultDependency} from '../types'; import CountingSet from '../../lib/CountingSet'; import DeltaCalculator from '../DeltaCalculator'; import {Graph} from '../Graph'; +import {createEmitChange, createPathNormalizer} from './test-utils'; +import path from 'path'; const {EventEmitter} = require('events'); @@ -36,6 +38,8 @@ const markModifiedContextModules = jest.spyOn( describe('DeltaCalculator + require.context', () => { let deltaCalculator; let fileWatcher; + let emitChange; + const p = createPathNormalizer(); const options: Options<> = { unstable_allowRequireContext: true, @@ -62,14 +66,15 @@ describe('DeltaCalculator + require.context', () => { beforeEach(async () => { fileWatcher = new EventEmitter(); + emitChange = createEmitChange(fileWatcher, p('/'), path.sep); markModifiedContextModules.mockImplementation(function ( this: Graph, filePath, modifiedContexts, ) { - if (filePath.startsWith('/ctx/')) { - modifiedContexts.add('/ctx?ctx=xxx'); + if (filePath.startsWith(p('/ctx/'))) { + modifiedContexts.add(p('/ctx?ctx=xxx')); } }); @@ -83,12 +88,12 @@ describe('DeltaCalculator + require.context', () => { this: Graph, options: Options, ): Promise> { - this.dependencies.set('/bundle', { + this.dependencies.set(p('/bundle'), { dependencies: new Map([ [ 'ctx', { - absolutePath: '/ctx?ctx=xxx', + absolutePath: p('/ctx?ctx=xxx'), data: { name: 'ctx', data: { @@ -103,15 +108,15 @@ describe('DeltaCalculator + require.context', () => { ]), inverseDependencies: new CountingSet([]), output: [], - path: '/bundle', + path: p('/bundle'), getSource: () => Buffer.of(), }); - this.dependencies.set('/ctx?ctx=xxx', { + this.dependencies.set(p('/ctx?ctx=xxx'), { dependencies: new Map([ [ 'foo', { - absolutePath: '/ctx/foo', + absolutePath: p('/ctx/foo'), data: { name: 'foo', data: { @@ -124,16 +129,16 @@ describe('DeltaCalculator + require.context', () => { }, ], ]), - inverseDependencies: new CountingSet(['/bundle']), + inverseDependencies: new CountingSet([p('/bundle')]), output: [], - path: '/ctx?ctx=xxx', + path: p('/ctx?ctx=xxx'), getSource: () => Buffer.of(), }); - this.dependencies.set('/ctx/foo', { + this.dependencies.set(p('/ctx/foo'), { dependencies: new Map(), - inverseDependencies: new CountingSet(['/ctx?ctx=xxx']), + inverseDependencies: new CountingSet([p('/ctx?ctx=xxx')]), output: [], - path: '/ctx/foo', + path: p('/ctx/foo'), getSource: () => Buffer.of(), }); @@ -155,7 +160,7 @@ describe('DeltaCalculator + require.context', () => { // $FlowFixMe[underconstrained-implicit-instantiation] deltaCalculator = new DeltaCalculator( - new Set(['/bundle']), + new Set([p('/bundle')]), fileWatcher, options, ); @@ -172,11 +177,7 @@ describe('DeltaCalculator + require.context', () => { // Initial build await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'delete', filePath: '/ctx/foo', metadata: {type: 'f'}}, - ], - }); + emitChange({removedFiles: ['ctx/foo']}); // Incremental build await deltaCalculator.getDelta({ @@ -185,7 +186,7 @@ describe('DeltaCalculator + require.context', () => { }); expect(traverseDependencies).toBeCalledWith( - ['/ctx?ctx=xxx'], + [p('/ctx?ctx=xxx')], expect.anything(), ); @@ -199,11 +200,7 @@ describe('DeltaCalculator + require.context', () => { // Initial build await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'add', filePath: '/ctx/foo2', metadata: {type: 'f'}}, - ], - }); + emitChange({addedFiles: ['ctx/foo2']}); // Incremental build await deltaCalculator.getDelta({ @@ -212,7 +209,7 @@ describe('DeltaCalculator + require.context', () => { }); expect(traverseDependencies).toBeCalledWith( - ['/ctx?ctx=xxx'], + [p('/ctx?ctx=xxx')], expect.anything(), ); @@ -223,11 +220,7 @@ describe('DeltaCalculator + require.context', () => { // Initial build await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: '/ctx/foo', metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['ctx/foo']}); // Incremental build await deltaCalculator.getDelta({ @@ -236,7 +229,7 @@ describe('DeltaCalculator + require.context', () => { }); expect(traverseDependencies).toBeCalledWith( - ['/ctx/foo'], + [p('/ctx/foo')], expect.anything(), ); @@ -247,11 +240,7 @@ describe('DeltaCalculator + require.context', () => { // Initial build await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: '/ctx/foo2', metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['ctx/foo2']}); // Incremental build await deltaCalculator.getDelta({ @@ -266,17 +255,9 @@ describe('DeltaCalculator + require.context', () => { // Initial build await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'add', filePath: '/ctx/foo2', metadata: {type: 'f'}}, - ], - }); + emitChange({addedFiles: ['ctx/foo2']}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: '/ctx/foo2', metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['ctx/foo2']}); // Incremental build await deltaCalculator.getDelta({ @@ -285,7 +266,7 @@ describe('DeltaCalculator + require.context', () => { }); expect(traverseDependencies).toBeCalledWith( - ['/ctx?ctx=xxx'], + [p('/ctx?ctx=xxx')], expect.anything(), ); @@ -296,17 +277,9 @@ describe('DeltaCalculator + require.context', () => { // Initial build await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'add', filePath: '/ctx/foo2', metadata: {type: 'f'}}, - ], - }); + emitChange({addedFiles: ['ctx/foo2']}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'delete', filePath: '/ctx/foo2', metadata: {type: 'f'}}, - ], - }); + emitChange({removedFiles: ['ctx/foo2']}); // Incremental build await deltaCalculator.getDelta({ @@ -321,15 +294,9 @@ describe('DeltaCalculator + require.context', () => { // Initial build await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'delete', filePath: '/ctx/foo', metadata: {type: 'f'}}, - ], - }); + emitChange({removedFiles: ['ctx/foo']}); - fileWatcher.emit('change', { - eventsQueue: [{type: 'add', filePath: '/ctx/foo', metadata: {type: 'f'}}], - }); + emitChange({addedFiles: ['ctx/foo']}); // Incremental build await deltaCalculator.getDelta({ @@ -338,7 +305,7 @@ describe('DeltaCalculator + require.context', () => { }); expect(traverseDependencies).toBeCalledWith( - ['/ctx/foo'], + [p('/ctx/foo')], expect.anything(), ); }); @@ -347,17 +314,9 @@ describe('DeltaCalculator + require.context', () => { // Initial build await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: '/ctx/foo', metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['ctx/foo']}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'delete', filePath: '/ctx/foo', metadata: {type: 'f'}}, - ], - }); + emitChange({removedFiles: ['ctx/foo']}); // Incremental build await deltaCalculator.getDelta({ @@ -366,7 +325,7 @@ describe('DeltaCalculator + require.context', () => { }); expect(traverseDependencies).toBeCalledWith( - ['/ctx?ctx=xxx'], + [p('/ctx?ctx=xxx')], expect.anything(), ); diff --git a/packages/metro/src/DeltaBundler/__tests__/DeltaCalculator-test.js b/packages/metro/src/DeltaBundler/__tests__/DeltaCalculator-test.js index 6aacc4449d..c8163cc96c 100644 --- a/packages/metro/src/DeltaBundler/__tests__/DeltaCalculator-test.js +++ b/packages/metro/src/DeltaBundler/__tests__/DeltaCalculator-test.js @@ -18,11 +18,11 @@ import type { } from '../types'; import CountingSet from '../../lib/CountingSet'; -import path from 'path'; +import {createEmitChange, createPathNormalizer} from './test-utils'; jest.mock('../../Bundler'); -describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { +describe.each(['posix', 'win32'])('DeltaCalculator (%s)', osPlatform => { let entryModule: Module<$FlowFixMe>; let fooModule: Module<$FlowFixMe>; let barModule: Module<$FlowFixMe>; @@ -33,6 +33,8 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { let fileWatcher; let traverseDependencies; let initialTraverseDependencies; + let emitChange; + const p = createPathNormalizer(osPlatform); const options: Options<> = { unstable_allowRequireContext: false, @@ -57,14 +59,6 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { }, }; - function p(posixPath: string): string { - if (osPlatform === 'win32') { - return path.win32.join('C:\\', ...posixPath.split('/')); - } - - return posixPath; - } - beforeEach(async () => { if (osPlatform === 'win32') { jest.doMock('path', () => jest.requireActual('path/win32')); @@ -211,6 +205,12 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { fileWatcher, options, ); + + emitChange = createEmitChange( + fileWatcher, + p('/'), + osPlatform === 'win32' ? '\\' : '/', + ); }); afterEach(() => { @@ -293,9 +293,7 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { test('should calculate a delta after a file addition', async () => { await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [{type: 'add', filePath: p('/foo'), metadata: {type: 'f'}}], - }); + emitChange({addedFiles: ['foo']}); traverseDependencies.mockResolvedValueOnce({ added: new Map([[p('/foo'), fooModule]]), @@ -322,11 +320,7 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { test('should calculate a delta after a simple modification', async () => { await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['foo']}); traverseDependencies.mockReturnValue( Promise.resolve({ @@ -355,11 +349,7 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { // Get initial delta await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['foo']}); traverseDependencies.mockReturnValue( Promise.resolve({ @@ -388,11 +378,7 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { // Get initial delta await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['foo']}); const quxModule: Module<$FlowFixMe> = { dependencies: new Map(), @@ -439,11 +425,7 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { .getDelta({reset: false, shallow: false}) .then(() => { deltaCalculator.on('change', () => done()); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['foo']}); }) .catch(done); }); @@ -454,9 +436,7 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { deltaCalculator.on('change', onChangeFile); - fileWatcher.emit('change', { - eventsQueue: [{type: 'add', filePath: p('/foo'), metadata: {type: 'f'}}], - }); + emitChange({addedFiles: ['foo']}); jest.runAllTimers(); @@ -469,11 +449,7 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { deltaCalculator.on('delete', onChangeFile); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'delete', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({removedFiles: ['foo']}); jest.runAllTimers(); @@ -483,13 +459,9 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { test('should retry to build the last delta after getting an error', async () => { await deltaCalculator.getDelta({reset: false, shallow: false}); - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['foo']}); - traverseDependencies.mockReturnValue(Promise.reject(new Error())); + traverseDependencies.mockRejectedValue(new Error()); await expect( deltaCalculator.getDelta({reset: false, shallow: false}), @@ -505,18 +477,10 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { await deltaCalculator.getDelta({reset: false, shallow: false}); // First modify the file - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['foo']}); // Then delete that same file - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'delete', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({removedFiles: ['foo']}); traverseDependencies.mockReturnValue( Promise.resolve({ @@ -543,18 +507,10 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { await deltaCalculator.getDelta({reset: false, shallow: false}); // Delete a file - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'delete', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({removedFiles: ['foo']}); // Delete a dependency of the deleted file - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'delete', filePath: p('/qux'), metadata: {type: 'f'}}, - ], - }); + emitChange({removedFiles: ['qux']}); traverseDependencies.mockReturnValue( Promise.resolve({ @@ -576,18 +532,10 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { await deltaCalculator.getDelta({reset: false, shallow: false}); // First delete a file - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'delete', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({removedFiles: ['foo']}); // Then add it again - fileWatcher.emit('change', { - eventsQueue: [ - {type: 'change', filePath: p('/foo'), metadata: {type: 'f'}}, - ], - }); + emitChange({modifiedFiles: ['foo']}); traverseDependencies.mockReturnValue( Promise.resolve({ @@ -612,11 +560,11 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { deltaCalculator.once('change', resolve), ); - fileWatcher.emit('change', { - eventsQueue: [ - {type: eventType, filePath: p('/link'), metadata: {type: 'l'}}, - ], - }); + if (eventType === 'add') { + emitChange({addedFiles: [['link', {isSymlink: true}]]}); + } else { + emitChange({removedFiles: [['link', {isSymlink: true}]]}); + } // Any symlink change should trigger a 'change' event await changeEmitted; @@ -658,15 +606,7 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { deltaCalculator.once('change', resolve), ); - fileWatcher.emit('change', { - eventsQueue: [ - { - type: 'change', - filePath: p('/node_modules/foo/package.json'), - metadata: {type: 'f'}, - }, - ], - }); + emitChange({modifiedFiles: ['node_modules/foo/package.json']}); // Any package.json change should trigger a 'change' event await changeEmitted; @@ -702,6 +642,41 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => { expect(traverseDependencies).not.toHaveBeenCalled(); }); + test('should emit a stable changeId for a change event', async () => { + await deltaCalculator.getDelta({reset: false, shallow: false}); + + const changeIds: Array = []; + deltaCalculator.on('change', ({changeId}: {changeId?: string}) => { + if (changeId != null) { + changeIds.push(changeId); + } + }); + + // Emit a change event with multiple file changes + emitChange({modifiedFiles: ['foo', 'bar']}); + + expect(changeIds).toHaveLength(1); + expect(typeof changeIds[0]).toBe('string'); + expect(changeIds[0].length).toBeGreaterThan(0); + }); + + test('should emit different changeIds for separate change events', async () => { + await deltaCalculator.getDelta({reset: false, shallow: false}); + + const changeIds: Array = []; + deltaCalculator.on('change', ({changeId}: {changeId?: string}) => { + if (changeId != null) { + changeIds.push(changeId); + } + }); + + emitChange({modifiedFiles: ['foo']}); + emitChange({modifiedFiles: ['bar']}); + + expect(changeIds).toHaveLength(2); + expect(changeIds[0]).not.toEqual(changeIds[1]); + }); + test('should not mutate an existing graph when calling end()', async () => { await deltaCalculator.getDelta({reset: false, shallow: false}); const graph = deltaCalculator.getGraph(); diff --git a/packages/metro/src/DeltaBundler/__tests__/test-utils.js b/packages/metro/src/DeltaBundler/__tests__/test-utils.js new file mode 100644 index 0000000000..669153c145 --- /dev/null +++ b/packages/metro/src/DeltaBundler/__tests__/test-utils.js @@ -0,0 +1,70 @@ +/** + * 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 + */ + +import type EventEmitter from 'events'; + +export type FileEntry = + | string + | [string, {isSymlink?: boolean, modifiedTime?: number}]; + +export type ChangeEventInput = { + addedFiles?: ReadonlyArray, + modifiedFiles?: ReadonlyArray, + removedFiles?: ReadonlyArray, +}; + +/** + * Creates an emitChange helper function for DeltaCalculator tests. + * The helper emits change events with canonical paths relative to rootDir. + */ +export function createEmitChange( + fileWatcher: EventEmitter, + rootDir: string, + pathSeparator: string, +): (changes: ChangeEventInput) => void { + return function emitChange(changes: ChangeEventInput): void { + const toEntry = ( + entry: FileEntry, + ): [string, {modifiedTime: ?number, isSymlink: boolean}] => { + const [file, opts] = typeof entry === 'string' ? [entry, {}] : entry; + // Convert forward slashes to platform-specific separators for canonical paths + const canonicalPath = + pathSeparator !== '/' ? file.replaceAll('/', '\\') : file; + return [ + canonicalPath, + { + modifiedTime: opts.modifiedTime ?? Date.now(), + isSymlink: opts.isSymlink ?? false, + }, + ]; + }; + fileWatcher.emit('change', { + changes: { + addedFiles: (changes.addedFiles ?? []).map(toEntry), + modifiedFiles: (changes.modifiedFiles ?? []).map(toEntry), + removedFiles: (changes.removedFiles ?? []).map(toEntry), + addedDirectories: [], + removedDirectories: [], + }, + rootDir, + logger: null, + }); + }; +} + +export const createPathNormalizer = ( + platform: 'win32' | 'posix' = process.platform === 'win32' + ? 'win32' + : 'posix', +): (string => string) => + platform === 'win32' + ? posixPath => posixPath.replace(/^\//, 'C:\\').replaceAll('/', '\\') + : posixPath => posixPath; diff --git a/packages/metro/src/DeltaBundler/getTransformCacheKey.js b/packages/metro/src/DeltaBundler/getTransformCacheKey.js index cc1ef90eac..899ed51269 100644 --- a/packages/metro/src/DeltaBundler/getTransformCacheKey.js +++ b/packages/metro/src/DeltaBundler/getTransformCacheKey.js @@ -19,7 +19,10 @@ import {getCacheKey} from 'metro-cache-key'; const VERSION = require('../../package.json').version; type CacheKeyProvider = { - getCacheKey?: JsTransformerConfig => string, + getCacheKey?: ( + config: JsTransformerConfig, + opts?: Readonly<{projectRoot: string}>, + ) => string, }; export default function getTransformCacheKey(opts: { @@ -32,7 +35,9 @@ export default function getTransformCacheKey(opts: { // eslint-disable-next-line no-useless-call const Transformer: CacheKeyProvider = require.call(null, transformerPath); const transformerKey = Transformer.getCacheKey - ? Transformer.getCacheKey(transformerConfig) + ? Transformer.getCacheKey(transformerConfig, { + projectRoot: opts.projectRoot, + }) : ''; return crypto diff --git a/packages/metro/src/HmrServer.js b/packages/metro/src/HmrServer.js index 024a26fc8c..b554240242 100644 --- a/packages/metro/src/HmrServer.js +++ b/packages/metro/src/HmrServer.js @@ -66,7 +66,7 @@ function send(sendFns: Array<(string) => void>, message: HmrMessage): void { * getting connected, disconnected or having errors (through the * `onClientConnect`, `onClientDisconnect` and `onClientError` methods). */ -export default class HmrServer { +export default class HmrServer { _config: ConfigT; _bundler: IncrementalBundler; _createModuleId: (path: string) => number; @@ -181,7 +181,8 @@ export default class HmrServer { this._clientGroups.set(id, clientGroup); let latestChangeEvent: ?{ - logger: ?RootPerfLogger, + +logger: ?RootPerfLogger, + +changeId: string, } = null; const debounceCallHandleFileChange = debounceAsyncQueue(async () => { @@ -240,6 +241,10 @@ export default class HmrServer { case 'log-opt-in': client.optedIntoHMR = true; break; + case 'heartbeat': + debug('Heartbeat received'); + sendFn(String(message)); + break; default: break; } @@ -273,7 +278,8 @@ export default class HmrServer { group: ClientGroup, options: {isInitialUpdate: boolean}, changeEvent: ?{ - logger: ?RootPerfLogger, + +logger: ?RootPerfLogger, + +changeId?: string, }, ): Promise { const logger = !options.isInitialUpdate ? changeEvent?.logger : null; @@ -308,7 +314,10 @@ export default class HmrServer { const message = await this._prepareMessage(group, options, changeEvent); send(sendFns, message); - send(sendFns, {type: 'update-done'}); + send(sendFns, { + type: 'update-done', + body: {changeId: changeEvent?.changeId}, + }); log({ ...createActionEndEntry(processingHmrChange), @@ -328,7 +337,8 @@ export default class HmrServer { group: ClientGroup, options: {isInitialUpdate: boolean}, changeEvent: ?{ - logger: ?RootPerfLogger, + +logger: ?RootPerfLogger, + +changeId?: string, }, ): Promise { const logger = !options.isInitialUpdate ? changeEvent?.logger : null; diff --git a/packages/metro/src/index.flow.js b/packages/metro/src/index.flow.js index aae037ad54..d4f718b42f 100644 --- a/packages/metro/src/index.flow.js +++ b/packages/metro/src/index.flow.js @@ -156,9 +156,28 @@ type DependenciesCommandOptions = Readonly<{[string]: unknown}> | null; export {Terminal, JsonReporter, TerminalReporter}; export type {AssetData} from './Assets'; -export type {Reporter, ReportableEvent} from './lib/reporting'; +export type { + AsyncDependencyType, + DeltaResult, + Dependency, + MixedOutput, + Module, + ReadOnlyDependencies, + ReadOnlyGraph, + SerializerOptions, + TransformInputOptions, + TransformResult, + TransformResultDependency, +} from './DeltaBundler/types'; +export type {default as DependencyGraph} from './node-haste/DependencyGraph'; +export type {BundleDetails, Reporter, ReportableEvent} from './lib/reporting'; export type {TerminalReportableEvent} from './lib/TerminalReporter'; -export type {MetroConfig}; +export type { + ContextMode, + RequireContextParams, +} from './ModuleGraph/worker/collectDependencies'; +export type {ServerOptions} from './Server'; +export type {MetroConfig, MetroServer}; async function getConfig(config: InputConfigT): Promise { const defaultConfig = await getDefaultConfig(config.projectRoot); diff --git a/packages/metro/src/lib/JsonReporter.js b/packages/metro/src/lib/JsonReporter.js index dfa3f2e996..8480230950 100644 --- a/packages/metro/src/lib/JsonReporter.js +++ b/packages/metro/src/lib/JsonReporter.js @@ -19,7 +19,7 @@ export type SerializedError = { ... }; -export type SerializedEvent = +export type SerializedEvent = TEvent extends { error: Error, ... @@ -31,7 +31,7 @@ export type SerializedEvent = } : TEvent; -export default class JsonReporter { +export default class JsonReporter { _stream: Writable; constructor(stream: Writable) { diff --git a/packages/metro/src/lib/getAppendScripts.js b/packages/metro/src/lib/getAppendScripts.js index 75b6200c2a..d04a9fe35a 100644 --- a/packages/metro/src/lib/getAppendScripts.js +++ b/packages/metro/src/lib/getAppendScripts.js @@ -18,7 +18,7 @@ import CountingSet from './CountingSet'; import countLines from './countLines'; import nullthrows from 'nullthrows'; -type Options = Readonly<{ +type Options = Readonly<{ asyncRequireModulePath: string, createModuleId: string => T, getRunModuleStatement: (moduleId: T, globalPrefix: string) => string, @@ -33,7 +33,7 @@ type Options = Readonly<{ ... }>; -export default function getAppendScripts( +export default function getAppendScripts( entryPoint: string, modules: ReadonlyArray>, options: Options, diff --git a/packages/metro/src/node-haste/DependencyGraph.js b/packages/metro/src/node-haste/DependencyGraph.js index 97761f3559..7cf3a80a54 100644 --- a/packages/metro/src/node-haste/DependencyGraph.js +++ b/packages/metro/src/node-haste/DependencyGraph.js @@ -14,7 +14,6 @@ import type { TransformResultDependency, } from '../DeltaBundler/types'; import type {ResolverInputOptions} from '../shared/types'; -import type Package from './Package'; import type {ConfigT} from 'metro-config'; import type { ChangeEvent, @@ -66,7 +65,7 @@ export default class DependencyGraph extends EventEmitter { #packageCache: PackageCache; _hasteMap: HasteMap; #dependencyPlugin: ?DependencyPlugin; - _moduleResolver: ModuleResolver; + _moduleResolver: ModuleResolver; _resolutionCache: Map< // Custom resolver options string | symbol, @@ -131,7 +130,10 @@ export default class DependencyGraph extends EventEmitter { this._onWatcherHealthCheck(result), ); this._resolutionCache = new Map(); - this.#packageCache = this._createPackageCache(); + this.#packageCache = new PackageCache({ + getClosestPackage: absoluteModulePath => + this._getClosestPackage(absoluteModulePath), + }); this._createModuleResolver(); }); } @@ -150,10 +152,14 @@ export default class DependencyGraph extends EventEmitter { await this._initializedPromise; } - _onHasteChange({eventsQueue}: ChangeEvent) { + _onHasteChange({changes, rootDir}: ChangeEvent) { this._resolutionCache = new Map(); - eventsQueue.forEach(({filePath}) => - this.#packageCache.invalidate(filePath), + [ + ...changes.addedFiles, + ...changes.modifiedFiles, + ...changes.removedFiles, + ].forEach(([canonicalPath]) => + this.#packageCache.invalidate(path.join(rootDir, canonicalPath)), ); this._createModuleResolver(); this.emit('change'); @@ -190,9 +196,20 @@ export default class DependencyGraph extends EventEmitter { this._hasteMap.getModule(name, platform, true), getHastePackagePath: (name, platform) => this._hasteMap.getPackage(name, platform, true), + getPackage: (packageJsonPath: string) => { + try { + return ( + this.#packageCache.getPackage(packageJsonPath).packageJson ?? null + ); + } catch { + // Non-existence or malformed JSON, we treat both as non-existent + return null; + } + }, + getPackageForModule: (absolutePath: string) => + this.#packageCache.getPackageForModule(absolutePath), mainFields: this._config.resolver.resolverMainFields, nodeModulesPaths: this._config.resolver.nodeModulesPaths, - packageCache: this.#packageCache, preferNativePlatform: true, projectRoot: this._config.projectRoot, reporter: this._config.reporter, @@ -241,12 +258,6 @@ export default class DependencyGraph extends EventEmitter { : null; } - _createPackageCache(): PackageCache { - return new PackageCache({ - getClosestPackage: absolutePath => this._getClosestPackage(absolutePath), - }); - } - getAllFiles(): Array { return nullthrows(this._fileSystem).getAllFiles(); } diff --git a/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js b/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js index 139f1a094b..dddf7cb497 100644 --- a/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js +++ b/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js @@ -35,28 +35,7 @@ import util from 'util'; export type DirExistsFn = (filePath: string) => boolean; -export type Packageish = interface { - path: string, - read(): PackageJson, -}; - -export type Moduleish = interface { - +path: string, -}; - -export type PackageishCache = interface { - getPackage( - name: string, - platform?: string, - supportsNativePlatform?: boolean, - ): TPackage, - getPackageOf(absolutePath: string): ?{ - pkg: TPackage, - packageRelativePath: string, - }, -}; - -type Options = Readonly<{ +type Options = Readonly<{ assetExts: ReadonlySet, dirExists: DirExistsFn, disableHierarchicalLookup: boolean, @@ -67,7 +46,8 @@ type Options = Readonly<{ getHasteModulePath: (name: string, platform: ?string) => ?string, getHastePackagePath: (name: string, platform: ?string) => ?string, mainFields: ReadonlyArray, - packageCache: PackageishCache, + getPackage: (packageJsonPath: string) => ?PackageJson, + getPackageForModule: (absolutePath: string) => ?PackageForModule, nodeModulesPaths: ReadonlyArray, preferNativePlatform: boolean, projectRoot: string, @@ -83,14 +63,14 @@ type Options = Readonly<{ unstable_incrementalResolution: boolean, }>; -export class ModuleResolver { - _options: Options; +export class ModuleResolver { + _options: Options; // A module representing the project root, used as the origin when resolving `emptyModulePath`. _projectRootFakeModulePath: string; // An empty module, the result of resolving `emptyModulePath` from the project root. _cachedEmptyModule: ?BundlerResolution; - constructor(options: Options) { + constructor(options: Options) { this._options = options; const {projectRoot} = this._options; this._projectRootFakeModulePath = path.join(projectRoot, '_'); @@ -132,6 +112,8 @@ export class ModuleResolver { doesFileExist, extraNodeModules, fileSystemLookup, + getPackage, + getPackageForModule, mainFields, nodeModulesPaths, preferNativePlatform, @@ -156,9 +138,8 @@ export class ModuleResolver { doesFileExist, extraNodeModules, fileSystemLookup, - getPackage: this._getPackage, - getPackageForModule: (absoluteModulePath: string) => - this._getPackageForModule(absoluteModulePath), + getPackage, + getPackageForModule, isESMImport: dependency.data.isESMImport, mainFields, nodeModulesPaths, @@ -240,36 +221,6 @@ export class ModuleResolver { } } - _getPackage = (packageJsonPath: string): ?PackageJson => { - try { - return this._options.packageCache.getPackage(packageJsonPath).read(); - } catch (e) { - // Do nothing. The standard module cache does not trigger any error, but - // the ModuleGraph one does, if the module does not exist. - } - - return null; - }; - - _getPackageForModule = (absolutePath: string): ?PackageForModule => { - let result; - - try { - result = this._options.packageCache.getPackageOf(absolutePath); - } catch (e) { - // Do nothing. The standard module cache does not trigger any error, but - // the ModuleGraph one does, if the module does not exist. - } - - return result != null - ? { - packageJson: result.pkg.read(), - packageRelativePath: result.packageRelativePath, - rootPath: path.dirname(result.pkg.path), - } - : null; - }; - /** * TODO: Return Resolution instead of coercing to BundlerResolution here */ diff --git a/packages/metro/src/node-haste/DependencyGraph/createFileMap.js b/packages/metro/src/node-haste/DependencyGraph/createFileMap.js index 6b6ab46b84..7b0db46499 100644 --- a/packages/metro/src/node-haste/DependencyGraph/createFileMap.js +++ b/packages/metro/src/node-haste/DependencyGraph/createFileMap.js @@ -10,7 +10,7 @@ */ import type {ConfigT} from 'metro-config'; -import type {HasteMap} from 'metro-file-map'; +import type {HasteMap, InputFileMapPlugin} from 'metro-file-map'; import ci from 'ci-info'; import MetroFileMap, { @@ -75,7 +75,9 @@ export default function createFileMap( config.watcher.unstable_autoSaveCache ?? {}; const autoSave = watch && autoSaveEnabled ? autoSaveOpts : false; - const plugins: Array = []; + const plugins: Array = [ + ...(config.unstable_fileMapPlugins ?? []), + ]; let dependencyPlugin = null; // Add DependencyPlugin if dependencies should be extracted diff --git a/packages/metro/src/node-haste/Package.js b/packages/metro/src/node-haste/Package.js deleted file mode 100644 index ad906ee3f4..0000000000 --- a/packages/metro/src/node-haste/Package.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 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 - */ - -import type {PackageJson} from 'metro-resolver/private/types'; - -import fs from 'fs'; -import path from 'path'; - -export default class Package { - path: string; - - _root: string; - _content: ?PackageJson; - - constructor({file}: {file: string, ...}) { - this.path = path.resolve(file); - this._root = path.dirname(this.path); - this._content = null; - } - - invalidate() { - this._content = null; - } - - read(): PackageJson { - if (this._content == null) { - this._content = JSON.parse(fs.readFileSync(this.path, 'utf8')); - } - return this._content; - } -} diff --git a/packages/metro/src/node-haste/PackageCache.js b/packages/metro/src/node-haste/PackageCache.js index 74323a719b..ad28792476 100644 --- a/packages/metro/src/node-haste/PackageCache.js +++ b/packages/metro/src/node-haste/PackageCache.js @@ -9,117 +9,141 @@ * @oncall react_native */ -import Package from './Package'; +import type {PackageJson} from 'metro-resolver/private/types'; + +import {readFileSync} from 'fs'; +import {dirname, sep} from 'path'; type GetClosestPackageFn = (absoluteFilePath: string) => ?{ packageJsonPath: string, packageRelativePath: string, }; +type PackageForModule = Readonly<{ + packageJson: PackageJson, + rootPath: string, + packageRelativePath: string, +}>; + export class PackageCache { - _getClosestPackage: GetClosestPackageFn; - _packageCache: { - [filePath: string]: Package, - __proto__: null, - ... - }; - // Cache for "closest package.json" queries by module path. - _packagePathAndSubpathByModulePath: { - [filePath: string]: ?{ - packageJsonPath: string, - packageRelativePath: string, + #getClosestPackage: GetClosestPackageFn; + #packageCache: Map< + string, + { + rootPath: string, + packageJson: PackageJson, }, - __proto__: null, - ... - }; - // The inverse of _packagePathByModulePath. - _modulePathsByPackagePath: { - [filePath: string]: Set, - __proto__: null, - ... - }; + >; + // Single cache: module path → pre-built result object, or null (no allocation on hit) + #resultByModulePath: Map; + // Reverse index for invalidation: package.json path → set of module paths + #modulePathsByPackagePath: Map>; + // Module paths that resolved to no package.json (null), for invalidation + #modulePathsWithNoPackage: Set; constructor(options: {getClosestPackage: GetClosestPackageFn, ...}) { - this._getClosestPackage = options.getClosestPackage; - this._packageCache = Object.create(null); - this._packagePathAndSubpathByModulePath = Object.create(null); - this._modulePathsByPackagePath = Object.create(null); + this.#getClosestPackage = options.getClosestPackage; + this.#packageCache = new Map(); + this.#resultByModulePath = new Map(); + this.#modulePathsByPackagePath = new Map(); + this.#modulePathsWithNoPackage = new Set(); } - getPackage(filePath: string): Package { - if (!this._packageCache[filePath]) { - this._packageCache[filePath] = new Package({ - file: filePath, - }); + getPackage(filePath: string): Readonly<{ + rootPath: string, + packageJson: PackageJson, + }> { + let cached = this.#packageCache.get(filePath); + if (cached == null) { + cached = { + rootPath: dirname(filePath), + packageJson: JSON.parse(readFileSync(filePath, 'utf8')), + }; + this.#packageCache.set(filePath, cached); } - return this._packageCache[filePath]; + return cached; } - getPackageOf( - absoluteModulePath: string, - ): ?{pkg: Package, packageRelativePath: string} { - let packagePathAndSubpath = - this._packagePathAndSubpathByModulePath[absoluteModulePath]; - if ( - packagePathAndSubpath && - this._packageCache[packagePathAndSubpath.packageJsonPath] - ) { - return { - pkg: this._packageCache[packagePathAndSubpath.packageJsonPath], - packageRelativePath: packagePathAndSubpath.packageRelativePath, - }; + getPackageForModule(absoluteModulePath: string): ?PackageForModule { + const cached = this.#resultByModulePath.get(absoluteModulePath); + + // Distinguish between `null` (positively no closest package) and + // `undefined` (no cached result yet) + // eslint-disable-next-line lint/strictly-null + if (cached !== undefined) { + return cached; } - packagePathAndSubpath = this._getClosestPackage(absoluteModulePath); - if (!packagePathAndSubpath) { + const closest = this.#getClosestPackage(absoluteModulePath); + if (closest == null) { + this.#resultByModulePath.set(absoluteModulePath, null); + this.#modulePathsWithNoPackage.add(absoluteModulePath); return null; } - const packagePath = packagePathAndSubpath.packageJsonPath; + const packagePath = closest.packageJsonPath; - this._packagePathAndSubpathByModulePath[absoluteModulePath] = - packagePathAndSubpath; - const modulePaths = - this._modulePathsByPackagePath[packagePath] ?? new Set(); + // Track module→package for invalidation + let modulePaths = this.#modulePathsByPackagePath.get(packagePath); + if (modulePaths == null) { + modulePaths = new Set(); + this.#modulePathsByPackagePath.set(packagePath, modulePaths); + } modulePaths.add(absoluteModulePath); - this._modulePathsByPackagePath[packagePath] = modulePaths; - return { - pkg: this.getPackage(packagePath), - packageRelativePath: packagePathAndSubpath.packageRelativePath, + const pkg = this.getPackage(packagePath); + if (pkg == null) { + return null; + } + + // Cache the pre-built result object — no allocation on future hits + const result: PackageForModule = { + packageJson: pkg.packageJson, + packageRelativePath: closest.packageRelativePath, + rootPath: pkg.rootPath, }; + this.#resultByModulePath.set(absoluteModulePath, result); + return result; } invalidate(filePath: string) { - if (this._packageCache[filePath]) { - this._packageCache[filePath].invalidate(); - delete this._packageCache[filePath]; - } - const packagePathAndSubpath = - this._packagePathAndSubpathByModulePath[filePath]; - if (packagePathAndSubpath) { - // filePath is a module inside a package. - const packagePath = packagePathAndSubpath.packageJsonPath; - delete this._packagePathAndSubpathByModulePath[filePath]; - // This change doesn't invalidate any cached "closest package.json" - // queries for the package's other modules. Clean up only this module. - const modulePaths = this._modulePathsByPackagePath[packagePath]; - if (modulePaths) { - modulePaths.delete(filePath); - if (modulePaths.size === 0) { - delete this._modulePathsByPackagePath[packagePath]; + this.#packageCache.delete(filePath); + + // Clean up any cached result for this module path (including null). + // Derive the package.json path from the cached result to clean up the + // reverse index. + const cachedResult = this.#resultByModulePath.get(filePath); + this.#resultByModulePath.delete(filePath); + this.#modulePathsWithNoPackage.delete(filePath); + + if (cachedResult != null) { + const packagePath = cachedResult.rootPath + sep + 'package.json'; + const modules = this.#modulePathsByPackagePath.get(packagePath); + if (modules != null) { + modules.delete(filePath); + if (modules.size === 0) { + this.#modulePathsByPackagePath.delete(packagePath); } } } - if (this._modulePathsByPackagePath[filePath]) { - // filePath is a package. This change invalidates all cached "closest - // package.json" queries for modules inside this package. - const modulePaths = this._modulePathsByPackagePath[filePath]; + + // If filePath is a package.json, invalidate all module lookups pointing to it + const modulePaths = this.#modulePathsByPackagePath.get(filePath); + if (modulePaths != null) { for (const modulePath of modulePaths) { - delete this._packagePathAndSubpathByModulePath[modulePath]; + this.#resultByModulePath.delete(modulePath); + } + this.#modulePathsByPackagePath.delete(filePath); + } + + // If a package.json was created, modified, or deleted, invalidate all + // null-cached module results, since modules that previously had no + // enclosing package.json may now resolve to this one. + if (filePath.endsWith(sep + 'package.json')) { + for (const modulePath of this.#modulePathsWithNoPackage) { + this.#resultByModulePath.delete(modulePath); } - modulePaths.clear(); - delete this._modulePathsByPackagePath[filePath]; + this.#modulePathsWithNoPackage.clear(); } } } diff --git a/packages/metro/src/node-haste/__tests__/PackageCache-test.js b/packages/metro/src/node-haste/__tests__/PackageCache-test.js new file mode 100644 index 0000000000..b2f6a26f57 --- /dev/null +++ b/packages/metro/src/node-haste/__tests__/PackageCache-test.js @@ -0,0 +1,402 @@ +/** + * 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 + */ + +import {sep} from 'path'; + +const {PackageCache} = require('../PackageCache'); + +const mockReadFileSync = jest.fn(); +jest.mock('fs', () => ({readFileSync: (...args) => mockReadFileSync(...args)})); + +type ClosestPackageMap = Map< + string, + ?{packageJsonPath: string, packageRelativePath: string}, +>; + +function createPackageCache(closestPackageByModule: ClosestPackageMap) { + return new PackageCache({ + getClosestPackage: absoluteFilePath => + closestPackageByModule.get(absoluteFilePath) ?? null, + }); +} + +function mockPackageJson(filePath: string, json: {name: string, ...}) { + mockReadFileSync.mockImplementation((path, encoding) => { + if (path === filePath && encoding === 'utf8') { + return JSON.stringify(json); + } + throw new Error(`ENOENT: no such file: ${String(path)}`); + }); +} + +function mockMultiplePackageJsons( + packages: Array<[string, {name: string, ...}]>, +) { + mockReadFileSync.mockImplementation((path, encoding) => { + if (encoding === 'utf8') { + for (const [filePath, json] of packages) { + if (path === filePath) { + return JSON.stringify(json); + } + } + } + throw new Error(`ENOENT: no such file: ${String(path)}`); + }); +} + +beforeEach(() => { + mockReadFileSync.mockReset(); +}); + +const PKG_ROOT = sep + ['project', 'src'].join(sep); +const PKG_PATH = PKG_ROOT + sep + 'package.json'; +const MODULE_A = PKG_ROOT + sep + 'moduleA.js'; +const MODULE_B = PKG_ROOT + sep + 'moduleB.js'; + +const PKG2_ROOT = sep + ['project', 'lib'].join(sep); +const PKG2_PATH = PKG2_ROOT + sep + 'package.json'; +const MODULE_C = PKG2_ROOT + sep + 'moduleC.js'; + +const MODULE_NO_PKG = sep + ['orphan', 'module.js'].join(sep); + +describe('PackageCache', () => { + describe('invalidate', () => { + test('invalidates a package.json and clears module lookups pointing to it', () => { + const closestPackages: ClosestPackageMap = new Map([ + [ + MODULE_A, + {packageJsonPath: PKG_PATH, packageRelativePath: 'moduleA.js'}, + ], + [ + MODULE_B, + {packageJsonPath: PKG_PATH, packageRelativePath: 'moduleB.js'}, + ], + ]); + const cache = createPackageCache(closestPackages); + mockPackageJson(PKG_PATH, {name: 'test-pkg'}); + + // Populate cache + const resultA1 = cache.getPackageForModule(MODULE_A); + const resultB1 = cache.getPackageForModule(MODULE_B); + expect(resultA1?.packageJson.name).toBe('test-pkg'); + expect(resultB1?.packageJson.name).toBe('test-pkg'); + + // Invalidate the package.json + cache.invalidate(PKG_PATH); + + // Update the mock to return new content + mockPackageJson(PKG_PATH, {name: 'updated-pkg'}); + + // Both modules should now return the updated package + const resultA2 = cache.getPackageForModule(MODULE_A); + const resultB2 = cache.getPackageForModule(MODULE_B); + expect(resultA2?.packageJson.name).toBe('updated-pkg'); + expect(resultB2?.packageJson.name).toBe('updated-pkg'); + }); + + test('invalidates a module file and clears its cached result', () => { + const closestPackages: ClosestPackageMap = new Map([ + [ + MODULE_A, + {packageJsonPath: PKG_PATH, packageRelativePath: 'moduleA.js'}, + ], + ]); + const cache = createPackageCache(closestPackages); + mockPackageJson(PKG_PATH, {name: 'test-pkg'}); + + // Populate cache + cache.getPackageForModule(MODULE_A); + expect(mockReadFileSync).toHaveBeenCalledTimes(1); + + // Module result is cached - no new reads + cache.getPackageForModule(MODULE_A); + expect(mockReadFileSync).toHaveBeenCalledTimes(1); + + // Invalidate the module + cache.invalidate(MODULE_A); + + // Next lookup should re-resolve + cache.getPackageForModule(MODULE_A); + // package.json is still cached, so no additional readFileSync + expect(mockReadFileSync).toHaveBeenCalledTimes(1); + }); + + test('invalidating a module cleans up the reverse index to prevent memory leaks', () => { + const closestPackages: ClosestPackageMap = new Map([ + [ + MODULE_A, + {packageJsonPath: PKG_PATH, packageRelativePath: 'moduleA.js'}, + ], + [ + MODULE_B, + {packageJsonPath: PKG_PATH, packageRelativePath: 'moduleB.js'}, + ], + ]); + const cache = createPackageCache(closestPackages); + mockPackageJson(PKG_PATH, {name: 'test-pkg'}); + + // Populate both modules + cache.getPackageForModule(MODULE_A); + cache.getPackageForModule(MODULE_B); + + // Invalidate module A + cache.invalidate(MODULE_A); + + // Invalidate the package.json - only module B should need re-resolution + cache.invalidate(PKG_PATH); + mockPackageJson(PKG_PATH, {name: 'updated-pkg'}); + + // Module B should re-resolve to the updated package + const resultB = cache.getPackageForModule(MODULE_B); + expect(resultB?.packageJson.name).toBe('updated-pkg'); + + // Module A should also re-resolve (it was already invalidated) + const resultA = cache.getPackageForModule(MODULE_A); + expect(resultA?.packageJson.name).toBe('updated-pkg'); + }); + + test('null-cached results are invalidated when a new package.json is created', () => { + const closestPackages = new Map< + string, + ?{packageJsonPath: string, packageRelativePath: string}, + >([[MODULE_NO_PKG, null]]); + const cache = createPackageCache(closestPackages); + + // Module resolves to null (no enclosing package.json) + expect(cache.getPackageForModule(MODULE_NO_PKG)).toBe(null); + + // Cached null is returned + expect(cache.getPackageForModule(MODULE_NO_PKG)).toBe(null); + + // Simulate creation of a new package.json that now covers this module + const newPkgRoot = sep + 'orphan'; + const newPkgPath = newPkgRoot + sep + 'package.json'; + closestPackages.set(MODULE_NO_PKG, { + packageJsonPath: newPkgPath, + packageRelativePath: 'module.js', + }); + mockPackageJson(newPkgPath, {name: 'new-pkg'}); + + // Invalidate the new package.json (file watcher would trigger this) + cache.invalidate(newPkgPath); + + // Module should now resolve to the new package, not stale null + const result = cache.getPackageForModule(MODULE_NO_PKG); + expect(result?.packageJson.name).toBe('new-pkg'); + expect(result?.packageRelativePath).toBe('module.js'); + }); + + test('null-cached results are not invalidated by non-package.json file changes', () => { + const closestPackages: ClosestPackageMap = new Map< + string, + ?{packageJsonPath: string, packageRelativePath: string}, + >([[MODULE_NO_PKG, null]]); + const cache = createPackageCache(closestPackages); + + // Module resolves to null + expect(cache.getPackageForModule(MODULE_NO_PKG)).toBe(null); + + // Invalidate an unrelated file (not a package.json) + cache.invalidate(sep + ['orphan', 'other.js'].join(sep)); + + // Null result should still be cached (closestPackages unchanged) + expect(cache.getPackageForModule(MODULE_NO_PKG)).toBe(null); + }); + + test('invalidating a module with null result cleans up null-tracking set', () => { + const closestPackages = new Map< + string, + ?{packageJsonPath: string, packageRelativePath: string}, + >([ + [MODULE_NO_PKG, null], + [ + MODULE_A, + {packageJsonPath: PKG_PATH, packageRelativePath: 'moduleA.js'}, + ], + ]); + const cache = createPackageCache(closestPackages); + mockPackageJson(PKG_PATH, {name: 'test-pkg'}); + + // Populate caches + expect(cache.getPackageForModule(MODULE_NO_PKG)).toBe(null); + expect(cache.getPackageForModule(MODULE_A)?.packageJson.name).toBe( + 'test-pkg', + ); + + // Invalidate the null-cached module directly + cache.invalidate(MODULE_NO_PKG); + + // Re-resolve - still null since closestPackages hasn't changed + expect(cache.getPackageForModule(MODULE_NO_PKG)).toBe(null); + }); + + test('modules across different packages are independently invalidated', () => { + const closestPackages: ClosestPackageMap = new Map([ + [ + MODULE_A, + {packageJsonPath: PKG_PATH, packageRelativePath: 'moduleA.js'}, + ], + [ + MODULE_C, + {packageJsonPath: PKG2_PATH, packageRelativePath: 'moduleC.js'}, + ], + ]); + const cache = createPackageCache(closestPackages); + mockMultiplePackageJsons([ + [PKG_PATH, {name: 'pkg1'}], + [PKG2_PATH, {name: 'pkg2'}], + ]); + + // Populate cache + expect(cache.getPackageForModule(MODULE_A)?.packageJson.name).toBe( + 'pkg1', + ); + expect(cache.getPackageForModule(MODULE_C)?.packageJson.name).toBe( + 'pkg2', + ); + + // Invalidate only pkg1's package.json + cache.invalidate(PKG_PATH); + mockMultiplePackageJsons([ + [PKG_PATH, {name: 'pkg1-updated'}], + [PKG2_PATH, {name: 'pkg2'}], + ]); + + // Module A should re-resolve to updated pkg1 + expect(cache.getPackageForModule(MODULE_A)?.packageJson.name).toBe( + 'pkg1-updated', + ); + + // Module C should still return cached pkg2 (unchanged) + expect(cache.getPackageForModule(MODULE_C)?.packageJson.name).toBe( + 'pkg2', + ); + }); + + test('package.json deletion invalidates associated modules and null results', () => { + const closestPackages = new Map< + string, + ?{packageJsonPath: string, packageRelativePath: string}, + >([ + [ + MODULE_A, + {packageJsonPath: PKG_PATH, packageRelativePath: 'moduleA.js'}, + ], + [MODULE_NO_PKG, null], + ]); + const cache = createPackageCache(closestPackages); + mockPackageJson(PKG_PATH, {name: 'test-pkg'}); + + // Populate both caches + cache.getPackageForModule(MODULE_A); + expect(cache.getPackageForModule(MODULE_NO_PKG)).toBe(null); + + // Simulate package.json deletion - module A's closest package changes + closestPackages.set(MODULE_A, null); + + // Invalidate the deleted package.json + cache.invalidate(PKG_PATH); + + // Module A should re-resolve (now to null) + expect(cache.getPackageForModule(MODULE_A)).toBe(null); + + // The orphan module's null cache was also cleared (package.json invalidation) + // so it re-resolves, still to null + expect(cache.getPackageForModule(MODULE_NO_PKG)).toBe(null); + }); + + test('invalidating the same file twice is safe', () => { + const closestPackages: ClosestPackageMap = new Map([ + [ + MODULE_A, + {packageJsonPath: PKG_PATH, packageRelativePath: 'moduleA.js'}, + ], + ]); + const cache = createPackageCache(closestPackages); + mockPackageJson(PKG_PATH, {name: 'test-pkg'}); + + cache.getPackageForModule(MODULE_A); + + // Double invalidation should not throw + cache.invalidate(MODULE_A); + cache.invalidate(MODULE_A); + + cache.invalidate(PKG_PATH); + cache.invalidate(PKG_PATH); + + mockPackageJson(PKG_PATH, {name: 'updated-pkg'}); + expect(cache.getPackageForModule(MODULE_A)?.packageJson.name).toBe( + 'updated-pkg', + ); + }); + + test('invalidating a file not in any cache is a no-op', () => { + const closestPackages: ClosestPackageMap = new Map([ + [ + MODULE_A, + {packageJsonPath: PKG_PATH, packageRelativePath: 'moduleA.js'}, + ], + ]); + const cache = createPackageCache(closestPackages); + mockPackageJson(PKG_PATH, {name: 'test-pkg'}); + + cache.getPackageForModule(MODULE_A); + + // Invalidating an unrelated file should not affect anything + cache.invalidate(sep + ['unrelated', 'file.js'].join(sep)); + + // Cached result is still valid + const result = cache.getPackageForModule(MODULE_A); + expect(result?.packageJson.name).toBe('test-pkg'); + expect(mockReadFileSync).toHaveBeenCalledTimes(1); + }); + + test('multiple null-cached modules are all invalidated when package.json appears', () => { + const moduleD = sep + ['orphan', 'deep', 'moduleD.js'].join(sep); + const moduleE = sep + ['orphan', 'deep', 'moduleE.js'].join(sep); + const closestPackages: ClosestPackageMap = new Map< + string, + ?{packageJsonPath: string, packageRelativePath: string}, + >([ + [moduleD, null], + [moduleE, null], + ]); + const cache = createPackageCache(closestPackages); + + // Both resolve to null + expect(cache.getPackageForModule(moduleD)).toBe(null); + expect(cache.getPackageForModule(moduleE)).toBe(null); + + // New package.json created + const newPkgRoot = sep + ['orphan', 'deep'].join(sep); + const newPkgPath = newPkgRoot + sep + 'package.json'; + closestPackages.set(moduleD, { + packageJsonPath: newPkgPath, + packageRelativePath: 'moduleD.js', + }); + closestPackages.set(moduleE, { + packageJsonPath: newPkgPath, + packageRelativePath: 'moduleE.js', + }); + mockPackageJson(newPkgPath, {name: 'orphan-pkg'}); + + cache.invalidate(newPkgPath); + + // Both modules should now resolve + expect(cache.getPackageForModule(moduleD)?.packageJson.name).toBe( + 'orphan-pkg', + ); + expect(cache.getPackageForModule(moduleE)?.packageJson.name).toBe( + 'orphan-pkg', + ); + }); + }); +}); diff --git a/packages/metro/types/Assets.d.ts b/packages/metro/types/Assets.d.ts index 2abd46c69e..eb77e92a9d 100644 --- a/packages/metro/types/Assets.d.ts +++ b/packages/metro/types/Assets.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/Assets.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export type AssetInfo = { diff --git a/packages/metro/types/Bundler.d.ts b/packages/metro/types/Bundler.d.ts index b2fab06525..86d8279403 100644 --- a/packages/metro/types/Bundler.d.ts +++ b/packages/metro/types/Bundler.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<3d5664309abdece0f247fcd0c53c3aaf>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/Bundler.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {TransformResultWithSource} from './DeltaBundler'; diff --git a/packages/metro/types/Bundler/util.d.ts b/packages/metro/types/Bundler/util.d.ts index 8af11471e0..6514a88ced 100644 --- a/packages/metro/types/Bundler/util.d.ts +++ b/packages/metro/types/Bundler/util.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/Bundler/util.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {AssetDataWithoutFiles} from '../Assets'; diff --git a/packages/metro/types/DeltaBundler.d.ts b/packages/metro/types/DeltaBundler.d.ts index 5821986aec..bfb51b9f57 100644 --- a/packages/metro/types/DeltaBundler.d.ts +++ b/packages/metro/types/DeltaBundler.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro/types/DeltaBundler/DeltaCalculator.d.ts b/packages/metro/types/DeltaBundler/DeltaCalculator.d.ts index 032ada7039..1f431c0fc5 100644 --- a/packages/metro/types/DeltaBundler/DeltaCalculator.d.ts +++ b/packages/metro/types/DeltaBundler/DeltaCalculator.d.ts @@ -4,12 +4,18 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/DeltaCalculator.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {DeltaResult, Options} from './types'; -import type {RootPerfLogger} from 'metro-config'; import type {ChangeEvent} from 'metro-file-map'; import {Graph} from './Graph'; @@ -53,15 +59,6 @@ declare class DeltaCalculator extends EventEmitter { */ getGraph(): Graph; _handleMultipleFileChanges: (changeEvent: ChangeEvent) => void; - /** - * Handles a single file change. To avoid doing any work before it's needed, - * the listener only stores the modified file, which will then be used later - * when the delta needs to be calculated. - */ - _handleFileChange: ( - $$PARAM_0$$: ChangeEvent['eventsQueue'][number], - logger: null | undefined | RootPerfLogger, - ) => unknown; _getChangedDependencies( modifiedFiles: Set, deletedFiles: Set, diff --git a/packages/metro/types/DeltaBundler/Graph.d.ts b/packages/metro/types/DeltaBundler/Graph.d.ts index b5832197ef..857b0c3e99 100644 --- a/packages/metro/types/DeltaBundler/Graph.d.ts +++ b/packages/metro/types/DeltaBundler/Graph.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<2af7aa3b61c2afa4d8794e127666c226>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Graph.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro/types/DeltaBundler/Serializers/baseJSBundle.d.ts b/packages/metro/types/DeltaBundler/Serializers/baseJSBundle.d.ts index a77bd41916..9031679e81 100644 --- a/packages/metro/types/DeltaBundler/Serializers/baseJSBundle.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/baseJSBundle.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Module, ReadOnlyGraph, SerializerOptions} from '../types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/getAllFiles.d.ts b/packages/metro/types/DeltaBundler/Serializers/getAllFiles.d.ts index 6f4b91c12a..5ba2f86d80 100644 --- a/packages/metro/types/DeltaBundler/Serializers/getAllFiles.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/getAllFiles.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<1d044a890d1eebbef947f78609d7c58f>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/getAllFiles.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Module, ReadOnlyGraph} from '../types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/getAssets.d.ts b/packages/metro/types/DeltaBundler/Serializers/getAssets.d.ts index dd0d5b92dc..c2ccffec0f 100644 --- a/packages/metro/types/DeltaBundler/Serializers/getAssets.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/getAssets.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<0a49d828c4a80d52ccab4d4766b84c86>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/getAssets.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {AssetData} from '../../Assets'; diff --git a/packages/metro/types/DeltaBundler/Serializers/getExplodedSourceMap.d.ts b/packages/metro/types/DeltaBundler/Serializers/getExplodedSourceMap.d.ts index cf3bc0cbc4..a9d62b738d 100644 --- a/packages/metro/types/DeltaBundler/Serializers/getExplodedSourceMap.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/getExplodedSourceMap.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<623892927b76c4f68802bb69f19d9974>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/getExplodedSourceMap.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Module} from '../types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/getRamBundleInfo.d.ts b/packages/metro/types/DeltaBundler/Serializers/getRamBundleInfo.d.ts index e210803454..d8f03390a6 100644 --- a/packages/metro/types/DeltaBundler/Serializers/getRamBundleInfo.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/getRamBundleInfo.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/getRamBundleInfo.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ModuleTransportLike} from '../../shared/types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/helpers/getInlineSourceMappingURL.d.ts b/packages/metro/types/DeltaBundler/Serializers/helpers/getInlineSourceMappingURL.d.ts index 99fcc74171..7d7febb759 100644 --- a/packages/metro/types/DeltaBundler/Serializers/helpers/getInlineSourceMappingURL.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/helpers/getInlineSourceMappingURL.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/helpers/getInlineSourceMappingURL.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function getInlineSourceMappingURL(sourceMap: string): string; diff --git a/packages/metro/types/DeltaBundler/Serializers/helpers/getSourceMapInfo.d.ts b/packages/metro/types/DeltaBundler/Serializers/helpers/getSourceMapInfo.d.ts index fa1dbb6360..e154274d74 100644 --- a/packages/metro/types/DeltaBundler/Serializers/helpers/getSourceMapInfo.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/helpers/getSourceMapInfo.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/helpers/getSourceMapInfo.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Module} from '../../types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/helpers/getTransitiveDependencies.d.ts b/packages/metro/types/DeltaBundler/Serializers/helpers/getTransitiveDependencies.d.ts index 7ead0045c7..ebd29d0eec 100644 --- a/packages/metro/types/DeltaBundler/Serializers/helpers/getTransitiveDependencies.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/helpers/getTransitiveDependencies.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<55bd91c160900bb31ffe72e2ddfad85d>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/helpers/getTransitiveDependencies.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ReadOnlyGraph} from '../../types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/helpers/js.d.ts b/packages/metro/types/DeltaBundler/Serializers/helpers/js.d.ts index 5205e8e5f1..021c42d296 100644 --- a/packages/metro/types/DeltaBundler/Serializers/helpers/js.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/helpers/js.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/helpers/js.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {MixedOutput, Module} from '../../types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/helpers/processModules.d.ts b/packages/metro/types/DeltaBundler/Serializers/helpers/processModules.d.ts index f5a5aa00d8..c6ac12639b 100644 --- a/packages/metro/types/DeltaBundler/Serializers/helpers/processModules.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/helpers/processModules.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<1c5fe56fba9dbedcde1dbaeb5a486467>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/helpers/processModules.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Module} from '../../types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/hmrJSBundle.d.ts b/packages/metro/types/DeltaBundler/Serializers/hmrJSBundle.d.ts index de19116214..bb05048ec2 100644 --- a/packages/metro/types/DeltaBundler/Serializers/hmrJSBundle.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/hmrJSBundle.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {DeltaResult, ReadOnlyGraph} from '../types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/sourceMapGenerator.d.ts b/packages/metro/types/DeltaBundler/Serializers/sourceMapGenerator.d.ts index 1ee837517b..efd22ce36c 100644 --- a/packages/metro/types/DeltaBundler/Serializers/sourceMapGenerator.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/sourceMapGenerator.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<49bc83c20821024a7b77f5d5c3168d62>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/sourceMapGenerator.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Module} from '../types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/sourceMapObject.d.ts b/packages/metro/types/DeltaBundler/Serializers/sourceMapObject.d.ts index d8bc791106..43a715d006 100644 --- a/packages/metro/types/DeltaBundler/Serializers/sourceMapObject.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/sourceMapObject.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/sourceMapObject.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Module} from '../types'; diff --git a/packages/metro/types/DeltaBundler/Serializers/sourceMapString.d.ts b/packages/metro/types/DeltaBundler/Serializers/sourceMapString.d.ts index 8f301e453a..ae11f5f995 100644 --- a/packages/metro/types/DeltaBundler/Serializers/sourceMapString.d.ts +++ b/packages/metro/types/DeltaBundler/Serializers/sourceMapString.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<578dd38524928420df15b0aba8f32e77>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Serializers/sourceMapString.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Module} from '../types'; diff --git a/packages/metro/types/DeltaBundler/Transformer.d.ts b/packages/metro/types/DeltaBundler/Transformer.d.ts index d84e0f47c9..30c0353bc2 100644 --- a/packages/metro/types/DeltaBundler/Transformer.d.ts +++ b/packages/metro/types/DeltaBundler/Transformer.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<15f603afc860c64c7acc5a6cfe2a6717>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Transformer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {TransformResult, TransformResultWithSource} from '../DeltaBundler'; diff --git a/packages/metro/types/DeltaBundler/Worker.d.ts b/packages/metro/types/DeltaBundler/Worker.d.ts index d79e889513..ec48d676ff 100644 --- a/packages/metro/types/DeltaBundler/Worker.d.ts +++ b/packages/metro/types/DeltaBundler/Worker.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<36640ae81894592fbdc160fac081bdbf>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/Worker.flow.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {TransformResult} from './types'; diff --git a/packages/metro/types/DeltaBundler/WorkerFarm.d.ts b/packages/metro/types/DeltaBundler/WorkerFarm.d.ts index 9e86474d9b..ec857eb608 100644 --- a/packages/metro/types/DeltaBundler/WorkerFarm.d.ts +++ b/packages/metro/types/DeltaBundler/WorkerFarm.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/WorkerFarm.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {TransformResult} from '../DeltaBundler'; diff --git a/packages/metro/types/DeltaBundler/buildSubgraph.d.ts b/packages/metro/types/DeltaBundler/buildSubgraph.d.ts index cc7febb34b..c5631d8241 100644 --- a/packages/metro/types/DeltaBundler/buildSubgraph.d.ts +++ b/packages/metro/types/DeltaBundler/buildSubgraph.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<1e334cd36bb429700b82654f1ddab0a0>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/buildSubgraph.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {RequireContext} from '../lib/contextModule'; diff --git a/packages/metro/types/DeltaBundler/getTransformCacheKey.d.ts b/packages/metro/types/DeltaBundler/getTransformCacheKey.d.ts index 0e2a08634c..e56aebe7d1 100644 --- a/packages/metro/types/DeltaBundler/getTransformCacheKey.d.ts +++ b/packages/metro/types/DeltaBundler/getTransformCacheKey.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<1a82c2238a94514c16e7bb93bf49d8c9>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/getTransformCacheKey.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {TransformerConfig} from './Worker'; diff --git a/packages/metro/types/DeltaBundler/mergeDeltas.d.ts b/packages/metro/types/DeltaBundler/mergeDeltas.d.ts index f38576c502..0846b43bc7 100644 --- a/packages/metro/types/DeltaBundler/mergeDeltas.d.ts +++ b/packages/metro/types/DeltaBundler/mergeDeltas.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<46981e9bc1ef3945b99b147cbdf9ec5d>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/mergeDeltas.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {DeltaBundle} from 'metro-runtime/src/modules/types'; diff --git a/packages/metro/types/DeltaBundler/types.d.ts b/packages/metro/types/DeltaBundler/types.d.ts index 6bd653f9c1..0abc6e6a72 100644 --- a/packages/metro/types/DeltaBundler/types.d.ts +++ b/packages/metro/types/DeltaBundler/types.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/DeltaBundler/types.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {RequireContext} from '../lib/contextModule'; diff --git a/packages/metro/types/HmrServer.d.ts b/packages/metro/types/HmrServer.d.ts index b974807d8e..da762e40b3 100644 --- a/packages/metro/types/HmrServer.d.ts +++ b/packages/metro/types/HmrServer.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/HmrServer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { @@ -68,12 +75,24 @@ declare class HmrServer { _handleFileChange( group: ClientGroup, options: {isInitialUpdate: boolean}, - changeEvent: null | undefined | {logger: null | undefined | RootPerfLogger}, + changeEvent: + | null + | undefined + | { + readonly logger: null | undefined | RootPerfLogger; + readonly changeId?: string; + }, ): Promise; _prepareMessage( group: ClientGroup, options: {isInitialUpdate: boolean}, - changeEvent: null | undefined | {logger: null | undefined | RootPerfLogger}, + changeEvent: + | null + | undefined + | { + readonly logger: null | undefined | RootPerfLogger; + readonly changeId?: string; + }, ): Promise; } export default HmrServer; diff --git a/packages/metro/types/IncrementalBundler.d.ts b/packages/metro/types/IncrementalBundler.d.ts index 96cdf19025..5e6a677e43 100644 --- a/packages/metro/types/IncrementalBundler.d.ts +++ b/packages/metro/types/IncrementalBundler.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<0ec72971869a882d97b381e3f1baa922>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/IncrementalBundler.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {DeltaResult, Graph, Module} from './DeltaBundler'; diff --git a/packages/metro/types/IncrementalBundler/GraphNotFoundError.d.ts b/packages/metro/types/IncrementalBundler/GraphNotFoundError.d.ts index ae41d87542..b4a401deb4 100644 --- a/packages/metro/types/IncrementalBundler/GraphNotFoundError.d.ts +++ b/packages/metro/types/IncrementalBundler/GraphNotFoundError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<850ba6867e140fb0973cd13d0fd1bc60>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/IncrementalBundler/GraphNotFoundError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {GraphId} from '../lib/getGraphId'; diff --git a/packages/metro/types/IncrementalBundler/ResourceNotFoundError.d.ts b/packages/metro/types/IncrementalBundler/ResourceNotFoundError.d.ts index ee6fad67bf..b6d6526c14 100644 --- a/packages/metro/types/IncrementalBundler/ResourceNotFoundError.d.ts +++ b/packages/metro/types/IncrementalBundler/ResourceNotFoundError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<6a9d75bc74b654362c3563ec8babda0b>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/IncrementalBundler/ResourceNotFoundError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare class ResourceNotFoundError extends Error { diff --git a/packages/metro/types/IncrementalBundler/RevisionNotFoundError.d.ts b/packages/metro/types/IncrementalBundler/RevisionNotFoundError.d.ts index e39aca0044..35b2b5890c 100644 --- a/packages/metro/types/IncrementalBundler/RevisionNotFoundError.d.ts +++ b/packages/metro/types/IncrementalBundler/RevisionNotFoundError.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<2c2d4a1a2d357eb73806a68bba897795>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/IncrementalBundler/RevisionNotFoundError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {RevisionId} from '../IncrementalBundler'; diff --git a/packages/metro/types/ModuleGraph/worker/JsFileWrapping.d.ts b/packages/metro/types/ModuleGraph/worker/JsFileWrapping.d.ts index 7c620fa91c..7b1f843fa6 100644 --- a/packages/metro/types/ModuleGraph/worker/JsFileWrapping.d.ts +++ b/packages/metro/types/ModuleGraph/worker/JsFileWrapping.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/ModuleGraph/worker/JsFileWrapping.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {File as BabelNodeFile} from '@babel/types'; diff --git a/packages/metro/types/ModuleGraph/worker/collectDependencies.d.ts b/packages/metro/types/ModuleGraph/worker/collectDependencies.d.ts index 25adbdf921..1b1d56067c 100644 --- a/packages/metro/types/ModuleGraph/worker/collectDependencies.d.ts +++ b/packages/metro/types/ModuleGraph/worker/collectDependencies.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/ModuleGraph/worker/collectDependencies.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ReadonlySourceLocation} from '../../shared/types'; diff --git a/packages/metro/types/ModuleGraph/worker/generateImportNames.d.ts b/packages/metro/types/ModuleGraph/worker/generateImportNames.d.ts index 931791d5c1..7d2813be7e 100644 --- a/packages/metro/types/ModuleGraph/worker/generateImportNames.d.ts +++ b/packages/metro/types/ModuleGraph/worker/generateImportNames.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<9632d9ca461f1fd6aad9131d7afb5839>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/ModuleGraph/worker/generateImportNames.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Node} from '@babel/types'; diff --git a/packages/metro/types/ModuleGraph/worker/importLocationsPlugin.d.ts b/packages/metro/types/ModuleGraph/worker/importLocationsPlugin.d.ts index a3d998f09e..d869be6cdb 100644 --- a/packages/metro/types/ModuleGraph/worker/importLocationsPlugin.d.ts +++ b/packages/metro/types/ModuleGraph/worker/importLocationsPlugin.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<644d25f2f9682a306271d052d09b1d2d>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/ModuleGraph/worker/importLocationsPlugin.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ReadonlySourceLocation} from '../../shared/types'; diff --git a/packages/metro/types/Server.d.ts b/packages/metro/types/Server.d.ts index 27adcc3834..dc787733a5 100644 --- a/packages/metro/types/Server.d.ts +++ b/packages/metro/types/Server.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<161e77301d04ce6cc254f1dbf15ef06b>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/Server.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {AssetData} from './Assets'; diff --git a/packages/metro/types/Server/MultipartResponse.d.ts b/packages/metro/types/Server/MultipartResponse.d.ts index 73d7c83e94..2ecb98be17 100644 --- a/packages/metro/types/Server/MultipartResponse.d.ts +++ b/packages/metro/types/Server/MultipartResponse.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<355f5514464c4989f90a211782db41e7>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/Server/MultipartResponse.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {IncomingMessage, ServerResponse} from 'http'; diff --git a/packages/metro/types/Server/symbolicate.d.ts b/packages/metro/types/Server/symbolicate.d.ts index 0d8616112e..b5d343f640 100644 --- a/packages/metro/types/Server/symbolicate.d.ts +++ b/packages/metro/types/Server/symbolicate.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<74bba01977b0b5887c4bb38eb8fc78ea>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/Server/symbolicate.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ExplodedSourceMap} from '../DeltaBundler/Serializers/getExplodedSourceMap'; diff --git a/packages/metro/types/cli-utils.d.ts b/packages/metro/types/cli-utils.d.ts index a83f884f89..4f4de07fdf 100644 --- a/packages/metro/types/cli-utils.d.ts +++ b/packages/metro/types/cli-utils.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/cli-utils.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export declare const watchFile: ( diff --git a/packages/metro/types/cli/parseKeyValueParamArray.d.ts b/packages/metro/types/cli/parseKeyValueParamArray.d.ts index 199fbdc34e..446582fefb 100644 --- a/packages/metro/types/cli/parseKeyValueParamArray.d.ts +++ b/packages/metro/types/cli/parseKeyValueParamArray.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/cli/parseKeyValueParamArray.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function coerceKeyValueArray(keyValueArray: ReadonlyArray): { diff --git a/packages/metro/types/commands/build.d.ts b/packages/metro/types/commands/build.d.ts index 2bfa186c72..9466883d23 100644 --- a/packages/metro/types/commands/build.d.ts +++ b/packages/metro/types/commands/build.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<10c41709fa9779dd0e12ef411eaef8eb>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/commands/build.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {CommandModule} from 'yargs'; diff --git a/packages/metro/types/commands/dependencies.d.ts b/packages/metro/types/commands/dependencies.d.ts index 2bfa186c72..52e19182ac 100644 --- a/packages/metro/types/commands/dependencies.d.ts +++ b/packages/metro/types/commands/dependencies.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/commands/dependencies.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {CommandModule} from 'yargs'; diff --git a/packages/metro/types/commands/serve.d.ts b/packages/metro/types/commands/serve.d.ts index 2bfa186c72..86fec5bf72 100644 --- a/packages/metro/types/commands/serve.d.ts +++ b/packages/metro/types/commands/serve.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<367f11c0b6933f8c8c58718c7dd41e78>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/commands/serve.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {CommandModule} from 'yargs'; diff --git a/packages/metro/types/index.d.ts b/packages/metro/types/index.d.ts index 3f9a735daa..615d384576 100644 --- a/packages/metro/types/index.d.ts +++ b/packages/metro/types/index.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<3c6460427c6760887187d6b16151c609>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/index.flow.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {AssetData} from './Assets'; @@ -118,9 +125,28 @@ type DependenciesCommandOptions = Readonly<{ }> | null; export {Terminal, JsonReporter, TerminalReporter}; export type {AssetData} from './Assets'; -export type {Reporter, ReportableEvent} from './lib/reporting'; +export type { + AsyncDependencyType, + DeltaResult, + Dependency, + MixedOutput, + Module, + ReadOnlyDependencies, + ReadOnlyGraph, + SerializerOptions, + TransformInputOptions, + TransformResult, + TransformResultDependency, +} from './DeltaBundler/types'; +export type {default as DependencyGraph} from './node-haste/DependencyGraph'; +export type {BundleDetails, Reporter, ReportableEvent} from './lib/reporting'; export type {TerminalReportableEvent} from './lib/TerminalReporter'; -export type {MetroConfig}; +export type { + ContextMode, + RequireContextParams, +} from './ModuleGraph/worker/collectDependencies'; +export type {ServerOptions} from './Server'; +export type {MetroConfig, MetroServer}; export declare function runMetro( config: InputConfigT, options?: RunMetroOptions, diff --git a/packages/metro/types/lib/BatchProcessor.d.ts b/packages/metro/types/lib/BatchProcessor.d.ts index f25e454059..5a605d9981 100644 --- a/packages/metro/types/lib/BatchProcessor.d.ts +++ b/packages/metro/types/lib/BatchProcessor.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<5872ab26db1c8f4499c971170c5012c4>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/BatchProcessor.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import {setTimeout} from 'timers'; diff --git a/packages/metro/types/lib/CountingSet.d.ts b/packages/metro/types/lib/CountingSet.d.ts index dc00d05597..09cfaecd13 100644 --- a/packages/metro/types/lib/CountingSet.d.ts +++ b/packages/metro/types/lib/CountingSet.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<0fccda5d7f0eb38539316fa1fedae97b>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/CountingSet.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export interface ReadOnlyCountingSet extends Iterable { diff --git a/packages/metro/types/lib/JsonReporter.d.ts b/packages/metro/types/lib/JsonReporter.d.ts index eaa4b495b6..9d29f11125 100644 --- a/packages/metro/types/lib/JsonReporter.d.ts +++ b/packages/metro/types/lib/JsonReporter.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/JsonReporter.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Writable} from 'stream'; diff --git a/packages/metro/types/lib/RamBundleParser.d.ts b/packages/metro/types/lib/RamBundleParser.d.ts index 10bccc60ab..74569bd6df 100644 --- a/packages/metro/types/lib/RamBundleParser.d.ts +++ b/packages/metro/types/lib/RamBundleParser.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/RamBundleParser.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro/types/lib/TerminalReporter.d.ts b/packages/metro/types/lib/TerminalReporter.d.ts index b02f034d4b..a0e01f1126 100644 --- a/packages/metro/types/lib/TerminalReporter.d.ts +++ b/packages/metro/types/lib/TerminalReporter.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<8218e45d6b5186264c4bf9e54086708a>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/TerminalReporter.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {BundleDetails, ReportableEvent} from './reporting'; diff --git a/packages/metro/types/lib/bundleProgressUtils.d.ts b/packages/metro/types/lib/bundleProgressUtils.d.ts index a0521c5671..ae88fe9c83 100644 --- a/packages/metro/types/lib/bundleProgressUtils.d.ts +++ b/packages/metro/types/lib/bundleProgressUtils.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<6f0cfa5c118fa3cbe65acee044b8c927>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/bundleProgressUtils.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ /** diff --git a/packages/metro/types/lib/bundleToString.d.ts b/packages/metro/types/lib/bundleToString.d.ts index a31b84a1a2..aa3435b1ba 100644 --- a/packages/metro/types/lib/bundleToString.d.ts +++ b/packages/metro/types/lib/bundleToString.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<89a26e72bdd126e3feb0abc9b3186d33>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/bundleToString.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Bundle, BundleMetadata} from 'metro-runtime/src/modules/types'; diff --git a/packages/metro/types/lib/contextModule.d.ts b/packages/metro/types/lib/contextModule.d.ts index 2fff14ef85..c9a4a0260f 100644 --- a/packages/metro/types/lib/contextModule.d.ts +++ b/packages/metro/types/lib/contextModule.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<461f7a7b3b3d99d1f1e7eeeeb5125686>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/contextModule.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/metro/types/lib/contextModuleTemplates.d.ts b/packages/metro/types/lib/contextModuleTemplates.d.ts index 4f9ad3a514..97225bc0cc 100644 --- a/packages/metro/types/lib/contextModuleTemplates.d.ts +++ b/packages/metro/types/lib/contextModuleTemplates.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/contextModuleTemplates.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ContextMode} from '../ModuleGraph/worker/collectDependencies'; diff --git a/packages/metro/types/lib/countLines.d.ts b/packages/metro/types/lib/countLines.d.ts index 56ce4a09ea..7b059f6dbd 100644 --- a/packages/metro/types/lib/countLines.d.ts +++ b/packages/metro/types/lib/countLines.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/countLines.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function countLines(string: string): number; diff --git a/packages/metro/types/lib/createWebsocketServer.d.ts b/packages/metro/types/lib/createWebsocketServer.d.ts index c2910e74db..81e74228ba 100644 --- a/packages/metro/types/lib/createWebsocketServer.d.ts +++ b/packages/metro/types/lib/createWebsocketServer.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/createWebsocketServer.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import ws from 'ws'; diff --git a/packages/metro/types/lib/debounceAsyncQueue.d.ts b/packages/metro/types/lib/debounceAsyncQueue.d.ts index 3bdd573591..b528914ed4 100644 --- a/packages/metro/types/lib/debounceAsyncQueue.d.ts +++ b/packages/metro/types/lib/debounceAsyncQueue.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<900f968546b7cc3e463e2de9d1f06200>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/debounceAsyncQueue.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function debounceAsyncQueue( diff --git a/packages/metro/types/lib/formatBundlingError.d.ts b/packages/metro/types/lib/formatBundlingError.d.ts index 64994cdf9f..1c0bb92668 100644 --- a/packages/metro/types/lib/formatBundlingError.d.ts +++ b/packages/metro/types/lib/formatBundlingError.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<0c18118765a7730747fbadfd10e5d8f6>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/formatBundlingError.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {FormattedError} from 'metro-runtime/src/modules/types'; diff --git a/packages/metro/types/lib/getAppendScripts.d.ts b/packages/metro/types/lib/getAppendScripts.d.ts index d5486f268f..abc98a50dc 100644 --- a/packages/metro/types/lib/getAppendScripts.d.ts +++ b/packages/metro/types/lib/getAppendScripts.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<72fd04e53dc895f1305e10043f986edc>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/getAppendScripts.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Module} from '../DeltaBundler'; diff --git a/packages/metro/types/lib/getGraphId.d.ts b/packages/metro/types/lib/getGraphId.d.ts index ba577ee795..3ac088ef92 100644 --- a/packages/metro/types/lib/getGraphId.d.ts +++ b/packages/metro/types/lib/getGraphId.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/getGraphId.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {TransformInputOptions} from '../DeltaBundler/types'; diff --git a/packages/metro/types/lib/getPreludeCode.d.ts b/packages/metro/types/lib/getPreludeCode.d.ts index 6ae0bbdc31..db40e398f2 100644 --- a/packages/metro/types/lib/getPreludeCode.d.ts +++ b/packages/metro/types/lib/getPreludeCode.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/getPreludeCode.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function getPreludeCode($$PARAM_0$$: { diff --git a/packages/metro/types/lib/getPrependedScripts.d.ts b/packages/metro/types/lib/getPrependedScripts.d.ts index 1142f16cbc..58907c2142 100644 --- a/packages/metro/types/lib/getPrependedScripts.d.ts +++ b/packages/metro/types/lib/getPrependedScripts.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/getPrependedScripts.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type Bundler from '../Bundler'; diff --git a/packages/metro/types/lib/isResolvedDependency.d.ts b/packages/metro/types/lib/isResolvedDependency.d.ts index 653f340c6e..050596a43e 100644 --- a/packages/metro/types/lib/isResolvedDependency.d.ts +++ b/packages/metro/types/lib/isResolvedDependency.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/isResolvedDependency.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Dependency, ResolvedDependency} from '../DeltaBundler/types'; diff --git a/packages/metro/types/lib/logToConsole.d.ts b/packages/metro/types/lib/logToConsole.d.ts index 03240f7503..495de395ab 100644 --- a/packages/metro/types/lib/logToConsole.d.ts +++ b/packages/metro/types/lib/logToConsole.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<95cf7d414507e2035210d06882d858b3>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/logToConsole.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Terminal} from 'metro-core'; diff --git a/packages/metro/types/lib/parseBundleOptionsFromBundleRequestUrl.d.ts b/packages/metro/types/lib/parseBundleOptionsFromBundleRequestUrl.d.ts index 6016c75993..ec56c75a03 100644 --- a/packages/metro/types/lib/parseBundleOptionsFromBundleRequestUrl.d.ts +++ b/packages/metro/types/lib/parseBundleOptionsFromBundleRequestUrl.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<972fc847af4a55ba0e4863c90532e99c>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/parseBundleOptionsFromBundleRequestUrl.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {BundleOptions} from '../shared/types'; diff --git a/packages/metro/types/lib/parseCustomResolverOptions.d.ts b/packages/metro/types/lib/parseCustomResolverOptions.d.ts index dad1bb36c4..225061498b 100644 --- a/packages/metro/types/lib/parseCustomResolverOptions.d.ts +++ b/packages/metro/types/lib/parseCustomResolverOptions.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/parseCustomResolverOptions.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {CustomResolverOptions} from 'metro-resolver'; diff --git a/packages/metro/types/lib/parseCustomTransformOptions.d.ts b/packages/metro/types/lib/parseCustomTransformOptions.d.ts index 1330d44eeb..9656262282 100644 --- a/packages/metro/types/lib/parseCustomTransformOptions.d.ts +++ b/packages/metro/types/lib/parseCustomTransformOptions.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<086e5d861160a99775fa58beba59492a>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/parseCustomTransformOptions.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {CustomTransformOptions} from 'metro-transform-worker'; diff --git a/packages/metro/types/lib/parseJsonBody.d.ts b/packages/metro/types/lib/parseJsonBody.d.ts index 21253fa9a8..b30bd9c329 100644 --- a/packages/metro/types/lib/parseJsonBody.d.ts +++ b/packages/metro/types/lib/parseJsonBody.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/parseJsonBody.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {IncomingMessage} from 'http'; diff --git a/packages/metro/types/lib/pathUtils.d.ts b/packages/metro/types/lib/pathUtils.d.ts index aed8edef57..ac78babc66 100644 --- a/packages/metro/types/lib/pathUtils.d.ts +++ b/packages/metro/types/lib/pathUtils.d.ts @@ -4,7 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat + * @generated SignedSource<<61d935169a7ad0d7f2d7b4c6e4eb0a96>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/pathUtils.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export declare const normalizePathSeparatorsToPosix: ( diff --git a/packages/metro/types/lib/relativizeSourceMap.d.ts b/packages/metro/types/lib/relativizeSourceMap.d.ts index 6f0809b41a..a3fc924bea 100644 --- a/packages/metro/types/lib/relativizeSourceMap.d.ts +++ b/packages/metro/types/lib/relativizeSourceMap.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<5282fe2c42baa79f957ef2a40bec560b>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/relativizeSourceMap.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {MixedSourceMap} from 'metro-source-map'; diff --git a/packages/metro/types/lib/reporting.d.ts b/packages/metro/types/lib/reporting.d.ts index f2075ecd06..07eef4140c 100644 --- a/packages/metro/types/lib/reporting.d.ts +++ b/packages/metro/types/lib/reporting.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<1ff95f1ad6bb911f3d2c2bc41d59bc1a>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/reporting.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {Terminal} from 'metro-core'; diff --git a/packages/metro/types/lib/splitBundleOptions.d.ts b/packages/metro/types/lib/splitBundleOptions.d.ts index 5da7131f2b..e9444a9a0f 100644 --- a/packages/metro/types/lib/splitBundleOptions.d.ts +++ b/packages/metro/types/lib/splitBundleOptions.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/splitBundleOptions.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {BundleOptions, SplitBundleOptions} from '../shared/types'; diff --git a/packages/metro/types/lib/transformHelpers.d.ts b/packages/metro/types/lib/transformHelpers.d.ts index 3ea34a7256..d56a62cf44 100644 --- a/packages/metro/types/lib/transformHelpers.d.ts +++ b/packages/metro/types/lib/transformHelpers.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/transformHelpers.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type Bundler from '../Bundler'; diff --git a/packages/metro/types/node-haste/DependencyGraph.d.ts b/packages/metro/types/node-haste/DependencyGraph.d.ts index 20e3a81d78..6daabfb1c1 100644 --- a/packages/metro/types/node-haste/DependencyGraph.d.ts +++ b/packages/metro/types/node-haste/DependencyGraph.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<13f1483d2a732241f8d9eae463399b0e>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/node-haste/DependencyGraph.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { @@ -13,7 +20,6 @@ import type { TransformResultDependency, } from '../DeltaBundler/types'; import type {ResolverInputOptions} from '../shared/types'; -import type Package from './Package'; import type {ConfigT} from 'metro-config'; import type { ChangeEvent, @@ -25,7 +31,6 @@ import type { } from 'metro-file-map'; import {ModuleResolver} from './DependencyGraph/ModuleResolution'; -import {PackageCache} from './PackageCache'; import EventEmitter from 'events'; declare class DependencyGraph extends EventEmitter { @@ -33,7 +38,7 @@ declare class DependencyGraph extends EventEmitter { _haste: MetroFileMap; _fileSystem: FileSystem; _hasteMap: HasteMap; - _moduleResolver: ModuleResolver; + _moduleResolver: ModuleResolver; _resolutionCache: Map< string | symbol, Map< @@ -57,7 +62,6 @@ declare class DependencyGraph extends EventEmitter { _getClosestPackage( absoluteModulePath: string, ): null | undefined | {packageJsonPath: string; packageRelativePath: string}; - _createPackageCache(): PackageCache; getAllFiles(): Array; /** * Used when watcher.unstable_lazySha1 is true diff --git a/packages/metro/types/node-haste/DependencyGraph/ModuleResolution.d.ts b/packages/metro/types/node-haste/DependencyGraph/ModuleResolution.d.ts index a07f3d9a83..7b591bb816 100644 --- a/packages/metro/types/node-haste/DependencyGraph/ModuleResolution.d.ts +++ b/packages/metro/types/node-haste/DependencyGraph/ModuleResolution.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<0024fd05b95efe19a24f9acc84ff474b>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { @@ -25,19 +32,7 @@ import type { import type {PackageForModule, PackageJson} from 'metro-resolver/private/types'; export type DirExistsFn = (filePath: string) => boolean; -export type Packageish = {path: string; read(): PackageJson}; -export type Moduleish = {readonly path: string}; -export type PackageishCache = { - getPackage( - name: string, - platform?: string, - supportsNativePlatform?: boolean, - ): TPackage; - getPackageOf( - absolutePath: string, - ): null | undefined | {pkg: TPackage; packageRelativePath: string}; -}; -type Options = Readonly<{ +type Options = Readonly<{ assetExts: ReadonlySet; dirExists: DirExistsFn; disableHierarchicalLookup: boolean; @@ -54,7 +49,10 @@ type Options = Readonly<{ platform: null | undefined | string, ) => null | undefined | string; mainFields: ReadonlyArray; - packageCache: PackageishCache; + getPackage: (packageJsonPath: string) => null | undefined | PackageJson; + getPackageForModule: ( + absolutePath: string, + ) => null | undefined | PackageForModule; nodeModulesPaths: ReadonlyArray; preferNativePlatform: boolean; projectRoot: string; @@ -69,11 +67,11 @@ type Options = Readonly<{ unstable_enablePackageExports: boolean; unstable_incrementalResolution: boolean; }>; -export declare class ModuleResolver { - _options: Options; +export declare class ModuleResolver { + _options: Options; _projectRootFakeModulePath: string; _cachedEmptyModule: null | undefined | BundlerResolution; - constructor(options: Options); + constructor(options: Options); _getEmptyModule(): BundlerResolution; resolveDependency( originModulePath: string, @@ -82,10 +80,6 @@ export declare class ModuleResolver { platform: string | null, resolverOptions: ResolverInputOptions, ): BundlerResolution; - _getPackage: (packageJsonPath: string) => null | undefined | PackageJson; - _getPackageForModule: ( - absolutePath: string, - ) => null | undefined | PackageForModule; /** * TODO: Return Resolution instead of coercing to BundlerResolution here */ diff --git a/packages/metro/types/node-haste/DependencyGraph/createFileMap.d.ts b/packages/metro/types/node-haste/DependencyGraph/createFileMap.d.ts index cdff80fcad..756ad15de7 100644 --- a/packages/metro/types/node-haste/DependencyGraph/createFileMap.d.ts +++ b/packages/metro/types/node-haste/DependencyGraph/createFileMap.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/node-haste/DependencyGraph/createFileMap.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ConfigT} from 'metro-config'; diff --git a/packages/metro/types/node-haste/Package.d.ts b/packages/metro/types/node-haste/Package.d.ts deleted file mode 100644 index 02d65dfd3f..0000000000 --- a/packages/metro/types/node-haste/Package.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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. - * - * @format - * @oncall react_native - */ - -import type {PackageJson} from 'metro-resolver/private/types'; - -declare class Package { - path: string; - _root: string; - _content: null | undefined | PackageJson; - constructor($$PARAM_0$$: {file: string}); - invalidate(): void; - read(): PackageJson; -} -export default Package; diff --git a/packages/metro/types/node-haste/PackageCache.d.ts b/packages/metro/types/node-haste/PackageCache.d.ts index 2abf30b3f9..0b54fd2ec6 100644 --- a/packages/metro/types/node-haste/PackageCache.d.ts +++ b/packages/metro/types/node-haste/PackageCache.d.ts @@ -4,31 +4,34 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<768dba0958b531c8edd43c2df24e25f6>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/node-haste/PackageCache.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ -import Package from './Package'; +import type {PackageJson} from 'metro-resolver/private/types'; type GetClosestPackageFn = ( absoluteFilePath: string, ) => null | undefined | {packageJsonPath: string; packageRelativePath: string}; +type PackageForModule = Readonly<{ + packageJson: PackageJson; + rootPath: string; + packageRelativePath: string; +}>; export declare class PackageCache { - _getClosestPackage: GetClosestPackageFn; - _packageCache: {[filePath: string]: Package}; - _packagePathAndSubpathByModulePath: { - [filePath: string]: - | null - | undefined - | {packageJsonPath: string; packageRelativePath: string}; - }; - _modulePathsByPackagePath: { - [filePath: string]: Set; - }; constructor(options: {getClosestPackage: GetClosestPackageFn}); - getPackage(filePath: string): Package; - getPackageOf( + getPackage( + filePath: string, + ): Readonly<{rootPath: string; packageJson: PackageJson}>; + getPackageForModule( absoluteModulePath: string, - ): null | undefined | {pkg: Package; packageRelativePath: string}; + ): null | undefined | PackageForModule; invalidate(filePath: string): void; } diff --git a/packages/metro/types/node-haste/lib/AssetPaths.d.ts b/packages/metro/types/node-haste/lib/AssetPaths.d.ts index 9a515cc905..1bf790a58b 100644 --- a/packages/metro/types/node-haste/lib/AssetPaths.d.ts +++ b/packages/metro/types/node-haste/lib/AssetPaths.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<31b3384bffa191e4c3c9916d93df8571>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/node-haste/lib/AssetPaths.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export type AssetPath = { diff --git a/packages/metro/types/node-haste/lib/parsePlatformFilePath.d.ts b/packages/metro/types/node-haste/lib/parsePlatformFilePath.d.ts index b6c2179ea7..4236364d2e 100644 --- a/packages/metro/types/node-haste/lib/parsePlatformFilePath.d.ts +++ b/packages/metro/types/node-haste/lib/parsePlatformFilePath.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<61c16b5ef31517dc44347558a4dd431a>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/node-haste/lib/parsePlatformFilePath.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ type PlatformFilePathParts = { diff --git a/packages/metro/types/shared/output/RamBundle.d.ts b/packages/metro/types/shared/output/RamBundle.d.ts index 663a5d4733..748e9112e9 100644 --- a/packages/metro/types/shared/output/RamBundle.d.ts +++ b/packages/metro/types/shared/output/RamBundle.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/RamBundle.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {RamBundleInfo} from '../../DeltaBundler/Serializers/getRamBundleInfo'; diff --git a/packages/metro/types/shared/output/RamBundle/as-assets.d.ts b/packages/metro/types/shared/output/RamBundle/as-assets.d.ts index 4553b06283..2c7d841cda 100644 --- a/packages/metro/types/shared/output/RamBundle/as-assets.d.ts +++ b/packages/metro/types/shared/output/RamBundle/as-assets.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<02d7f6eec9c93d02612c9b2fdef18cef>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/RamBundle/as-assets.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {RamBundleInfo} from '../../../DeltaBundler/Serializers/getRamBundleInfo'; diff --git a/packages/metro/types/shared/output/RamBundle/as-indexed-file.d.ts b/packages/metro/types/shared/output/RamBundle/as-indexed-file.d.ts index b1c6be4546..72cdc51cfd 100644 --- a/packages/metro/types/shared/output/RamBundle/as-indexed-file.d.ts +++ b/packages/metro/types/shared/output/RamBundle/as-indexed-file.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/RamBundle/as-indexed-file.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {RamBundleInfo} from '../../../DeltaBundler/Serializers/getRamBundleInfo'; diff --git a/packages/metro/types/shared/output/RamBundle/buildSourcemapWithMetadata.d.ts b/packages/metro/types/shared/output/RamBundle/buildSourcemapWithMetadata.d.ts index 9d6df3eff4..f04534e288 100644 --- a/packages/metro/types/shared/output/RamBundle/buildSourcemapWithMetadata.d.ts +++ b/packages/metro/types/shared/output/RamBundle/buildSourcemapWithMetadata.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<669a46ab2a802ea2b93d98edf337fff0>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/RamBundle/buildSourcemapWithMetadata.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ModuleGroups, ModuleTransportLike} from '../../types'; diff --git a/packages/metro/types/shared/output/RamBundle/magic-number.d.ts b/packages/metro/types/shared/output/RamBundle/magic-number.d.ts index 19d807ad28..95e148ba5e 100644 --- a/packages/metro/types/shared/output/RamBundle/magic-number.d.ts +++ b/packages/metro/types/shared/output/RamBundle/magic-number.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<94cd41a2f04528e81f82e6c2feadaf52>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/RamBundle/magic-number.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare const $$EXPORT_DEFAULT_DECLARATION$$: 0xfb0bd1e5; diff --git a/packages/metro/types/shared/output/RamBundle/util.d.ts b/packages/metro/types/shared/output/RamBundle/util.d.ts index a093366277..3a21604fd8 100644 --- a/packages/metro/types/shared/output/RamBundle/util.d.ts +++ b/packages/metro/types/shared/output/RamBundle/util.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/RamBundle/util.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {ModuleGroups, ModuleTransportLike} from '../../types'; diff --git a/packages/metro/types/shared/output/RamBundle/write-sourcemap.d.ts b/packages/metro/types/shared/output/RamBundle/write-sourcemap.d.ts index 145b077c0f..a1c9a86cf5 100644 --- a/packages/metro/types/shared/output/RamBundle/write-sourcemap.d.ts +++ b/packages/metro/types/shared/output/RamBundle/write-sourcemap.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<000f29900c01342de92d247507075575>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/RamBundle/write-sourcemap.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function writeSourcemap( diff --git a/packages/metro/types/shared/output/bundle.d.ts b/packages/metro/types/shared/output/bundle.d.ts index d4a21054b4..8756c93d23 100644 --- a/packages/metro/types/shared/output/bundle.d.ts +++ b/packages/metro/types/shared/output/bundle.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/bundle.flow.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type {AssetData} from '../../Assets'; diff --git a/packages/metro/types/shared/output/meta.d.ts b/packages/metro/types/shared/output/meta.d.ts index e5adc984c1..ad7b49ebbd 100644 --- a/packages/metro/types/shared/output/meta.d.ts +++ b/packages/metro/types/shared/output/meta.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<17ae3fc242dc6b3915a72d04dad032b8>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/meta.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function meta( diff --git a/packages/metro/types/shared/output/unbundle.d.ts b/packages/metro/types/shared/output/unbundle.d.ts index f66611d395..524f634f46 100644 --- a/packages/metro/types/shared/output/unbundle.d.ts +++ b/packages/metro/types/shared/output/unbundle.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<12b27507f799c8170067738c227bb2f3>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/unbundle.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export * from './RamBundle'; diff --git a/packages/metro/types/shared/output/writeFile.d.ts b/packages/metro/types/shared/output/writeFile.d.ts index 9cc942f3d2..a6bb323c0c 100644 --- a/packages/metro/types/shared/output/writeFile.d.ts +++ b/packages/metro/types/shared/output/writeFile.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<37de849e415f8aa1e94b73289a573ee3>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/output/writeFile.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ declare function writeFile( diff --git a/packages/metro/types/shared/types.d.ts b/packages/metro/types/shared/types.d.ts index 858c6c6fe8..3ecb68180c 100644 --- a/packages/metro/types/shared/types.d.ts +++ b/packages/metro/types/shared/types.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<31593693c0cafcd55eaf4885efddc6be>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/shared/types.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ import type { diff --git a/packages/ob1/package.json b/packages/ob1/package.json index 5c715fd194..6e42f0e2d1 100644 --- a/packages/ob1/package.json +++ b/packages/ob1/package.json @@ -1,6 +1,6 @@ { "name": "ob1", - "version": "0.83.5", + "version": "0.83.6", "description": "A small library for working with 0- and 1-based offsets in a type-checked way.", "main": "src/ob1.js", "exports": { diff --git a/packages/ob1/types/ob1.d.ts b/packages/ob1/types/ob1.d.ts index e701489038..334841a6ca 100644 --- a/packages/ob1/types/ob1.d.ts +++ b/packages/ob1/types/ob1.d.ts @@ -4,8 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @noformat * @oncall react_native + * @generated SignedSource<<60dfc45cb0b1c5cfcb0b454c7539e9c9>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/ob1/src/ob1.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) */ export declare type Number0 = symbol & {__Number0__: string}; diff --git a/scripts/__tests__/ts-defs-sync-test.js b/scripts/__tests__/ts-defs-sync-test.js index 23ee7ad145..599f99fe86 100644 --- a/scripts/__tests__/ts-defs-sync-test.js +++ b/scripts/__tests__/ts-defs-sync-test.js @@ -28,4 +28,4 @@ test('TypeScript defs are in sync (yarn run build-ts-defs produces no changes)', expect(error.errors).toEqual([]); } expect(error).toBeUndefined(); -}, 30000); +}, 60000); diff --git a/scripts/generateTypeScriptDefinitions.js b/scripts/generateTypeScriptDefinitions.js index a216a9669c..d889aca227 100644 --- a/scripts/generateTypeScriptDefinitions.js +++ b/scripts/generateTypeScriptDefinitions.js @@ -20,6 +20,8 @@ import nullthrows from 'nullthrows'; import path from 'path'; import * as prettier from 'prettier'; // $FlowFixMe[untyped-import] in OSS only +import SignedSource from 'signedsource'; +// $FlowFixMe[untyped-import] in OSS only import {globSync} from 'tinyglobby'; const WORKSPACE_ROOT = path.resolve(__dirname, '..'); @@ -116,11 +118,29 @@ export async function generateTsDefsForJsGlobs( console.warn(sourceFile, lintResult.messages); } - const finalOutput = await prettier.format( + const formattedOutput = await prettier.format( lintResult.output ?? sourceContent, prettierConfig, ); + // Add signedsource (generated) token to the header + const withToken = formattedOutput + .replace( + '\n */\n', + `\n * ${SignedSource.getSigningToken()}\n *` + + `\n * This file was translated from Flow by ${path.relative(WORKSPACE_ROOT, __filename).replaceAll(path.sep, '/')}` + + `\n * Original file: ${sourceFile.replaceAll(path.sep, '/')}` + + '\n * To regenerate, run:' + + '\n * js1 build metro-ts-defs (internal) OR' + + '\n * yarn run build-ts-defs (OSS) ' + + '\n */\n', + ) + // format -> noformat + .replace(`\n * ${'@'}format\n`, `\n * ${'@'}noformat\n`); + + // Sign the file + const finalOutput = SignedSource.signFile(withToken); + existingDefs.delete(absoluteTsFile); if (opts.verifyOnly) { diff --git a/scripts/jestFilter.js b/scripts/jestFilter.js index 39df016464..1f85f6bfc8 100644 --- a/scripts/jestFilter.js +++ b/scripts/jestFilter.js @@ -43,6 +43,9 @@ const BROKEN_ON_WINDOWS = [ 'packages/metro-resolver/src/__tests__/package-exports-test.js', 'packages/metro-resolver/src/__tests__/index-test.js', + // flow-api-translator line ending issues + 'scripts/__tests__/ts-defs-sync-test.js', + // unclear issue 'packages/metro/src/DeltaBundler/__tests__/DeltaCalculator-test.js', 'packages/metro-file-map/src/crawlers/__tests__/integration-test.js', diff --git a/website/package.json b/website/package.json index 358fc18db0..a60ae784a7 100644 --- a/website/package.json +++ b/website/package.json @@ -28,5 +28,8 @@ "@docusaurus/module-type-aliases": "3.6.3", "@docusaurus/types": "3.6.3", "prettier": "3.4.2" + }, + "resolutions": { + "minimatch": "3.1.4" } } diff --git a/website/src/pages/help/index.js b/website/src/pages/help/index.js index 2d95dcaff1..3270c46f5a 100644 --- a/website/src/pages/help/index.js +++ b/website/src/pages/help/index.js @@ -9,7 +9,7 @@ */ import Layout from '@theme/Layout'; -import React from 'react'; +import * as React from 'react'; const supportLinks = [ { diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 372557a6d7..e03381fe04 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -16,7 +16,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import Layout from '@theme/Layout'; import classnames from 'classnames'; -import React from 'react'; +import * as React from 'react'; import GitHubButton from 'react-github-btn'; const contents = [ diff --git a/website/yarn.lock b/website/yarn.lock index e9427e1bb7..62239397e8 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -2146,10 +2146,10 @@ dependencies: graceful-fs "4.2.10" -"@pnpm/npm-conf@^2.1.0": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz#bb375a571a0bd63ab0a23bece33033c683e9b6b0" - integrity sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw== +"@pnpm/npm-conf@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@pnpm/npm-conf/-/npm-conf-3.0.2.tgz#857622421aa9bbf254e557b8a022c216a7928f47" + integrity sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA== dependencies: "@pnpm/config.env-replace" "^1.1.0" "@pnpm/network.ca-file" "^1.0.1" @@ -7956,10 +7956,10 @@ minimalistic-assert@^1.0.0: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== +minimatch@3.1.2, minimatch@3.1.4, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: + version "3.1.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.4.tgz#89d910ea3970a77ac8edfd30340ccd038b758079" + integrity sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw== dependencies: brace-expansion "^1.1.7" @@ -9511,11 +9511,11 @@ regexpu-core@^6.2.0: unicode-match-property-value-ecmascript "^2.1.0" registry-auth-token@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-5.1.0.tgz#3c659047ecd4caebd25bc1570a3aa979ae490eca" - integrity sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw== + version "5.1.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-5.1.1.tgz#f1ff69c8e492e7edee07110b4752dd0a8aa82853" + integrity sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q== dependencies: - "@pnpm/npm-conf" "^2.1.0" + "@pnpm/npm-conf" "^3.0.2" registry-url@^6.0.0: version "6.0.1" diff --git a/yarn.lock b/yarn.lock index b54c8071de..9a1432f989 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2003,12 +2003,12 @@ babel-plugin-syntax-hermes-parser@0.25.1: dependencies: hermes-parser "0.25.1" -babel-plugin-syntax-hermes-parser@0.33.3: - version "0.33.3" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz#07602f8163cc7e63c4ec17d47467f2a3c5db70e9" - integrity sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA== +babel-plugin-syntax-hermes-parser@0.35.0: + version "0.35.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.35.0.tgz#9daa5340b6a5b329985f4c5cda2c6b1a355daed5" + integrity sha512-9Hbqe8S8JWQ4EiHBFFkqLiFPXJL4bHhYooT536r78jVPfSUtgLtY9lCelY4QJzJORSy/9L3zXDhyN+QsPBMsTw== dependencies: - hermes-parser "0.33.3" + hermes-parser "0.35.0" babel-plugin-tester@^6.0.1: version "6.5.0" @@ -2055,24 +2055,29 @@ babel-preset-jest@^29.6.3: babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== +brace-expansion@^5.0.2: + version "5.0.4" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.4.tgz#614daaecd0a688f660bbbc909a8748c3d80d4336" + integrity sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg== dependencies: - balanced-match "^1.0.0" + balanced-match "^4.0.2" braces@^3.0.3: version "3.0.3" @@ -2250,7 +2255,7 @@ compare-versions@^3.4.0: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== connect@^3.6.5: version "3.7.0" @@ -3046,25 +3051,25 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== -flow-api-translator@0.33.3: - version "0.33.3" - resolved "https://registry.yarnpkg.com/flow-api-translator/-/flow-api-translator-0.33.3.tgz#0bf7a1258ab7feefab9b69552f91da2d8b7905cc" - integrity sha512-9JKiKbJ5H1Q7CMt6YFvlRolxXBdAw+VQzrfecYJ1PoQJzy0V67brXMFMVwdyCGZSEaXDf9NBisCtkBURLc4TBg== +flow-api-translator@0.35.0: + version "0.35.0" + resolved "https://registry.yarnpkg.com/flow-api-translator/-/flow-api-translator-0.35.0.tgz#96f5aba20d4514bb2de0b681e875ae6d50781c48" + integrity sha512-YYLZLC/6PJMsBmTmBkXLCmMgVPBjJTHi5z8XB1HAIZShxYcIwK5lv3idwsE2im3VGRjXWBwI3J3lfrvSOt553A== dependencies: "@babel/code-frame" "^7.16.0" "@typescript-eslint/parser" "8.38.0" "@typescript-eslint/visitor-keys" "8.38.0" flow-enums-runtime "^0.0.6" - hermes-eslint "0.33.3" - hermes-estree "0.33.3" - hermes-parser "0.33.3" - hermes-transform "0.33.3" + hermes-eslint "0.35.0" + hermes-estree "0.35.0" + hermes-parser "0.35.0" + hermes-transform "0.35.0" typescript "5.3.2" -flow-bin@^0.302.0: - version "0.302.0" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.302.0.tgz#0758b788cb3427237917bacda62bd5af2bd5f155" - integrity sha512-TPIcb9JBKAbwfjoGhA+yJrkPdG+h7JyVuT+uUQt8AiSmpdUJcBBGUotHgkTTRXXjWUrLaJ10gKKawdk2sQgkcw== +flow-bin@^0.309.0: + version "0.309.0" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.309.0.tgz#c983003377c80a558e1c4afbf47b6c38cfbca075" + integrity sha512-/RH68gcCY8OHzcdSVTUCw+fhDSEYmNHoovfK0EcbB4rs1Xbc5HhxhHTvr7U+h55De4bDRlE52ghH23MRP625cQ== flow-enums-runtime@^0.0.6: version "0.0.6" @@ -3294,24 +3299,24 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" -hermes-eslint@0.33.3: - version "0.33.3" - resolved "https://registry.yarnpkg.com/hermes-eslint/-/hermes-eslint-0.33.3.tgz#d9d6b4e9bcf9184f99ad86e087343eb05ab8a0c0" - integrity sha512-eGY0l6T5U9LDdC+uN88NrSOrvPPtXGPxN7EaD38hytWuBEVXypq0eQ1SNVsnQPBZLWi+b1jkF4F5aVtTCQC6wg== +hermes-eslint@0.35.0: + version "0.35.0" + resolved "https://registry.yarnpkg.com/hermes-eslint/-/hermes-eslint-0.35.0.tgz#b9b0d414d6a00a409319d07fe66d7b8617746357" + integrity sha512-JzoEjvCCBxjicUSwzqJK0JBQU6+31qKIh2xvBQXLKmFtX9YUj0wqNVEHrIU0sAds1deUL5kA62REDSmcuSVNxA== dependencies: esrecurse "^4.3.0" - hermes-estree "0.33.3" - hermes-parser "0.33.3" + hermes-estree "0.35.0" + hermes-parser "0.35.0" hermes-estree@0.25.1: version "0.25.1" resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== -hermes-estree@0.33.3: - version "0.33.3" - resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.33.3.tgz#6d6b593d4b471119772c82bdb0212dfadabb6f17" - integrity sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg== +hermes-estree@0.35.0: + version "0.35.0" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.35.0.tgz#767cce0b14a68b4bc06cd5db7efe889f6188c565" + integrity sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg== hermes-parser@0.25.1: version "0.25.1" @@ -3320,24 +3325,24 @@ hermes-parser@0.25.1: dependencies: hermes-estree "0.25.1" -hermes-parser@0.33.3: - version "0.33.3" - resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.33.3.tgz#da50ababb7a5ab636d339e7b2f6e3848e217e09d" - integrity sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA== +hermes-parser@0.35.0: + version "0.35.0" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.35.0.tgz#7625ec2f34ab897c2a17a7bea9788d136d5fd8c9" + integrity sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA== dependencies: - hermes-estree "0.33.3" + hermes-estree "0.35.0" -hermes-transform@0.33.3: - version "0.33.3" - resolved "https://registry.yarnpkg.com/hermes-transform/-/hermes-transform-0.33.3.tgz#a92e3f4f74a8a0f228dd37c7b5b94d8fd75cec04" - integrity sha512-OI9uMoCjzM6RHxyBKsVRtoJ2B/BqpyJXWqByW48qXkMNDTYADly2vjAz/74Z0MdhrB/xPs6s4o8yo+vpguTe6w== +hermes-transform@0.35.0: + version "0.35.0" + resolved "https://registry.yarnpkg.com/hermes-transform/-/hermes-transform-0.35.0.tgz#81c83482c93f28b8f36abf8f3a33a426c2fab12d" + integrity sha512-JTsO7k868agX0IDphpNk51AX4HI+jYQdmVw4DIXzqYtNDTr8e2D7gItzdCh8gzEcdGfTtkIpgIFYJlUBEPJOig== dependencies: "@babel/code-frame" "^7.16.0" esquery "^1.4.0" flow-enums-runtime "^0.0.6" - hermes-eslint "0.33.3" - hermes-estree "0.33.3" - hermes-parser "0.33.3" + hermes-eslint "0.35.0" + hermes-estree "0.35.0" + hermes-parser "0.35.0" string-width "4.2.3" html-escaper@^2.0.0: @@ -4466,18 +4471,18 @@ mimic-fn@^2.1.0: integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + version "3.1.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== dependencies: brace-expansion "^1.1.7" minimatch@^9.0.4: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + version "9.0.7" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.7.tgz#d76c4d0b3b527877016d6cc1b9922fc8e0ffe7b0" + integrity sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg== dependencies: - brace-expansion "^2.0.1" + brace-expansion "^5.0.2" minimist@^1.2.6: version "1.2.7" @@ -5242,6 +5247,11 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +signedsource@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-2.0.0.tgz#f72dc0f98f5bca2763b464a555511a84a4da8eee" + integrity sha512-MscTxXbMij5JVgrW1xDiMIc+vFa0+H0+HP+rRrFjwa7ef2VAxIP/4L/E75I5H4xvyb4l1X+a9ch+6Zy5uFu7Fg== + sisteransi@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"