@parcel/core
Version:
352 lines (305 loc) • 9.75 kB
JavaScript
// @flow strict-local
import type {
Bundle as InternalBundle,
ParcelOptions,
PackagedBundleInfo,
} from '../types';
import type {
Asset as IAsset,
Bundle as IBundle,
BundleTraversable,
Dependency as IDependency,
Environment as IEnvironment,
GraphVisitor,
NamedBundle as INamedBundle,
PackagedBundle as IPackagedBundle,
Stats,
Target as ITarget,
BundleBehavior,
PackagedBundleFile,
} from '@parcel/types';
import type BundleGraph from '../BundleGraph';
import invariant from 'assert';
import nullthrows from 'nullthrows';
import {DefaultWeakMap} from '@parcel/utils';
import {assetToAssetValue, assetFromValue} from './Asset';
import {mapVisitor} from '@parcel/graph';
import Environment from './Environment';
import {
dependencyToInternalDependency,
getPublicDependency,
} from './Dependency';
import Target from './Target';
import {BundleBehaviorNames} from '../types';
import {fromProjectPath} from '../projectPath';
const internalBundleToBundle: DefaultWeakMap<
ParcelOptions,
DefaultWeakMap<BundleGraph, WeakMap<InternalBundle, Bundle>>,
> = new DefaultWeakMap(() => new DefaultWeakMap(() => new WeakMap()));
const internalBundleToNamedBundle: DefaultWeakMap<
ParcelOptions,
DefaultWeakMap<BundleGraph, WeakMap<InternalBundle, NamedBundle>>,
> = new DefaultWeakMap(() => new DefaultWeakMap(() => new WeakMap()));
const internalBundleToPackagedBundle: DefaultWeakMap<
ParcelOptions,
DefaultWeakMap<BundleGraph, WeakMap<InternalBundle, PackagedBundle>>,
> = new DefaultWeakMap(() => new DefaultWeakMap(() => new WeakMap()));
// Friendly access for other modules within this package that need access
// to the internal bundle.
const _bundleToInternalBundle: WeakMap<IBundle, InternalBundle> = new WeakMap();
export function bundleToInternalBundle(bundle: IBundle): InternalBundle {
return nullthrows(_bundleToInternalBundle.get(bundle));
}
const _bundleToInternalBundleGraph: WeakMap<IBundle, BundleGraph> =
new WeakMap();
export function bundleToInternalBundleGraph(bundle: IBundle): BundleGraph {
return nullthrows(_bundleToInternalBundleGraph.get(bundle));
}
// Require this private object to be present when invoking these constructors,
// preventing others from using them. They should use the static `get` method.
let _private = {};
export class Bundle implements IBundle {
#bundle /*: InternalBundle */;
#bundleGraph /*: BundleGraph */;
#options /*: ParcelOptions */;
constructor(
sentinel: mixed,
bundle: InternalBundle,
bundleGraph: BundleGraph,
options: ParcelOptions,
) {
if (sentinel !== _private) {
throw new Error('Unexpected public usage');
}
this.#bundle = bundle;
this.#bundleGraph = bundleGraph;
this.#options = options;
}
static get(
internalBundle: InternalBundle,
bundleGraph: BundleGraph,
options: ParcelOptions,
): Bundle {
let existingMap = internalBundleToBundle.get(options).get(bundleGraph);
let existing = existingMap.get(internalBundle);
if (existing != null) {
return existing;
}
let bundle = new Bundle(_private, internalBundle, bundleGraph, options);
_bundleToInternalBundle.set(bundle, internalBundle);
_bundleToInternalBundleGraph.set(bundle, bundleGraph);
existingMap.set(internalBundle, bundle);
return bundle;
}
get id(): string {
return this.#bundle.id;
}
get hashReference(): string {
return this.#bundle.hashReference;
}
get type(): string {
return this.#bundle.type;
}
get env(): IEnvironment {
return new Environment(this.#bundle.env, this.#options);
}
get needsStableName(): ?boolean {
return this.#bundle.needsStableName;
}
get bundleBehavior(): ?BundleBehavior {
let bundleBehavior = this.#bundle.bundleBehavior;
return bundleBehavior != null ? BundleBehaviorNames[bundleBehavior] : null;
}
get isSplittable(): ?boolean {
return this.#bundle.isSplittable;
}
get manualSharedBundle(): ?string {
return this.#bundle.manualSharedBundle;
}
get target(): ITarget {
return new Target(this.#bundle.target, this.#options);
}
hasAsset(asset: IAsset): boolean {
return this.#bundleGraph.bundleHasAsset(
this.#bundle,
assetToAssetValue(asset),
);
}
hasDependency(dep: IDependency): boolean {
return this.#bundleGraph.bundleHasDependency(
this.#bundle,
dependencyToInternalDependency(dep),
);
}
getEntryAssets(): Array<IAsset> {
return this.#bundle.entryAssetIds.map(id => {
let assetNode = this.#bundleGraph._graph.getNodeByContentKey(id);
invariant(assetNode != null && assetNode.type === 'asset');
return assetFromValue(assetNode.value, this.#options);
});
}
getMainEntry(): ?IAsset {
if (this.#bundle.mainEntryId != null) {
let assetNode = this.#bundleGraph._graph.getNodeByContentKey(
this.#bundle.mainEntryId,
);
invariant(assetNode != null && assetNode.type === 'asset');
return assetFromValue(assetNode.value, this.#options);
}
}
traverse<TContext>(
visit: GraphVisitor<BundleTraversable, TContext>,
): ?TContext {
return this.#bundleGraph.traverseBundle(
this.#bundle,
mapVisitor(node => {
if (node.type === 'asset') {
return {
type: 'asset',
value: assetFromValue(node.value, this.#options),
};
} else if (node.type === 'dependency') {
return {
type: 'dependency',
value: getPublicDependency(node.value, this.#options),
};
}
}, visit),
);
}
traverseAssets<TContext>(
visit: GraphVisitor<IAsset, TContext>,
startAsset?: IAsset,
): ?TContext {
return this.#bundleGraph.traverseAssets(
this.#bundle,
mapVisitor(asset => assetFromValue(asset, this.#options), visit),
startAsset ? assetToAssetValue(startAsset) : undefined,
);
}
getContentHash(): string {
return this.#bundleGraph.getContentHash(this.#bundle);
}
}
export class NamedBundle extends Bundle implements INamedBundle {
#bundle /*: InternalBundle */;
#bundleGraph /*: BundleGraph */;
#options /*: ParcelOptions */;
constructor(
sentinel: mixed,
bundle: InternalBundle,
bundleGraph: BundleGraph,
options: ParcelOptions,
) {
super(sentinel, bundle, bundleGraph, options);
this.#bundle = bundle; // Repeating for flow
this.#bundleGraph = bundleGraph; // Repeating for flow
this.#options = options;
}
static get(
internalBundle: InternalBundle,
bundleGraph: BundleGraph,
options: ParcelOptions,
): NamedBundle {
let existingMap = internalBundleToNamedBundle.get(options).get(bundleGraph);
let existing = existingMap.get(internalBundle);
if (existing != null) {
return existing;
}
let namedBundle = new NamedBundle(
_private,
internalBundle,
bundleGraph,
options,
);
_bundleToInternalBundle.set(namedBundle, internalBundle);
_bundleToInternalBundleGraph.set(namedBundle, bundleGraph);
existingMap.set(internalBundle, namedBundle);
return namedBundle;
}
get name(): string {
return nullthrows(this.#bundle.name);
}
get displayName(): string {
return nullthrows(this.#bundle.displayName);
}
get publicId(): string {
return nullthrows(this.#bundle.publicId);
}
}
export class PackagedBundle extends NamedBundle implements IPackagedBundle {
#bundle /*: InternalBundle */;
#bundleGraph /*: BundleGraph */;
#options /*: ParcelOptions */;
#bundleInfo /*: ?PackagedBundleInfo[] */;
constructor(
sentinel: mixed,
bundle: InternalBundle,
bundleGraph: BundleGraph,
options: ParcelOptions,
) {
super(sentinel, bundle, bundleGraph, options);
this.#bundle = bundle; // Repeating for flow
this.#bundleGraph = bundleGraph; // Repeating for flow
this.#options = options; // Repeating for flow
}
static get(
internalBundle: InternalBundle,
bundleGraph: BundleGraph,
options: ParcelOptions,
): PackagedBundle {
let existingMap = internalBundleToPackagedBundle
.get(options)
.get(bundleGraph);
let existing = existingMap.get(internalBundle);
if (existing != null) {
return existing;
}
let packagedBundle = new PackagedBundle(
_private,
internalBundle,
bundleGraph,
options,
);
_bundleToInternalBundle.set(packagedBundle, internalBundle);
_bundleToInternalBundleGraph.set(packagedBundle, bundleGraph);
existingMap.set(internalBundle, packagedBundle);
return packagedBundle;
}
static getWithInfo(
internalBundle: InternalBundle,
bundleGraph: BundleGraph,
options: ParcelOptions,
bundleInfo: ?(PackagedBundleInfo[]),
): PackagedBundle {
let packagedBundle = PackagedBundle.get(
internalBundle,
bundleGraph,
options,
);
packagedBundle.#bundleInfo = bundleInfo;
return packagedBundle;
}
get filePath(): string {
return fromProjectPath(
this.#options.projectRoot,
nullthrows(this.#bundleInfo)[0].filePath,
);
}
get type(): string {
// The bundle type may be overridden in the packager.
// However, inline bundles will not have a bundleInfo here since they are not written to the filesystem.
return this.#bundleInfo ? this.#bundleInfo[0].type : this.#bundle.type;
}
get stats(): Stats {
return nullthrows(this.#bundleInfo)[0].stats;
}
get files(): PackagedBundleFile[] {
return this.#bundleInfo
? this.#bundleInfo.map(i => ({
filePath: fromProjectPath(this.#options.projectRoot, i.filePath),
stats: i.stats,
}))
: [];
}
}