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

View File

@@ -0,0 +1,52 @@
/**
* 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<<e07a9c061b0224fc44191d956461bd6f>>
*
* 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 {
FileMapPlugin,
FileMapPluginInitOptions,
FileMapPluginWorker,
Path,
} from '../flow-types';
export type DependencyPluginOptions = Readonly<{
/** Path to custom dependency extractor module */
dependencyExtractor: null | undefined | string;
/** Whether to compute dependencies (performance optimization) */
computeDependencies: boolean;
rootDir: Path;
}>;
declare class DependencyPlugin
implements FileMapPlugin<null, ReadonlyArray<string> | null>
{
readonly name: 'dependencies';
constructor(options: DependencyPluginOptions);
initialize(
initOptions: FileMapPluginInitOptions<null, ReadonlyArray<string> | null>,
): Promise<void>;
getSerializableSnapshot(): null;
onChanged(): void;
assertValid(): void;
getCacheKey(): string;
getWorker(): FileMapPluginWorker;
/**
* Get the list of dependencies for a given file.
* @param mixedPath Absolute or project-relative path to the file
* @returns Array of dependency module names, or null if the file doesn't exist
*/
getDependencies(mixedPath: Path): null | undefined | ReadonlyArray<string>;
}
export default DependencyPlugin;

View File

@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
class DependencyPlugin {
name = "dependencies";
#dependencyExtractor;
#computeDependencies;
#getDependencies;
#rootDir;
constructor(options) {
this.#dependencyExtractor = options.dependencyExtractor;
this.#computeDependencies = options.computeDependencies;
this.#rootDir = options.rootDir;
}
async initialize(initOptions) {
const { files } = initOptions;
this.#getDependencies = (mixedPath) => {
const result = files.lookup(mixedPath);
if (result.exists && result.type === "f") {
return result.pluginData ?? [];
}
return null;
};
}
getSerializableSnapshot() {
return null;
}
onChanged() {}
assertValid() {}
getCacheKey() {
if (this.#dependencyExtractor != null) {
const extractor = require(this.#dependencyExtractor);
return JSON.stringify({
extractorKey: extractor.getCacheKey?.() ?? null,
extractorPath: this.#dependencyExtractor,
});
}
return "default-dependency-extractor";
}
getWorker() {
const excludedExtensions = require("../workerExclusionList");
return {
worker: {
modulePath: require.resolve("./dependencies/worker.js"),
setupArgs: {
dependencyExtractor: this.#dependencyExtractor ?? null,
},
},
filter: ({ normalPath, isNodeModules }) => {
if (!this.#computeDependencies) {
return false;
}
if (isNodeModules) {
return false;
}
const ext = normalPath.substr(normalPath.lastIndexOf("."));
return !excludedExtensions.has(ext);
},
};
}
getDependencies(mixedPath) {
if (this.#getDependencies == null) {
throw new Error(
"DependencyPlugin has not been initialized before getDependencies",
);
}
return this.#getDependencies(mixedPath);
}
}
exports.default = DependencyPlugin;

View File

@@ -0,0 +1,129 @@
/**
* 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 {
FileMapPlugin,
FileMapPluginInitOptions,
FileMapPluginWorker,
Path,
} from '../flow-types';
export type DependencyPluginOptions = Readonly<{
/** Path to custom dependency extractor module */
dependencyExtractor: ?string,
/** Whether to compute dependencies (performance optimization) */
computeDependencies: boolean,
rootDir: Path,
}>;
export default class DependencyPlugin
implements FileMapPlugin<null, ReadonlyArray<string> | null>
{
+name: 'dependencies' = 'dependencies';
#dependencyExtractor: ?string;
#computeDependencies: boolean;
#getDependencies: Path => ?ReadonlyArray<string>;
#rootDir: Path;
constructor(options: DependencyPluginOptions) {
this.#dependencyExtractor = options.dependencyExtractor;
this.#computeDependencies = options.computeDependencies;
this.#rootDir = options.rootDir;
}
async initialize(
initOptions: FileMapPluginInitOptions<null, ReadonlyArray<string> | null>,
): Promise<void> {
const {files} = initOptions;
// Create closure to access dependencies from file metadata plugin data
this.#getDependencies = (mixedPath: Path) => {
const result = files.lookup(mixedPath);
if (result.exists && result.type === 'f') {
// Backwards compatibility: distinguish an extant file that we've not
// run the worker on (probably because it fails the extension filter)
// from a missing file. Non-source files are expected to have empty
// dependencies.
return result.pluginData ?? [];
}
return null;
};
}
getSerializableSnapshot(): null {
// Dependencies stored in plugin data, no separate serialization needed
return null;
}
onChanged(): void {
// No-op: Worker already populated plugin data
// Plugin data is write-only from worker
}
assertValid(): void {
// No validation needed
}
getCacheKey(): string {
if (this.#dependencyExtractor != null) {
// Dynamic require to get extractor's cache key
// $FlowFixMe[unsupported-syntax] - dynamic require
const extractor = require(this.#dependencyExtractor);
return JSON.stringify({
extractorKey: extractor.getCacheKey?.() ?? null,
extractorPath: this.#dependencyExtractor,
});
}
return 'default-dependency-extractor';
}
getWorker(): FileMapPluginWorker {
const excludedExtensions = require('../workerExclusionList');
return {
worker: {
modulePath: require.resolve('./dependencies/worker.js'),
setupArgs: {
dependencyExtractor: this.#dependencyExtractor ?? null,
},
},
filter: ({normalPath, isNodeModules}) => {
// Respect computeDependencies flag
if (!this.#computeDependencies) {
return false;
}
// Never process node_modules
if (isNodeModules) {
return false;
}
// Skip excluded extensions
const ext = normalPath.substr(normalPath.lastIndexOf('.'));
return !excludedExtensions.has(ext);
},
};
}
/**
* Get the list of dependencies for a given file.
* @param mixedPath Absolute or project-relative path to the file
* @returns Array of dependency module names, or null if the file doesn't exist
*/
getDependencies(mixedPath: Path): ?ReadonlyArray<string> {
if (this.#getDependencies == null) {
throw new Error(
'DependencyPlugin has not been initialized before getDependencies',
);
}
return this.#getDependencies(mixedPath);
}
}

View File

@@ -0,0 +1,69 @@
/**
* 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<<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,
FileMapPlugin,
FileMapPluginInitOptions,
FileMapPluginWorker,
HasteConflict,
HasteMap,
HasteMapItemMetadata,
HTypeValue,
Path,
PerfLogger,
ReadonlyFileSystemChanges,
} from '../flow-types';
export type HasteMapOptions = Readonly<{
console?: null | undefined | Console;
enableHastePackages: boolean;
hasteImplModulePath: null | undefined | string;
perfLogger?: null | undefined | PerfLogger;
platforms: ReadonlySet<string>;
rootDir: Path;
failValidationOnConflicts: boolean;
}>;
declare class HastePlugin
implements HasteMap, FileMapPlugin<null, string | null>
{
readonly name: 'haste';
constructor(options: HasteMapOptions);
initialize(
$$PARAM_0$$: FileMapPluginInitOptions<null, string | null>,
): Promise<void>;
getSerializableSnapshot(): null;
getModule(
name: string,
platform?: null | undefined | string,
supportsNativePlatform?: null | undefined | boolean,
type?: null | undefined | HTypeValue,
): null | undefined | Path;
getModuleNameByPath(mixedPath: Path): null | undefined | string;
getPackage(
name: string,
platform: null | undefined | string,
_supportsNativePlatform?: null | undefined | boolean,
): null | undefined | Path;
onChanged(delta: ReadonlyFileSystemChanges<null | undefined | string>): void;
setModule(id: string, module: HasteMapItemMetadata): void;
assertValid(): void;
computeConflicts(): Array<HasteConflict>;
getCacheKey(): string;
getWorker(): FileMapPluginWorker;
}
export default HastePlugin;

403
node_modules/metro-file-map/src/plugins/HastePlugin.js generated vendored Normal file
View File

@@ -0,0 +1,403 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _constants = _interopRequireDefault(require("../constants"));
var _RootPathUtils = require("../lib/RootPathUtils");
var _sorting = require("../lib/sorting");
var _DuplicateHasteCandidatesError = require("./haste/DuplicateHasteCandidatesError");
var _getPlatformExtension = _interopRequireDefault(
require("./haste/getPlatformExtension"),
);
var _HasteConflictsError = require("./haste/HasteConflictsError");
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const EMPTY_OBJ = {};
const EMPTY_MAP = new Map();
const PACKAGE_JSON = /(?:[/\\]|^)package\.json$/;
const YIELD_EVERY_NUM_HASTE_FILES = 10000;
class HastePlugin {
name = "haste";
#console;
#duplicates = new Map();
#enableHastePackages;
#failValidationOnConflicts;
#getModuleNameByPath;
#hasteImplModulePath;
#map = new Map();
#pathUtils;
#perfLogger;
#platforms;
#rootDir;
constructor(options) {
this.#console = options.console ?? global.console;
this.#enableHastePackages = options.enableHastePackages;
this.#hasteImplModulePath = options.hasteImplModulePath;
this.#perfLogger = options.perfLogger;
this.#platforms = options.platforms;
this.#rootDir = options.rootDir;
this.#pathUtils = new _RootPathUtils.RootPathUtils(options.rootDir);
this.#failValidationOnConflicts = options.failValidationOnConflicts;
}
async initialize({ files }) {
this.#perfLogger?.point("constructHasteMap_start");
let hasteFiles = 0;
for (const {
baseName,
canonicalPath,
pluginData: hasteId,
} of files.fileIterator({
includeNodeModules: false,
includeSymlinks: false,
})) {
if (hasteId == null) {
continue;
}
this.setModule(hasteId, [
canonicalPath,
this.#enableHastePackages && baseName === "package.json"
? _constants.default.PACKAGE
: _constants.default.MODULE,
]);
if (++hasteFiles % YIELD_EVERY_NUM_HASTE_FILES === 0) {
await new Promise(setImmediate);
}
}
this.#getModuleNameByPath = (mixedPath) => {
const result = files.lookup(mixedPath);
return result.exists &&
result.type === "f" &&
typeof result.pluginData === "string"
? result.pluginData
: null;
};
this.#perfLogger?.point("constructHasteMap_end");
this.#perfLogger?.annotate({
int: {
hasteFiles,
},
});
}
getSerializableSnapshot() {
return null;
}
getModule(name, platform, supportsNativePlatform, type) {
const module = this.#getModuleMetadata(
name,
platform,
!!supportsNativePlatform,
);
if (
module &&
module[_constants.default.TYPE] === (type ?? _constants.default.MODULE)
) {
const modulePath = module[_constants.default.PATH];
return modulePath && this.#pathUtils.normalToAbsolute(modulePath);
}
return null;
}
getModuleNameByPath(mixedPath) {
if (this.#getModuleNameByPath == null) {
throw new Error(
"HastePlugin has not been initialized before getModuleNameByPath",
);
}
return this.#getModuleNameByPath(mixedPath) ?? null;
}
getPackage(name, platform, _supportsNativePlatform) {
return this.getModule(name, platform, null, _constants.default.PACKAGE);
}
#getModuleMetadata(name, platform, supportsNativePlatform) {
const map = this.#map.get(name) || EMPTY_OBJ;
const dupMap = this.#duplicates.get(name) || EMPTY_MAP;
if (platform != null) {
this.#assertNoDuplicates(
name,
platform,
supportsNativePlatform,
dupMap.get(platform),
);
if (map[platform] != null) {
return map[platform];
}
}
if (supportsNativePlatform) {
this.#assertNoDuplicates(
name,
_constants.default.NATIVE_PLATFORM,
supportsNativePlatform,
dupMap.get(_constants.default.NATIVE_PLATFORM),
);
if (map[_constants.default.NATIVE_PLATFORM]) {
return map[_constants.default.NATIVE_PLATFORM];
}
}
this.#assertNoDuplicates(
name,
_constants.default.GENERIC_PLATFORM,
supportsNativePlatform,
dupMap.get(_constants.default.GENERIC_PLATFORM),
);
if (map[_constants.default.GENERIC_PLATFORM]) {
return map[_constants.default.GENERIC_PLATFORM];
}
return null;
}
#assertNoDuplicates(name, platform, supportsNativePlatform, relativePathSet) {
if (relativePathSet == null) {
return;
}
const duplicates = new Map();
for (const [relativePath, type] of relativePathSet) {
const duplicatePath = this.#pathUtils.normalToAbsolute(relativePath);
duplicates.set(duplicatePath, type);
}
throw new _DuplicateHasteCandidatesError.DuplicateHasteCandidatesError(
name,
platform,
supportsNativePlatform,
duplicates,
);
}
onChanged(delta) {
for (const [canonicalPath, maybeHasteId] of delta.removedFiles) {
this.#onRemovedFile(canonicalPath, maybeHasteId);
}
for (const [canonicalPath, maybeHasteId] of delta.addedFiles) {
this.#onNewFile(canonicalPath, maybeHasteId);
}
}
#onNewFile(canonicalPath, id) {
if (id == null) {
return;
}
const module = [
canonicalPath,
this.#enableHastePackages &&
_path.default.basename(canonicalPath) === "package.json"
? _constants.default.PACKAGE
: _constants.default.MODULE,
];
this.setModule(id, module);
}
setModule(id, module) {
let hasteMapItem = this.#map.get(id);
if (!hasteMapItem) {
hasteMapItem = Object.create(null);
this.#map.set(id, hasteMapItem);
}
const platform =
(0, _getPlatformExtension.default)(
module[_constants.default.PATH],
this.#platforms,
) || _constants.default.GENERIC_PLATFORM;
const existingModule = hasteMapItem[platform];
if (
existingModule &&
existingModule[_constants.default.PATH] !==
module[_constants.default.PATH]
) {
if (this.#console) {
this.#console.warn(
[
"metro-file-map: Haste module naming collision: " + id,
" The following files share their name; please adjust your hasteImpl:",
" * <rootDir>" +
_path.default.sep +
existingModule[_constants.default.PATH],
" * <rootDir>" +
_path.default.sep +
module[_constants.default.PATH],
"",
].join("\n"),
);
}
delete hasteMapItem[platform];
if (Object.keys(hasteMapItem).length === 0) {
this.#map.delete(id);
}
let dupsByPlatform = this.#duplicates.get(id);
if (dupsByPlatform == null) {
dupsByPlatform = new Map();
this.#duplicates.set(id, dupsByPlatform);
}
const dups = new Map([
[module[_constants.default.PATH], module[_constants.default.TYPE]],
[
existingModule[_constants.default.PATH],
existingModule[_constants.default.TYPE],
],
]);
dupsByPlatform.set(platform, dups);
return;
}
const dupsByPlatform = this.#duplicates.get(id);
if (dupsByPlatform != null) {
const dups = dupsByPlatform.get(platform);
if (dups != null) {
dups.set(
module[_constants.default.PATH],
module[_constants.default.TYPE],
);
}
return;
}
hasteMapItem[platform] = module;
}
#onRemovedFile(canonicalPath, moduleName) {
if (moduleName == null) {
return;
}
const platform =
(0, _getPlatformExtension.default)(canonicalPath, this.#platforms) ||
_constants.default.GENERIC_PLATFORM;
const hasteMapItem = this.#map.get(moduleName);
if (hasteMapItem != null) {
delete hasteMapItem[platform];
if (Object.keys(hasteMapItem).length === 0) {
this.#map.delete(moduleName);
} else {
this.#map.set(moduleName, hasteMapItem);
}
}
this.#recoverDuplicates(moduleName, canonicalPath);
}
assertValid() {
if (!this.#failValidationOnConflicts) {
return;
}
const conflicts = this.computeConflicts();
if (conflicts.length > 0) {
throw new _HasteConflictsError.HasteConflictsError(conflicts);
}
}
#recoverDuplicates(moduleName, relativeFilePath) {
let dupsByPlatform = this.#duplicates.get(moduleName);
if (dupsByPlatform == null) {
return;
}
const platform =
(0, _getPlatformExtension.default)(relativeFilePath, this.#platforms) ||
_constants.default.GENERIC_PLATFORM;
let dups = dupsByPlatform.get(platform);
if (dups == null) {
return;
}
dupsByPlatform = new Map(dupsByPlatform);
this.#duplicates.set(moduleName, dupsByPlatform);
dups = new Map(dups);
dupsByPlatform.set(platform, dups);
dups.delete(relativeFilePath);
if (dups.size !== 1) {
return;
}
const uniqueModule = dups.entries().next().value;
if (!uniqueModule) {
return;
}
let dedupMap = this.#map.get(moduleName);
if (dedupMap == null) {
dedupMap = Object.create(null);
this.#map.set(moduleName, dedupMap);
}
dedupMap[platform] = uniqueModule;
dupsByPlatform.delete(platform);
if (dupsByPlatform.size === 0) {
this.#duplicates.delete(moduleName);
}
}
computeConflicts() {
const conflicts = [];
for (const [id, dupsByPlatform] of this.#duplicates.entries()) {
for (const [platform, conflictingModules] of dupsByPlatform) {
conflicts.push({
absolutePaths: [...conflictingModules.keys()]
.map((modulePath) => this.#pathUtils.normalToAbsolute(modulePath))
.sort(),
id,
platform:
platform === _constants.default.GENERIC_PLATFORM ? null : platform,
type: "duplicate",
});
}
}
for (const [id, data] of this.#map) {
const conflictPaths = new Set();
const basePaths = [];
for (const basePlatform of [
_constants.default.NATIVE_PLATFORM,
_constants.default.GENERIC_PLATFORM,
]) {
if (data[basePlatform] == null) {
continue;
}
const basePath = data[basePlatform][0];
basePaths.push(basePath);
const basePathDir = _path.default.dirname(basePath);
for (const platform of Object.keys(data)) {
if (
platform === basePlatform ||
platform === _constants.default.GENERIC_PLATFORM
) {
continue;
}
const platformPath = data[platform][0];
if (_path.default.dirname(platformPath) !== basePathDir) {
conflictPaths.add(platformPath);
}
}
}
if (conflictPaths.size) {
conflicts.push({
absolutePaths: [...new Set([...conflictPaths, ...basePaths])]
.map((modulePath) => this.#pathUtils.normalToAbsolute(modulePath))
.sort(),
id,
platform: null,
type: "shadowing",
});
}
}
conflicts.sort(
(0, _sorting.chainComparators)(
(a, b) => (0, _sorting.compareStrings)(a.type, b.type),
(a, b) => (0, _sorting.compareStrings)(a.id, b.id),
(a, b) => (0, _sorting.compareStrings)(a.platform, b.platform),
),
);
return conflicts;
}
getCacheKey() {
return JSON.stringify([
this.#enableHastePackages,
this.#hasteImplModulePath != null
? require(this.#hasteImplModulePath).getCacheKey()
: null,
[...this.#platforms].sort(),
]);
}
getWorker() {
return {
worker: {
modulePath: require.resolve("./haste/worker.js"),
setupArgs: {
hasteImplModulePath: this.#hasteImplModulePath ?? null,
},
},
filter: ({ isNodeModules, normalPath }) => {
if (isNodeModules) {
return false;
}
if (PACKAGE_JSON.test(normalPath)) {
return this.#enableHastePackages;
}
return this.#hasteImplModulePath != null;
},
};
}
}
exports.default = HastePlugin;

View File

@@ -0,0 +1,514 @@
/**
* 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 {
Console,
DuplicatesIndex,
DuplicatesSet,
FileMapPlugin,
FileMapPluginInitOptions,
FileMapPluginWorker,
HasteConflict,
HasteMap,
HasteMapItem,
HasteMapItemMetadata,
HTypeValue,
Path,
PerfLogger,
ReadonlyFileSystemChanges,
} from '../flow-types';
import H from '../constants';
import {RootPathUtils} from '../lib/RootPathUtils';
import {chainComparators, compareStrings} from '../lib/sorting';
import {DuplicateHasteCandidatesError} from './haste/DuplicateHasteCandidatesError';
import getPlatformExtension from './haste/getPlatformExtension';
import {HasteConflictsError} from './haste/HasteConflictsError';
import path from 'path';
const EMPTY_OBJ: Readonly<{[string]: HasteMapItemMetadata}> = {};
const EMPTY_MAP: ReadonlyMap<string, DuplicatesSet> = new Map();
const PACKAGE_JSON = /(?:[/\\]|^)package\.json$/;
// Periodically yield to the event loop to allow parallel I/O, etc.
// Based on 200k files taking up to 800ms => max 40ms between yields.
const YIELD_EVERY_NUM_HASTE_FILES = 10000;
export type HasteMapOptions = Readonly<{
console?: ?Console,
enableHastePackages: boolean,
hasteImplModulePath: ?string,
perfLogger?: ?PerfLogger,
platforms: ReadonlySet<string>,
rootDir: Path,
failValidationOnConflicts: boolean,
}>;
export default class HastePlugin
implements HasteMap, FileMapPlugin<null, string | null>
{
+name: 'haste' = 'haste';
+#console: ?Console;
+#duplicates: DuplicatesIndex = new Map();
+#enableHastePackages: boolean;
+#failValidationOnConflicts: boolean;
#getModuleNameByPath: string => ?string;
+#hasteImplModulePath: ?string;
+#map: Map<string, HasteMapItem> = new Map();
+#pathUtils: RootPathUtils;
+#perfLogger: ?PerfLogger;
+#platforms: ReadonlySet<string>;
+#rootDir: Path;
constructor(options: HasteMapOptions) {
this.#console = options.console ?? global.console;
this.#enableHastePackages = options.enableHastePackages;
this.#hasteImplModulePath = options.hasteImplModulePath;
this.#perfLogger = options.perfLogger;
this.#platforms = options.platforms;
this.#rootDir = options.rootDir;
this.#pathUtils = new RootPathUtils(options.rootDir);
this.#failValidationOnConflicts = options.failValidationOnConflicts;
}
async initialize({
files,
}: FileMapPluginInitOptions<null, string | null>): Promise<void> {
this.#perfLogger?.point('constructHasteMap_start');
let hasteFiles = 0;
for (const {
baseName,
canonicalPath,
pluginData: hasteId,
} of files.fileIterator({
// Symlinks and node_modules are never Haste modules or packages.
includeNodeModules: false,
includeSymlinks: false,
})) {
if (hasteId == null) {
continue;
}
this.setModule(hasteId, [
canonicalPath,
this.#enableHastePackages && baseName === 'package.json'
? H.PACKAGE
: H.MODULE,
]);
if (++hasteFiles % YIELD_EVERY_NUM_HASTE_FILES === 0) {
await new Promise(setImmediate);
}
}
this.#getModuleNameByPath = mixedPath => {
const result = files.lookup(mixedPath);
return result.exists &&
result.type === 'f' &&
typeof result.pluginData === 'string'
? result.pluginData
: null;
};
this.#perfLogger?.point('constructHasteMap_end');
this.#perfLogger?.annotate({int: {hasteFiles}});
}
getSerializableSnapshot(): null {
// Haste is not serialised, but built from traversing the file metadata
// on each run. This turns out to have comparable performance to
// serialisation, at least when Haste is dense, and makes for a much
// smaller cache.
return null;
}
getModule(
name: string,
platform?: ?string,
supportsNativePlatform?: ?boolean,
type?: ?HTypeValue,
): ?Path {
const module = this.#getModuleMetadata(
name,
platform,
!!supportsNativePlatform,
);
/* $FlowFixMe[invalid-compare] Error discovered during Constant Condition
* roll out. See https://fburl.com/workplace/4oq3zi07. */
if (module && module[H.TYPE] === (type ?? H.MODULE)) {
const modulePath = module[H.PATH];
return modulePath && this.#pathUtils.normalToAbsolute(modulePath);
}
return null;
}
getModuleNameByPath(mixedPath: Path): ?string {
if (this.#getModuleNameByPath == null) {
throw new Error(
'HastePlugin has not been initialized before getModuleNameByPath',
);
}
return this.#getModuleNameByPath(mixedPath) ?? null;
}
getPackage(
name: string,
platform: ?string,
_supportsNativePlatform?: ?boolean,
): ?Path {
return this.getModule(name, platform, null, H.PACKAGE);
}
/**
* When looking up a module's data, we walk through each eligible platform for
* the query. For each platform, we want to check if there are known
* duplicates for that name+platform pair. The duplication logic normally
* removes elements from the `map` object, but we want to check upfront to be
* extra sure. If metadata exists both in the `duplicates` object and the
* `map`, this would be a bug.
*/
#getModuleMetadata(
name: string,
platform: ?string,
supportsNativePlatform: boolean,
): HasteMapItemMetadata | null {
const map = this.#map.get(name) || EMPTY_OBJ;
const dupMap = this.#duplicates.get(name) || EMPTY_MAP;
if (platform != null) {
this.#assertNoDuplicates(
name,
platform,
supportsNativePlatform,
dupMap.get(platform),
);
if (map[platform] != null) {
return map[platform];
}
}
if (supportsNativePlatform) {
this.#assertNoDuplicates(
name,
H.NATIVE_PLATFORM,
supportsNativePlatform,
dupMap.get(H.NATIVE_PLATFORM),
);
if (map[H.NATIVE_PLATFORM]) {
return map[H.NATIVE_PLATFORM];
}
}
this.#assertNoDuplicates(
name,
H.GENERIC_PLATFORM,
supportsNativePlatform,
dupMap.get(H.GENERIC_PLATFORM),
);
if (map[H.GENERIC_PLATFORM]) {
return map[H.GENERIC_PLATFORM];
}
return null;
}
#assertNoDuplicates(
name: string,
platform: string,
supportsNativePlatform: boolean,
relativePathSet: ?DuplicatesSet,
): void {
if (relativePathSet == null) {
return;
}
const duplicates = new Map<string, number>();
for (const [relativePath, type] of relativePathSet) {
const duplicatePath = this.#pathUtils.normalToAbsolute(relativePath);
duplicates.set(duplicatePath, type);
}
throw new DuplicateHasteCandidatesError(
name,
platform,
supportsNativePlatform,
duplicates,
);
}
onChanged(delta: ReadonlyFileSystemChanges<?string>): void {
// Process removals first so that moves aren't treated as duplicates.
for (const [canonicalPath, maybeHasteId] of delta.removedFiles) {
this.#onRemovedFile(canonicalPath, maybeHasteId);
}
for (const [canonicalPath, maybeHasteId] of delta.addedFiles) {
this.#onNewFile(canonicalPath, maybeHasteId);
}
}
#onNewFile(canonicalPath: string, id: ?string) {
if (id == null) {
// Not a Haste module or package
return;
}
const module: HasteMapItemMetadata = [
canonicalPath,
this.#enableHastePackages &&
path.basename(canonicalPath) === 'package.json'
? H.PACKAGE
: H.MODULE,
];
this.setModule(id, module);
}
setModule(id: string, module: HasteMapItemMetadata) {
let hasteMapItem = this.#map.get(id);
if (!hasteMapItem) {
// $FlowFixMe[unclear-type] - Add type coverage
hasteMapItem = Object.create(null) as any;
this.#map.set(id, hasteMapItem);
}
const platform =
getPlatformExtension(module[H.PATH], this.#platforms) ||
H.GENERIC_PLATFORM;
const existingModule = hasteMapItem[platform];
if (existingModule && existingModule[H.PATH] !== module[H.PATH]) {
if (this.#console) {
this.#console.warn(
[
'metro-file-map: Haste module naming collision: ' + id,
' The following files share their name; please adjust your hasteImpl:',
' * <rootDir>' + path.sep + existingModule[H.PATH],
' * <rootDir>' + path.sep + module[H.PATH],
'',
].join('\n'),
);
}
// We do NOT want consumers to use a module that is ambiguous.
delete hasteMapItem[platform];
if (Object.keys(hasteMapItem).length === 0) {
this.#map.delete(id);
}
let dupsByPlatform = this.#duplicates.get(id);
if (dupsByPlatform == null) {
dupsByPlatform = new Map();
this.#duplicates.set(id, dupsByPlatform);
}
const dups = new Map([
[module[H.PATH], module[H.TYPE]],
[existingModule[H.PATH], existingModule[H.TYPE]],
]);
dupsByPlatform.set(platform, dups);
return;
}
const dupsByPlatform = this.#duplicates.get(id);
if (dupsByPlatform != null) {
const dups = dupsByPlatform.get(platform);
if (dups != null) {
dups.set(module[H.PATH], module[H.TYPE]);
}
return;
}
hasteMapItem[platform] = module;
}
#onRemovedFile(canonicalPath: string, moduleName: ?string) {
if (moduleName == null) {
// Not a Haste module or package
return;
}
const platform =
getPlatformExtension(canonicalPath, this.#platforms) ||
H.GENERIC_PLATFORM;
const hasteMapItem = this.#map.get(moduleName);
if (hasteMapItem != null) {
delete hasteMapItem[platform];
if (Object.keys(hasteMapItem).length === 0) {
this.#map.delete(moduleName);
} else {
this.#map.set(moduleName, hasteMapItem);
}
}
this.#recoverDuplicates(moduleName, canonicalPath);
}
assertValid(): void {
if (!this.#failValidationOnConflicts) {
return;
}
const conflicts = this.computeConflicts();
if (conflicts.length > 0) {
throw new HasteConflictsError(conflicts);
}
}
/**
* This function should be called when the file under `filePath` is removed
* or changed. When that happens, we want to figure out if that file was
* part of a group of files that had the same ID. If it was, we want to
* remove it from the group. Furthermore, if there is only one file
* remaining in the group, then we want to restore that single file as the
* correct resolution for its ID, and cleanup the duplicates index.
*/
#recoverDuplicates(moduleName: string, relativeFilePath: string) {
let dupsByPlatform = this.#duplicates.get(moduleName);
if (dupsByPlatform == null) {
return;
}
const platform =
getPlatformExtension(relativeFilePath, this.#platforms) ||
H.GENERIC_PLATFORM;
let dups = dupsByPlatform.get(platform);
if (dups == null) {
return;
}
dupsByPlatform = new Map(dupsByPlatform);
this.#duplicates.set(moduleName, dupsByPlatform);
dups = new Map(dups);
dupsByPlatform.set(platform, dups);
dups.delete(relativeFilePath);
if (dups.size !== 1) {
return;
}
const uniqueModule = dups.entries().next().value;
if (!uniqueModule) {
return;
}
let dedupMap: ?HasteMapItem = this.#map.get(moduleName);
if (dedupMap == null) {
dedupMap = Object.create(null) as HasteMapItem;
this.#map.set(moduleName, dedupMap);
}
dedupMap[platform] = uniqueModule;
dupsByPlatform.delete(platform);
if (dupsByPlatform.size === 0) {
this.#duplicates.delete(moduleName);
}
}
computeConflicts(): Array<HasteConflict> {
const conflicts: Array<HasteConflict> = [];
// Add literal duplicates tracked in the #duplicates map
for (const [id, dupsByPlatform] of this.#duplicates.entries()) {
for (const [platform, conflictingModules] of dupsByPlatform) {
conflicts.push({
absolutePaths: [...conflictingModules.keys()]
.map(modulePath => this.#pathUtils.normalToAbsolute(modulePath))
// Sort for ease of testing
.sort(),
id,
platform: platform === H.GENERIC_PLATFORM ? null : platform,
type: 'duplicate',
});
}
}
// Add cases of "shadowing at a distance": a module with a platform suffix and
// a module with a lower priority platform suffix (or no suffix), in different
// directories.
for (const [id, data] of this.#map) {
const conflictPaths = new Set<string>();
const basePaths = [];
for (const basePlatform of [H.NATIVE_PLATFORM, H.GENERIC_PLATFORM]) {
if (data[basePlatform] == null) {
continue;
}
const basePath = data[basePlatform][0];
basePaths.push(basePath);
const basePathDir = path.dirname(basePath);
// Find all platforms that can shadow basePlatform
// Given that X.(specific platform).js > x.native.js > X.js
// and basePlatform is either 'native' or generic (no platform).
for (const platform of Object.keys(data)) {
if (
platform === basePlatform ||
platform === H.GENERIC_PLATFORM /* lowest priority */
) {
continue;
}
const platformPath = data[platform][0];
if (path.dirname(platformPath) !== basePathDir) {
conflictPaths.add(platformPath);
}
}
}
if (conflictPaths.size) {
conflicts.push({
absolutePaths: [...new Set([...conflictPaths, ...basePaths])]
.map(modulePath => this.#pathUtils.normalToAbsolute(modulePath))
// Sort for ease of testing
.sort(),
id,
platform: null,
type: 'shadowing',
});
}
}
// Sort for ease of testing
conflicts.sort(
chainComparators(
(a, b) => compareStrings(a.type, b.type),
(a, b) => compareStrings(a.id, b.id),
(a, b) => compareStrings(a.platform, b.platform),
),
);
return conflicts;
}
getCacheKey(): string {
return JSON.stringify([
this.#enableHastePackages,
this.#hasteImplModulePath != null
? // $FlowFixMe[unsupported-syntax] - dynamic require
require(this.#hasteImplModulePath).getCacheKey()
: null,
[...this.#platforms].sort(),
]);
}
getWorker(): FileMapPluginWorker {
return {
worker: {
modulePath: require.resolve('./haste/worker.js'),
setupArgs: {
hasteImplModulePath: this.#hasteImplModulePath ?? null,
},
},
filter: ({isNodeModules, normalPath}) => {
if (isNodeModules) {
return false;
}
if (PACKAGE_JSON.test(normalPath)) {
return this.#enableHastePackages;
}
return this.#hasteImplModulePath != null;
},
};
}
}

View File

@@ -0,0 +1,48 @@
/**
* 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<<d9402d4670982b1e675e1edd9201cf75>>
*
* 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 {
FileMapPlugin,
FileMapPluginInitOptions,
FileMapPluginWorker,
MockMap as IMockMap,
Path,
RawMockMap,
ReadonlyFileSystemChanges,
} from '../flow-types';
export declare const CACHE_VERSION: 2;
export declare type CACHE_VERSION = typeof CACHE_VERSION;
export type MockMapOptions = Readonly<{
console: typeof console;
mocksPattern: RegExp;
rawMockMap?: RawMockMap;
rootDir: Path;
throwOnModuleCollision: boolean;
}>;
declare class MockPlugin implements FileMapPlugin<RawMockMap, void>, IMockMap {
readonly name: 'mocks';
constructor($$PARAM_0$$: MockMapOptions);
initialize($$PARAM_0$$: FileMapPluginInitOptions<RawMockMap>): Promise<void>;
getMockModule(name: string): null | undefined | Path;
onChanged(delta: ReadonlyFileSystemChanges<null | undefined | void>): void;
getSerializableSnapshot(): RawMockMap;
assertValid(): void;
getCacheKey(): string;
getWorker(): null | undefined | FileMapPluginWorker;
}
export default MockPlugin;

179
node_modules/metro-file-map/src/plugins/MockPlugin.js generated vendored Normal file
View File

@@ -0,0 +1,179 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = exports.CACHE_VERSION = void 0;
var _normalizePathSeparatorsToPosix = _interopRequireDefault(
require("../lib/normalizePathSeparatorsToPosix"),
);
var _normalizePathSeparatorsToSystem = _interopRequireDefault(
require("../lib/normalizePathSeparatorsToSystem"),
);
var _RootPathUtils = require("../lib/RootPathUtils");
var _getMockName = _interopRequireDefault(require("./mocks/getMockName"));
var _nullthrows = _interopRequireDefault(require("nullthrows"));
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const CACHE_VERSION = (exports.CACHE_VERSION = 2);
class MockPlugin {
name = "mocks";
#mocksPattern;
#raw;
#rootDir;
#pathUtils;
#console;
#throwOnModuleCollision;
constructor({
console,
mocksPattern,
rawMockMap = {
duplicates: new Map(),
mocks: new Map(),
version: CACHE_VERSION,
},
rootDir,
throwOnModuleCollision,
}) {
this.#mocksPattern = mocksPattern;
if (rawMockMap.version !== CACHE_VERSION) {
throw new Error("Incompatible state passed to MockPlugin");
}
this.#raw = rawMockMap;
this.#rootDir = rootDir;
this.#console = console;
this.#pathUtils = new _RootPathUtils.RootPathUtils(rootDir);
this.#throwOnModuleCollision = throwOnModuleCollision;
}
async initialize({ files, pluginState }) {
if (pluginState != null && pluginState.version === this.#raw.version) {
this.#raw = pluginState;
} else {
for (const { canonicalPath } of files.fileIterator({
includeNodeModules: false,
includeSymlinks: false,
})) {
this.#onFileAdded(canonicalPath);
}
}
}
getMockModule(name) {
const mockPosixRelativePath =
this.#raw.mocks.get(name) || this.#raw.mocks.get(name + "/index");
if (typeof mockPosixRelativePath !== "string") {
return null;
}
return this.#pathUtils.normalToAbsolute(
(0, _normalizePathSeparatorsToSystem.default)(mockPosixRelativePath),
);
}
onChanged(delta) {
for (const [canonicalPath] of delta.removedFiles) {
this.#onFileRemoved(canonicalPath);
}
for (const [canonicalPath] of delta.addedFiles) {
this.#onFileAdded(canonicalPath);
}
}
#onFileAdded(canonicalPath) {
const absoluteFilePath = this.#pathUtils.normalToAbsolute(canonicalPath);
if (!this.#mocksPattern.test(absoluteFilePath)) {
return;
}
const mockName = (0, _getMockName.default)(absoluteFilePath);
const posixRelativePath = (0, _normalizePathSeparatorsToPosix.default)(
canonicalPath,
);
const existingMockPosixPath = this.#raw.mocks.get(mockName);
if (existingMockPosixPath != null) {
if (existingMockPosixPath !== posixRelativePath) {
let duplicates = this.#raw.duplicates.get(mockName);
if (duplicates == null) {
duplicates = new Set([existingMockPosixPath, posixRelativePath]);
this.#raw.duplicates.set(mockName, duplicates);
} else {
duplicates.add(posixRelativePath);
}
this.#console.warn(this.#getMessageForDuplicates(mockName, duplicates));
}
}
this.#raw.mocks.set(mockName, posixRelativePath);
}
#onFileRemoved(canonicalPath) {
const absoluteFilePath = this.#pathUtils.normalToAbsolute(canonicalPath);
if (!this.#mocksPattern.test(absoluteFilePath)) {
return;
}
const mockName = (0, _getMockName.default)(absoluteFilePath);
const duplicates = this.#raw.duplicates.get(mockName);
if (duplicates != null) {
const posixRelativePath = (0, _normalizePathSeparatorsToPosix.default)(
canonicalPath,
);
duplicates.delete(posixRelativePath);
if (duplicates.size === 1) {
this.#raw.duplicates.delete(mockName);
}
const remaining = (0, _nullthrows.default)(
duplicates.values().next().value,
);
this.#raw.mocks.set(mockName, remaining);
} else {
this.#raw.mocks.delete(mockName);
}
}
getSerializableSnapshot() {
return {
duplicates: new Map(
[...this.#raw.duplicates].map(([k, v]) => [k, new Set(v)]),
),
mocks: new Map(this.#raw.mocks),
version: this.#raw.version,
};
}
assertValid() {
if (!this.#throwOnModuleCollision) {
return;
}
const errors = [];
for (const [mockName, relativePosixPaths] of this.#raw.duplicates) {
errors.push(this.#getMessageForDuplicates(mockName, relativePosixPaths));
}
if (errors.length > 0) {
throw new Error(
`Mock map has ${errors.length} error${errors.length > 1 ? "s" : ""}:\n${errors.join("\n")}`,
);
}
}
#getMessageForDuplicates(mockName, relativePosixPaths) {
return (
"Duplicate manual mock found for `" +
mockName +
"`:\n" +
[...relativePosixPaths]
.map(
(relativePosixPath) =>
" * <rootDir>" +
_path.default.sep +
this.#pathUtils.absoluteToNormal(
(0, _normalizePathSeparatorsToSystem.default)(relativePosixPath),
) +
"\n",
)
.join("")
);
}
getCacheKey() {
return (
this.#mocksPattern.source.replaceAll("\\\\", "\\/") +
"," +
this.#mocksPattern.flags
);
}
getWorker() {
return null;
}
}
exports.default = MockPlugin;

View File

@@ -0,0 +1,221 @@
/**
* 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 {
FileMapPlugin,
FileMapPluginInitOptions,
FileMapPluginWorker,
MockMap as IMockMap,
Path,
RawMockMap,
ReadonlyFileSystemChanges,
} from '../flow-types';
import normalizePathSeparatorsToPosix from '../lib/normalizePathSeparatorsToPosix';
import normalizePathSeparatorsToSystem from '../lib/normalizePathSeparatorsToSystem';
import {RootPathUtils} from '../lib/RootPathUtils';
import getMockName from './mocks/getMockName';
import nullthrows from 'nullthrows';
import path from 'path';
export const CACHE_VERSION = 2;
export type MockMapOptions = Readonly<{
console: typeof console,
mocksPattern: RegExp,
rawMockMap?: RawMockMap,
rootDir: Path,
throwOnModuleCollision: boolean,
}>;
export default class MockPlugin
implements FileMapPlugin<RawMockMap, void>, IMockMap
{
+name: 'mocks' = 'mocks';
+#mocksPattern: RegExp;
#raw: RawMockMap;
+#rootDir: Path;
+#pathUtils: RootPathUtils;
+#console: typeof console;
#throwOnModuleCollision: boolean;
constructor({
console,
mocksPattern,
rawMockMap = {
duplicates: new Map(),
mocks: new Map(),
version: CACHE_VERSION,
},
rootDir,
throwOnModuleCollision,
}: MockMapOptions) {
this.#mocksPattern = mocksPattern;
if (rawMockMap.version !== CACHE_VERSION) {
throw new Error('Incompatible state passed to MockPlugin');
}
this.#raw = rawMockMap;
this.#rootDir = rootDir;
this.#console = console;
this.#pathUtils = new RootPathUtils(rootDir);
this.#throwOnModuleCollision = throwOnModuleCollision;
}
async initialize({
files,
pluginState,
}: FileMapPluginInitOptions<RawMockMap>): Promise<void> {
if (pluginState != null && pluginState.version === this.#raw.version) {
// Use cached state directly if available
this.#raw = pluginState;
} else {
// Otherwise, traverse all files to rebuild
for (const {canonicalPath} of files.fileIterator({
includeNodeModules: false,
includeSymlinks: false,
})) {
this.#onFileAdded(canonicalPath);
}
}
}
getMockModule(name: string): ?Path {
const mockPosixRelativePath =
this.#raw.mocks.get(name) || this.#raw.mocks.get(name + '/index');
if (typeof mockPosixRelativePath !== 'string') {
return null;
}
return this.#pathUtils.normalToAbsolute(
normalizePathSeparatorsToSystem(mockPosixRelativePath),
);
}
onChanged(delta: ReadonlyFileSystemChanges<?void>): void {
// Process removals first so that moves aren't treated as duplicates.
for (const [canonicalPath] of delta.removedFiles) {
this.#onFileRemoved(canonicalPath);
}
for (const [canonicalPath] of delta.addedFiles) {
this.#onFileAdded(canonicalPath);
}
}
#onFileAdded(canonicalPath: Path): void {
const absoluteFilePath = this.#pathUtils.normalToAbsolute(canonicalPath);
if (!this.#mocksPattern.test(absoluteFilePath)) {
return;
}
const mockName = getMockName(absoluteFilePath);
const posixRelativePath = normalizePathSeparatorsToPosix(canonicalPath);
const existingMockPosixPath = this.#raw.mocks.get(mockName);
if (existingMockPosixPath != null) {
if (existingMockPosixPath !== posixRelativePath) {
let duplicates = this.#raw.duplicates.get(mockName);
if (duplicates == null) {
duplicates = new Set([existingMockPosixPath, posixRelativePath]);
this.#raw.duplicates.set(mockName, duplicates);
} else {
duplicates.add(posixRelativePath);
}
this.#console.warn(this.#getMessageForDuplicates(mockName, duplicates));
}
}
// If there are duplicates and we don't throw, the latest mock wins.
// This is to preserve backwards compatibility, but it's unpredictable.
this.#raw.mocks.set(mockName, posixRelativePath);
}
#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(canonicalPath);
duplicates.delete(posixRelativePath);
if (duplicates.size === 1) {
this.#raw.duplicates.delete(mockName);
}
// Set the mock to a remaining duplicate. Should never be empty.
const remaining = nullthrows(duplicates.values().next().value);
this.#raw.mocks.set(mockName, remaining);
} else {
this.#raw.mocks.delete(mockName);
}
}
getSerializableSnapshot(): RawMockMap {
return {
duplicates: new Map(
[...this.#raw.duplicates].map(([k, v]) => [k, new Set(v)]),
),
mocks: new Map(this.#raw.mocks),
version: this.#raw.version,
};
}
assertValid(): void {
if (!this.#throwOnModuleCollision) {
return;
}
// Throw an aggregate error for each duplicate.
const errors = [];
for (const [mockName, relativePosixPaths] of this.#raw.duplicates) {
errors.push(this.#getMessageForDuplicates(mockName, relativePosixPaths));
}
if (errors.length > 0) {
throw new Error(
`Mock map has ${errors.length} error${errors.length > 1 ? 's' : ''}:\n${errors.join('\n')}`,
);
}
}
#getMessageForDuplicates(
mockName: string,
relativePosixPaths: ReadonlySet<string>,
): string {
return (
'Duplicate manual mock found for `' +
mockName +
'`:\n' +
[...relativePosixPaths]
.map(
relativePosixPath =>
' * <rootDir>' +
path.sep +
this.#pathUtils.absoluteToNormal(
normalizePathSeparatorsToSystem(relativePosixPath),
) +
'\n',
)
.join('')
);
}
getCacheKey(): string {
return (
this.#mocksPattern.source.replaceAll('\\\\', '\\/') +
',' +
this.#mocksPattern.flags
);
}
getWorker(): ?FileMapPluginWorker {
return null;
}
}

View File

@@ -0,0 +1,14 @@
/**
* 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.
*
* @generated by js1 build metro-ts-defs / yarn run build-ts-defs
*/
declare const dependencyExtractor: {
extract(code: string): Set<string>;
};
export = dependencyExtractor;

View File

@@ -0,0 +1,73 @@
"use strict";
const NOT_A_DOT = "(?<!\\.\\s*)";
const CAPTURE_STRING_LITERAL = (pos) => `([\`'"])([^'"\`]*?)(?:\\${pos})`;
const WORD_SEPARATOR = "\\b";
const LEFT_PARENTHESIS = "\\(";
const RIGHT_PARENTHESIS = "\\)";
const WHITESPACE = "\\s*";
const OPTIONAL_COMMA = "(:?,\\s*)?";
function createRegExp(parts, flags) {
return new RegExp(parts.join(""), flags);
}
function alternatives(...parts) {
return `(?:${parts.join("|")})`;
}
function functionCallStart(...names) {
return [
NOT_A_DOT,
WORD_SEPARATOR,
alternatives(...names),
WHITESPACE,
LEFT_PARENTHESIS,
WHITESPACE,
];
}
const BLOCK_COMMENT_RE = /\/\*[^]*?\*\//g;
const LINE_COMMENT_RE = /\/\/.*/g;
const REQUIRE_OR_DYNAMIC_IMPORT_RE = createRegExp(
[
...functionCallStart("require", "import"),
CAPTURE_STRING_LITERAL(1),
WHITESPACE,
OPTIONAL_COMMA,
RIGHT_PARENTHESIS,
],
"g",
);
const IMPORT_OR_EXPORT_RE = createRegExp(
[
"\\b(?:import|export)\\s+(?!type(?:of)?\\s+)(?:[^'\"]+\\s+from\\s+)?",
CAPTURE_STRING_LITERAL(1),
],
"g",
);
const JEST_EXTENSIONS_RE = createRegExp(
[
...functionCallStart(
"jest\\s*\\.\\s*(?:requireActual|requireMock|genMockFromModule|createMockFromModule)",
),
CAPTURE_STRING_LITERAL(1),
WHITESPACE,
OPTIONAL_COMMA,
RIGHT_PARENTHESIS,
],
"g",
);
function extract(code) {
const dependencies = new Set();
const addDependency = (match, _, dep) => {
dependencies.add(dep);
return match;
};
code
.replace(BLOCK_COMMENT_RE, "")
.replace(LINE_COMMENT_RE, "")
.replace(IMPORT_OR_EXPORT_RE, addDependency)
.replace(REQUIRE_OR_DYNAMIC_IMPORT_RE, addDependency)
.replace(JEST_EXTENSIONS_RE, addDependency);
return dependencies;
}
module.exports = {
extract,
};

View File

@@ -0,0 +1,101 @@
/**
* 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
* @flow strict
*/
// Required by worker, must be commonjs
/* eslint-disable import/no-commonjs */
'use strict';
const NOT_A_DOT = '(?<!\\.\\s*)';
const CAPTURE_STRING_LITERAL = (pos /*: number */) =>
`([\`'"])([^'"\`]*?)(?:\\${pos})`;
const WORD_SEPARATOR = '\\b';
const LEFT_PARENTHESIS = '\\(';
const RIGHT_PARENTHESIS = '\\)';
const WHITESPACE = '\\s*';
const OPTIONAL_COMMA = '(:?,\\s*)?';
function createRegExp(parts /*: ReadonlyArray<string> */, flags /*: string */) {
return new RegExp(parts.join(''), flags);
}
function alternatives(...parts /*: ReadonlyArray<string> */) {
return `(?:${parts.join('|')})`;
}
function functionCallStart(...names /*: ReadonlyArray<string> */) {
return [
NOT_A_DOT,
WORD_SEPARATOR,
alternatives(...names),
WHITESPACE,
LEFT_PARENTHESIS,
WHITESPACE,
];
}
const BLOCK_COMMENT_RE = /\/\*[^]*?\*\//g;
const LINE_COMMENT_RE = /\/\/.*/g;
const REQUIRE_OR_DYNAMIC_IMPORT_RE = createRegExp(
[
...functionCallStart('require', 'import'),
CAPTURE_STRING_LITERAL(1),
WHITESPACE,
OPTIONAL_COMMA,
RIGHT_PARENTHESIS,
],
'g',
);
const IMPORT_OR_EXPORT_RE = createRegExp(
[
'\\b(?:import|export)\\s+(?!type(?:of)?\\s+)(?:[^\'"]+\\s+from\\s+)?',
CAPTURE_STRING_LITERAL(1),
],
'g',
);
const JEST_EXTENSIONS_RE = createRegExp(
[
...functionCallStart(
'jest\\s*\\.\\s*(?:requireActual|requireMock|genMockFromModule|createMockFromModule)',
),
CAPTURE_STRING_LITERAL(1),
WHITESPACE,
OPTIONAL_COMMA,
RIGHT_PARENTHESIS,
],
'g',
);
function extract(code /*: string */) /*: Set<string> */ {
const dependencies /*: Set<string> */ = new Set();
const addDependency = (
match /*: string */,
_ /*: string */,
dep /*: string */,
) => {
dependencies.add(dep);
return match;
};
code
.replace(BLOCK_COMMENT_RE, '')
.replace(LINE_COMMENT_RE, '')
.replace(IMPORT_OR_EXPORT_RE, addDependency)
.replace(REQUIRE_OR_DYNAMIC_IMPORT_RE, addDependency)
.replace(JEST_EXTENSIONS_RE, addDependency);
return dependencies;
}
module.exports = {extract};

View File

@@ -0,0 +1,24 @@
/**
* 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 {
MetadataWorker,
V8Serializable,
WorkerMessage,
} from '../../flow-types';
declare class DependencyExtractorWorker implements MetadataWorker {
constructor(opts: Readonly<{dependencyExtractor: null | undefined | string}>);
processFile(
data: WorkerMessage,
utils: Readonly<{getContent: () => Buffer}>,
): V8Serializable;
}
export = DependencyExtractorWorker;

View File

@@ -0,0 +1,24 @@
"use strict";
const defaultDependencyExtractor = require("./dependencyExtractor");
module.exports = class DependencyExtractorWorker {
#dependencyExtractor;
constructor({ dependencyExtractor }) {
if (dependencyExtractor != null) {
this.#dependencyExtractor = require(dependencyExtractor);
}
}
processFile(data, utils) {
const content = utils.getContent().toString();
const { filePath } = data;
const dependencies =
this.#dependencyExtractor != null
? this.#dependencyExtractor.extract(
content,
filePath,
defaultDependencyExtractor.extract,
)
: defaultDependencyExtractor.extract(content);
return Array.from(dependencies);
}
};

View File

@@ -0,0 +1,53 @@
/**
* 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
*/
/* eslint-disable import/no-commonjs */
'use strict';
const defaultDependencyExtractor = require('./dependencyExtractor');
/*::
import type {MetadataWorker, WorkerMessage, V8Serializable, DependencyExtractor} from '../../flow-types';
*/
module.exports = class DependencyExtractorWorker /*:: implements MetadataWorker */ {
/*:: + */ #dependencyExtractor /*: ?DependencyExtractor */;
constructor(
{dependencyExtractor} /*: Readonly<{dependencyExtractor: ?string}> */,
) {
if (dependencyExtractor != null) {
// $FlowFixMe[unsupported-syntax] - dynamic require
this.#dependencyExtractor = require(dependencyExtractor);
}
}
processFile(
data /*: WorkerMessage */,
utils /*: Readonly<{getContent: () => Buffer}> */,
) /*: V8Serializable */ {
const content = utils.getContent().toString();
const {filePath} = data;
const dependencies =
this.#dependencyExtractor != null
? this.#dependencyExtractor.extract(
content,
filePath,
defaultDependencyExtractor.extract,
)
: defaultDependencyExtractor.extract(content);
// Return as array (PerFileData type)
return Array.from(dependencies);
}
};

View File

@@ -0,0 +1,31 @@
/**
* 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<<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';
export declare class DuplicateHasteCandidatesError extends Error {
hasteName: string;
platform: string | null;
supportsNativePlatform: boolean;
duplicatesSet: DuplicatesSet;
constructor(
name: string,
platform: string,
supportsNativePlatform: boolean,
duplicatesSet: DuplicatesSet,
);
}

View File

@@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.DuplicateHasteCandidatesError = void 0;
var _constants = _interopRequireDefault(require("../../constants"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
class DuplicateHasteCandidatesError extends Error {
constructor(name, platform, supportsNativePlatform, duplicatesSet) {
const platformMessage = getPlatformMessage(platform);
super(
`The name \`${name}\` was looked up in the Haste module map. It ` +
"cannot be resolved, because there exists several different " +
"files, or packages, that provide a module for " +
`that particular name and platform. ${platformMessage} You must ` +
"delete or exclude files until there remains only one of these:\n\n" +
Array.from(duplicatesSet)
.map(
([dupFilePath, dupFileType]) =>
` * \`${dupFilePath}\` (${getTypeMessage(dupFileType)})\n`,
)
.sort()
.join(""),
);
this.hasteName = name;
this.platform = platform;
this.supportsNativePlatform = supportsNativePlatform;
this.duplicatesSet = duplicatesSet;
}
}
exports.DuplicateHasteCandidatesError = DuplicateHasteCandidatesError;
function getPlatformMessage(platform) {
if (platform === _constants.default.GENERIC_PLATFORM) {
return "The platform is generic (no extension).";
}
return `The platform extension is \`${platform}\`.`;
}
function getTypeMessage(type) {
switch (type) {
case _constants.default.MODULE:
return "module";
case _constants.default.PACKAGE:
return "package";
}
return "unknown";
}

View File

@@ -0,0 +1,65 @@
/**
* 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 {DuplicatesSet} from '../../flow-types';
import H from '../../constants';
export class DuplicateHasteCandidatesError extends Error {
hasteName: string;
platform: string | null;
supportsNativePlatform: boolean;
duplicatesSet: DuplicatesSet;
constructor(
name: string,
platform: string,
supportsNativePlatform: boolean,
duplicatesSet: DuplicatesSet,
) {
const platformMessage = getPlatformMessage(platform);
super(
`The name \`${name}\` was looked up in the Haste module map. It ` +
'cannot be resolved, because there exists several different ' +
'files, or packages, that provide a module for ' +
`that particular name and platform. ${platformMessage} You must ` +
'delete or exclude files until there remains only one of these:\n\n' +
Array.from(duplicatesSet)
.map(
([dupFilePath, dupFileType]) =>
` * \`${dupFilePath}\` (${getTypeMessage(dupFileType)})\n`,
)
.sort()
.join(''),
);
this.hasteName = name;
this.platform = platform;
this.supportsNativePlatform = supportsNativePlatform;
this.duplicatesSet = duplicatesSet;
}
}
function getPlatformMessage(platform: string) {
if (platform === H.GENERIC_PLATFORM) {
return 'The platform is generic (no extension).';
}
return `The platform extension is \`${platform}\`.`;
}
function getTypeMessage(type: number) {
switch (type) {
case H.MODULE:
return 'module';
case H.PACKAGE:
return 'package';
}
return 'unknown';
}

View File

@@ -0,0 +1,23 @@
/**
* 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<<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';
export declare class HasteConflictsError extends Error {
constructor(conflicts: ReadonlyArray<HasteConflict>);
getDetailedMessage(pathsRelativeToRoot: null | undefined | string): string;
}

View File

@@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.HasteConflictsError = void 0;
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
class HasteConflictsError extends Error {
#conflicts;
constructor(conflicts) {
super(
`Found ${conflicts.length} Haste conflict(s). Haste module IDs must be globally unique in the codebase.`,
);
this.#conflicts = conflicts;
}
getDetailedMessage(pathsRelativeToRoot) {
const messages = [];
const conflicts = this.#conflicts;
if (conflicts.some((conflict) => conflict.type === "duplicate")) {
messages.push(
'Advice: Resolve conflicts of type "duplicate" by renaming one or both of the conflicting modules, or by excluding conflicting paths from Haste.',
);
}
if (conflicts.some((conflict) => conflict.type === "shadowing")) {
messages.push(
'Advice: Resolve conflicts of type "shadowing" by moving the modules to the same folder, or by excluding conflicting paths from Haste.',
);
}
let index = 0;
for (const conflict of conflicts) {
const itemHeader = index + 1 + ". ";
const indent = " ".repeat(itemHeader.length + 2);
messages.push(
"\n" +
itemHeader +
conflict.id +
(conflict.platform != null ? `.${conflict.platform}` : "") +
` (${conflict.type})`,
);
for (const modulePath of conflict.absolutePaths) {
messages.push(
indent +
(pathsRelativeToRoot != null
? _path.default.relative(pathsRelativeToRoot, modulePath)
: modulePath),
);
}
++index;
}
return messages.join("\n");
}
}
exports.HasteConflictsError = HasteConflictsError;

View File

@@ -0,0 +1,62 @@
/**
* 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 {HasteConflict} from '../../flow-types';
import path from 'path';
export class HasteConflictsError extends Error {
#conflicts: ReadonlyArray<HasteConflict>;
constructor(conflicts: ReadonlyArray<HasteConflict>) {
super(
`Found ${conflicts.length} Haste conflict(s). Haste module IDs must be globally unique in the codebase.`,
);
this.#conflicts = conflicts;
}
getDetailedMessage(pathsRelativeToRoot: ?string): string {
const messages: Array<string> = [];
const conflicts = this.#conflicts;
if (conflicts.some(conflict => conflict.type === 'duplicate')) {
messages.push(
'Advice: Resolve conflicts of type "duplicate" by renaming one or both of the conflicting modules, or by excluding conflicting paths from Haste.',
);
}
if (conflicts.some(conflict => conflict.type === 'shadowing')) {
messages.push(
'Advice: Resolve conflicts of type "shadowing" by moving the modules to the same folder, or by excluding conflicting paths from Haste.',
);
}
let index = 0;
for (const conflict of conflicts) {
const itemHeader = index + 1 + '. ';
const indent = ' '.repeat(itemHeader.length + 2);
messages.push(
'\n' +
itemHeader +
conflict.id +
(conflict.platform != null ? `.${conflict.platform}` : '') +
` (${conflict.type})`,
);
for (const modulePath of conflict.absolutePaths) {
messages.push(
indent +
(pathsRelativeToRoot != null
? path.relative(pathsRelativeToRoot, modulePath)
: modulePath),
);
}
++index;
}
return messages.join('\n');
}
}

View File

@@ -0,0 +1,34 @@
/**
* 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
* @generated SignedSource<<f56c9fdb2fc1c692fa880c61a14ba1e3>>
*
* 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';
type Conflict = {
id: string;
platform: string | null;
absolutePaths: Array<string>;
type: 'duplicate' | 'shadowing';
};
export declare function computeHasteConflicts(
options: Readonly<{
duplicates: ReadonlyMap<
string,
ReadonlyMap<string, ReadonlyMap<string, number>>
>;
map: ReadonlyMap<string, HasteMapItem>;
rootDir: string;
}>,
): Array<Conflict>;

View File

@@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.computeHasteConflicts = computeHasteConflicts;
var _constants = _interopRequireDefault(require("../../constants"));
var _sorting = require("../../lib/sorting");
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function computeHasteConflicts(options) {
const { duplicates, map, rootDir } = options;
const conflicts = [];
for (const [id, dupsByPlatform] of duplicates.entries()) {
for (const [platform, conflictingModules] of dupsByPlatform) {
conflicts.push({
id,
platform:
platform === _constants.default.GENERIC_PLATFORM ? null : platform,
absolutePaths: [...conflictingModules.keys()]
.map((modulePath) => _path.default.resolve(rootDir, modulePath))
.sort(),
type: "duplicate",
});
}
}
for (const [id, data] of map) {
const conflictPaths = new Set();
const basePaths = [];
for (const basePlatform of [
_constants.default.NATIVE_PLATFORM,
_constants.default.GENERIC_PLATFORM,
]) {
if (data[basePlatform] == null) {
continue;
}
const basePath = data[basePlatform][0];
basePaths.push(basePath);
const basePathDir = _path.default.dirname(basePath);
for (const platform of Object.keys(data)) {
if (
platform === basePlatform ||
platform === _constants.default.GENERIC_PLATFORM
) {
continue;
}
const platformPath = data[platform][0];
if (_path.default.dirname(platformPath) !== basePathDir) {
conflictPaths.add(platformPath);
}
}
}
if (conflictPaths.size) {
conflicts.push({
id,
platform: null,
absolutePaths: [...new Set([...conflictPaths, ...basePaths])]
.map((modulePath) => _path.default.resolve(rootDir, modulePath))
.sort(),
type: "shadowing",
});
}
}
conflicts.sort(
(0, _sorting.chainComparators)(
(a, b) => (0, _sorting.compareStrings)(a.type, b.type),
(a, b) => (0, _sorting.compareStrings)(a.id, b.id),
(a, b) => (0, _sorting.compareStrings)(a.platform, b.platform),
),
);
return conflicts;
}

View File

@@ -0,0 +1,104 @@
/**
* 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
*/
import type {HasteMapItem} from '../../flow-types';
import H from '../../constants';
import {chainComparators, compareStrings} from '../../lib/sorting';
import path from 'path';
type Conflict = {
id: string,
platform: string | null,
absolutePaths: Array<string>,
type: 'duplicate' | 'shadowing',
};
export function computeHasteConflicts(
options: Readonly<{
duplicates: ReadonlyMap<
string,
ReadonlyMap<string, ReadonlyMap<string, number>>,
>,
map: ReadonlyMap<string, HasteMapItem>,
rootDir: string,
}>,
): Array<Conflict> {
const {duplicates, map, rootDir} = options;
const conflicts: Array<Conflict> = [];
// Add duplicates reported by metro-file-map
for (const [id, dupsByPlatform] of duplicates.entries()) {
for (const [platform, conflictingModules] of dupsByPlatform) {
conflicts.push({
id,
platform: platform === H.GENERIC_PLATFORM ? null : platform,
absolutePaths: [...conflictingModules.keys()]
.map(modulePath => path.resolve(rootDir, modulePath))
// Sort for ease of testing
.sort(),
type: 'duplicate',
});
}
}
// Add cases of "shadowing at a distance": a module with a platform suffix and
// a module with a lower priority platform suffix (or no suffix), in different
// directories.
for (const [id, data] of map) {
const conflictPaths = new Set<string>();
const basePaths = [];
for (const basePlatform of [H.NATIVE_PLATFORM, H.GENERIC_PLATFORM]) {
if (data[basePlatform] == null) {
continue;
}
const basePath = data[basePlatform][0];
basePaths.push(basePath);
const basePathDir = path.dirname(basePath);
// Find all platforms that can shadow basePlatform
// Given that X.(specific platform).js > x.native.js > X.js
// and basePlatform is either 'native' or generic (no platform).
for (const platform of Object.keys(data)) {
if (
platform === basePlatform ||
platform === H.GENERIC_PLATFORM /* lowest priority */
) {
continue;
}
const platformPath = data[platform][0];
if (path.dirname(platformPath) !== basePathDir) {
conflictPaths.add(platformPath);
}
}
}
if (conflictPaths.size) {
conflicts.push({
id,
platform: null,
absolutePaths: [...new Set([...conflictPaths, ...basePaths])]
.map(modulePath => path.resolve(rootDir, modulePath))
// Sort for ease of testing
.sort(),
type: 'shadowing',
});
}
}
// Sort for ease of testing
conflicts.sort(
chainComparators(
(a, b) => compareStrings(a.type, b.type),
(a, b) => compareStrings(a.id, b.id),
(a, b) => compareStrings(a.platform, b.platform),
),
);
return conflicts;
}

View File

@@ -0,0 +1,21 @@
/**
* 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
* @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(
file: string,
platforms: ReadonlySet<string>,
): null | undefined | string;
export default getPlatformExtension;

View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = getPlatformExtension;
function getPlatformExtension(file, platforms) {
const last = file.lastIndexOf(".");
const secondToLast = file.lastIndexOf(".", last - 1);
if (secondToLast === -1) {
return null;
}
const platform = file.substring(secondToLast + 1, last);
return platforms.has(platform) ? platform : null;
}

View File

@@ -0,0 +1,23 @@
/**
* 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
*/
// Extract platform extension: index.ios.js -> ios
export default function getPlatformExtension(
file: string,
platforms: ReadonlySet<string>,
): ?string {
const last = file.lastIndexOf('.');
const secondToLast = file.lastIndexOf('.', last - 1);
if (secondToLast === -1) {
return null;
}
const platform = file.substring(secondToLast + 1, last);
return platforms.has(platform) ? platform : null;
}

View File

@@ -0,0 +1,24 @@
/**
* 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
*/
import type {
MetadataWorker,
V8Serializable,
WorkerMessage,
} from '../../flow-types';
declare class Worker implements MetadataWorker {
constructor(opts: Readonly<{hasteImplModulePath: null | undefined | string}>);
processFile(
data: WorkerMessage,
utils: Readonly<{getContent: () => Buffer}>,
): V8Serializable;
}
export = Worker;

View File

@@ -0,0 +1,35 @@
"use strict";
const excludedExtensions = require("../../workerExclusionList");
const path = require("path");
const PACKAGE_JSON = path.sep + "package.json";
module.exports = class Worker {
#hasteImpl = null;
constructor({ hasteImplModulePath }) {
if (hasteImplModulePath != null) {
this.#hasteImpl = require(hasteImplModulePath);
}
}
processFile(data, utils) {
let hasteName = null;
const { filePath } = data;
if (filePath.endsWith(PACKAGE_JSON)) {
try {
const fileData = JSON.parse(utils.getContent().toString());
if (fileData.name) {
hasteName = fileData.name;
}
} catch (err) {
throw new Error(`Cannot parse ${filePath} as JSON: ${err.message}`);
}
} else if (
!excludedExtensions.has(filePath.substr(filePath.lastIndexOf(".")))
) {
if (!this.#hasteImpl) {
throw new Error("computeHaste is true but hasteImplModulePath not set");
}
hasteName = this.#hasteImpl.getHasteName(filePath) || null;
}
return hasteName;
}
};

View File

@@ -0,0 +1,64 @@
/**
* 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
*/
/* eslint-disable import/no-commonjs */
'use strict';
const excludedExtensions = require('../../workerExclusionList');
const path = require('path');
/*::
import type {MetadataWorker, WorkerMessage, V8Serializable} from '../../flow-types';
*/
const PACKAGE_JSON = path.sep + 'package.json';
module.exports = class Worker /*:: implements MetadataWorker */ {
/*:: + */ #hasteImpl /*: ?Readonly<{getHasteName: string => ?string}> */ =
null;
constructor(
{hasteImplModulePath} /*: Readonly<{hasteImplModulePath: ?string}> */,
) {
if (hasteImplModulePath != null) {
// $FlowFixMe[unsupported-syntax] - dynamic require
this.#hasteImpl = require(hasteImplModulePath);
}
}
processFile(
data /*: WorkerMessage */,
utils /*: Readonly<{getContent: () => Buffer }> */,
) /*: V8Serializable */ {
let hasteName /*: string | null */ = null;
const {filePath} = data;
if (filePath.endsWith(PACKAGE_JSON)) {
// Process a package.json that is returned as a PACKAGE type with its name.
try {
const fileData = JSON.parse(utils.getContent().toString());
if (fileData.name) {
hasteName = fileData.name;
}
} catch (err) {
throw new Error(`Cannot parse ${filePath} as JSON: ${err.message}`);
}
} else if (
!excludedExtensions.has(filePath.substr(filePath.lastIndexOf('.')))
) {
if (!this.#hasteImpl) {
throw new Error('computeHaste is true but hasteImplModulePath not set');
}
// Process a random file that is returned as a MODULE.
hasteName = this.#hasteImpl.getHasteName(filePath) || null;
}
return hasteName;
}
};

View File

@@ -0,0 +1,20 @@
/**
* 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
* @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;
declare type $$EXPORT_DEFAULT_DECLARATION$$ =
typeof $$EXPORT_DEFAULT_DECLARATION$$;
export default $$EXPORT_DEFAULT_DECLARATION$$;

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var path = _interopRequireWildcard(require("path"));
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);
}
const MOCKS_PATTERN = path.sep + "__mocks__" + path.sep;
var _default = (filePath) => {
const mockPath = filePath.split(MOCKS_PATTERN)[1];
return mockPath
.substring(0, mockPath.lastIndexOf(path.extname(mockPath)))
.replaceAll("\\", "/");
};
exports.default = _default;

View File

@@ -0,0 +1,20 @@
/**
* 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
*/
import * as path from 'path';
const MOCKS_PATTERN = path.sep + '__mocks__' + path.sep;
export default (filePath: string): string => {
const mockPath = filePath.split(MOCKS_PATTERN)[1];
return mockPath
.substring(0, mockPath.lastIndexOf(path.extname(mockPath)))
.replaceAll('\\', '/');
};