@expo/metro-config
Version:
A Metro config for running React Native projects with the Metro bundler
244 lines • 11.5 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSerializerFromSerialProcessors = exports.createDefaultExportCustomSerializer = exports.withSerializerPlugins = exports.withExpoSerializers = void 0;
/**
* 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.
*/
const jsc_safe_url_1 = require("jsc-safe-url");
const sourceMapString_1 = __importDefault(require("metro/src/DeltaBundler/Serializers/sourceMapString"));
const bundleToString_1 = __importDefault(require("metro/src/lib/bundleToString"));
const debugId_1 = require("./debugId");
const environmentVariableSerializerPlugin_1 = require("./environmentVariableSerializerPlugin");
const baseJSBundle_1 = require("./fork/baseJSBundle");
const reconcileTransformSerializerPlugin_1 = require("./reconcileTransformSerializerPlugin");
const serializeChunks_1 = require("./serializeChunks");
const treeShakeSerializerPlugin_1 = require("./treeShakeSerializerPlugin");
const env_1 = require("../env");
const sourceMapString = typeof sourceMapString_1.default !== 'function'
? sourceMapString_1.default.sourceMapString
: sourceMapString_1.default;
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(treeShakeSerializerPlugin_1.treeShakeSerializer);
// Then finish transforming the modules from AST to JS.
processors.push(reconcileTransformSerializerPlugin_1.reconcileTransformSerializerPlugin);
return withSerializerPlugins(config, processors, options);
}
exports.withExpoSerializers = withExpoSerializers;
// 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 originalSerializer = config.serializer?.customSerializer;
return {
...config,
serializer: {
...config.serializer,
customSerializer: createSerializerFromSerialProcessors(config, processors, originalSerializer ?? null, options),
},
};
}
exports.withSerializerPlugins = withSerializerPlugins;
function createDefaultExportCustomSerializer(config, configOptions = {}) {
return async (entryPoint, preModules, graph, inputOptions) => {
const isPossiblyDev = graph.transformOptions.hot;
// 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 = (0, baseJSBundle_1.baseJSBundle)(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;
if (config.serializer?.customSerializer) {
const bundle = await config.serializer?.customSerializer(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)((0, baseJSBundle_1.baseJSBundle)(entryPoint, premodulesToBundle, graph, {
...options,
debugId,
})).code;
}
const getEnsuredMaps = () => {
bundleMap ??= sourceMapString([...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,
};
};
}
exports.createDefaultExportCustomSerializer = createDefaultExportCustomSerializer;
function getDefaultSerializer(config, fallbackSerializer, configOptions = {}) {
const defaultSerializer = fallbackSerializer ?? createDefaultExportCustomSerializer(config, configOptions);
return async (entryPoint, preModules, graph, inputOptions) => {
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);
},
};
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);
};
}
function createSerializerFromSerialProcessors(config, processors, originalSerializer, options = {}) {
const finalSerializer = getDefaultSerializer(config, originalSerializer, options);
return async (...props) => {
for (const processor of processors) {
if (processor) {
props = await processor(...props);
}
}
return finalSerializer(...props);
};
}
exports.createSerializerFromSerialProcessors = createSerializerFromSerialProcessors;
//# sourceMappingURL=withExpoSerializers.js.map
;