UNPKG

expo-atlas

Version:

Visualize React Native bundles to understand and optimize your app

255 lines 12 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MetroGraphSource = void 0; exports.convertMetroConfig = convertMetroConfig; exports.convertGraph = convertGraph; exports.collectEntryPointModules = collectEntryPointModules; exports.convertModule = convertModule; exports.convertTransformOptions = convertTransformOptions; exports.convertSerializeOptions = convertSerializeOptions; var path_1 = __importDefault(require("path")); var buffer_1 = require("../utils/buffer"); var jsc_1 = require("../utils/jsc"); var package_1 = require("../utils/package"); var paths_1 = require("../utils/paths"); var MetroGraphSource = /** @class */ (function () { function MetroGraphSource() { /** All known entries, and detected changes, stored by ID */ this.entries = new Map(); this.serializeGraph = this.serializeGraph.bind(this); } MetroGraphSource.prototype.hasHmrSupport = function () { return true; }; MetroGraphSource.prototype.getBundleHmr = function (id) { var _a; // Get the required data from the bundle var bundle = this.getBundle(id); var bundleSourceUrl = (_a = bundle.serializeOptions) === null || _a === void 0 ? void 0 : _a.sourceUrl; if (!bundleSourceUrl) { return null; } // Construct the HMR information, based on React Native // See: https://github.com/facebook/react-native/blob/2eb7bcb8d9c0f239a13897e3a5d4397d81d3f627/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java#L696-L702 var socketUrl = new URL('/hot', bundleSourceUrl); // Fix the entry point URL query parameter to be compatible with the HMR server var entryPoint = (0, jsc_1.getUrlFromJscSafeUrl)(bundleSourceUrl); return { bundleId: bundle.id, socketUrl: socketUrl, entryPoints: [entryPoint], }; }; MetroGraphSource.prototype.listBundles = function () { return Array.from(this.entries.values()).map(function (bundle) { return ({ id: bundle.id, platform: bundle.platform, environment: bundle.environment, projectRoot: bundle.projectRoot, sharedRoot: bundle.sharedRoot, entryPoint: bundle.entryPoint, }); }); }; MetroGraphSource.prototype.getBundle = function (id) { var bundle = this.entries.get(id); if (!bundle) throw new Error("Bundle \"".concat(id, "\" not found.")); return bundle; }; /** * Serializes the Metro graph, converting it to an Atlas entry. * This also registers a listener to the Metro server to track changes, when possible. * All data is kept in memory, where stale data is overwritten by new data. */ MetroGraphSource.prototype.serializeGraph = function (options) { var bundle = convertGraph(options); this.entries.set(bundle.id, bundle); return bundle; }; return MetroGraphSource; }()); exports.MetroGraphSource = MetroGraphSource; /** Convert options from the Metro config, used during graph conversions to Atlas */ function convertMetroConfig(config) { var _a, _b; return { watchFolders: config.watchFolders, resolver: { sourceExts: (_a = config.resolver) === null || _a === void 0 ? void 0 : _a.sourceExts, assetExts: (_b = config.resolver) === null || _b === void 0 ? void 0 : _b.assetExts, }, }; } /** Convert a Metro graph instance to a JSON-serializable entry */ function convertGraph(options) { var _a, _b; var sharedRoot = getSharedRoot(options); var platform = (_a = getPlatform(options)) !== null && _a !== void 0 ? _a : 'unknown'; var environment = (_b = getEnvironment(options)) !== null && _b !== void 0 ? _b : 'client'; var serializeOptions = convertSerializeOptions(options); var transformOptions = convertTransformOptions(options); var entryPoint = getEntryPoint(options, environment); return { id: Buffer.from("".concat(path_1.default.relative(sharedRoot, entryPoint), "+").concat(platform, "+").concat(environment)).toString('base64url'), platform: platform, environment: environment, projectRoot: options.projectRoot, sharedRoot: sharedRoot, entryPoint: entryPoint, runtimeModules: options.preModules.map(function (module) { return convertModule(options, module, sharedRoot); }), modules: collectEntryPointModules(options, sharedRoot), serializeOptions: serializeOptions, transformOptions: transformOptions, }; } /** Find and collect all dependnecies related to the entrypoint within the graph */ function collectEntryPointModules(options, sharedRoot) { var modules = new Map(); /** Discover and collect all files related to the provided module path */ function discover(modulePath) { var module = options.graph.dependencies.get(modulePath); if (module && !modules.has(modulePath) && !moduleIsVirtual(module)) { modules.set(modulePath, convertModule(options, module, sharedRoot)); module.dependencies.forEach(function (modulePath) { return discover(modulePath.absolutePath); }); } } // Find and collect all modules related to the entry point discover(options.entryPoint); return modules; } /** Convert a Metro module to a JSON-serializable Atlas module */ function convertModule(options, module, sharedRoot) { var createModuleId = options.serializeOptions.createModuleId; return { id: createModuleId(module.path), absolutePath: module.path, relativePath: (0, paths_1.convertPathToPosix)(path_1.default.relative(sharedRoot, module.path)), package: (0, package_1.getPackageNameFromPath)(module.path), size: module.output.reduce(function (bytes, output) { return bytes + Buffer.byteLength(output.data.code); }, 0), imports: Array.from(module.dependencies.values()).map(function (module) { return ({ id: createModuleId(module.absolutePath), absolutePath: module.absolutePath, relativePath: (0, paths_1.convertPathToPosix)(path_1.default.relative(sharedRoot, module.absolutePath)), package: (0, package_1.getPackageNameFromPath)(module.absolutePath), }); }), importedBy: Array.from(module.inverseDependencies) .filter(function (path) { return options.graph.dependencies.has(path); }) .map(function (absolutePath) { return ({ id: createModuleId(absolutePath), absolutePath: absolutePath, relativePath: (0, paths_1.convertPathToPosix)(path_1.default.relative(sharedRoot, absolutePath)), // TODO package: (0, package_1.getPackageNameFromPath)(absolutePath), }); }), source: getModuleSourceContent(options, module), output: module.output.map(function (output) { return ({ type: output.type, data: { code: output.data.code }, }); }), }; } /** * Attempt to load the source file content from module. * If a file is an asset, it returns `[binary file]` instead. */ function getModuleSourceContent(options, module) { var _a, _b, _c, _d; var fileExtension = path_1.default.extname(module.path).replace('.', ''); if ((_b = (_a = options.metroConfig.resolver) === null || _a === void 0 ? void 0 : _a.sourceExts) === null || _b === void 0 ? void 0 : _b.includes(fileExtension)) { return module.getSource().toString(); } if ((_d = (_c = options.metroConfig.resolver) === null || _c === void 0 ? void 0 : _c.assetExts) === null || _d === void 0 ? void 0 : _d.includes(fileExtension)) { return '[binary file]'; } if (module.path.includes('?ctx=')) { return module.getSource().toString(); } if ((0, buffer_1.bufferIsUtf8)(module.getSource())) { return module.getSource().toString(); } return '[binary file]'; } /** Convert Metro transform options to a JSON-serializable object */ function convertTransformOptions(options) { var _a; return (_a = options.graph.transformOptions) !== null && _a !== void 0 ? _a : {}; } /** Convert Metro serialize options to a JSON-serializable object */ function convertSerializeOptions(options) { var serializeOptions = __assign({}, options.serializeOptions); // Delete all non-serializable functions delete serializeOptions['processModuleFilter']; delete serializeOptions['createModuleId']; delete serializeOptions['getRunModuleStatement']; delete serializeOptions['shouldAddToIgnoreList']; return serializeOptions; } /** Get the shared root of `projectRoot` and `watchFolders`, used to make all paths within the bundle relative */ function getSharedRoot(options) { var _a; var watchFolders = options.metroConfig.watchFolders; return !(watchFolders === null || watchFolders === void 0 ? void 0 : watchFolders.length) ? options.projectRoot : ((_a = (0, paths_1.findSharedRoot)(__spreadArray([options.projectRoot], watchFolders, true))) !== null && _a !== void 0 ? _a : options.projectRoot); } /** Determine if the module is a virtual module, like shims or canaries, which should be excluded from results */ function moduleIsVirtual(module) { return module.path.startsWith('\0'); } /** Determine the bundle target environment based on the `transformOptions.customTransformOptions` */ function getEnvironment(options) { var _a, _b, _c; var environment = (_b = (_a = options.graph.transformOptions) === null || _a === void 0 ? void 0 : _a.customTransformOptions) === null || _b === void 0 ? void 0 : _b.environment; // Check if this graph is related to a DOM component // NOTE(cedric): this is early/alpha support and may change in the future if (options.graph.transformOptions.platform === 'web' && !!((_c = options.graph.transformOptions.customTransformOptions) === null || _c === void 0 ? void 0 : _c.dom)) { return 'dom'; } if (typeof environment === 'string') { return environment; } return null; } /** Determine the bundle target platform based on the `transformOptions` */ function getPlatform(options) { var _a; var platform = (_a = options.graph.transformOptions) === null || _a === void 0 ? void 0 : _a.platform; if (typeof platform === 'string') { return platform; } return null; } /** * Determine if this graph is related to a DOM component, using the (custom) transform options. * @remarks - This is preliminary support, and may change in the future */ function getEntryPoint(options, environment) { if (environment === void 0) { environment = 'client'; } return environment !== 'dom' ? options.entryPoint : path_1.default.join(path_1.default.dirname(options.entryPoint), options.graph.transformOptions.customTransformOptions.dom); } //# sourceMappingURL=MetroGraphSource.js.map