UNPKG

@expo/metro-config

Version:

A Metro config for running React Native projects with the Metro bundler

90 lines 4.77 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getUniversalAssetData = getUniversalAssetData; exports.default = getAssets; const Assets_1 = require("metro/src/Assets"); const js_js_1 = require("metro/src/DeltaBundler/Serializers/helpers/js.js"); const node_crypto_1 = __importDefault(require("node:crypto")); const node_fs_1 = __importDefault(require("node:fs")); const node_path_1 = __importDefault(require("node:path")); const debug = require('debug')('expo:metro-config:assets'); function getMD5ForData(data) { if (data.length === 1) return data[0]; const hash = node_crypto_1.default.createHash('md5'); hash.update(data.join('')); return hash.digest('hex'); } function getMD5ForFilePathAsync(path) { return new Promise((resolve, reject) => { const output = node_crypto_1.default.createHash('md5'); const input = node_fs_1.default.createReadStream(path); input.on('error', (err) => reject(err)); output.on('error', (err) => reject(err)); output.once('readable', () => resolve(output.read().toString('hex'))); input.pipe(output); }); } function isHashedAssetData(asset) { if ('fileHashes' in asset && Array.isArray(asset.fileHashes)) { return true; } return false; } async function ensureOtaAssetHashesAsync(asset) { // Legacy cases where people have the `expo-asset/tools/hashAssetFiles` set still. if (isHashedAssetData(asset)) { debug('fileHashes already added, skipping injection for: ' + asset.name); return asset; } const hashes = await Promise.all(asset.files.map(getMD5ForFilePathAsync)); // New version where we run the asset plugin every time. asset.fileHashes = hashes; // Convert the `../` segments of the server URL to `_` to support monorepos. // This same transformation takes place in `AssetSourceResolver.web` (expo-assets, expo-image) and `persistMetroAssets` of Expo CLI, // this originally came from the Metro opinion https://github.com/react-native-community/cli/blob/2204d357379e2067cebe2791e90388f7e97fc5f5/packages/cli-plugin-metro/src/commands/bundle/getAssetDestPathIOS.ts#L19C5-L19C10 if (asset.httpServerLocation.includes('?export_path=')) { // @ts-expect-error: marked as read-only asset.httpServerLocation = asset.httpServerLocation .match(/\?export_path=(.*)/)[1] .replace(/\.\.\//g, '_'); } // URL encode asset paths defined as `?export_path` or `?unstable_path` query parameters. // Decoding should be done automatically when parsing the URL through Node or the browser. const assetPathQueryParameter = asset.httpServerLocation.match(/\?(export_path|unstable_path)=(.*)/); if (assetPathQueryParameter && assetPathQueryParameter[2]) { const assetPath = assetPathQueryParameter[2]; // @ts-expect-error: marked as read-only asset.httpServerLocation = asset.httpServerLocation.replace(assetPath, encodeURIComponent(assetPath)); } return asset; } async function getUniversalAssetData(assetPath, localPath, assetDataPlugins, platform, publicPath) { const metroAssetData = await (0, Assets_1.getAssetData)(assetPath, localPath, assetDataPlugins, platform, publicPath); const data = await ensureOtaAssetHashesAsync(metroAssetData); // NOTE(EvanBacon): This is where we modify the asset to include a hash in the name for web cache invalidation. if (platform === 'web' && publicPath.includes('?export_path=')) { // `local-image.[contenthash]`. Using `.` but this won't work if we ever apply to Android because Android res files cannot contain `.`. // TODO: Prevent one multi-res image from updating the hash in all images. // @ts-expect-error: name is typed as readonly. data.name = `${data.name}.${getMD5ForData(data.fileHashes)}`; } return data; } async function getAssets(dependencies, options) { const promises = []; const { processModuleFilter } = options; for (const module of dependencies.values()) { if ((0, js_js_1.isJsModule)(module) && processModuleFilter(module) && (0, js_js_1.getJsOutput)(module).type === 'js/module/asset' && node_path_1.default.relative(options.projectRoot, module.path) !== 'package.json') { promises.push(getUniversalAssetData(module.path, node_path_1.default.relative(options.projectRoot, module.path), options.assetPlugins, options.platform, options.publicPath)); } } return await Promise.all(promises); } //# sourceMappingURL=getAssets.js.map