Skip to content

Commit

Permalink
Merge pull request #8294 from ljqx/zhbliu/import-parser-plugin-fix-co…
Browse files Browse the repository at this point in the history
…ntext-return

[ImportParserPlugin] fix return value when creating context for `import()`
  • Loading branch information
sokra committed Nov 3, 2018
2 parents 9e397b5 + 22aee1e commit eb68316
Show file tree
Hide file tree
Showing 21 changed files with 296 additions and 70 deletions.
43 changes: 39 additions & 4 deletions lib/BasicEvaluatedExpression.js
Expand Up @@ -29,11 +29,13 @@ class BasicEvaluatedExpression {
this.regExp = null;
this.string = null;
this.quasis = null;
this.parts = null;
this.array = null;
this.items = null;
this.options = null;
this.prefix = null;
this.postfix = null;
this.expression = null;
}

isNull() {
Expand Down Expand Up @@ -105,10 +107,36 @@ class BasicEvaluatedExpression {
: undefined;
}
if (this.isTemplateString()) {
for (const quasi of this.quasis) {
if (quasi.asBool()) return true;
const str = this.asString();
if (typeof str === "string") return str !== "";
}
return undefined;
}

asString() {
if (this.isBoolean()) return `${this.bool}`;
if (this.isNull()) return "null";
if (this.isString()) return this.string;
if (this.isNumber()) return `${this.number}`;
if (this.isRegExp()) return `${this.regExp}`;
if (this.isArray()) {
let array = [];
for (const item of this.items) {
const itemStr = item.asString();
if (itemStr === undefined) return undefined;
array.push(itemStr);
}
return `${array}`;
}
if (this.isConstArray()) return `${this.array}`;
if (this.isTemplateString()) {
let str = "";
for (const part of this.parts) {
const partStr = part.asString();
if (partStr === undefined) return undefined;
str += partStr;
}
// can't tell if string will be empty without executing
return str;
}
return undefined;
}
Expand Down Expand Up @@ -184,9 +212,11 @@ class BasicEvaluatedExpression {
return this;
}

setTemplateString(quasis) {
setTemplateString(quasis, parts, kind) {
this.type = TypeTemplateString;
this.quasis = quasis;
this.parts = parts;
this.templateStringKind = kind;
return this;
}

Expand All @@ -206,6 +236,11 @@ class BasicEvaluatedExpression {
this.range = range;
return this;
}

setExpression(expression) {
this.expression = expression;
return this;
}
}

module.exports = BasicEvaluatedExpression;
87 changes: 49 additions & 38 deletions lib/Parser.js
Expand Up @@ -602,62 +602,66 @@ class Parser extends Tapable {

/**
* @param {string} kind "cooked" | "raw"
* @param {TODO[]} quasis quasis
* @param {TODO[]} expressions expressions
* @returns {BasicEvaluatedExpression[]} Simplified template
* @param {TODO} templateLiteralExpr TemplateLiteral expr
* @returns {{quasis: BasicEvaluatedExpression[], parts: BasicEvaluatedExpression[]}} Simplified template
*/
const getSimplifiedTemplateResult = (kind, quasis, expressions) => {
const getSimplifiedTemplateResult = (kind, templateLiteralExpr) => {
const quasis = [];
const parts = [];

for (let i = 0; i < quasis.length; i++) {
parts.push(
new BasicEvaluatedExpression()
.setString(quasis[i].value[kind])
.setRange(quasis[i].range)
);
for (let i = 0; i < templateLiteralExpr.quasis.length; i++) {
const quasiExpr = templateLiteralExpr.quasis[i];
const quasi = quasiExpr.value[kind];

if (i > 0) {
const prevExpr = parts[parts.length - 2],
lastExpr = parts[parts.length - 1];
const expr = this.evaluateExpression(expressions[i - 1]);
if (!(expr.isString() || expr.isNumber())) continue;

prevExpr.setString(
prevExpr.string +
(expr.isString() ? expr.string : expr.number) +
lastExpr.string
const prevExpr = parts[parts.length - 1];
const expr = this.evaluateExpression(
templateLiteralExpr.expressions[i - 1]
);
prevExpr.setRange([prevExpr.range[0], lastExpr.range[1]]);
parts.pop();
const exprAsString = expr.asString();
if (typeof exprAsString === "string") {
// We can merge quasi + expr + quasi when expr
// is a const string

prevExpr.setString(prevExpr.string + exprAsString + quasi);
prevExpr.setRange([prevExpr.range[0], quasiExpr.range[1]]);
// We unset the expression as it doesn't match to a single expression
prevExpr.setExpression(undefined);
continue;
}
parts.push(expr);
}

const part = new BasicEvaluatedExpression()
.setString(quasi)
.setRange(quasiExpr.range)
.setExpression(quasiExpr);
quasis.push(part);
parts.push(part);
}
return parts;
return {
quasis,
parts
};
};

this.hooks.evaluate.for("TemplateLiteral").tap("Parser", node => {
const parts = getSimplifiedTemplateResult.call(
this,
"cooked",
node.quasis,
node.expressions
);
const { quasis, parts } = getSimplifiedTemplateResult("cooked", node);
if (parts.length === 1) {
return parts[0].setRange(node.range);
}
return new BasicEvaluatedExpression()
.setTemplateString(parts)
.setTemplateString(quasis, parts, "cooked")
.setRange(node.range);
});
this.hooks.evaluate.for("TaggedTemplateExpression").tap("Parser", node => {
if (this.evaluateExpression(node.tag).identifier !== "String.raw") return;
const parts = getSimplifiedTemplateResult.call(
this,
"raw",
node.quasi.quasis,
node.quasi.expressions
);
const { quasis, parts } = getSimplifiedTemplateResult("raw", node.quasi);
if (parts.length === 1) {
return parts[0].setRange(node.range);
}
return new BasicEvaluatedExpression()
.setTemplateString(parts)
.setTemplateString(quasis, parts, "raw")
.setRange(node.range);
});

Expand Down Expand Up @@ -1926,13 +1930,20 @@ class Parser extends Tapable {
const hook = this.hooks.evaluate.get(expression.type);
if (hook !== undefined) {
const result = hook.call(expression);
if (result !== undefined) return result;
if (result !== undefined) {
if (result) {
result.setExpression(expression);
}
return result;
}
}
} catch (e) {
console.warn(e);
// ignore error
}
return new BasicEvaluatedExpression().setRange(expression.range);
return new BasicEvaluatedExpression()
.setRange(expression.range)
.setExpression(expression);
}

parseString(expression) {
Expand Down
4 changes: 3 additions & 1 deletion lib/dependencies/AMDDefineDependencyParserPlugin.js
Expand Up @@ -136,7 +136,9 @@ class AMDDefineDependencyParserPlugin {
param.range,
param,
expr,
this.options
this.options,
{},
parser
);
if (!dep) return;
dep.loc = expr.loc;
Expand Down
4 changes: 3 additions & 1 deletion lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js
Expand Up @@ -142,7 +142,9 @@ class AMDRequireDependenciesBlockParserPlugin {
param.range,
param,
expr,
this.options
this.options,
{},
parser
);
if (!dep) return;
dep.loc = expr.loc;
Expand Down
4 changes: 3 additions & 1 deletion lib/dependencies/CommonJsRequireDependencyParserPlugin.js
Expand Up @@ -35,7 +35,9 @@ class CommonJsRequireDependencyParserPlugin {
expr.range,
param,
expr,
options
options,
{},
parser
);
if (!dep) return;
dep.loc = expr.loc;
Expand Down
92 changes: 70 additions & 22 deletions lib/dependencies/ContextDependencyHelpers.js
Expand Up @@ -47,27 +47,40 @@ ContextDependencyHelpers.create = (
param,
expr,
options,
contextOptions
contextOptions,
// when parser is not passed in, expressions won't be walked
parser = null
) => {
if (param.isTemplateString()) {
let prefixRaw = param.quasis[0].string;
let postfixRaw =
param.quasis.length > 1
? param.quasis[param.quasis.length - 1].string
: "";
const prefixRange = [param.quasis[0].range[0], param.quasis[0].range[1]];
const postfixRange =
param.quasis.length > 1
? param.quasis[param.quasis.length - 1].range
: "";

const valueRange = param.range;
const { context, prefix } = splitContextFromPrefix(prefixRaw);
const { postfix, query } = splitQueryFromPostfix(postfixRaw);
// If there are more than two quasis, maybe the generated RegExp can be more precise?

// When there are more than two quasis, the generated RegExp can be more precise
// We join the quasis with the expression regexp
const innerQuasis = param.quasis.slice(1, param.quasis.length - 1);
const innerRegExp =
options.wrappedContextRegExp.source +
innerQuasis
.map(q => quotemeta(q.string) + options.wrappedContextRegExp.source)
.join("");

// Example: `./context/pre${e}inner${e}inner2${e}post?query`
// context: "./context"
// prefix: "./pre"
// innerQuasis: [BEE("inner"), BEE("inner2")]
// (BEE = BasicEvaluatedExpression)
// postfix: "post"
// query: "?query"
// regExp: /^\.\/pre.*inner.*inner2.*post$/
const regExp = new RegExp(
`^${quotemeta(prefix)}${options.wrappedContextRegExp.source}${quotemeta(
postfix
)}$`
`^${quotemeta(prefix)}${innerRegExp}${quotemeta(postfix)}$`
);
const dep = new Dep(
Object.assign(
Expand All @@ -84,18 +97,48 @@ ContextDependencyHelpers.create = (
);
dep.loc = expr.loc;
const replaces = [];
if (prefixRange && prefix !== prefixRaw) {
replaces.push({
range: prefixRange,
value: prefix
});
}
if (postfixRange && postfix !== postfixRaw) {
replaces.push({
range: postfixRange,
value: postfix
});
}

param.parts.forEach((part, i) => {
if (i % 2 === 0) {
// Quasis or merged quasi
let range = part.range;
let value = part.string;
if (param.templateStringKind === "cooked") {
value = JSON.stringify(value);
value = value.slice(1, value.length - 1);
}
if (i === 0) {
// prefix
value = prefix;
range = [param.range[0], part.range[1]];
value =
(param.templateStringKind === "cooked" ? "`" : "String.raw`") +
value;
} else if (i === param.parts.length - 1) {
// postfix
value = postfix;
range = [part.range[0], param.range[1]];
value = value + "`";
} else if (
part.expression &&
part.expression.type === "TemplateElement" &&
part.expression.value.raw === value
) {
// Shortcut when it's a single quasi and doesn't need to be replaced
return;
}
replaces.push({
range,
value
});
} else {
// Expression
if (parser) {
parser.walkExpression(part.expression);
}
}
});

dep.replaces = replaces;
dep.critical =
options.wrappedContextCritical &&
Expand Down Expand Up @@ -172,6 +215,11 @@ ContextDependencyHelpers.create = (
dep.critical =
options.exprContextCritical &&
"the request of a dependency is an expression";

if (parser) {
parser.walkExpression(param.expression);
}

return dep;
}
};
3 changes: 2 additions & 1 deletion lib/dependencies/ImportParserPlugin.js
Expand Up @@ -248,7 +248,8 @@ class ImportParserPlugin {
namespaceObject: parser.state.module.buildMeta.strictHarmonyModule
? "strict"
: true
}
},
parser
);
if (!dep) return;
dep.loc = expr.loc;
Expand Down
3 changes: 2 additions & 1 deletion lib/dependencies/RequireResolveDependencyParserPlugin.js
Expand Up @@ -61,7 +61,8 @@ class RequireResolveDependencyParserPlugin {
options,
{
mode: weak ? "weak" : "sync"
}
},
parser
);
if (!dep) return;
dep.loc = expr.loc;
Expand Down
2 changes: 1 addition & 1 deletion test/cases/parsing/issue-7778/index.js
Expand Up @@ -34,7 +34,7 @@ it("should detect query strings in dynamic import as a static value 2", function
]);
});

it("should detect query strings in dynamic import as a static value 2", function() {
it("should detect query strings in dynamic import as a static value 3", function() {
var testFileName = "a";

return Promise.all([
Expand Down

0 comments on commit eb68316

Please sign in to comment.