expo-atlas
Version:
Visualize React Native bundles to understand and optimize your app
255 lines • 12 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 __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