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,113 @@
import type { MixedOutput, Module, ReadOnlyGraph } from '@expo/metro/metro/DeltaBundler/types';
import type { ConfigT as MetroConfig } from '@expo/metro/metro-config';
import { INTERNAL_CALLSITES_REGEX } from './customizeFrame';
import { FileStore } from './file-store';
export interface DefaultConfigOptions {
/** @deprecated */
mode?: 'exotic';
/**
* **Experimental:** Enable CSS support for Metro web, and shim on native.
*
* This is an experimental feature and may change in the future. The underlying implementation
* is subject to change, and native support for CSS Modules may be added in the future during a non-major SDK release.
*/
isCSSEnabled?: boolean;
/**
* **Experimental:** Modify premodules before a code asset is serialized
*
* This is an experimental feature and may change in the future. The underlying implementation
* is subject to change.
*/
unstable_beforeAssetSerializationPlugins?: ((serializationInput: {
graph: ReadOnlyGraph<MixedOutput>;
premodules: Module[];
debugId?: string;
}) => Module[])[];
}
export declare function createStableModuleIdFactory(root: string): (path: string, context?: {
platform: string;
environment?: string | null;
}) => number;
export declare function getDefaultConfig(projectRoot: string, { mode, isCSSEnabled, unstable_beforeAssetSerializationPlugins }?: DefaultConfigOptions): MetroConfig & {
reporter: {
update(): void;
};
watchFolders: string[];
resolver: {
unstable_conditionsByPlatform: {
ios: string[];
android: string[];
web: string[];
};
resolverMainFields: string[];
platforms: string[];
assetExts: string[];
sourceExts: string[];
nodeModulesPaths: string[];
blockList: RegExp[];
};
cacheStores: FileStore<any>[];
watcher: {
unstable_workerThreads: false;
additionalExts: string[];
};
serializer: {
isThirdPartyModule(module: {
readonly path: string;
}): boolean;
createModuleIdFactory: () => (path: string, context?: {
platform: string;
environment?: string | null;
}) => number;
getModulesRunBeforeMainModule: () => string[];
getPolyfills: ({ platform }: {
platform?: null | string;
}) => any;
};
server: {
rewriteRequestUrl: (url: string) => string;
port: number;
unstable_serverRoot: string;
};
symbolicator: {
customizeFrame: ($$PARAM_0$$: {
readonly file?: null | string;
readonly lineNumber?: null | number;
readonly column?: null | number;
readonly methodName?: null | string;
}) => (null | undefined | {
readonly collapse?: boolean;
}) | Promise<null | undefined | {
readonly collapse?: boolean;
}>;
};
transformerPath: string;
transformer: {
unstable_workerThreads: true;
unstable_renameRequire: false;
_expoRouterPath: string | undefined;
postcssHash: string | null;
browserslistHash: string | null;
sassVersion: string | null;
reanimatedVersion: string | null;
workletsVersion: string | null;
_expoRelativeProjectRoot: string;
unstable_allowRequireContext: true;
allowOptionalDependencies: true;
babelTransformerPath: string;
asyncRequireModulePath: string;
assetRegistryPath: string;
enableBabelRuntime: string | undefined;
getTransformOptions: () => Promise<{
transform: {
experimentalImportSupport: true;
inlineRequires: false;
};
}>;
};
};
/** Use to access the Expo Metro transformer path */
export declare const unstable_transformerPath: string;
export declare const internal_supervisingTransformerPath: string;
export { MetroConfig, INTERNAL_CALLSITES_REGEX };
export declare const EXPO_DEBUG: boolean;

View File

@@ -0,0 +1,388 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EXPO_DEBUG = exports.INTERNAL_CALLSITES_REGEX = exports.internal_supervisingTransformerPath = exports.unstable_transformerPath = void 0;
exports.createStableModuleIdFactory = createStableModuleIdFactory;
exports.getDefaultConfig = getDefaultConfig;
// Copyright 2023-present 650 Industries (Expo). All rights reserved.
const config_1 = require("@expo/config");
const paths_1 = require("@expo/config/paths");
const json_file_1 = __importDefault(require("@expo/json-file"));
const metro_cache_1 = require("@expo/metro/metro-cache");
const chalk_1 = __importDefault(require("chalk"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const customizeFrame_1 = require("./customizeFrame");
Object.defineProperty(exports, "INTERNAL_CALLSITES_REGEX", { enumerable: true, get: function () { return customizeFrame_1.INTERNAL_CALLSITES_REGEX; } });
const env_1 = require("./env");
const file_store_1 = require("./file-store");
const getModulesPaths_1 = require("./getModulesPaths");
const getWatchFolders_1 = require("./getWatchFolders");
const rewriteRequestUrl_1 = require("./rewriteRequestUrl");
const sideEffects_1 = require("./serializer/sideEffects");
const withExpoSerializers_1 = require("./serializer/withExpoSerializers");
const postcss_1 = require("./transform-worker/postcss");
const filePath_1 = require("./utils/filePath");
const setOnReadonly_1 = require("./utils/setOnReadonly");
const debug = require('debug')('expo:metro:config');
let hasWarnedAboutExotic = false;
let hasWarnedAboutReactNative = false;
// Patch Metro's graph to support always parsing certain modules. This enables
// things like Tailwind CSS which update based on their own heuristics.
function patchMetroGraphToSupportUncachedModules() {
const { Graph, } = require('@expo/metro/metro/DeltaBundler/Graph');
const original_traverseDependencies = Graph.prototype
.traverseDependencies;
if (!original_traverseDependencies.__patched) {
original_traverseDependencies.__patched = true;
// eslint-disable-next-line no-inner-declarations
function traverseDependencies(paths, options) {
this.dependencies.forEach((dependency) => {
// Find any dependencies that have been marked as `skipCache` and ensure they are invalidated.
// `skipCache` is set when a CSS module is found by PostCSS.
if (dependency.output.find((file) => file.data.css?.skipCache) &&
!paths.includes(dependency.path)) {
// Ensure we invalidate the `unstable_transformResultKey` (input hash) so the module isn't removed in
// the Graph._processModule method.
(0, setOnReadonly_1.setOnReadonly)(dependency, 'unstable_transformResultKey', dependency.unstable_transformResultKey + '.');
// Add the path to the list of modified paths so it gets run through the transformer again,
// this will ensure it is passed to PostCSS -> Tailwind.
paths.push(dependency.path);
}
});
// Invoke the original method with the new paths to ensure the standard behavior is preserved.
return original_traverseDependencies.call(this, paths, options);
}
// Ensure we don't patch the method twice.
Graph.prototype.traverseDependencies = traverseDependencies;
traverseDependencies.__patched = true;
}
}
function createNumericModuleIdFactory() {
const fileToIdMap = new Map();
let nextId = 0;
return (modulePath) => {
let id = fileToIdMap.get(modulePath);
if (typeof id !== 'number') {
id = nextId++;
fileToIdMap.set(modulePath, id);
}
return id;
};
}
function memoize(fn) {
const cache = new Map();
return ((...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
});
}
function asMetroConfigInput(config) {
return config;
}
function createStableModuleIdFactory(root) {
const getModulePath = (modulePath, scope) => {
// NOTE: Metro allows this but it can lead to confusing errors when dynamic requires cannot be resolved, e.g. `module 456 cannot be found`.
if (modulePath == null) {
return 'MODULE_NOT_FOUND';
}
else if ((0, sideEffects_1.isVirtualModule)(modulePath)) {
// Virtual modules should be stable.
return modulePath;
}
else if (path_1.default.isAbsolute(modulePath)) {
return (0, filePath_1.toPosixPath)(path_1.default.relative(root, modulePath)) + scope;
}
else {
return (0, filePath_1.toPosixPath)(modulePath) + scope;
}
};
const memoizedGetModulePath = memoize(getModulePath);
// This is an absolute file path.
// TODO: We may want a hashed version for production builds in the future.
return (modulePath, context) => {
const env = context?.environment ?? 'client';
if (env === 'client') {
// Only need scope for server bundles where multiple dimensions could run simultaneously.
// @ts-expect-error: we patch this to support being a string.
return memoizedGetModulePath(modulePath, '');
}
// Helps find missing parts to the patch.
if (!context?.platform) {
// context = { platform: 'web' };
throw new Error('createStableModuleIdFactory: `context.platform` is required');
}
// Only need scope for server bundles where multiple dimensions could run simultaneously.
const scope = env !== 'client' ? `?platform=${context?.platform}&env=${env}` : '';
// @ts-expect-error: we patch this to support being a string.
return memoizedGetModulePath(modulePath, scope);
};
}
function getDefaultConfig(projectRoot, { mode, isCSSEnabled = true, unstable_beforeAssetSerializationPlugins } = {}) {
const { getDefaultConfig: getDefaultMetroConfig, mergeConfig, } = require('@expo/metro/metro-config');
if (isCSSEnabled) {
patchMetroGraphToSupportUncachedModules();
}
const isExotic = mode === 'exotic' || env_1.env.EXPO_USE_EXOTIC;
if (isExotic && !hasWarnedAboutExotic) {
hasWarnedAboutExotic = true;
console.log(chalk_1.default.gray(`\u203A Feature ${chalk_1.default.bold `EXPO_USE_EXOTIC`} has been removed in favor of the default transformer.`));
}
const reactNativePath = path_1.default.dirname(resolve_from_1.default.silent(projectRoot, 'react-native/package.json') ?? 'react-native/package.json');
if (reactNativePath === 'react-native' && !hasWarnedAboutReactNative) {
hasWarnedAboutReactNative = true;
console.log(chalk_1.default.yellow(`\u203A Could not resolve react-native! Is it installed and a project dependency?`));
}
const sourceExtsConfig = { isTS: true, isReact: true, isModern: true };
const sourceExts = (0, paths_1.getBareExtensions)([], sourceExtsConfig);
// Add support for cjs (without platform extensions).
sourceExts.push('cjs');
const reanimatedVersion = getPkgVersion(projectRoot, 'react-native-reanimated');
const workletsVersion = getPkgVersion(projectRoot, 'react-native-worklets');
const babelRuntimeVersion = getPkgVersion(projectRoot, '@babel/runtime');
let sassVersion = null;
if (isCSSEnabled) {
sassVersion = getPkgVersion(projectRoot, 'sass');
// Enable SCSS by default so we can provide a better error message
// when sass isn't installed.
sourceExts.push('scss', 'sass', 'css');
}
let pkg;
try {
pkg = (0, config_1.getPackageJson)(projectRoot);
}
catch (error) {
if (error && error.name === 'ConfigError') {
console.log(chalk_1.default.yellow(`\u203A Could not find a package.json at the project root! ("${projectRoot}")`));
}
else {
throw error;
}
}
const watchFolders = (0, getWatchFolders_1.getWatchFolders)(projectRoot);
const nodeModulesPaths = (0, getModulesPaths_1.getModulesPaths)(projectRoot);
if (env_1.env.EXPO_DEBUG) {
console.log();
console.log(`Expo Metro config:`);
try {
console.log(`- Version: ${require('../package.json').version}`);
}
catch { }
console.log(`- Extensions: ${sourceExts.join(', ')}`);
console.log(`- React Native: ${reactNativePath}`);
console.log(`- Watch Folders: ${watchFolders.join(', ')}`);
console.log(`- Node Module Paths: ${nodeModulesPaths.join(', ')}`);
console.log(`- Sass: ${sassVersion}`);
console.log(`- Reanimated: ${reanimatedVersion}`);
console.log(`- Worklets: ${workletsVersion}`);
console.log(`- Babel Runtime: ${babelRuntimeVersion}`);
console.log();
}
const metroDefaultValues = getDefaultMetroConfig.getDefaultValues(projectRoot);
const cacheStore = new file_store_1.FileStore({
root: path_1.default.join(os_1.default.tmpdir(), 'metro-cache'),
});
const serverRoot = (0, paths_1.getMetroServerRoot)(projectRoot);
const routerPackageRoot = resolve_from_1.default.silent(projectRoot, 'expo-router');
const expoMetroConfig = asMetroConfigInput({
reporter: {
// Remove the default reporter which metro always resolves to be the react-native-community/cli reporter.
// This prints a giant React logo which is less accessible to users on smaller terminals.
update() {
/*noop*/
},
},
watchFolders,
resolver: {
unstable_conditionsByPlatform: {
ios: ['react-native'],
android: ['react-native'],
// This is removed for server platforms.
web: ['browser'],
},
resolverMainFields: ['react-native', 'browser', 'main'],
platforms: ['ios', 'android'],
assetExts: metroDefaultValues.resolver.assetExts
.concat(
// Add default support for `expo-image` file types.
['heic', 'avif'],
// Add default support for `expo-sqlite` file types.
['db'])
.filter((assetExt) => !sourceExts.includes(assetExt)),
sourceExts,
nodeModulesPaths,
blockList: [
// .expo/types contains generated declaration files which are not and should not be processed by Metro.
// This prevents unwanted fast refresh on the declaration files changes.
/\.expo[\\/]types/,
].concat(metroDefaultValues.resolver.blockList ?? []),
},
cacheStores: [cacheStore],
watcher: {
unstable_workerThreads: false,
// strip starting dot from env files. We only support watching development variants of env files as production is inlined using a different system.
additionalExts: ['env', 'local', 'development'],
},
serializer: {
isThirdPartyModule(module) {
// Block virtual modules from appearing in the source maps.
if ((0, sideEffects_1.isVirtualModule)(module.path))
return true;
// Generally block node modules
if (/(?:^|[/\\])node_modules[/\\]/.test(module.path)) {
// Allow the expo-router/entry and expo/AppEntry modules to be considered first party so the root of the app appears in the trace.
return !module.path.match(/[/\\](expo-router[/\\]entry|expo[/\\]AppEntry)/);
}
return false;
},
createModuleIdFactory: env_1.env.EXPO_USE_METRO_REQUIRE
? createStableModuleIdFactory.bind(null, serverRoot)
: createNumericModuleIdFactory,
getModulesRunBeforeMainModule: () => {
const preModules = [
// MUST be first
require.resolve(path_1.default.join(reactNativePath, 'Libraries/Core/InitializeCore')),
];
const stdRuntime = resolve_from_1.default.silent(projectRoot, 'expo/src/winter/index.ts');
if (stdRuntime) {
preModules.push(stdRuntime);
}
else {
debug('"expo/src/winter" not found, this may cause issues');
}
// We need to shift this to be the first module so web Fast Refresh works as expected.
// This will only be applied if the module is installed and imported somewhere in the bundle already.
const metroRuntime = getExpoMetroRuntimeOptional(projectRoot);
if (metroRuntime) {
preModules.push(metroRuntime);
}
else {
debug('"@expo/metro-runtime" not found, this may cause issues');
}
return preModules;
},
getPolyfills: ({ platform }) => {
// Do nothing for nullish platforms.
if (!platform) {
return [];
}
// Native behavior.
return require(path_1.default.join(reactNativePath, 'rn-get-polyfills'))();
},
},
server: {
rewriteRequestUrl: (0, rewriteRequestUrl_1.getRewriteRequestUrl)(projectRoot),
port: Number(env_1.env.RCT_METRO_PORT) || 8081,
// NOTE(EvanBacon): Moves the server root down to the monorepo root.
// This enables proper monorepo support for web.
unstable_serverRoot: serverRoot,
},
symbolicator: {
customizeFrame: (0, customizeFrame_1.getDefaultCustomizeFrame)(),
},
transformerPath: require.resolve('./transform-worker/transform-worker'),
// NOTE: All of these values are used in the cache key. They should not contain any absolute paths.
transformer: {
unstable_workerThreads: true,
// Custom: These are passed to `getCacheKey` and ensure invalidation when the version changes.
unstable_renameRequire: false,
_expoRouterPath: routerPackageRoot ? path_1.default.relative(serverRoot, routerPackageRoot) : undefined,
postcssHash: (0, postcss_1.getPostcssConfigHash)(projectRoot),
browserslistHash: pkg?.browserslist
? (0, metro_cache_1.stableHash)(JSON.stringify(pkg?.browserslist)).toString('hex')
: null,
sassVersion,
// Ensure invalidation when the version changes due to the Reanimated and Worklets Babel plugins.
reanimatedVersion,
workletsVersion,
// Ensure invalidation when using identical projects in monorepos
_expoRelativeProjectRoot: path_1.default.relative(serverRoot, projectRoot),
// `require.context` support
unstable_allowRequireContext: true,
allowOptionalDependencies: true,
babelTransformerPath: require.resolve('./babel-transformer'),
// Only apply expo internal asyncRequireModulePath when `expo` is installed
// This must be a module request, rather than an absolute path to keep the cache clean
asyncRequireModulePath: getExpoOptional(projectRoot, 'internal/async-require-module')
? 'expo/internal/async-require-module'
: metroDefaultValues.transformer.asyncRequireModulePath,
assetRegistryPath: '@react-native/assets-registry/registry',
// Determines the minimum version of `@babel/runtime`, so we default it to the project's installed version of `@babel/runtime`
enableBabelRuntime: babelRuntimeVersion ?? undefined,
// hermesParser: true,
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: true,
inlineRequires: false,
},
}),
},
});
// Merge in the default config from Metro here, even though loadConfig uses it as defaults.
// This is a convenience for getDefaultConfig use in metro.config.js, e.g. to modify assetExts.
const metroConfig = mergeConfig(
// NOTE(@kitten): We neither want ConfigT/MetroConfig here, which is mostly marked as readonly,
// nor InputConfigT which is inexact and partial. Instead, we want an exact type combination of
// the default config and Expo's config
metroDefaultValues, expoMetroConfig);
return (0, withExpoSerializers_1.withExpoSerializers)(metroConfig, { unstable_beforeAssetSerializationPlugins });
}
/** Use to access the Expo Metro transformer path */
exports.unstable_transformerPath = require.resolve('./transform-worker/transform-worker');
exports.internal_supervisingTransformerPath = require.resolve('./transform-worker/supervising-transform-worker');
// re-export for legacy cases.
exports.EXPO_DEBUG = env_1.env.EXPO_DEBUG;
function getPkgVersion(projectRoot, pkgName) {
const targetPkg = resolve_from_1.default.silent(projectRoot, pkgName);
if (!targetPkg)
return null;
const targetPkgJson = findUpPackageJson(targetPkg);
if (!targetPkgJson)
return null;
const pkg = json_file_1.default.read(targetPkgJson);
debug(`${pkgName} package.json:`, targetPkgJson);
const pkgVersion = pkg.version;
if (typeof pkgVersion === 'string') {
return pkgVersion;
}
return null;
}
function findUpPackageJson(cwd) {
if (['.', path_1.default.sep].includes(cwd))
return null;
const found = resolve_from_1.default.silent(cwd, './package.json');
if (found) {
return found;
}
return findUpPackageJson(path_1.default.dirname(cwd));
}
function getExpoOptional(projectRoot, subModule = 'package.json') {
return resolve_from_1.default.silent(projectRoot, `expo/${subModule}`);
}
function getExpoMetroRuntimeOptional(projectRoot) {
const EXPO_METRO_RUNTIME = '@expo/metro-runtime';
const metroRuntime = resolve_from_1.default.silent(projectRoot, EXPO_METRO_RUNTIME);
if (metroRuntime) {
return metroRuntime;
}
// NOTE(@kitten): While `@expo/metro-runtime` is a peer, auto-installing this peer is valid and expected
// When it's auto-installed it may not be hoisted or not accessible from the project root, so we need to
// try to also resolve it via `expo-router`, where it's a required peer
const baseExpoRouter = resolve_from_1.default.silent(projectRoot, 'expo-router/package.json');
if (baseExpoRouter) {
return resolve_from_1.default.silent(baseExpoRouter, EXPO_METRO_RUNTIME);
}
// When expo-router isn't installed, however, we instead try to resolve it from `expo`, where it's an
// optional peer dependency
const baseExpo = getExpoOptional(projectRoot);
return baseExpo ? resolve_from_1.default.silent(baseExpo, EXPO_METRO_RUNTIME) : undefined;
}
//# sourceMappingURL=ExpoMetroConfig.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export { type PluginItem, type TransformOptions, transformFromAstSync, transformSync, } from '@babel/core';

8
node_modules/@expo/metro-config/build/babel-core.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformSync = exports.transformFromAstSync = void 0;
// re-exported because babel/core is hard to mock.
var core_1 = require("@babel/core");
Object.defineProperty(exports, "transformFromAstSync", { enumerable: true, get: function () { return core_1.transformFromAstSync; } });
Object.defineProperty(exports, "transformSync", { enumerable: true, get: function () { return core_1.transformSync; } });
//# sourceMappingURL=babel-core.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"babel-core.js","sourceRoot":"","sources":["../src/babel-core.ts"],"names":[],"mappings":";;;AAAA,kDAAkD;AAClD,oCAKqB;AAFnB,4GAAA,oBAAoB,OAAA;AACpB,qGAAA,aAAa,OAAA"}

View File

@@ -0,0 +1,21 @@
import type { TransformOptions } from './babel-core';
export type ExpoBabelCaller = TransformOptions['caller'] & {
babelRuntimeVersion?: string;
metroSourceType?: 'script' | 'module' | 'asset';
supportsReactCompiler?: boolean;
isReactServer?: boolean;
isHMREnabled?: boolean;
isServer?: boolean;
isNodeModule?: boolean;
preserveEnvVars?: boolean;
isDev?: boolean;
asyncRoutes?: boolean;
baseUrl?: string;
engine?: string;
bundler?: 'metro' | (string & object);
platform?: string | null;
routerRoot?: string;
projectRoot: string;
/** When true, indicates this bundle should contain only the loader export */
isLoaderBundle?: boolean;
};

View File

@@ -0,0 +1,153 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_assert_1 = __importDefault(require("node:assert"));
const loadBabelConfig_1 = require("./loadBabelConfig");
const transformSync_1 = require("./transformSync");
const debug = require('debug')('expo:metro-config:babel-transformer');
function isCustomTruthy(value) {
return String(value) === 'true';
}
function memoize(fn) {
const cache = new Map();
return ((...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
});
}
const memoizeWarning = memoize((message) => {
debug(message);
});
function getBabelCaller({ filename, options, }) {
const isNodeModule = filename.includes('node_modules');
const isReactServer = options.customTransformOptions?.environment === 'react-server';
const isGenericServer = options.customTransformOptions?.environment === 'node';
const isServer = isReactServer || isGenericServer;
const routerRoot = typeof options.customTransformOptions?.routerRoot === 'string'
? decodeURI(options.customTransformOptions.routerRoot)
: undefined;
if (routerRoot == null) {
memoizeWarning('Warning: Missing transform.routerRoot option in Metro bundling request, falling back to `app` as routes directory. This can occur if you bundle without Expo CLI or expo/metro-config.');
}
return {
name: 'metro',
bundler: 'metro',
platform: options.platform,
// Empower the babel preset to know the env it's bundling for.
// Metro automatically updates the cache to account for the custom transform options.
isServer,
// Enable React Server Component rules for AST. The naming maps to the resolver property `--conditions=react-server`.
isReactServer,
// The base url to make requests from, used for hosting from non-standard locations.
baseUrl: typeof options.customTransformOptions?.baseUrl === 'string'
? decodeURI(options.customTransformOptions.baseUrl)
: '',
// Ensure we always use a mostly-valid router root.
routerRoot: routerRoot ?? 'app',
isDev: options.dev,
// This value indicates if the user has disabled the feature or not.
// Other criteria may still cause the feature to be disabled, but all inputs used are
// already considered in the cache key.
preserveEnvVars: isCustomTruthy(options.customTransformOptions?.preserveEnvVars)
? true
: undefined,
asyncRoutes: isCustomTruthy(options.customTransformOptions?.asyncRoutes) ? true : undefined,
// Pass the engine to babel so we can automatically transpile for the correct
// target environment.
engine: stringOrUndefined(options.customTransformOptions?.engine),
// Provide the project root for accurately reading the Expo config.
projectRoot: options.projectRoot,
isNodeModule,
// TODO(@kitten): Removed and the default; The `hot` parameter is now force-enabled in Metro
// to align caching for `dev` with `hot` being enforced. Hence, we match this by forcing our
// own caller flag to `true` for `babel-preset-expo`. However, `babel-preset-expo` is still
// able to disable the React Refresh transform plugin for other runtimes and uses this flag
// to identify Metro / React Refresh runtime targets
isHMREnabled: true,
// Pass on the input type. Scripts shall be transformed to avoid dependencies (imports/requires),
// for example by polyfills or Babel runtime
metroSourceType: options.type,
// Set the standard Babel flag to disable ESM transformations.
supportsStaticESM: isCustomTruthy(options.customTransformOptions?.optimize) || options.experimentalImportSupport,
// Enable React compiler support in Babel.
// TODO: Remove this in the future when compiler is on by default.
supportsReactCompiler: isCustomTruthy(options.customTransformOptions?.reactCompiler)
? true
: undefined,
// When true, indicates this bundle should contain only the loader export.
// Used by server-data-loaders-plugin to strip everything except the loader function.
isLoaderBundle: isCustomTruthy(options.customTransformOptions?.isLoaderBundle)
? true
: undefined,
// This is picked up by `babel-preset-expo` if it's set, and overrides the minimum supported
// `@babel/runtime` version that `@babel/plugin-transform-runtime` can assume is installed
// This option should be set to the project's version of `@babel/runtime`, if it's installed directly
babelRuntimeVersion: typeof options.enableBabelRuntime === 'string' ? options.enableBabelRuntime : undefined,
};
}
function stringOrUndefined(value) {
return typeof value === 'string' ? value : undefined;
}
const transform = ({ filename, src, options,
// `plugins` is used for `functionMapBabelPlugin` from `metro-source-map`. Could make sense to move this to `babel-preset-expo` too.
plugins, }) => {
const OLD_BABEL_ENV = process.env.BABEL_ENV;
process.env.BABEL_ENV = options.dev ? 'development' : process.env.BABEL_ENV || 'production';
try {
const babelConfig = {
// ES modules require sourceType='module' but OSS may not always want that
sourceType: 'unambiguous',
// The output we want from Babel methods
ast: true,
code: false,
// NOTE(EvanBacon): We split the parse/transform steps up to accommodate
// Hermes parsing, but this defaults to cloning the AST which increases
// the transformation time by a fair amount.
// You get this behavior by default when using Babel's `transform` method directly.
cloneInputAst: false,
// Options for debugging
cwd: options.projectRoot,
filename,
highlightCode: true,
// Load the project babel config file.
...(0, loadBabelConfig_1.loadBabelConfig)(options),
babelrc: typeof options.enableBabelRCLookup === 'boolean' ? options.enableBabelRCLookup : true,
plugins,
// NOTE(EvanBacon): We heavily leverage the caller functionality to mutate the babel config.
// This compensates for the lack of a format plugin system in Metro. Users can modify the
// all (most) of the transforms in their local Babel config.
// This also helps us keep the transform layers small and focused on a single task. We can also use this to
// ensure the Babel config caching is more accurate.
// Additionally, by moving everything Babel-related to the Babel preset, it makes it easier for users to reason
// about the requirements of an Expo project, making it easier to migrate to other transpilers in the future.
caller: getBabelCaller({ filename, options }),
};
const result = (0, transformSync_1.transformSync)(src, babelConfig, options);
// The result from `transformFromAstSync` can be null (if the file is ignored)
if (!result) {
// BabelTransformer specifies that the `ast` can never be null but
// the function returns here. Discovered when typing `BabelNode`.
// @ts-expect-error: see https://github.com/facebook/react-native/blob/401991c3f073bf734ee04f9220751c227d2abd31/packages/react-native-babel-transformer/src/index.js#L220-L224
return { ast: null };
}
(0, node_assert_1.default)(result.ast);
return { ast: result.ast, metadata: result.metadata };
}
finally {
if (OLD_BABEL_ENV) {
process.env.BABEL_ENV = OLD_BABEL_ENV;
}
}
};
const babelTransformer = {
transform,
};
module.exports = babelTransformer;
//# sourceMappingURL=babel-transformer.js.map

View File

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

View File

@@ -0,0 +1,10 @@
import type { SymbolicatorConfigT } from '@expo/metro/metro-config';
type CustomizeFrameFunc = SymbolicatorConfigT['customizeFrame'];
export declare const INTERNAL_CALLSITES_REGEX: RegExp;
/**
* The default frame processor. This is used to modify the stack traces.
* This method attempts to collapse all frames that aren't relevant to
* the user by default.
*/
export declare function getDefaultCustomizeFrame(): CustomizeFrameFunc;
export {};

133
node_modules/@expo/metro-config/build/customizeFrame.js generated vendored Normal file
View File

@@ -0,0 +1,133 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.INTERNAL_CALLSITES_REGEX = void 0;
exports.getDefaultCustomizeFrame = getDefaultCustomizeFrame;
const url_1 = require("url");
// Import only the types here, the values will be imported from the project, at runtime.
exports.INTERNAL_CALLSITES_REGEX = new RegExp([
'/Libraries/Renderer/implementations/.+\\.js$',
'/Libraries/BatchedBridge/MessageQueue\\.js$',
'/Libraries/YellowBox/.+\\.js$',
'/Libraries/LogBox/.+\\.js$',
'/Libraries/Core/Timers/.+\\.js$',
'node_modules/react-devtools-core/.+\\.js$',
'node_modules/react-refresh/.+\\.js$',
'node_modules/scheduler/.+\\.js$',
// Metro replaces `require()` with a different method,
// we want to omit this method from the stack trace.
// This is akin to most React tooling.
'/metro/.*/polyfills/require.js$',
// Hide frames related to a fast refresh.
'/metro/.*/lib/bundle-modules/.+\\.js$',
'node_modules/react-native/Libraries/Utilities/HMRClient.js$',
'node_modules/eventemitter3/index.js',
'node_modules/event-target-shim/dist/.+\\.js$',
// Improve errors thrown by invariant (ex: `Invariant Violation: "main" has not been registered`).
'node_modules/invariant/.+\\.js$',
// Remove babel runtime additions
'node_modules/regenerator-runtime/.+\\.js$',
// Remove react native setImmediate ponyfill
'node_modules/promise/setimmediate/.+\\.js$',
// Babel helpers that implement language features
'node_modules/@babel/runtime/.+\\.js$',
// Hide Hermes internal bytecode
'/(?:InternalBytecode/)?InternalBytecode\\.js$',
// Block native code invocations
`\\[native code\\]`,
// Hide react-dom (web)
'node_modules/react-dom/.+\\.js$',
// Hide node.js evaluation code
'node_modules/require-from-string/.+\\.js$',
// Block expo's metro-runtime
'@expo/metro-runtime/.+\\.ts',
'@expo/server/.+\\.ts',
'expo-server/.+\\.ts',
// Block upstream metro-runtime
'/metro-runtime/.+\\.js$',
// Expo's metro-runtime require patch:
'@expo/metro-config/require/.+',
// Block all whatwg polyfills
'node_modules/whatwg-.+\\.js$',
// Hide expo-router warnings which are often wrapping all routes and imports.
'node_modules/expo-router/build/',
// No Expo CLI logs
'/@expo/cli/.+',
// No context modules as these are virtual
'.+?ctx=[a-zA-Z0-9]+$',
// Hide react-native-web warning wrappers. These are most likely related to style deprecations.
'/react-native-web/dist/.+\\.js$',
// React Server Components adapter (note we should probably use an Expo-Metro-specific version in the future).
'node_modules/react-server-dom-webpack/.+\\.js$',
// Block all node modules.
'node_modules/.+/',
].join('|'));
function isUrl(value) {
try {
// eslint-disable-next-line no-new
new url_1.URL(value);
return true;
}
catch {
return false;
}
}
/**
* The default frame processor. This is used to modify the stack traces.
* This method attempts to collapse all frames that aren't relevant to
* the user by default.
*/
function getDefaultCustomizeFrame() {
return (frame) => {
if (frame.file && isUrl(frame.file)) {
return {
...frame,
// HACK: This prevents Metro from attempting to read the invalid file URL it sent us.
lineNumber: null,
column: null,
// This prevents the invalid frame from being shown by default.
collapse: true,
};
}
let collapse = Boolean(frame.file && exports.INTERNAL_CALLSITES_REGEX.test(frame.file));
if (!collapse) {
// This represents the first frame of the stacktrace.
// Often this looks like: `__r(0);`.
// The URL will also be unactionable in the app and therefore not very useful to the developer.
if (frame.column === 3 &&
frame.methodName &&
['global', 'global code'].includes(frame.methodName) &&
frame.file?.match(/^https?:\/\//g)) {
collapse = true;
}
else if ((frame.file === 'unknown' || frame.file === '<anonymous>') &&
(frame.column == null || frame.column === -1)) {
// If we definitively don't have a file, as indicated by the invalid column value,
// this frame won't be able to desymbolicate properly
collapse = true;
}
else if (frame.file === '<native>') {
collapse = true;
}
else if (
// Some internal component stacks often don't have a file name.
frame.file === '<anonymous>' &&
frame.methodName &&
[
// React
'Suspense',
// React Native
'RCTView',
'RCTScrollView',
'RCTScrollContentView',
// React Native Screens
'RNSScreen',
'RNSScreenContentWrapper',
'RNSScreenNavigationContainer',
].includes(frame.methodName)) {
collapse = true;
}
}
return { ...(frame || {}), collapse };
};
}
//# sourceMappingURL=customizeFrame.js.map

View File

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

14
node_modules/@expo/metro-config/build/env.d.ts generated vendored Normal file
View File

@@ -0,0 +1,14 @@
declare class Env {
/** Enable debug logging */
get EXPO_DEBUG(): boolean;
/** Enable the experimental "exotic" mode. [Learn more](https://blog.expo.dev/drastically-faster-bundling-in-react-native-a54f268e0ed1). */
get EXPO_USE_EXOTIC(): boolean;
/** The React Metro port that's baked into react-native scripts and tools. */
get RCT_METRO_PORT(): number;
/** Disable Environment Variable injection in client bundles. */
get EXPO_NO_CLIENT_ENV_VARS(): boolean;
/** Enable the use of Expo's custom metro require implementation. The custom require supports better debugging, tree shaking, and React Server Components. */
get EXPO_USE_METRO_REQUIRE(): boolean;
}
export declare const env: Env;
export {};

28
node_modules/@expo/metro-config/build/env.js generated vendored Normal file
View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.env = void 0;
const getenv_1 = require("getenv");
class Env {
/** Enable debug logging */
get EXPO_DEBUG() {
return (0, getenv_1.boolish)('EXPO_DEBUG', false);
}
/** Enable the experimental "exotic" mode. [Learn more](https://blog.expo.dev/drastically-faster-bundling-in-react-native-a54f268e0ed1). */
get EXPO_USE_EXOTIC() {
return (0, getenv_1.boolish)('EXPO_USE_EXOTIC', false);
}
/** The React Metro port that's baked into react-native scripts and tools. */
get RCT_METRO_PORT() {
return (0, getenv_1.int)('RCT_METRO_PORT', 8081);
}
/** Disable Environment Variable injection in client bundles. */
get EXPO_NO_CLIENT_ENV_VARS() {
return (0, getenv_1.boolish)('EXPO_NO_CLIENT_ENV_VARS', false);
}
/** Enable the use of Expo's custom metro require implementation. The custom require supports better debugging, tree shaking, and React Server Components. */
get EXPO_USE_METRO_REQUIRE() {
return (0, getenv_1.boolish)('EXPO_USE_METRO_REQUIRE', false);
}
}
exports.env = new Env();
//# sourceMappingURL=env.js.map

1
node_modules/@expo/metro-config/build/env.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":";;;AAAA,mCAAsC;AAEtC,MAAM,GAAG;IACP,2BAA2B;IAC3B,IAAI,UAAU;QACZ,OAAO,IAAA,gBAAO,EAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,2IAA2I;IAC3I,IAAI,eAAe;QACjB,OAAO,IAAA,gBAAO,EAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,6EAA6E;IAC7E,IAAI,cAAc;QAChB,OAAO,IAAA,YAAG,EAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,gEAAgE;IAChE,IAAI,uBAAuB;QACzB,OAAO,IAAA,gBAAO,EAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;IACnD,CAAC;IAED,6JAA6J;IAC7J,IAAI,sBAAsB;QACxB,OAAO,IAAA,gBAAO,EAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;CACF;AAEY,QAAA,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC"}

View File

@@ -0,0 +1,4 @@
import UpstreamFileStore from '@expo/metro/metro-cache/stores/FileStore';
export declare class FileStore<T> extends UpstreamFileStore<T> {
set(key: Buffer, value: any): Promise<void>;
}

20
node_modules/@expo/metro-config/build/file-store.js generated vendored Normal file
View File

@@ -0,0 +1,20 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileStore = void 0;
const FileStore_1 = __importDefault(require("@expo/metro/metro-cache/stores/FileStore"));
const debug = require('debug')('expo:metro:cache');
class FileStore extends FileStore_1.default {
async set(key, value) {
// Prevent caching of CSS files that have the skipCache flag set.
if (value?.output?.[0]?.data?.css?.skipCache) {
debug('Skipping caching for CSS file:', value.path);
return;
}
return await super.set(key, value);
}
}
exports.FileStore = FileStore;
//# sourceMappingURL=file-store.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"file-store.js","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":";;;;;;AAAA,yFAAyE;AAEzE,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,kBAAkB,CAAuB,CAAC;AAEzE,MAAa,SAAa,SAAQ,mBAAoB;IACpD,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAU;QAC/B,iEAAiE;QACjE,IAAI,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;YAC7C,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QACD,OAAO,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;CACF;AATD,8BASC"}

View File

@@ -0,0 +1 @@
export declare function getModulesPaths(projectRoot: string): string[];

View File

@@ -0,0 +1,21 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getModulesPaths = getModulesPaths;
const paths_1 = require("@expo/config/paths");
const path_1 = __importDefault(require("path"));
function getModulesPaths(projectRoot) {
const paths = [];
// Only add the project root if it's not the current working directory
// this minimizes the chance of Metro resolver breaking on new Node.js versions.
const resolvedProjectRoot = path_1.default.resolve(projectRoot);
const workspaceRoot = (0, paths_1.getMetroServerRoot)(resolvedProjectRoot);
if (workspaceRoot !== resolvedProjectRoot) {
paths.push(path_1.default.resolve(projectRoot, 'node_modules'));
paths.push(path_1.default.resolve(workspaceRoot, 'node_modules'));
}
return paths;
}
//# sourceMappingURL=getModulesPaths.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getModulesPaths.js","sourceRoot":"","sources":["../src/getModulesPaths.ts"],"names":[],"mappings":";;;;;AAGA,0CAaC;AAhBD,8CAAwD;AACxD,gDAAwB;AAExB,SAAgB,eAAe,CAAC,WAAmB;IACjD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,sEAAsE;IACtE,gFAAgF;IAChF,MAAM,mBAAmB,GAAG,cAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,aAAa,GAAG,IAAA,0BAAkB,EAAC,mBAAmB,CAAC,CAAC;IAC9D,IAAI,aAAa,KAAK,mBAAmB,EAAE,CAAC;QAC1C,KAAK,CAAC,IAAI,CAAC,cAAI,CAAC,OAAO,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,cAAI,CAAC,OAAO,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}

View File

@@ -0,0 +1,16 @@
/**
* @param workspaceProjectRoot Root file path for the yarn workspace
* @param linkedPackages List of folders that contain linked node modules, ex: `['packages/*', 'apps/*']`
* @returns List of valid package.json file paths, ex: `['/Users/me/app/apps/my-app/package.json', '/Users/me/app/packages/my-package/package.json']`
*/
export declare function globAllPackageJsonPaths(workspaceProjectRoot: string, linkedPackages: string[]): string[];
/**
* @param workspaceProjectRoot root file path for a yarn workspace.
* @returns list of package.json file paths that are linked to the yarn workspace.
*/
export declare function resolveAllWorkspacePackageJsonPaths(workspaceProjectRoot: string): string[];
/**
* @param projectRoot file path to app's project root
* @returns list of node module paths to watch in Metro bundler, ex: `['/Users/me/app/node_modules/', '/Users/me/app/apps/my-app/', '/Users/me/app/packages/my-package/']`
*/
export declare function getWatchFolders(projectRoot: string): string[];

View File

@@ -0,0 +1,90 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.globAllPackageJsonPaths = globAllPackageJsonPaths;
exports.resolveAllWorkspacePackageJsonPaths = resolveAllWorkspacePackageJsonPaths;
exports.getWatchFolders = getWatchFolders;
const paths_1 = require("@expo/config/paths");
const fs_1 = __importDefault(require("fs"));
const glob_1 = require("glob");
const path_1 = __importDefault(require("path"));
function readJsonFile(filePath) {
// Read with fs
const file = fs_1.default.readFileSync(filePath, 'utf8');
// Parse with JSON.parse
return JSON.parse(file);
}
function isValidJsonFile(filePath) {
try {
// Throws if invalid or unable to read.
readJsonFile(filePath);
return true;
}
catch {
return false;
}
}
/**
* @param workspaceProjectRoot Root file path for the yarn workspace
* @param linkedPackages List of folders that contain linked node modules, ex: `['packages/*', 'apps/*']`
* @returns List of valid package.json file paths, ex: `['/Users/me/app/apps/my-app/package.json', '/Users/me/app/packages/my-package/package.json']`
*/
function globAllPackageJsonPaths(workspaceProjectRoot, linkedPackages) {
return linkedPackages
.map((glob) => {
// Globs should only contain `/` as separator, even on Windows.
return (0, glob_1.globSync)(path_1.default.posix.join(glob, 'package.json').replace(/\\/g, '/'), {
cwd: workspaceProjectRoot,
absolute: true,
ignore: ['**/@(Carthage|Pods|node_modules)/**'],
}).map((pkgPath) => {
return isValidJsonFile(pkgPath) ? pkgPath : null;
});
})
.flat()
.filter(Boolean)
.map((p) => path_1.default.join(p));
}
/**
* @param workspaceProjectRoot root file path for a yarn workspace.
* @returns list of package.json file paths that are linked to the yarn workspace.
*/
function resolveAllWorkspacePackageJsonPaths(workspaceProjectRoot) {
try {
// Extract the "packages" array or use "workspaces" as packages array (yarn workspaces spec).
const workspaceGlobs = (0, paths_1.getMetroWorkspaceGlobs)(workspaceProjectRoot);
if (!workspaceGlobs?.length)
return [];
// Glob all package.json files and return valid paths.
return globAllPackageJsonPaths(workspaceProjectRoot, workspaceGlobs);
}
catch {
return [];
}
}
/**
* @param projectRoot file path to app's project root
* @returns list of node module paths to watch in Metro bundler, ex: `['/Users/me/app/node_modules/', '/Users/me/app/apps/my-app/', '/Users/me/app/packages/my-package/']`
*/
function getWatchFolders(projectRoot) {
const resolvedProjectRoot = path_1.default.resolve(projectRoot);
const workspaceRoot = (0, paths_1.getMetroServerRoot)(resolvedProjectRoot);
// Rely on default behavior in standard projects.
if (workspaceRoot === resolvedProjectRoot) {
return [];
}
const packages = resolveAllWorkspacePackageJsonPaths(workspaceRoot);
if (!packages?.length) {
return [];
}
return uniqueItems([
path_1.default.join(workspaceRoot, 'node_modules'),
...packages.map((pkg) => path_1.default.dirname(pkg)),
]);
}
function uniqueItems(items) {
return [...new Set(items)];
}
//# sourceMappingURL=getWatchFolders.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getWatchFolders.js","sourceRoot":"","sources":["../src/getWatchFolders.ts"],"names":[],"mappings":";;;;;AA2BA,0DAkBC;AAMD,kFAUC;AAMD,0CAiBC;AApFD,8CAAgF;AAChF,4CAAoB;AACpB,+BAAgC;AAChC,gDAAwB;AAExB,SAAS,YAAY,CAAC,QAAgB;IACpC,eAAe;IACf,MAAM,IAAI,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/C,wBAAwB;IACxB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,eAAe,CAAC,QAAgB;IACvC,IAAI,CAAC;QACH,uCAAuC;QACvC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAgB,uBAAuB,CACrC,oBAA4B,EAC5B,cAAwB;IAExB,OAAO,cAAc;SAClB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,+DAA+D;QAC/D,OAAO,IAAA,eAAQ,EAAC,cAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE;YACzE,GAAG,EAAE,oBAAoB;YACzB,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,CAAC,qCAAqC,CAAC;SAChD,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YACjB,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;SACD,IAAI,EAAE;SACN,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAI,CAAC,IAAI,CAAC,CAAW,CAAC,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,SAAgB,mCAAmC,CAAC,oBAA4B;IAC9E,IAAI,CAAC;QACH,6FAA6F;QAC7F,MAAM,cAAc,GAAG,IAAA,8BAAsB,EAAC,oBAAoB,CAAC,CAAC;QACpE,IAAI,CAAC,cAAc,EAAE,MAAM;YAAE,OAAO,EAAE,CAAC;QACvC,sDAAsD;QACtD,OAAO,uBAAuB,CAAC,oBAAoB,EAAE,cAAc,CAAC,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,eAAe,CAAC,WAAmB;IACjD,MAAM,mBAAmB,GAAG,cAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,aAAa,GAAG,IAAA,0BAAkB,EAAC,mBAAmB,CAAC,CAAC;IAC9D,iDAAiD;IACjD,IAAI,aAAa,KAAK,mBAAmB,EAAE,CAAC;QAC1C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAG,mCAAmC,CAAC,aAAa,CAAC,CAAC;IACpE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,WAAW,CAAC;QACjB,cAAI,CAAC,IAAI,CAAC,aAAa,EAAE,cAAc,CAAC;QACxC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,cAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;KAC5C,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,KAAe;IAClC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7B,CAAC"}

View File

@@ -0,0 +1,9 @@
import type { TransformOptions } from './babel-core';
/**
* Returns a memoized function that checks for the existence of a
* project-level .babelrc file. If it doesn't exist, it reads the
* default React Native babelrc file and uses that.
*/
export declare const loadBabelConfig: ({ projectRoot }: {
projectRoot: string;
}) => Pick<TransformOptions, "extends" | "presets">;

View File

@@ -0,0 +1,52 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadBabelConfig = void 0;
/**
* Copyright (c) 650 Industries (Expo). All rights reserved.
* 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.
*/
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
/**
* Returns a memoized function that checks for the existence of a
* project-level .babelrc file. If it doesn't exist, it reads the
* default React Native babelrc file and uses that.
*/
exports.loadBabelConfig = (function () {
let babelRC = null;
return function _getBabelRC({ projectRoot }) {
if (babelRC !== null) {
return babelRC;
}
babelRC = {};
if (projectRoot) {
// Check for various babel config files in the project root
// TODO(EvanBacon): We might want to disable babelrc lookup when the user specifies `enableBabelRCLookup: false`.
const possibleBabelRCPaths = ['.babelrc', '.babelrc.js', 'babel.config.js'];
const foundBabelRCPath = possibleBabelRCPaths.find((configFileName) => node_fs_1.default.existsSync(node_path_1.default.resolve(projectRoot, configFileName)));
// Extend the config if a babel config file is found
if (foundBabelRCPath) {
babelRC.extends = node_path_1.default.resolve(projectRoot, foundBabelRCPath);
}
}
// Use the default preset for react-native if no babel config file is found
if (!babelRC.extends) {
try {
babelRC.presets = [require('expo/internal/babel-preset')];
}
catch {
// TODO(@kitten): Temporary, since our E2E tests don't use monorepo
// packages consistently, including the `expo` package
babelRC.presets = [require('babel-preset-expo')];
}
}
return babelRC;
};
})();
//# sourceMappingURL=loadBabelConfig.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"loadBabelConfig.js","sourceRoot":"","sources":["../src/loadBabelConfig.ts"],"names":[],"mappings":";;;;;;AAAA;;;;;;GAMG;AACH,sDAAyB;AACzB,0DAA6B;AAI7B;;;;GAIG;AACU,QAAA,eAAe,GAAG,CAAC;IAC9B,IAAI,OAAO,GAAyD,IAAI,CAAC;IAEzE,OAAO,SAAS,WAAW,CAAC,EAAE,WAAW,EAA2B;QAClE,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,OAAO,GAAG,EAAE,CAAC;QAEb,IAAI,WAAW,EAAE,CAAC;YAChB,2DAA2D;YAC3D,iHAAiH;YACjH,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,aAAa,EAAE,iBAAiB,CAAC,CAAC;YAE5E,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,EAAE,CACpE,iBAAE,CAAC,UAAU,CAAC,mBAAI,CAAC,OAAO,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CACzD,CAAC;YAEF,oDAAoD;YACpD,IAAI,gBAAgB,EAAE,CAAC;gBACrB,OAAO,CAAC,OAAO,GAAG,mBAAI,CAAC,OAAO,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;QAED,2EAA2E;QAC3E,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACrB,IAAI,CAAC;gBACH,OAAO,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC,CAAC;YAC5D,CAAC;YAAC,MAAM,CAAC;gBACP,mEAAmE;gBACnE,sDAAsD;gBACtD,OAAO,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC,CAAC,EAAE,CAAC"}

View File

@@ -0,0 +1,2 @@
export declare function getRouterDirectory(projectRoot: string): string;
export declare function getRewriteRequestUrl(projectRoot: string): (url: string) => string;

View File

@@ -0,0 +1,121 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRouterDirectory = getRouterDirectory;
exports.getRewriteRequestUrl = getRewriteRequestUrl;
// Copyright 2023-present 650 Industries (Expo). All rights reserved.
const config_1 = require("@expo/config");
const paths_1 = require("@expo/config/paths");
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const debug = require('debug')('expo:metro:config:rewriteRequestUrl');
function directoryExistsSync(file) {
try {
return fs_1.default.statSync(file)?.isDirectory() ?? false;
}
catch {
return false;
}
}
function isEnableHermesManaged(expoConfig, platform) {
switch (platform) {
case 'android': {
return (expoConfig.android?.jsEngine ?? expoConfig.jsEngine) !== 'jsc';
}
case 'ios': {
return (expoConfig.ios?.jsEngine ?? expoConfig.jsEngine) !== 'jsc';
}
default:
return false;
}
}
function getAsyncRoutesFromExpoConfig(exp, mode, platform) {
let asyncRoutesSetting;
if (exp.extra?.router?.asyncRoutes) {
const asyncRoutes = exp.extra?.router?.asyncRoutes;
if (['boolean', 'string'].includes(typeof asyncRoutes)) {
asyncRoutesSetting = asyncRoutes;
}
else if (typeof asyncRoutes === 'object') {
asyncRoutesSetting = asyncRoutes[platform] ?? asyncRoutes.default;
}
}
return [mode, true].includes(asyncRoutesSetting);
}
function getRouterDirectoryModuleIdWithManifest(projectRoot, exp) {
return exp.extra?.router?.root ?? getRouterDirectory(projectRoot);
}
function getRouterDirectory(projectRoot) {
// more specific directories first
if (directoryExistsSync(path_1.default.join(projectRoot, 'src', 'app'))) {
debug('Using src/app as the root directory for Expo Router.');
return path_1.default.join('src', 'app');
}
debug('Using app as the root directory for Expo Router.');
return 'app';
}
function getRewriteRequestUrl(projectRoot) {
function rewriteExpoRequestUrl(url) {
// Like: `/.expo/.virtual-metro-entry.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.bacon.test-custom-entry`
// Sometimes a fully qualified URL is passed in, e.g. `http://localhost:19001/.expo/.virtual-metro-entry.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.bacon.test-custom-entry`
if (url.includes('/.expo/.virtual-metro-entry.bundle?')) {
const { pkg, exp } = (0, config_1.getConfig)(projectRoot, { skipSDKVersionRequirement: true });
const ensured = url.startsWith('/') ? new URL(url, 'https://acme.dev') : new URL(url);
// TODO: Maybe this function could be memoized in some capacity?
const { searchParams } = ensured;
const isDev = searchParams.has('dev') ? searchParams.get('dev') === 'true' : true;
const platform = searchParams.get('platform') ?? 'web';
debug('Rewriting magic request url to entry point', { url, platform });
const entry = (0, paths_1.resolveEntryPoint)(projectRoot, {
platform,
pkg,
});
if (!entry) {
throw new Error((0, chalk_1.default) `The project entry file could not be resolved (platform: ${platform}, root: ${projectRoot}). Define it in the {bold package.json} "main" field.`);
}
// Infer the missing transform properties to attempt to match the manifest request.
// NOTE: Keep in sync with metroOptions.ts
if (!ensured.searchParams.has('transform.routerRoot')) {
ensured.searchParams.set('transform.routerRoot', getRouterDirectoryModuleIdWithManifest(projectRoot, exp));
}
if (!ensured.searchParams.has('transform.reactCompiler') && exp.experiments?.reactCompiler) {
ensured.searchParams.set('transform.reactCompiler', String(!!exp.experiments?.reactCompiler));
}
if (!ensured.searchParams.has('transform.asyncRoutes')) {
const asyncRoutes = getAsyncRoutesFromExpoConfig(exp, isDev ? 'development' : 'production', platform);
if (asyncRoutes) {
ensured.searchParams.set('transform.asyncRoutes', String(asyncRoutes));
}
}
if (!ensured.searchParams.has('transform.engine')) {
const isHermesEnabled = isEnableHermesManaged(exp, platform);
if (isHermesEnabled) {
debug('Enabling Hermes for managed project');
ensured.searchParams.set('transform.engine', 'hermes');
ensured.searchParams.set('transform.bytecode', '1');
ensured.searchParams.set('unstable_transformProfile', 'hermes-stable');
}
}
const serverRoot = (0, paths_1.getMetroServerRoot)(projectRoot);
const relativeEntry = path_1.default.relative(serverRoot, entry).replace(/\.[tj]sx?$/, '');
debug('Resolved entry point', { entry, relativeEntry, serverRoot });
// Only return the pathname when url is relative
if (url.startsWith('/')) {
// Like: `/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.bacon.test-custom-entry`
return '/' + relativeEntry + '.bundle?' + searchParams.toString();
}
// Modify the pathname within the URL and return the full URL
ensured.pathname = '/' + relativeEntry + '.bundle';
const outputUrl = ensured.toString();
debug('Redirected:', outputUrl);
// Like: `http://localhost:19001/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.bacon.test-custom-entry`
return outputUrl;
}
return url;
}
return rewriteExpoRequestUrl;
}
//# sourceMappingURL=rewriteRequestUrl.js.map

View File

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

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":";;;;;AA4CA,gDA6FC;AAeD,oDAKC;AAED,kCAEC;AAjKD,4GAA4G;AAC5G,oFAAsF;AAEtF,qFAAkF;AAClF,gDAAwB;AAIxB,iDAA6D;AAC7D,gDAAgD;AAChD,wCAA2C;AAU3C,aAAa;AACb,MAAM,uBAAuB,GAAG,kBAAkB,CAAC;AAmBnD,SAAS,cAAc,CAAC,MAAmB;IACzC,OAAO,IAAA,kBAAU,EAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,SAAgB,kBAAkB,CAChC,YAAqC,EACrC,EAAE,WAAW,EAAE,SAAS,EAAwD;IAEhF,MAAM,MAAM,GAAkB,EAAE,CAAC;IAEjC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,SAAS,aAAa,CAAC,MAAgB;QACrC,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC;YAElC,qFAAqF;YACrF,8EAA8E;YAC9E,MAAM,cAAc,GAAG,IAAA,sBAAW,EAAC,cAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5E,MAAM,QAAQ,GAAG,IAAA,sBAAW,EAC1B,cAAI,CAAC,IAAI;YACP,sBAAsB;YACtB,uBAAuB;YACvB,0CAA0C;YAC1C,oBAAoB,CAAC;gBACnB,qCAAqC;gBACrC,QAAQ,EAAE,cAAc;gBACxB,GAAG,EAAE,QAAQ;aACd,CAAC,GAAG,MAAM,CACZ,CACF,CAAC;YAEF,IAAI,WAAW,CAAC,eAAe,EAAE,CAAC;gBAChC,KAAK,MAAM,QAAQ,IAAI,WAAW,CAAC,eAAe,EAAE,CAAC;oBACnD,IAAI,MAAM,GAAG,gCAAgC,QAAQ,CAAC,GAAG,GAAG,CAAC;oBAE7D,kDAAkD;oBAClD,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;wBACnB,MAAM,IAAI,UAAU,QAAQ,CAAC,KAAK,GAAG,CAAC;oBACxC,CAAC;oBAED,2BAA2B;oBAE3B,MAAM,IAAI,GAAG,CAAC;oBAEd,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,cAAc;wBACpB,cAAc;wBACd,QAAQ,EAAE,QAAQ,CAAC,GAAG;wBACtB,gBAAgB;wBAChB,MAAM;wBACN,QAAQ,EAAE;4BACR,KAAK,EAAE,IAAA,wBAAkB,EAAC,cAAc,CAAC;yBAC1C;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,KAAK;gBACX,cAAc;gBACd,QAAQ;gBACR,MAAM,EAAE,QAAQ;gBAChB,QAAQ,EAAE;oBACR,KAAK,EAAE,IAAA,wBAAkB,EAAC,cAAc,CAAC;iBAC1C;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,SAAS,QAAQ,CAAC,YAAoB;QACpC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QAED,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YAChC,2EAA2E;YAC3E,IAAI,IAAA,2CAAoB,EAAC,GAAG,CAAC,EAAE,CAAC;gBAC9B,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,uCAAuC;QACvC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,aAAa,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,SAAS,CAAC,CAAC;IAEpB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,cAAc,CAAC,MAAmB;IACzC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IACpC,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QACtD,IAAI,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAK,IAAY,CAAC,GAAG,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,KAAK,CACb,4CAA4C,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CACxF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,GAAkB,CAAC;IACjC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAqC;IACvF,8JAA8J;IAC9J,qGAAqG;IACrG,MAAM,OAAO,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACjE,OAAO,WAAW,CAAC,OAAO,CAAC,GAAG,GAAG,GAAG,IAAA,iBAAU,EAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,SAAgB,WAAW,CAAC,MAAc;IACxC,OAAO,cAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AACvD,CAAC"}

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":";;;;;AA4BA,kEAsCC;AAmCD,4DA8BC;AAmCD,0CAEC;AA5JD,qFAAkF;AAClF,4CAAoB;AACpB,gDAAwB;AACxB,0DAAkC;AAElC,mEAAgE;AAChE,gDAAgD;AAEhD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,mBAAmB,CAAuB,CAAC;AAQ1E,SAAgB,2BAA2B,CACzC,OAA0B,EAC1B,KAAoB,EACpB,KAAqB,EACrB,cAAwB,CAAC,KAAK,CAAC,IAAI,CAAC,EACpC,UAAuB,IAAI,GAAG,EAAE;IAEhC,MAAM,0BAA0B,GAAG,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxE,IAAI,0BAA0B,EAAE,CAAC;QAC/B,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC7B,CAAC;IACD,kEAAkE;IAClE,KAAK,MAAM,YAAY,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC,IAAA,2CAAoB,EAAC,YAAY,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,CAAC;YAClF,SAAS;QACX,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,YAAY,CAAE,CAAC;QAC/D,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,SAAS;QACX,CAAC;QAED,MAAM,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,2BAA2B,CACxD,OAAO,EACP,KAAK,EACL,GAAG,EACH,CAAC,GAAG,WAAW,EAAE,YAAY,CAAC,YAAY,CAAC,EAC3C,OAAO,CACR,CAAC;QAEF,IAAI,aAAa,EAAE,CAAC;YAClB,2CAA2C;YAC3C,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;YAEzB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,YAAY,GAAG,IAAI,GAAG,EAAe,CAAC;AAE5C,MAAM,qBAAqB,GAAG,CAC5B,OAEC,EACD,GAAW,EAC8B,EAAE;IAC3C,IAAI,WAAgB,CAAC;IACrB,IAAI,eAAe,GAAkB,IAAI,CAAC;IAC1C,IAAI,OAAO,OAAO,CAAC,oBAAoB,KAAK,UAAU,EAAE,CAAC;QACvD,CAAC,WAAW,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACrE,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,eAAe,GAAG,IAAA,6CAAqB,EAAC,GAAG,CAAC,CAAC;QAC7C,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iCAAiC;IACjC,MAAM,OAAO,GAAG,cAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9C,MAAM,YAAY,GAAG,wBAAwB,CAAC,OAAO,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC;IACrF,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACpC,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAEF,SAAgB,wBAAwB,CACtC,OAAe,EACf,WAAiD,EACjD,kBAA0B,EAAE;IAE5B,IAAI,iBAA0D,CAAC;IAC/D,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3C,MAAM,WAAW,GAAG,WAAW,CAAC,WAAW;aACxC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,OAAO,UAAU,KAAK,QAAQ,CAAC;aACtD,GAAG,CAAC,CAAC,UAAe,EAAE,EAAE;YACvB,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAChD,OAAO,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC;QAC3D,CAAC,CAAC,CAAC;QACL,iBAAiB,GAAG,IAAA,mBAAS,EAAC,WAAW,CAAC,CAAC;IAC7C,CAAC;SAAM,IAAI,OAAO,WAAW,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;QACpF,iBAAiB,GAAG,WAAW,CAAC,WAAW,CAAC;IAC9C,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,4CAA4C,EAAE,eAAe,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,CAAC,EAAU,EAAE,EAAE;QACpB,+EAA+E;QAC/E,IAAI,iBAAiB,IAAI,IAAI,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;aAAM,IAAI,OAAO,iBAAiB,KAAK,SAAS,EAAE,CAAC;YAClD,OAAO,iBAAiB,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,MAAM,YAAY,GAAG,cAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,cAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,cAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAC3F,OAAO,iBAAiB,CAAC,IAAA,sBAAW,EAAC,YAAY,CAAC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,OAA0B,EAAE,KAAqB;IAC7E,IAAI,KAAK,EAAE,WAAW,KAAK,SAAS,EAAE,CAAC;QACrC,OAAO,KAAK,CAAC,WAAW,CAAC;IAC3B,CAAC;IACD,MAAM,YAAY,GAAG,gCAAgC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACtE,KAAK,CAAC,WAAW,GAAG,YAAY,CAAC;IACjC,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,SAAS,gCAAgC,CACvC,OAA0B,EAC1B,KAAqB;IAErB,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO,KAAK,CAAC,WAAW,CAAC;IAC3B,CAAC;IACD,2CAA2C;IAC3C,IAAI,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,WAAW,CAAC,EAAE,CAAC;QAC/D,MAAM,YAAY,GAAG,qBAAqB,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAEhE,IAAI,YAAY,IAAI,IAAI,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC"}

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

View File

@@ -0,0 +1,18 @@
import type { types, template } from '@babel/core';
export declare const defaultWrapHelper: ({ statement }: typeof template, name: string) => types.Statement;
export declare const namespaceWrapHelper: ({ statement }: typeof template, name: string) => types.Statement;
export declare const liveExportAllHelper: ({ statement }: typeof template, id: string) => types.Statement;
export declare const liveExportHelper: (t: typeof types, exportName: string, expr: types.Expression) => types.Statement;
export declare const assignExportHelper: (t: typeof types, exportName: string, expr: types.Expression) => types.Statement;
export declare const varDeclaratorHelper: (t: typeof types, name: string, expr: types.Expression) => types.Statement;
/** `var %id% = require("%source%");` */
export declare const requireCall: (t: typeof types, id: string, source: types.StringLiteral) => types.VariableDeclaration;
/** `require("%source%");` */
export declare const sideEffectRequireCall: (t: typeof types, source: types.StringLiteral) => types.Statement;
/** `var %id% = %fnName%(%source%);` */
export declare const varDeclaratorCallHelper: (t: typeof types, id: string, fn: string, arg: string) => types.Statement;
export declare const esModuleExportTemplate: ({ statement }: typeof template) => types.Statement;
export declare const nullBoundExpression: (t: typeof types, expr: types.Expression) => types.ParenthesizedExpression;
declare function withLocation<TNode extends types.Node>(node: TNode, loc: types.SourceLocation | null | undefined): TNode;
declare function withLocation<TNode extends types.Node>(nodeArray: readonly TNode[], loc: types.SourceLocation | null | undefined): TNode[];
export { withLocation };

View File

@@ -0,0 +1,101 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.nullBoundExpression = exports.esModuleExportTemplate = exports.varDeclaratorCallHelper = exports.sideEffectRequireCall = exports.requireCall = exports.varDeclaratorHelper = exports.assignExportHelper = exports.liveExportHelper = exports.liveExportAllHelper = exports.namespaceWrapHelper = exports.defaultWrapHelper = void 0;
exports.withLocation = withLocation;
const defaultWrapHelper = ({ statement }, name) => statement(`
function %%name%%(e) {
return e && e.__esModule ? e : { default: e };
}
`)({ name });
exports.defaultWrapHelper = defaultWrapHelper;
const namespaceWrapHelper = ({ statement }, name) => {
// NOTE(@kitten): A looser option than the above that matches Metro's legacy behaviour
return statement(`
function %%name%%(e) {
if (e && e.__esModule) return e;
var n = {};
if (e) Object.keys(e).forEach(function (k) {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
});
n.default = e;
return n;
}
`)({ name });
};
exports.namespaceWrapHelper = namespaceWrapHelper;
const liveExportAllHelper = ({ statement }, id) => {
return statement(`
Object.keys(%%id%%).forEach(function (k) {
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) {
Object.defineProperty(exports, k, {
enumerable: true,
get: function () { return %%id%%[k]; }
});
}
});
`)({ id });
};
exports.liveExportAllHelper = liveExportAllHelper;
const liveExportHelper = (t, exportName, expr) => {
return t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('defineProperty')), [
t.identifier('exports'),
t.stringLiteral(exportName),
t.objectExpression([
t.objectProperty(t.identifier('enumerable'), t.booleanLiteral(true)),
t.objectProperty(t.identifier('get'), t.functionExpression(null, [], t.blockStatement([t.returnStatement(expr)]))),
]),
]));
};
exports.liveExportHelper = liveExportHelper;
const assignExportHelper = (t, exportName, expr) => {
if (exportName === '__proto__') {
// NOTE(@kitten): `exports` is instantiated as `{}` instead of `Object.create(null)`, so the __proto__
// assignment still carries its special meaning. We switch to the live export helper implicitly here
// to avoid this
return (0, exports.liveExportHelper)(t, exportName, expr);
}
const member = t.isValidIdentifier(exportName)
? t.identifier(exportName)
: t.stringLiteral(exportName);
return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('exports'), member), expr));
};
exports.assignExportHelper = assignExportHelper;
const varDeclaratorHelper = (t, name, expr) => t.variableDeclaration('var', [t.variableDeclarator(t.identifier(name), expr)]);
exports.varDeclaratorHelper = varDeclaratorHelper;
/** `var %id% = require("%source%");` */
const requireCall = (t, id, source) => t.variableDeclaration('var', [
t.variableDeclarator(t.identifier(id), t.callExpression(t.identifier('require'), [source])),
]);
exports.requireCall = requireCall;
/** `require("%source%");` */
const sideEffectRequireCall = (t, source) => t.expressionStatement(t.callExpression(t.identifier('require'), [source]));
exports.sideEffectRequireCall = sideEffectRequireCall;
/** `var %id% = %fnName%(%source%);` */
const varDeclaratorCallHelper = (t, id, fn, arg) => t.variableDeclaration('var', [
t.variableDeclarator(t.identifier(id), t.callExpression(t.identifier(fn), [t.identifier(arg)])),
]);
exports.varDeclaratorCallHelper = varDeclaratorCallHelper;
// Needs to be kept in 1:1 compatibility with Babel.
const esModuleExportTemplate = ({ statement }) => {
return statement(`
Object.defineProperty(exports, '__esModule', {value: true});
`)();
};
exports.esModuleExportTemplate = esModuleExportTemplate;
const nullBoundExpression = (t, expr) => t.parenthesizedExpression(t.sequenceExpression([t.numericLiteral(0), expr]));
exports.nullBoundExpression = nullBoundExpression;
function withLocation(nodeOrArray, loc) {
if (Array.isArray(nodeOrArray)) {
return nodeOrArray.map((n) => withLocation(n, loc));
}
const node = nodeOrArray;
if (!node.loc) {
node.loc = loc;
}
return node;
}
//# sourceMappingURL=helpers.js.map

View File

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

View File

@@ -0,0 +1,59 @@
import type { ConfigAPI, PluginObj, types as t } from '@babel/core';
export interface Options {
readonly performConstantFolding?: boolean;
readonly resolve: boolean;
readonly out?: {
isESModule: boolean;
};
}
type ModuleRequest = t.StringLiteral;
type ModuleSource = string;
type ID = string;
declare const enum ImportDeclarationKind {
REQUIRE = "REQUIRE",
IMPORT_DEFAULT = "DEFAULT",
IMPORT_NAMESPACE = "NAMESPACE"
}
/** Record of specifiers (arbitrary name, default, or namespace import, to local IDs) */
interface ModuleSpecifiers {
[ImportDeclarationKind.REQUIRE]?: ID;
[ImportDeclarationKind.IMPORT_DEFAULT]?: ID;
[ImportDeclarationKind.IMPORT_NAMESPACE]?: ID;
/** Marks that the require call should be kept due to a side-effect */
sideEffect?: boolean;
}
/** Instruction for how to replace an expression when inlining */
interface InlineRef {
/** ID to access (MemberExpression.object) */
parentId: ID;
/** Specifier property to access (undefined for direct namespace access) */
member: 'default' | (string & {}) | undefined;
}
interface ImportDeclaration {
kind: ImportDeclarationKind;
source: ModuleRequest;
local: ID | undefined;
loc: t.SourceLocation | null | undefined;
}
interface ExportDeclaration {
statement: t.Statement;
local: ID | undefined;
}
interface State {
readonly opts: Options;
importSpecifiers: Map<ModuleSource, ModuleSpecifiers>;
/** Identifiers referencing import specifiers that should be rewritten (ID/key will be replaced) */
inlineBodyRefs: Map<ID, InlineRef>;
/** Identifiers by name that are referenced in the output */
referencedLocals: Set<ID>;
/** Cached untransformed export statements */
exportStatements: t.ExportNamedDeclaration[];
/** Transformed exports to add to the body, while referencing ID */
exportDeclarations: ExportDeclaration[];
/** Transformed export namespaces to add to the body, while referencing ID */
exportAll: Map<ID, t.Statement>;
/** Transformed imports to add to the body, if IDs referenced */
importDeclarations: ImportDeclaration[];
}
export declare function importExportLiveBindingsPlugin({ template, types: t, }: ConfigAPI & typeof import('@babel/core')): PluginObj<State>;
export {};

View File

@@ -0,0 +1,429 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.importExportLiveBindingsPlugin = importExportLiveBindingsPlugin;
const helpers_1 = require("./helpers");
function importExportLiveBindingsPlugin({ template, types: t, }) {
const addModuleSpecifiers = (state, source) => {
let moduleSpecifiers = state.importSpecifiers.get(source.value);
if (!moduleSpecifiers) {
moduleSpecifiers = Object.create(null);
state.importSpecifiers.set(source.value, moduleSpecifiers);
}
return moduleSpecifiers;
};
const addImport = (path, state, source, sideEffect = false) => {
const moduleSpecifiers = addModuleSpecifiers(state, source);
if (sideEffect || !state.opts.performConstantFolding) {
moduleSpecifiers.sideEffect = true;
}
let id = moduleSpecifiers["REQUIRE" /* ImportDeclarationKind.REQUIRE */];
if (!id) {
id = path.scope.generateUid(source.value);
moduleSpecifiers["REQUIRE" /* ImportDeclarationKind.REQUIRE */] = id;
state.importDeclarations.push({
kind: "REQUIRE" /* ImportDeclarationKind.REQUIRE */,
local: undefined,
source,
loc: path.node.loc,
});
}
return id;
};
const addDefaultImport = (path, state, source, name) => {
const moduleSpecifiers = addModuleSpecifiers(state, source);
let id = moduleSpecifiers["DEFAULT" /* ImportDeclarationKind.IMPORT_DEFAULT */];
if (!id) {
// Use the given name, if possible, or generate one. If no initial name is given,
// we'll create one based on the parent import
const parentImportLocal = addImport(path, state, source);
id =
!name || !t.isValidIdentifier(name)
? path.scope.generateUid(name ?? parentImportLocal)
: name;
moduleSpecifiers["DEFAULT" /* ImportDeclarationKind.IMPORT_DEFAULT */] = id;
state.importDeclarations.push({
kind: "DEFAULT" /* ImportDeclarationKind.IMPORT_DEFAULT */,
local: parentImportLocal,
source,
loc: path.node.loc,
});
}
return id;
};
const addNamespaceImport = (path, state, source, name) => {
const moduleSpecifiers = addModuleSpecifiers(state, source);
let id = moduleSpecifiers["NAMESPACE" /* ImportDeclarationKind.IMPORT_NAMESPACE */];
if (!id) {
// Use the given name, if possible, or generate one. If no initial name is given,
// we'll create one based on the parent import
const parentImportLocal = addImport(path, state, source);
id =
!name || !t.isValidIdentifier(name)
? path.scope.generateUid(name ?? parentImportLocal)
: name;
moduleSpecifiers["NAMESPACE" /* ImportDeclarationKind.IMPORT_NAMESPACE */] = id;
state.importDeclarations.push({
kind: "NAMESPACE" /* ImportDeclarationKind.IMPORT_NAMESPACE */,
local: parentImportLocal,
source,
loc: path.node.loc,
});
}
return id;
};
return {
visitor: {
// (1): Scan imports and prepare require calls
ImportDeclaration(path, state) {
if (path.node.importKind && path.node.importKind !== 'value') {
path.remove();
return;
}
const source = path.node.source;
if (!path.node.specifiers.length) {
addImport(path, state, source, true);
path.remove();
return;
}
for (const specifier of path.node.specifiers) {
const localId = specifier.local.name;
let importId;
let member;
switch (specifier.type) {
case 'ImportNamespaceSpecifier':
// The `namespaceWrapHelper` ensures a namespace object, but namespaces are accessed directly
member = undefined;
importId = addNamespaceImport(path, state, source, localId);
break;
case 'ImportSpecifier':
if (specifier.importKind && specifier.importKind !== 'value') {
continue;
}
member = t.isIdentifier(specifier.imported)
? specifier.imported.name
: specifier.imported.value;
// An imported default specifier is the same as an ImportDefaultSpecifier
importId =
member === 'default'
? addDefaultImport(path, state, source, localId)
: addImport(path, state, source);
break;
case 'ImportDefaultSpecifier':
// The `defaultWrapHelper` works by wrapping CommonJS modules in a fake module wrapper
member = 'default';
importId = addDefaultImport(path, state, source, localId);
break;
}
state.inlineBodyRefs.set(localId, {
parentId: importId,
member,
});
}
path.remove();
},
// (2.1): Declare live exports for ExportAllDeclarations immediately (References the import)
ExportAllDeclaration(path, state) {
if (path.node.exportKind && path.node.exportKind !== 'value') {
path.remove();
return;
}
const loc = path.node.loc;
const source = path.node.source;
const importId = addImport(path, state, source);
if (!state.exportAll.has(importId)) {
state.referencedLocals.add(importId);
state.exportAll.set(importId, (0, helpers_1.withLocation)((0, helpers_1.liveExportAllHelper)(template, importId), loc));
}
path.remove();
},
// (2.2): Store ExportDefaultDeclaration for later, for processing after all imports are evaluated
ExportDefaultDeclaration(path, state) {
if (path.node.exportKind && path.node.exportKind !== 'value') {
path.remove();
return;
}
let localId;
// We purposefully don't check for `Identifier` or `MemberExpression` here
// `export default` values are assigne at the point they're declared. We don't want them to be mutated
if (t.isDeclaration(path.node.declaration)) {
if (!path.node.declaration.id) {
path.node.declaration.id = path.scope.generateUidIdentifierBasedOnNode(path.node.declaration);
}
localId = path.node.declaration.id.name;
path.replaceWith(path.node.declaration);
}
else {
localId = path.scope.generateUid('_default');
path.replaceWith((0, helpers_1.withLocation)((0, helpers_1.varDeclaratorHelper)(t, localId, path.node.declaration), path.node.loc));
}
state.exportDeclarations.push({
statement: (0, helpers_1.withLocation)((0, helpers_1.liveExportHelper)(t, 'default', t.identifier(localId)), path.node.loc),
local: undefined,
});
},
// (2.3): Store ExportNamedDeclaration for later (if it has a local declaration), for processing after all imports are evaluated
// - If we have a source, create live bindings immediately for specifiers (References the import)
ExportNamedDeclaration(path, state) {
if (path.node.exportKind && path.node.exportKind !== 'value') {
path.remove();
return;
}
else if (path.node.declaration || !path.node.source) {
state.exportStatements.push(path.node);
if (path.node.declaration) {
// If we have a declaration, we'll replace the export with it
// In (3.1), we can then refer to the declarations by their local ids
path.replaceWith(path.node.declaration);
path.skip();
if (!path.node.source) {
return;
}
}
else if (!path.node.source) {
path.remove();
return;
}
}
const source = path.node.source;
if (!path.node.specifiers.length) {
addImport(path, state, source, true);
path.remove();
return;
}
for (const specifier of path.node.specifiers) {
let importId;
let specifierLocal;
let exportExpression;
switch (specifier.type) {
case 'ExportNamespaceSpecifier':
// The `namespaceWrapHelper` ensures a namespace object, but namespaces are accessed directly
specifierLocal = undefined;
importId = addNamespaceImport(path, state, source);
exportExpression = t.identifier(importId);
break;
case 'ExportSpecifier':
if (specifier.exportKind && specifier.exportKind !== 'value') {
continue;
}
specifierLocal = specifier.local.name;
// An imported default specifier is the same as an ImportDefaultSpecifier
importId =
specifierLocal === 'default'
? addDefaultImport(path, state, source)
: addImport(path, state, source);
exportExpression = t.memberExpression(t.identifier(importId), t.identifier(specifierLocal));
break;
case 'ExportDefaultSpecifier':
// The `defaultWrapHelper` works by wrapping CommonJS modules in a fake module wrapper
specifierLocal = 'default';
importId = addDefaultImport(path, state, source);
exportExpression = t.memberExpression(t.identifier(importId), t.identifier(specifierLocal));
break;
}
const exportName = t.isIdentifier(specifier.exported)
? specifier.exported.name
: specifier.exported.value;
state.referencedLocals.add(importId);
state.exportDeclarations.push({
statement: (0, helpers_1.withLocation)((0, helpers_1.liveExportHelper)(t, exportName, exportExpression), path.node.loc),
local: importId,
});
}
path.remove();
},
Program: {
// (0): Initialize all state
enter(path, state) {
state.importSpecifiers = new Map();
state.inlineBodyRefs = new Map();
state.referencedLocals = new Set();
state.exportStatements = [];
state.exportDeclarations = [];
state.exportAll = new Map();
state.importDeclarations = [];
// Ensure the iife "globals" don't have conflicting variables in the module.
['global', 'require', 'module', 'exports'].forEach((name) => {
path.scope.rename(name, path.scope.generateUidIdentifier(name).name);
});
},
exit(path, state) {
function getInlineRefExpression(node, localId) {
const inlineRef = state.inlineBodyRefs.get(localId);
if (!inlineRef)
return undefined;
// Reference count the target ID to ensure its import will be added,
// then replace this ID with the InlineRef
state.referencedLocals.add(inlineRef.parentId);
node.name = inlineRef.parentId;
let refNode;
if (inlineRef.member == null) {
refNode = node;
}
else if (node.type !== 'JSXIdentifier') {
refNode = t.memberExpression(node, t.identifier(inlineRef.member));
}
else {
refNode = t.jsxMemberExpression(t.jsxIdentifier(inlineRef.parentId), t.jsxIdentifier(inlineRef.member));
}
return refNode;
}
// (3): Process all "deferred" export declarations in `state.exportDeclarations`
for (const exportStatement of state.exportStatements) {
// (3.1): Convert all local exports into export declarations, while making sure
// to reference imports if necessary
if (!exportStatement.source && exportStatement.specifiers) {
for (const specifier of exportStatement.specifiers) {
if (specifier.type !== 'ExportSpecifier') {
continue; // NOTE: This is not a legal AST type without `source`
}
else if (specifier.exportKind && specifier.exportKind !== 'value') {
continue;
}
const exportName = t.isIdentifier(specifier.exported)
? specifier.exported.name
: specifier.exported.value;
const exportExpression = getInlineRefExpression(specifier.local, specifier.local.name) ?? specifier.local;
state.exportDeclarations.push({
statement: (0, helpers_1.withLocation)((0, helpers_1.liveExportHelper)(t, exportName, exportExpression), exportStatement.loc),
local: undefined,
});
}
}
// (3.2): Process all locally exported declarations
const declaration = exportStatement.declaration;
if (declaration) {
// Live bindings are used for variables, since they can be reassigned and may not be declared until later on
const exportHelper = declaration.type === 'VariableDeclaration' ||
declaration.type !== 'FunctionDeclaration'
? helpers_1.liveExportHelper
: helpers_1.assignExportHelper;
const exportBindings = t.getBindingIdentifiers(declaration, false, true);
for (const exportName in exportBindings) {
state.exportDeclarations.push({
statement: (0, helpers_1.withLocation)(exportHelper(t, exportName, t.identifier(exportBindings[exportName].name)), exportStatement.loc),
local: undefined,
});
}
}
}
// (4): Traverse reference identifiers and replace as needed with `state.inlineBodyRefs`'
// synthetic IDs, while marking the IDs that are referenced in `state.syntheticRefs`
path.traverse({
ReferencedIdentifier(path, state) {
if (path.parent.type === 'ExportSpecifier') {
return;
}
const localId = path.node.name;
// We skip this identifier if it's not a program binding, since
// that means it was declared in a child scope
const localBinding = path.scope.getBinding(localId);
const rootBinding = state.programScope.getBinding(localId);
if (rootBinding !== localBinding)
return;
// Replace the local ID with the inlined reference, if there is one
let inlineRefExpression = getInlineRefExpression(path.node, localId);
if (inlineRefExpression) {
// NOTE(@kitten): Ensure that calls after this member access aren't implicitly bound
// to the object they're called on
if (path.parent.type === 'CallExpression' &&
path.parent.callee === path.node &&
inlineRefExpression.type !== 'JSXMemberExpression') {
inlineRefExpression = (0, helpers_1.nullBoundExpression)(t, inlineRefExpression);
}
path.replaceWith(inlineRefExpression);
path.skip();
}
},
}, {
referencedLocals: state.referencedLocals,
inlineBodyRefs: state.inlineBodyRefs,
programScope: path.scope,
});
const preambleStatements = [];
const esmStatements = [];
let _defaultWrapName;
const wrapDefault = (localId, sourceId) => {
if (!_defaultWrapName) {
_defaultWrapName = '_interopDefault';
preambleStatements.push((0, helpers_1.defaultWrapHelper)(template, _defaultWrapName));
}
return (0, helpers_1.varDeclaratorCallHelper)(t, localId, _defaultWrapName, sourceId);
};
let _namespaceWrapName;
const wrapNamespace = (localId, sourceId) => {
if (!_namespaceWrapName) {
_namespaceWrapName = '_interopNamespace';
preambleStatements.push((0, helpers_1.namespaceWrapHelper)(template, _namespaceWrapName));
}
return (0, helpers_1.varDeclaratorCallHelper)(t, localId, _namespaceWrapName, sourceId);
};
// Add `__esModule` marker if we have any exports
if (state.exportDeclarations.length || state.exportAll.size) {
preambleStatements.push((0, helpers_1.esModuleExportTemplate)(template));
}
// (5): Add all exports, and all referenced imports
for (const exportDeclaration of state.exportDeclarations) {
esmStatements.push(exportDeclaration.statement);
if (exportDeclaration.local) {
state.referencedLocals.add(exportDeclaration.local);
}
}
// Reference locals that are referenced by import declarations
for (const importDeclaration of state.importDeclarations) {
// NOTE(@kitten): The first check removes default/namespace import wrappers when they're unused.
// This diverges from the previous implementation a lot, and is basically unused local elimination
// If we don't want this, this can safely be removed
const source = importDeclaration.source;
const local = addModuleSpecifiers(state, source)[importDeclaration.kind];
if (!local || !state.referencedLocals.has(local)) {
continue;
}
else if (importDeclaration.local) {
state.referencedLocals.add(importDeclaration.local);
}
}
// Insert imports, if they're referenced
for (const importDeclaration of state.importDeclarations) {
const source = importDeclaration.source;
const moduleSpecifiers = addModuleSpecifiers(state, source);
const local = moduleSpecifiers[importDeclaration.kind];
if (!local || !state.referencedLocals.has(local)) {
// Don't add imports that aren't referenced, unless they're required for a side-effect
// We check for REQUIRE, to make sure we only ever add a single side-effect require
if (importDeclaration.kind === "REQUIRE" /* ImportDeclarationKind.REQUIRE */ &&
moduleSpecifiers.sideEffect) {
esmStatements.push((0, helpers_1.withLocation)((0, helpers_1.sideEffectRequireCall)(t, source), importDeclaration.loc));
}
continue;
}
let importStatement;
switch (importDeclaration.kind) {
case "REQUIRE" /* ImportDeclarationKind.REQUIRE */:
importStatement = (0, helpers_1.requireCall)(t, local, source);
break;
case "DEFAULT" /* ImportDeclarationKind.IMPORT_DEFAULT */:
importStatement = wrapDefault(local, importDeclaration.local);
break;
case "NAMESPACE" /* ImportDeclarationKind.IMPORT_NAMESPACE */:
importStatement = wrapNamespace(local, importDeclaration.local);
break;
}
importStatement = (0, helpers_1.withLocation)(importStatement, importDeclaration.loc);
esmStatements.push(importStatement);
const exportAllStatement = state.exportAll.get(local);
if (exportAllStatement != null) {
esmStatements.push(exportAllStatement);
}
}
// WARN(@kitten): This isn't only dependent on exports! If we set this to `false` but
// added any imports, then those imports will accidentally be shifted back to CJS-mode
if (esmStatements.length && state.opts.out) {
state.opts.out.isESModule = true;
}
path.node.body = [...preambleStatements, ...esmStatements, ...path.node.body];
},
},
},
};
}
//# sourceMappingURL=importExportLiveBindings.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 650 Industries (Expo). All rights reserved.
* 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 { ConfigAPI, PluginObj, types as t } from '@babel/core';
export type Options = Readonly<{
importDefault: string;
importAll: string;
resolve: boolean;
out?: {
isESModule: boolean;
[key: string]: unknown;
};
}>;
type State = {
importDefault: t.Node;
importAll: t.Node;
opts: Options;
originalImportOrder: Map<string, t.StringLiteral>;
exportAllFrom: Map<string, {
loc: t.SourceLocation | null | undefined;
}>;
importAllFromAs: Map<string, {
loc: t.SourceLocation | null | undefined;
as: string;
}[]>;
exportAllFromAs: Map<string, {
loc: t.SourceLocation | null | undefined;
as: string;
}[]>;
importDefaultFromAs: Map<string, {
loc: t.SourceLocation | null | undefined;
as: t.Identifier;
}[]>;
exportDefault: {
loc: t.SourceLocation | null | undefined;
name: string;
}[];
exportNamedFrom: Map<string, {
loc: t.SourceLocation | null | undefined;
name: string;
as: string;
}[]>;
importNamedFrom: Map<string, {
loc: t.SourceLocation | null | undefined;
name: string;
as: string;
}[]>;
exportNamed: {
loc: t.SourceLocation | null | undefined;
name: string;
as: string;
}[];
importSideEffect: Map<string, {
loc: t.SourceLocation | null | undefined;
}>;
[key: string]: unknown;
};
export declare function importExportPlugin({ types: t, }: ConfigAPI & typeof import('@babel/core')): PluginObj<State>;
export {};

View File

@@ -0,0 +1,462 @@
"use strict";
/**
* Copyright (c) 650 Industries (Expo). All rights reserved.
* 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.
*/
// A fork of the upstream metro-babel-transform-plugin/import-export-plugin that uses Expo-specific features
// and adds support for export-namespace-from
// https://github.com/facebook/metro/blob/8e48aa823378962beccbe37d85f1aff2c34b28b1/packages/metro-transform-plugins/src/import-export-plugin.js
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.importExportPlugin = importExportPlugin;
const core_1 = require("@babel/core");
const node_assert_1 = __importDefault(require("node:assert"));
const helpers_1 = require("./helpers");
const debug = require('debug')('expo:metro-config:import-export-plugin');
function nullthrows(x, message) {
(0, node_assert_1.default)(x != null, message);
return x;
}
function getArray(map, key) {
if (!map.has(key)) {
map.set(key, []);
}
return nullthrows(map.get(key));
}
function setFirst(map, key, value) {
if (!map.has(key)) {
map.set(key, value);
}
}
const importTemplate = core_1.template.statement(`
var %%asId%% = %%importHelperId%%(%%moduleStr%%);
`);
const requireNameTemplate = core_1.template.statement(`
var %%asId%% = require(%%moduleStr%%).%%nameId%%;
`);
const requireSideEffectTemplate = core_1.template.statement(`
require(%%moduleStr%%);
`);
// NOTE(@krystofwoldrich): Export all template doesn't have to check for export existence
// because it always runs before default and named exports which overwrite the exports object.
// NOTE(@krystofwoldrich): This also re-exports `default` and `__esModule` properties
// we might want to remove that in the future to align with the spec.
const staticExportAllTemplate = core_1.template.statements(`
var %%namespaceId%% = require(%%moduleStr%%);
for (var %%keyId%% in %%namespaceId%%) {
exports[%%keyId%%] = %%namespaceId%%[%%keyId%%];
}
`);
const staticExportTemplate = core_1.template.statement(`
exports.%%asId%% = %%nameId%%;
`);
// Needs to be kept in 1:1 compatibility with Babel.
const esModuleExportTemplate = core_1.template.statement(`
Object.defineProperty(exports, '__esModule', {value: true});
`);
const resolveTemplate = core_1.template.expression(`
require.resolve(NODE)
`);
/**
* Enforces the resolution of a path to a fully-qualified one, if set.
*/
function resolvePath(node, resolve) {
if (!resolve) {
return node;
}
return resolveTemplate({
NODE: node,
});
}
function importExportPlugin({ types: t, }) {
const { isDeclaration, isVariableDeclaration } = t;
return {
visitor: {
ExportAllDeclaration(path, state) {
setFirst(state.originalImportOrder, path.node.source.value, path.node.source);
state.exportAllFrom.set(path.node.source.value, {
loc: path.node.loc,
});
path.remove();
},
ExportDefaultDeclaration(path, state) {
const declaration = path.node.declaration;
const id = ('id' in declaration && declaration.id) || path.scope.generateUidIdentifier('default');
declaration.id = id;
const loc = path.node.loc;
if (isDeclaration(declaration)) {
path.insertBefore((0, helpers_1.withLocation)(declaration, loc));
}
else {
path.insertBefore((0, helpers_1.withLocation)(t.variableDeclaration('var', [t.variableDeclarator(id, declaration)]), loc));
}
state.exportDefault.push({
name: id.name,
loc,
});
path.remove();
},
ExportNamedDeclaration(path, state) {
if (path.node.exportKind && path.node.exportKind !== 'value') {
return;
}
const loc = path.node.loc;
const declaration = path.node.declaration;
if (declaration) {
if (isVariableDeclaration(declaration)) {
declaration.declarations.forEach((d) => {
switch (d.id.type) {
case 'ObjectPattern':
{
const properties = d.id.properties;
properties.forEach((p) => {
const nameCandidate = p.type === 'ObjectProperty' ? p.value : p.argument;
const name = 'name' in nameCandidate ? nameCandidate.name : undefined;
if (name) {
state.exportNamed.push({
name,
as: name,
loc,
});
}
else {
debug('Unexpected export named declaration with object pattern without name.', p.toString());
}
});
}
break;
case 'ArrayPattern':
{
const elements = d.id.elements;
elements.forEach((e) => {
if (!e) {
return;
}
const nameCandidate = 'argument' in e ? e.argument : e;
const name = 'name' in nameCandidate ? nameCandidate.name : undefined;
if (name) {
state.exportNamed.push({
name,
as: name,
loc,
});
}
else {
debug('Unexpected export named declaration with array pattern without name.', e?.toString());
}
});
}
break;
default:
{
const id = d.id;
const name = 'name' in id ? id.name : undefined;
if (name) {
state.exportNamed.push({
name,
as: name,
loc,
});
}
else {
debug('Unexpected export named declaration with identifier without name.', id.toString());
}
}
break;
}
});
}
else {
if ('id' in declaration) {
const id = declaration.id || path.scope.generateUidIdentifier();
const name = id.type === 'StringLiteral' ? id.value : id.name;
declaration.id = id;
state.exportNamed.push({
name,
as: name,
loc,
});
}
else {
debug('Unexpected export named declaration without id.', declaration.toString());
}
}
path.insertBefore(declaration);
}
const specifiers = path.node.specifiers;
if (specifiers && specifiers.length) {
const source = path.node.source;
if (source)
setFirst(state.originalImportOrder, source.value, source);
specifiers.forEach((s) => {
if (s.exported.type === 'StringLiteral') {
// https://babeljs.io/docs/en/babel-plugin-syntax-module-string-names
throw path.buildCodeFrameError('Module string names are not supported');
}
switch (s.type) {
case 'ExportSpecifier':
(source ? getArray(state.exportNamedFrom, source.value) : state.exportNamed).push({
name: s.local.name,
as: s.exported.name,
loc: path.node.loc,
});
break;
case 'ExportDefaultSpecifier':
state.exportDefault.push({
name: s.exported.name,
loc: path.node.loc,
});
break;
case 'ExportNamespaceSpecifier':
// export * as b from 'a'
getArray(state.exportAllFromAs, nullthrows(source?.value)).push({
as: s.exported.name,
loc: path.node.loc,
});
break;
default:
debug('Unexpected export named declaration specifier type.', s.toString());
break;
}
});
}
path.remove();
},
ImportDeclaration(path, state) {
if (path.node.importKind && path.node.importKind !== 'value') {
return;
}
const file = path.node.source;
const specifiers = path.node.specifiers;
const loc = path.node.loc;
setFirst(state.originalImportOrder, file.value, file);
if (!specifiers.length) {
state.importSideEffect.set(file.value, { loc });
}
else {
specifiers.forEach((s) => {
switch (s.type) {
case 'ImportNamespaceSpecifier':
getArray(state.importAllFromAs, file.value).push({
as: s.local.name,
loc: path.node.loc,
});
break;
case 'ImportDefaultSpecifier':
getArray(state.importDefaultFromAs, file.value).push({
as: s.local,
loc: path.node.loc,
});
break;
case 'ImportSpecifier': {
const importedName = s.imported.type === 'StringLiteral' ? s.imported.value : s.imported.name;
if (importedName === 'default') {
getArray(state.importDefaultFromAs, file.value).push({
as: s.local,
loc: path.node.loc,
});
}
else {
getArray(state.importNamedFrom, file.value).push({
name: importedName,
as: s.local.name,
loc: path.node.loc,
});
}
break;
}
default:
throw new TypeError('Unknown import type: ' + s.type);
}
});
}
path.remove();
},
Program: {
enter(path, state) {
state.exportAll = [];
state.exportDefault = [];
state.exportNamed = [];
state.imports = [];
state.importAll = t.identifier(state.opts.importAll);
state.importDefault = t.identifier(state.opts.importDefault);
state.originalImportOrder = new Map();
state.exportAllFrom = new Map();
state.importAllFromAs = new Map();
state.exportAllFromAs = new Map();
state.importDefaultFromAs = new Map();
state.exportDefault = [];
state.exportNamedFrom = new Map();
state.importNamedFrom = new Map();
state.exportNamed = [];
state.importSideEffect = new Map();
// Rename declarations at module scope that might otherwise conflict
// with arguments we inject into the module factory.
// Note that it isn't necessary to rename importAll/importDefault
// because Metro already uses generateUid to generate unused names.
['module', 'global', 'exports', 'require'].forEach((name) => path.scope.rename(name));
},
exit(path, state) {
const body = path.node.body;
const hasESExports = state.exportDefault.length ||
state.exportAllFrom.size ||
state.exportNamed.length ||
state.exportAllFromAs.size ||
state.exportNamedFrom.size;
const hasESImports = state.importAllFromAs.size ||
state.importDefaultFromAs.size ||
state.importNamedFrom.size ||
state.importSideEffect.size;
const imports = [];
const exportAll = [];
const staticExports = [];
const defaultStaticExports = [];
for (const [, module] of state.originalImportOrder) {
const resolved = resolvePath(module, state.opts.resolve);
const exportAllFrom = state.exportAllFrom.get(module.value);
if (exportAllFrom) {
// export * from 'module'
exportAll.push(...(0, helpers_1.withLocation)(staticExportAllTemplate({
keyId: path.scope.generateUidIdentifier('key'),
moduleStr: t.cloneNode(resolved),
namespaceId: path.scope.generateUidIdentifier(module.value),
}), exportAllFrom.loc));
}
for (const { as, loc } of getArray(state.exportAllFromAs, module.value)) {
// export * as name from 'module' -> var _name = _$$_IMPORT_ALL('module'); exports.name = _name;
const ns = path.scope.generateUidIdentifier(as);
imports.push((0, helpers_1.withLocation)(importTemplate({
importHelperId: t.cloneNode(state.importAll),
moduleStr: t.cloneNode(resolved),
asId: t.cloneNode(ns),
}), loc));
// NOTE: To move all defaults to bottom of the program, we should check as === 'default' here
// but the original plugin doesn't do that.
staticExports.push((0, helpers_1.withLocation)(staticExportTemplate({
nameId: t.cloneNode(ns),
asId: t.identifier(as),
}), loc));
}
for (const { name, as, loc } of getArray(state.exportNamedFrom, module.value)) {
if (name === 'default' && as === 'default') {
// export { default } from 'module' -> exports.default = requireDefault('module');
const tmp = path.scope.generateUidIdentifier(name);
imports.push((0, helpers_1.withLocation)(importTemplate({
importHelperId: t.cloneNode(state.importDefault),
moduleStr: t.cloneNode(resolved),
asId: t.cloneNode(tmp),
}), loc));
defaultStaticExports.push((0, helpers_1.withLocation)(staticExportTemplate({
nameId: t.cloneNode(tmp),
asId: t.identifier(as),
}), loc));
}
else if (name === 'default') {
// export { default as name } from 'module' -> var _default = requireDefault('module'); exports.name = _default;
const tmp = path.scope.generateUidIdentifier(name);
imports.push((0, helpers_1.withLocation)(importTemplate({
importHelperId: t.cloneNode(state.importDefault),
moduleStr: t.cloneNode(resolved),
asId: t.cloneNode(tmp),
}), loc));
staticExports.push((0, helpers_1.withLocation)(staticExportTemplate({
nameId: t.cloneNode(tmp),
asId: t.identifier(as),
}), loc));
}
else {
// export { one as two } from 'module' -> var _one = require('module').one; exports.two = _one;
const tmp = path.scope.generateUidIdentifier(name);
imports.push((0, helpers_1.withLocation)(requireNameTemplate({
moduleStr: t.cloneNode(resolved),
nameId: t.identifier(name),
asId: t.cloneNode(tmp),
}), loc));
staticExports.push((0, helpers_1.withLocation)(staticExportTemplate({
nameId: t.cloneNode(tmp),
asId: t.identifier(as),
}), loc));
}
}
let sharedModuleVariableDeclaration = null;
if (getArray(state.importNamedFrom, module.value).length === 1) {
// import { one as two } from 'module' -> var two = require('module').one;
const importNamed = getArray(state.importNamedFrom, module.value)[0];
imports.push((0, helpers_1.withLocation)(requireNameTemplate({
moduleStr: t.cloneNode(resolved),
nameId: t.identifier(importNamed.name),
asId: t.identifier(importNamed.as),
}), getArray(state.importNamedFrom, module.value)[0].loc));
}
else {
// import { one as two, three as four } from 'module'
// -> var _module = require('module'), two = _module.one, four = _module.four;
let sharedModuleImport = null;
for (const { name, as, loc } of getArray(state.importNamedFrom, module.value)) {
if (!sharedModuleVariableDeclaration) {
sharedModuleImport = path.scope.generateUidIdentifierBasedOnNode(module);
sharedModuleVariableDeclaration = (0, helpers_1.withLocation)(t.variableDeclaration('var', [
t.variableDeclarator(t.cloneNode(sharedModuleImport), t.callExpression(t.identifier('require'), [t.cloneNode(resolved)])),
]), loc);
imports.push(sharedModuleVariableDeclaration);
}
sharedModuleVariableDeclaration.declarations.push((0, helpers_1.withLocation)(t.variableDeclarator(t.identifier(as), t.memberExpression(t.cloneNode(nullthrows(sharedModuleImport)), t.identifier(name))), loc));
}
}
if (!sharedModuleVariableDeclaration && state.importSideEffect.has(module.value)) {
// import 'module' -> require('module')
imports.push((0, helpers_1.withLocation)(requireSideEffectTemplate({
moduleStr: t.cloneNode(resolved),
}), state.importSideEffect.get(module.value)?.loc));
}
for (const { as, loc } of getArray(state.importAllFromAs, module.value)) {
// import * as name from 'module'
imports.push((0, helpers_1.withLocation)(importTemplate({
importHelperId: t.cloneNode(state.importAll),
moduleStr: t.cloneNode(resolved),
asId: t.identifier(as),
}), loc));
}
for (const { as, loc } of getArray(state.importDefaultFromAs, module.value)) {
// import name from 'module'
imports.push((0, helpers_1.withLocation)(importTemplate({
importHelperId: t.cloneNode(state.importDefault),
moduleStr: t.cloneNode(resolved),
asId: as,
}), loc));
}
}
for (const { name, as, loc } of state.exportNamed) {
staticExports.push((0, helpers_1.withLocation)(staticExportTemplate({
nameId: t.identifier(name),
asId: t.identifier(as),
}), loc));
}
for (const { name, loc } of state.exportDefault) {
defaultStaticExports.push((0, helpers_1.withLocation)(staticExportTemplate({
nameId: t.identifier(name),
asId: t.identifier('default'),
}), loc));
}
body.unshift(...imports);
body.push(...exportAll);
body.push(...staticExports);
body.push(...defaultStaticExports);
if (hasESExports) {
body.unshift(esModuleExportTemplate());
}
if (state.opts.out && (hasESExports || hasESImports)) {
state.opts.out.isESModule = true;
}
},
},
},
};
}
//# sourceMappingURL=importExportStatic.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
export { importExportLiveBindingsPlugin } from './importExportLiveBindings';
export { importExportPlugin } from './importExportStatic';

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.importExportPlugin = exports.importExportLiveBindingsPlugin = void 0;
var importExportLiveBindings_1 = require("./importExportLiveBindings");
Object.defineProperty(exports, "importExportLiveBindingsPlugin", { enumerable: true, get: function () { return importExportLiveBindings_1.importExportLiveBindingsPlugin; } });
var importExportStatic_1 = require("./importExportStatic");
Object.defineProperty(exports, "importExportPlugin", { enumerable: true, get: function () { return importExportStatic_1.importExportPlugin; } });
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transform-plugins/index.ts"],"names":[],"mappings":";;;AAAA,uEAA4E;AAAnE,0IAAA,8BAA8B,OAAA;AACvC,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA"}

View File

@@ -0,0 +1,19 @@
/**
* Copyright 2023-present 650 Industries (Expo). All rights reserved.
* 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 upstream transformer, but with modifications made for web production hashing.
* https://github.com/facebook/metro/blob/412771475c540b6f85d75d9dcd5a39a6e0753582/packages/metro-transform-worker/src/utils/assetTransformer.js#L1
*/
import { type ParseResult } from '@babel/core';
import type { BabelTransformerArgs } from '@expo/metro/metro-babel-transformer';
export declare function transform({ filename, options, }: {
filename: string;
options: Pick<BabelTransformerArgs['options'], 'platform' | 'projectRoot' | 'customTransformOptions' | 'publicPath'>;
}, assetRegistryPath: string, assetDataPlugins: readonly string[]): Promise<{
ast: ParseResult;
reactClientReference?: string;
}>;

View File

@@ -0,0 +1,120 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.transform = transform;
/**
* Copyright 2023-present 650 Industries (Expo). All rights reserved.
* 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 upstream transformer, but with modifications made for web production hashing.
* https://github.com/facebook/metro/blob/412771475c540b6f85d75d9dcd5a39a6e0753582/packages/metro-transform-worker/src/utils/assetTransformer.js#L1
*/
const core_1 = require("@babel/core");
const util_1 = require("@expo/metro/metro/Bundler/util");
const node_path_1 = __importDefault(require("node:path"));
const node_url_1 = __importDefault(require("node:url"));
const getAssets_1 = require("./getAssets");
const filePath_1 = require("../utils/filePath");
// Register client components for assets in server component environments.
const buildClientReferenceRequire = core_1.template.statement(`module.exports = require('react-server-dom-webpack/server').createClientModuleProxy(FILE_PATH);`);
const buildStringRef = core_1.template.statement(`module.exports = FILE_PATH;`);
// The React Server Component version cannot have a function otherwise we'd be passing a function to the client component <Image />.
// TODO: Make react-native Image and expo-image server components that can simplify the asset before passing to the client component.
const buildStaticObjectRef = core_1.template.statement(
// Matches the `ImageSource` type from React Native: https://reactnative.dev/docs/image#source
`module.exports = { uri: FILE_PATH, width: WIDTH, height: HEIGHT };`);
const buildStaticObjectClientRef = core_1.template.statement(
// Matches the `ImageSource` type from React Native: https://reactnative.dev/docs/image#source
`module.exports = { uri: FILE_PATH, width: WIDTH, height: HEIGHT, toString() { return this.uri } };`);
async function transform({ filename, options, }, assetRegistryPath, assetDataPlugins) {
options ??= options || {
platform: '',
projectRoot: '',
};
// Is bundling for webview.
const isDomComponent = options.platform === 'web' && options.customTransformOptions?.dom;
const useMd5Filename = options.customTransformOptions?.useMd5Filename;
const isExport = options.publicPath.includes('?export_path=');
const isHosted = options.platform === 'web' || (options.customTransformOptions?.hosted && isExport);
const isReactServer = options.customTransformOptions?.environment === 'react-server';
const isServerEnv = isReactServer || options.customTransformOptions?.environment === 'node';
const absolutePath = node_path_1.default.resolve(options.projectRoot, filename);
const getClientReference = () => isReactServer ? node_url_1.default.pathToFileURL(absolutePath).href : undefined;
if ((options.platform !== 'web' ||
// React Server DOM components should use the client reference in order to local embedded assets.
isDomComponent) &&
// NOTE(EvanBacon): There may be value in simply evaluating assets on the server.
// Here, we're passing the info back to the client so the multi-resolution asset can be evaluated and downloaded.
isReactServer) {
return {
ast: {
comments: null,
...core_1.types.file(core_1.types.program([
buildClientReferenceRequire({
FILE_PATH: JSON.stringify(`./${(0, filePath_1.toPosixPath)(node_path_1.default.relative(options.projectRoot, absolutePath))}`),
}),
])),
errors: [],
},
reactClientReference: getClientReference(),
};
}
const data = await (0, getAssets_1.getUniversalAssetData)(absolutePath, filename, assetDataPlugins, options.platform, isDomComponent && isExport
? // If exporting a dom component, we need to use a public path that doesn't start with `/` to ensure that assets are loaded
// relative to the `DOM_COMPONENTS_BUNDLE_DIR`.
`/assets?export_path=assets`
: options.publicPath, isHosted);
if (isServerEnv || options.platform === 'web') {
const type = !data.type ? '' : `.${data.type}`;
let assetPath;
if (useMd5Filename) {
assetPath = data.hash + type;
}
else if (!isExport) {
assetPath = data.httpServerLocation + '/' + data.name + type;
}
else {
assetPath = data.httpServerLocation.replace(/\.\.\//g, '_') + '/' + data.name + type;
}
// If size data is known then it should be passed back to ensure the correct dimensions are used.
if (data.width != null || data.height != null) {
const options = {
FILE_PATH: JSON.stringify(assetPath),
WIDTH: data.width != null ? core_1.types.numericLiteral(data.width) : core_1.types.buildUndefinedNode(),
HEIGHT: data.height != null ? core_1.types.numericLiteral(data.height) : core_1.types.buildUndefinedNode(),
};
const creatorFunction = isReactServer ? buildStaticObjectRef : buildStaticObjectClientRef;
return {
ast: {
comments: null,
...core_1.types.file(core_1.types.program([creatorFunction(options)])),
errors: [],
},
reactClientReference: getClientReference(),
};
}
// Use single string references outside of client-side React Native.
// module.exports = "/foo/bar.png";
return {
ast: {
comments: null,
...core_1.types.file(core_1.types.program([buildStringRef({ FILE_PATH: JSON.stringify(assetPath) })])),
errors: [],
},
reactClientReference: getClientReference(),
};
}
return {
ast: {
comments: null,
...(0, util_1.generateAssetCodeFileAst)(assetRegistryPath, data),
errors: [],
},
};
}
//# sourceMappingURL=asset-transformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"asset-transformer.js","sourceRoot":"","sources":["../../src/transform-worker/asset-transformer.ts"],"names":[],"mappings":";;;;;AAsCA,8BA4HC;AAlKD;;;;;;;;;GASG;AACH,sCAAqE;AACrE,yDAA0E;AAE1E,0DAA6B;AAC7B,wDAA2B;AAE3B,2CAAoD;AACpD,gDAAgD;AAEhD,0EAA0E;AAC1E,MAAM,2BAA2B,GAAG,eAAQ,CAAC,SAAS,CACpD,iGAAiG,CAClG,CAAC;AAEF,MAAM,cAAc,GAAG,eAAQ,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;AAEzE,oIAAoI;AACpI,qIAAqI;AACrI,MAAM,oBAAoB,GAAG,eAAQ,CAAC,SAAS;AAC7C,8FAA8F;AAC9F,oEAAoE,CACrE,CAAC;AAEF,MAAM,0BAA0B,GAAG,eAAQ,CAAC,SAAS;AACnD,8FAA8F;AAC9F,oGAAoG,CACrG,CAAC;AAEK,KAAK,UAAU,SAAS,CAC7B,EACE,QAAQ,EACR,OAAO,GAOR,EACD,iBAAyB,EACzB,gBAAmC;IAKnC,OAAO,KAAK,OAAO,IAAI;QACrB,QAAQ,EAAE,EAAE;QACZ,WAAW,EAAE,EAAE;KAChB,CAAC;IAEF,2BAA2B;IAC3B,MAAM,cAAc,GAAG,OAAO,CAAC,QAAQ,KAAK,KAAK,IAAI,OAAO,CAAC,sBAAsB,EAAE,GAAG,CAAC;IACzF,MAAM,cAAc,GAAG,OAAO,CAAC,sBAAsB,EAAE,cAAc,CAAC;IACtE,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IAC9D,MAAM,QAAQ,GACZ,OAAO,CAAC,QAAQ,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,MAAM,IAAI,QAAQ,CAAC,CAAC;IACrF,MAAM,aAAa,GAAG,OAAO,CAAC,sBAAsB,EAAE,WAAW,KAAK,cAAc,CAAC;IACrF,MAAM,WAAW,GAAG,aAAa,IAAI,OAAO,CAAC,sBAAsB,EAAE,WAAW,KAAK,MAAM,CAAC;IAE5F,MAAM,YAAY,GAAG,mBAAI,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAEjE,MAAM,kBAAkB,GAAG,GAAG,EAAE,CAC9B,aAAa,CAAC,CAAC,CAAC,kBAAG,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnE,IACE,CAAC,OAAO,CAAC,QAAQ,KAAK,KAAK;QACzB,iGAAiG;QACjG,cAAc,CAAC;QACjB,iFAAiF;QACjF,iHAAiH;QACjH,aAAa,EACb,CAAC;QACD,OAAO;YACL,GAAG,EAAE;gBACH,QAAQ,EAAE,IAAI;gBACd,GAAG,YAAC,CAAC,IAAI,CACP,YAAC,CAAC,OAAO,CAAC;oBACR,2BAA2B,CAAC;wBAC1B,SAAS,EAAE,IAAI,CAAC,SAAS,CACvB,KAAK,IAAA,sBAAW,EAAC,mBAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,EAAE,CACrE;qBACF,CAAC;iBACH,CAAC,CACH;gBACD,MAAM,EAAE,EAAE;aACX;YACD,oBAAoB,EAAE,kBAAkB,EAAG;SAC5C,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,IAAA,iCAAqB,EACtC,YAAY,EACZ,QAAQ,EACR,gBAAgB,EAChB,OAAO,CAAC,QAAQ,EAChB,cAAc,IAAI,QAAQ;QACxB,CAAC,CAAC,0HAA0H;YAC1H,+CAA+C;YAC/C,4BAA4B;QAC9B,CAAC,CAAC,OAAO,CAAC,UAAU,EACtB,QAAQ,CACT,CAAC;IAEF,IAAI,WAAW,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;QAC9C,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/C,IAAI,SAAiB,CAAC;QACtB,IAAI,cAAc,EAAE,CAAC;YACnB,SAAS,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAC/B,CAAC;aAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrB,SAAS,GAAG,IAAI,CAAC,kBAAkB,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACvF,CAAC;QAED,iGAAiG;QACjG,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;YAC9C,MAAM,OAAO,GAA+C;gBAC1D,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;gBACpC,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,YAAC,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAC,CAAC,kBAAkB,EAAE;gBACjF,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,YAAC,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAC,CAAC,kBAAkB,EAAE;aACrF,CAAC;YACF,MAAM,eAAe,GAAG,aAAa,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,0BAA0B,CAAC;YAE1F,OAAO;gBACL,GAAG,EAAE;oBACH,QAAQ,EAAE,IAAI;oBACd,GAAG,YAAC,CAAC,IAAI,CAAC,YAAC,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBAChD,MAAM,EAAE,EAAE;iBACX;gBACD,oBAAoB,EAAE,kBAAkB,EAAE;aAC3C,CAAC;QACJ,CAAC;QAED,oEAAoE;QACpE,mCAAmC;QACnC,OAAO;YACL,GAAG,EAAE;gBACH,QAAQ,EAAE,IAAI;gBACd,GAAG,YAAC,CAAC,IAAI,CAAC,YAAC,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAChF,MAAM,EAAE,EAAE;aACX;YACD,oBAAoB,EAAE,kBAAkB,EAAE;SAC3C,CAAC;IACJ,CAAC;IAED,OAAO;QACL,GAAG,EAAE;YACH,QAAQ,EAAE,IAAI;YACd,GAAG,IAAA,+BAAwB,EAAC,iBAAiB,EAAE,IAAI,CAAC;YACpD,MAAM,EAAE,EAAE;SACX;KACF,CAAC;AACJ,CAAC"}

View File

@@ -0,0 +1,4 @@
/**
* Copyright © 2025 650 Industries.
*/
export declare function getBrowserslistTargets(projectRoot: string): Promise<import('lightningcss').Targets>;

View File

@@ -0,0 +1,26 @@
"use strict";
/**
* Copyright © 2025 650 Industries.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBrowserslistTargets = getBrowserslistTargets;
const debug = require('debug')('expo:metro:browserslist');
const browserslistCache = {};
// Suppress `browserslist`'s own "data is X months old" warning in transform workers.
process.env.BROWSERSLIST_IGNORE_OLD_DATA = '1';
async function getBrowserslistTargets(projectRoot) {
if (browserslistCache[projectRoot]) {
return browserslistCache[projectRoot];
}
const browserslist = await import('browserslist');
const { browserslistToTargets } = await import('lightningcss');
const targets = browserslistToTargets(browserslist.default(undefined, {
throwOnMissing: false,
ignoreUnknownVersions: true,
path: projectRoot,
}));
debug('Browserslist targets: %O', targets);
browserslistCache[projectRoot] = targets;
return targets;
}
//# sourceMappingURL=browserslist.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"browserslist.js","sourceRoot":"","sources":["../../src/transform-worker/browserslist.ts"],"names":[],"mappings":";AAAA;;GAEG;;AASH,wDAoBC;AA3BD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,yBAAyB,CAAuB,CAAC;AAEhF,MAAM,iBAAiB,GAAmD,EAAE,CAAC;AAE7E,qFAAqF;AACrF,OAAO,CAAC,GAAG,CAAC,4BAA4B,GAAG,GAAG,CAAC;AAExC,KAAK,UAAU,sBAAsB,CAC1C,WAAmB;IAEnB,IAAI,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;QACnC,OAAO,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;IAClD,MAAM,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;IAE/D,MAAM,OAAO,GAAG,qBAAqB,CACnC,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE;QAC9B,cAAc,EAAE,KAAK;QACrB,qBAAqB,EAAE,IAAI;QAC3B,IAAI,EAAE,WAAW;KAClB,CAAC,CACH,CAAC;IAEF,KAAK,CAAC,0BAA0B,EAAE,OAAO,CAAC,CAAC;IAC3C,iBAAiB,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,OAAO,CAAC;AACjB,CAAC"}

View File

@@ -0,0 +1,141 @@
/**
* Copyright 2024-present 650 Industries (Expo). All rights reserved.
* 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 { types as t } from '@babel/core';
import type { NodePath } from '@babel/core';
export type AsyncDependencyType = 'weak' | 'maybeSync' | 'async' | 'prefetch' | 'worker';
type AllowOptionalDependenciesWithOptions = {
exclude: string[];
};
type AllowOptionalDependencies = boolean | AllowOptionalDependenciesWithOptions;
export type Dependency = Readonly<{
data: DependencyData;
name: string;
}>;
type ContextMode = 'sync' | 'eager' | 'lazy' | 'lazy-once';
type ContextFilter = Readonly<{
pattern: string;
flags: string;
}>;
type RequireContextParams = Readonly<{
recursive: boolean;
filter: Readonly<ContextFilter>;
mode: ContextMode;
}>;
type MutableDependencyData = {
/** A locally unique key for this dependency within the current module. */
key: string;
/** If null, then the dependency is synchronous. (ex. `require('foo')`) */
asyncType: AsyncDependencyType | null;
/**
* If true, the dependency is declared using an ESM import, e.g.
* "import x from 'y'" or "await import('z')". A resolver should typically
* use this to assert either "import" or "require" for conditional exports
* and subpath imports.
*/
isESMImport: boolean;
isOptional?: boolean;
locs: readonly t.SourceLocation[];
/** Context for requiring a collection of modules. */
contextParams?: RequireContextParams;
exportNames: string[];
css?: {
url: string;
supports: string | null;
media: string | null;
};
};
export type DependencyData = Readonly<MutableDependencyData>;
export type MutableInternalDependency = MutableDependencyData & {
locs: t.SourceLocation[];
index: number;
name: string;
/** Usage of the dep, number of imports of the dep in other modules, used for tree shaking. */
imports: number;
};
export type InternalDependency = Readonly<MutableInternalDependency>;
export type State = {
asyncRequireModulePathStringLiteral: t.StringLiteral | null;
dependencyCalls: Set<string>;
dependencyRegistry: DependencyRegistry;
dependencyTransformer: DependencyTransformer;
dynamicRequires: DynamicRequiresBehavior;
dependencyMapIdentifier: t.Identifier | null;
keepRequireNames: boolean;
allowOptionalDependencies: AllowOptionalDependencies;
unstable_allowRequireContext: boolean;
unstable_isESMImportAtSource: ((location: t.SourceLocation) => boolean) | null;
/** Indicates that the pass should only collect dependencies and avoid mutating the AST. This is used for tree shaking passes. */
collectOnly?: boolean;
};
export type Options = Readonly<{
asyncRequireModulePath: string;
dependencyMapName?: string | null;
dynamicRequires: DynamicRequiresBehavior;
inlineableCalls: readonly string[];
keepRequireNames: boolean;
allowOptionalDependencies: AllowOptionalDependencies;
dependencyTransformer?: DependencyTransformer;
unstable_allowRequireContext: boolean;
unstable_isESMImportAtSource: ((location: t.SourceLocation) => boolean) | null;
/** Indicates that the pass should only collect dependencies and avoid mutating the AST. This is used for tree shaking passes. */
collectOnly?: boolean;
}>;
export type CollectedDependencies<TAst extends t.File = t.File> = Readonly<{
ast: TAst;
dependencyMapName: string;
dependencies: readonly Dependency[];
}>;
export interface DependencyTransformer {
transformSyncRequire(path: NodePath<t.CallExpression>, dependency: InternalDependency, state: State): void;
transformImportMaybeSyncCall(path: NodePath<any>, dependency: InternalDependency, state: State): void;
transformImportCall(path: NodePath<any>, dependency: InternalDependency, state: State): void;
transformPrefetch(path: NodePath<any>, dependency: InternalDependency, state: State): void;
transformIllegalDynamicRequire(path: NodePath<any>, state: State): void;
}
export type DynamicRequiresBehavior = 'throwAtRuntime' | 'reject' | 'warn';
type ImportQualifier = Readonly<{
name: string;
asyncType: AsyncDependencyType | null;
isESMImport: boolean;
optional: boolean;
contextParams?: RequireContextParams;
exportNames: string[];
}>;
declare function collectDependencies<TAst extends t.File>(ast: TAst, options: Options): CollectedDependencies<TAst>;
export declare function getExportNamesFromPath(path: NodePath<any>): string[];
export declare class InvalidRequireCallError extends Error {
constructor({ node }: NodePath<any>, message?: string);
}
/**
* Given an import qualifier, return a key used to register the dependency.
* Attributes can be appended to distinguish various combinations that would
* otherwise be considered the same dependency edge.
*
* For example, the following dependencies would collapse into a single edge
* if they simply utilized the `name` property:
*
* ```
* require('./foo');
* import foo from './foo'
* await import('./foo')
* require.context('./foo');
* require.context('./foo', true, /something/);
* require.context('./foo', false, /something/);
* require.context('./foo', false, /something/, 'lazy');
* ```
*
* This method should be utilized by `registerDependency`.
*/
export declare function getKeyForDependency(qualifier: Pick<ImportQualifier, 'asyncType' | 'contextParams' | 'isESMImport' | 'name'>): string;
export declare function hashKey(key: string): string;
declare class DependencyRegistry {
private _dependencies;
registerDependency(qualifier: ImportQualifier): InternalDependency;
getDependencies(): InternalDependency[];
}
export default collectDependencies;

Some files were not shown because too many files have changed in this diff Show More