@velcro/bundler
Version:
Build a module graph of modules and serialize these to different formats
1,371 lines (1,361 loc) • 68 kB
JavaScript
import { Base64, Uri, isThenable, checkCancellation, DependencyNotFoundError, EntryExcludedError, EntryNotFoundError, MapSet, DisposableStore, Emitter, CancellationTokenSource, isCanceledError } from '@velcro/common';
import MagicString, { Bundle } from 'magic-string';
import { parse as parse$2 } from 'acorn';
import { runtime } from '@velcro/runtime';
import { version as version$1 } from '@velcro/node-libs/package.json';
class BaseError extends Error {
constructor() {
super(...arguments);
this.name = this.constructor.name;
}
}
class GraphBuildError extends BaseError {
constructor(errors) {
super(`Graph building failed with errors:\n${errors.map((err) => ` ${err.message}`).join('\n')}`);
this.errors = errors;
}
}
var charToInteger = {};
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
for (var i = 0; i < chars.length; i++) {
charToInteger[chars.charCodeAt(i)] = i;
}
function decode(mappings) {
var decoded = [];
var line = [];
var segment = [
0,
0,
0,
0,
0,
];
var j = 0;
for (var i = 0, shift = 0, value = 0; i < mappings.length; i++) {
var c = mappings.charCodeAt(i);
if (c === 44) { // ","
segmentify(line, segment, j);
j = 0;
}
else if (c === 59) { // ";"
segmentify(line, segment, j);
j = 0;
decoded.push(line);
line = [];
segment[0] = 0;
}
else {
var integer = charToInteger[c];
if (integer === undefined) {
throw new Error('Invalid character (' + String.fromCharCode(c) + ')');
}
var hasContinuationBit = integer & 32;
integer &= 31;
value += integer << shift;
if (hasContinuationBit) {
shift += 5;
}
else {
var shouldNegate = value & 1;
value >>>= 1;
if (shouldNegate) {
value = value === 0 ? -0x80000000 : -value;
}
segment[j] += value;
j++;
value = shift = 0; // reset
}
}
}
segmentify(line, segment, j);
decoded.push(line);
return decoded;
}
function segmentify(line, segment, j) {
// This looks ugly, but we're creating specialized arrays with a specific
// length. This is much faster than creating a new array (which v8 expands to
// a capacity of 17 after pushing the first item), or slicing out a subarray
// (which is slow). Length 4 is assumed to be the most frequent, followed by
// length 5 (since not everything will have an associated name), followed by
// length 1 (it's probably rare for a source substring to not have an
// associated segment data).
if (j === 4)
line.push([segment[0], segment[1], segment[2], segment[3]]);
else if (j === 5)
line.push([segment[0], segment[1], segment[2], segment[3], segment[4]]);
else if (j === 1)
line.push([segment[0]]);
}
function encode(decoded) {
var sourceFileIndex = 0; // second field
var sourceCodeLine = 0; // third field
var sourceCodeColumn = 0; // fourth field
var nameIndex = 0; // fifth field
var mappings = '';
for (var i = 0; i < decoded.length; i++) {
var line = decoded[i];
if (i > 0)
mappings += ';';
if (line.length === 0)
continue;
var generatedCodeColumn = 0; // first field
var lineMappings = [];
for (var _i = 0, line_1 = line; _i < line_1.length; _i++) {
var segment = line_1[_i];
var segmentMappings = encodeInteger(segment[0] - generatedCodeColumn);
generatedCodeColumn = segment[0];
if (segment.length > 1) {
segmentMappings +=
encodeInteger(segment[1] - sourceFileIndex) +
encodeInteger(segment[2] - sourceCodeLine) +
encodeInteger(segment[3] - sourceCodeColumn);
sourceFileIndex = segment[1];
sourceCodeLine = segment[2];
sourceCodeColumn = segment[3];
}
if (segment.length === 5) {
segmentMappings += encodeInteger(segment[4] - nameIndex);
nameIndex = segment[4];
}
lineMappings.push(segmentMappings);
}
mappings += lineMappings.join(',');
}
return mappings;
}
function encodeInteger(num) {
var result = '';
num = num < 0 ? (-num << 1) | 1 : num << 1;
do {
var clamped = num & 31;
num >>>= 5;
if (num > 0) {
clamped |= 32;
}
result += chars[clamped];
} while (num > 0);
return result;
}
class SourceMap {
constructor(input) {
this.file = input.file;
this.mappings = input.mappings;
this.sourceRoot = input.sourceRoot;
this.names = input.names;
this.sources = input.sources;
this.sourcesContent = input.sourcesContent;
this.version = input.version;
}
toString() {
return JSON.stringify(this);
}
toDataUri() {
return `data:application/json;charset=utf-8;base64,${Base64.encode(this.toString())}`;
}
}
function getSourceMappingUrlMatch(str) {
const re = /(?:(?:\/\/|\/\*)[@#][\s]*(?:source)MappingURL=([^\s'"]+)[\s]*$)|(?:\/\*[@#][\s]*(?:source)MappingURL=([^\s*'"]+)[\s]*(?:\*\/)[\s]*$)/gm;
// Keep executing the search to find the *last* sourceMappingURL to avoid
// picking up sourceMappingURLs from comments, strings, etc.
let lastMatch = null;
let match;
while ((match = re.exec(str)))
lastMatch = match;
return lastMatch;
}
function getSourceMappingUrl(str) {
const lastMatch = getSourceMappingUrlMatch(str);
if (!lastMatch)
return '';
return lastMatch[1];
}
function updateSourceMappingUrl(str, url) {
const lastMatch = getSourceMappingUrlMatch(str);
if (!lastMatch)
return str;
return str.slice(0, lastMatch.index) + str.slice(lastMatch.index).replace(lastMatch[1], url);
}
function decodeDataUriAsSourceMap(href) {
const match = href.match(/^data:application\/json;(?:charset=([^;]+);)?base64,(.*)$/);
if (match) {
if (match[1] && match[1] !== 'utf-8') {
return null;
}
try {
const decoded = JSON.parse(Base64.decode(match[2]));
if (decoded.mappings === '') {
return {
file: '',
mappings: [],
names: [],
sources: [],
sourcesContent: [],
};
}
if (typeof decoded.mappings === 'string') {
decoded.mappings = decode(decoded.mappings);
}
return decoded;
}
catch (err) {
return null;
}
}
return null;
}
/**
* Copyright (c) Rollup 2020 authors: https://github.com/rollup/rollup/graphs/contributors)
*
* Copied with light modifications from:
* https://github.com/rollup/rollup/blob/36a4527473ea1fe678ed866c9f8dfd3c2542cd22/src/utils/collapseSourcemaps.ts
*/
class Source {
constructor(filename, content) {
this.filename = filename;
this.content = content;
}
traceSegment(line, column, name) {
return { line, column, name, source: this };
}
}
class Link {
constructor(map, sources) {
this.sources = sources;
this.names = map.names;
this.mappings = typeof map.mappings === 'string' ? decode(map.mappings) : map.mappings;
}
traceMappings() {
return traceMappings(this);
}
traceSegment(line, column, name) {
return traceSegment(this, line, column, name);
}
}
class LazyLink {
constructor(loadLink) {
this.loadLink = loadLink;
this.link = undefined;
}
getLink() {
let link = this.link;
if (!link) {
link = this.loadLink();
this.link = link;
}
return link;
}
traceMappings() {
return traceMappings(this.getLink());
}
traceSegment(line, column, name) {
return traceSegment(this.getLink(), line, column, name);
}
}
function traceMappings(map) {
const sources = [];
const sourcesContent = [];
const names = [];
const mappings = [];
for (const line of map.mappings) {
const tracedLine = [];
for (const segment of line) {
if (segment.length == 1)
continue;
const source = map.sources[segment[1]];
if (!source)
continue;
const traced = source.traceSegment(segment[2], segment[3], segment.length === 5 ? map.names[segment[4]] : '');
if (traced) {
// newer sources are more likely to be used, so search backwards.
let sourceIndex = sources.lastIndexOf(traced.source.filename);
if (sourceIndex === -1) {
sourceIndex = sources.length;
sources.push(traced.source.filename);
sourcesContent[sourceIndex] = traced.source.content;
}
else if (sourcesContent[sourceIndex] == null) {
sourcesContent[sourceIndex] = traced.source.content;
}
else if (traced.source.content != null &&
sourcesContent[sourceIndex] !== traced.source.content) {
return new Error(`Multiple conflicting contents for sourcemap source ${traced.source.filename}`);
}
const tracedSegment = [
segment[0],
sourceIndex,
traced.line,
traced.column,
];
if (traced.name) {
let nameIndex = names.indexOf(traced.name);
if (nameIndex === -1) {
nameIndex = names.length;
names.push(traced.name);
}
tracedSegment[4] = nameIndex;
}
tracedLine.push(tracedSegment);
}
}
mappings.push(tracedLine);
}
return { sources, sourcesContent, names, mappings };
}
function traceSegment(map, line, column, name) {
const segments = map.mappings[line];
if (!segments)
return null;
// binary search through segments for the given column
let i = 0;
let j = segments.length - 1;
while (i <= j) {
const m = (i + j) >> 1;
const segment = segments[m];
if (segment[0] === column) {
if (segment.length == 1)
return null;
const source = map.sources[segment[1]];
if (!source)
return null;
return source.traceSegment(segment[2], segment[3], segment.length === 5 ? map.names[segment[4]] : name);
}
if (segment[0] > column) {
j = m - 1;
}
else {
i = m + 1;
}
}
return null;
}
/**
* This function attempts to compensate for the loss of precision when lower
* layers of source maps have higher precision than upper layers, leading to
* a loss of fidelity.
*
* The code was lifted from [Alec Larson](https://github.com/aleclarson)'s
* [fork of sorcery](https://github.com/aleclarson/sorcery/blob/3934a3f38a6d8604fc9dbaa576cbb6e4d733040f/src/blend.js).
*
* NOTE: This function mutates the given node.
*
* @copyright [Alec Larson](https://github.com/aleclarson) 2018
*/
// function blend(node: Link) {
// let mappings: SourceMapSegment[][] = []; // traced lines
// let sources: (Link | Source)[] = []; // traced sources
// let names: string[] = []; // traced symbols
// // Precompute which source/line/column triples are mapped by the given node.
// // These references are useful when interweaving old segments.
// const refs: number[][][] = Object.keys(node.sources).map(() => []);
// for (const segments of node.mappings) {
// let segment: SourceMapSegment;
// let lines: number[][];
// let columns: number[];
// for (let i = 0; i < segments.length; i++) {
// segment = segments[i];
// if (segment.length === 4 || segment.length === 5) {
// lines = refs[segment[1]];
// if (!lines) refs[segment[1]] = lines = [];
// columns = lines[segment[2]];
// if (columns) {
// uniqueAscendingInsert(columns, segment[3]);
// } else {
// lines[segment[2]] = [segment[3]];
// }
// }
// }
// }
// let traced: SourceMapSegment[] | undefined = undefined; // the traced line mapping
// let untraced: SourceMapSegment[] | undefined = undefined; // the untraced line mapping
// function addSegment(
// segment: SourceMapSegment,
// source?: { names: string[]; sources: (Link | Source)[] }
// ) {
// if (source) {
// segment[1] = uniq<Link | Source>(sources, source.sources[segment[1]!]);
// if (segment.length === 5) {
// segment[4] = uniq(names, source.names[segment[4]]);
// }
// } else if (segment.length === 5) {
// segment[4] = uniq(names, node.names[segment[4]]);
// }
// traced!.push(segment);
// }
// let tracedLine: number; // the last traced line
// let generatedLine = -1; // the current line
// let sourceIndex: number | undefined = -1; // source of last traced segment
// let sourceLine: number | undefined = undefined; // source line of last traced segment
// // Find the next line with segments.
// function nextLine() {
// tracedLine = generatedLine;
// while (++generatedLine < node.mappings.length) {
// untraced = node.mappings[generatedLine];
// if (untraced.length) return true;
// }
// }
// // Provide mappings for lines between the
// // last traced line and the current line.
// function fillSkippedLines() {
// const skipped = generatedLine - (tracedLine + 1);
// if (skipped !== 0) {
// let line = tracedLine;
// // Take line mappings from the current source.
// if (sourceIndex !== -1) {
// const source = node.sources[sourceIndex!];
// if (source instanceof Link) {
// while (line < generatedLine - 1) {
// if (++sourceLine! !== source.mappings.length) {
// mappings[++line] = traced = [];
// // Check referenced columns to avoid duplicate segments.
// const columns = refs[sourceIndex!][sourceLine!] || [];
// let prevColumn = -1;
// // Interweave old segments from the current source.
// const segments = source.mappings[sourceLine!];
// for (let i = 0; i < segments.length; i++) {
// const segment = segments[i];
// if (!hasValueBetween(columns, prevColumn, segment[0] + 1)) {
// addSegment([...segment] as SourceMapSegment, source);
// prevColumn = segment[0];
// } else break;
// }
// } else {
// // End of source file.
// sourceIndex = -1;
// break;
// }
// }
// }
// }
// // Default to empty arrays for unmapped lines.
// while (++line < generatedLine) {
// mappings[line] = [];
// }
// }
// }
// while (nextLine()) {
// fillSkippedLines();
// // Trace the segments of this generated line.
// mappings[generatedLine] = traced = [];
// // Interweave old segments before the first mapped column of each line.
// const sourceColumn = untraced![0][3];
// if (sourceIndex !== -1 && sourceColumn !== 0) {
// const source = node.sources[sourceIndex];
// if (source instanceof Link) {
// const segments =
// sourceLine! < source.mappings.length - 1 ? source.mappings[++sourceLine!] : [];
// for (let i = 0; i < segments.length; i++) {
// const segment = segments[i];
// if (segment[0] < sourceColumn!) {
// addSegment(segment.slice(0) as SourceMapSegment, source);
// } else break;
// }
// }
// }
// const last = untraced!.length - 1;
// untraced!.forEach((curr: SourceMapSegment | null, i) => {
// [, sourceIndex, sourceLine] = curr!;
// const source = node.sources[sourceIndex!];
// if (source === null) {
// curr![1] = uniq(sources, null);
// return addSegment(curr!);
// }
// if (!(source instanceof Link)) {
// curr![1] = uniq(sources, source);
// return addSegment(curr!);
// }
// const next = i !== last ? untraced![i + 1] : null;
// const sourceColumn = curr![3];
// const generatedColumn = curr![0];
// // Find the first segment with a greater column.
// const segments = source.mappings[sourceLine!] || [];
// let j = findGreaterColumn(segments, sourceColumn!);
// // A "base segment" is required for tracing to a grand-parent.
// let base;
// if (--j !== -1) {
// base = segments[j];
// curr![1] = uniq(sources, source.sources[base[1]!]);
// curr![2] = base[2];
// curr![3] = base[3]! + sourceColumn! - base[0];
// if (base.length === 5) {
// // Inherit the names of aligned base segments.
// if (base[0] === sourceColumn) {
// curr![4] = uniq(names, source.names[base[4]!]);
// }
// } else if (curr!.length === 5) {
// // When our segment is named and the base segment is not,
// // assume this segment cannot be traced to its original source.
// if (base[0] !== sourceColumn) curr = null;
// }
// } else {
// curr![1] = uniq(sources, null);
// }
// curr && addSegment(curr);
// // Check referenced columns to avoid duplicate segments.
// const columns = refs[sourceIndex!][sourceLine!] || [];
// let baseColumn = base ? base[0] : -1;
// // Interweave old segments between our current and next segments.
// const nextColumn = next ? next[0] : 1 / 0;
// while (++j < segments.length) {
// let segment = segments[j];
// // The generated column is shifted to fit into the root source map.
// const column = segment[0] + generatedColumn - sourceColumn!;
// if (column >= nextColumn) break;
// // Avoid duplicates by checking if this segment goes elsewhere.
// if (!hasValueBetween(columns, baseColumn, segment[0] + 1)) {
// baseColumn = segment[0];
// segment = segment.slice(0) as SourceMapSegment;
// segment[0] = column;
// addSegment(segment, source);
// } else break;
// }
// });
// }
// fillSkippedLines();
// node.mappings = mappings;
// node.sources = sources;
// node.names = names;
// return node;
// }
// // Check if a value exists before pushing it to an array.
// // Return the new or existing index of the value.
// function uniq<T>(arr: T[], val: T): number {
// const i = arr.indexOf(val);
// return ~i ? i : arr.push(val) - 1;
// }
// // Get the first segment with a greater column.
// function findGreaterColumn(segments: SourceMapSegment[], column: number) {
// let low = 0,
// high = segments.length;
// while (low < high) {
// const mid = (low + high) >>> 1;
// segments[mid][0] <= column ? (low = mid + 1) : (high = mid);
// }
// return low;
// }
// // The range is exclusive.
// function hasValueBetween(arr: number[], start: number, end: number) {
// let low = 0,
// high = arr.length;
// while (low < high) {
// const mid = (low + high) >>> 1;
// const val = arr[mid];
// if (val <= start) {
// low = mid + 1;
// } else if (val >= end) {
// high = mid;
// } else {
// return true;
// }
// }
// return false;
// }
// // Insert unique values in ascending order.
// function uniqueAscendingInsert(arr: number[], val: number) {
// let low = 0,
// high = arr.length;
// while (low < high) {
// const mid = (low + high) >>> 1;
// const x = arr[mid];
// if (x === val) return;
// if (x < val) {
// low = mid + 1;
// } else {
// high = mid;
// }
// }
// arr.splice(low, 0, val);
// }
class PluginManager {
constructor(plugins) {
this.plugins = plugins;
this.plugins.push({
name: 'builtIn',
load: async (ctx, id) => {
const uri = Uri.parse(id);
const readReturn = ctx.resolver.readFileContent(uri);
const readResult = isThenable(readReturn)
? await checkCancellation(readReturn, ctx.token)
: readReturn;
return {
code: ctx.resolver.decode(readResult.content),
visited: readResult.visited,
};
},
resolveDependency: async (ctx, dependency, fromSourceModule) => {
const resolveReturn = ctx.resolver.resolve(dependency.spec, fromSourceModule.uri);
const resolveResult = isThenable(resolveReturn)
? await checkCancellation(resolveReturn, ctx.token)
: resolveReturn;
if (!resolveResult.found) {
throw new DependencyNotFoundError(dependency.spec, fromSourceModule);
}
if (!resolveResult.uri) {
// TODO: Inject empty module
throw new EntryExcludedError(dependency.spec);
}
return {
uri: resolveResult.uri,
rootUri: resolveResult.rootUri,
visited: resolveResult.visited,
};
},
resolveEntrypoint: async (ctx, uri) => {
const resolveResult = await ctx.resolver.resolve(uri);
if (!resolveResult.found) {
throw new EntryNotFoundError(`Entry point not found: ${uri}`);
}
if (!resolveResult.uri) {
throw new EntryExcludedError(uri);
}
return resolveResult;
},
transform: async ({ createMagicString }, id) => {
if (id.path.endsWith('.json')) {
const magicString = createMagicString();
magicString.prepend('module.exports = ');
return {
code: magicString.toString(),
sourceMap: magicString.generateDecodedMap(),
};
}
},
});
}
async executeLoad(ctx, uri) {
for (const plugin of this.plugins) {
if (typeof plugin.load === 'function') {
const loadReturn = plugin.load(ctx, uri.toString());
const loadResult = isThenable(loadReturn)
? await checkCancellation(loadReturn, ctx.token)
: loadReturn;
if (!loadResult) {
continue;
}
return {
code: loadResult.code,
visited: loadResult.visited || [],
};
}
}
throw new Error(`No plugin was found that was able to load the uri ${uri.toString()}`);
}
async executeResolveDependency(ctx, dependency, fromModule) {
for (const plugin of this.plugins) {
if (typeof plugin.resolveDependency === 'function') {
const loadReturn = plugin.resolveDependency(ctx, dependency, fromModule);
const loadResult = isThenable(loadReturn)
? await checkCancellation(loadReturn, ctx.token)
: loadReturn;
if (!loadResult) {
continue;
}
return {
uri: loadResult.uri,
rootUri: loadResult.rootUri,
visited: loadResult.visited || [],
};
}
}
throw new Error(`No plugin was able to resolve the '${dependency.kind}' dependency, '${dependency.spec}' from '${fromModule.href}'`);
}
async executeResolveEntrypoint(ctx, uri) {
for (const plugin of this.plugins) {
if (typeof plugin.resolveEntrypoint === 'function') {
const loadReturn = plugin.resolveEntrypoint(ctx, uri);
const loadResult = isThenable(loadReturn)
? await checkCancellation(loadReturn, ctx.token)
: loadReturn;
if (!loadResult) {
continue;
}
return {
uri: loadResult.uri,
rootUri: loadResult.rootUri,
visited: loadResult.visited || [],
};
}
}
throw new Error(`No plugin was able to resolve the entrypoint '${uri.toString()}'`);
}
async executeTransform(ctx, uri, code) {
if (typeof code !== 'string') {
code = ctx.resolver.decode(code);
}
const pluginCtx = Object.assign(ctx, {
createMagicString() {
return new MagicString(code);
},
});
let sourceMapTree = new Source(uri.toString(), code);
// Figure out if our original code, itself has a sourcemap.
// For now, we will not recurse beyond that depth.
const sourceMapRef = getSourceMappingUrl(code);
if (sourceMapRef) {
let sourceMap = decodeDataUriAsSourceMap(sourceMapRef);
if (!sourceMap) {
const sourceMapUri = Uri.joinPath(uri, `../${sourceMapRef}`);
code = updateSourceMappingUrl(code, sourceMapUri.toString());
}
if (sourceMap) {
const sources = sourceMap.sources;
const sourcesContent = sourceMap.sourcesContent || [];
const baseSources = [];
for (const idx in sources) {
if (sources[idx] && sourcesContent[idx]) {
baseSources.push(new Source(sources[idx], sourcesContent[idx]));
}
}
sourceMapTree = new Link(sourceMap, baseSources);
}
}
const visited = [];
for (const plugin of this.plugins) {
if (typeof plugin.transform === 'function') {
const transformReturn = plugin.transform(pluginCtx, uri, code);
const transformResult = isThenable(transformReturn)
? await checkCancellation(transformReturn, ctx.token)
: transformReturn;
if (transformResult === null || transformResult === undefined) {
continue;
}
if (transformResult.sourceMap) {
sourceMapTree = new Link(transformResult.sourceMap, [sourceMapTree]);
}
code = transformResult.code;
if (transformResult.visited) {
visited.push(...transformResult.visited);
}
}
}
return {
code,
sourceMapTree,
visited,
};
}
}
var SourceModuleDependencyKind;
(function (SourceModuleDependencyKind) {
SourceModuleDependencyKind["Entrypoint"] = "Entrypoint";
SourceModuleDependencyKind["Require"] = "Require";
SourceModuleDependencyKind["RequireResolve"] = "RequireResolve";
SourceModuleDependencyKind["GlobalObject"] = "GlobalObject";
})(SourceModuleDependencyKind || (SourceModuleDependencyKind = {}));
class SourceModuleDependency {
constructor(kind, spec, locations, options = {}) {
this.kind = kind;
this.spec = spec;
this.locations = locations;
this.options = options;
}
static areIdentical(l, r) {
return l.kind === r.kind && l.spec === r.spec;
}
static fromEntrypoint(uri) {
return new SourceModuleDependency(SourceModuleDependencyKind.Entrypoint, uri.toString(), []);
}
static fromGlobalObject(spec, locations, exportName) {
return new SourceModuleDependency(SourceModuleDependencyKind.GlobalObject, spec, locations, {
exportName,
});
}
static fromRequire(spec, locations) {
return new SourceModuleDependency(SourceModuleDependencyKind.Require, spec, locations);
}
static fromRequireResolve(spec, locations) {
return new SourceModuleDependency(SourceModuleDependencyKind.RequireResolve, spec, locations);
}
}
function isArrowFunctionExpression(node) {
return node.type === 'ArrowFunctionExpression';
}
function isArrayPattern(node) {
return node.type === 'ArrayPattern';
}
function isAssignmentPattern(node) {
return node.type === 'AssignmentPattern';
}
function isBinaryExpression(node) {
return node.type === 'BinaryExpression';
}
function isBlockStatement(node) {
return node.type === 'BlockStatement';
}
function isCallExpression(node) {
return node.type === 'CallExpression';
}
function isClassDeclaration(node) {
return node.type === 'ClassDeclaration';
}
function isFunctionDeclaration(node) {
return node.type === 'FunctionDeclaration';
}
function isFunctionExpression(node) {
return node.type === 'FunctionExpression';
}
function isIdentifier(node) {
return node.type === 'Identifier';
}
function isIfStatement(node) {
return node.type === 'IfStatement';
}
function isLiteral(node) {
return node.type === 'Literal';
}
function isMemberExpression(node) {
return node.type === 'MemberExpression';
}
function isMethodDefinition(node) {
return node.type === 'MethodDefinition';
}
function isObjectPattern(node) {
return node.type === 'ObjectPattern';
}
function isProperty(node) {
return node.type === 'Property';
}
function isRestElement(node) {
return node.type === 'RestElement';
}
function isProgram(node) {
return node.type === 'Program';
}
function isTemplateLiteral(node) {
return node.type === 'TemplateLiteral';
}
function isThisExpression(node) {
return node.type === 'ThisExpression';
}
function isTryStatement(node) {
return node.type === 'TryStatement';
}
function isUnaryExpression(node) {
return node.type === 'UnaryExpression';
}
function isVariableDeclaration(node) {
return node.type === 'VariableDeclaration';
}
// Refinements or groups
function isFunction(node) {
return (isFunctionDeclaration(node) || isFunctionExpression(node) || isArrowFunctionExpression(node));
}
function isStringLiteral(node) {
return isLiteral(node) && typeof node.value === 'string';
}
function parse(code, options) {
return parse$2(code, {
...options,
allowReturnOutsideFunction: true,
sourceType: 'script',
});
}
function traverse(ast, ctx, { enter, leave }) {
visit(ast, null, ctx, enter, leave);
}
let shouldSkip = false;
const context = { skip: () => (shouldSkip = true) };
const childKeys = {};
function visit(node, parent, ctx, enter, leave
// prop?: string,
// index?: number
) {
if (!node)
return;
node.parent = parent;
if (enter) {
const _shouldSkip = shouldSkip;
shouldSkip = false;
enter.call(context, node, parent, ctx);
const skipped = shouldSkip;
shouldSkip = _shouldSkip;
if (skipped)
return;
}
const keys = childKeys[node.type] ||
(childKeys[node.type] = Object.keys(node).filter((key) => key !== 'parent' && typeof node[key] === 'object'));
const children = [];
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = node[key];
if (Array.isArray(value)) {
for (let j = 0; j < value.length; j++) {
if (value[j])
children.push(value[j]);
}
}
else if (value && value.type) {
children.push(value);
}
}
children.sort((a, b) => a.start - b.start);
for (const child of children) {
visit(child, node, ctx, enter, leave);
}
if (leave) {
leave(node, parent, ctx);
}
}
const parse$1 = function parseJavaScript(uri, code, options) {
const visitorCtx = {
unboundSymbols: new Map(),
locals: new Map(),
magicString: new MagicString(code, { filename: uri.toString(), indentExclusionRanges: [] }),
nodeEnv: options.nodeEnv,
replacedSymbols: new Set(),
requires: [],
requireResolves: [],
skip: new Set(),
skipTransform: new Set(),
};
const dependencies = [];
try {
// let lastToken: Token | undefined;
const ast = parse(code, {
// onComment: (_isBlock, _test, start, end) => {
// result.changes.push({ type: 'remove', start, end });
// },
// onInsertedSemicolon(lastTokEnd) {
// result.changes.push({ type: 'appendRight', position: lastTokEnd, value: ';' });
// },
// onToken: (token) => {
// const start = lastToken ? lastToken.end + 1 : 0;
// const end = token.start;
// if (end > start) {
// result.changes.push({ type: 'remove', start, end });
// }
// lastToken = token;
// },
});
traverse(ast, visitorCtx, scopingAndRequiresVisitor);
traverse(ast, visitorCtx, collectGlobalsVisitor);
}
catch (err) {
// console.debug(code);
// console.trace(err);
throw new Error(`Error parsing ${uri}: ${err.message}`);
}
// Handle explicit requires
const requiresBySpec = new Map();
for (const requireDependency of visitorCtx.requires) {
let locations = requiresBySpec.get(requireDependency.spec.value);
if (!locations) {
locations = [];
requiresBySpec.set(requireDependency.spec.value, locations);
}
locations.push({ start: requireDependency.spec.start, end: requireDependency.spec.end });
}
for (const [spec, locations] of requiresBySpec) {
dependencies.push(SourceModuleDependency.fromRequire(spec, locations));
}
// Handle require.resolve
const requireResolvesBySpec = new Map();
for (const requireDependency of visitorCtx.requireResolves) {
let locations = requiresBySpec.get(requireDependency.spec.value);
if (!locations) {
locations = [];
requiresBySpec.set(requireDependency.spec.value, locations);
}
locations.push({ start: requireDependency.spec.start, end: requireDependency.spec.end });
}
for (const [spec, locations] of requireResolvesBySpec) {
dependencies.push(SourceModuleDependency.fromRequireResolve(spec, locations));
}
for (const [symbolName, locations] of visitorCtx.unboundSymbols) {
const shim = options.globalModules[symbolName];
if (shim) {
dependencies.push(SourceModuleDependency.fromGlobalObject(shim.spec, locations, shim.export));
for (const location of locations) {
visitorCtx.magicString.overwrite(location.start, location.end, `require(${JSON.stringify(`${shim.spec}`)})${shim.export ? `.${shim.export}` : ''}`);
}
}
}
return {
code: visitorCtx.magicString,
dependencies,
};
};
const scopingAndRequiresVisitor = {
enter(node, parent, ctx) {
// Get AST-node level locations in the source map
ctx.magicString.addSourcemapLocation(node.start);
ctx.magicString.addSourcemapLocation(node.end);
if (ctx.skip.has(node)) {
return this.skip();
}
visitAndCaptureScoping(node, parent, ctx);
visitAndSkipBranches(node, parent, ctx);
visitRequires(node, parent, ctx);
},
leave(node, _parent, ctx) {
let skipped = false;
let nextCheck = node;
while (nextCheck) {
if (ctx.skipTransform.has(nextCheck)) {
skipped = true;
break;
}
nextCheck = nextCheck.parent;
}
if (!skipped &&
isMemberExpression(node) &&
memberExpressionMatches(node, 'process.env.NODE_ENV')) {
ctx.magicString.overwrite(node.start, node.end, JSON.stringify(ctx.nodeEnv), {
contentOnly: true,
storeName: true,
});
ctx.skip.add(node);
ctx.skipTransform.add(node);
}
},
};
const collectGlobalsVisitor = {
enter(node, _parent, ctx) {
if (ctx.skip.has(node)) {
return this.skip();
}
if (isBindingIdentifier(node) && isIdentifier(node) && !isArgumentOfTypeOf(node)) {
var name = node.name;
if (name === 'undefined')
return;
if (ctx.replacedSymbols.has(node)) {
return;
}
let foundBinding = false;
let nextParent = node.parent;
while (nextParent) {
if (name === 'arguments' && declaresArguments(nextParent)) {
foundBinding = true;
break;
}
const locals = ctx.locals.get(nextParent);
if (locals && locals[name]) {
foundBinding = true;
break;
}
nextParent = nextParent.parent;
}
if (!foundBinding) {
let unboundSymbols = ctx.unboundSymbols.get(name);
if (!unboundSymbols) {
unboundSymbols = [];
ctx.unboundSymbols.set(name, unboundSymbols);
}
unboundSymbols.push(node);
}
}
else if (isThisExpression(node)) {
let foundBinding = false;
let nextParent = node.parent;
while (nextParent) {
if (declaresThis(nextParent)) {
foundBinding = true;
break;
}
nextParent = nextParent.parent;
}
if (!foundBinding) {
let unboundSymbols = ctx.unboundSymbols.get('this');
if (!unboundSymbols) {
unboundSymbols = [];
ctx.unboundSymbols.set('this', unboundSymbols);
}
unboundSymbols.push(node);
}
}
},
};
function visitAndCaptureScoping(node, _parent, ctx) {
if (isVariableDeclaration(node)) {
let parent;
let nextParent = node.parent;
while (nextParent) {
if (node.kind === 'var' ? isScope(nextParent) : isBlockScope(nextParent)) {
parent = nextParent;
break;
}
nextParent = nextParent.parent;
}
if (!parent) {
throw new Error(`Invariant violation: Failed to find a parent`);
}
let locals = ctx.locals.get(parent);
if (!locals) {
locals = {};
ctx.locals.set(parent, locals);
}
for (const declaration of node.declarations) {
declarePattern(declaration.id, locals);
}
}
else if (isFunctionDeclaration(node)) {
let parent;
let nextParent = node.parent;
if (nextParent && nextParent.parent) {
nextParent = nextParent.parent;
}
while (nextParent) {
if (isScope(nextParent)) {
parent = nextParent;
break;
}
nextParent = nextParent.parent;
}
if (!parent) {
throw new Error(`Invariant violation: Failed to find a parent`);
}
let locals = ctx.locals.get(parent);
if (!locals) {
locals = {};
ctx.locals.set(parent, locals);
}
declareFunction(node, locals);
}
else if (isFunction(node)) {
let locals = ctx.locals.get(node);
if (!locals) {
locals = {};
ctx.locals.set(node, locals);
}
declareFunction(node, locals);
}
else if (isClassDeclaration(node) && node.id) {
let parent;
let nextParent = node.parent;
if (nextParent && nextParent.parent) {
nextParent = nextParent.parent;
}
while (nextParent) {
if (isScope(nextParent)) {
parent = nextParent;
break;
}
nextParent = nextParent.parent;
}
if (!parent) {
throw new Error(`Invariant violation: Failed to find a parent`);
}
let locals = ctx.locals.get(parent);
if (!locals) {
locals = {};
ctx.locals.set(parent, locals);
}
locals[node.id.name] = true;
}
else if (isTryStatement(node)) {
if (node.handler) {
let locals = ctx.locals.get(node.handler);
if (!locals) {
locals = {};
ctx.locals.set(node.handler, locals);
}
if (node.handler.param) {
declarePattern(node.handler.param, locals);
}
}
}
}
function visitAndSkipBranches(node, _parent, ctx) {
if (isIfStatement(node) && isBinaryExpression(node.test)) {
const tests = {
'!=': (l, r) => l != r,
'!==': (l, r) => l !== r,
'==': (l, r) => l == r,
'===': (l, r) => l === r,
};
const test = tests[node.test.operator];
if (test) {
if (isStringLiteral(node.test.left) &&
isMemberExpression(node.test.right) &&
memberExpressionMatches(node.test.right, 'process.env.NODE_ENV')) {
let rootObject = node.test.right;
while (isMemberExpression(rootObject.object)) {
rootObject = rootObject.object;
}
if (isIdentifier(rootObject.object)) {
ctx.replacedSymbols.add(rootObject.object);
}
ctx.skipTransform.add(node.test.right);
// if ('development' === process.env.NODE_ENV) {}
if (!test(node.test.left.value, ctx.nodeEnv)) {
ctx.skip.add(node.consequent);
// We can blow away the consequent
ctx.magicString.remove(node.start, node.alternate ? node.alternate.start : node.consequent.end);
}
else {
// We can blow away the test
ctx.magicString.remove(node.start, node.consequent.start - 1);
if (node.alternate) {
ctx.skip.add(node.alternate);
// We can blow away the alternate but we need to start and the end of the consequent + 1 char
ctx.magicString.remove(node.consequent.end + 1, node.alternate.end);
}
}
}
else if (isStringLiteral(node.test.right) &&
isMemberExpression(node.test.left) &&
memberExpressionMatches(node.test.left, 'process.env.NODE_ENV')) {
let rootObject = node.test.left;
while (isMemberExpression(rootObject.object)) {
rootObject = rootObject.object;
}
if (isIdentifier(rootObject.object)) {
ctx.replacedSymbols.add(rootObject.object);
}
ctx.skipTransform.add(node.test.left);
// if (process.env.NODE_ENV === 'development') {}
if (!test(node.test.right.value, ctx.nodeEnv)) {
ctx.skip.add(node.consequent);
// We can blow away the consequent
ctx.magicString.remove(node.start, node.alternate ? node.alternate.start : node.consequent.end);
}
else {
// We can blow away the test and the alternate
ctx.magicString.remove(node.start, node.consequent.start - 1);
if (node.alternate) {
ctx.skip.add(node.alternate);
// We can blow away the alternate but we need to start and the end of the consequent + 1 char
ctx.magicString.remove(node.consequent.end + 1, node.alternate.end);
}
}
}
}
}
}
function visitRequires(node, _parent, ctx) {
if (isCallExpression(node)) {
const callee = node.callee;
if (isIdentifier(callee) && callee.name === 'require') {
const firstArg = node.arguments[0];
if (isStringLiteral(firstArg)) {
ctx.requires.push({
spec: { start: firstArg.start, end: firstArg.end, value: firstArg.value },
callee: { start: callee.start, end: callee.end },
});
}
else if (isTemplateLiteral(firstArg) &&
firstArg.expressions.length === 0 &&
firstArg.quasis.length === 1) {
ctx.requires.push({
spec: {
start: firstArg.quasis[0].start,
end: firstArg.quasis[0].end,
value: firstArg.quasis[0].value.raw,
},
callee: { start: callee.start, end: callee.end },
});
}
else {
console.warn('Non string-literal first arg to require', firstArg);
}
}
else if (isMemberExpression(callee) &&
isIdentifier(callee.object) &&
callee.object.name === 'require' &&
isIdentifier(callee.property) &&
callee.property.name === 'resolve') {
const firstArg = node.arguments[0];
if (isStringLiteral(firstArg)) {
ctx.requireResolves.push({
spec: { start: firstArg.start, end: firstArg.end, value: firstArg.value },
callee: { start: callee.start, end: callee.end },
});
}
else {
console.warn('Non string-literal first arg to require.resolve', firstArg);
}
}
}
}
function declareFunction(node, locals) {
node.params.forEach(function (node) {
declarePattern(node, locals);
});
if (node.id) {
locals[node.id.name] = true;
}
}
function declarePattern(node, locals) {
if (isIdentifier(node)) {
locals[node.name] = true;
}
else if (isObjectPattern(node)) {
node.properties.forEach((node) => isRestElement(node)
? declarePattern(node.argument, locals)
: declarePattern(node.value, locals));
}
else if (isArrayPattern(node)) {
node.elements.forEach((node) => node && declarePattern(node, locals));
}
else if (isRestElement(node)) {
declarePattern(node.argument, locals);
}
else if (isAssignmentPattern(node)) {
declarePattern(node.left, locals);
}
else {
throw new Error(`Invariant violation: Unexpected pattern type: ${node.type}`);
}
}
function isArgumentOfTypeOf(node) {
const parent = node.parent;
return isUnaryExpression(parent) && parent.operator === 'typeof';
}
function isBindingIdentifier(node) {
return (isIdentifier(node) &&
!isPropertyOfMemberExpression(node) &&
!isKeyOfProperty(node) &&
!isKeyOfMethodDefinition(node));
}
function isKeyOfProperty(node) {
return node.parent && isProperty(node.parent) && node.parent.key === node;
}
function isPropertyOfMemberExpression(node) {
return node.parent && isMemberExpression(node.parent) && node.parent.object !== node;
}
function isKeyOfMethodDefinition(node) {
return node.parent && isMethodDefinition(node.parent);
}
function isScope(node) {
return (isFunctionDeclaration(node) ||
isFunctionExpression(node) ||
isArrowFunctionExpression(node) ||
isProgram(node));
}
function isBlockScope(node) {
return isBlockStatement(node) || isScope(node);
}
function declaresArguments(node) {
return isFunctionDeclaration(node) || isFunctionExpression(node);
}
function declaresThis(node) {
return isFunctionDeclaration(node) || isFunctionExpression(node);
}
function memberExpressionMatches(node, pattern) {
const memberParts = pattern.split('.');
if (memberParts.length < 2) {
return false;
}
const object = memberParts.shift();
const property = memberParts.shift();
for (let i = memberParts.length - 1; i >= 0; i--) {
if (!isIdentifier(node.property) || node.property.name !== memberParts[i]) {
return false;
}
if (!isMemberExpression(node.object)) {
return false;
}
node = node.object;
}
if (!isIdentifier(node.object) || !isIdentifier(node.property)) {
return false;
}
return node.object.name === object && node.property.name === property;
}
class ChunkOutput {
constructor(bundle, sourceMapTree, entrypoints) {
this.bundle = bundle;
this.sourceMapTree = sourceMapTree;
this.entrypoints = entrypoints;
}
get code() {
i