first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

42
node_modules/metro-transform-worker/package.json generated vendored Normal file
View File

@@ -0,0 +1,42 @@
{
"name": "metro-transform-worker",
"version": "0.83.3",
"description": "🚇 Transform worker for Metro.",
"main": "src/index.js",
"exports": {
".": "./src/index.js",
"./package.json": "./package.json",
"./private/*": "./src/*.js"
},
"repository": {
"type": "git",
"url": "git@github.com:facebook/metro.git"
},
"scripts": {
"prepare-release": "test -d build && rm -rf src.real && mv src src.real && mv build src",
"cleanup-release": "test ! -e build && mv src build && mv src.real src"
},
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/generator": "^7.25.0",
"@babel/parser": "^7.25.3",
"@babel/types": "^7.25.2",
"flow-enums-runtime": "^0.0.6",
"metro": "0.83.3",
"metro-babel-transformer": "0.83.3",
"metro-cache": "0.83.3",
"metro-cache-key": "0.83.3",
"metro-minify-terser": "0.83.3",
"metro-source-map": "0.83.3",
"metro-transform-plugins": "0.83.3",
"nullthrows": "^1.1.1"
},
"devDependencies": {
"@react-native/metro-babel-transformer": "0.78.0",
"metro-memory-fs": "*"
},
"engines": {
"node": ">=20.19.4"
}
}

138
node_modules/metro-transform-worker/src/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,138 @@
/**
* 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 {DynamicRequiresBehavior} from 'metro';
import type {
CustomTransformOptions,
TransformProfile,
} from 'metro-babel-transformer';
import type {
BasicSourceMap,
FBSourceFunctionMap,
MetroSourceMapSegmentTuple,
} from 'metro-source-map';
import type {TransformResultDependency} from 'metro/private/DeltaBundler';
import type {AllowOptionalDependencies} from 'metro/private/DeltaBundler/types';
export type MinifierConfig = Readonly<Record<string, unknown>>;
export interface MinifierOptions {
code: string;
map?: BasicSourceMap;
filename: string;
reserved: ReadonlyArray<string>;
config: MinifierConfig;
}
export interface MinifierResult {
code: string;
map?: BasicSourceMap;
}
export type Minifier = (
options: MinifierOptions,
) => MinifierResult | Promise<MinifierResult>;
export type Type = 'script' | 'module' | 'asset';
export type JsTransformerConfig = Readonly<{
assetPlugins: ReadonlyArray<string>;
assetRegistryPath: string;
asyncRequireModulePath: string;
babelTransformerPath: string;
dynamicDepsInPackages: DynamicRequiresBehavior;
enableBabelRCLookup: boolean;
enableBabelRuntime: boolean;
globalPrefix: string;
hermesParser: boolean;
minifierConfig: MinifierConfig;
minifierPath: string;
optimizationSizeLimit: number;
publicPath: string;
allowOptionalDependencies: AllowOptionalDependencies;
unstable_collectDependenciesPath: string;
unstable_dependencyMapReservedName?: string;
unstable_disableModuleWrapping: boolean;
unstable_disableNormalizePseudoGlobals: boolean;
unstable_compactOutput: boolean;
/** Enable `require.context` statements which can be used to import multiple files in a directory. */
unstable_allowRequireContext: boolean;
/** With inlineRequires, enable a module-scope memo var and inline as (v || v=require('foo')) */
unstable_memoizeInlineRequires?: boolean;
/** With inlineRequires, do not memoize these module specifiers */
unstable_nonMemoizedInlineRequires?: ReadonlyArray<string>;
/** Whether to rename scoped `require` functions to `_$$_REQUIRE`, usually an extraneous operation when serializing to iife (default). */
unstable_renameRequire?: boolean;
}>;
export {CustomTransformOptions} from 'metro-babel-transformer';
export type JsTransformOptions = Readonly<{
customTransformOptions?: CustomTransformOptions;
dev: boolean;
experimentalImportSupport?: boolean;
inlinePlatform: boolean;
inlineRequires: boolean;
minify: boolean;
nonInlinedRequires?: ReadonlyArray<string>;
platform?: string;
runtimeBytecodeVersion?: number;
type: Type;
unstable_transformProfile: TransformProfile;
}>;
export type BytecodeFileType =
| 'bytecode/module'
| 'bytecode/module/asset'
| 'bytecode/script';
export type JSFileType = 'js/script' | 'js/module' | 'js/module/asset';
export type JsOutput = Readonly<{
data: Readonly<{
code: string;
lineCount: number;
map: MetroSourceMapSegmentTuple[];
functionMap: FBSourceFunctionMap | null;
}>;
type: JSFileType;
}>;
// Hermes byte-code output type
export type BytecodeOutput = unknown;
export type TransformResponse = Readonly<{
dependencies: ReadonlyArray<TransformResultDependency>;
output: ReadonlyArray<JsOutput | BytecodeOutput>;
}>;
export function transform(
config: JsTransformerConfig,
projectRoot: string,
filename: string,
data: Buffer,
options: JsTransformOptions,
): Promise<TransformResponse>;
export function getCacheKey(config: JsTransformerConfig): string;
/**
* Backwards-compatibility with CommonJS consumers using interopRequireDefault.
* Do not add to this list.
*
* @deprecated Default import from 'metro-transform-worker' is deprecated, use named exports.
*/
declare const $$EXPORT_DEFAULT_DECLARATION$$: {
getCacheKey: typeof getCacheKey;
transform: typeof transform;
};
declare type $$EXPORT_DEFAULT_DECLARATION$$ =
typeof $$EXPORT_DEFAULT_DECLARATION$$;
export default $$EXPORT_DEFAULT_DECLARATION$$;

549
node_modules/metro-transform-worker/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,549 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.transform = exports.getCacheKey = exports.default = void 0;
var assetTransformer = _interopRequireWildcard(
require("./utils/assetTransformer"),
);
var _getMinifier = _interopRequireDefault(require("./utils/getMinifier"));
var _core = require("@babel/core");
var _generator = _interopRequireDefault(require("@babel/generator"));
var babylon = _interopRequireWildcard(require("@babel/parser"));
var types = _interopRequireWildcard(require("@babel/types"));
var _metroCache = require("metro-cache");
var _metroCacheKey = require("metro-cache-key");
var _metroSourceMap = require("metro-source-map");
var _metroTransformPlugins = _interopRequireDefault(
require("metro-transform-plugins"),
);
var _collectDependencies = _interopRequireDefault(
require("metro/private/ModuleGraph/worker/collectDependencies"),
);
var _generateImportNames = _interopRequireDefault(
require("metro/private/ModuleGraph/worker/generateImportNames"),
);
var _importLocationsPlugin = require("metro/private/ModuleGraph/worker/importLocationsPlugin");
var JsFileWrapping = _interopRequireWildcard(
require("metro/private/ModuleGraph/worker/JsFileWrapping"),
);
var _nullthrows = _interopRequireDefault(require("nullthrows"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function _getRequireWildcardCache(e) {
if ("function" != typeof WeakMap) return null;
var r = new WeakMap(),
t = new WeakMap();
return (_getRequireWildcardCache = function (e) {
return e ? t : r;
})(e);
}
function _interopRequireWildcard(e, r) {
if (!r && e && e.__esModule) return e;
if (null === e || ("object" != typeof e && "function" != typeof e))
return { default: e };
var t = _getRequireWildcardCache(r);
if (t && t.has(e)) return t.get(e);
var n = { __proto__: null },
a = Object.defineProperty && Object.getOwnPropertyDescriptor;
for (var u in e)
if ("default" !== u && {}.hasOwnProperty.call(e, u)) {
var i = a ? Object.getOwnPropertyDescriptor(e, u) : null;
i && (i.get || i.set) ? Object.defineProperty(n, u, i) : (n[u] = e[u]);
}
return ((n.default = e), t && t.set(e, n), n);
}
const InternalInvalidRequireCallError =
_collectDependencies.default.InvalidRequireCallError;
function getDynamicDepsBehavior(inPackages, filename) {
switch (inPackages) {
case "reject":
return "reject";
case "throwAtRuntime":
const isPackage = /(?:^|[/\\])node_modules[/\\]/.test(filename);
return isPackage ? inPackages : "reject";
default:
inPackages;
throw new Error(
`invalid value for dynamic deps behavior: \`${inPackages}\``,
);
}
}
const minifyCode = async (
config,
projectRoot,
filename,
code,
source,
map,
reserved = [],
) => {
const sourceMap = (0, _metroSourceMap.fromRawMappings)([
{
code,
source,
map,
functionMap: null,
path: filename,
isIgnored: false,
},
]).toMap(undefined, {});
const minify = (0, _getMinifier.default)(config.minifierPath);
try {
const minified = await minify({
code,
map: sourceMap,
filename,
reserved,
config: config.minifierConfig,
});
return {
code: minified.code,
map: minified.map
? (0, _metroSourceMap.toBabelSegments)(minified.map).map(
_metroSourceMap.toSegmentTuple,
)
: [],
};
} catch (error) {
if (error.constructor.name === "JS_Parse_Error") {
throw new Error(
`${error.message} in file ${filename} at ${error.line}:${error.col}`,
);
}
throw error;
}
};
const disabledDependencyTransformer = {
transformSyncRequire: () => void 0,
transformImportCall: () => void 0,
transformImportMaybeSyncCall: () => void 0,
transformPrefetch: () => void 0,
transformIllegalDynamicRequire: () => void 0,
};
class InvalidRequireCallError extends Error {
constructor(innerError, filename) {
super(`${filename}:${innerError.message}`);
this.innerError = innerError;
this.filename = filename;
}
}
async function transformJS(file, { config, options, projectRoot }) {
let ast =
file.ast ??
babylon.parse(file.code, {
sourceType: "unambiguous",
});
const { importDefault, importAll } = (0, _generateImportNames.default)(ast);
const { directives } = ast.program;
if (
ast.program.sourceType === "module" &&
directives != null &&
directives.findIndex((d) => d.value.value === "use strict") === -1
) {
directives.push(types.directive(types.directiveLiteral("use strict")));
}
const plugins = [];
if (options.experimentalImportSupport === true) {
plugins.push([
_metroTransformPlugins.default.importExportPlugin,
{
importAll,
importDefault,
resolve: false,
},
]);
}
if (options.inlineRequires) {
plugins.push([
_metroTransformPlugins.default.inlineRequiresPlugin,
{
ignoredRequires: options.nonInlinedRequires,
inlineableCalls: [importDefault, importAll],
memoizeCalls:
options.customTransformOptions?.unstable_memoizeInlineRequires ??
options.unstable_memoizeInlineRequires,
nonMemoizedModules: options.unstable_nonMemoizedInlineRequires,
},
]);
}
plugins.push([
_metroTransformPlugins.default.inlinePlugin,
{
dev: options.dev,
inlinePlatform: options.inlinePlatform,
isWrapped: false,
platform: options.platform,
},
]);
ast = (0, _nullthrows.default)(
(0, _core.transformFromAstSync)(ast, "", {
ast: true,
babelrc: false,
code: false,
configFile: false,
comments: true,
filename: file.filename,
plugins,
sourceMaps: false,
cloneInputAst: true,
}).ast,
);
if (!options.dev) {
ast = (0, _nullthrows.default)(
(0, _core.transformFromAstSync)(ast, "", {
ast: true,
babelrc: false,
code: false,
configFile: false,
comments: true,
filename: file.filename,
plugins: [_metroTransformPlugins.default.constantFoldingPlugin],
sourceMaps: false,
cloneInputAst: false,
}).ast,
);
}
let dependencyMapName = "";
let dependencies;
let wrappedAst;
if (file.type === "js/script") {
dependencies = [];
wrappedAst = JsFileWrapping.wrapPolyfill(ast);
} else {
try {
const importDeclarationLocs = file.unstable_importDeclarationLocs ?? null;
const opts = {
asyncRequireModulePath: config.asyncRequireModulePath,
dependencyTransformer:
config.unstable_disableModuleWrapping === true
? disabledDependencyTransformer
: undefined,
dynamicRequires: getDynamicDepsBehavior(
config.dynamicDepsInPackages,
file.filename,
),
inlineableCalls: [importDefault, importAll],
keepRequireNames: options.dev,
allowOptionalDependencies: config.allowOptionalDependencies,
dependencyMapName: config.unstable_dependencyMapReservedName,
unstable_allowRequireContext: config.unstable_allowRequireContext,
unstable_isESMImportAtSource:
importDeclarationLocs != null
? (loc) =>
importDeclarationLocs.has(
(0, _importLocationsPlugin.locToKey)(loc),
)
: null,
};
({ ast, dependencies, dependencyMapName } = (0,
_collectDependencies.default)(ast, opts));
} catch (error) {
if (error instanceof InternalInvalidRequireCallError) {
throw new InvalidRequireCallError(error, file.filename);
}
throw error;
}
if (config.unstable_disableModuleWrapping === true) {
wrappedAst = ast;
} else {
({ ast: wrappedAst } = JsFileWrapping.wrapModule(
ast,
importDefault,
importAll,
dependencyMapName,
config.globalPrefix,
config.unstable_renameRequire === false,
{
unstable_useStaticHermesModuleFactory: Boolean(
options.customTransformOptions
?.unstable_staticHermesOptimizedRequire,
),
},
));
}
}
const minify =
options.minify &&
options.unstable_transformProfile !== "hermes-canary" &&
options.unstable_transformProfile !== "hermes-stable";
const reserved = [];
if (config.unstable_dependencyMapReservedName != null) {
reserved.push(config.unstable_dependencyMapReservedName);
}
if (
minify &&
file.inputFileSize <= config.optimizationSizeLimit &&
!config.unstable_disableNormalizePseudoGlobals
) {
reserved.push(
..._metroTransformPlugins.default.normalizePseudoGlobals(wrappedAst, {
reservedNames: reserved,
}),
);
}
const result = (0, _generator.default)(
wrappedAst,
{
comments: true,
compact: config.unstable_compactOutput,
filename: file.filename,
retainLines: false,
sourceFileName: file.filename,
sourceMaps: true,
},
file.code,
);
let map = result.rawMappings
? result.rawMappings.map(_metroSourceMap.toSegmentTuple)
: [];
let code = result.code;
if (minify) {
({ map, code } = await minifyCode(
config,
projectRoot,
file.filename,
result.code,
file.code,
map,
reserved,
));
}
let lineCount;
({ lineCount, map } = countLinesAndTerminateMap(code, map));
const output = [
{
data: {
code,
lineCount,
map,
functionMap: file.functionMap,
},
type: file.type,
},
];
return {
dependencies,
output,
};
}
async function transformAsset(file, context) {
const { assetRegistryPath, assetPlugins } = context.config;
const result = await assetTransformer.transform(
getBabelTransformArgs(file, context),
assetRegistryPath,
assetPlugins,
);
const jsFile = {
...file,
type: "js/module/asset",
ast: result.ast,
functionMap: null,
};
return transformJS(jsFile, context);
}
async function transformJSWithBabel(file, context) {
const { babelTransformerPath } = context.config;
const transformer = require(babelTransformerPath);
const transformResult = await transformer.transform(
getBabelTransformArgs(file, context, [
_metroSourceMap.functionMapBabelPlugin,
_importLocationsPlugin.importLocationsPlugin,
]),
);
const jsFile = {
...file,
ast: transformResult.ast,
functionMap:
transformResult.metadata?.metro?.functionMap ??
transformResult.functionMap ??
null,
unstable_importDeclarationLocs:
transformResult.metadata?.metro?.unstable_importDeclarationLocs,
};
return await transformJS(jsFile, context);
}
async function transformJSON(file, { options, config, projectRoot }) {
let code =
config.unstable_disableModuleWrapping === true
? JsFileWrapping.jsonToCommonJS(file.code)
: JsFileWrapping.wrapJson(
file.code,
config.globalPrefix,
Boolean(
options.customTransformOptions
?.unstable_staticHermesOptimizedRequire,
),
);
let map = [];
const minify =
options.minify &&
options.unstable_transformProfile !== "hermes-canary" &&
options.unstable_transformProfile !== "hermes-stable";
if (minify) {
({ map, code } = await minifyCode(
config,
projectRoot,
file.filename,
code,
file.code,
map,
));
}
let jsType;
if (file.type === "asset") {
jsType = "js/module/asset";
} else if (file.type === "script") {
jsType = "js/script";
} else {
jsType = "js/module";
}
let lineCount;
({ lineCount, map } = countLinesAndTerminateMap(code, map));
const output = [
{
data: {
code,
lineCount,
map,
functionMap: null,
},
type: jsType,
},
];
return {
dependencies: [],
output,
};
}
function getBabelTransformArgs(
file,
{ options, config, projectRoot },
plugins = [],
) {
const { inlineRequires: _, ...babelTransformerOptions } = options;
return {
filename: file.filename,
options: {
...babelTransformerOptions,
enableBabelRCLookup: config.enableBabelRCLookup,
enableBabelRuntime: config.enableBabelRuntime,
globalPrefix: config.globalPrefix,
hermesParser: config.hermesParser,
projectRoot,
publicPath: config.publicPath,
},
plugins,
src: file.code,
};
}
const transform = async (config, projectRoot, filename, data, options) => {
const context = {
config,
projectRoot,
options,
};
const sourceCode = data.toString("utf8");
const reservedStrings = [];
if (
options.customTransformOptions?.unstable_staticHermesOptimizedRequire ==
true
) {
reservedStrings.push("_$$_METRO_MODULE_ID");
}
if (config.unstable_dependencyMapReservedName != null) {
reservedStrings.push(config.unstable_dependencyMapReservedName);
}
for (const reservedString of reservedStrings) {
const position = sourceCode.indexOf(reservedString);
if (position > -1) {
throw new SyntaxError(
"Source code contains the reserved string `" +
reservedString +
"` at character offset " +
position,
);
}
}
if (filename.endsWith(".json")) {
const jsonFile = {
filename,
inputFileSize: data.length,
code: sourceCode,
type: options.type,
};
return await transformJSON(jsonFile, context);
}
if (options.type === "asset") {
const file = {
filename,
inputFileSize: data.length,
code: sourceCode,
type: options.type,
};
return await transformAsset(file, context);
}
const file = {
filename,
inputFileSize: data.length,
code: sourceCode,
type: options.type === "script" ? "js/script" : "js/module",
functionMap: null,
};
return await transformJSWithBabel(file, context);
};
exports.transform = transform;
const getCacheKey = (config) => {
const { babelTransformerPath, minifierPath, ...remainingConfig } = config;
const filesKey = (0, _metroCacheKey.getCacheKey)([
__filename,
require.resolve(babelTransformerPath),
require.resolve(minifierPath),
require.resolve("./utils/getMinifier"),
require.resolve("./utils/assetTransformer"),
require.resolve("metro/private/ModuleGraph/worker/generateImportNames"),
require.resolve("metro/private/ModuleGraph/worker/JsFileWrapping"),
..._metroTransformPlugins.default.getTransformPluginCacheKeyFiles(),
]);
const babelTransformer = require(babelTransformerPath);
return [
filesKey,
(0, _metroCache.stableHash)(remainingConfig).toString("hex"),
babelTransformer.getCacheKey ? babelTransformer.getCacheKey() : "",
].join("$");
};
exports.getCacheKey = getCacheKey;
function countLinesAndTerminateMap(code, map) {
const NEWLINE = /\r\n?|\n|\u2028|\u2029/g;
let lineCount = 1;
let lastLineStart = 0;
for (const match of code.matchAll(NEWLINE)) {
lineCount++;
lastLineStart = match.index + match[0].length;
}
const lastLineLength = code.length - lastLineStart;
const lastLineIndex1Based = lineCount;
const lastLineNextColumn0Based = lastLineLength;
const lastMapping = map[map.length - 1];
const terminatingMapping = [lastLineIndex1Based, lastLineNextColumn0Based];
if (
!lastMapping ||
lastMapping[0] !== terminatingMapping[0] ||
lastMapping[1] !== terminatingMapping[1]
) {
return {
lineCount,
map: map.concat([terminatingMapping]),
};
}
return {
lineCount,
map: [...map],
};
}
var _default = (exports.default = {
getCacheKey,
transform,
});

792
node_modules/metro-transform-worker/src/index.js.flow generated vendored Normal file
View File

@@ -0,0 +1,792 @@
/**
* 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 {PluginEntry, Plugins} from '@babel/core';
import type {
BabelTransformer,
BabelTransformerArgs,
CustomTransformOptions,
TransformProfile,
} from 'metro-babel-transformer';
import type {
BasicSourceMap,
FBSourceFunctionMap,
MetroSourceMapSegmentTuple,
} from 'metro-source-map';
import type {
ImportExportPluginOptions,
InlinePluginOptions,
InlineRequiresPluginOptions,
} from 'metro-transform-plugins';
import type {TransformResultDependency} from 'metro/private/DeltaBundler';
import type {AllowOptionalDependencies} from 'metro/private/DeltaBundler/types';
import type {
DependencyTransformer,
DynamicRequiresBehavior,
} from 'metro/private/ModuleGraph/worker/collectDependencies';
import * as assetTransformer from './utils/assetTransformer';
import getMinifier from './utils/getMinifier';
import {transformFromAstSync} from '@babel/core';
import generate from '@babel/generator';
import * as babylon from '@babel/parser';
import * as types from '@babel/types';
import {stableHash} from 'metro-cache';
import {getCacheKey as metroGetCacheKey} from 'metro-cache-key';
import {
fromRawMappings,
functionMapBabelPlugin,
toBabelSegments,
toSegmentTuple,
} from 'metro-source-map';
import metroTransformPlugins from 'metro-transform-plugins';
import collectDependencies from 'metro/private/ModuleGraph/worker/collectDependencies';
import generateImportNames from 'metro/private/ModuleGraph/worker/generateImportNames';
import {
importLocationsPlugin,
locToKey,
} from 'metro/private/ModuleGraph/worker/importLocationsPlugin';
import * as JsFileWrapping from 'metro/private/ModuleGraph/worker/JsFileWrapping';
import nullthrows from 'nullthrows';
const InternalInvalidRequireCallError =
collectDependencies.InvalidRequireCallError;
type MinifierConfig = $ReadOnly<{[string]: mixed, ...}>;
export type MinifierOptions = {
code: string,
map: ?BasicSourceMap,
filename: string,
reserved: $ReadOnlyArray<string>,
config: MinifierConfig,
...
};
export type MinifierResult = {
code: string,
map?: BasicSourceMap,
...
};
export type Minifier = MinifierOptions =>
| MinifierResult
| Promise<MinifierResult>;
export type Type = 'script' | 'module' | 'asset';
export type JsTransformerConfig = $ReadOnly<{
assetPlugins: $ReadOnlyArray<string>,
assetRegistryPath: string,
asyncRequireModulePath: string,
babelTransformerPath: string,
dynamicDepsInPackages: DynamicRequiresBehavior,
enableBabelRCLookup: boolean,
enableBabelRuntime: boolean | string,
globalPrefix: string,
hermesParser: boolean,
minifierConfig: MinifierConfig,
minifierPath: string,
optimizationSizeLimit: number,
publicPath: string,
allowOptionalDependencies: AllowOptionalDependencies,
unstable_dependencyMapReservedName: ?string,
unstable_disableModuleWrapping: boolean,
unstable_disableNormalizePseudoGlobals: boolean,
unstable_compactOutput: boolean,
/** Enable `require.context` statements which can be used to import multiple files in a directory. */
unstable_allowRequireContext: boolean,
/** With inlineRequires, enable a module-scope memo var and inline as (v || v=require('foo')) */
unstable_memoizeInlineRequires?: boolean,
/** With inlineRequires, do not memoize these module specifiers */
unstable_nonMemoizedInlineRequires?: $ReadOnlyArray<string>,
/** Whether to rename scoped `require` functions to `_$$_REQUIRE`, usually an extraneous operation when serializing to iife (default). */
unstable_renameRequire?: boolean,
}>;
export type {CustomTransformOptions} from 'metro-babel-transformer';
export type JsTransformOptions = $ReadOnly<{
customTransformOptions?: CustomTransformOptions,
dev: boolean,
experimentalImportSupport?: boolean,
inlinePlatform: boolean,
inlineRequires: boolean,
minify: boolean,
nonInlinedRequires?: $ReadOnlyArray<string>,
platform: ?string,
type: Type,
unstable_memoizeInlineRequires?: boolean,
unstable_nonMemoizedInlineRequires?: $ReadOnlyArray<string>,
unstable_staticHermesOptimizedRequire?: boolean,
unstable_transformProfile: TransformProfile,
}>;
opaque type Path = string;
type BaseFile = $ReadOnly<{
code: string,
filename: Path,
inputFileSize: number,
}>;
type AssetFile = $ReadOnly<{
...BaseFile,
type: 'asset',
}>;
type JSFileType = 'js/script' | 'js/module' | 'js/module/asset';
type JSFile = $ReadOnly<{
...BaseFile,
ast?: ?BabelNodeFile,
type: JSFileType,
functionMap: FBSourceFunctionMap | null,
unstable_importDeclarationLocs?: ?$ReadOnlySet<string>,
}>;
type JSONFile = {
...BaseFile,
type: Type,
};
type TransformationContext = $ReadOnly<{
config: JsTransformerConfig,
projectRoot: Path,
options: JsTransformOptions,
}>;
export type JsOutput = $ReadOnly<{
data: $ReadOnly<{
code: string,
lineCount: number,
map: Array<MetroSourceMapSegmentTuple>,
functionMap: ?FBSourceFunctionMap,
}>,
type: JSFileType,
}>;
type TransformResponse = $ReadOnly<{
dependencies: $ReadOnlyArray<TransformResultDependency>,
output: $ReadOnlyArray<JsOutput>,
}>;
function getDynamicDepsBehavior(
inPackages: DynamicRequiresBehavior,
filename: string,
): DynamicRequiresBehavior {
switch (inPackages) {
case 'reject':
return 'reject';
case 'throwAtRuntime':
const isPackage = /(?:^|[/\\])node_modules[/\\]/.test(filename);
return isPackage ? inPackages : 'reject';
default:
(inPackages: empty);
throw new Error(
`invalid value for dynamic deps behavior: \`${inPackages}\``,
);
}
}
const minifyCode = async (
config: JsTransformerConfig,
projectRoot: string,
filename: string,
code: string,
source: string,
map: Array<MetroSourceMapSegmentTuple>,
reserved?: $ReadOnlyArray<string> = [],
): Promise<{
code: string,
map: Array<MetroSourceMapSegmentTuple>,
...
}> => {
const sourceMap = fromRawMappings([
{
code,
source,
map,
// functionMap is overridden by the serializer
functionMap: null,
path: filename,
// isIgnored is overriden by the serializer
isIgnored: false,
},
]).toMap(undefined, {});
const minify = getMinifier(config.minifierPath);
try {
const minified = await minify({
code,
map: sourceMap,
filename,
reserved,
config: config.minifierConfig,
});
return {
code: minified.code,
map: minified.map
? toBabelSegments(minified.map).map(toSegmentTuple)
: [],
};
} catch (error) {
if (error.constructor.name === 'JS_Parse_Error') {
throw new Error(
`${error.message} in file ${filename} at ${error.line}:${error.col}`,
);
}
throw error;
}
};
const disabledDependencyTransformer: DependencyTransformer = {
transformSyncRequire: () => void 0,
transformImportCall: () => void 0,
transformImportMaybeSyncCall: () => void 0,
transformPrefetch: () => void 0,
transformIllegalDynamicRequire: () => void 0,
};
class InvalidRequireCallError extends Error {
innerError: InternalInvalidRequireCallError;
filename: string;
constructor(innerError: InternalInvalidRequireCallError, filename: string) {
super(`${filename}:${innerError.message}`);
this.innerError = innerError;
this.filename = filename;
}
}
async function transformJS(
file: JSFile,
{config, options, projectRoot}: TransformationContext,
): Promise<TransformResponse> {
// Transformers can output null ASTs (if they ignore the file). In that case
// we need to parse the module source code to get their AST.
let ast = file.ast ?? babylon.parse(file.code, {sourceType: 'unambiguous'});
const {importDefault, importAll} = generateImportNames(ast);
// Add "use strict" if the file was parsed as a module, and the directive did
// not exist yet.
const {directives} = ast.program;
if (
ast.program.sourceType === 'module' &&
directives != null &&
directives.findIndex(d => d.value.value === 'use strict') === -1
) {
directives.push(types.directive(types.directiveLiteral('use strict')));
}
// Perform the import-export transform (in case it's still needed), then
// fold requires and perform constant folding (if in dev).
const plugins: Array<PluginEntry> = [];
if (options.experimentalImportSupport === true) {
plugins.push([
metroTransformPlugins.importExportPlugin,
{
importAll,
importDefault,
resolve: false,
} as ImportExportPluginOptions,
]);
}
if (options.inlineRequires) {
plugins.push([
metroTransformPlugins.inlineRequiresPlugin,
{
ignoredRequires: options.nonInlinedRequires,
inlineableCalls: [importDefault, importAll],
memoizeCalls:
// $FlowFixMe[incompatible-type] is this always (?boolean)?
options.customTransformOptions?.unstable_memoizeInlineRequires ??
options.unstable_memoizeInlineRequires,
nonMemoizedModules: options.unstable_nonMemoizedInlineRequires,
} as InlineRequiresPluginOptions,
]);
}
plugins.push([
metroTransformPlugins.inlinePlugin,
{
dev: options.dev,
inlinePlatform: options.inlinePlatform,
isWrapped: false,
// $FlowFixMe[incompatible-type] expects a string if inlinePlatform
platform: options.platform,
} as InlinePluginOptions,
]);
ast = nullthrows(
transformFromAstSync(ast, '', {
ast: true,
babelrc: false,
code: false,
configFile: false,
comments: true,
filename: file.filename,
plugins,
sourceMaps: false,
// Not-Cloning the input AST here should be safe because other code paths above this call
// are mutating the AST as well and no code is depending on the original AST.
// However, switching the flag to false caused issues with ES Modules if `experimentalImportSupport` isn't used https://github.com/facebook/metro/issues/641
// either because one of the plugins is doing something funky or Babel messes up some caches.
// Make sure to test the above mentioned case before flipping the flag back to false.
cloneInputAst: true,
}).ast,
);
if (!options.dev) {
// Run the constant folding plugin in its own pass, avoiding race conditions
// with other plugins that have exit() visitors on Program (e.g. the ESM
// transform).
ast = nullthrows(
transformFromAstSync(ast, '', {
ast: true,
babelrc: false,
code: false,
configFile: false,
comments: true,
filename: file.filename,
plugins: [metroTransformPlugins.constantFoldingPlugin],
sourceMaps: false,
cloneInputAst: false,
}).ast,
);
}
let dependencyMapName = '';
let dependencies;
let wrappedAst;
// If the module to transform is a script (meaning that is not part of the
// dependency graph and it code will just be prepended to the bundle modules),
// we need to wrap it differently than a commonJS module (also, scripts do
// not have dependencies).
if (file.type === 'js/script') {
dependencies = [];
wrappedAst = JsFileWrapping.wrapPolyfill(ast);
} else {
try {
const importDeclarationLocs = file.unstable_importDeclarationLocs ?? null;
const opts = {
asyncRequireModulePath: config.asyncRequireModulePath,
dependencyTransformer:
config.unstable_disableModuleWrapping === true
? disabledDependencyTransformer
: undefined,
dynamicRequires: getDynamicDepsBehavior(
config.dynamicDepsInPackages,
file.filename,
),
inlineableCalls: [importDefault, importAll],
keepRequireNames: options.dev,
allowOptionalDependencies: config.allowOptionalDependencies,
dependencyMapName: config.unstable_dependencyMapReservedName,
unstable_allowRequireContext: config.unstable_allowRequireContext,
unstable_isESMImportAtSource:
importDeclarationLocs != null
? (loc: BabelSourceLocation) =>
importDeclarationLocs.has(locToKey(loc))
: null,
};
({ast, dependencies, dependencyMapName} = collectDependencies(ast, opts));
} catch (error) {
if (error instanceof InternalInvalidRequireCallError) {
throw new InvalidRequireCallError(error, file.filename);
}
throw error;
}
if (config.unstable_disableModuleWrapping === true) {
wrappedAst = ast;
} else {
({ast: wrappedAst} = JsFileWrapping.wrapModule(
ast,
importDefault,
importAll,
dependencyMapName,
config.globalPrefix,
// TODO: This config is optional to allow its introduction in a minor
// release. It should be made non-optional in ConfigT or removed in
// future.
config.unstable_renameRequire === false,
{
unstable_useStaticHermesModuleFactory: Boolean(
options.customTransformOptions
?.unstable_staticHermesOptimizedRequire,
),
},
));
}
}
const minify =
options.minify &&
options.unstable_transformProfile !== 'hermes-canary' &&
options.unstable_transformProfile !== 'hermes-stable';
const reserved = [];
if (config.unstable_dependencyMapReservedName != null) {
reserved.push(config.unstable_dependencyMapReservedName);
}
if (
minify &&
file.inputFileSize <= config.optimizationSizeLimit &&
!config.unstable_disableNormalizePseudoGlobals
) {
reserved.push(
...metroTransformPlugins.normalizePseudoGlobals(wrappedAst, {
reservedNames: reserved,
}),
);
}
const result = generate(
wrappedAst,
{
comments: true,
compact: config.unstable_compactOutput,
filename: file.filename,
retainLines: false,
sourceFileName: file.filename,
sourceMaps: true,
},
file.code,
);
let map = result.rawMappings ? result.rawMappings.map(toSegmentTuple) : [];
let code = result.code;
if (minify) {
({map, code} = await minifyCode(
config,
projectRoot,
file.filename,
result.code,
file.code,
map,
reserved,
));
}
let lineCount;
({lineCount, map} = countLinesAndTerminateMap(code, map));
const output: Array<JsOutput> = [
{
data: {
code,
lineCount,
map,
functionMap: file.functionMap,
},
type: file.type,
},
];
return {
dependencies,
output,
};
}
/**
* Transforms an asset file
*/
async function transformAsset(
file: AssetFile,
context: TransformationContext,
): Promise<TransformResponse> {
const {assetRegistryPath, assetPlugins} = context.config;
const result = await assetTransformer.transform(
getBabelTransformArgs(file, context),
assetRegistryPath,
assetPlugins,
);
const jsFile = {
...file,
type: 'js/module/asset' as const,
ast: result.ast,
functionMap: null,
};
return transformJS(jsFile, context);
}
/**
* Transforms a JavaScript file with Babel before processing the file with
* the generic JavaScript transformation.
*/
async function transformJSWithBabel(
file: JSFile,
context: TransformationContext,
): Promise<TransformResponse> {
const {babelTransformerPath} = context.config;
// $FlowFixMe[unsupported-syntax] dynamic require
const transformer: BabelTransformer = require(babelTransformerPath);
const transformResult = await transformer.transform(
getBabelTransformArgs(file, context, [
// functionMapBabelPlugin populates metadata.metro.functionMap
functionMapBabelPlugin,
// importLocationsPlugin populates metadata.metro.unstable_importDeclarationLocs
importLocationsPlugin,
]),
);
const jsFile: JSFile = {
...file,
ast: transformResult.ast,
functionMap:
transformResult.metadata?.metro?.functionMap ??
// Fallback to deprecated explicitly-generated `functionMap`
transformResult.functionMap ??
null,
unstable_importDeclarationLocs:
transformResult.metadata?.metro?.unstable_importDeclarationLocs,
};
return await transformJS(jsFile, context);
}
async function transformJSON(
file: JSONFile,
{options, config, projectRoot}: TransformationContext,
): Promise<TransformResponse> {
let code =
config.unstable_disableModuleWrapping === true
? JsFileWrapping.jsonToCommonJS(file.code)
: JsFileWrapping.wrapJson(
file.code,
config.globalPrefix,
Boolean(
options.customTransformOptions
?.unstable_staticHermesOptimizedRequire,
),
);
let map: Array<MetroSourceMapSegmentTuple> = [];
// TODO: When we can reuse transformJS for JSON, we should not derive `minify` separately.
const minify =
options.minify &&
options.unstable_transformProfile !== 'hermes-canary' &&
options.unstable_transformProfile !== 'hermes-stable';
if (minify) {
({map, code} = await minifyCode(
config,
projectRoot,
file.filename,
code,
file.code,
map,
));
}
let jsType: JSFileType;
if (file.type === 'asset') {
jsType = 'js/module/asset';
} else if (file.type === 'script') {
jsType = 'js/script';
} else {
jsType = 'js/module';
}
let lineCount;
({lineCount, map} = countLinesAndTerminateMap(code, map));
const output: Array<JsOutput> = [
{
data: {code, lineCount, map, functionMap: null},
type: jsType,
},
];
return {
dependencies: [],
output,
};
}
function getBabelTransformArgs(
file: $ReadOnly<{filename: Path, code: string, ...}>,
{options, config, projectRoot}: TransformationContext,
plugins?: Plugins = [],
): BabelTransformerArgs {
const {inlineRequires: _, ...babelTransformerOptions} = options;
return {
filename: file.filename,
options: {
...babelTransformerOptions,
enableBabelRCLookup: config.enableBabelRCLookup,
enableBabelRuntime: config.enableBabelRuntime,
globalPrefix: config.globalPrefix,
hermesParser: config.hermesParser,
projectRoot,
publicPath: config.publicPath,
},
plugins,
src: file.code,
};
}
export const transform = async (
config: JsTransformerConfig,
projectRoot: string,
filename: string,
data: Buffer,
options: JsTransformOptions,
): Promise<TransformResponse> => {
const context: TransformationContext = {
config,
projectRoot,
options,
};
const sourceCode = data.toString('utf8');
const reservedStrings = [];
if (
options.customTransformOptions?.unstable_staticHermesOptimizedRequire ==
true
) {
reservedStrings.push('_$$_METRO_MODULE_ID');
}
if (config.unstable_dependencyMapReservedName != null) {
reservedStrings.push(config.unstable_dependencyMapReservedName);
}
for (const reservedString of reservedStrings) {
const position = sourceCode.indexOf(reservedString);
if (position > -1) {
throw new SyntaxError(
'Source code contains the reserved string `' +
reservedString +
'` at character offset ' +
position,
);
}
}
if (filename.endsWith('.json')) {
const jsonFile: JSONFile = {
filename,
inputFileSize: data.length,
code: sourceCode,
type: options.type,
};
return await transformJSON(jsonFile, context);
}
if (options.type === 'asset') {
const file: AssetFile = {
filename,
inputFileSize: data.length,
code: sourceCode,
type: options.type,
};
return await transformAsset(file, context);
}
const file: JSFile = {
filename,
inputFileSize: data.length,
code: sourceCode,
type: options.type === 'script' ? 'js/script' : 'js/module',
functionMap: null,
};
return await transformJSWithBabel(file, context);
};
export const getCacheKey = (config: JsTransformerConfig): string => {
const {babelTransformerPath, minifierPath, ...remainingConfig} = config;
const filesKey = metroGetCacheKey([
__filename,
require.resolve(babelTransformerPath),
require.resolve(minifierPath),
require.resolve('./utils/getMinifier'),
require.resolve('./utils/assetTransformer'),
require.resolve('metro/private/ModuleGraph/worker/generateImportNames'),
require.resolve('metro/private/ModuleGraph/worker/JsFileWrapping'),
...metroTransformPlugins.getTransformPluginCacheKeyFiles(),
]);
// $FlowFixMe[unsupported-syntax]
const babelTransformer = require(babelTransformerPath);
return [
filesKey,
stableHash(remainingConfig).toString('hex'),
babelTransformer.getCacheKey ? babelTransformer.getCacheKey() : '',
].join('$');
};
function countLinesAndTerminateMap(
code: string,
map: $ReadOnlyArray<MetroSourceMapSegmentTuple>,
): {
lineCount: number,
map: Array<MetroSourceMapSegmentTuple>,
} {
const NEWLINE = /\r\n?|\n|\u2028|\u2029/g;
let lineCount = 1;
let lastLineStart = 0;
// Count lines and keep track of where the last line starts
for (const match of code.matchAll(NEWLINE)) {
lineCount++;
lastLineStart = match.index + match[0].length;
}
const lastLineLength = code.length - lastLineStart;
const lastLineIndex1Based = lineCount;
const lastLineNextColumn0Based = lastLineLength;
// If there isn't a mapping at one-past-the-last column of the last line,
// add one that maps to nothing. This ensures out-of-bounds lookups hit the
// null mapping rather than aliasing to whichever mapping happens to be last.
// ASSUMPTION: Mappings are generated in order of increasing line and column.
const lastMapping = map[map.length - 1];
const terminatingMapping = [lastLineIndex1Based, lastLineNextColumn0Based];
if (
!lastMapping ||
lastMapping[0] !== terminatingMapping[0] ||
lastMapping[1] !== terminatingMapping[1]
) {
return {
lineCount,
map: map.concat([terminatingMapping]),
};
}
return {lineCount, map: [...map]};
}
/**
* Backwards-compatibility with CommonJS consumers using interopRequireDefault.
* Do not add to this list.
*
* @deprecated Default import from 'metro-transform-worker' is deprecated, use named exports.
*/
export default {
getCacheKey,
transform,
};

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.transform = transform;
var _Assets = require("metro/private/Assets");
var _util = require("metro/private/Bundler/util");
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
async function transform(
{ filename, options, src },
assetRegistryPath,
assetDataPlugins,
) {
options = options || {
platform: "",
projectRoot: "",
inlineRequires: false,
minify: false,
};
const absolutePath = _path.default.resolve(options.projectRoot, filename);
const data = await (0, _Assets.getAssetData)(
absolutePath,
filename,
assetDataPlugins,
options.platform,
options.publicPath,
);
return {
ast: (0, _util.generateAssetCodeFileAst)(assetRegistryPath, data),
};
}

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
* @format
* @oncall react_native
*/
import type {File} from '@babel/types';
import type {BabelTransformerArgs} from 'metro-babel-transformer';
import {getAssetData} from 'metro/private/Assets';
import {generateAssetCodeFileAst} from 'metro/private/Bundler/util';
import path from 'path';
export async function transform(
{filename, options, src}: BabelTransformerArgs,
assetRegistryPath: string,
assetDataPlugins: $ReadOnlyArray<string>,
): Promise<{ast: File, ...}> {
options = options || {
platform: '',
projectRoot: '',
inlineRequires: false,
minify: false,
};
const absolutePath = path.resolve(options.projectRoot, filename);
const data = await getAssetData(
absolutePath,
filename,
assetDataPlugins,
options.platform,
options.publicPath,
);
return {
ast: generateAssetCodeFileAst(assetRegistryPath, data),
};
}

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = getMinifier;
function getMinifier(minifierPath) {
try {
return require(minifierPath);
} catch (e) {
throw new Error(
'A problem occurred while trying to fetch the minifier. Path: "' +
minifierPath +
'", error message: ' +
e.message,
);
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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 {Minifier} from '../index.js';
export default function getMinifier(minifierPath: string): Minifier {
// Note: minifierPath should be an absolute path OR a module name here!
// The options allow relative paths but they HAVE to be normalized at
// any entry point that accepts them...
try {
// $FlowFixMe[unsupported-syntax] TODO t0 cannot do require with literal
return require(minifierPath);
} catch (e) {
throw new Error(
'A problem occurred while trying to fetch the minifier. Path: "' +
minifierPath +
'", error message: ' +
e.message,
);
}
}