FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

90
node_modules/metro/src/node-haste/DependencyGraph.d.ts generated vendored Normal file
View File

@@ -0,0 +1,90 @@
/**
* 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<<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 {
BundlerResolution,
TransformResultDependency,
} from '../DeltaBundler/types';
import type {ResolverInputOptions} from '../shared/types';
import type {ConfigT} from 'metro-config';
import type {
ChangeEvent,
FileSystem,
HasteMap,
HealthCheckResult,
WatcherStatus,
default as MetroFileMap,
} from 'metro-file-map';
import {ModuleResolver} from './DependencyGraph/ModuleResolution';
import EventEmitter from 'events';
declare class DependencyGraph extends EventEmitter {
_config: ConfigT;
_haste: MetroFileMap;
_fileSystem: FileSystem;
_hasteMap: HasteMap;
_moduleResolver: ModuleResolver;
_resolutionCache: Map<
string | symbol,
Map<
string | symbol,
Map<string | symbol, Map<string | symbol, BundlerResolution>>
>
>;
_initializedPromise: Promise<void>;
constructor(
config: ConfigT,
options?: {
readonly hasReducedPerformance?: boolean;
readonly watch?: boolean;
},
);
_onWatcherHealthCheck(result: HealthCheckResult): void;
_onWatcherStatus(status: WatcherStatus): void;
ready(): Promise<void>;
_onHasteChange($$PARAM_0$$: ChangeEvent): void;
_createModuleResolver(): void;
_getClosestPackage(
absoluteModulePath: string,
): null | undefined | {packageJsonPath: string; packageRelativePath: string};
getAllFiles(): Array<string>;
/**
* Used when watcher.unstable_lazySha1 is true
*/
getOrComputeSha1(
mixedPath: string,
): Promise<{content?: Buffer; sha1: string}>;
getWatcher(): EventEmitter;
end(): void;
/** Given a search context, return a list of file paths matching the query. */
matchFilesWithContext(
from: string,
context: Readonly<{recursive: boolean; filter: RegExp}>,
): Iterable<string>;
resolveDependency(
originModulePath: string,
dependency: TransformResultDependency,
platform: string | null,
resolverOptions: ResolverInputOptions,
$$PARAM_4$$?: {assumeFlatNodeModules: boolean},
): BundlerResolution;
doesFileExist: (filePath: string) => boolean;
getHasteName(filePath: string): string;
getDependencies(filePath: string): Array<string>;
}
export default DependencyGraph;

313
node_modules/metro/src/node-haste/DependencyGraph.js generated vendored Normal file
View File

@@ -0,0 +1,313 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _createFileMap = _interopRequireDefault(
require("./DependencyGraph/createFileMap"),
);
var _ModuleResolution = require("./DependencyGraph/ModuleResolution");
var _PackageCache = require("./PackageCache");
var _events = _interopRequireDefault(require("events"));
var _fs = _interopRequireDefault(require("fs"));
var _metroCore = require("metro-core");
var _canonicalize = _interopRequireDefault(
require("metro-core/private/canonicalize"),
);
var _metroFileMap = require("metro-file-map");
var _metroResolver = require("metro-resolver");
var _nullthrows = _interopRequireDefault(require("nullthrows"));
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const { createActionStartEntry, createActionEndEntry, log } = _metroCore.Logger;
const NULL_PLATFORM = Symbol();
function getOrCreateMap(map, field) {
let subMap = map.get(field);
if (!subMap) {
subMap = new Map();
map.set(field, subMap);
}
return subMap;
}
class DependencyGraph extends _events.default {
#packageCache;
#dependencyPlugin;
constructor(config, options) {
super();
this._config = config;
const { hasReducedPerformance, watch } = options ?? {};
const initializingMetroLogEntry = log(
createActionStartEntry("Initializing Metro"),
);
config.reporter.update({
type: "dep_graph_loading",
hasReducedPerformance: !!hasReducedPerformance,
});
const { fileMap, hasteMap, dependencyPlugin } = (0, _createFileMap.default)(
config,
{
throwOnModuleCollision: false,
watch,
},
);
fileMap.setMaxListeners(1000);
this._haste = fileMap;
this._haste.on("status", (status) => this._onWatcherStatus(status));
this._initializedPromise = fileMap.build().then(({ fileSystem }) => {
log(createActionEndEntry(initializingMetroLogEntry));
config.reporter.update({
type: "dep_graph_loaded",
});
this._fileSystem = fileSystem;
this._hasteMap = hasteMap;
this.#dependencyPlugin = dependencyPlugin;
this._haste.on("change", (changeEvent) =>
this._onHasteChange(changeEvent),
);
this._haste.on("healthCheck", (result) =>
this._onWatcherHealthCheck(result),
);
this._resolutionCache = new Map();
this.#packageCache = new _PackageCache.PackageCache({
getClosestPackage: (absoluteModulePath) =>
this._getClosestPackage(absoluteModulePath),
});
this._createModuleResolver();
});
}
_onWatcherHealthCheck(result) {
this._config.reporter.update({
type: "watcher_health_check_result",
result,
});
}
_onWatcherStatus(status) {
this._config.reporter.update({
type: "watcher_status",
status,
});
}
async ready() {
await this._initializedPromise;
}
_onHasteChange({ changes, rootDir }) {
this._resolutionCache = new Map();
[
...changes.addedFiles,
...changes.modifiedFiles,
...changes.removedFiles,
].forEach(([canonicalPath]) =>
this.#packageCache.invalidate(_path.default.join(rootDir, canonicalPath)),
);
this._createModuleResolver();
this.emit("change");
}
_createModuleResolver() {
const fileSystemLookup = (path) => {
const result = this._fileSystem.lookup(path);
if (result.exists) {
return {
exists: true,
realPath: result.realPath,
type: result.type,
};
}
return {
exists: false,
};
};
this._moduleResolver = new _ModuleResolution.ModuleResolver({
assetExts: new Set(this._config.resolver.assetExts),
dirExists: (filePath) => {
try {
return _fs.default.lstatSync(filePath).isDirectory();
} catch (e) {}
return false;
},
disableHierarchicalLookup:
this._config.resolver.disableHierarchicalLookup,
doesFileExist: this.doesFileExist,
emptyModulePath: this._config.resolver.emptyModulePath,
extraNodeModules: this._config.resolver.extraNodeModules,
fileSystemLookup,
getHasteModulePath: (name, platform) =>
this._hasteMap.getModule(name, platform, true),
getHastePackagePath: (name, platform) =>
this._hasteMap.getPackage(name, platform, true),
getPackage: (packageJsonPath) => {
try {
return (
this.#packageCache.getPackage(packageJsonPath).packageJson ?? null
);
} catch {
return null;
}
},
getPackageForModule: (absolutePath) =>
this.#packageCache.getPackageForModule(absolutePath),
mainFields: this._config.resolver.resolverMainFields,
nodeModulesPaths: this._config.resolver.nodeModulesPaths,
preferNativePlatform: true,
projectRoot: this._config.projectRoot,
reporter: this._config.reporter,
resolveAsset: (dirPath, assetName, extension) => {
const basePath = dirPath + _path.default.sep + assetName;
const assets = [
basePath + extension,
...this._config.resolver.assetResolutions.map(
(resolution) => basePath + "@" + resolution + "x" + extension,
),
]
.map((assetPath) => fileSystemLookup(assetPath).realPath)
.filter(Boolean);
return assets.length ? assets : null;
},
resolveRequest: this._config.resolver.resolveRequest,
sourceExts: this._config.resolver.sourceExts,
unstable_conditionNames: this._config.resolver.unstable_conditionNames,
unstable_conditionsByPlatform:
this._config.resolver.unstable_conditionsByPlatform,
unstable_enablePackageExports:
this._config.resolver.unstable_enablePackageExports,
unstable_incrementalResolution:
this._config.resolver.unstable_incrementalResolution,
});
}
_getClosestPackage(absoluteModulePath) {
const result = this._fileSystem.hierarchicalLookup(
absoluteModulePath,
"package.json",
{
breakOnSegment: "node_modules",
invalidatedBy: null,
subpathType: "f",
},
);
return result
? {
packageJsonPath: result.absolutePath,
packageRelativePath: result.containerRelativePath,
}
: null;
}
getAllFiles() {
return (0, _nullthrows.default)(this._fileSystem).getAllFiles();
}
async getOrComputeSha1(mixedPath) {
const result = await this._fileSystem.getOrComputeSha1(mixedPath);
if (!result || !result.sha1) {
throw new Error(`Failed to get the SHA-1 for: ${mixedPath}.
Potential causes:
1) The file is not watched. Ensure it is under the configured \`projectRoot\` or \`watchFolders\`.
2) Check \`blockList\` in your metro.config.js and make sure it isn't excluding the file path.
3) The file may have been deleted since it was resolved - try refreshing your app.
4) Otherwise, this is a bug in Metro or the configured resolver - please report it.`);
}
return result;
}
getWatcher() {
return this._haste;
}
async end() {
await this.ready();
await this._haste.end();
}
matchFilesWithContext(from, context) {
return this._fileSystem.matchFiles({
rootDir: from,
recursive: context.recursive,
filter: context.filter,
filterComparePosix: true,
follow: true,
});
}
resolveDependency(
originModulePath,
dependency,
platform,
resolverOptions,
{ assumeFlatNodeModules } = {
assumeFlatNodeModules: false,
},
) {
const to = dependency.name;
const isSensitiveToOriginFolder =
!assumeFlatNodeModules ||
to.includes("/") ||
to === "." ||
to === ".." ||
originModulePath.includes(
_path.default.sep + "node_modules" + _path.default.sep,
);
const resolverOptionsKey =
JSON.stringify(resolverOptions ?? {}, _canonicalize.default) ?? "";
const originKey = isSensitiveToOriginFolder
? _path.default.dirname(originModulePath)
: "";
const targetKey =
to + (dependency.data.isESMImport === true ? "\0esm" : "\0cjs");
const platformKey = platform ?? NULL_PLATFORM;
const mapByResolverOptions = this._resolutionCache;
const mapByOrigin = getOrCreateMap(
mapByResolverOptions,
resolverOptionsKey,
);
const mapByTarget = getOrCreateMap(mapByOrigin, originKey);
const mapByPlatform = getOrCreateMap(mapByTarget, targetKey);
let resolution = mapByPlatform.get(platformKey);
if (!resolution) {
try {
resolution = this._moduleResolver.resolveDependency(
originModulePath,
dependency,
true,
platform,
resolverOptions,
);
} catch (error) {
if (error instanceof _metroFileMap.DuplicateHasteCandidatesError) {
throw new _metroCore.AmbiguousModuleResolutionError(
originModulePath,
error,
);
}
if (error instanceof _metroResolver.InvalidPackageError) {
throw new _metroCore.PackageResolutionError({
packageError: error,
originModulePath,
targetModuleName: to,
});
}
throw error;
}
}
mapByPlatform.set(platformKey, resolution);
return resolution;
}
doesFileExist = (filePath) => {
return this._fileSystem.exists(filePath);
};
getHasteName(filePath) {
const hasteName = this._hasteMap.getModuleNameByPath(filePath);
if (hasteName) {
return hasteName;
}
return _path.default.relative(this._config.projectRoot, filePath);
}
getDependencies(filePath) {
if (!this.#dependencyPlugin) {
throw new Error(
"getDependencies called but extractDependencies is false",
);
}
return Array.from(
(0, _nullthrows.default)(
this.#dependencyPlugin.getDependencies(filePath),
),
);
}
}
exports.default = DependencyGraph;

View File

@@ -0,0 +1,405 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/
import type {
BundlerResolution,
TransformResultDependency,
} from '../DeltaBundler/types';
import type {ResolverInputOptions} from '../shared/types';
import type {ConfigT} from 'metro-config';
import type {
ChangeEvent,
DependencyPlugin,
FileSystem,
HasteMap,
HealthCheckResult,
WatcherStatus,
default as MetroFileMap,
} from 'metro-file-map';
import type {FileSystemLookup} from 'metro-resolver';
import createFileMap from './DependencyGraph/createFileMap';
import {ModuleResolver} from './DependencyGraph/ModuleResolution';
import {PackageCache} from './PackageCache';
import EventEmitter from 'events';
import fs from 'fs';
import {
AmbiguousModuleResolutionError,
Logger,
PackageResolutionError,
} from 'metro-core';
import canonicalize from 'metro-core/private/canonicalize';
import {DuplicateHasteCandidatesError} from 'metro-file-map';
import {InvalidPackageError} from 'metro-resolver';
import nullthrows from 'nullthrows';
import path from 'path';
const {createActionStartEntry, createActionEndEntry, log} = Logger;
const NULL_PLATFORM = Symbol();
function getOrCreateMap<T>(
map: Map<string | symbol, Map<string | symbol, T>>,
field: string,
): Map<string | symbol, T> {
let subMap = map.get(field);
if (!subMap) {
subMap = new Map();
map.set(field, subMap);
}
return subMap;
}
export default class DependencyGraph extends EventEmitter {
_config: ConfigT;
_haste: MetroFileMap;
_fileSystem: FileSystem;
#packageCache: PackageCache;
_hasteMap: HasteMap;
#dependencyPlugin: ?DependencyPlugin;
_moduleResolver: ModuleResolver;
_resolutionCache: Map<
// Custom resolver options
string | symbol,
Map<
// Origin folder
string | symbol,
Map<
// Dependency name
string | symbol,
Map<
// Platform
string | symbol,
BundlerResolution,
>,
>,
>,
>;
_initializedPromise: Promise<void>;
constructor(
config: ConfigT,
options?: {
+hasReducedPerformance?: boolean,
+watch?: boolean,
},
) {
super();
this._config = config;
const {hasReducedPerformance, watch} = options ?? {};
const initializingMetroLogEntry = log(
createActionStartEntry('Initializing Metro'),
);
config.reporter.update({
type: 'dep_graph_loading',
hasReducedPerformance: !!hasReducedPerformance,
});
const {fileMap, hasteMap, dependencyPlugin} = createFileMap(config, {
throwOnModuleCollision: false,
watch,
});
// We can have a lot of graphs listening to Haste for changes.
// Bump this up to silence the max listeners EventEmitter warning.
fileMap.setMaxListeners(1000);
this._haste = fileMap;
this._haste.on('status', status => this._onWatcherStatus(status));
this._initializedPromise = fileMap.build().then(({fileSystem}) => {
log(createActionEndEntry(initializingMetroLogEntry));
config.reporter.update({type: 'dep_graph_loaded'});
this._fileSystem = fileSystem;
this._hasteMap = hasteMap;
this.#dependencyPlugin = dependencyPlugin;
this._haste.on('change', changeEvent => this._onHasteChange(changeEvent));
this._haste.on('healthCheck', result =>
this._onWatcherHealthCheck(result),
);
this._resolutionCache = new Map();
this.#packageCache = new PackageCache({
getClosestPackage: absoluteModulePath =>
this._getClosestPackage(absoluteModulePath),
});
this._createModuleResolver();
});
}
_onWatcherHealthCheck(result: HealthCheckResult) {
this._config.reporter.update({type: 'watcher_health_check_result', result});
}
_onWatcherStatus(status: WatcherStatus) {
this._config.reporter.update({type: 'watcher_status', status});
}
// Waits for the dependency graph to become ready after initialisation.
// Don't read anything from the graph until this resolves.
async ready(): Promise<void> {
await this._initializedPromise;
}
_onHasteChange({changes, rootDir}: ChangeEvent) {
this._resolutionCache = new Map();
[
...changes.addedFiles,
...changes.modifiedFiles,
...changes.removedFiles,
].forEach(([canonicalPath]) =>
this.#packageCache.invalidate(path.join(rootDir, canonicalPath)),
);
this._createModuleResolver();
this.emit('change');
}
_createModuleResolver() {
const fileSystemLookup = (path: string): ReturnType<FileSystemLookup> => {
const result = this._fileSystem.lookup(path);
if (result.exists) {
return {
exists: true,
realPath: result.realPath,
type: result.type,
};
}
return {exists: false};
};
this._moduleResolver = new ModuleResolver({
assetExts: new Set(this._config.resolver.assetExts),
dirExists: (filePath: string) => {
try {
return fs.lstatSync(filePath).isDirectory();
} catch (e) {}
return false;
},
disableHierarchicalLookup:
this._config.resolver.disableHierarchicalLookup,
doesFileExist: this.doesFileExist,
emptyModulePath: this._config.resolver.emptyModulePath,
extraNodeModules: this._config.resolver.extraNodeModules,
fileSystemLookup,
getHasteModulePath: (name, platform) =>
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,
preferNativePlatform: true,
projectRoot: this._config.projectRoot,
reporter: this._config.reporter,
resolveAsset: (dirPath: string, assetName: string, extension: string) => {
const basePath = dirPath + path.sep + assetName;
const assets = [
basePath + extension,
...this._config.resolver.assetResolutions.map(
resolution => basePath + '@' + resolution + 'x' + extension,
),
]
.map(assetPath => fileSystemLookup(assetPath).realPath)
.filter(Boolean);
return assets.length ? assets : null;
},
resolveRequest: this._config.resolver.resolveRequest,
sourceExts: this._config.resolver.sourceExts,
unstable_conditionNames: this._config.resolver.unstable_conditionNames,
unstable_conditionsByPlatform:
this._config.resolver.unstable_conditionsByPlatform,
unstable_enablePackageExports:
this._config.resolver.unstable_enablePackageExports,
unstable_incrementalResolution:
this._config.resolver.unstable_incrementalResolution,
});
}
_getClosestPackage(
absoluteModulePath: string,
): ?{packageJsonPath: string, packageRelativePath: string} {
const result = this._fileSystem.hierarchicalLookup(
absoluteModulePath,
'package.json',
{
breakOnSegment: 'node_modules',
invalidatedBy: null,
subpathType: 'f',
},
);
return result
? {
packageJsonPath: result.absolutePath,
packageRelativePath: result.containerRelativePath,
}
: null;
}
getAllFiles(): Array<string> {
return nullthrows(this._fileSystem).getAllFiles();
}
/**
* Used when watcher.unstable_lazySha1 is true
*/
async getOrComputeSha1(
mixedPath: string,
): Promise<{content?: Buffer, sha1: string}> {
const result = await this._fileSystem.getOrComputeSha1(mixedPath);
if (!result || !result.sha1) {
throw new Error(`Failed to get the SHA-1 for: ${mixedPath}.
Potential causes:
1) The file is not watched. Ensure it is under the configured \`projectRoot\` or \`watchFolders\`.
2) Check \`blockList\` in your metro.config.js and make sure it isn't excluding the file path.
3) The file may have been deleted since it was resolved - try refreshing your app.
4) Otherwise, this is a bug in Metro or the configured resolver - please report it.`);
}
return result;
}
getWatcher(): EventEmitter {
return this._haste;
}
async end() {
await this.ready();
await this._haste.end();
}
/** Given a search context, return a list of file paths matching the query. */
matchFilesWithContext(
from: string,
context: Readonly<{
/* Should search for files recursively. */
recursive: boolean,
/* Filter relative paths against a pattern. */
filter: RegExp,
}>,
): Iterable<string> {
return this._fileSystem.matchFiles({
rootDir: from,
recursive: context.recursive,
filter: context.filter,
filterComparePosix: true,
follow: true,
});
}
resolveDependency(
originModulePath: string,
dependency: TransformResultDependency,
platform: string | null,
resolverOptions: ResolverInputOptions,
// TODO: Fold assumeFlatNodeModules into resolverOptions and add to graphId
{assumeFlatNodeModules}: {assumeFlatNodeModules: boolean} = {
assumeFlatNodeModules: false,
},
): BundlerResolution {
const to = dependency.name;
const isSensitiveToOriginFolder =
// Resolution is always relative to the origin folder unless we assume a flat node_modules
!assumeFlatNodeModules ||
// Path requests are resolved relative to the origin folder
to.includes('/') ||
to === '.' ||
to === '..' ||
// Preserve standard assumptions under node_modules
originModulePath.includes(path.sep + 'node_modules' + path.sep);
// Compound key for the resolver cache
const resolverOptionsKey =
JSON.stringify(resolverOptions ?? {}, canonicalize) ?? '';
const originKey = isSensitiveToOriginFolder
? path.dirname(originModulePath)
: '';
const targetKey =
to + (dependency.data.isESMImport === true ? '\0esm' : '\0cjs');
const platformKey = platform ?? NULL_PLATFORM;
// Traverse the resolver cache, which is a tree of maps
const mapByResolverOptions = this._resolutionCache;
const mapByOrigin = getOrCreateMap(
mapByResolverOptions,
resolverOptionsKey,
);
const mapByTarget = getOrCreateMap(mapByOrigin, originKey);
const mapByPlatform = getOrCreateMap(mapByTarget, targetKey);
let resolution: ?BundlerResolution = mapByPlatform.get(platformKey);
if (!resolution) {
try {
resolution = this._moduleResolver.resolveDependency(
originModulePath,
dependency,
true,
platform,
resolverOptions,
);
} catch (error) {
if (error instanceof DuplicateHasteCandidatesError) {
throw new AmbiguousModuleResolutionError(originModulePath, error);
}
if (error instanceof InvalidPackageError) {
throw new PackageResolutionError({
packageError: error,
originModulePath,
targetModuleName: to,
});
}
throw error;
}
}
mapByPlatform.set(platformKey, resolution);
return resolution;
}
doesFileExist = (filePath: string): boolean => {
return this._fileSystem.exists(filePath);
};
getHasteName(filePath: string): string {
const hasteName = this._hasteMap.getModuleNameByPath(filePath);
if (hasteName) {
return hasteName;
}
return path.relative(this._config.projectRoot, filePath);
}
getDependencies(filePath: string): Array<string> {
if (!this.#dependencyPlugin) {
throw new Error(
'getDependencies called but extractDependencies is false',
);
}
return Array.from(
nullthrows(this.#dependencyPlugin.getDependencies(filePath)),
);
}
}

View File

@@ -0,0 +1,120 @@
/**
* 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<<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 {
BundlerResolution,
TransformResultDependency,
} from '../../DeltaBundler/types';
import type {Reporter} from '../../lib/reporting';
import type {ResolverInputOptions} from '../../shared/types';
import type {
CustomResolver,
DoesFileExist,
FileCandidates,
FileSystemLookup,
Resolution,
ResolveAsset,
} from 'metro-resolver';
import type {PackageForModule, PackageJson} from 'metro-resolver/private/types';
export type DirExistsFn = (filePath: string) => boolean;
type Options = Readonly<{
assetExts: ReadonlySet<string>;
dirExists: DirExistsFn;
disableHierarchicalLookup: boolean;
doesFileExist: DoesFileExist;
emptyModulePath: string;
extraNodeModules: null | undefined | object;
fileSystemLookup: FileSystemLookup;
getHasteModulePath: (
name: string,
platform: null | undefined | string,
) => null | undefined | string;
getHastePackagePath: (
name: string,
platform: null | undefined | string,
) => null | undefined | string;
mainFields: ReadonlyArray<string>;
getPackage: (packageJsonPath: string) => null | undefined | PackageJson;
getPackageForModule: (
absolutePath: string,
) => null | undefined | PackageForModule;
nodeModulesPaths: ReadonlyArray<string>;
preferNativePlatform: boolean;
projectRoot: string;
reporter: Reporter;
resolveAsset: ResolveAsset;
resolveRequest: null | undefined | CustomResolver;
sourceExts: ReadonlyArray<string>;
unstable_conditionNames: ReadonlyArray<string>;
unstable_conditionsByPlatform: Readonly<{
[platform: string]: ReadonlyArray<string>;
}>;
unstable_enablePackageExports: boolean;
unstable_incrementalResolution: boolean;
}>;
export declare class ModuleResolver {
_options: Options;
_projectRootFakeModulePath: string;
_cachedEmptyModule: null | undefined | BundlerResolution;
constructor(options: Options);
_getEmptyModule(): BundlerResolution;
resolveDependency(
originModulePath: string,
dependency: TransformResultDependency,
allowHaste: boolean,
platform: string | null,
resolverOptions: ResolverInputOptions,
): BundlerResolution;
/**
* TODO: Return Resolution instead of coercing to BundlerResolution here
*/
_getFileResolvedModule(resolution: Resolution): BundlerResolution;
_logWarning: (message: string) => void;
_removeRoot(candidates: FileCandidates): FileCandidates;
}
export declare class UnableToResolveError extends Error {
/**
* File path of the module that tried to require a module, ex. `/js/foo.js`.
*/
originModulePath: string;
/**
* The name of the module that was required, no necessarily a path,
* ex. `./bar`, or `invariant`.
*/
targetModuleName: string;
/**
* Original error that causes this error
*/
cause: null | undefined | Error;
/**
* Fixed type field in common with other Metro build errors.
*/
readonly type: 'UnableToResolveError';
constructor(
originModulePath: string,
targetModuleName: string,
message: string,
options?: Readonly<{
dependency?: null | undefined | TransformResultDependency;
cause?: Error;
}>,
);
buildCodeFrameMessage(
dependency: null | undefined | TransformResultDependency,
): null | undefined | string;
}

View File

@@ -0,0 +1,351 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.UnableToResolveError = exports.ModuleResolver = void 0;
var _codeFrame = require("@babel/code-frame");
var _fs = _interopRequireDefault(require("fs"));
var _invariant = _interopRequireDefault(require("invariant"));
var Resolver = _interopRequireWildcard(require("metro-resolver"));
var _createDefaultContext = _interopRequireDefault(
require("metro-resolver/private/createDefaultContext"),
);
var _path = _interopRequireDefault(require("path"));
var _util = _interopRequireDefault(require("util"));
function _interopRequireWildcard(e, t) {
if ("function" == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ("object" != typeof e && "function" != typeof e))
return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
"default" !== t &&
{}.hasOwnProperty.call(e, t) &&
((i =
(o = Object.defineProperty) &&
Object.getOwnPropertyDescriptor(e, t)) &&
(i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
class ModuleResolver {
constructor(options) {
this._options = options;
const { projectRoot } = this._options;
this._projectRootFakeModulePath = _path.default.join(projectRoot, "_");
}
_getEmptyModule() {
let emptyModule = this._cachedEmptyModule;
if (!emptyModule) {
emptyModule = this.resolveDependency(
this._projectRootFakeModulePath,
{
data: {
asyncType: null,
isESMImport: false,
key: this._options.emptyModulePath,
locs: [],
},
name: this._options.emptyModulePath,
},
false,
null,
{
dev: false,
},
);
this._cachedEmptyModule = emptyModule;
}
return emptyModule;
}
resolveDependency(
originModulePath,
dependency,
allowHaste,
platform,
resolverOptions,
) {
const {
assetExts,
disableHierarchicalLookup,
doesFileExist,
extraNodeModules,
fileSystemLookup,
getPackage,
getPackageForModule,
mainFields,
nodeModulesPaths,
preferNativePlatform,
resolveAsset,
resolveRequest,
sourceExts,
unstable_conditionNames,
unstable_conditionsByPlatform,
unstable_enablePackageExports,
unstable_incrementalResolution,
} = this._options;
try {
const result = Resolver.resolve(
(0, _createDefaultContext.default)(
{
allowHaste,
assetExts,
customResolverOptions: resolverOptions.customResolverOptions ?? {},
dev: resolverOptions.dev,
disableHierarchicalLookup,
doesFileExist,
extraNodeModules,
fileSystemLookup,
getPackage,
getPackageForModule,
isESMImport: dependency.data.isESMImport,
mainFields,
nodeModulesPaths,
originModulePath,
preferNativePlatform,
resolveAsset,
resolveHasteModule: (name) =>
this._options.getHasteModulePath(name, platform),
resolveHastePackage: (name) =>
this._options.getHastePackagePath(name, platform),
resolveRequest,
sourceExts,
unstable_conditionNames,
unstable_conditionsByPlatform,
unstable_enablePackageExports,
unstable_incrementalResolution,
unstable_logWarning: this._logWarning,
},
dependency,
),
dependency.name,
platform,
);
return this._getFileResolvedModule(result);
} catch (error) {
if (error instanceof Resolver.FailedToResolvePathError) {
const { candidates } = error;
throw new UnableToResolveError(
originModulePath,
dependency.name,
"\n\nNone of these files exist:\n" +
[candidates.file, candidates.dir]
.filter(Boolean)
.map(
(candidates) =>
` * ${Resolver.formatFileCandidates(this._removeRoot(candidates))}`,
)
.join("\n"),
{
cause: error,
dependency,
},
);
} else if (error instanceof Resolver.FailedToResolveUnsupportedError) {
throw new UnableToResolveError(
originModulePath,
dependency.name,
error.message,
{
cause: error,
dependency,
},
);
} else if (error instanceof Resolver.FailedToResolveNameError) {
const dirPaths = error.dirPaths;
const extraPaths = error.extraPaths;
const displayDirPaths = dirPaths
.filter((dirPath) => this._options.dirExists(dirPath))
.map((dirPath) =>
_path.default.relative(this._options.projectRoot, dirPath),
)
.concat(extraPaths);
const hint = displayDirPaths.length ? " or in these directories:" : "";
throw new UnableToResolveError(
originModulePath,
dependency.name,
[
`${dependency.name} could not be found within the project${hint || "."}`,
...displayDirPaths.map((dirPath) => ` ${dirPath}`),
].join("\n"),
{
cause: error,
dependency,
},
);
}
throw error;
}
}
_getFileResolvedModule(resolution) {
switch (resolution.type) {
case "sourceFile":
return resolution;
case "assetFiles":
const arbitrary = getArrayLowestItem(resolution.filePaths);
(0, _invariant.default)(arbitrary != null, "invalid asset resolution");
return {
filePath: arbitrary,
type: "sourceFile",
};
case "empty":
return this._getEmptyModule();
default:
resolution.type;
throw new Error("invalid type");
}
}
_logWarning = (message) => {
this._options.reporter.update({
message,
type: "resolver_warning",
});
};
_removeRoot(candidates) {
if (candidates.filePathPrefix) {
candidates.filePathPrefix = _path.default.relative(
this._options.projectRoot,
candidates.filePathPrefix,
);
}
return candidates;
}
}
exports.ModuleResolver = ModuleResolver;
function getArrayLowestItem(a) {
if (a.length === 0) {
return undefined;
}
let lowest = a[0];
for (let i = 1; i < a.length; ++i) {
if (a[i] < lowest) {
lowest = a[i];
}
}
return lowest;
}
class UnableToResolveError extends Error {
type = "UnableToResolveError";
constructor(originModulePath, targetModuleName, message, options) {
super();
this.originModulePath = originModulePath;
this.targetModuleName = targetModuleName;
const codeFrameMessage = this.buildCodeFrameMessage(options?.dependency);
this.message =
_util.default.format(
"Unable to resolve module %s from %s: %s",
targetModuleName,
originModulePath,
message,
) + (codeFrameMessage ? "\n" + codeFrameMessage : "");
this.cause = options?.cause;
}
buildCodeFrameMessage(dependency) {
let file;
try {
file = _fs.default.readFileSync(this.originModulePath, "utf8");
} catch (error) {
if (error.code === "ENOENT" || error.code === "EISDIR") {
return null;
}
throw error;
}
const location = dependency?.data.locs.length
? refineDependencyLocation(
dependency.data.locs[0],
file,
this.targetModuleName,
)
: guessDependencyLocation(file, this.targetModuleName);
return (0, _codeFrame.codeFrameColumns)(
_fs.default.readFileSync(this.originModulePath, "utf8"),
location,
{
forceColor: process.env.NODE_ENV !== "test",
},
);
}
}
exports.UnableToResolveError = UnableToResolveError;
function refineDependencyLocation(loc, fileContents, targetSpecifier) {
const lines = fileContents.split("\n");
for (let line = loc.end.line - 1; line >= loc.start.line - 1; line--) {
const maxColumn =
line === loc.end.line ? loc.end.column + 2 : lines[line].length;
const minColumn = line === loc.start.line ? loc.start.column - 1 : 0;
const lineStr = lines[line];
const lineSlice = lineStr.slice(minColumn, maxColumn);
for (
let offset = lineSlice.lastIndexOf(targetSpecifier);
offset !== -1 && offset > 0 && offset < lineSlice.length - 1;
offset = lineSlice.lastIndexOf(targetSpecifier, offset - 1)
) {
const maybeQuoteBefore = lineSlice[minColumn + offset - 1];
const maybeQuoteAfter =
lineStr[minColumn + offset + targetSpecifier.length];
if (isQuote(maybeQuoteBefore) && maybeQuoteBefore === maybeQuoteAfter) {
return {
start: {
column: minColumn + offset + 1,
line: line + 1,
},
};
}
}
}
if (loc.start.line === loc.end.line) {
return {
end: {
column: loc.end.column + 1,
line: loc.end.line,
},
start: {
column: loc.start.column + 1,
line: loc.start.line,
},
};
}
return {
start: {
column: loc.start.column + 1,
line: loc.start.line,
},
};
}
function guessDependencyLocation(fileContents, targetSpecifier) {
const lines = fileContents.split("\n");
let lineNumber = 0;
let column = -1;
for (let line = 0; line < lines.length; line++) {
const columnLocation = lines[line].lastIndexOf(targetSpecifier);
if (columnLocation >= 0) {
lineNumber = line;
column = columnLocation;
break;
}
}
return {
start: {
column: column + 1,
line: lineNumber + 1,
},
};
}
function isQuote(str) {
return str === '"' || str === "'" || str === "`";
}

View File

@@ -0,0 +1,438 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/
import type {
BundlerResolution,
TransformResultDependency,
} from '../../DeltaBundler/types';
import type {Reporter} from '../../lib/reporting';
import type {ResolverInputOptions} from '../../shared/types';
import type {
CustomResolver,
DoesFileExist,
FileCandidates,
FileSystemLookup,
Resolution,
ResolveAsset,
} from 'metro-resolver';
import type {PackageForModule, PackageJson} from 'metro-resolver/private/types';
import {codeFrameColumns} from '@babel/code-frame';
import fs from 'fs';
import invariant from 'invariant';
import * as Resolver from 'metro-resolver';
import createDefaultContext from 'metro-resolver/private/createDefaultContext';
import path from 'path';
import util from 'util';
export type DirExistsFn = (filePath: string) => boolean;
type Options = Readonly<{
assetExts: ReadonlySet<string>,
dirExists: DirExistsFn,
disableHierarchicalLookup: boolean,
doesFileExist: DoesFileExist,
emptyModulePath: string,
extraNodeModules: ?Object,
fileSystemLookup: FileSystemLookup,
getHasteModulePath: (name: string, platform: ?string) => ?string,
getHastePackagePath: (name: string, platform: ?string) => ?string,
mainFields: ReadonlyArray<string>,
getPackage: (packageJsonPath: string) => ?PackageJson,
getPackageForModule: (absolutePath: string) => ?PackageForModule,
nodeModulesPaths: ReadonlyArray<string>,
preferNativePlatform: boolean,
projectRoot: string,
reporter: Reporter,
resolveAsset: ResolveAsset,
resolveRequest: ?CustomResolver,
sourceExts: ReadonlyArray<string>,
unstable_conditionNames: ReadonlyArray<string>,
unstable_conditionsByPlatform: Readonly<{
[platform: string]: ReadonlyArray<string>,
}>,
unstable_enablePackageExports: boolean,
unstable_incrementalResolution: boolean,
}>;
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) {
this._options = options;
const {projectRoot} = this._options;
this._projectRootFakeModulePath = path.join(projectRoot, '_');
}
_getEmptyModule(): BundlerResolution {
let emptyModule = this._cachedEmptyModule;
if (!emptyModule) {
emptyModule = this.resolveDependency(
this._projectRootFakeModulePath,
{
data: {
asyncType: null,
isESMImport: false,
key: this._options.emptyModulePath,
locs: [],
},
name: this._options.emptyModulePath,
},
false,
null,
/* resolverOptions */ {dev: false},
);
this._cachedEmptyModule = emptyModule;
}
return emptyModule;
}
resolveDependency(
originModulePath: string,
dependency: TransformResultDependency,
allowHaste: boolean,
platform: string | null,
resolverOptions: ResolverInputOptions,
): BundlerResolution {
const {
assetExts,
disableHierarchicalLookup,
doesFileExist,
extraNodeModules,
fileSystemLookup,
getPackage,
getPackageForModule,
mainFields,
nodeModulesPaths,
preferNativePlatform,
resolveAsset,
resolveRequest,
sourceExts,
unstable_conditionNames,
unstable_conditionsByPlatform,
unstable_enablePackageExports,
unstable_incrementalResolution,
} = this._options;
try {
const result = Resolver.resolve(
createDefaultContext(
{
allowHaste,
assetExts,
customResolverOptions: resolverOptions.customResolverOptions ?? {},
dev: resolverOptions.dev,
disableHierarchicalLookup,
doesFileExist,
extraNodeModules,
fileSystemLookup,
getPackage,
getPackageForModule,
isESMImport: dependency.data.isESMImport,
mainFields,
nodeModulesPaths,
originModulePath,
preferNativePlatform,
resolveAsset,
resolveHasteModule: (name: string) =>
this._options.getHasteModulePath(name, platform),
resolveHastePackage: (name: string) =>
this._options.getHastePackagePath(name, platform),
resolveRequest,
sourceExts,
unstable_conditionNames,
unstable_conditionsByPlatform,
unstable_enablePackageExports,
unstable_incrementalResolution,
unstable_logWarning: this._logWarning,
},
dependency,
),
dependency.name,
platform,
);
return this._getFileResolvedModule(result);
} catch (error) {
if (error instanceof Resolver.FailedToResolvePathError) {
const {candidates} = error;
throw new UnableToResolveError(
originModulePath,
dependency.name,
'\n\nNone of these files exist:\n' +
[candidates.file, candidates.dir]
.filter(Boolean)
.map(
candidates =>
` * ${Resolver.formatFileCandidates(
this._removeRoot(candidates),
)}`,
)
.join('\n'),
{
cause: error,
dependency,
},
);
} else if (error instanceof Resolver.FailedToResolveUnsupportedError) {
throw new UnableToResolveError(
originModulePath,
dependency.name,
error.message,
{cause: error, dependency},
);
} else if (error instanceof Resolver.FailedToResolveNameError) {
const dirPaths = error.dirPaths;
const extraPaths = error.extraPaths;
const displayDirPaths = dirPaths
.filter((dirPath: string) => this._options.dirExists(dirPath))
.map(dirPath => path.relative(this._options.projectRoot, dirPath))
.concat(extraPaths);
const hint = displayDirPaths.length ? ' or in these directories:' : '';
throw new UnableToResolveError(
originModulePath,
dependency.name,
[
`${dependency.name} could not be found within the project${
hint || '.'
}`,
...displayDirPaths.map((dirPath: string) => ` ${dirPath}`),
].join('\n'),
{
cause: error,
dependency,
},
);
}
throw error;
}
}
/**
* TODO: Return Resolution instead of coercing to BundlerResolution here
*/
_getFileResolvedModule(resolution: Resolution): BundlerResolution {
switch (resolution.type) {
case 'sourceFile':
return resolution;
case 'assetFiles':
// FIXME: we should forward ALL the paths/metadata,
// not just an arbitrary item!
const arbitrary = getArrayLowestItem(resolution.filePaths);
invariant(arbitrary != null, 'invalid asset resolution');
return {filePath: arbitrary, type: 'sourceFile'};
case 'empty':
return this._getEmptyModule();
default:
resolution.type as empty;
throw new Error('invalid type');
}
}
_logWarning = (message: string): void => {
this._options.reporter.update({
message,
type: 'resolver_warning',
});
};
_removeRoot(candidates: FileCandidates): FileCandidates {
if (candidates.filePathPrefix) {
candidates.filePathPrefix = path.relative(
this._options.projectRoot,
candidates.filePathPrefix,
);
}
return candidates;
}
}
function getArrayLowestItem(a: ReadonlyArray<string>): string | void {
if (a.length === 0) {
return undefined;
}
let lowest = a[0];
for (let i = 1; i < a.length; ++i) {
if (a[i] < lowest) {
lowest = a[i];
}
}
return lowest;
}
// $FlowFixMe[incompatible-type]
export class UnableToResolveError extends Error {
/**
* File path of the module that tried to require a module, ex. `/js/foo.js`.
*/
originModulePath: string;
/**
* The name of the module that was required, no necessarily a path,
* ex. `./bar`, or `invariant`.
*/
targetModuleName: string;
/**
* Original error that causes this error
*/
cause: ?Error;
/**
* Fixed type field in common with other Metro build errors.
*/
+type: 'UnableToResolveError' = 'UnableToResolveError';
constructor(
originModulePath: string,
targetModuleName: string,
message: string,
options?: Readonly<{
dependency?: ?TransformResultDependency,
cause?: Error,
}>,
) {
super();
this.originModulePath = originModulePath;
this.targetModuleName = targetModuleName;
const codeFrameMessage = this.buildCodeFrameMessage(options?.dependency);
this.message =
util.format(
'Unable to resolve module %s from %s: %s',
targetModuleName,
originModulePath,
message,
) + (codeFrameMessage ? '\n' + codeFrameMessage : '');
this.cause = options?.cause;
}
buildCodeFrameMessage(dependency: ?TransformResultDependency): ?string {
let file;
try {
file = fs.readFileSync(this.originModulePath, 'utf8');
} catch (error) {
if (error.code === 'ENOENT' || error.code === 'EISDIR') {
// We're probably dealing with a virtualised file system where
// `this.originModulePath` doesn't actually exist on disk.
// We can't show a code frame, but there's no need to let this I/O
// error shadow the original module resolution error.
return null;
}
throw error;
}
const location = dependency?.data.locs.length
? refineDependencyLocation(
dependency.data.locs[0],
file,
this.targetModuleName,
)
: // TODO: Ultimately we shouldn't ever have to guess the location.
guessDependencyLocation(file, this.targetModuleName);
return codeFrameColumns(
fs.readFileSync(this.originModulePath, 'utf8'),
location,
{forceColor: process.env.NODE_ENV !== 'test'},
);
}
}
// Given a source location for an import declaration or `require()` call (etc),
// return a location for use with @babel/code-frame in the resolution error.
function refineDependencyLocation(
loc: BabelSourceLocation,
fileContents: string,
targetSpecifier: string,
): {
start: {column: number, line: number},
end?: {column: number, line: number},
} {
const lines = fileContents.split('\n');
// If we can find the module name in range of the given loc, surrounded by
// matching quotes, that's likely our specifier. Point to the first column of
// the *last* valid occurrence.
// Note that module names may not always be found in the source code verbatim,
// whether because of escaping or because of exotic dependency APIs.
for (let line = loc.end.line - 1; line >= loc.start.line - 1; line--) {
const maxColumn =
line === loc.end.line ? loc.end.column + 2 : lines[line].length;
const minColumn = line === loc.start.line ? loc.start.column - 1 : 0;
const lineStr = lines[line];
const lineSlice = lineStr.slice(minColumn, maxColumn);
for (
let offset = lineSlice.lastIndexOf(targetSpecifier);
offset !== -1 && // leave room for quotes
offset > 0 &&
offset < lineSlice.length - 1;
offset = lineSlice.lastIndexOf(targetSpecifier, offset - 1)
) {
const maybeQuoteBefore = lineSlice[minColumn + offset - 1];
const maybeQuoteAfter =
lineStr[minColumn + offset + targetSpecifier.length];
if (isQuote(maybeQuoteBefore) && maybeQuoteBefore === maybeQuoteAfter) {
return {
start: {
column: minColumn + offset + 1,
line: line + 1,
},
};
}
}
}
// Otherwise, if this is a single-line loc, return it exactly, as a range.
if (loc.start.line === loc.end.line) {
return {
end: {
column: loc.end.column + 1,
line: loc.end.line,
},
start: {
column: loc.start.column + 1,
line: loc.start.line,
},
};
}
// Otherwise, point to the first column of the loc, to avoid including too
// much unnecessary context.
return {
start: {
column: loc.start.column + 1,
line: loc.start.line,
},
};
}
function guessDependencyLocation(
fileContents: string,
targetSpecifier: string,
) {
const lines = fileContents.split('\n');
let lineNumber = 0;
let column = -1;
for (let line = 0; line < lines.length; line++) {
const columnLocation = lines[line].lastIndexOf(targetSpecifier);
if (columnLocation >= 0) {
lineNumber = line;
column = columnLocation;
break;
}
}
return {
start: {column: column + 1, line: lineNumber + 1},
};
}
function isQuote(str: ?string): boolean {
return str === '"' || str === "'" || str === '`';
}

View File

@@ -0,0 +1,36 @@
/**
* 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<<c008adad2ea747972e2f301a6375b447>>
*
* 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';
import type {HasteMap} from 'metro-file-map';
import MetroFileMap, {DependencyPlugin} from 'metro-file-map';
declare function createFileMap(
config: ConfigT,
options?: Readonly<{
extractDependencies?: boolean;
watch?: boolean;
throwOnModuleCollision?: boolean;
cacheFilePrefix?: string;
}>,
): {
fileMap: MetroFileMap;
hasteMap: HasteMap;
dependencyPlugin: null | undefined | DependencyPlugin;
};
export default createFileMap;

View File

@@ -0,0 +1,136 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = createFileMap;
var _ciInfo = _interopRequireDefault(require("ci-info"));
var _metroFileMap = _interopRequireWildcard(require("metro-file-map"));
function _interopRequireWildcard(e, t) {
if ("function" == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ("object" != typeof e && "function" != typeof e))
return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
"default" !== t &&
{}.hasOwnProperty.call(e, t) &&
((i =
(o = Object.defineProperty) &&
Object.getOwnPropertyDescriptor(e, t)) &&
(i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function getIgnorePattern(config) {
const { blockList, blacklistRE } = config.resolver;
const ignorePattern = blacklistRE || blockList;
if (!ignorePattern) {
return / ^/;
}
const combine = (regexes) =>
new RegExp(
regexes
.map((regex, index) => {
if (regex.flags !== regexes[0].flags) {
throw new Error(
"Cannot combine blockList patterns, because they have different flags:\n" +
" - Pattern 0: " +
regexes[0].toString() +
"\n" +
` - Pattern ${index}: ` +
regexes[index].toString(),
);
}
return "(" + regex.source + ")";
})
.join("|"),
regexes[0]?.flags ?? "",
);
if (Array.isArray(ignorePattern)) {
return combine(ignorePattern);
}
return ignorePattern;
}
function createFileMap(config, options) {
const watch = options?.watch == null ? !_ciInfo.default.isCI : options.watch;
const { enabled: autoSaveEnabled, ...autoSaveOpts } =
config.watcher.unstable_autoSaveCache ?? {};
const autoSave = watch && autoSaveEnabled ? autoSaveOpts : false;
const plugins = [...(config.unstable_fileMapPlugins ?? [])];
let dependencyPlugin = null;
if (
config.resolver.dependencyExtractor != null &&
options?.extractDependencies !== false
) {
dependencyPlugin = new _metroFileMap.DependencyPlugin({
dependencyExtractor: config.resolver.dependencyExtractor,
computeDependencies: true,
rootDir: config.projectRoot,
});
plugins.push(dependencyPlugin);
}
const hasteMap = new _metroFileMap.HastePlugin({
platforms: new Set([
...config.resolver.platforms,
_metroFileMap.default.H.NATIVE_PLATFORM,
]),
hasteImplModulePath: config.resolver.hasteImplModulePath,
enableHastePackages: config.resolver.enableGlobalPackages,
rootDir: config.projectRoot,
failValidationOnConflicts: options?.throwOnModuleCollision ?? true,
});
plugins.push(hasteMap);
const fileMap = new _metroFileMap.default({
cacheManagerFactory:
config?.unstable_fileMapCacheManagerFactory ??
((factoryParams) =>
new _metroFileMap.DiskCacheManager(factoryParams, {
cacheDirectory:
config.fileMapCacheDirectory ?? config.hasteMapCacheDirectory,
cacheFilePrefix: options?.cacheFilePrefix,
autoSave,
})),
perfLoggerFactory: config.unstable_perfLoggerFactory,
computeSha1: !config.watcher.unstable_lazySha1,
enableSymlinks: true,
extensions: Array.from(
new Set([
...config.resolver.sourceExts,
...config.resolver.assetExts,
...config.watcher.additionalExts,
]),
),
forceNodeFilesystemAPI: !config.resolver.useWatchman,
healthCheck: config.watcher.healthCheck,
ignorePattern: getIgnorePattern(config),
maxWorkers: config.maxWorkers,
plugins,
retainAllFiles: true,
resetCache: config.resetCache,
rootDir: config.projectRoot,
roots: config.watchFolders,
useWatchman: config.resolver.useWatchman,
watch,
watchmanDeferStates: config.watcher.watchman.deferStates,
});
return {
fileMap,
hasteMap,
dependencyPlugin,
};
}

View File

@@ -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 {ConfigT} from 'metro-config';
import type {HasteMap, InputFileMapPlugin} from 'metro-file-map';
import ci from 'ci-info';
import MetroFileMap, {
DependencyPlugin,
DiskCacheManager,
HastePlugin,
} from 'metro-file-map';
function getIgnorePattern(config: ConfigT): RegExp {
// For now we support both options
const {blockList, blacklistRE} = config.resolver;
const ignorePattern = blacklistRE || blockList;
// If neither option has been set, use default pattern
if (!ignorePattern) {
return / ^/;
}
const combine = (regexes: Array<RegExp>) =>
new RegExp(
regexes
.map((regex, index) => {
if (regex.flags !== regexes[0].flags) {
throw new Error(
'Cannot combine blockList patterns, because they have different flags:\n' +
' - Pattern 0: ' +
regexes[0].toString() +
'\n' +
` - Pattern ${index}: ` +
regexes[index].toString(),
);
}
return '(' + regex.source + ')';
})
.join('|'),
regexes[0]?.flags ?? '',
);
// If ignorePattern is an array, merge it into one
if (Array.isArray(ignorePattern)) {
return combine(ignorePattern);
}
return ignorePattern;
}
export default function createFileMap(
config: ConfigT,
options?: Readonly<{
extractDependencies?: boolean,
watch?: boolean,
throwOnModuleCollision?: boolean,
cacheFilePrefix?: string,
}>,
): {
fileMap: MetroFileMap,
hasteMap: HasteMap,
dependencyPlugin: ?DependencyPlugin,
} {
const watch = options?.watch == null ? !ci.isCI : options.watch;
const {enabled: autoSaveEnabled, ...autoSaveOpts} =
config.watcher.unstable_autoSaveCache ?? {};
const autoSave = watch && autoSaveEnabled ? autoSaveOpts : false;
const plugins: Array<InputFileMapPlugin> = [
...(config.unstable_fileMapPlugins ?? []),
];
let dependencyPlugin = null;
// Add DependencyPlugin if dependencies should be extracted
if (
config.resolver.dependencyExtractor != null &&
options?.extractDependencies !== false
) {
dependencyPlugin = new DependencyPlugin({
dependencyExtractor: config.resolver.dependencyExtractor,
computeDependencies: true,
rootDir: config.projectRoot,
});
plugins.push(dependencyPlugin);
}
const hasteMap = new HastePlugin({
platforms: new Set([
...config.resolver.platforms,
MetroFileMap.H.NATIVE_PLATFORM,
]),
hasteImplModulePath: config.resolver.hasteImplModulePath,
enableHastePackages: config.resolver.enableGlobalPackages,
rootDir: config.projectRoot,
failValidationOnConflicts: options?.throwOnModuleCollision ?? true,
});
plugins.push(hasteMap);
const fileMap = new MetroFileMap({
cacheManagerFactory:
config?.unstable_fileMapCacheManagerFactory ??
(factoryParams =>
new DiskCacheManager(factoryParams, {
cacheDirectory:
config.fileMapCacheDirectory ?? config.hasteMapCacheDirectory,
cacheFilePrefix: options?.cacheFilePrefix,
autoSave,
})),
perfLoggerFactory: config.unstable_perfLoggerFactory,
computeSha1: !config.watcher.unstable_lazySha1,
enableSymlinks: true,
extensions: Array.from(
new Set([
...config.resolver.sourceExts,
...config.resolver.assetExts,
...config.watcher.additionalExts,
]),
),
forceNodeFilesystemAPI: !config.resolver.useWatchman,
healthCheck: config.watcher.healthCheck,
ignorePattern: getIgnorePattern(config),
maxWorkers: config.maxWorkers,
plugins,
retainAllFiles: true,
resetCache: config.resetCache,
rootDir: config.projectRoot,
roots: config.watchFolders,
useWatchman: config.resolver.useWatchman,
watch,
watchmanDeferStates: config.watcher.watchman.deferStates,
});
return {fileMap, hasteMap, dependencyPlugin};
}

37
node_modules/metro/src/node-haste/PackageCache.d.ts generated vendored Normal file
View File

@@ -0,0 +1,37 @@
/**
* 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<<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 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 {
constructor(options: {getClosestPackage: GetClosestPackageFn});
getPackage(
filePath: string,
): Readonly<{rootPath: string; packageJson: PackageJson}>;
getPackageForModule(
absoluteModulePath: string,
): null | undefined | PackageForModule;
invalidate(filePath: string): void;
}

93
node_modules/metro/src/node-haste/PackageCache.js generated vendored Normal file
View File

@@ -0,0 +1,93 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.PackageCache = void 0;
var _fs = require("fs");
var _path = require("path");
class PackageCache {
#getClosestPackage;
#packageCache;
#resultByModulePath;
#modulePathsByPackagePath;
#modulePathsWithNoPackage;
constructor(options) {
this.#getClosestPackage = options.getClosestPackage;
this.#packageCache = new Map();
this.#resultByModulePath = new Map();
this.#modulePathsByPackagePath = new Map();
this.#modulePathsWithNoPackage = new Set();
}
getPackage(filePath) {
let cached = this.#packageCache.get(filePath);
if (cached == null) {
cached = {
rootPath: (0, _path.dirname)(filePath),
packageJson: JSON.parse((0, _fs.readFileSync)(filePath, "utf8")),
};
this.#packageCache.set(filePath, cached);
}
return cached;
}
getPackageForModule(absoluteModulePath) {
const cached = this.#resultByModulePath.get(absoluteModulePath);
if (cached !== undefined) {
return cached;
}
const closest = this.#getClosestPackage(absoluteModulePath);
if (closest == null) {
this.#resultByModulePath.set(absoluteModulePath, null);
this.#modulePathsWithNoPackage.add(absoluteModulePath);
return null;
}
const packagePath = closest.packageJsonPath;
let modulePaths = this.#modulePathsByPackagePath.get(packagePath);
if (modulePaths == null) {
modulePaths = new Set();
this.#modulePathsByPackagePath.set(packagePath, modulePaths);
}
modulePaths.add(absoluteModulePath);
const pkg = this.getPackage(packagePath);
if (pkg == null) {
return null;
}
const result = {
packageJson: pkg.packageJson,
packageRelativePath: closest.packageRelativePath,
rootPath: pkg.rootPath,
};
this.#resultByModulePath.set(absoluteModulePath, result);
return result;
}
invalidate(filePath) {
this.#packageCache.delete(filePath);
const cachedResult = this.#resultByModulePath.get(filePath);
this.#resultByModulePath.delete(filePath);
this.#modulePathsWithNoPackage.delete(filePath);
if (cachedResult != null) {
const packagePath = cachedResult.rootPath + _path.sep + "package.json";
const modules = this.#modulePathsByPackagePath.get(packagePath);
if (modules != null) {
modules.delete(filePath);
if (modules.size === 0) {
this.#modulePathsByPackagePath.delete(packagePath);
}
}
}
const modulePaths = this.#modulePathsByPackagePath.get(filePath);
if (modulePaths != null) {
for (const modulePath of modulePaths) {
this.#resultByModulePath.delete(modulePath);
}
this.#modulePathsByPackagePath.delete(filePath);
}
if (filePath.endsWith(_path.sep + "package.json")) {
for (const modulePath of this.#modulePathsWithNoPackage) {
this.#resultByModulePath.delete(modulePath);
}
this.#modulePathsWithNoPackage.clear();
}
}
}
exports.PackageCache = PackageCache;

149
node_modules/metro/src/node-haste/PackageCache.js.flow generated vendored Normal file
View File

@@ -0,0 +1,149 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/
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: Map<
string,
{
rootPath: string,
packageJson: PackageJson,
},
>;
// Single cache: module path → pre-built result object, or null (no allocation on hit)
#resultByModulePath: Map<string, PackageForModule | null>;
// Reverse index for invalidation: package.json path → set of module paths
#modulePathsByPackagePath: Map<string, Set<string>>;
// Module paths that resolved to no package.json (null), for invalidation
#modulePathsWithNoPackage: Set<string>;
constructor(options: {getClosestPackage: GetClosestPackageFn, ...}) {
this.#getClosestPackage = options.getClosestPackage;
this.#packageCache = new Map();
this.#resultByModulePath = new Map();
this.#modulePathsByPackagePath = new Map();
this.#modulePathsWithNoPackage = new Set();
}
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 cached;
}
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;
}
const closest = this.#getClosestPackage(absoluteModulePath);
if (closest == null) {
this.#resultByModulePath.set(absoluteModulePath, null);
this.#modulePathsWithNoPackage.add(absoluteModulePath);
return null;
}
const packagePath = closest.packageJsonPath;
// 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);
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) {
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 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) {
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);
}
this.#modulePathsWithNoPackage.clear();
}
}
}

36
node_modules/metro/src/node-haste/lib/AssetPaths.d.ts generated vendored Normal file
View File

@@ -0,0 +1,36 @@
/**
* 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<<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 = {
assetName: string;
name: string;
platform: null | undefined | string;
resolution: number;
type: string;
};
/**
* Return `null` if the `filePath` doesn't have a valid extension, required
* to describe the type of an asset.
*/
export declare function tryParse(
filePath: string,
platforms: ReadonlySet<string>,
): null | undefined | AssetPath;
export declare function parse(
filePath: string,
platforms: ReadonlySet<string>,
): AssetPath;

57
node_modules/metro/src/node-haste/lib/AssetPaths.js generated vendored Normal file
View File

@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.parse = parse;
exports.tryParse = tryParse;
var _parsePlatformFilePath = _interopRequireDefault(
require("./parsePlatformFilePath"),
);
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const ASSET_BASE_NAME_RE = /(.+?)(@([\d.]+)x)?$/;
function parseBaseName(baseName) {
const match = baseName.match(ASSET_BASE_NAME_RE);
if (!match) {
throw new Error(`invalid asset name: \`${baseName}'`);
}
const rootName = match[1];
if (match[3] != null) {
const resolution = parseFloat(match[3]);
if (!Number.isNaN(resolution)) {
return {
resolution,
rootName,
};
}
}
return {
resolution: 1,
rootName,
};
}
function tryParse(filePath, platforms) {
const result = (0, _parsePlatformFilePath.default)(filePath, platforms);
const { dirPath, baseName, platform, extension } = result;
if (extension == null) {
return null;
}
const { rootName, resolution } = parseBaseName(baseName);
return {
assetName: _path.default.join(dirPath, `${rootName}.${extension}`),
name: rootName,
platform,
resolution,
type: extension,
};
}
function parse(filePath, platforms) {
const result = tryParse(filePath, platforms);
if (result == null) {
throw new Error(`invalid asset file path: ${filePath}`);
}
return result;
}

View File

@@ -0,0 +1,76 @@
/**
* 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
* @format
* @oncall react_native
*/
import parsePlatformFilePath from './parsePlatformFilePath';
import path from 'path';
export type AssetPath = {
assetName: string,
name: string,
platform: ?string,
resolution: number,
type: string,
};
const ASSET_BASE_NAME_RE = /(.+?)(@([\d.]+)x)?$/;
function parseBaseName(baseName: string): {
resolution: number,
rootName: string,
...
} {
const match = baseName.match(ASSET_BASE_NAME_RE);
if (!match) {
throw new Error(`invalid asset name: \`${baseName}'`);
}
const rootName = match[1];
if (match[3] != null) {
const resolution = parseFloat(match[3]);
if (!Number.isNaN(resolution)) {
return {resolution, rootName};
}
}
return {resolution: 1, rootName};
}
/**
* Return `null` if the `filePath` doesn't have a valid extension, required
* to describe the type of an asset.
*/
export function tryParse(
filePath: string,
platforms: ReadonlySet<string>,
): ?AssetPath {
const result = parsePlatformFilePath(filePath, platforms);
const {dirPath, baseName, platform, extension} = result;
if (extension == null) {
return null;
}
const {rootName, resolution} = parseBaseName(baseName);
return {
assetName: path.join(dirPath, `${rootName}.${extension}`),
name: rootName,
platform,
resolution,
type: extension,
};
}
export function parse(
filePath: string,
platforms: ReadonlySet<string>,
): AssetPath {
const result = tryParse(filePath, platforms);
if (result == null) {
throw new Error(`invalid asset file path: ${filePath}`);
}
return result;
}

View File

@@ -0,0 +1,32 @@
/**
* 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<<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 = {
dirPath: string;
baseName: string;
platform: null | undefined | string;
extension: null | undefined | string;
};
/**
* Extract the components of a file path that can have a platform specifier: Ex.
* `index.ios.js` is specific to the `ios` platform and has the extension `js`.
*/
declare function parsePlatformFilePath(
filePath: string,
platforms: ReadonlySet<string>,
): PlatformFilePathParts;
export default parsePlatformFilePath;

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = parsePlatformFilePath;
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const PATH_RE = /^(.+?)(\.([^.]+))?\.([^.]+)$/;
function parsePlatformFilePath(filePath, platforms) {
const dirPath = _path.default.dirname(filePath);
const fileName = _path.default.basename(filePath);
const match = fileName.match(PATH_RE);
if (!match) {
return {
baseName: fileName,
dirPath,
extension: null,
platform: null,
};
}
const extension = match[4] || null;
const platform = match[3] || null;
if (platform == null || platforms.has(platform)) {
return {
baseName: match[1],
dirPath,
extension,
platform,
};
}
const baseName = `${match[1]}.${platform}`;
return {
baseName,
dirPath,
extension,
platform: null,
};
}

View File

@@ -0,0 +1,44 @@
/**
* 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
* @format
* @oncall react_native
*/
import path from 'path';
type PlatformFilePathParts = {
dirPath: string,
baseName: string,
platform: ?string,
extension: ?string,
};
const PATH_RE = /^(.+?)(\.([^.]+))?\.([^.]+)$/;
/**
* Extract the components of a file path that can have a platform specifier: Ex.
* `index.ios.js` is specific to the `ios` platform and has the extension `js`.
*/
export default function parsePlatformFilePath(
filePath: string,
platforms: ReadonlySet<string>,
): PlatformFilePathParts {
const dirPath = path.dirname(filePath);
const fileName = path.basename(filePath);
const match = fileName.match(PATH_RE);
if (!match) {
return {baseName: fileName, dirPath, extension: null, platform: null};
}
const extension = match[4] || null;
const platform = match[3] || null;
if (platform == null || platforms.has(platform)) {
return {baseName: match[1], dirPath, extension, platform};
}
const baseName = `${match[1]}.${platform}`;
return {baseName, dirPath, extension, platform: null};
}