Skip to content

Commit

Permalink
Add types for BundleGraph nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
wbinnssmith committed Mar 12, 2019
1 parent f1220a9 commit ab9366d
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 72 deletions.
1 change: 1 addition & 0 deletions packages/bundlers/default/src/DefaultBundler.js
Expand Up @@ -166,6 +166,7 @@ export default new Bundler({
const dumpGraphToGraphViz = require('@parcel/utils/src/dumpGraphToGraphViz')
.default;
dumpGraphToGraphViz(assetGraph, 'BundlerInputAssetGraph');
// $FlowFixMe
dumpGraphToGraphViz(bundleGraph, 'BundleGraph');
bundleGraph.traverseBundles(bundle => {
dumpGraphToGraphViz(bundle.assetGraph, `${bundle.id}`);
Expand Down
42 changes: 27 additions & 15 deletions packages/core/core/src/BundleGraph.js
Expand Up @@ -2,18 +2,21 @@
import type {
Asset,
Bundle,
BundleGraph as IBundleGraph,
BundleGraphNode,
BundleGroup,
GraphTraversalCallback,
Node
BundleNode,
GraphTraversalCallback
} from '@parcel/types';
import type AssetGraph from './AssetGraph';

import invariant from 'assert';
import Graph from './Graph';

const getBundleGroupId = (bundleGroup: BundleGroup) =>
'bundle_group:' + bundleGroup.entryAssetId;

export default class BundleGraph extends Graph<Node> {
export default class BundleGraph extends Graph<BundleGraphNode>
implements IBundleGraph {
constructor() {
super();
this.setRootNode({
Expand Down Expand Up @@ -56,7 +59,7 @@ export default class BundleGraph extends Graph<Node> {
}

let bundleGroupId = getBundleGroupId(bundleGroup);
let bundleNode = {
let bundleNode: BundleNode = {
id: bundle.id,
type: 'bundle',
value: bundle
Expand All @@ -73,11 +76,11 @@ export default class BundleGraph extends Graph<Node> {
// already created bundles in the bundle graph. This can happen when two
// bundles point to the same dependency, which has an async import.
if (node.type === 'bundle_group') {
// TODO: fix the AssetGraph interface so we don't need to do this
let assetGraph: AssetGraph = (bundle.assetGraph: any);
let {assetGraph} = bundle;
let bundleGroup: BundleGroup = node.value;
let depNode = assetGraph.getNode(bundleGroup.dependency.id);
if (depNode && !assetGraph.hasNode(node.id)) {
// $FlowFixMe Merging a graph of a subtype into a graph of the supertype
assetGraph.merge(this.getSubGraph(node));
assetGraph.replaceNodesConnectedTo(depNode, [node]);
this.addEdge({from: bundle.id, to: node.id});
Expand Down Expand Up @@ -105,7 +108,10 @@ export default class BundleGraph extends Graph<Node> {
return [];
}

return this.getNodesConnectedFrom(node).map(node => node.value);
return this.getNodesConnectedFrom(node).map(node => {
invariant(node.type === 'bundle');
return node.value;
});
}

getBundleGroups(bundle: Bundle): Array<BundleGroup> {
Expand All @@ -114,7 +120,10 @@ export default class BundleGraph extends Graph<Node> {
return [];
}

return this.getNodesConnectedTo(node).map(node => node.value);
return this.getNodesConnectedTo(node).map(node => {
invariant(node.type === 'bundle_group');
return node.value;
});
}

isAssetInAncestorBundle(bundle: Bundle, asset: Asset): boolean {
Expand Down Expand Up @@ -145,12 +154,15 @@ export default class BundleGraph extends Graph<Node> {
}

findBundlesWithAsset(asset: Asset): Array<Bundle> {
return Array.from(this.nodes.values())
.filter(
node =>
node.type === 'bundle' && node.value.assetGraph.hasNode(asset.id)
)
.map(node => node.value);
return (
Array.from(this.nodes.values())
.filter(
node =>
node.type === 'bundle' && node.value.assetGraph.hasNode(asset.id)
)
// $FlowFixMe Flow can't refine on filter https://github.com/facebook/flow/issues/1414
.map(node => node.value)
);
}

traverseBundles<TContext>(
Expand Down
12 changes: 4 additions & 8 deletions packages/core/core/src/Graph.js
Expand Up @@ -5,20 +5,19 @@ import type {
Node,
NodeId,
GraphTraversalCallback,
GraphUpdates,
TraversalActions,
Graph as IGraph
} from '@parcel/types';

type GraphUpdates<TNode> = {|
added: Graph<TNode>,
removed: Graph<TNode>
|};
import nullthrows from 'nullthrows';

type GraphOpts<TNode> = {|
nodes?: Array<[NodeId, TNode]>,
edges?: Array<Edge>,
rootNodeId?: ?NodeId
|};

export default class Graph<TNode: Node> implements IGraph<TNode> {
nodes: Map<NodeId, TNode>;
edges: Set<Edge>;
Expand Down Expand Up @@ -87,10 +86,7 @@ export default class Graph<TNode: Node> implements IGraph<TNode> {

getNodesConnectedFrom(node: TNode): Array<TNode> {
let edges = Array.from(this.edges).filter(edge => edge.from === node.id);
return edges.map(edge => {
// $FlowFixMe
return this.nodes.get(edge.to);
});
return edges.map(edge => nullthrows(this.nodes.get(edge.to)));
}

merge(graph: IGraph<TNode>): void {
Expand Down
82 changes: 53 additions & 29 deletions packages/core/types/index.js
Expand Up @@ -299,6 +299,12 @@ export type AssetReferenceNode = {|
value: Asset
|};

export type BundleNode = {|
id: string,
type: 'bundle',
value: Bundle
|};

export type BundleGroupNode = {|
id: string,
type: 'bundle_group',
Expand All @@ -320,24 +326,58 @@ export type TransformerRequestNode = {|
value: TransformerRequest
|};

export type BundleGroup = {
dependency: Dependency,
target: ?Target,
entryAssetId: string
};

export type Bundle = {|
id: string,
type: string,
assetGraph: AssetGraph,
env: Environment,
isEntry?: boolean,
target?: Target,
filePath?: FilePath
|};

export type AssetGraphNode =
| AssetNode
| AssetReferenceNode
| BundleGroupNode
| DependencyNode
| FileNode
| RootNode
| TransformerRequestNode;
| TransformerRequestNode
// Bundle graphs are merged into asset graphs during the bundling phase
| BundleGraphNode;

export type BundleGraphNode = BundleNode | BundleGroupNode | RootNode;

export type Edge = {|
from: NodeId,
to: NodeId
|};

export type GraphUpdates<TNode> = {|
added: Graph<TNode>,
removed: Graph<TNode>
|};

export interface Graph<TNode: Node> {
nodes: Map<string, TNode>;
edges: Set<Edge>;
nodes: Map<string, TNode>;
addEdge(edge: Edge): Edge;
addNode(node: TNode): TNode;
getNode(id: string): ?TNode;
getNodesConnectedFrom(node: TNode): Array<TNode>;
getRootNode(): ?TNode;
hasNode(id: string): boolean;
merge(graph: Graph<TNode>): void;
replaceNodesConnectedTo(
fromNode: TNode,
toNodes: Array<TNode>
): GraphUpdates<TNode>;
traverse<TContext>(
visit: GraphTraversalCallback<TNode, TContext>,
startNode: ?TNode
Expand All @@ -346,40 +386,24 @@ export interface Graph<TNode: Node> {

// TODO: what do we want to expose here?
export interface AssetGraph extends Graph<AssetGraphNode> {
traverseAssets(
visit: GraphTraversalCallback<Asset, AssetGraphNode>
): ?AssetGraphNode;
createBundle(asset: Asset): Bundle;
getTotalSize(asset?: Asset): number;
getEntryAssets(): Array<Asset>;
removeAsset(asset: Asset): void;
getDependencies(asset: Asset): Array<Dependency>;
getDependencyResolution(dependency: Dependency): ?Asset;
getEntryAssets(): Array<Asset>;
getTotalSize(asset?: Asset): number;
removeAsset(asset: Asset): void;
traverseAssets(
visit: GraphTraversalCallback<Asset, AssetGraphNode>
): ?AssetGraphNode;
}

export type BundleGroup = {
dependency: Dependency,
target: ?Target,
entryAssetId: string
};

export type Bundle = {|
id: string,
type: string,
assetGraph: AssetGraph,
env: Environment,
isEntry?: boolean,
target?: Target,
filePath?: FilePath
|};

export interface BundleGraph extends Graph<Node> {
addBundleGroup(parentBundle: ?Bundle, bundleGroup: BundleGroup): void;
export interface BundleGraph extends Graph<BundleGraphNode> {
addBundle(bundleGroup: BundleGroup, bundle: Bundle): void;
isAssetInAncestorBundle(bundle: Bundle, asset: Asset): boolean;
addBundleGroup(parentBundle: ?Bundle, bundleGroup: BundleGroup): void;
findBundlesWithAsset(asset: Asset): Array<Bundle>;
getBundles(bundleGroup: BundleGroup): Array<Bundle>;
getBundleGroups(bundle: Bundle): Array<BundleGroup>;
getBundles(bundleGroup: BundleGroup): Array<Bundle>;
isAssetInAncestorBundle(bundle: Bundle, asset: Asset): boolean;
traverseBundles<TContext>(
visit: GraphTraversalCallback<Bundle, TContext>
): ?TContext;
Expand Down
3 changes: 2 additions & 1 deletion packages/core/utils/package.json
Expand Up @@ -24,6 +24,7 @@
"@parcel/workers": "^1.10.3"
},
"devDependencies": {
"@babel/plugin-transform-flow-strip-types": "^7.2.0"
"@babel/plugin-transform-flow-strip-types": "^7.2.0",
"nullthrows": "^1.1.1"
}
}
37 changes: 23 additions & 14 deletions packages/core/utils/src/dumpGraphToGraphViz.js
@@ -1,31 +1,39 @@
// @flow

import type {Environment, Graph, Node} from '@parcel/types';
import type {
Environment,
Graph,
BundleGraphNode,
AssetGraphNode
} from '@parcel/types';

import invariant from 'assert';
import nullthrows from 'nullthrows';
import graphviz from 'graphviz';
import tempy from 'tempy';
import path from 'path';

const COLORS = {
root: 'gray',
asset: 'green',
dependency: 'orange',
transformer_request: 'cyan',
file: 'gray',
default: 'white'
};

export default async function dumpGraphToGraphViz(
graph: Graph<Node>,
graph: Graph<AssetGraphNode | BundleGraphNode>,
name: string
): Promise<void> {
let g = graphviz.digraph('G');

let colors = {
root: 'gray',
asset: 'green',
dependency: 'orange',
transformer_request: 'cyan',
file: 'gray',
default: 'white'
};

let nodes: Array<Node> = Array.from(graph.nodes.values());
let nodes = Array.from(graph.nodes.values());
for (let node of nodes) {
let n = g.addNode(node.id);

n.set('color', colors[node.type || 'default']);
// $FlowFixMe default is fine. Not every type needs to be in the map.
n.set('color', COLORS[node.type || 'default']);
n.set('shape', 'box');
n.set('style', 'filled');

Expand All @@ -49,10 +57,11 @@ export default async function dumpGraphToGraphViz(
` (${getEnvDescription(node.value.env)})`;
} else if (node.type === 'bundle') {
let rootAssets = node.value.assetGraph.getNodesConnectedFrom(
node.value.assetGraph.getRootNode()
nullthrows(node.value.assetGraph.getRootNode())
);
label += rootAssets
.map(asset => {
invariant(asset.type === 'asset');
let parts = asset.value.filePath.split(path.sep);
let index = parts.lastIndexOf('node_modules');
if (index >= 0) {
Expand Down
13 changes: 8 additions & 5 deletions packages/runtimes/js/src/JSRuntime.js
@@ -1,9 +1,10 @@
// @flow

import type {BundleGroup} from '@parcel/types';
import type {BundleGroupNode} from '@parcel/types';

import {Runtime} from '@parcel/plugin';
import invariant from 'assert';
import path from 'path';
import {Runtime} from '@parcel/plugin';

const LOADERS = {
browser: {
Expand Down Expand Up @@ -40,7 +41,7 @@ export default new Runtime({
}

// $FlowFixMe Flow can't refine on filter https://github.com/facebook/flow/issues/1414
let bundleGroups: Array<BundleGroup> = Array.from(
let bundleGroups: Array<BundleGroupNode> = Array.from(
bundle.assetGraph.nodes.values()
).filter(n => n.type === 'bundle_group');

Expand All @@ -51,9 +52,11 @@ export default new Runtime({
}

let bundles = bundle.assetGraph
// $FlowFixMe - define a better asset graph interface
.getNodesConnectedFrom(bundleGroup)
.map(node => node.value)
.map(node => {
invariant(node.type === 'bundle');
return node.value;
})
.sort(
bundle =>
bundle.assetGraph.hasNode(bundleGroup.value.entryAssetId) ? 1 : -1
Expand Down

0 comments on commit ab9366d

Please sign in to comment.