Files
Fluxup_PAP/node_modules/@expo/metro-config/build/serializer/withExpoSerializers.js
2026-03-10 16:18:05 +00:00

301 lines
14 KiB
JavaScript

"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