mirror of https://github.com/webpack/webpack.git
Refactor how source type is handled by the parser
This commit is contained in:
parent
a2eada1d33
commit
86e6edf1ab
|
|
@ -11,13 +11,15 @@ class JavascriptModulesPlugin {
|
|||
compiler.hooks.compilation.tap("JavascriptModulesPlugin", (compilation, {
|
||||
normalModuleFactory
|
||||
}) => {
|
||||
const createParser = () => {
|
||||
return new Parser();
|
||||
};
|
||||
|
||||
normalModuleFactory.hooks.createParser.for("javascript/auto").tap("JavascriptModulesPlugin", createParser);
|
||||
normalModuleFactory.hooks.createParser.for("javascript/dynamic").tap("JavascriptModulesPlugin", createParser);
|
||||
normalModuleFactory.hooks.createParser.for("javascript/esm").tap("JavascriptModulesPlugin", createParser);
|
||||
normalModuleFactory.hooks.createParser.for("javascript/auto").tap("JavascriptModulesPlugin", options => {
|
||||
return new Parser(options, "auto");
|
||||
});
|
||||
normalModuleFactory.hooks.createParser.for("javascript/dynamic").tap("JavascriptModulesPlugin", options => {
|
||||
return new Parser(options, "script");
|
||||
});
|
||||
normalModuleFactory.hooks.createParser.for("javascript/esm").tap("JavascriptModulesPlugin", options => {
|
||||
return new Parser(options, "module");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class NormalModuleFactory extends Tapable {
|
|||
this.ruleSet = new RuleSet(options.defaultRules.concat(options.rules));
|
||||
this.cachePredicate = typeof options.unsafeCache === "function" ? options.unsafeCache : Boolean.bind(null, options.unsafeCache);
|
||||
this.context = context || "";
|
||||
this.parserCache = {};
|
||||
this.parserCache = Object.create(null);
|
||||
this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
|
||||
let resolver = this.hooks.resolver.call(null);
|
||||
|
||||
|
|
@ -332,14 +332,13 @@ class NormalModuleFactory extends Tapable {
|
|||
else
|
||||
ident = JSON.stringify([type, parserOptions]);
|
||||
}
|
||||
const parser = this.parserCache[ident];
|
||||
if(parser)
|
||||
return parser;
|
||||
if(ident in this.parserCache) {
|
||||
return this.parserCache[ident];
|
||||
}
|
||||
return this.parserCache[ident] = this.createParser(type, parserOptions);
|
||||
}
|
||||
|
||||
createParser(type, parserOptions) {
|
||||
parserOptions = parserOptions || {};
|
||||
createParser(type, parserOptions = {}) {
|
||||
const parser = this.hooks.createParser.for(type).call(parserOptions);
|
||||
if(!parser) {
|
||||
throw new Error(`No parser registered for ${type}`);
|
||||
|
|
|
|||
101
lib/Parser.js
101
lib/Parser.js
|
|
@ -21,28 +21,19 @@ const joinRanges = (startRange, endRange) => {
|
|||
return [startRange[0], endRange[1]];
|
||||
};
|
||||
|
||||
const ECMA_VERSION = 2017;
|
||||
|
||||
const POSSIBLE_AST_OPTIONS = [{
|
||||
const defaultParserOptions = {
|
||||
ranges: true,
|
||||
locations: true,
|
||||
ecmaVersion: ECMA_VERSION,
|
||||
ecmaVersion: 2017,
|
||||
sourceType: "module",
|
||||
onComment: null,
|
||||
plugins: {
|
||||
dynamicImport: true
|
||||
}
|
||||
}, {
|
||||
ranges: true,
|
||||
locations: true,
|
||||
ecmaVersion: ECMA_VERSION,
|
||||
sourceType: "script",
|
||||
plugins: {
|
||||
dynamicImport: true
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
class Parser extends Tapable {
|
||||
constructor(options) {
|
||||
constructor(options, sourceType = "auto") {
|
||||
super();
|
||||
this.hooks = {
|
||||
evaluateTypeof: new HookMap(() => new SyncBailHook(["expression"])),
|
||||
|
|
@ -116,6 +107,7 @@ class Parser extends Tapable {
|
|||
}
|
||||
});
|
||||
this.options = options;
|
||||
this.sourceType = sourceType;
|
||||
this.scope = undefined;
|
||||
this.state = undefined;
|
||||
this.comments = undefined;
|
||||
|
|
@ -1761,37 +1753,18 @@ class Parser extends Tapable {
|
|||
|
||||
parse(source, initialState) {
|
||||
let ast;
|
||||
let comments = [];
|
||||
let comments;
|
||||
if(typeof source === "object" && source !== null) {
|
||||
ast = source;
|
||||
comments = source.comments;
|
||||
}
|
||||
for(let i = 0, len = POSSIBLE_AST_OPTIONS.length; i < len; i++) {
|
||||
if(!ast) {
|
||||
try {
|
||||
comments.length = 0;
|
||||
POSSIBLE_AST_OPTIONS[i].onComment = comments;
|
||||
ast = acorn.parse(source, POSSIBLE_AST_OPTIONS[i]);
|
||||
} catch(e) {
|
||||
// ignore the error
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!ast) {
|
||||
// for the error
|
||||
ast = acorn.parse(source, {
|
||||
ranges: true,
|
||||
locations: true,
|
||||
ecmaVersion: ECMA_VERSION,
|
||||
sourceType: "module",
|
||||
plugins: {
|
||||
dynamicImport: true
|
||||
},
|
||||
} else {
|
||||
comments = [];
|
||||
ast = Parser.parse(source, {
|
||||
sourceType: this.sourceType,
|
||||
onComment: comments
|
||||
});
|
||||
}
|
||||
if(!ast || typeof ast !== "object")
|
||||
throw new Error("Source couldn't be parsed");
|
||||
|
||||
const oldScope = this.scope;
|
||||
const oldState = this.state;
|
||||
const oldComments = this.comments;
|
||||
|
|
@ -1817,17 +1790,10 @@ class Parser extends Tapable {
|
|||
}
|
||||
|
||||
evaluate(source) {
|
||||
const ast = acorn.parse("(" + source + ")", {
|
||||
ranges: true,
|
||||
locations: true,
|
||||
ecmaVersion: ECMA_VERSION,
|
||||
sourceType: "module",
|
||||
plugins: {
|
||||
dynamicImport: true
|
||||
}
|
||||
const ast = Parser.parse("(" + source + ")", {
|
||||
sourceType: this.sourceType,
|
||||
locations: false,
|
||||
});
|
||||
if(!ast || typeof ast !== "object" || ast.type !== "Program")
|
||||
throw new Error("evaluate: Source couldn't be parsed");
|
||||
if(ast.body.length !== 1 || ast.body[0].type !== "ExpressionStatement")
|
||||
throw new Error("evaluate: Source is not a expression");
|
||||
return this.evaluateExpression(ast.body[0].expression);
|
||||
|
|
@ -1885,8 +1851,41 @@ class Parser extends Tapable {
|
|||
};
|
||||
}
|
||||
|
||||
static parse(code, options) {
|
||||
const type = options.sourceType;
|
||||
const parserOptions = Object.assign(Object.create(null), defaultParserOptions, options);
|
||||
|
||||
if(type === "auto") {
|
||||
parserOptions.sourceType = "module";
|
||||
}
|
||||
|
||||
let ast;
|
||||
let error;
|
||||
let threw = false;
|
||||
try {
|
||||
ast = acorn.parse(code, parserOptions);
|
||||
} catch(e) {
|
||||
error = e;
|
||||
threw = true;
|
||||
}
|
||||
|
||||
if(threw && type === "auto") {
|
||||
parserOptions.sourceType = "script";
|
||||
if(Array.isArray(parserOptions.onComment)) {
|
||||
parserOptions.onComment.length = 0;
|
||||
}
|
||||
try {
|
||||
ast = acorn.parse(code, parserOptions);
|
||||
threw = false;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
if(threw) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
Parser.ECMA_VERSION = ECMA_VERSION;
|
||||
|
||||
module.exports = Parser;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
const Module = require("../Module");
|
||||
const Template = require("../Template");
|
||||
const Parser = require("../Parser");
|
||||
const acorn = require("acorn");
|
||||
const eslintScope = require("eslint-scope");
|
||||
const ReplaceSource = require("webpack-sources").ReplaceSource;
|
||||
const ConcatSource = require("webpack-sources").ConcatSource;
|
||||
|
|
@ -483,11 +482,8 @@ class ConcatenatedModule extends Module {
|
|||
const code = source.source();
|
||||
let ast;
|
||||
try {
|
||||
ast = acorn.parse(code, {
|
||||
ranges: true,
|
||||
locations: true,
|
||||
ecmaVersion: Parser.ECMA_VERSION,
|
||||
sourceType: "module"
|
||||
ast = Parser.parse(code, {
|
||||
sourceType: "module",
|
||||
});
|
||||
} catch(err) {
|
||||
if(err.loc && typeof err.loc === "object" && typeof err.loc.line === "number") {
|
||||
|
|
|
|||
Loading…
Reference in New Issue