UNPKG

expo-atlas

Version:

Inspect bundle contents, on module level, from Metro

295 lines 15 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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; 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.convertSerializeOptions = exports.convertTransformOptions = exports.convertModule = exports.collectEntryPointModules = exports.convertGraph = exports.convertMetroConfig = exports.MetroGraphSource = void 0; var path_1 = __importDefault(require("path")); var buffer_1 = require("../utils/buffer"); var package_1 = require("../utils/package"); var paths_1 = require("../utils/paths"); var MetroGraphSource = /** @class */ (function () { function MetroGraphSource() { /** The Metro delta listener, instantiated when the Metro server is registered */ this.deltaListener = null; /** All known entries, and detected changes, stored by ID */ this.entries = new Map(); this.serializeGraph = this.serializeGraph.bind(this); } MetroGraphSource.prototype.listBundles = function () { return Array.from(this.entries.values()).map(function (item) { return ({ id: item.entry.id, platform: item.entry.platform, projectRoot: item.entry.projectRoot, sharedRoot: item.entry.sharedRoot, entryPoint: item.entry.entryPoint, }); }); }; MetroGraphSource.prototype.getBundle = function (id) { var item = this.entries.get(id); if (!item) throw new Error("Entry \"".concat(id, "\" not found.")); return item.entry; }; MetroGraphSource.prototype.getBundleDelta = function (id) { var _a; return ((_a = this.entries.get(id)) === null || _a === void 0 ? void 0 : _a.delta) || null; }; MetroGraphSource.prototype.bundleDeltaEnabled = function () { return !!this.deltaListener; }; /** * 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 _a; var entry = convertGraph(options); this.entries.set(entry.id, { entry: entry }); (_a = this.deltaListener) === null || _a === void 0 ? void 0 : _a.registerGraph(entry.id, options.graph); return entry; }; /** * Register the Metro server to listen for changes in serialized graphs. * Once changes are detected, the delta is generated and stored with the entry. * Changes allows the client to know when to refetch data. */ MetroGraphSource.prototype.registerMetro = function (metro) { if (!this.deltaListener) { this.deltaListener = new MetroDeltaListener(this, metro); } }; return MetroGraphSource; }()); exports.MetroGraphSource = MetroGraphSource; var MetroDeltaListener = /** @class */ (function () { function MetroDeltaListener(source, metro) { this.listeners = new Map(); this.source = source; this.bundler = metro.getBundler().getDeltaBundler(); } MetroDeltaListener.prototype.registerGraph = function (entryId, graph) { var _this = this; // Unregister the previous listener, to always have the most up-to-date graph if (this.listeners.has(entryId)) { this.listeners.get(entryId)(); } // Register the (new) delta listener this.listeners.set(entryId, this.bundler.listen(graph, function () { return __awaiter(_this, void 0, void 0, function () { var createdAt; var _this = this; return __generator(this, function (_a) { createdAt = new Date(); this.bundler .getDelta(graph, { reset: false, shallow: true }) .then(function (delta) { return _this.onMetroChange(entryId, delta, createdAt); }); return [2 /*return*/]; }); }); })); }; /** * Event handler invoked when a change is detected by Metro, using the DeltaBundler. * The detected change is combined with the Atlas entry ID, and updates the source entry with the delta. */ MetroDeltaListener.prototype.onMetroChange = function (entryId, delta, createdAt) { var item = this.source.entries.get(entryId); var hasChanges = (delta.added.size || delta.modified.size || delta.deleted.size) > 0; if (item && hasChanges) { item.delta = { createdAt: createdAt, modifiedPaths: Array.from(delta.added.keys()).concat(Array.from(delta.modified.keys())), deletedPaths: Array.from(delta.deleted), }; } }; return MetroDeltaListener; }()); /** 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, }, }; } exports.convertMetroConfig = convertMetroConfig; /** Convert a Metro graph instance to a JSON-serializable entry */ function convertGraph(options) { var _a, _b; var sharedRoot = getSharedRoot(options); var serializeOptions = convertSerializeOptions(options); var transformOptions = convertTransformOptions(options); var platform = ((_a = transformOptions === null || transformOptions === void 0 ? void 0 : transformOptions.customTransformOptions) === null || _a === void 0 ? void 0 : _a.environment) === 'node' ? 'server' : (_b = transformOptions === null || transformOptions === void 0 ? void 0 : transformOptions.platform) !== null && _b !== void 0 ? _b : 'unknown'; return { id: Buffer.from("".concat(path_1.default.relative(sharedRoot, options.entryPoint), "+").concat(platform)).toString('base64url'), // FIX: only use URL allowed characters platform: platform, projectRoot: options.projectRoot, sharedRoot: sharedRoot, entryPoint: options.entryPoint, runtimeModules: options.preModules.map(function (module) { return convertModule(options, module, sharedRoot); }), modules: collectEntryPointModules(options, sharedRoot), serializeOptions: serializeOptions, transformOptions: transformOptions, }; } exports.convertGraph = convertGraph; /** 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; } exports.collectEntryPointModules = collectEntryPointModules; /** 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 }, }); }), }; } exports.convertModule = convertModule; /** * 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 : {}; } exports.convertTransformOptions = convertTransformOptions; /** 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; } exports.convertSerializeOptions = convertSerializeOptions; /** 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'); } //# sourceMappingURL=MetroGraphSource.js.map