From cafaeaf62a887fb7a388a4ef1895716ee09a68a2 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Tue, 13 Dec 2022 18:57:43 +0100 Subject: [PATCH] parcel-query tool (#8478) --- packages/dev/query/README.md | 42 ++ packages/dev/query/package.json | 17 + packages/dev/query/src/bin.js | 13 + packages/dev/query/src/cli.js | 688 +++++++++++++++++++++++++ packages/dev/query/src/deep-imports.js | 33 ++ packages/dev/query/src/index.js | 128 +++++ 6 files changed, 921 insertions(+) create mode 100644 packages/dev/query/README.md create mode 100644 packages/dev/query/package.json create mode 100755 packages/dev/query/src/bin.js create mode 100644 packages/dev/query/src/cli.js create mode 100644 packages/dev/query/src/deep-imports.js create mode 100644 packages/dev/query/src/index.js diff --git a/packages/dev/query/README.md b/packages/dev/query/README.md new file mode 100644 index 00000000000..f7dda2611c7 --- /dev/null +++ b/packages/dev/query/README.md @@ -0,0 +1,42 @@ +# `parcel-query` + +A REPL to investigate the Parcel graphs in the cache ("offline", after the build). + +## Installation + +Clone and run `yarn`, then `cd packages/dev/query && yarn link` to make the `parcel-query` binary +globally available. + +## Usage + +Call `.help` to view a list of commands. + +In a project root containing a `.parcel-cache` folder: + +```sh +$ parcel-query +> .findBundleReason 27494aebac508bd8 TJbGI +# Asset is main entry of bundle: false +# Asset is an entry of bundle: false +# Incoming dependencies contained in the bundle: +{ + id: '3fd182662e2a6fa9', + type: 'dependency', + value: { + specifier: '../../foo', + specifierType: 0, +... +``` + +In the `.getAsset 3Xzt5` variant, no quotes/parenthesis are +needed. Alternatively, you can use proper JS: `getAsset("3Xzt5")`. + +The variables `assetGraph` and `bundleGraph` contain the deserialized objects. + +For a single query, the command (which has to be a JS call) can be specified as a CLI parameter (the +disadvantage here is that the graphs have to be loaded on every call as opposed to once when +starting the REPL): + +```sh +$ parcel-query 'findBundleReason("b0696febf20b57ce", "bNAUZ")' +``` diff --git a/packages/dev/query/package.json b/packages/dev/query/package.json new file mode 100644 index 00000000000..019a2aae7e3 --- /dev/null +++ b/packages/dev/query/package.json @@ -0,0 +1,17 @@ +{ + "name": "parcel-query", + "version": "2.8.1", + "private": true, + "bin": "src/bin.js", + "main": "src/index.js", + "dependencies": { + "@parcel/core": "2.8.1", + "@parcel/graph": "2.8.1", + "nullthrows": "^1.1.1", + "v8-compile-cache": "^2.0.0" + }, + "devDependencies": { + "@babel/core": "^7.0.0", + "@parcel/babel-register": "2.8.1" + } +} diff --git a/packages/dev/query/src/bin.js b/packages/dev/query/src/bin.js new file mode 100755 index 00000000000..e7b33128252 --- /dev/null +++ b/packages/dev/query/src/bin.js @@ -0,0 +1,13 @@ +#!/usr/bin/env node + +'use strict'; + +if ( + process.env.PARCEL_BUILD_ENV !== 'production' || + process.env.PARCEL_SELF_BUILD +) { + require('@parcel/babel-register'); +} + +require('v8-compile-cache'); +require('./cli'); diff --git a/packages/dev/query/src/cli.js b/packages/dev/query/src/cli.js new file mode 100644 index 00000000000..a759ce4af1f --- /dev/null +++ b/packages/dev/query/src/cli.js @@ -0,0 +1,688 @@ +// @flow strict-local +/* eslint-disable no-console, monorepo/no-internal-import */ +import type {ContentGraph, ContentKey, NodeId} from '@parcel/graph'; +import type {AssetGraphNode, BundleGraphNode} from '@parcel/core/src/types'; +import type {BundleGraphEdgeType} from '@parcel/core/src/BundleGraph.js'; + +import path from 'path'; +import fs from 'fs'; +import repl from 'repl'; +import os from 'os'; +import nullthrows from 'nullthrows'; +import invariant from 'assert'; + +import {fromProjectPathRelative} from '@parcel/core/src/projectPath'; +import {bundleGraphEdgeTypes} from '@parcel/core/src/BundleGraph.js'; +import {Priority} from '@parcel/core/src/types'; + +import {loadGraphs} from './index.js'; + +let args = process.argv.slice(2); +let cacheDir = path.join(process.cwd(), '.parcel-cache'); +if (args[0] === '--cache') { + cacheDir = path.resolve(process.cwd(), args[1]); + args = args.slice(2); +} +let initialCmd = args[0]; + +try { + fs.accessSync(cacheDir); +} catch (e) { + console.error("Can't find cache dir", cacheDir); + process.exit(1); +} + +console.log('Loading graphs...'); +let {assetGraph, bundleGraph, bundleInfo, requestTracker} = + loadGraphs(cacheDir); + +if (requestTracker == null) { + console.error('Request Graph could not be found'); + process.exit(1); + throw new Error(); +} + +if (bundleGraph == null) { + console.error('Bundle Graph could not be found'); + process.exit(1); + throw new Error(); +} + +if (assetGraph == null) { + console.error('Asset Graph could not be found'); + process.exit(1); + throw new Error(); +} + +if (bundleInfo == null) { + console.error('Bundle Info could not be found'); + process.exit(1); + throw new Error(); +} + +// ------------------------------------------------------- + +function getBundleFilePath(id: ContentKey) { + return fromProjectPathRelative(nullthrows(bundleInfo.get(id)?.filePath)); +} + +function parseAssetLocator(v: string) { + let id: ?string = null; + if (v.length === 16) { + id = v; + } else { + for (let [assetId, publicId] of bundleGraph._publicIdByAssetId) { + if (publicId === v) { + id = assetId; + break; + } + } + } + + if (id == null && v.length > 0) { + let assetRegex = new RegExp(v); + for (let node of assetGraph.nodes.values()) { + if ( + node.type === 'asset' && + assetRegex.test(fromProjectPathRelative(node.value.filePath)) + ) { + id = node.id; + break; + } + } + } + return id; +} + +function parseBundleLocator(v: string) { + let bundleRegex = new RegExp(v); + for (let b of bundleGraph.getBundles()) { + if (bundleRegex.test(getBundleFilePath(b.id)) || b.id === v) { + return b.id; + } + } +} + +function getAsset(v: string) { + let id: ?string = parseAssetLocator(v); + + if (id == null) { + console.log(null); + } else { + try { + let asset = bundleGraph.getAssetById(id); + console.log('Public id', bundleGraph.getAssetPublicId(asset)); + console.log(asset); + } catch (e) { + let node = nullthrows(assetGraph.getNodeByContentKey(id)); + invariant(node.type === 'asset'); + console.log(node.value); + } + } +} + +function findAsset(v: string) { + let assetRegex = new RegExp(v); + for (let node of assetGraph.nodes.values()) { + if ( + node.type === 'asset' && + assetRegex.test(fromProjectPathRelative(node.value.filePath)) + ) { + try { + console.log( + `${bundleGraph.getAssetPublicId( + bundleGraph.getAssetById(node.id), + )} ${fromProjectPathRelative(node.value.filePath)}`, + ); + } catch (e) { + console.log(fromProjectPathRelative(node.value.filePath)); + } + } + } +} + +function getNodeAssetGraph(v: string) { + console.log(assetGraph.getNodeByContentKey(v)); +} +function getNodeBundleGraph(v: string) { + console.log(bundleGraph._graph.getNodeByContentKey(v)); +} + +class Paths { + value: T; + label: string; + suffix: string; + children: Array> = []; + constructor(value: T, label = '-', suffix = '') { + this.value = value; + this.label = label; + this.suffix = suffix; + } + add(v: T, label: string | void, suffix: string | void): Paths { + let next = new Paths(v, label, suffix); + this.children.push(next); + return next; + } + print(format: T => string, prefix = '') { + console.log(`${prefix}${this.label} ${format(this.value)} ${this.suffix}`); + for (let i = 0; i < this.children.length; i++) { + this.children[i].print(format, prefix + ' '); + } + } +} + +function _findEntries( + graph: + | ContentGraph + | ContentGraph, + v: string, +) { + let asset = nullthrows(parseAssetLocator(v), 'Asset not found'); + + let paths = new Paths(graph.getNodeIdByContentKey(asset), ' '); + let cb = (id, ctx, revisiting) => { + let {paths, lazyOutgoing} = ctx; + let node = nullthrows(graph.getNode(id)); + if (node.id === asset) return ctx; + if (node.type === 'asset') { + paths = paths.add( + id, + lazyOutgoing ? '<' : undefined, + revisiting ? '(revisiting)' : undefined, + ); + lazyOutgoing = false; + } else if (node.type === 'dependency') { + if (node.value.priority === Priority.lazy) { + lazyOutgoing = true; + } + } + return {paths, lazyOutgoing}; + }; + + // like graph.dfs, but revisiting nodes and skipping its children + let seen = new Set(); + function walk(id, ctx) { + let revisiting = seen.has(id); + let newCtx = cb(id, ctx, revisiting); + if (revisiting) return; + seen.add(id); + + for (let parent of graph.getNodeIdsConnectedTo(id)) { + walk(parent, newCtx); + } + } + walk(graph.getNodeIdByContentKey(asset), {paths, lazyOutgoing: false}); + + paths.print(id => { + let node = nullthrows(graph.getNode(id)); + invariant(node.type === 'asset'); + return fromProjectPathRelative(node.value.filePath); + }); +} + +function findEntriesAssetGraph(v: string) { + _findEntries(assetGraph, v); +} +function findEntriesBundleGraph(v: string) { + _findEntries(bundleGraph._graph, v); +} +function findEntries(v: string) { + findEntriesBundleGraph(v); +} + +function getBundlesWithAsset(v: string) { + let asset = nullthrows(parseAssetLocator(v), 'Asset not found'); + + for (let b of bundleGraph.getBundlesWithAsset( + bundleGraph.getAssetById(asset), + )) { + console.log( + `${b.id} ${getBundleFilePath(b.id)} ${ + b.mainEntryId != null ? `(main: ${b.mainEntryId})` : '' + }`, + ); + } +} + +function getBundlesWithDependency(v: string) { + let node = nullthrows(bundleGraph._graph.getNodeByContentKey(v)); + invariant(node.type === 'dependency'); + + for (let b of bundleGraph.getBundlesWithDependency(node.value)) { + console.log( + `${b.id} ${getBundleFilePath(b.id)} ${ + b.mainEntryId != null ? `(main: ${b.mainEntryId})` : '' + }`, + ); + } +} + +// eslint-disable-next-line no-unused-vars +function getBundles(_) { + for (let b of bundleGraph.getBundles()) { + console.log( + `${b.id} ${getBundleFilePath(b.id)} ${ + b.mainEntryId != null ? `(main: ${b.mainEntryId})` : '' + }`, + ); + } +} + +function getReferencingBundles(v: string) { + let bundleId = nullthrows(parseBundleLocator(v), 'Bundle not found'); + let bundleNodeId = bundleGraph._graph.getNodeIdByContentKey(bundleId); + let bundleNode = nullthrows( + bundleGraph._graph.getNode(bundleNodeId), + 'Bundle not found', + ); + invariant(bundleNode.type === 'bundle', 'Not a bundle'); + + for (let b of bundleGraph.getReferencingBundles(bundleNode.value)) { + console.log( + `${b.id} ${getBundleFilePath(b.id)} ${ + b.mainEntryId != null ? `(main: ${b.mainEntryId})` : '' + }`, + ); + } +} + +function getIncomingDependenciesAssetGraph(v: string) { + let asset = nullthrows(parseAssetLocator(v), 'Asset not found'); + let node = nullthrows(assetGraph.getNodeByContentKey(asset)); + invariant(node.type === 'asset'); + + console.log(assetGraph.getIncomingDependencies(node.value)); +} +function getIncomingDependenciesBundleGraph(v: string) { + let asset = nullthrows(parseAssetLocator(v), 'Asset not found'); + let value = nullthrows(bundleGraph.getAssetById(asset)); + + console.log(bundleGraph.getIncomingDependencies(value)); +} + +function getIncomingDependencies(v: string) { + getIncomingDependenciesBundleGraph(v); +} + +function getResolvedAsset(v: string) { + let node = nullthrows( + bundleGraph._graph.getNodeByContentKey(v), + 'Dependency not found', + ); + invariant( + node.type === 'dependency', + 'Node is not a dependency, but a ' + node.type, + ); + console.log(bundleGraph.getResolvedAsset(node.value)); +} + +function getAssetWithDependency(v: string) { + let node = nullthrows( + bundleGraph._graph.getNodeByContentKey(v), + 'Dependency not found', + ); + invariant( + node.type === 'dependency', + 'Node is not a dependency, but a ' + node.type, + ); + console.log(bundleGraph.getAssetWithDependency(node.value)); +} + +function traverseAssets(v: string) { + let bundleId = nullthrows(parseBundleLocator(v), 'Bundle not found'); + let node = nullthrows( + bundleGraph._graph.getNodeByContentKey(bundleId), + 'Bundle not found', + ); + invariant(node.type === 'bundle', 'Node is not a bundle, but a ' + node.type); + + bundleGraph.traverseAssets(node.value, asset => { + console.log(asset.id, asset.filePath); + }); +} +function traverseBundle(v: string) { + let bundleId = nullthrows(parseBundleLocator(v), 'Bundle not found'); + let node = nullthrows( + bundleGraph._graph.getNodeByContentKey(bundleId), + 'Bundle not found', + ); + invariant(node.type === 'bundle', 'Node is not a bundle, but a ' + node.type); + + bundleGraph.traverseBundle(node.value, node => { + if (node.type === 'asset') { + console.log(node.id, node.value.filePath); + } else { + console.log( + node.id, + node.value.sourcePath, + '->', + node.value.specifier, + node.value.symbols + ? `(${[...node.value.symbols.keys()].join(',')})` + : '', + node.excluded ? `- excluded` : '', + ); + } + }); +} + +function getBundle(v: string) { + let bundleRegex = new RegExp(v); + for (let b of bundleGraph.getBundles()) { + if (bundleRegex.test(getBundleFilePath(b.id)) || b.id === v) { + console.log(getBundleFilePath(b.id), b); + } + } +} + +function findBundleReason(bundle: string, asset: string) { + let bundleId = nullthrows(parseBundleLocator(bundle), 'Bundle not found'); + let bundleNodeId = bundleGraph._graph.getNodeIdByContentKey(bundleId); + let bundleNode = nullthrows( + bundleGraph._graph.getNode(bundleNodeId), + 'Bundle not found', + ); + invariant(bundleNode.type === 'bundle', 'Not a bundle'); + let assetId = nullthrows(parseAssetLocator(asset), 'Asset not found'); + let assetNodeId = bundleGraph._graph.getNodeIdByContentKey(assetId); + let assetNode = nullthrows( + bundleGraph._graph.getNode(assetNodeId), + 'Asset not found', + ); + invariant(assetNode.type === 'asset', 'Not an asset'); + + invariant( + bundleGraph._graph.hasEdge( + bundleNodeId, + assetNodeId, + bundleGraphEdgeTypes.contains, + ), + 'Asset is not part of the bundle', + ); + + console.log( + '# Asset is main entry of bundle:', + bundleNode.value.mainEntryId === assetId, + ); + + console.log( + '# Asset is an entry of bundle:', + bundleNode.value.entryAssetIds.includes(assetId), + ); + + console.log('# Incoming dependencies contained in the bundle:'); + for (let incoming of bundleGraph._graph.getNodeIdsConnectedTo(assetNodeId)) { + if ( + bundleGraph._graph.hasEdge( + bundleNodeId, + incoming, + bundleGraphEdgeTypes.contains, + ) + ) { + console.log(bundleGraph._graph.getNode(incoming)); + } + } + + console.log( + '# Incoming dependencies contained in referencing bundles (using this bundle as a shared bundle)', + ); + let referencingBundles = bundleGraph.getReferencingBundles(bundleNode.value); + for (let incoming of bundleGraph._graph.getNodeIdsConnectedTo(assetNodeId)) { + if ( + referencingBundles.some(ref => + bundleGraph._graph.hasEdge( + bundleGraph._graph.getNodeIdByContentKey(ref.id), + incoming, + bundleGraphEdgeTypes.contains, + ), + ) + ) { + console.log(bundleGraph._graph.getNode(incoming)); + } + } +} + +// eslint-disable-next-line no-unused-vars +function stats(_) { + let ag = { + asset: 0, + dependency: 0, + asset_group: 0, + }; + + for (let [, n] of assetGraph.nodes) { + if (n.type in ag) { + // $FlowFixMe + ag[n.type]++; + } + } + + let bg = { + dependency: 0, + bundle: 0, + asset: 0, + asset_node_modules: 0, + asset_source: 0, + }; + for (let [, n] of bundleGraph._graph.nodes) { + if (n.type in bg) { + // $FlowFixMe + bg[n.type]++; + } + if (n.type === 'asset') { + if (fromProjectPathRelative(n.value.filePath).includes('node_modules')) { + bg.asset_node_modules++; + } else { + bg.asset_source++; + } + } + } + + console.log('# Asset Graph Node Counts'); + for (let k in ag) { + console.log(k, ag[k]); + } + console.log(); + + console.log('# Bundle Graph Node Counts'); + for (let k in bg) { + console.log(k, bg[k]); + } +} + +// ------------------------------------------------------- + +if (initialCmd != null) { + eval(initialCmd); + process.exit(0); +} else { + console.log( + 'See .help. The graphs can be accessed via `assetGraph`, `bundleGraph` and `requestTracker`.', + ); + process.on('uncaughtException', function (err) { + console.error(err); + server.displayPrompt(); + }); + + const server = repl.start({useColors: true, useGlobal: true}); + // $FlowFixMe[prop-missing] + server.setupHistory( + path.join(os.homedir(), '.parcel_query_history'), + () => {}, + ); + + // $FlowFixMe[prop-missing] + server.context.bundleGraph = bundleGraph; + // $FlowFixMe[prop-missing] + server.context.assetGraph = assetGraph; + // $FlowFixMe[prop-missing] + server.context.requestTracker = requestTracker; + + for (let [name, cmd] of new Map([ + [ + 'getAsset', + { + help: 'args: ', + action: getAsset, + }, + ], + [ + 'getNodeAssetGraph', + { + help: 'args: . Find node by content key in the asset graph', + action: getNodeAssetGraph, + }, + ], + [ + 'getNodeBundleGraph', + { + help: 'args: . Find node by content key in the bundle graph', + action: getNodeBundleGraph, + }, + ], + [ + 'findEntriesAssetGraph', + { + help: 'args: . List paths from an asset to entry points (in asset graph)', + action: findEntriesAssetGraph, + }, + ], + [ + 'findEntriesBundleGraph', + { + help: 'args: . List paths from an asset to entry points (in bundle graph)', + action: findEntriesBundleGraph, + }, + ], + [ + 'findEntries', + { + help: '= findEntriesBundleGraph', + action: findEntries, + }, + ], + [ + 'getBundlesWithAsset', + { + help: 'args: . Gets bundles containing the asset', + action: getBundlesWithAsset, + }, + ], + [ + 'getBundlesWithDependency', + { + help: 'args: . Gets bundles containing the dependency', + action: getBundlesWithDependency, + }, + ], + [ + 'getIncomingDependenciesAssetGraph', + { + help: 'args: ', + action: getIncomingDependenciesAssetGraph, + }, + ], + [ + 'getIncomingDependenciesBundleGraph', + { + help: 'args: ', + action: getIncomingDependenciesBundleGraph, + }, + ], + [ + 'getIncomingDependencies', + { + help: '= getIncomingDependenciesBundleGraph', + action: getIncomingDependencies, + }, + ], + [ + 'getResolvedAsset', + { + help: 'args: . Resolve the dependency', + action: getResolvedAsset, + }, + ], + [ + 'getAssetWithDependency', + { + help: 'args: . Show which asset created the dependency', + action: getAssetWithDependency, + }, + ], + [ + 'traverseAssets', + { + help: 'args: . List assets in bundle', + action: traverseAssets, + }, + ], + [ + 'traverseBundle', + { + help: 'args: . List assets and dependencies in bundle', + action: traverseBundle, + }, + ], + [ + 'getBundle', + { + help: 'args: . List matching bundles', + action: getBundle, + }, + ], + [ + 'findBundleReason', + { + help: 'args: . Why is the asset in the bundle', + action: v => findBundleReason(...v.split(' ')), + }, + ], + [ + 'getBundles', + { + help: 'List all bundles', + action: getBundles, + }, + ], + [ + 'getReferencingBundles', + { + help: 'args: . List bundles that reference the bundle', + action: getReferencingBundles, + }, + ], + [ + 'stats', + { + help: 'Statistics', + action: stats, + }, + ], + [ + 'findAsset', + { + help: 'args: . Lsit assets matching the filepath regex', + action: findAsset, + }, + ], + ])) { + // $FlowFixMe + server.context[name] = cmd.action; + // $FlowFixMe + server.defineCommand(name, { + // $FlowFixMe + help: '📦 ' + cmd.help, + action: v => { + // $FlowFixMe + server.clearBufferedCommand(); + // $FlowFixMe + try { + cmd.action(v); + } finally { + server.displayPrompt(); + } + }, + }); + } +} diff --git a/packages/dev/query/src/deep-imports.js b/packages/dev/query/src/deep-imports.js new file mode 100644 index 00000000000..1c064db2ccc --- /dev/null +++ b/packages/dev/query/src/deep-imports.js @@ -0,0 +1,33 @@ +// @flow +/* eslint-disable monorepo/no-internal-import */ +import typeof AssetGraph from '@parcel/core/src/AssetGraph.js'; +import typeof BundleGraph from '@parcel/core/src/BundleGraph.js'; +import typeof RequestTracker, { + RequestGraph, +} from '@parcel/core/src/RequestTracker.js'; +import {typeof requestGraphEdgeTypes} from '@parcel/core/src/RequestTracker.js'; + +module.exports = ((process.env.PARCEL_BUILD_ENV === 'production' + ? { + // Split up require specifier to outsmart packages/dev/babel-register/babel-plugin-module-translate.js + // $FlowFixMe(unsupported-syntax) + AssetGraph: require('@parcel/core' + '/lib/AssetGraph.js').default, + // $FlowFixMe(unsupported-syntax) + BundleGraph: require('@parcel/core' + '/lib/BundleGraph.js').default, + // $FlowFixMe(unsupported-syntax) + RequestTracker: require('@parcel/core' + '/lib/RequestTracker.js'), + } + : { + AssetGraph: require('@parcel/core/src/AssetGraph.js').default, + BundleGraph: require('@parcel/core/src/BundleGraph.js').default, + RequestTracker: require('@parcel/core/src/RequestTracker.js'), + }): {| + AssetGraph: AssetGraph, + BundleGraph: BundleGraph, + RequestTracker: { + default: RequestTracker, + RequestGraph: RequestGraph, + requestGraphEdgeTypes: requestGraphEdgeTypes, + ... + }, +|}); diff --git a/packages/dev/query/src/index.js b/packages/dev/query/src/index.js new file mode 100644 index 00000000000..78b00417c17 --- /dev/null +++ b/packages/dev/query/src/index.js @@ -0,0 +1,128 @@ +// @flow strict-local +/* eslint-disable monorepo/no-internal-import */ +import type {ContentKey, NodeId} from '@parcel/graph'; +import type {PackagedBundleInfo} from '@parcel/core/src/types'; + +import fs from 'fs'; +import path from 'path'; +import v8 from 'v8'; +import nullthrows from 'nullthrows'; +import invariant from 'assert'; + +const { + AssetGraph, + BundleGraph, + RequestTracker: { + default: RequestTracker, + RequestGraph, + requestGraphEdgeTypes, + }, +} = require('./deep-imports.js'); + +export function loadGraphs(cacheDir: string): {| + assetGraph: ?AssetGraph, + bundleGraph: ?BundleGraph, + requestTracker: ?RequestTracker, + bundleInfo: ?Map, +|} { + function filesBySizeAndModifiedTime() { + let files = fs.readdirSync(cacheDir).map(f => { + let stat = fs.statSync(path.join(cacheDir, f)); + return [path.join(cacheDir, f), stat.size, stat.mtime]; + }); + + files.sort(([, a], [, b]) => b - a); + files.sort(([, , a], [, , b]) => b - a); + + return files.map(([f]) => f); + } + + let requestTracker; + for (let f of filesBySizeAndModifiedTime()) { + // if (bundleGraph && assetGraph && requestTracker) break; + if (path.extname(f) !== '') continue; + try { + let obj = v8.deserialize(fs.readFileSync(f)); + /* if (obj.assetGraph != null && obj.assetGraph.value.hash != null) { + assetGraph = AssetGraph.deserialize(obj.assetGraph.value); + } else if (obj.bundleGraph != null) { + bundleGraph = BundleGraph.deserialize(obj.bundleGraph.value); + } else */ + if (obj['$$type']?.endsWith('RequestGraph')) { + requestTracker = new RequestTracker({ + graph: RequestGraph.deserialize(obj.value), + // $FlowFixMe + farm: null, + // $FlowFixMe + options: null, + }); + break; + } + } catch (e) { + // noop + } + } + + function getSubRequests(id: NodeId) { + return requestTracker.graph + .getNodeIdsConnectedFrom(id, requestGraphEdgeTypes.subrequest) + .map(n => nullthrows(requestTracker.graph.getNode(n))); + } + + // Load graphs by finding the main subrequests and loading their results + let assetGraph, bundleGraph, bundleInfo; + + invariant(requestTracker); + let buildRequestId = requestTracker.graph.getNodeIdByContentKey( + 'parcel_build_request', + ); + let buildRequestNode = nullthrows( + requestTracker.graph.getNode(buildRequestId), + ); + invariant( + buildRequestNode.type === 'request' && + buildRequestNode.value.type === 'parcel_build_request', + ); + let buildRequestSubRequests = getSubRequests(buildRequestId); + + let bundleGraphRequestNode = buildRequestSubRequests.find( + n => n.type === 'request' && n.value.type === 'bundle_graph_request', + ); + if (bundleGraphRequestNode != null) { + bundleGraph = BundleGraph.deserialize( + loadLargeBlobRequestRequestSync(cacheDir, bundleGraphRequestNode) + .bundleGraph.value, + ); + + let assetGraphRequest = getSubRequests( + requestTracker.graph.getNodeIdByContentKey(bundleGraphRequestNode.id), + ).find(n => n.type === 'request' && n.value.type === 'asset_graph_request'); + if (assetGraphRequest != null) { + assetGraph = AssetGraph.deserialize( + loadLargeBlobRequestRequestSync(cacheDir, assetGraphRequest).assetGraph + .value, + ); + } + } + + let writeBundlesRequest = buildRequestSubRequests.find( + n => n.type === 'request' && n.value.type === 'write_bundles_request', + ); + if (writeBundlesRequest != null) { + invariant(writeBundlesRequest.type === 'request'); + // $FlowFixMe[incompatible-cast] + bundleInfo = (nullthrows(writeBundlesRequest.value.result): Map< + ContentKey, + PackagedBundleInfo, + >); + } + + return {assetGraph, bundleGraph, requestTracker, bundleInfo}; +} + +function loadLargeBlobRequestRequestSync(cacheDir, node) { + invariant(node.type === 'request'); + return v8.deserialize( + fs.readFileSync(path.join(cacheDir, nullthrows(node.value.resultCacheKey))), + ); +}