expo-atlas
Version:
Inspect bundle contents, on module level, from Metro
295 lines • 15 kB
JavaScript
;
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