webpack-common-shake
Version:
CommonJS Tree Shake Plugin for Webpack
142 lines (112 loc) • 4.11 kB
JavaScript
'use strict';
const assert = require('assert');
const acorn = require('acorn');
const WebpackModule = require('webpack/lib/Module');
const RawSource = require('webpack-sources').RawSource;
const shake = require('common-shake');
const Analyzer = shake.Analyzer;
const root = require('../shake');
const Range = root.Range;
const ExportsRange = root.ExportsRange;
const ModuleRange = root.ModuleRange;
function getDeclarationRange(type, node, isStatement) {
// `exports.a = 1`
if (type === 'exports')
return new ExportsRange(node, isStatement);
// `module.exports = { ... key: value }`
assert.equal(type, 'module.exports');
return new ModuleRange(node);
}
class ReplacementModule extends WebpackModule {
constructor(info, original, options) {
super(original.type, original.context);
this.shake = { info, original, options };
this.usedExports = original.usedExports;
this.providedExports = original.providedExports;
this.optimizationBailout = original.optimizationBailout;
this.used = original.used;
this.index = original.index;
this.index2 = original.index2;
this.depth = original.depth;
this.built = original.built;
this.cacheable = original.cacheable;
this.dependencies = original.dependencies;
this.reasons = original.reasons;
this.buildMeta = original.buildMeta;
this.buildInfo = original.buildInfo;
this.strict = original.strict;
this.variables = original.variables;
// TODO(indutny): is this.loaders needed?
this.fileDependencies = original.fileDependencies;
this.contextDependencies = original.contextDependencies;
for (const chunk of original.chunksIterable) {
this.addChunk(chunk);
}
this.dependencies.forEach((dep) => {
if (!dep.module)
return;
dep.module.reasons.forEach(reason => {
if (reason.dependency === dep)
reason.module = this;
});
});
this.reasons.forEach((reason) => {
reason.dependency.module = this;
});
this.dependenciesWarnings = original.dependenciesWarnings;
this.dependenciesErrors = original.dependenciesErrors;
this.warnings = original.warnings;
this.errors = original.errors;
}
identifier() {
return this.shake.original.identifier();
}
readableIdentifier(requestShortener) {
return this.shake.original.readableIdentifier(requestShortener);
}
build(options, compilation, resolver, fs, callback) {
return this.shake.original.build(options, compilation, resolver, fs,
callback);
}
source(dependencyTemplates, outputOptions, requestShortener) {
const shake = this.shake;
let original = shake.original.source(dependencyTemplates,
outputOptions,
requestShortener).source();
const analyzer = new Analyzer();
const parser = new acorn.Parser({
ecmaVersion: 8,
locations: true
}, original);
const statements = new Set();
parser.extend('parseExpressionStatement', (nextMethod) => {
return function(node, expr) {
statements.add(expr);
return nextMethod.call(this, node, expr);
};
});
const declarations = analyzer.run(
parser.parse(),
'replacement'
).getDeclarations().filter((decl) => {
return !shake.info.isUsed(decl.name);
});
if (shake.options.onExportDelete) {
const resource = shake.original.resource;
declarations.forEach((decl) => {
shake.options.onExportDelete(resource, decl.name);
});
}
let range = new Range(0, original.length);
declarations.forEach((decl) => {
const isStatement = statements.has(decl.ast);
const child = getDeclarationRange(decl.type, decl.ast, isStatement);
range = range.concat(child);
});
range.compute();
return new RawSource(range.replace(original));
}
size() { return this.shake.original.size(); }
nameForCondition() { return this.shake.original.nameForCondition(); }
}
module.exports = ReplacementModule;