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

View File

@@ -0,0 +1,12 @@
/**
* Copyright © 2023 650 Industries.
* Copyright (c) 2022, Sentry.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Deterministically hashes a string and turns the hash into a uuid.
* https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/58271f1af2ade6b3e64d393d70376ae53bc5bd2f/packages/bundler-plugin-core/src/utils.ts#L174
*/
export declare function stringToUUID(str: string): string;

View File

@@ -0,0 +1,37 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.stringToUUID = stringToUUID;
const node_crypto_1 = __importDefault(require("node:crypto"));
/**
* Copyright © 2023 650 Industries.
* Copyright (c) 2022, Sentry.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Deterministically hashes a string and turns the hash into a uuid.
* https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/58271f1af2ade6b3e64d393d70376ae53bc5bd2f/packages/bundler-plugin-core/src/utils.ts#L174
*/
function stringToUUID(str) {
const md5sum = node_crypto_1.default.createHash('md5');
md5sum.update(str);
const md5Hash = md5sum.digest('hex');
// Position 16 is fixed to either 8, 9, a, or b in the uuid v4 spec (10xx in binary)
// RFC 4122 section 4.4
const v4variant = ['8', '9', 'a', 'b'][md5Hash.substring(16, 17).charCodeAt(0) % 4];
return (md5Hash.substring(0, 8) +
'-' +
md5Hash.substring(8, 12) +
'-4' +
md5Hash.substring(13, 16) +
'-' +
v4variant +
md5Hash.substring(17, 20) +
'-' +
md5Hash.substring(20)).toLowerCase();
}
//# sourceMappingURL=debugId.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"debugId.js","sourceRoot":"","sources":["../../src/serializer/debugId.ts"],"names":[],"mappings":";;;;;AAcA,oCAqBC;AAnCD,8DAAiC;AAEjC;;;;;;GAMG;AAEH;;;GAGG;AACH,SAAgB,YAAY,CAAC,GAAW;IACtC,MAAM,MAAM,GAAG,qBAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACnB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAErC,oFAAoF;IACpF,uBAAuB;IACvB,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAW,CAAC;IAE9F,OAAO,CACL,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC;QACvB,GAAG;QACH,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC;QACxB,IAAI;QACJ,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC;QACzB,GAAG;QACH,SAAS;QACT,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC;QACzB,GAAG;QACH,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CACtB,CAAC,WAAW,EAAE,CAAC;AAClB,CAAC"}

View File

@@ -0,0 +1,13 @@
/**
* Copyright © 2022 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { ReadOnlyGraph, MixedOutput, Module, SerializerOptions } from '@expo/metro/metro/DeltaBundler/types';
import { SerializerParameters } from './withExpoSerializers';
export declare function getTransformEnvironment(url: string): string | null;
/** Strips the process.env polyfill in server environments to allow for accessing environment variables off the global. */
export declare function serverPreludeSerializerPlugin(entryPoint: string, preModules: readonly Module<MixedOutput>[], graph: ReadOnlyGraph, options: SerializerOptions): SerializerParameters;
export declare function environmentVariableSerializerPlugin(entryPoint: string, preModules: readonly Module<MixedOutput>[], graph: ReadOnlyGraph, options: SerializerOptions): SerializerParameters;
export declare function getEnvVarDevString(env?: NodeJS.ProcessEnv): string;

View File

@@ -0,0 +1,117 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTransformEnvironment = getTransformEnvironment;
exports.serverPreludeSerializerPlugin = serverPreludeSerializerPlugin;
exports.environmentVariableSerializerPlugin = environmentVariableSerializerPlugin;
exports.getEnvVarDevString = getEnvVarDevString;
const CountingSet_1 = __importDefault(require("@expo/metro/metro/lib/CountingSet"));
const countLines_1 = __importDefault(require("@expo/metro/metro/lib/countLines"));
const debug = require('debug')('expo:metro-config:serializer:env-var');
function getTransformEnvironment(url) {
const match = url.match(/[&?]transform\.environment=([^&]+)/);
return match ? match[1] : null;
}
function getAllExpoPublicEnvVars(inputEnv = process.env) {
// Create an object containing all environment variables that start with EXPO_PUBLIC_
const env = {};
for (const key in inputEnv) {
if (key.startsWith('EXPO_PUBLIC_')) {
// @ts-expect-error: TS doesn't know that the key starts with EXPO_PUBLIC_
env[key] = inputEnv[key];
}
}
return env;
}
function isServerEnvironment(graph, options) {
// Requests from a dev server will use sourceUrl.
if (!graph.transformOptions.customTransformOptions) {
if (options.sourceUrl) {
const env = getTransformEnvironment(options.sourceUrl);
return env === 'node' || env === 'react-server';
}
return false;
}
// Other requests will use customTransformOptions.environment.
const env = graph.transformOptions.customTransformOptions.environment;
return env === 'node' || env === 'react-server';
}
/** Strips the process.env polyfill in server environments to allow for accessing environment variables off the global. */
function serverPreludeSerializerPlugin(entryPoint, preModules, graph, options) {
if (isServerEnvironment(graph, options)) {
const prelude = preModules.find((module) => module.path === '__prelude__');
if (prelude) {
debug('Stripping environment variable polyfill in server environment.');
prelude.output[0].data.code = prelude.output[0].data.code
.replace(/process=this\.process\|\|{},/, '')
.replace(/process\.env=process\.env\|\|{};process\.env\.NODE_ENV=process\.env\.NODE_ENV\|\|"\w+";/, '');
}
}
return [entryPoint, preModules, graph, options];
}
function environmentVariableSerializerPlugin(entryPoint, preModules, graph, options) {
// Skip replacement in Node.js environments.
if (isServerEnvironment(graph, options)) {
debug('Skipping environment variable inlining in Node.js environment.');
return [entryPoint, preModules, graph, options];
}
// In development, we need to add the process.env object to ensure it
// persists between Fast Refresh updates.
if (!options.dev) {
debug('Skipping environment variable inlining in production environment in favor of babel-preset-expo inlining with source maps.');
return [entryPoint, preModules, graph, options];
}
const code = getEnvVarDevString();
const prelude = preModules.find((module) => module.path === '\0polyfill:environment-variables');
if (prelude) {
debug('Injecting environment variables in virtual module.');
// !!MUST!! be one line in order to ensure Metro's asymmetric serializer system can handle it.
prelude.output[0].data.code = code;
return [entryPoint, preModules, graph, options];
}
// Old system which doesn't work very well since Metro doesn't serialize graphs the same way in all cases.
// e.g. the `.map` endpoint is serialized differently to error symbolication.
// Inject the new module at index 1
// @ts-expect-error: The preModules are mutable and we need to mutate them in order to ensure the changes are applied outside of the serializer.
preModules.splice(
// Inject at index 1 to ensure it runs after the prelude (which injects env vars).
1, 0, getEnvPrelude(code));
return [entryPoint, preModules, graph, options];
}
function getEnvVarDevString(env = process.env) {
// Set the process.env object to the current environment variables object
// ensuring they aren't iterable, settable, or enumerable.
const str = `process.env=Object.defineProperties(process.env, {` +
Object.keys(getAllExpoPublicEnvVars(env))
.map((key) => `${JSON.stringify(key)}: { enumerable: true, value: ${JSON.stringify(env[key])} }`)
.join(',') +
'});';
const code = '/* HMR env vars from Expo CLI (dev-only) */ ' + str;
const lineCount = (0, countLines_1.default)(code);
if (lineCount !== 1) {
throw new Error(`Virtual environment variable code must be one line, got "${lineCount}" lines.`);
}
return code;
}
function getEnvPrelude(code) {
const name = `\0polyfill:environment-variables`;
return {
dependencies: new Map(),
getSource: () => Buffer.from(code),
inverseDependencies: new CountingSet_1.default(),
path: name,
output: [
{
type: 'js/script/virtual',
data: {
code,
lineCount: 1,
map: [],
},
},
],
};
}
//# sourceMappingURL=environmentVariableSerializerPlugin.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"environmentVariableSerializerPlugin.js","sourceRoot":"","sources":["../../src/serializer/environmentVariableSerializerPlugin.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,13 @@
interface HermesBundleOutput {
hbc: Uint8Array;
sourcemap: string | null;
}
type BuildHermesOptions = {
projectRoot: string;
filename: string;
code: string;
map: string | null;
minify?: boolean;
};
export declare function buildHermesBundleAsync(options: BuildHermesOptions): Promise<HermesBundleOutput>;
export {};

View File

@@ -0,0 +1,118 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildHermesBundleAsync = buildHermesBundleAsync;
const metro_source_map_1 = require("@expo/metro/metro-source-map");
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const process_1 = __importDefault(require("process"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const debug = require('debug')('expo:metro:hermes');
function importHermesCommandFromProject(projectRoot) {
const platformExecutable = getHermesCommandPlatform();
const reactNativeRoot = path_1.default.dirname((0, resolve_from_1.default)(projectRoot, 'react-native/package.json'));
const hermesCompilerRoot = path_1.default.dirname((0, resolve_from_1.default)(reactNativeRoot, 'hermes-compiler/package.json'));
const hermescPaths = [
// Override hermesc dir by environment variables
process_1.default.env['REACT_NATIVE_OVERRIDE_HERMES_DIR']
? `${process_1.default.env['REACT_NATIVE_OVERRIDE_HERMES_DIR']}/build/bin/hermesc`
: '',
// Building hermes from source
`${reactNativeRoot}/ReactAndroid/hermes-engine/build/hermes/bin/hermesc`,
// react-native 0.83+ moved hermesc to a separate package
`${hermesCompilerRoot}/hermesc/${platformExecutable}`,
// Prebuilt hermesc in official react-native 0.69+
`${reactNativeRoot}/sdks/hermesc/${platformExecutable}`,
];
for (const hermescPath of hermescPaths) {
try {
if (fs_1.default.existsSync(hermescPath)) {
return hermescPath;
}
}
catch { }
}
throw new Error('Cannot find the hermesc executable.');
}
function getHermesCommandPlatform() {
switch (os_1.default.platform()) {
case 'darwin':
return 'osx-bin/hermesc';
case 'linux':
return 'linux64-bin/hermesc';
case 'win32':
return 'win64-bin/hermesc.exe';
default:
throw new Error(`Unsupported host platform for Hermes compiler: ${os_1.default.platform()}`);
}
}
// Only one hermes build at a time is supported.
let currentHermesBuild = null;
async function buildHermesBundleAsync(options) {
if (currentHermesBuild) {
debug(`Waiting for existing Hermes builds to finish`);
await currentHermesBuild;
}
currentHermesBuild = directlyBuildHermesBundleAsync(options);
return await currentHermesBuild;
}
async function directlyBuildHermesBundleAsync({ projectRoot, code, map, minify = false, filename, }) {
const tempDir = path_1.default.join(os_1.default.tmpdir(), `expo-bundler-${Math.random()}-${Date.now()}`);
await fs_1.default.promises.mkdir(tempDir, { recursive: true });
try {
const tempBundleFile = path_1.default.join(tempDir, 'index.js');
await fs_1.default.promises.writeFile(tempBundleFile, code, 'utf8');
if (map) {
const tempSourcemapFile = path_1.default.join(tempDir, 'index.js.map');
await fs_1.default.promises.writeFile(tempSourcemapFile, map, 'utf8');
}
const tempHbcFile = path_1.default.join(tempDir, 'index.hbc');
const hermesCommand = importHermesCommandFromProject(projectRoot);
const args = ['-emit-binary', '-out', tempHbcFile, tempBundleFile];
if (minify) {
args.push('-O');
}
if (map) {
args.push('-output-source-map');
}
debug(`Running hermesc: ${hermesCommand} ${args.join(' ')}`);
await (0, spawn_async_1.default)(hermesCommand, args);
let hbc;
let sourcemap = null;
if (!map) {
hbc = await fs_1.default.promises.readFile(tempHbcFile);
}
else {
[hbc, sourcemap] = await Promise.all([
fs_1.default.promises.readFile(tempHbcFile),
createHermesSourcemapAsync(map, `${tempHbcFile}.map`),
]);
}
return {
hbc,
sourcemap,
};
}
catch (error) {
console.error(chalk_1.default.red(`\nFailed to generate Hermes bytecode for: ${filename}`));
if ('status' in error) {
console.error(error.output.join('\n'));
}
throw error;
}
finally {
await fs_1.default.promises.rm(tempDir, { force: true, recursive: true });
}
}
async function createHermesSourcemapAsync(sourcemap, hermesMapFile) {
const bundlerSourcemap = JSON.parse(sourcemap);
const hermesSourcemapContent = await fs_1.default.promises.readFile(hermesMapFile, 'utf8');
const hermesSourcemap = JSON.parse(hermesSourcemapContent);
return JSON.stringify((0, metro_source_map_1.composeSourceMaps)([bundlerSourcemap, hermesSourcemap]));
}
//# sourceMappingURL=exportHermes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"exportHermes.js","sourceRoot":"","sources":["../../src/serializer/exportHermes.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,5 @@
export declare function getExportPathForDependencyWithOptions(dependencyPath: string, { platform, src, serverRoot }: {
platform: string;
serverRoot: string;
src: string;
}): string;

View File

@@ -0,0 +1,26 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getExportPathForDependencyWithOptions = getExportPathForDependencyWithOptions;
/**
* Copyright © 2023 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const path_1 = __importDefault(require("path"));
const getCssDeps_1 = require("./getCssDeps");
function getExportPathForDependencyWithOptions(dependencyPath, { platform, src, serverRoot }) {
const bundlePath = path_1.default.relative(serverRoot, dependencyPath);
const relativePathname = path_1.default.join(path_1.default.dirname(bundlePath),
// Strip the file extension
path_1.default.basename(bundlePath, path_1.default.extname(bundlePath)));
const name = (0, getCssDeps_1.fileNameFromContents)({
filepath: relativePathname,
src,
});
return `_expo/static/js/${platform}/${name}.js`;
}
//# sourceMappingURL=exportPath.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"exportPath.js","sourceRoot":"","sources":["../../src/serializer/exportPath.ts"],"names":[],"mappings":";;;;;AAUA,sFAeC;AAzBD;;;;;GAKG;AACH,gDAAwB;AAExB,6CAAoD;AAEpD,SAAgB,qCAAqC,CACnD,cAAsB,EACtB,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAyD;IAEpF,MAAM,UAAU,GAAG,cAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IAC7D,MAAM,gBAAgB,GAAG,cAAI,CAAC,IAAI,CAChC,cAAI,CAAC,OAAO,CAAC,UAAU,CAAC;IACxB,2BAA2B;IAC3B,cAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,cAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CACpD,CAAC;IACF,MAAM,IAAI,GAAG,IAAA,iCAAoB,EAAC;QAChC,QAAQ,EAAE,gBAAgB;QAC1B,GAAG;KACJ,CAAC,CAAC;IACH,OAAO,mBAAmB,QAAQ,IAAI,IAAI,KAAK,CAAC;AAClD,CAAC"}

View File

@@ -0,0 +1 @@
export declare const findUpPackageJsonPath: (root: string) => string;

View File

@@ -0,0 +1,25 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findUpPackageJsonPath = void 0;
/**
* Copyright © 2024 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const findUpPackageJsonPath = (root) => {
for (let dir = root; path_1.default.dirname(dir) !== dir; dir = path_1.default.dirname(dir)) {
const file = path_1.default.resolve(dir, 'package.json');
if (fs_1.default.existsSync(file)) {
return file;
}
}
throw new Error(`Cannot find package.json from "${root}"`);
};
exports.findUpPackageJsonPath = findUpPackageJsonPath;
//# sourceMappingURL=findUpPackageJsonPath.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"findUpPackageJsonPath.js","sourceRoot":"","sources":["../../src/serializer/findUpPackageJsonPath.ts"],"names":[],"mappings":";;;;;;AAAA;;;;;GAKG;AACH,4CAAoB;AACpB,gDAAwB;AAEjB,MAAM,qBAAqB,GAAG,CAAC,IAAY,EAAU,EAAE;IAC5D,KAAK,IAAI,GAAG,GAAG,IAAI,EAAE,cAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,GAAG,GAAG,cAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,GAAG,cAAI,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAC/C,IAAI,YAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,GAAG,CAAC,CAAC;AAC7D,CAAC,CAAC;AARW,QAAA,qBAAqB,yBAQhC"}

View File

@@ -0,0 +1,41 @@
/**
* Copyright © 2022 650 Industries.
* 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.
*
* Fork with bundle splitting and better source map support.
* https://github.com/facebook/metro/blob/bbdd7d7c5e6e0feb50a9967ffae1f723c1d7c4e8/packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js#L1
*/
import type { MixedOutput, Module, ReadOnlyGraph, SerializerOptions } from '@expo/metro/metro/DeltaBundler/types';
export type ModuleMap = [number, string][];
export type Bundle = {
modules: ModuleMap;
post: string;
pre: string;
paths: Record<string, Record<string, string>>;
};
export type ExpoSerializerOptions = SerializerOptions & {
serializerOptions?: {
baseUrl?: string;
skipWrapping?: boolean;
usedExports?: boolean;
splitChunks?: boolean;
output?: string;
includeSourceMaps?: boolean;
exporting?: boolean;
};
debugId?: string;
};
export declare function getPlatformOption(graph: Pick<ReadOnlyGraph, 'transformOptions'>, options: Pick<SerializerOptions, 'sourceUrl'>): string | null;
export declare function getBaseUrlOption(graph: Pick<ReadOnlyGraph, 'transformOptions'>, options: Pick<ExpoSerializerOptions, 'serializerOptions'>): string;
export declare function baseJSBundle(entryPoint: string, preModules: readonly Module[], graph: Pick<ReadOnlyGraph, 'dependencies' | 'transformOptions'>, options: ExpoSerializerOptions): Bundle;
export declare function baseJSBundleWithDependencies(entryPoint: string, preModules: readonly Module[], dependencies: Module<MixedOutput>[], options: ExpoSerializerOptions & {
platform: string;
baseUrl: string;
splitChunks: boolean;
skipWrapping: boolean;
computedAsyncModulePaths: Record<string, string> | null;
debugId?: string;
}): Bundle;

View File

@@ -0,0 +1,143 @@
"use strict";
/**
* Copyright © 2022 650 Industries.
* 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.
*
* Fork with bundle splitting and better source map support.
* https://github.com/facebook/metro/blob/bbdd7d7c5e6e0feb50a9967ffae1f723c1d7c4e8/packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js#L1
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPlatformOption = getPlatformOption;
exports.getBaseUrlOption = getBaseUrlOption;
exports.baseJSBundle = baseJSBundle;
exports.baseJSBundleWithDependencies = baseJSBundleWithDependencies;
const CountingSet_1 = __importDefault(require("@expo/metro/metro/lib/CountingSet"));
const countLines_1 = __importDefault(require("@expo/metro/metro/lib/countLines"));
const getAppendScripts_1 = __importDefault(require("@expo/metro/metro/lib/getAppendScripts"));
const jsc_safe_url_1 = require("jsc-safe-url");
const processModules_1 = require("./processModules");
function getPlatformOption(graph, options) {
if (graph.transformOptions?.platform != null) {
return graph.transformOptions.platform;
}
if (!options.sourceUrl) {
return null;
}
const sourceUrl = (0, jsc_safe_url_1.isJscSafeUrl)(options.sourceUrl)
? (0, jsc_safe_url_1.toNormalUrl)(options.sourceUrl)
: options.sourceUrl;
const url = new URL(sourceUrl, 'https://expo.dev');
return url.searchParams.get('platform') ?? null;
}
function getBaseUrlOption(graph, options) {
const baseUrl = graph.transformOptions?.customTransformOptions?.baseUrl;
if (typeof baseUrl === 'string') {
// This tells us that the value came over a URL and may be encoded.
const mayBeEncoded = options.serializerOptions == null;
const option = mayBeEncoded ? decodeURIComponent(baseUrl) : baseUrl;
return option.replace(/\/+$/, '') + '/';
}
return '/';
}
function baseJSBundle(entryPoint, preModules, graph, options) {
const platform = getPlatformOption(graph, options);
if (platform == null) {
throw new Error('platform could not be determined for Metro bundle');
}
return baseJSBundleWithDependencies(entryPoint, preModules, [...graph.dependencies.values()], {
...options,
baseUrl: getBaseUrlOption(graph, options),
splitChunks: !!options.serializerOptions?.splitChunks,
platform,
skipWrapping: !!options.serializerOptions?.skipWrapping,
computedAsyncModulePaths: null,
});
}
function baseJSBundleWithDependencies(entryPoint, preModules, dependencies, options) {
for (const module of dependencies) {
options.createModuleId(module.path);
}
const processModulesOptions = {
filter: options.processModuleFilter,
createModuleId: options.createModuleId,
dev: options.dev,
includeAsyncPaths: options.includeAsyncPaths,
projectRoot: options.projectRoot,
serverRoot: options.serverRoot,
sourceUrl: options.sourceUrl,
platform: options.platform,
baseUrl: options.baseUrl,
splitChunks: options.splitChunks,
skipWrapping: options.skipWrapping,
computedAsyncModulePaths: options.computedAsyncModulePaths,
};
// Do not prepend polyfills or the require runtime when only modules are requested
if (options.modulesOnly) {
preModules = [];
}
const preCode = (0, processModules_1.processModules)(preModules, processModulesOptions)
.map(([, code]) => code.src)
.join('\n');
const modules = [...dependencies].sort((a, b) => options.createModuleId(a.path) - options.createModuleId(b.path));
const sourceMapUrl = options.serializerOptions?.includeSourceMaps === false ? undefined : options.sourceMapUrl;
const modulesWithAnnotations = (0, getAppendScripts_1.default)(entryPoint, [...preModules, ...modules], {
asyncRequireModulePath: options.asyncRequireModulePath,
createModuleId: options.createModuleId,
getRunModuleStatement: options.getRunModuleStatement,
globalPrefix: options.globalPrefix,
inlineSourceMap: options.inlineSourceMap,
runBeforeMainModule: options.runBeforeMainModule,
runModule: options.runModule,
shouldAddToIgnoreList: options.shouldAddToIgnoreList,
sourceMapUrl,
// This directive doesn't make a lot of sense in the context of a large single bundle that represent
// multiple files. It's usually used for things like TypeScript where you want the file name to appear with a
// different extension. Since it's unclear to me (Bacon) how it is used on native, I'm only disabling in web and native in production.
sourceUrl: options.platform === 'web' ? undefined : !options.dev ? undefined : options.sourceUrl,
});
// If the `debugId` annotation is available and we aren't inlining the source map, add it to the bundle.
// NOTE: We may want to move this assertion up further.
const hasExternalMaps = !options.inlineSourceMap && !!sourceMapUrl;
if (hasExternalMaps && options.debugId != null) {
const code = `//# debugId=${options.debugId}`;
modulesWithAnnotations.push({
path: 'debug-id-annotation',
dependencies: new Map(),
getSource: () => Buffer.from(''),
inverseDependencies: new CountingSet_1.default(),
output: [
{
type: 'js/script/virtual',
data: {
code,
lineCount: (0, countLines_1.default)(code),
map: [],
},
},
],
});
}
const postCode = (0, processModules_1.processModules)(modulesWithAnnotations, processModulesOptions)
.map(([, code]) => code.src)
.join('\n');
const mods = (0, processModules_1.processModules)([...dependencies], processModulesOptions).map(([module, code]) => [
options.createModuleId(module.path),
code,
]);
return {
pre: preCode,
post: postCode,
modules: mods.map(([id, code]) => [
id,
typeof code === 'number' ? code : code.src,
]),
paths: Object.fromEntries(mods.filter(([id, code]) => typeof code !== 'number' && Object.keys(code.paths).length).map(([id, code]) => [id, code.paths])),
};
}
//# sourceMappingURL=baseJSBundle.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"baseJSBundle.js","sourceRoot":"","sources":["../../../src/serializer/fork/baseJSBundle.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,25 @@
/**
* Copyright © 2025 650 Industries.
* 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.
*
* Fork with support for using the same serializer paths as production and the first bundle.
* https://github.com/facebook/metro/blob/87f717b8f5987827c75c82b3cb390060672628f0/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js#L1C1-L152C30
*/
import type { DeltaResult, ReadOnlyGraph } from '@expo/metro/metro/DeltaBundler';
import type { HmrModule } from '@expo/metro/metro-runtime/modules/types';
type Options = {
clientUrl: URL;
createModuleId: (id: string) => number;
includeAsyncPaths: boolean;
projectRoot: string;
serverRoot: string;
};
declare function hmrJSBundle(delta: DeltaResult<any>, graph: ReadOnlyGraph<any>, options: Options): {
added: readonly HmrModule[];
modified: readonly HmrModule[];
deleted: readonly number[];
};
export default hmrJSBundle;

View File

@@ -0,0 +1,99 @@
"use strict";
/**
* Copyright © 2025 650 Industries.
* 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.
*
* Fork with support for using the same serializer paths as production and the first bundle.
* https://github.com/facebook/metro/blob/87f717b8f5987827c75c82b3cb390060672628f0/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js#L1C1-L152C30
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const metro_transform_plugins_1 = require("@expo/metro/metro-transform-plugins");
const jsc_safe_url_1 = __importDefault(require("jsc-safe-url"));
const node_path_1 = __importDefault(require("node:path"));
const js_1 = require("./js");
const debug = require('debug')('Metro:HMR');
function generateModules(sourceModules, graph, options) {
const modules = [];
for (const module of sourceModules) {
if ((0, js_1.isJsModule)(module)) {
const getPathname = (extension) => {
return (node_path_1.default
.relative(options.serverRoot ?? options.projectRoot, node_path_1.default.join(node_path_1.default.dirname(module.path), node_path_1.default.basename(module.path, node_path_1.default.extname(module.path)) + '.' + extension))
.split(node_path_1.default.sep)
// using this Metro particular convention for encoding file paths as URL paths.
.map((segment) => encodeURIComponent(segment))
.join('/'));
};
const clientUrl = new URL(options.clientUrl);
clientUrl.searchParams.delete('excludeSource');
clientUrl.pathname = getPathname('map');
const sourceMappingURL = clientUrl.toString();
clientUrl.pathname = getPathname('bundle');
const sourceURL = jsc_safe_url_1.default.toJscSafeUrl(clientUrl.toString());
debug('got sourceMappingURL: %s\nand sourceURL: %s\nfor module: %s', sourceMappingURL, sourceURL, module.path);
const code = prepareModule(module, graph, options) +
`\n//# sourceMappingURL=${sourceMappingURL}\n` +
`//# sourceURL=${sourceURL}\n`;
modules.push({
module: [options.createModuleId(module.path), code],
sourceMappingURL,
sourceURL,
});
}
}
return modules;
}
function prepareModule(module, graph, options) {
const code = (0, js_1.wrapModule)(module, {
...options,
sourceUrl: options.clientUrl.toString(),
dev: true,
skipWrapping: false,
computedAsyncModulePaths: null,
splitChunks: false,
});
const inverseDependencies = getInverseDependencies(module.path, graph);
// Transform the inverse dependency paths to ids.
const inverseDependenciesById = Object.create(null);
Object.keys(inverseDependencies).forEach((path) => {
inverseDependenciesById[options.createModuleId(path)] = inverseDependencies[path].map(options.createModuleId);
});
return (0, metro_transform_plugins_1.addParamsToDefineCall)(code.src, inverseDependenciesById);
}
/**
* Instead of adding the whole inverseDependencies object into each changed
* module (which can be really huge if the dependency graph is big), we only
* add the needed inverseDependencies for each changed module (we do this by
* traversing upwards the dependency graph).
*/
function getInverseDependencies(path, graph, inverseDependencies = {}) {
// Dependency already traversed.
if (path in inverseDependencies) {
return inverseDependencies;
}
const module = graph.dependencies.get(path);
if (!module) {
return inverseDependencies;
}
inverseDependencies[path] = [];
for (const inverse of module.inverseDependencies) {
inverseDependencies[path].push(inverse);
getInverseDependencies(inverse, graph, inverseDependencies);
}
return inverseDependencies;
}
function hmrJSBundle(delta, graph, options) {
return {
added: generateModules(delta.added.values(), graph, options),
modified: generateModules(delta.modified.values(), graph, options),
deleted: [...delta.deleted].map((path) => options.createModuleId(path)),
};
}
exports.default = hmrJSBundle;
//# sourceMappingURL=hmrJSBundle.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hmrJSBundle.js","sourceRoot":"","sources":["../../../src/serializer/fork/hmrJSBundle.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,37 @@
/**
* Copyright © 2022 650 Industries.
* 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.
*
* Fork of the metro helper, but with bundle splitting support.
* https://github.com/facebook/metro/blob/bbdd7d7c5e6e0feb50a9967ffae1f723c1d7c4e8/packages/metro/src/DeltaBundler/Serializers/helpers/js.js#L1
*/
import type { MixedOutput, Module } from '@expo/metro/metro/DeltaBundler';
import type { JsOutput } from '@expo/metro/metro-transform-worker';
export type Options = {
createModuleId: (module: string) => number | string;
dev: boolean;
includeAsyncPaths: boolean;
projectRoot: string;
serverRoot: string;
sourceUrl?: string | null;
splitChunks: boolean;
skipWrapping: boolean;
computedAsyncModulePaths: Record<string, string> | null;
};
export declare function wrapModule(module: Module, options: Options): {
src: string;
paths: Record<string, string>;
};
export declare function getModuleParams(module: Module, options: Pick<Options, 'createModuleId' | 'sourceUrl' | 'includeAsyncPaths' | 'serverRoot' | 'splitChunks' | 'dev' | 'projectRoot' | 'computedAsyncModulePaths'>): {
params: any[];
paths: Record<string, string>;
};
export declare function getJsOutput(module: {
output: readonly MixedOutput[];
path?: string;
}): JsOutput;
export declare function isJsModule(module: Module): boolean;
export declare function isJsOutput(output: MixedOutput): output is MixedOutput;

View File

@@ -0,0 +1,126 @@
"use strict";
/**
* Copyright © 2022 650 Industries.
* 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.
*
* Fork of the metro helper, but with bundle splitting support.
* https://github.com/facebook/metro/blob/bbdd7d7c5e6e0feb50a9967ffae1f723c1d7c4e8/packages/metro/src/DeltaBundler/Serializers/helpers/js.js#L1
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.wrapModule = wrapModule;
exports.getModuleParams = getModuleParams;
exports.getJsOutput = getJsOutput;
exports.isJsModule = isJsModule;
exports.isJsOutput = isJsOutput;
const isResolvedDependency_1 = require("@expo/metro/metro/lib/isResolvedDependency");
const metro_transform_plugins_1 = require("@expo/metro/metro-transform-plugins");
const assert_1 = __importDefault(require("assert"));
const jsc_safe_url_1 = __importDefault(require("jsc-safe-url"));
const path_1 = __importDefault(require("path"));
const filePath_1 = require("../../utils/filePath");
function wrapModule(module, options) {
const output = getJsOutput(module);
if (output.type.startsWith('js/script')) {
return { src: output.data.code, paths: {} };
}
const { params, paths } = getModuleParams(module, options);
const src = (0, metro_transform_plugins_1.addParamsToDefineCall)(output.data.code, ...params);
return { src, paths };
}
function getModuleParams(module, options) {
const moduleId = options.createModuleId(module.path);
const paths = {};
let hasPaths = false;
const dependencyMapArray = Array.from(module.dependencies.values()).map((dependency) => {
if (!(0, isResolvedDependency_1.isResolvedDependency)(dependency)) {
return null;
}
let modulePath = dependency.absolutePath;
if (modulePath == null) {
if (dependency.data.data.isOptional) {
// For optional dependencies, that could not be resolved.
modulePath = dependency.data.name;
}
else {
throw new Error(`Module "${module.path}" has a dependency with missing absolutePath: ${JSON.stringify(dependency, null, 2)}`);
}
}
const id = options.createModuleId(modulePath);
if (
// NOTE(EvanBacon): Disabled this to ensure that paths are provided even when the entire bundle
// is created. This is required for production bundle splitting.
// options.includeAsyncPaths &&
dependency.data.data.asyncType != null) {
if (options.includeAsyncPaths) {
if (options.sourceUrl) {
hasPaths = true;
// TODO: Only include path if the target is not in the bundle
// Construct a server-relative URL for the split bundle, propagating
// most parameters from the main bundle's URL.
const { searchParams } = new URL(jsc_safe_url_1.default.toNormalUrl(options.sourceUrl));
if (dependency.data.data.asyncType === 'worker') {
// Include all modules and run the module when of type worker.
searchParams.set('modulesOnly', 'false');
searchParams.set('runModule', 'true');
searchParams.delete('shallow');
}
else {
searchParams.set('modulesOnly', 'true');
searchParams.set('runModule', 'false');
}
const bundlePath = path_1.default.relative(options.serverRoot, dependency.absolutePath);
paths[id] =
'/' +
path_1.default.join(
// TODO: This is not the proper Metro URL encoding of a file path
path_1.default.dirname(bundlePath),
// Strip the file extension
path_1.default.basename(bundlePath, path_1.default.extname(bundlePath))) +
'.bundle?' +
searchParams.toString();
}
}
else if (options.splitChunks && options.computedAsyncModulePaths != null) {
hasPaths = true;
// A template string that we'll match and replace later when we know the content hash for a given path.
paths[id] = options.computedAsyncModulePaths[dependency.absolutePath];
}
}
return id;
});
const params = [
moduleId,
hasPaths
? {
...dependencyMapArray,
paths,
}
: dependencyMapArray,
];
if (options.dev) {
// Add the relative path of the module to make debugging easier.
// This is mapped to `module.verboseName` in `require.js`.
params.push((0, filePath_1.toPosixPath)(path_1.default.relative(options.projectRoot, module.path)));
}
return { params, paths };
}
function getJsOutput(module) {
const jsModules = module.output.filter(({ type }) => type.startsWith('js/'));
(0, assert_1.default)(jsModules.length === 1, `Modules must have exactly one JS output, but ${module.path ?? 'unknown module'} has ${jsModules.length} JS outputs.`);
const jsOutput = jsModules[0];
(0, assert_1.default)(Number.isFinite(jsOutput.data.lineCount), `JS output must populate lineCount, but ${module.path ?? 'unknown module'} has ${jsOutput.type} output with lineCount '${jsOutput.data.lineCount}'`);
return jsOutput;
}
function isJsModule(module) {
return module.output.filter(isJsOutput).length > 0;
}
function isJsOutput(output) {
return output.type.startsWith('js/');
}
//# sourceMappingURL=js.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"js.js","sourceRoot":"","sources":["../../../src/serializer/fork/js.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,23 @@
/**
* Copyright © 2022 650 Industries.
* 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.
*/
import type { Module } from '@expo/metro/metro/DeltaBundler';
export declare function processModules(modules: readonly Module[], { filter, createModuleId, dev, includeAsyncPaths, projectRoot, serverRoot, sourceUrl, splitChunks, skipWrapping, computedAsyncModulePaths, }: {
splitChunks: boolean;
filter?: (module: Module) => boolean;
createModuleId: (module: string) => number;
dev: boolean;
includeAsyncPaths: boolean;
projectRoot: string;
serverRoot: string;
sourceUrl?: string | null;
skipWrapping: boolean;
computedAsyncModulePaths: Record<string, string> | null;
}): readonly [Module, {
src: string;
paths: Record<string, string>;
}][];

View File

@@ -0,0 +1,31 @@
"use strict";
/**
* Copyright © 2022 650 Industries.
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.processModules = processModules;
const js_1 = require("./js");
function processModules(modules, { filter = () => true, createModuleId, dev, includeAsyncPaths, projectRoot, serverRoot, sourceUrl, splitChunks, skipWrapping, computedAsyncModulePaths, }) {
return [...modules]
.filter(js_1.isJsModule)
.filter(filter)
.map((module) => [
module,
(0, js_1.wrapModule)(module, {
splitChunks,
createModuleId,
dev,
includeAsyncPaths,
projectRoot,
serverRoot,
sourceUrl,
skipWrapping,
computedAsyncModulePaths,
}),
]);
}
//# sourceMappingURL=processModules.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"processModules.js","sourceRoot":"","sources":["../../../src/serializer/fork/processModules.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAMH,wCA2CC;AA7CD,6BAA8C;AAE9C,SAAgB,cAAc,CAC5B,OAA0B,EAC1B,EACE,MAAM,GAAG,GAAG,EAAE,CAAC,IAAI,EACnB,cAAc,EACd,GAAG,EACH,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,wBAAwB,GAYzB;IAED,OAAO,CAAC,GAAG,OAAO,CAAC;SAChB,MAAM,CAAC,eAAU,CAAC;SAClB,MAAM,CAAC,MAAM,CAAC;SACd,GAAG,CAAC,CAAC,MAAc,EAAE,EAAE,CAAC;QACvB,MAAM;QACN,IAAA,eAAU,EAAC,MAAM,EAAE;YACjB,WAAW;YACX,cAAc;YACd,GAAG;YACH,iBAAiB;YACjB,WAAW;YACX,UAAU;YACV,SAAS;YACT,YAAY;YACZ,wBAAwB;SACzB,CAAC;KACH,CAAC,CAAC;AACP,CAAC"}

View File

@@ -0,0 +1,34 @@
import type { Module, ReadOnlyDependencies } from '@expo/metro/metro/DeltaBundler/types';
import { SerialAsset } from './serializerAssets';
type Options = {
processModuleFilter: (modules: Module) => boolean;
assetPlugins: readonly string[];
platform?: string | null;
projectRoot: string;
publicPath: string;
};
export type JSModule = Module<{
data: {
code: string;
map: unknown;
lineCount: number;
css?: {
code: string;
map: unknown;
lineCount: number;
skipCache?: boolean;
};
};
type: 'js/module';
}> & {
unstable_transformResultKey?: string;
};
export declare function getCssSerialAssets<T extends any>(dependencies: ReadOnlyDependencies<T>, { projectRoot, entryFile }: Pick<Options, 'projectRoot'> & {
entryFile: string;
}): SerialAsset[];
export declare function fileNameFromContents({ filepath, src }: {
filepath: string;
src: string;
}): string;
export declare function getFileName(module: string): string;
export {};

View File

@@ -0,0 +1,114 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCssSerialAssets = getCssSerialAssets;
exports.fileNameFromContents = fileNameFromContents;
exports.getFileName = getFileName;
// NOTE(@kitten): jest-resolver -> resolve.exports bug (https://github.com/lukeed/resolve.exports/issues/40)
const js_js_1 = require("@expo/metro/metro/DeltaBundler/Serializers/helpers/js.js");
const isResolvedDependency_1 = require("@expo/metro/metro/lib/isResolvedDependency");
const path_1 = __importDefault(require("path"));
const css_1 = require("../transform-worker/css");
const filePath_1 = require("../utils/filePath");
const hash_1 = require("../utils/hash");
// s = static
const STATIC_EXPORT_DIRECTORY = '_expo/static/css';
function isTypeJSModule(module) {
return (0, js_js_1.isJsModule)(module);
}
function getCssSerialAssets(dependencies, { projectRoot, entryFile }) {
const assets = [];
const visited = new Set();
function pushCssModule(module) {
const cssMetadata = getCssMetadata(module);
if (cssMetadata) {
const contents = cssMetadata.code;
// NOTE(cedric): these relative paths are used as URL pathnames when serializing HTML
// Use POSIX-format to avoid urls like `_expo/static/css/some\\file\\name.css`
const originFilename = (0, filePath_1.toPosixPath)(path_1.default.relative(projectRoot, module.path));
const filename = (0, filePath_1.toPosixPath)(path_1.default.join(
// Consistent location
STATIC_EXPORT_DIRECTORY,
// Hashed file contents + name for caching
fileNameFromContents({
// Stable filename for hashing in CI.
filepath: originFilename,
src: contents,
}) + '.css'));
if (cssMetadata.externalImports) {
for (const external of cssMetadata.externalImports) {
let source = `<link rel="stylesheet" href="${external.url}"`;
// TODO: How can we do this for local css imports?
if (external.media) {
source += `media="${external.media}"`;
}
// TODO: supports attribute
source += '>';
assets.push({
type: 'css-external',
originFilename,
filename: external.url,
// Link CSS file
source,
metadata: {
hmrId: (0, css_1.pathToHtmlSafeName)(originFilename),
},
});
}
}
assets.push({
type: 'css',
originFilename,
filename,
source: contents,
metadata: {
hmrId: (0, css_1.pathToHtmlSafeName)(originFilename),
},
});
}
}
function checkDep(absolutePath) {
if (visited.has(absolutePath)) {
return;
}
visited.add(absolutePath);
const next = dependencies.get(absolutePath);
if (!next) {
return;
}
next.dependencies.forEach((dep) => {
// Traverse the deps next to ensure the CSS is pushed in the correct order.
if ((0, isResolvedDependency_1.isResolvedDependency)(dep)) {
checkDep(dep.absolutePath);
}
});
// Then push the JS after the siblings.
if (getCssMetadata(next) && isTypeJSModule(next)) {
pushCssModule(next);
}
}
checkDep(entryFile);
return assets;
}
function getCssMetadata(module) {
const data = module.output[0]?.data;
if (data && typeof data === 'object' && 'css' in data) {
if (typeof data.css !== 'object' || !('code' in data.css)) {
throw new Error(`Unexpected CSS metadata in Metro module (${module.path}): ${JSON.stringify(data.css)}`);
}
return data.css;
}
return null;
}
function fileNameFromContents({ filepath, src }) {
// TODO(@kitten): As of metro@0.83.2 but maybe before, this does not look correct. Encoding has changed, see: https://github.com/facebook/metro/commit/cb02cdb
// Decode if the path is encoded from the Metro dev server, then normalize paths for Windows support.
const decoded = decodeURIComponent(filepath).replace(/\\/g, '/');
return getFileName(decoded) + '-' + (0, hash_1.hashString)(src);
}
function getFileName(module) {
return path_1.default.basename(module).replace(/\.[^.]+$/, '');
}
//# sourceMappingURL=getCssDeps.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getCssDeps.js","sourceRoot":"","sources":["../../src/serializer/getCssDeps.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,74 @@
/**
* Copyright © 2023-present 650 Industries (Expo). All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { types as t } from '@babel/core';
import type { FBSourceFunctionMap, MetroSourceMapSegmentTuple } from '@expo/metro/metro-source-map';
import type { JsTransformerConfig } from '@expo/metro/metro-transform-worker';
import { Options as CollectDependenciesOptions } from '../transform-worker/collect-dependencies';
export type JSFileType = 'js/script' | 'js/module' | 'js/module/asset';
export type JsOutput = {
data: {
code: string;
lineCount: number;
map: MetroSourceMapSegmentTuple[];
functionMap: FBSourceFunctionMap | null;
css?: {
code: string;
lineCount: number;
map: MetroSourceMapSegmentTuple[];
functionMap: FBSourceFunctionMap | null;
};
ast?: t.File;
hasCjsExports?: boolean;
readonly reconcile?: ReconcileTransformSettings;
readonly reactServerReference?: string;
readonly reactClientReference?: string;
readonly expoDomComponentReference?: string;
readonly loaderReference?: string;
};
type: JSFileType;
};
export type CSSMetadata = {
code: string;
lineCount: number;
map: unknown[];
functionMap: null;
skipCache?: boolean;
externalImports: {
url: string;
supports: string | null;
media: string | null;
}[];
};
export type ExpoJsOutput = Omit<JsOutput, 'data'> & {
data: JsOutput['data'] & {
profiling?: {
start: number;
end: number;
duration: number;
};
css?: CSSMetadata;
};
};
export type ReconcileTransformSettings = {
inlineRequires: boolean;
importDefault: string;
importAll: string;
globalPrefix: string;
unstable_renameRequire?: boolean;
unstable_compactOutput?: boolean;
minify?: {
minifierPath: string;
minifierConfig: JsTransformerConfig['minifierConfig'];
};
collectDependenciesOptions: CollectDependenciesOptions;
unstable_dependencyMapReservedName?: string | null;
optimizationSizeLimit?: number;
unstable_disableNormalizePseudoGlobals?: boolean;
normalizePseudoGlobals: boolean;
};
export declare function isExpoJsOutput(output: any): output is ExpoJsOutput;
export declare function isTransformOptionTruthy(option: any): boolean;

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isExpoJsOutput = isExpoJsOutput;
exports.isTransformOptionTruthy = isTransformOptionTruthy;
function isExpoJsOutput(output) {
return 'data' in output && typeof output.data === 'object';
}
// Because transform options can be passed directly during export, or through a query parameter
// during a request, we need to normalize the options.
function isTransformOptionTruthy(option) {
return option === true || option === 'true' || option === '1';
}
//# sourceMappingURL=jsOutput.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"jsOutput.js","sourceRoot":"","sources":["../../src/serializer/jsOutput.ts"],"names":[],"mappings":";;AAiFA,wCAEC;AAID,0DAEC;AARD,SAAgB,cAAc,CAAC,MAAW;IACxC,OAAO,MAAM,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC7D,CAAC;AAED,+FAA+F;AAC/F,sDAAsD;AACtD,SAAgB,uBAAuB,CAAC,MAAW;IACjD,OAAO,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,GAAG,CAAC;AAChE,CAAC"}

View File

@@ -0,0 +1,9 @@
import type { MixedOutput, Module, ReadOnlyGraph, SerializerOptions } from '@expo/metro/metro/DeltaBundler/types';
import type { SerializerConfigT } from '@expo/metro/metro-config';
import { Dependency } from '../transform-worker/collect-dependencies';
type Serializer = NonNullable<SerializerConfigT['customSerializer']>;
type SerializerParameters = Parameters<Serializer>;
export declare function sortDependencies(dependencies: readonly Dependency[], accordingTo: Module['dependencies']): Map<string, Dependency>;
export declare function isEnvBoolean(graph: ReadOnlyGraph, name: string): boolean;
export declare function reconcileTransformSerializerPlugin(entryPoint: string, preModules: readonly Module<MixedOutput>[], graph: ReadOnlyGraph, options: SerializerOptions): Promise<SerializerParameters>;
export {};

View File

@@ -0,0 +1,259 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sortDependencies = sortDependencies;
exports.isEnvBoolean = isEnvBoolean;
exports.reconcileTransformSerializerPlugin = reconcileTransformSerializerPlugin;
const generator_1 = __importDefault(require("@babel/generator"));
const JsFileWrapping = __importStar(require("@expo/metro/metro/ModuleGraph/worker/JsFileWrapping"));
const importLocationsPlugin_1 = require("@expo/metro/metro/ModuleGraph/worker/importLocationsPlugin");
const isResolvedDependency_1 = require("@expo/metro/metro/lib/isResolvedDependency");
const metro_source_map_1 = require("@expo/metro/metro-source-map");
const metro_transform_plugins_1 = require("@expo/metro/metro-transform-plugins");
const assert_1 = __importDefault(require("assert"));
const node_util_1 = __importDefault(require("node:util"));
const jsOutput_1 = require("./jsOutput");
const sideEffects_1 = require("./sideEffects");
const collect_dependencies_1 = __importStar(require("../transform-worker/collect-dependencies"));
const count_lines_1 = require("../transform-worker/count-lines");
const metro_transform_worker_1 = require("../transform-worker/metro-transform-worker");
const debug = require('debug')('expo:treeshaking');
const FORCE_REQUIRE_NAME_HINTS = false;
// Some imports may change order during the transform, so we need to resort them.
// Resort the dependencies to match the current order of the AST.
function sortDependencies(dependencies, accordingTo) {
// Some imports may change order during the transform, so we need to resort them.
// Resort the dependencies to match the current order of the AST.
const nextDependencies = new Map();
const findDependency = (dep) => {
const original = accordingTo.get(dep.data.key);
// We can do a quick check first but this may not always work.
//
// In cases where the original import was ESM but mutated during tree-shaking (such as `export * from "./"`) then the
// key will always be based on CJS because we need to transform before collecting a second time.
//
// In this case, we'll create the inverse key based on ESM to try and find the original dependency.
if (original) {
return original;
}
// Only perform the hacky inverse key check if it's this specific case that we know about, otherwise throw an error.
if (dep.data.isESMImport === false) {
const inverseKey = (0, collect_dependencies_1.hashKey)((0, collect_dependencies_1.getKeyForDependency)({
asyncType: dep.data.asyncType,
isESMImport: !dep.data.isESMImport,
name: dep.name,
contextParams: dep.data.contextParams,
}));
if (accordingTo.has(inverseKey)) {
return accordingTo.get(inverseKey);
}
}
// If the dependency was optional, then we can skip throwing the error.
if (dep.data.isOptional) {
return null;
}
debug('failed to finding matching dependency', node_util_1.default.inspect(dep, { colors: true, depth: 6 }), node_util_1.default.inspect(accordingTo, { colors: true, depth: 6 }));
throw new Error(`Dependency ${dep.data.key} (${dep.name}) not found in the original module during optimization pass. Available keys: ${Array.from(accordingTo.entries())
.map(([key, dep]) => `${key} (${dep.data.name})`)
.join(', ')}`);
};
// Metro uses this Map hack so we need to create a new map and add the items in the expected order/
dependencies.forEach((dep) => {
const original = findDependency(dep);
// In the case of missing optional dependencies, the absolutePath will not be defined.
if (!original) {
nextDependencies.set(dep.data.key, {
// @ts-expect-error: Missing async types. This could be a problem for bundle splitting.
data: dep,
});
}
nextDependencies.set(dep.data.key, {
...original,
// @ts-expect-error: Missing async types. This could be a problem for bundle splitting.
data: dep,
});
});
return nextDependencies;
}
function isOptimizeEnabled(graph) {
return isEnvBoolean(graph, 'optimize');
}
function isEnvBoolean(graph, name) {
if (!graph.transformOptions.customTransformOptions)
return false;
return String(graph.transformOptions.customTransformOptions[name]) === 'true';
}
// This is the insane step which reconciles the second half of the transformation process but it does it uncached at the end of the bundling process when we have tree shaking completed.
async function reconcileTransformSerializerPlugin(entryPoint, preModules, graph, options) {
if (!isOptimizeEnabled(graph)) {
return [entryPoint, preModules, graph, options];
}
// Convert all remaining AST and dependencies to standard output that Metro expects.
// This is normally done in the transformer, but we skipped it so we could perform graph analysis (tree-shake).
for (const value of graph.dependencies.values()) {
for (const index in value.output) {
const output = value.output[index];
if ((0, jsOutput_1.isExpoJsOutput)(output)) {
// @ts-expect-error: Typed as readonly
value.output[index] =
//
await transformDependencyOutput(value, output);
}
}
}
return [entryPoint, preModules, graph, options];
async function transformDependencyOutput(value, outputItem) {
if (outputItem.type !== 'js/module' ||
value.path.endsWith('.json') ||
value.path.match(/\.(s?css|sass)$/)) {
debug('Skipping post transform for non-js/module: ' + value.path);
return outputItem;
}
// This should be cached by the transform worker for use here to ensure close to consistent
// results between the tree-shake and the final transform.
const reconcile = outputItem.data.reconcile;
(0, assert_1.default)(reconcile, 'reconcile settings are required in the module graph for post transform.');
let ast = outputItem.data.ast;
(0, assert_1.default)(ast, 'Missing AST for module: ' + value.path);
delete outputItem.data.ast;
const { importDefault, importAll } = reconcile;
const sideEffectReferences = () => [...value.dependencies.values()]
.filter((dep) => {
const fullDep = (0, isResolvedDependency_1.isResolvedDependency)(dep)
? graph.dependencies.get(dep.absolutePath)
: undefined;
return fullDep && (0, sideEffects_1.hasSideEffectWithDebugTrace)(options, graph, fullDep)[0];
})
.map((dep) => dep.data.name);
const file = (0, metro_transform_worker_1.applyImportSupport)(ast, {
collectLocations: true,
filename: value.path,
importAll,
importDefault,
options: {
// NOTE: This might not be needed...
...graph.transformOptions,
experimentalImportSupport: true,
inlineRequires: reconcile.inlineRequires,
// Add side-effects to the ignore list.
nonInlinedRequires: reconcile.inlineRequires
? graph.transformOptions.nonInlinedRequires
? sideEffectReferences().concat(graph.transformOptions.nonInlinedRequires)
: sideEffectReferences()
: [],
},
});
ast = file.ast;
let dependencyMapName = '';
let dependencies;
const importDeclarationLocs = file.metadata?.metro?.unstable_importDeclarationLocs ?? null;
// This pass converts the modules to use the generated import names.
try {
// Rewrite the deps to use Metro runtime, collect the new dep positions.
({ ast, dependencies, dependencyMapName } = (0, collect_dependencies_1.default)(ast, {
...reconcile.collectDependenciesOptions,
unstable_isESMImportAtSource: importDeclarationLocs != null
? (loc) => {
return importDeclarationLocs.has((0, importLocationsPlugin_1.locToKey)(loc));
}
: null,
collectOnly: false,
// This is here for debugging purposes.
keepRequireNames: FORCE_REQUIRE_NAME_HINTS,
// This setting shouldn't be shared + it can't be serialized and cached anyways.
dependencyTransformer: undefined,
}));
}
catch (error) {
if (error instanceof collect_dependencies_1.InvalidRequireCallError) {
throw new metro_transform_worker_1.InvalidRequireCallError(error, value.path);
}
throw error;
}
// @ts-expect-error: Mutating the value in place.
value.dependencies =
//
sortDependencies(dependencies, value.dependencies);
const { ast: wrappedAst } = JsFileWrapping.wrapModule(ast, reconcile.importDefault, reconcile.importAll, dependencyMapName, reconcile.globalPrefix, reconcile.unstable_renameRequire === false);
const reserved = [];
if (reconcile.unstable_dependencyMapReservedName != null) {
reserved.push(reconcile.unstable_dependencyMapReservedName);
}
if (reconcile.normalizePseudoGlobals) {
// This MUST run before `generate` as it mutates the ast out of place.
reserved.push(...(0, metro_transform_plugins_1.normalizePseudoGlobals)(wrappedAst, {
reservedNames: reserved,
}));
}
const result = (0, generator_1.default)(wrappedAst, {
// comments: true,
// https://github.com/facebook/metro/blob/6151e7eb241b15f3bb13b6302abeafc39d2ca3ad/packages/metro-config/src/defaults/index.js#L137
compact: reconcile.unstable_compactOutput,
filename: value.path,
retainLines: false,
sourceFileName: value.path,
sourceMaps: true,
}, outputItem.data.code);
// @ts-expect-error: incorrectly typed upstream
let map = result.rawMappings ? result.rawMappings.map(metro_source_map_1.toSegmentTuple) : [];
let code = result.code;
if (reconcile.minify) {
const source = value.getSource().toString('utf-8');
({ map, code } = await (0, metro_transform_worker_1.minifyCode)(reconcile.minify, value.path, result.code, source, map, reserved));
}
let lineCount;
({ lineCount, map } = (0, count_lines_1.countLinesAndTerminateMap)(code, map));
return {
...outputItem,
data: {
...outputItem.data,
code,
map,
lineCount,
functionMap:
// @ts-expect-error: https://github.com/facebook/metro/blob/6151e7eb241b15f3bb13b6302abeafc39d2ca3ad/packages/metro-transform-worker/src/index.js#L508-L512
ast.metadata?.metro?.functionMap ??
// @ts-expect-error: Fallback to deprecated explicitly-generated `functionMap`
ast.functionMap ??
outputItem.data.functionMap ??
null,
},
};
}
}
//# sourceMappingURL=reconcileTransformSerializerPlugin.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,52 @@
/**
* Copyright © 2023 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { MetroConfig, AssetData } from '@expo/metro/metro';
import type { MixedOutput, Module, ReadOnlyGraph } from '@expo/metro/metro/DeltaBundler/types';
import type { ConfigT, SerializerConfigT } from '@expo/metro/metro-config';
import type { ExpoSerializerOptions } from './fork/baseJSBundle';
import { SerialAsset } from './serializerAssets';
import { SerializerConfigOptions } from './withExpoSerializers';
type Serializer = NonNullable<ConfigT['serializer']['customSerializer']>;
type SerializerParameters = Parameters<Serializer>;
export type SerializeChunkOptions = {
includeSourceMaps: boolean;
splitChunks: boolean;
} & SerializerConfigOptions;
export declare function graphToSerialAssetsAsync(config: MetroConfig, serializeChunkOptions: SerializeChunkOptions, ...props: SerializerParameters): Promise<{
artifacts: SerialAsset[] | null;
assets: AssetData[];
}>;
export declare class Chunk {
name: string;
entries: Module<MixedOutput>[];
graph: ReadOnlyGraph<MixedOutput>;
options: ExpoSerializerOptions;
isAsync: boolean;
isVendor: boolean;
isEntry: boolean;
deps: Set<Module>;
preModules: Set<Module>;
requiredChunks: Set<Chunk>;
constructor(name: string, entries: Module<MixedOutput>[], graph: ReadOnlyGraph<MixedOutput>, options: ExpoSerializerOptions, isAsync?: boolean, isVendor?: boolean, isEntry?: boolean);
private getPlatform;
private getFilename;
private getStableChunkSource;
private getFilenameForConfig;
private serializeToCodeWithTemplates;
hasAbsolutePath(absolutePath: string): boolean;
private getComputedPathsForAsyncDependencies;
private getAdjustedSourceMapUrl;
private serializeToCode;
private boolishTransformOption;
serializeToAssetsAsync(serializerConfig: Partial<SerializerConfigT>, chunks: Chunk[], { includeSourceMaps, unstable_beforeAssetSerializationPlugins }: SerializeChunkOptions): Promise<SerialAsset[]>;
private supportsBytecode;
isHermesEnabled(): boolean;
}
export declare function getSortedModules(modules: Module<MixedOutput>[], { createModuleId, }: {
createModuleId: (path: string) => number;
}): readonly Module<any>[];
export {};

View File

@@ -0,0 +1,569 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Chunk = void 0;
exports.graphToSerialAssetsAsync = graphToSerialAssetsAsync;
exports.getSortedModules = getSortedModules;
const bundleToString_1 = __importDefault(require("@expo/metro/metro/lib/bundleToString"));
const isResolvedDependency_1 = require("@expo/metro/metro/lib/isResolvedDependency");
const assert_1 = __importDefault(require("assert"));
const path_1 = __importDefault(require("path"));
const debugId_1 = require("./debugId");
const exportPath_1 = require("./exportPath");
const getCssDeps_1 = require("./getCssDeps");
const getAssets_1 = __importDefault(require("../transform-worker/getAssets"));
const filePath_1 = require("../utils/filePath");
// Lazy-loaded to avoid pulling in metro-source-map at startup
let _buildHermesBundleAsync;
function getBuildHermesBundleAsync() {
if (!_buildHermesBundleAsync) {
_buildHermesBundleAsync = require('./exportHermes').buildHermesBundleAsync;
}
return _buildHermesBundleAsync;
}
// Lazy-loaded to avoid pulling in metro's getAppendScripts -> sourceMapString -> @babel/traverse at startup
let _sourceMapString;
function getSourceMapString() {
if (!_sourceMapString) {
_sourceMapString =
require('@expo/metro/metro/DeltaBundler/Serializers/sourceMapString').sourceMapString;
}
return _sourceMapString;
}
let _baseJSBundleWithDependencies;
function getBaseJSBundleWithDependencies() {
if (!_baseJSBundleWithDependencies) {
_baseJSBundleWithDependencies = require('./fork/baseJSBundle').baseJSBundleWithDependencies;
}
return _baseJSBundleWithDependencies;
}
let _getBaseUrlOption;
function getBaseUrlOption(...args) {
if (!_getBaseUrlOption) {
_getBaseUrlOption = require('./fork/baseJSBundle').getBaseUrlOption;
}
return _getBaseUrlOption(...args);
}
let _getPlatformOption;
function getPlatformOption(...args) {
if (!_getPlatformOption) {
_getPlatformOption = require('./fork/baseJSBundle').getPlatformOption;
}
return _getPlatformOption(...args);
}
async function graphToSerialAssetsAsync(config, serializeChunkOptions, ...props) {
const [entryFile, preModules, graph, options] = props;
const cssDeps = (0, getCssDeps_1.getCssSerialAssets)(graph.dependencies, {
entryFile,
projectRoot: options.projectRoot,
});
// Create chunks for splitting.
const chunks = new Set();
gatherChunks(preModules, chunks, { test: pathToRegex(entryFile) }, preModules, graph, options, false, true);
const entryChunk = findEntryChunk(chunks, entryFile);
if (entryChunk) {
removeEntryDepsFromAsyncChunks(entryChunk, chunks);
const commonChunk = extractCommonChunk(chunks, graph, options);
if (commonChunk) {
entryChunk.requiredChunks.add(commonChunk);
chunks.add(commonChunk);
}
deduplicateAgainstKnownChunks(chunks, entryChunk, commonChunk);
removeEmptyChunks(chunks);
if (commonChunk) {
createRuntimeChunk(entryChunk, chunks, graph, options);
}
}
const jsAssets = await serializeChunksAsync(chunks, config.serializer ?? {}, serializeChunkOptions);
// TODO: Can this be anything besides true?
const isExporting = true;
const baseUrl = getBaseUrlOption(graph, { serializerOptions: serializeChunkOptions });
const assetPublicUrl = (baseUrl.replace(/\/+$/, '') ?? '') + '/assets';
const platform = getPlatformOption(graph, options) ?? 'web';
const isHosted = platform === 'web' || (graph.transformOptions?.customTransformOptions?.hosted && isExporting);
const publicPath = isExporting
? isHosted
? `/assets?export_path=${assetPublicUrl}`
: assetPublicUrl
: '/assets/?unstable_path=.';
// TODO: Convert to serial assets
// TODO: Disable this call dynamically in development since assets are fetched differently.
const metroAssets = (await (0, getAssets_1.default)(graph.dependencies, {
processModuleFilter: options.processModuleFilter,
assetPlugins: config.transformer?.assetPlugins ?? [],
platform,
projectRoot: options.projectRoot, // this._getServerRootDir(),
publicPath,
isHosted,
}));
return {
artifacts: [...jsAssets, ...cssDeps],
assets: metroAssets,
};
}
class Chunk {
name;
entries;
graph;
options;
isAsync;
isVendor;
isEntry;
deps = new Set();
preModules = new Set();
// Chunks that are required to be loaded synchronously before this chunk.
// These are included in the HTML as <script> tags.
requiredChunks = new Set();
constructor(name, entries, graph, options, isAsync = false, isVendor = false, isEntry = false) {
this.name = name;
this.entries = entries;
this.graph = graph;
this.options = options;
this.isAsync = isAsync;
this.isVendor = isVendor;
this.isEntry = isEntry;
this.deps = new Set(entries);
}
getPlatform() {
(0, assert_1.default)(this.graph.transformOptions.platform, "platform is required to be in graph's transformOptions");
return this.graph.transformOptions.platform;
}
getFilename(src) {
return !this.options.serializerOptions?.exporting
? this.name
: (0, exportPath_1.getExportPathForDependencyWithOptions)(this.name, {
platform: this.getPlatform(),
src,
serverRoot: this.options.serverRoot,
});
}
getStableChunkSource(serializerConfig) {
return this.options.dev
? ''
: this.serializeToCodeWithTemplates(serializerConfig, {
// Disable source maps when creating a sha to reduce the number of possible changes that could
// influence the cache hit.
serializerOptions: {
includeSourceMaps: false,
},
sourceMapUrl: undefined,
debugId: undefined,
}).code;
}
getFilenameForConfig(serializerConfig) {
return this.getFilename(this.getStableChunkSource(serializerConfig));
}
serializeToCodeWithTemplates(serializerConfig, options = {}) {
const entryFile = this.name;
// TODO: Disable all debugId steps when a dev server is enabled. This is an export-only feature.
const preModules = [...(options.preModules ?? this.preModules).values()];
const dependencies = [...this.deps];
const jsSplitBundle = getBaseJSBundleWithDependencies()(entryFile, preModules, dependencies, {
...this.options,
runBeforeMainModule: serializerConfig?.getModulesRunBeforeMainModule?.(path_1.default.relative(this.options.projectRoot, entryFile)) ?? [],
runModule: this.options.runModule && !this.isVendor && (this.isEntry || !this.isAsync),
modulesOnly: this.options.modulesOnly || preModules.length === 0,
platform: this.getPlatform(),
baseUrl: getBaseUrlOption(this.graph, this.options),
splitChunks: !!this.options.serializerOptions?.splitChunks,
skipWrapping: true,
computedAsyncModulePaths: null,
...options,
});
return { code: (0, bundleToString_1.default)(jsSplitBundle).code, paths: jsSplitBundle.paths };
}
hasAbsolutePath(absolutePath) {
return [...this.deps].some((module) => module.path === absolutePath);
}
getComputedPathsForAsyncDependencies(serializerConfig, chunks) {
const baseUrl = getBaseUrlOption(this.graph, this.options);
// Only calculate production paths when all chunks are being exported.
if (this.options.includeAsyncPaths) {
return null;
}
const computedAsyncModulePaths = {};
this.deps.forEach((module) => {
module.dependencies.forEach((dependency) => {
if ((0, isResolvedDependency_1.isResolvedDependency)(dependency) && dependency.data.data.asyncType) {
const chunkContainingModule = chunks.find((chunk) => chunk.hasAbsolutePath(dependency.absolutePath));
(0, assert_1.default)(chunkContainingModule, 'Chunk containing module not found: ' + dependency.absolutePath);
// NOTE(kitten): We shouldn't have any async imports on non-async chunks
// However, due to how chunks merge, some async imports may now be pointing
// at entrypoint (or vendor) chunks. We omit the path so that the async import
// helper doesn't reload and reevaluate the entrypoint.
if (chunkContainingModule.isAsync) {
const moduleIdName = chunkContainingModule.getFilenameForConfig(serializerConfig);
computedAsyncModulePaths[dependency.absolutePath] = (baseUrl ?? '/') + moduleIdName;
}
}
});
});
return computedAsyncModulePaths;
}
getAdjustedSourceMapUrl(serializerConfig) {
// Metro really only accounts for development, so we'll use the defaults here.
if (this.options.dev) {
return this.options.sourceMapUrl ?? null;
}
if (this.options.serializerOptions?.includeSourceMaps !== true) {
return null;
}
if (this.options.inlineSourceMap || !this.options.sourceMapUrl) {
return this.options.sourceMapUrl ?? null;
}
const platform = this.getPlatform();
const isAbsolute = platform !== 'web';
const baseUrl = getBaseUrlOption(this.graph, this.options);
const filename = this.getFilenameForConfig(serializerConfig);
const isAbsoluteBaseUrl = !!baseUrl?.match(/https?:\/\//);
const pathname = (isAbsoluteBaseUrl ? '' : baseUrl.replace(/\/+$/, '')) +
'/' +
filename.replace(/^\/+$/, '') +
'.map';
let adjustedSourceMapUrl = this.options.sourceMapUrl;
// Metro has lots of issues...
if (this.options.sourceMapUrl.startsWith('//localhost')) {
adjustedSourceMapUrl = 'http:' + this.options.sourceMapUrl;
}
try {
const parsed = new URL(pathname, isAbsoluteBaseUrl ? baseUrl : adjustedSourceMapUrl);
if (isAbsoluteBaseUrl || isAbsolute) {
return parsed.href;
}
return parsed.pathname;
}
catch (error) {
// NOTE: export:embed that don't use baseUrl will use file paths instead of URLs.
if (!this.options.dev && isAbsolute) {
return adjustedSourceMapUrl;
}
console.error(`Failed to link source maps because the source map URL "${this.options.sourceMapUrl}" is corrupt:`, error);
return null;
}
}
serializeToCode(serializerConfig, { debugId, chunks, preModules }) {
return this.serializeToCodeWithTemplates(serializerConfig, {
skipWrapping: false,
sourceMapUrl: this.getAdjustedSourceMapUrl(serializerConfig) ?? undefined,
computedAsyncModulePaths: this.getComputedPathsForAsyncDependencies(serializerConfig, chunks),
debugId,
preModules,
});
}
boolishTransformOption(name) {
const value = this.graph.transformOptions?.customTransformOptions?.[name];
return value === true || value === 'true' || value === '1';
}
async serializeToAssetsAsync(serializerConfig, chunks, { includeSourceMaps, unstable_beforeAssetSerializationPlugins }) {
// Create hash without wrapping to prevent it changing when the wrapping changes.
const outputFile = this.getFilenameForConfig(serializerConfig);
// We already use a stable hash for the output filename, so we'll reuse that for the debugId.
const debugId = (0, debugId_1.stringToUUID)(path_1.default.basename(outputFile, path_1.default.extname(outputFile)));
let finalPreModules = [...this.preModules];
if (unstable_beforeAssetSerializationPlugins) {
for (const plugin of unstable_beforeAssetSerializationPlugins) {
finalPreModules = plugin({
graph: this.graph,
premodules: finalPreModules,
debugId,
});
}
}
const jsCode = this.serializeToCode(serializerConfig, {
chunks,
debugId,
preModules: new Set(finalPreModules),
});
const relativeEntry = path_1.default.relative(this.options.projectRoot, this.name);
const jsAsset = {
filename: outputFile,
originFilename: relativeEntry,
type: 'js',
metadata: {
isAsync: this.isAsync,
requires: [...this.requiredChunks.values()].map((chunk) => chunk.getFilenameForConfig(serializerConfig)),
// Provide a list of module paths that can be used for matching chunks to routes.
// TODO: Move HTML serializing closer to this code so we can reduce passing this much data around.
modulePaths: [...this.deps].map((module) => module.path),
paths: jsCode.paths,
expoDomComponentReferences: collectOutputReferences(this.deps, 'expoDomComponentReference'),
reactClientReferences: collectOutputReferences(this.deps, 'reactClientReference'),
reactServerReferences: collectOutputReferences(this.deps, 'reactServerReference'),
loaderReferences: collectOutputReferences(this.deps, 'loaderReference'),
},
source: jsCode.code,
};
const assets = [jsAsset];
const mutateSourceMapWithDebugId = (sourceMap) => {
// TODO: Upstream this so we don't have to parse the source map back and forth.
if (!debugId) {
return sourceMap;
}
// NOTE: debugId isn't required for inline source maps because the source map is included in the same file, therefore
// we don't need to disambiguate between multiple source maps.
const sourceMapObject = JSON.parse(sourceMap);
sourceMapObject.debugId = debugId;
// NOTE: Sentry does this, but bun does not.
// sourceMapObject.debug_id = debugId;
return JSON.stringify(sourceMapObject);
};
if (
// Only include the source map if the `options.sourceMapUrl` option is provided and we are exporting a static build.
includeSourceMaps &&
!this.options.inlineSourceMap &&
this.options.sourceMapUrl) {
const modules = [
...finalPreModules,
...getSortedModules([...this.deps], {
createModuleId: this.options.createModuleId,
}),
].map((module) => {
// TODO: Make this user-configurable.
// Make all paths relative to the server root to prevent the entire user filesystem from being exposed.
if (path_1.default.isAbsolute(module.path)) {
return {
...module,
path: '/' +
(0, filePath_1.toPosixPath)(path_1.default.relative(this.options.serverRoot ?? this.options.projectRoot, module.path)),
};
}
return module;
});
// TODO: We may not need to mutate the original source map with a `debugId` when hermes is enabled since we'll have different source maps.
const sourceMap = mutateSourceMapWithDebugId(getSourceMapString()(modules, {
excludeSource: false,
...this.options,
}));
assets.push({
filename: this.options.dev ? jsAsset.filename + '.map' : outputFile + '.map',
originFilename: jsAsset.originFilename,
type: 'map',
metadata: {},
source: sourceMap,
});
}
if (this.boolishTransformOption('bytecode') && this.isHermesEnabled()) {
const adjustedSource = jsAsset.source.replace(/^\/\/# (sourceMappingURL)=(.*)$/gm, (...props) => {
if (props[1] === 'sourceMappingURL') {
const mapName = props[2].replace(/\.js\.map$/, '.hbc.map');
return `//# ${props[1]}=` + mapName;
}
return '';
});
// TODO: Generate hbc for each chunk
const hermesBundleOutput = await getBuildHermesBundleAsync()({
projectRoot: this.options.projectRoot,
filename: this.name,
code: adjustedSource,
map: assets[1] ? assets[1].source : null,
// TODO: Maybe allow prod + no minify.
minify: true, //!this.options.dev,
});
if (hermesBundleOutput.hbc) {
// TODO: Unclear if we should add multiple assets, link the assets, or mutate the first asset.
// jsAsset.metadata.hbc = hermesBundleOutput.hbc;
// @ts-expect-error: TODO
jsAsset.source = hermesBundleOutput.hbc;
jsAsset.filename = jsAsset.filename.replace(/\.js$/, '.hbc');
// Replace mappings with hbc
if (jsAsset.metadata.paths) {
jsAsset.metadata.paths = Object.fromEntries(Object.entries(jsAsset.metadata.paths).map(([key, value]) => [
key,
Object.fromEntries(Object.entries(value).map(([key, value]) => [
key,
value ? value.replace(/\.js$/, '.hbc') : value,
])),
]));
}
}
if (assets[1] && hermesBundleOutput.sourcemap) {
assets[1].source = mutateSourceMapWithDebugId(hermesBundleOutput.sourcemap);
assets[1].filename = assets[1].filename.replace(/\.js\.map$/, '.hbc.map');
}
}
return assets;
}
supportsBytecode() {
return this.getPlatform() !== 'web';
}
isHermesEnabled() {
// TODO: Revisit.
// TODO: There could be an issue with having the serializer for export:embed output hermes since the native scripts will
// also create hermes bytecode. We may need to disable in one of the two places.
return (!this.options.dev &&
this.supportsBytecode() &&
this.graph.transformOptions.customTransformOptions?.engine === 'hermes');
}
}
exports.Chunk = Chunk;
function getSortedModules(modules, { createModuleId, }) {
// Assign IDs to modules in a consistent order
for (const module of modules) {
createModuleId(module.path);
}
// Sort by IDs
return modules.sort((a, b) => createModuleId(a.path) - createModuleId(b.path));
}
// Convert file paths to regex matchers.
function pathToRegex(path) {
// Escape regex special characters, except for '*'
let regexSafePath = path.replace(/[-[\]{}()+?.,\\^$|#\s]/g, '\\$&');
// Replace '*' with '.*' to act as a wildcard in regex
regexSafePath = regexSafePath.replace(/\*/g, '.*');
// Create a RegExp object with the modified string
return new RegExp('^' + regexSafePath + '$');
}
function collectOutputReferences(modules, key) {
return [
...new Set([...modules]
.map((module) => {
return module.output.map((output) => {
if (key in output.data && typeof output.data[key] === 'string') {
return output.data[key];
}
return undefined;
});
})
.flat()),
].filter((value) => typeof value === 'string');
}
function getEntryModulesForChunkSettings(graph, settings) {
return [...graph.dependencies.entries()]
.filter(([path]) => settings.test.test(path))
.map(([, module]) => module);
}
function chunkIdForModules(modules) {
return modules
.map((module) => module.path)
.sort()
.join('=>');
}
function gatherChunks(runtimePremodules, chunks, settings, preModules, graph, options, isAsync = false, isEntry = false) {
let entryModules = getEntryModulesForChunkSettings(graph, settings);
const existingChunks = [...chunks.values()];
entryModules = entryModules.filter((module) => {
return !existingChunks.find((chunk) => chunk.entries.includes(module));
});
// Prevent processing the same entry file twice.
if (!entryModules.length) {
return chunks;
}
const entryChunk = new Chunk(chunkIdForModules(entryModules), entryModules, graph, options, isAsync, false, isEntry);
// Add all the pre-modules to the first chunk.
if (preModules.length) {
// On native, use the preModules in insert code in the entry chunk.
for (const module of preModules.values()) {
entryChunk.preModules.add(module);
}
}
chunks.add(entryChunk);
function includeModule(entryModule) {
for (const dependency of entryModule.dependencies.values()) {
if (!(0, isResolvedDependency_1.isResolvedDependency)(dependency)) {
continue;
}
else if (dependency.data.data.asyncType &&
// Support disabling multiple chunks.
entryChunk.options.serializerOptions?.splitChunks !== false) {
const isEntry = dependency.data.data.asyncType === 'worker';
gatherChunks(runtimePremodules, chunks, { test: pathToRegex(dependency.absolutePath) }, isEntry ? runtimePremodules : [], graph, options, true, isEntry);
}
else {
const module = graph.dependencies.get(dependency.absolutePath);
if (module) {
// Prevent circular dependencies from creating infinite loops.
if (!entryChunk.deps.has(module)) {
entryChunk.deps.add(module);
includeModule(module);
}
}
}
}
}
for (const entryModule of entryModules) {
includeModule(entryModule);
}
return chunks;
}
function findEntryChunk(chunks, entryFile) {
return [...chunks.values()].find((chunk) => !chunk.isAsync && chunk.hasAbsolutePath(entryFile));
}
function removeEntryDepsFromAsyncChunks(entryChunk, chunks) {
for (const chunk of chunks.values()) {
if (!chunk.isEntry && chunk.isAsync) {
for (const dep of chunk.deps.values()) {
if (entryChunk.deps.has(dep)) {
// Remove the dependency from the async chunk since it will be loaded in the main chunk.
chunk.deps.delete(dep);
}
}
}
}
}
function extractCommonChunk(chunks, graph, options) {
const toCompare = [...chunks.values()];
const commonDependencies = [];
while (toCompare.length) {
const chunk = toCompare.shift();
for (const chunk2 of toCompare) {
if (chunk !== chunk2 && chunk.isAsync && chunk2.isAsync) {
const commonDeps = [...chunk.deps].filter((dep) => chunk2.deps.has(dep));
for (const dep of commonDeps) {
chunk.deps.delete(dep);
chunk2.deps.delete(dep);
}
commonDependencies.push(...commonDeps);
}
}
}
// If common dependencies were found, extract them to the shared chunk.
if (commonDependencies.length) {
const commonDependenciesUnique = [...new Set(commonDependencies)];
return new Chunk('/__common.js', commonDependenciesUnique, graph, options, false, true);
}
return undefined;
}
function deduplicateAgainstKnownChunks(chunks, entryChunk, commonChunk) {
// TODO: Optimize this pass more.
// Remove all dependencies from async chunks that are already in the common chunk.
for (const chunk of [...chunks.values()]) {
if (!chunk.isEntry && chunk !== commonChunk) {
for (const dep of chunk.deps) {
if (entryChunk.deps.has(dep) || commonChunk?.deps.has(dep)) {
chunk.deps.delete(dep);
}
}
}
}
}
function removeEmptyChunks(chunks) {
for (const chunk of [...chunks.values()]) {
if (!chunk.isEntry && chunk.deps.size === 0) {
chunks.delete(chunk);
}
}
}
function createRuntimeChunk(entryChunk, chunks, graph, options) {
const runtimeChunk = new Chunk('/__expo-metro-runtime.js', [], graph, options, false, true);
// All premodules (including metro-runtime) should load first
for (const preModule of entryChunk.preModules) {
runtimeChunk.preModules.add(preModule);
}
entryChunk.preModules = new Set();
for (const chunk of chunks) {
// Runtime chunk has to load before any other a.k.a all chunks require it.
chunk.requiredChunks.add(runtimeChunk);
}
chunks.add(runtimeChunk);
}
async function serializeChunksAsync(chunks, serializerConfig, options) {
const jsAssets = [];
const chunksArray = [...chunks.values()];
await Promise.all(chunksArray.map(async (chunk) => {
jsAssets.push(...(await chunk.serializeToAssetsAsync(serializerConfig, chunksArray, options)));
}));
return jsAssets;
}
//# sourceMappingURL=serializeChunks.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,17 @@
export type SerialAsset = {
originFilename: string;
filename: string;
source: string;
type: 'css-external' | 'css' | 'js' | 'map' | 'json';
metadata: {
hmrId?: string;
isAsync?: boolean;
modulePaths?: string[];
paths?: Record<string, Record<string, string>>;
reactServerReferences?: string[];
reactClientReferences?: string[];
expoDomComponentReferences?: string[];
loaderReferences?: string[];
requires?: string[];
};
};

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=serializerAssets.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"serializerAssets.js","sourceRoot":"","sources":["../../src/serializer/serializerAssets.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,16 @@
/**
* Copyright © 2024 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { MixedOutput, Module, ReadOnlyGraph, SerializerOptions } from '@expo/metro/metro/DeltaBundler/types';
type AdvancedModule = Module<MixedOutput> & {
sideEffects?: boolean | null;
};
export declare function hasSideEffectWithDebugTrace(options: SerializerOptions, graph: ReadOnlyGraph, value: AdvancedModule, parentTrace?: string[], checked?: Set<string>): [boolean | null, string[]];
export declare function _createSideEffectMatcher(dirRoot: string, packageJson: {
sideEffects?: boolean | string[];
}, packageJsonPath?: string): (fp: string) => boolean | null;
export declare function isVirtualModule(path: string): boolean;
export {};

View File

@@ -0,0 +1,123 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasSideEffectWithDebugTrace = hasSideEffectWithDebugTrace;
exports._createSideEffectMatcher = _createSideEffectMatcher;
exports.isVirtualModule = isVirtualModule;
const isResolvedDependency_1 = require("@expo/metro/metro/lib/isResolvedDependency");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const picomatch_1 = __importDefault(require("picomatch"));
const findUpPackageJsonPath_1 = require("./findUpPackageJsonPath");
const filePath_1 = require("../utils/filePath");
const debug = require('debug')('expo:side-effects');
function hasSideEffectWithDebugTrace(options, graph, value, parentTrace = [value.path], checked = new Set()) {
const currentModuleHasSideEffect = getShallowSideEffect(options, value);
if (currentModuleHasSideEffect) {
return [true, parentTrace];
}
// Recursively check if any of the dependencies have side effects.
for (const depReference of value.dependencies.values()) {
if (!(0, isResolvedDependency_1.isResolvedDependency)(depReference) || checked.has(depReference.absolutePath)) {
continue;
}
checked.add(depReference.absolutePath);
const dep = graph.dependencies.get(depReference.absolutePath);
if (!dep) {
continue;
}
const [hasSideEffect, trace] = hasSideEffectWithDebugTrace(options, graph, dep, [...parentTrace, depReference.absolutePath], checked);
if (hasSideEffect) {
// Propagate the side effect to the parent.
value.sideEffects = true;
return [true, trace];
}
}
return [currentModuleHasSideEffect, []];
}
const pkgJsonCache = new Map();
const getPackageJsonMatcher = (options, dir) => {
let packageJson;
let packageJsonPath = null;
if (typeof options._test_getPackageJson === 'function') {
[packageJson, packageJsonPath] = options._test_getPackageJson(dir);
}
else {
const cached = pkgJsonCache.get(dir);
if (cached) {
return cached;
}
packageJsonPath = (0, findUpPackageJsonPath_1.findUpPackageJsonPath)(dir);
packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
}
if (!packageJsonPath) {
return null;
}
// TODO: Split out and unit test.
const dirRoot = path_1.default.dirname(packageJsonPath);
const isSideEffect = _createSideEffectMatcher(dirRoot, packageJson, packageJsonPath);
pkgJsonCache.set(dir, isSideEffect);
return isSideEffect;
};
function _createSideEffectMatcher(dirRoot, packageJson, packageJsonPath = '') {
let sideEffectMatcher;
if (Array.isArray(packageJson.sideEffects)) {
const sideEffects = packageJson.sideEffects
.filter((sideEffect) => typeof sideEffect === 'string')
.map((sideEffect) => {
const pattern = sideEffect.replace(/^\.\//, '');
return pattern.includes('/') ? pattern : `**/${pattern}`;
});
sideEffectMatcher = (0, picomatch_1.default)(sideEffects);
}
else if (typeof packageJson.sideEffects === 'boolean' || !packageJson.sideEffects) {
sideEffectMatcher = packageJson.sideEffects;
}
else {
debug('Invalid sideEffects field in package.json:', packageJsonPath, packageJson.sideEffects);
}
return (fp) => {
// Default is that everything is a side-effect unless explicitly marked as not.
if (sideEffectMatcher == null) {
return null;
}
else if (typeof sideEffectMatcher === 'boolean') {
return sideEffectMatcher;
}
else {
const relativeName = path_1.default.isAbsolute(fp) ? path_1.default.relative(dirRoot, fp) : path_1.default.normalize(fp);
return sideEffectMatcher((0, filePath_1.toPosixPath)(relativeName));
}
};
}
function getShallowSideEffect(options, value) {
if (value?.sideEffects !== undefined) {
return value.sideEffects;
}
const isSideEffect = detectHasSideEffectInPackageJson(options, value);
value.sideEffects = isSideEffect;
return isSideEffect;
}
function detectHasSideEffectInPackageJson(options, value) {
if (value.sideEffects !== undefined) {
return value.sideEffects;
}
// Don't perform lookup on virtual modules.
if (isVirtualModule(value.path)) {
return false;
}
if (value.output.some((output) => output.type === 'js/module')) {
const isSideEffect = getPackageJsonMatcher(options, value.path);
if (isSideEffect == null) {
return null;
}
return isSideEffect(value.path);
}
return null;
}
function isVirtualModule(path) {
return path.startsWith('\0');
}
//# sourceMappingURL=sideEffects.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"sideEffects.js","sourceRoot":"","sources":["../../src/serializer/sideEffects.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,15 @@
/**
* Copyright © 2024 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { types } from '@babel/core';
import type { MixedOutput, Module, ReadOnlyGraph } from '@expo/metro/metro/DeltaBundler/types';
import type { SerializerConfigT } from '@expo/metro/metro-config';
import { ExpoSerializerOptions } from './fork/baseJSBundle';
type Serializer = NonNullable<SerializerConfigT['customSerializer']>;
type SerializerParameters = Parameters<Serializer>;
export declare function isModuleEmptyFor(ast?: types.File): boolean;
export declare function treeShakeSerializer(entryPoint: string, preModules: readonly Module<MixedOutput>[], graph: ReadOnlyGraph, options: ExpoSerializerOptions): Promise<SerializerParameters>;
export {};

View File

@@ -0,0 +1,723 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isModuleEmptyFor = isModuleEmptyFor;
exports.treeShakeSerializer = treeShakeSerializer;
/**
* Copyright © 2024 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const core_1 = require("@babel/core");
const generator_1 = __importDefault(require("@babel/generator"));
const isResolvedDependency_1 = require("@expo/metro/metro/lib/isResolvedDependency");
const assert_1 = __importDefault(require("assert"));
const jsOutput_1 = require("./jsOutput");
const reconcileTransformSerializerPlugin_1 = require("./reconcileTransformSerializerPlugin");
const sideEffects_1 = require("./sideEffects");
const metro_transform_worker_1 = require("../transform-worker/metro-transform-worker");
const debug = require('debug')('expo:treeshake');
const isDebugEnabled = require('debug').enabled('expo:treeshake');
const OPTIMIZE_GRAPH = true;
function isModuleEmptyFor(ast) {
if (!ast?.program.body.length) {
return true;
}
let isEmptyOrCommentsAndUseStrict = true; // Assume true until proven otherwise
(0, core_1.traverse)(ast, {
enter(path) {
const { node } = path;
// If it's not a Directive, ExpressionStatement, or empty body,
// it means we have actual code
if (node.type !== 'Directive' &&
node.type !== 'ExpressionStatement' &&
!(node.type === 'Program' && node.body.length === 0)) {
isEmptyOrCommentsAndUseStrict = false;
path.stop(); // No need to traverse further
}
},
// If we encounter any non-comment nodes, it's not empty
noScope: true,
});
return isEmptyOrCommentsAndUseStrict;
}
function isEmptyModule(value) {
return value.output.every((outputItem) => {
return isModuleEmptyFor(accessAst(outputItem));
});
}
// Collect a list of exports that are not used within the module.
function getExportsThatAreNotUsedInModule(ast) {
const exportedIdentifiers = new Set();
const usedIdentifiers = new Set();
const unusedExports = [];
// First pass: collect all export identifiers
(0, core_1.traverse)(ast, {
ExportNamedDeclaration(path) {
const { declaration, specifiers } = path.node;
if (declaration) {
if ('declarations' in declaration && declaration.declarations) {
declaration.declarations.forEach((decl) => {
if (core_1.types.isIdentifier(decl.id)) {
exportedIdentifiers.add(decl.id.name);
}
});
}
else if ('id' in declaration && core_1.types.isIdentifier(declaration.id)) {
exportedIdentifiers.add(declaration.id.name);
}
}
specifiers.forEach((spec) => {
if (core_1.types.isIdentifier(spec.exported)) {
exportedIdentifiers.add(spec.exported.name);
}
});
},
ExportDefaultDeclaration(path) {
// Default exports need to be handled separately
// Assuming the default export is a function or class declaration:
if ('id' in path.node.declaration && core_1.types.isIdentifier(path.node.declaration.id)) {
exportedIdentifiers.add(path.node.declaration.id.name);
}
},
});
// Second pass: find all used identifiers
(0, core_1.traverse)(ast, {
ReferencedIdentifier(path) {
usedIdentifiers.add(path.node.name);
},
});
// Determine which exports are unused
exportedIdentifiers.forEach((exported) => {
if (!usedIdentifiers.has(exported)) {
unusedExports.push(exported);
}
});
return unusedExports;
}
function populateModuleWithImportUsage(value) {
for (const index in value.output) {
const outputItem = value.output[index];
if (!(0, jsOutput_1.isExpoJsOutput)(outputItem)) {
continue;
}
const ast = outputItem.data.ast;
// This should be cached by the transform worker for use here to ensure close to consistent
// results between the tree-shake and the final transform.
const reconcile = outputItem.data.reconcile;
(0, assert_1.default)(ast, 'ast must be defined.');
(0, assert_1.default)(reconcile, 'reconcile settings are required in the module graph for post transform.');
const deps = (0, metro_transform_worker_1.collectDependenciesForShaking)(ast, reconcile.collectDependenciesOptions).dependencies;
// @ts-expect-error: Mutating the value in place.
value.dependencies =
//
(0, reconcileTransformSerializerPlugin_1.sortDependencies)(deps, value.dependencies);
}
}
function markUnused(path) {
// Format path as code
if (isDebugEnabled) {
debug('Delete AST:\n' + (0, generator_1.default)(path.node).code);
}
path.remove();
}
async function treeShakeSerializer(entryPoint, preModules, graph, options) {
if (!options.serializerOptions?.usedExports) {
return [entryPoint, preModules, graph, options];
}
if (!OPTIMIZE_GRAPH) {
// Useful for testing the transform reconciler...
return [entryPoint, preModules, graph, options];
}
const starExportsForModules = new Map();
for (const value of graph.dependencies.values()) {
// TODO: Move this to the transformer and combine with collect dependencies.
getExportsForModule(value);
}
const beforeList = [...graph.dependencies.keys()];
// Tree shake the graph.
optimizePaths([entryPoint]);
if (isDebugEnabled) {
// Debug pass: Print all orphaned modules.
for (const [, value] of graph.dependencies.entries()) {
if (value.inverseDependencies.size !== 0) {
let hasNormalNode = false;
for (const dep of value.inverseDependencies) {
if (!graph.dependencies.has(dep)) {
debug(`[ISSUE]: Dependency: ${value.path}, has inverse relation to missing node: ${dep}`);
}
else {
hasNormalNode = true;
}
}
if (!hasNormalNode) {
debug(`[ERROR]: All inverse dependencies are missing for: ${value.path}`);
}
}
}
const afterList = [...graph.dependencies.keys()];
// Print the removed modules:
const removedModules = beforeList.filter((value) => !afterList.includes(value));
if (removedModules.length) {
debug('Modules that were fully removed:');
debug(removedModules.sort().join('\n'));
}
}
return [entryPoint, preModules, graph, options];
function getExportsForModule(value, checkedModules = new Set()) {
if (starExportsForModules.has(value.path)) {
return starExportsForModules.get(value.path);
}
if (checkedModules.has(value.path)) {
// TODO: Handle circular dependencies.
throw new Error('Circular dependency detected while tree-shaking: ' + value.path);
// return {
// exportNames: [],
// isStatic: true,
// hasUnresolvableStarExport: true,
// };
}
checkedModules.add(value.path);
function getDepForImportId(importModuleId) {
// The hash key for the dependency instance in the module.
const targetHashId = getDependencyHashIdForImportModuleId(value, importModuleId);
// If the dependency was already removed, then we don't need to do anything.
const importInstance = value.dependencies.get(targetHashId);
const graphEntryForTargetImport = importInstance &&
(0, isResolvedDependency_1.isResolvedDependency)(importInstance) &&
graph.dependencies.get(importInstance.absolutePath);
// Should never happen but we're playing with fire here.
if (!graphEntryForTargetImport) {
throw new Error(`Failed to find graph key for re-export "${importModuleId}" while optimizing ${value.path}. Options: ${[...value.dependencies.values()].map((v) => v.data.name).join(', ')}`);
}
return graphEntryForTargetImport;
}
const exportNames = new Set();
// Indicates that the module does not have any dynamic exports, e.g. `module.exports`, `Object.assign(exports)`, etc.
let isStatic = true;
let hasUnresolvableStarExport = false;
for (const index in value.output) {
const outputItem = value.output[index];
if (!(0, jsOutput_1.isExpoJsOutput)(outputItem)) {
continue;
}
const ast = outputItem.data.ast;
if (!ast)
continue;
// Detect if the module is static...
if (outputItem.data.hasCjsExports) {
isStatic = false;
}
// Collect export names
(0, core_1.traverse)(ast, {
ExportNamedDeclaration(path) {
const { declaration, specifiers } = path.node;
if (declaration) {
if ('declarations' in declaration && declaration.declarations) {
declaration.declarations.forEach((decl) => {
if (core_1.types.isIdentifier(decl.id)) {
exportNames.add(decl.id.name);
}
});
}
else if ('id' in declaration && core_1.types.isIdentifier(declaration.id)) {
exportNames.add(declaration.id.name);
}
}
specifiers.forEach((spec) => {
if (core_1.types.isIdentifier(spec.exported)) {
exportNames.add(spec.exported.name);
}
});
},
ExportDefaultDeclaration(path) {
// Default exports need to be handled separately
// Assuming the default export is a function or class declaration
if ('id' in path.node.declaration && core_1.types.isIdentifier(path.node.declaration.id)) {
exportNames.add(path.node.declaration.id.name);
}
// If it's an expression, then it's a static export.
isStatic = true;
},
});
(0, core_1.traverse)(ast, {
// export * from 'a'
// NOTE: This only runs on normal `* from` syntax as `* as X from` is converted to an import.
ExportAllDeclaration(path) {
if (path.node.source) {
// Get module for import ID:
const nextModule = getDepForImportId(path.node.source.value);
const exportResults = getExportsForModule(nextModule, checkedModules);
// console.log('exportResults', exportResults);
if (exportResults.isStatic && !exportResults.hasUnresolvableStarExport) {
// Collect all exports from the module.
// exportNames.push(...exportResults.exportNames);
// Convert the export all to named exports.
// ```
// export * from 'a';
// ```
// becomes
// ```
// export { a, b, c } from 'a';
// ```
// NOTE: It's important we only use one statement so we don't skew the multi-dep tracking from collect dependencies.
// The `default` specifier isn't re-exported by export-all, as per the spec
const exportSpecifiers = [];
for (const exportName of exportResults.exportNames) {
if (exportName !== 'default' && !exportNames.has(exportName)) {
exportNames.add(exportName);
exportSpecifiers.push(core_1.types.exportSpecifier(core_1.types.identifier(exportName), core_1.types.identifier(exportName)));
}
}
path.replaceWith(core_1.types.exportNamedDeclaration(null, exportSpecifiers, core_1.types.stringLiteral(path.node.source.value)));
// TODO: Update deps
populateModuleWithImportUsage(value);
}
else {
debug('Cannot resolve star export:', nextModule.path);
hasUnresolvableStarExport = true;
}
// Collect all exports from the module.
// If list of exports does not contain any CJS, then re-write the export all as named exports.
}
},
});
}
const starExport = {
exportNames,
isStatic,
hasUnresolvableStarExport,
};
starExportsForModules.set(value.path, starExport);
return starExport;
}
function disposeOfGraphNode(nodePath) {
const node = graph.dependencies.get(nodePath);
if (!node)
return;
// Recursively remove all dependencies.
for (const dep of node.dependencies.values()) {
if (!(0, isResolvedDependency_1.isResolvedDependency)(dep))
continue;
const child = graph.dependencies.get(dep.absolutePath);
if (!child)
continue;
// TODO: Might need to clean up the multi-dep tracking, e.g. importInstance.data.data.locs.pop();
// Remove inverse dependency on the node we're about to delete.
child.inverseDependencies.delete(nodePath);
// If the child has no more dependencies, then remove it from the graph too.
if (child.inverseDependencies.size === 0) {
disposeOfGraphNode(dep.absolutePath);
}
}
// @ts-expect-error
graph.dependencies.delete(nodePath);
}
function getDependencyHashIdForImportModuleId(graphModule, importModuleId) {
// Unlink the module in the graph
// The hash key for the dependency instance in the module.
const depId = [...graphModule.dependencies.entries()].find(([_key, dep]) => {
return dep.data.name === importModuleId;
})?.[0];
// // Should never happen but we're playing with fire here.
if (!depId) {
throw new Error(`Failed to find graph key for import "${importModuleId}" from "${importModuleId}" while optimizing ${graphModule.path}. Options: ${[...graphModule.dependencies.values()].map((v) => v.data.name).join(', ')}`);
}
return depId;
}
function disconnectGraphNode(graphModule, importModuleId, { isSideEffectyImport, bailOnDuplicateDefaultExport, } = {}) {
// Unlink the module in the graph
// The hash key for the dependency instance in the module.
const targetHashId = getDependencyHashIdForImportModuleId(graphModule, importModuleId);
// If the dependency was already removed, then we don't need to do anything.
const importInstance = graphModule.dependencies.get(targetHashId);
const graphEntryForTargetImport = importInstance &&
(0, isResolvedDependency_1.isResolvedDependency)(importInstance) &&
graph.dependencies.get(importInstance.absolutePath);
// Should never happen but we're playing with fire here.
if (!graphEntryForTargetImport) {
throw new Error(`Failed to find graph key for re-export "${importModuleId}" while optimizing ${graphModule.path}. Options: ${[...graphModule.dependencies.values()].map((v) => v.data.name).join(', ')}`);
}
//
if (graphEntryForTargetImport.path.match(/\.(s?css|sass)$/)) {
debug('Skip graph unlinking for CSS:');
debug('- Origin module:', graphModule.path);
debug('- Module ID:', importModuleId);
// Skip CSS imports.
return { path: importInstance.absolutePath, removed: false };
}
if (bailOnDuplicateDefaultExport &&
// @ts-expect-error: exportNames is added by babel
importInstance.data.data.exportNames?.includes('default')) {
debug('Skip graph unlinking for duplicate default export:');
debug('- Origin module:', graphModule.path);
debug('- Module ID:', importModuleId);
// Skip CSS imports.
return { path: importInstance.absolutePath, removed: false };
}
const [authorMarkedSideEffect, trace] = (0, sideEffects_1.hasSideEffectWithDebugTrace)(options, graph, graphEntryForTargetImport);
// If the package.json chain explicitly marks the module as side-effect-free, then we can remove imports that have no specifiers.
const isFx = authorMarkedSideEffect ?? isSideEffectyImport;
if (isDebugEnabled && isSideEffectyImport) {
if (authorMarkedSideEffect == null) {
// This is for debugging modules that should be marked as side-effects but are not.
if (!trace.length) {
debug('----');
debug('Found side-effecty import (no specifiers) that is not marked as a side effect in the package.json:');
debug('- Origin module:', graphModule.path);
debug('- Module ID (needs marking):', importModuleId);
// debug('- FX trace:', trace.join(' > '));
debug('----');
}
}
else if (!isFx) {
debug('Removing side-effecty import (package.json indicates it is not a side-effect):', importModuleId, 'from:', graphModule.path);
}
}
// let trace: string[] = [];
if (
// Don't remove the module if it has side effects.
!isFx ||
// Unless it's an empty module.
isEmptyModule(graphEntryForTargetImport)) {
// TODO: Get the exact instance of the import. This help with more readable errors when import was not counted.
const importData = importInstance.data.data;
(0, assert_1.default)('imports' in importData, 'Expo InternalDependency type expected, but `imports` key is missing.');
importData.imports -= 1;
if (importData.imports <= 0) {
// Remove dependency from this module so it doesn't appear in the dependency map.
graphModule.dependencies.delete(targetHashId);
// Remove inverse link to this dependency
graphEntryForTargetImport.inverseDependencies.delete(graphModule.path);
if (graphEntryForTargetImport.inverseDependencies.size === 0) {
debug('Unlink module from graph:', importInstance.absolutePath);
// Remove the dependency from the graph as no other modules are using it anymore.
disposeOfGraphNode(importInstance.absolutePath);
}
}
// Mark the module as removed so we know to traverse again.
return { path: importInstance.absolutePath, removed: true };
}
else {
if (isFx) {
debug('Skip graph unlinking due to side-effect:');
debug('- Origin module:', graphModule.path);
debug('- Module ID:', importModuleId);
debug('- FX trace:', trace.join(' > '));
}
else {
debug('Skip graph unlinking:', {
depId: targetHashId,
isFx,
});
}
}
return { path: importInstance.absolutePath, removed: false };
}
function removeUnusedExports(value, depth = 0) {
if (!accessAst(value.output[0]) || !value.inverseDependencies.size) {
return [];
}
if (depth > 5) {
debug('Max export removal depth reached for:', value.path);
return [];
}
const dirtyImports = [];
let needsImportReindex = false;
let shouldRecurseUnusedExports = false;
const inverseDeps = [
// @ts-expect-error: Type 'Iterator<string, any, undefined>' must have a '[Symbol.iterator]()' method that returns an iterator.
...value.inverseDependencies.values(),
].map((id) => {
return graph.dependencies.get(id);
});
const isExportUsed = (importName) => {
return inverseDeps.some((dep) => {
const isModule = dep?.output.some((outputItem) => {
return outputItem.type === 'js/module';
});
if (!isModule) {
return false;
}
return [
// @ts-expect-error: Type 'Iterator<string, any, undefined>' must have a '[Symbol.iterator]()' method that returns an iterator.
...dep?.dependencies.values(),
].some((importItem) => {
if (importItem.absolutePath !== value.path) {
return false;
}
// If the import is async or weak then we can't tree shake it.
if (importItem.asyncType) {
// if (['async', 'prefetch', 'weak'].includes(importItem.asyncType)) {
return true;
}
if (!importItem.data.data.exportNames) {
throw new Error('Missing export names for: ' + importItem.absolutePath);
}
const isUsed = importItem.data.data.exportNames.some((exportName) => exportName === '*' || exportName === importName);
return isUsed;
});
});
};
for (const index in value.output) {
const outputItem = value.output[index];
if (!(0, jsOutput_1.isExpoJsOutput)(outputItem)) {
continue;
}
const ast = outputItem.data.ast;
if (!ast) {
throw new Error('AST missing for module: ' + value.path);
}
// Collect a list of exports that are not used within the module.
const possibleUnusedExports = getExportsThatAreNotUsedInModule(ast);
// Traverse exports and mark them as used or unused based on if inverse dependencies are importing them.
(0, core_1.traverse)(ast, {
ExportDefaultDeclaration(path) {
if (possibleUnusedExports.includes('default') && !isExportUsed('default')) {
// TODO: Update source maps
markUnused(path);
}
},
ExportNamedDeclaration(path) {
const importModuleId = path.node.source?.value;
// Remove specifiers, e.g. `export { Foo, Bar as Bax }`
if (core_1.types.isExportNamedDeclaration(path.node)) {
for (let i = 0; i < path.node.specifiers.length; i++) {
const specifier = path.node.specifiers[i];
if (core_1.types.isExportSpecifier(specifier) &&
core_1.types.isIdentifier(specifier.local) &&
core_1.types.isIdentifier(specifier.exported) &&
possibleUnusedExports.includes(specifier.exported.name) &&
!isExportUsed(specifier.exported.name)) {
// Remove specifier
path.node.specifiers.splice(i, 1);
needsImportReindex = true;
i--;
}
}
}
// Remove the entire node if the export has been completely removed.
const declaration = path.node.declaration;
if (core_1.types.isVariableDeclaration(declaration)) {
declaration.declarations = declaration.declarations.filter((decl) => {
if (decl.id.type === 'Identifier') {
if (possibleUnusedExports.includes(decl.id.name) && !isExportUsed(decl.id.name)) {
// TODO: Update source maps
debug(`mark remove (type: var, depth: ${depth}):`, decl.id.name, 'from:', value.path);
// Account for variables, and classes which may contain references to other exports.
shouldRecurseUnusedExports = true;
return false; // Remove this declaration
}
}
return true; // Keep this declaration
});
// If all declarations were removed, remove the entire path
if (declaration.declarations.length === 0) {
markUnused(path);
}
}
else if (declaration && 'id' in declaration && core_1.types.isIdentifier(declaration.id)) {
// function, class, etc.
if (possibleUnusedExports.includes(declaration.id.name) &&
!isExportUsed(declaration.id.name)) {
debug(`mark remove (type: function, depth: ${depth}):`, declaration.id.name, 'from:', value.path);
// TODO: Update source maps
markUnused(path);
// Code inside of an unused export may affect other exports in the module.
// e.g.
//
// export const a = () => {}
//
// // Removed `b` -> recurse for `a`
// export const b = () => { a() };
//
shouldRecurseUnusedExports = true;
}
}
// If export from import then check if we can remove the import.
if (importModuleId) {
if (path.node.specifiers.length === 0) {
const removeRequest = disconnectGraphNode(value, importModuleId, {
// Due to a quirk in how we've added tree shaking, export-all is expanded and could overlap with existing exports in the same module.
// If we find that the existing export has a default export then we should skip removing the node entirely.
bailOnDuplicateDefaultExport: true,
});
if (removeRequest.removed) {
dirtyImports.push(removeRequest.path);
}
// TODO: Update source maps
// We still want to remove the empty `export {} from 'a'` declaration even if the graph node was kept
// due to the duplicate default export
markUnused(path);
}
}
},
});
}
if (needsImportReindex) {
// TODO: Do this better with a tracked removal of the import rather than a full reparse.
populateModuleWithImportUsage(value);
}
if (shouldRecurseUnusedExports) {
return unique(removeUnusedExports(value, depth + 1).concat(dirtyImports));
}
return unique(dirtyImports);
}
function removeUnusedImportsFromModule(value, ast) {
// json, asset, script, etc.
if (!ast) {
return [];
}
// Traverse imports and remove unused imports.
// Keep track of all the imported identifiers
const importedIdentifiers = new Set();
// Keep track of all used identifiers
const usedIdentifiers = new Set();
const importDecs = [];
(0, core_1.traverse)(ast, {
ImportSpecifier(path) {
if (
// Support `import { foo as bar } from './foo'`
path.node.local.name != null) {
importedIdentifiers.add(path.node.local.name);
}
else if (
// Support `import { foo } from './foo'`
core_1.types.isIdentifier(path.node.imported) &&
path.node.imported.name != null) {
importedIdentifiers.add(path.node.imported.name);
}
else {
throw new Error('Unknown import specifier: ' + path.node.type);
}
},
ImportDefaultSpecifier(path) {
importedIdentifiers.add(path.node.local.name);
},
ImportNamespaceSpecifier(path) {
importedIdentifiers.add(path.node.local.name);
},
Identifier(path) {
// Make sure this identifier isn't coming from an import specifier
if (!path.scope.bindingIdentifierEquals(path.node.name, path.node) &&
// `import { Foo as Bar } from 'bax'` Foo should not be marked as used.
path.parent.type !== 'ImportSpecifier') {
usedIdentifiers.add(path.node.name);
}
},
ImportDeclaration(path) {
// Mark the path with some metadata indicating that it originally had `n` specifiers.
// This is used to determine if the import was side-effecty.
// NOTE: This could be a problem if the AST is re-parsed.
// TODO: This doesn't account for `import {} from './foo'`
path.opts.originalSpecifiers ??= path.node.specifiers.length;
importDecs.push(path);
},
});
const dirtyImports = [];
if (!importDecs.length) {
return dirtyImports;
}
// Determine unused identifiers by subtracting the used from the imported
const unusedImports = [...importedIdentifiers].filter((identifier) => !usedIdentifiers.has(identifier));
// Remove the unused imports from the AST
importDecs.forEach((path) => {
const importModuleId = path.node.source.value;
const originalSize = path.node.specifiers.length;
const absoluteOriginalSize = path.opts.originalSpecifiers ?? originalSize;
const isUsed = (specifier) => !unusedImports.includes(specifier.local.name);
path.node.specifiers = path.node.specifiers.filter(isUsed);
if (originalSize !== path.node.specifiers.length) {
// The hash key for the dependency instance in the module.
const targetHashId = getDependencyHashIdForImportModuleId(value, importModuleId);
const importInstance = value.dependencies.get(targetHashId);
if (importInstance && (0, isResolvedDependency_1.isResolvedDependency)(importInstance)) {
dirtyImports.push(importInstance.absolutePath);
}
}
// If no specifiers are left after filtering, remove the whole import declaration
// e.g. `import './unused'` or `import {} from './unused'` -> remove.
if (path.node.specifiers.length === 0) {
const removeRequest = disconnectGraphNode(value, importModuleId, {
isSideEffectyImport: absoluteOriginalSize === 0 ? true : undefined,
});
if (removeRequest.removed) {
debug('Disconnect import:', importModuleId, 'from:', value.path);
// TODO: Update source maps
// Delete the import AST
markUnused(path);
dirtyImports.push(removeRequest.path);
}
}
});
return unique(dirtyImports);
}
function removeUnusedImports(value) {
if (!value.dependencies.size) {
return [];
}
const dirtyImports = value.output
.map((outputItem) => {
return removeUnusedImportsFromModule(value, accessAst(outputItem));
})
.flat();
if (dirtyImports.length) {
// TODO: Do this better with a tracked removal of the import rather than a full reparse.
populateModuleWithImportUsage(value);
}
return dirtyImports;
}
function unique(array) {
return Array.from(new Set(array));
}
function optimizePaths(paths) {
const checked = new Set();
function markDirty(...paths) {
paths.forEach((path) => {
checked.delete(path);
paths.push(path);
});
}
// This pass will annotate the AST with the used and unused exports.
while (paths.length) {
const absolutePath = paths.pop();
if (absolutePath == null)
continue;
if (checked.has(absolutePath)) {
// debug('Bail:', absolutePath);
continue;
}
const dep = graph.dependencies.get(absolutePath);
if (!dep)
continue;
checked.add(absolutePath);
markDirty(
// Order is important (not sure why though)
...removeUnusedExports(dep),
// Remove imports after exports
...removeUnusedImports(dep));
// Optimize all deps without marking as dirty to prevent
// circular dependencies from creating infinite loops.
dep.dependencies.forEach((dep) => {
if ((0, isResolvedDependency_1.isResolvedDependency)(dep))
paths.push(dep.absolutePath);
});
}
if (isDebugEnabled) {
// Print if any dependencies weren't checked (this shouldn't happen)
const unchecked = [...graph.dependencies.entries()]
.filter(([key, value]) => !checked.has(key) && accessAst(value.output[0]))
.map(([key]) => key);
if (unchecked.length) {
debug('[ISSUE]: Unchecked modules:', unchecked);
}
}
}
}
function accessAst(output) {
return output.data.ast;
}
//# sourceMappingURL=treeShakeSerializerPlugin.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,31 @@
/**
* Copyright © 2022 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { MetroConfig } from '@expo/metro/metro';
import type { Module, ReadOnlyGraph, MixedOutput } from '@expo/metro/metro/DeltaBundler';
import type { ConfigT, InputConfigT } from '@expo/metro/metro-config';
import type { ExpoSerializerOptions } from './fork/baseJSBundle';
import { SerialAsset } from './serializerAssets';
export type Serializer = NonNullable<ConfigT['serializer']['customSerializer']>;
export type SerializerParameters = [
string,
readonly Module[],
ReadOnlyGraph,
ExpoSerializerOptions
];
export type SerializerConfigOptions = {
unstable_beforeAssetSerializationPlugins?: ((serializationInput: {
graph: ReadOnlyGraph<MixedOutput>;
premodules: Module[];
debugId?: string;
}) => Module[])[];
};
export type SerializerPlugin = (...props: SerializerParameters) => SerializerParameters | Promise<SerializerParameters>;
export declare function withExpoSerializers<Config extends InputConfigT = InputConfigT>(config: Config, options?: SerializerConfigOptions): Config;
export declare function withSerializerPlugins<Config extends InputConfigT = InputConfigT>(config: Config, processors: SerializerPlugin[], options?: SerializerConfigOptions): Config;
export declare function createDefaultExportCustomSerializer(config: Partial<MetroConfig>, configOptions?: SerializerConfigOptions): Serializer;
export declare function createSerializerFromSerialProcessors(config: MetroConfig, processors: (SerializerPlugin | undefined)[], originalSerializer: Serializer | null, options?: SerializerConfigOptions): Serializer;
export { SerialAsset };

View File

@@ -0,0 +1,301 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.withExpoSerializers = withExpoSerializers;
exports.withSerializerPlugins = withSerializerPlugins;
exports.createDefaultExportCustomSerializer = createDefaultExportCustomSerializer;
exports.createSerializerFromSerialProcessors = createSerializerFromSerialProcessors;
const bundleToString_1 = __importDefault(require("@expo/metro/metro/lib/bundleToString"));
const jsc_safe_url_1 = require("jsc-safe-url");
const debugId_1 = require("./debugId");
const environmentVariableSerializerPlugin_1 = require("./environmentVariableSerializerPlugin");
const serializeChunks_1 = require("./serializeChunks");
const env_1 = require("../env");
// Lazy-loaded to avoid pulling in metro-source-map and @babel/traverse at startup (~100ms savings)
let _sourceMapString;
function getSourceMapString() {
if (!_sourceMapString) {
_sourceMapString =
require('@expo/metro/metro/DeltaBundler/Serializers/sourceMapString').sourceMapString;
}
return _sourceMapString;
}
// Lazy-loaded to avoid pulling in @babel/generator, @babel/core at startup
let _reconcileTransformSerializerPlugin;
function getReconcileTransformSerializerPlugin() {
if (!_reconcileTransformSerializerPlugin) {
_reconcileTransformSerializerPlugin =
require('./reconcileTransformSerializerPlugin').reconcileTransformSerializerPlugin;
}
return _reconcileTransformSerializerPlugin;
}
let _treeShakeSerializer;
function getTreeShakeSerializer() {
if (!_treeShakeSerializer) {
_treeShakeSerializer = require('./treeShakeSerializerPlugin').treeShakeSerializer;
}
return _treeShakeSerializer;
}
// Lazy-loaded to avoid pulling in metro's getAppendScripts -> sourceMapString chain at startup
let _baseJSBundle;
function getBaseJSBundle() {
if (!_baseJSBundle) {
_baseJSBundle = require('./fork/baseJSBundle').baseJSBundle;
}
return _baseJSBundle;
}
function withExpoSerializers(config, options = {}) {
const processors = [];
processors.push(environmentVariableSerializerPlugin_1.serverPreludeSerializerPlugin);
if (!env_1.env.EXPO_NO_CLIENT_ENV_VARS) {
processors.push(environmentVariableSerializerPlugin_1.environmentVariableSerializerPlugin);
}
// Then tree-shake the modules.
processors.push(getTreeShakeSerializer());
// Then finish transforming the modules from AST to JS.
processors.push(getReconcileTransformSerializerPlugin());
return withSerializerPlugins(config, processors, options);
}
// There can only be one custom serializer as the input doesn't match the output.
// Here we simply run
function withSerializerPlugins(config, processors, options = {}) {
const expoSerializer = createSerializerFromSerialProcessors(config, processors, config.serializer?.customSerializer ?? null, options);
// We can't object-spread the config, it loses the reference to the original config
// Meaning that any user-provided changes are not propagated to the serializer config
// @ts-expect-error TODO(cedric): it's a read only property, but we can actually write it
config.serializer ??= {};
// @ts-expect-error TODO(cedric): it's a read only property, but we can actually write it
config.serializer.customSerializer = expoSerializer;
return config;
}
function createDefaultExportCustomSerializer(config, configOptions = {}) {
return async (entryPoint, preModules, graph, inputOptions) => {
// NOTE(@kitten): My guess is that this was supposed to always be disabled for `node` since we set `hot: true` manually for it
const isPossiblyDev = graph.transformOptions.dev ||
graph.transformOptions.customTransformOptions?.environment === 'node';
// TODO: This is a temporary solution until we've converged on using the new serializer everywhere.
const enableDebugId = inputOptions.inlineSourceMap !== true && !isPossiblyDev;
const context = {
platform: graph.transformOptions?.platform,
environment: graph.transformOptions?.customTransformOptions?.environment ?? 'client',
};
const options = {
...inputOptions,
createModuleId: (moduleId, ...props) => {
if (props.length > 0) {
return inputOptions.createModuleId(moduleId, ...props);
}
return inputOptions.createModuleId(moduleId,
// @ts-expect-error: context is added by Expo and not part of the upstream Metro implementation.
context);
},
};
let debugId;
const loadDebugId = () => {
if (!enableDebugId || debugId) {
return debugId;
}
// TODO: Perform this cheaper.
const bundle = getBaseJSBundle()(entryPoint, preModules, graph, {
...options,
debugId: undefined,
});
const outputCode = (0, bundleToString_1.default)(bundle).code;
debugId = (0, debugId_1.stringToUUID)(outputCode);
return debugId;
};
let premodulesToBundle = [...preModules];
let bundleCode = null;
let bundleMap = null;
// Only invoke the custom serializer if it's not our serializer
// We write the Expo serializer back to the original config object, possibly falling into recursive loops
const originalCustomSerializer = unwrapOriginalSerializer(config.serializer?.customSerializer);
if (originalCustomSerializer) {
const bundle = await originalCustomSerializer(entryPoint, premodulesToBundle, graph, options);
if (typeof bundle === 'string') {
bundleCode = bundle;
}
else {
bundleCode = bundle.code;
bundleMap = bundle.map;
}
}
else {
const debugId = loadDebugId();
if (configOptions.unstable_beforeAssetSerializationPlugins) {
for (const plugin of configOptions.unstable_beforeAssetSerializationPlugins) {
premodulesToBundle = plugin({ graph, premodules: [...premodulesToBundle], debugId });
}
}
bundleCode = (0, bundleToString_1.default)(getBaseJSBundle()(entryPoint, premodulesToBundle, graph, {
...options,
debugId,
})).code;
}
const getEnsuredMaps = () => {
bundleMap ??= getSourceMapString()([...premodulesToBundle, ...(0, serializeChunks_1.getSortedModules)([...graph.dependencies.values()], options)], {
// TODO: Surface this somehow.
excludeSource: false,
// excludeSource: options.serializerOptions?.excludeSource,
processModuleFilter: options.processModuleFilter,
shouldAddToIgnoreList: options.shouldAddToIgnoreList,
});
return bundleMap;
};
if (!bundleMap && options.sourceUrl) {
const url = (0, jsc_safe_url_1.isJscSafeUrl)(options.sourceUrl)
? (0, jsc_safe_url_1.toNormalUrl)(options.sourceUrl)
: options.sourceUrl;
const parsed = new URL(url, 'http://expo.dev');
// Is dev server request for source maps...
if (parsed.pathname.endsWith('.map')) {
return {
code: bundleCode,
map: getEnsuredMaps(),
};
}
}
if (isPossiblyDev) {
if (bundleMap == null) {
return bundleCode;
}
return {
code: bundleCode,
map: bundleMap,
};
}
// Exports....
bundleMap ??= getEnsuredMaps();
if (enableDebugId) {
const mutateSourceMapWithDebugId = (sourceMap) => {
// NOTE: debugId isn't required for inline source maps because the source map is included in the same file, therefore
// we don't need to disambiguate between multiple source maps.
const sourceMapObject = JSON.parse(sourceMap);
sourceMapObject.debugId = loadDebugId();
// NOTE: Sentry does this, but bun does not.
// sourceMapObject.debug_id = debugId;
return JSON.stringify(sourceMapObject);
};
return {
code: bundleCode,
map: mutateSourceMapWithDebugId(bundleMap),
};
}
return {
code: bundleCode,
map: bundleMap,
};
};
}
function getDefaultSerializer(config, fallbackSerializer, configOptions = {}) {
const defaultSerializer = fallbackSerializer ?? createDefaultExportCustomSerializer(config, configOptions);
const expoSerializer = async (entryPoint, preModules, graph, inputOptions) => {
const context = {
platform: graph.transformOptions?.platform,
environment: graph.transformOptions?.customTransformOptions?.environment ?? 'client',
};
const isLoaderBundle = graph.transformOptions?.customTransformOptions?.isLoaderBundle === 'true';
const loaderPaths = isLoaderBundle ? getLoaderPaths(graph.dependencies) : new Set();
const options = {
...inputOptions,
createModuleId: (moduleId, ...props) => {
// For loader bundles, append `+loader` to modules with `loaderReference`.
// This creates different module IDs from `render.js` for the same source file,
// avoiding module ID collisions when both bundles are loaded in the same runtime.
let pathToHash = moduleId;
if (isLoaderBundle && loaderPaths.has(moduleId)) {
pathToHash = `${moduleId}+loader`;
}
if (props.length > 0) {
return inputOptions.createModuleId(pathToHash, ...props);
}
return inputOptions.createModuleId(pathToHash,
// @ts-expect-error: context is added by Expo and not part of the upstream Metro implementation.
context);
},
};
const customSerializerOptions = inputOptions.serializerOptions;
// Custom options can only be passed outside of the dev server, meaning
// we don't need to stringify the results at the end, i.e. this is `npx expo export` or `npx expo export:embed`.
const supportsNonSerialReturn = !!customSerializerOptions?.output;
const serializerOptions = (() => {
if (customSerializerOptions) {
return {
outputMode: customSerializerOptions.output,
splitChunks: customSerializerOptions.splitChunks,
usedExports: customSerializerOptions.usedExports,
includeSourceMaps: customSerializerOptions.includeSourceMaps,
};
}
if (options.sourceUrl) {
const sourceUrl = (0, jsc_safe_url_1.isJscSafeUrl)(options.sourceUrl)
? (0, jsc_safe_url_1.toNormalUrl)(options.sourceUrl)
: options.sourceUrl;
const url = new URL(sourceUrl, 'https://expo.dev');
return {
outputMode: url.searchParams.get('serializer.output'),
usedExports: url.searchParams.get('serializer.usedExports') === 'true',
splitChunks: url.searchParams.get('serializer.splitChunks') === 'true',
includeSourceMaps: url.searchParams.get('serializer.map') === 'true',
};
}
return null;
})();
if (serializerOptions?.outputMode !== 'static') {
return defaultSerializer(entryPoint, preModules, graph, options);
}
// Mutate the serializer options with the parsed options.
options.serializerOptions = {
...options.serializerOptions,
...serializerOptions,
};
const assets = await (0, serializeChunks_1.graphToSerialAssetsAsync)(config, {
includeSourceMaps: !!serializerOptions.includeSourceMaps,
splitChunks: !!serializerOptions.splitChunks,
...configOptions,
}, entryPoint, preModules, graph, options);
if (supportsNonSerialReturn) {
// @ts-expect-error: this is future proofing for adding assets to the output as well.
return assets;
}
return JSON.stringify(assets);
};
return Object.assign(expoSerializer, { __expoSerializer: true });
}
function createSerializerFromSerialProcessors(config, processors, originalSerializer, options = {}) {
const finalSerializer = getDefaultSerializer(config, originalSerializer, options);
return wrapSerializerWithOriginal(originalSerializer, async (...props) => {
for (const processor of processors) {
if (processor) {
props = await processor(...props);
}
}
return finalSerializer(...props);
});
}
function wrapSerializerWithOriginal(original, expo) {
return Object.assign(expo, { __originalSerializer: original });
}
function unwrapOriginalSerializer(serializer) {
if (!serializer || !('__originalSerializer' in serializer))
return null;
return serializer.__originalSerializer;
}
/**
* Collect paths of modules that have `loaderReference` metadata.
* In loader bundles, these modules need different IDs to avoid collisions with `render.js`.
*/
function getLoaderPaths(dependencies) {
const loaderPaths = new Set();
for (const module of dependencies.values()) {
for (const output of module.output) {
if ('loaderReference' in output.data && typeof output.data.loaderReference === 'string') {
loaderPaths.add(module.path);
}
}
}
return loaderPaths;
}
//# sourceMappingURL=withExpoSerializers.js.map

File diff suppressed because one or more lines are too long