Properly parse export default from when exportDefaultFrom is not enabled (#11676)

Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
This commit is contained in:
Huáng Jùnliàng 2020-06-05 10:08:21 -04:00 committed by GitHub
parent 3874470841
commit 71d3527ef5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 279 additions and 6 deletions

View File

@ -42,6 +42,8 @@ export const ErrorMessages = Object.freeze({
DuplicateRegExpFlags: "Duplicate regular expression flag", DuplicateRegExpFlags: "Duplicate regular expression flag",
ElementAfterRest: "Rest element must be last element", ElementAfterRest: "Rest element must be last element",
EscapedCharNotAnIdentifier: "Invalid Unicode escape", EscapedCharNotAnIdentifier: "Invalid Unicode escape",
ExportDefaultFromAsIdentifier:
"'from' is not allowed as an identifier after 'export default'",
ForInOfLoopInitializer: ForInOfLoopInitializer:
"%0 loop variable declaration may not have an initializer", "%0 loop variable declaration may not have an initializer",
GeneratorInSingleStatementContext: GeneratorInSingleStatementContext:

View File

@ -1857,10 +1857,24 @@ export default class StatementParser extends ExpressionParser {
} }
const next = this.nextTokenStart(); const next = this.nextTokenStart();
return ( const hasFrom = this.isUnparsedContextual(next, "from");
if (
this.input.charCodeAt(next) === charCodes.comma || this.input.charCodeAt(next) === charCodes.comma ||
this.isUnparsedContextual(next, "from") (this.match(tt.name) && hasFrom)
); ) {
return true;
}
// lookahead again when `export default from` is seen
if (this.match(tt._default) && hasFrom) {
const nextAfterFrom = this.input.charCodeAt(
this.nextTokenStartSince(next + 4),
);
return (
nextAfterFrom === charCodes.quotationMark ||
nextAfterFrom === charCodes.apostrophe
);
}
return false;
} }
parseExportFrom(node: N.ExportNamedDeclaration, expect?: boolean): void { parseExportFrom(node: N.ExportNamedDeclaration, expect?: boolean): void {
@ -1911,6 +1925,18 @@ export default class StatementParser extends ExpressionParser {
if (isDefault) { if (isDefault) {
// Default exports // Default exports
this.checkDuplicateExports(node, "default"); this.checkDuplicateExports(node, "default");
if (this.hasPlugin("exportDefaultFrom")) {
const declaration = ((node: any): N.ExportDefaultDeclaration)
.declaration;
if (
declaration.type === "Identifier" &&
declaration.name === "from" &&
declaration.end - declaration.start === 4 && // does not contain escape
!declaration.extra?.parenthesized
) {
this.raise(declaration.start, Errors.ExportDefaultFromAsIdentifier);
}
}
} else if (node.specifiers && node.specifiers.length) { } else if (node.specifiers && node.specifiers.length) {
// Named exports // Named exports
for (const specifier of node.specifiers) { for (const specifier of node.specifiers) {

View File

@ -251,6 +251,23 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return super.parseExport(node); return super.parseExport(node);
} }
isExportDefaultSpecifier(): boolean {
if (this.match(tt._default)) {
const next = this.nextTokenStart();
if (this.isUnparsedContextual(next, "from")) {
if (
this.input.startsWith(
tt.placeholder.label,
this.nextTokenStartSince(next + 4),
)
) {
return true;
}
}
}
return super.isExportDefaultSpecifier();
}
maybeParseExportDefaultSpecifier(node: N.Node): boolean { maybeParseExportDefaultSpecifier(node: N.Node): boolean {
if (node.specifiers && node.specifiers.length > 0) { if (node.specifiers && node.specifiers.length > 0) {
// "export %%NAME%%" has already been parsed by #parseExport. // "export %%NAME%%" has already been parsed by #parseExport.

View File

@ -190,11 +190,14 @@ export default class Tokenizer extends ParserErrors {
} }
nextTokenStart(): number { nextTokenStart(): number {
const thisTokEnd = this.state.pos; return this.nextTokenStartSince(this.state.pos);
skipWhiteSpace.lastIndex = thisTokEnd; }
nextTokenStartSince(pos: number): number {
skipWhiteSpace.lastIndex = pos;
const skip = skipWhiteSpace.exec(this.input); const skip = skipWhiteSpace.exec(this.input);
// $FlowIgnore: The skipWhiteSpace ensures to match any string // $FlowIgnore: The skipWhiteSpace ensures to match any string
return thisTokEnd + skip[0].length; return pos + skip[0].length;
} }
lookaheadCharCode(): number { lookaheadCharCode(): number {

View File

@ -0,0 +1 @@
export default from (bar);

View File

@ -0,0 +1,33 @@
{
"type": "File",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"program": {
"type": "Program",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExportDefaultDeclaration",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"declaration": {
"type": "CallExpression",
"start":15,"end":25,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":25}},
"callee": {
"type": "Identifier",
"start":15,"end":19,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":19},"identifierName":"from"},
"name": "from"
},
"arguments": [
{
"type": "Identifier",
"start":21,"end":24,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":24},"identifierName":"bar"},
"name": "bar"
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1 @@
export default from ?? 42;

View File

@ -0,0 +1,36 @@
{
"type": "File",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"program": {
"type": "Program",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExportDefaultDeclaration",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"declaration": {
"type": "LogicalExpression",
"start":15,"end":25,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":25}},
"left": {
"type": "Identifier",
"start":15,"end":19,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":19},"identifierName":"from"},
"name": "from"
},
"operator": "??",
"right": {
"type": "NumericLiteral",
"start":23,"end":25,"loc":{"start":{"line":1,"column":23},"end":{"line":1,"column":25}},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,22 @@
{
"type": "File",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"program": {
"type": "Program",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExportDefaultDeclaration",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"declaration": {
"type": "Identifier",
"start":15,"end":19,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":19},"identifierName":"from"},
"name": "from"
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,2 @@
export default from
"bar";

View File

@ -0,0 +1,4 @@
{
"sourceType": "module",
"plugins": ["exportDefaultFrom"]
}

View File

@ -0,0 +1,37 @@
{
"type": "File",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":6}},
"program": {
"type": "Program",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":6}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExportNamedDeclaration",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":6}},
"specifiers": [
{
"type": "ExportDefaultSpecifier",
"start":7,"end":14,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":14}},
"exported": {
"type": "Identifier",
"start":7,"end":14,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":14},"identifierName":"default"},
"name": "default"
}
}
],
"source": {
"type": "StringLiteral",
"start":20,"end":25,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}},
"extra": {
"rawValue": "bar",
"raw": "\"bar\""
},
"value": "bar"
}
}
],
"directives": []
}
}

View File

@ -0,0 +1 @@
export default \u{66}rom;

View File

@ -0,0 +1,4 @@
{
"plugins": ["exportDefaultFrom"],
"sourceType": "module"
}

View File

@ -0,0 +1,22 @@
{
"type": "File",
"start":0,"end":25,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":25}},
"program": {
"type": "Program",
"start":0,"end":25,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":25}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExportDefaultDeclaration",
"start":0,"end":25,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":25}},
"declaration": {
"type": "Identifier",
"start":15,"end":24,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":24},"identifierName":"from"},
"name": "from"
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
{
"plugins": ["exportDefaultFrom"],
"sourceType": "module"
}

View File

@ -0,0 +1,26 @@
{
"type": "File",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}},
"program": {
"type": "Program",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExportDefaultDeclaration",
"start":0,"end":22,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}},
"declaration": {
"type": "Identifier",
"start":16,"end":20,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":20},"identifierName":"from"},
"name": "from",
"extra": {
"parenthesized": true,
"parenStart": 15
}
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
{
"plugins": ["exportDefaultFrom"],
"sourceType": "module"
}

View File

@ -0,0 +1,25 @@
{
"type": "File",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"errors": [
"SyntaxError: 'from' is not allowed as an identifier after 'export default' (1:15)"
],
"program": {
"type": "Program",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExportDefaultDeclaration",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"declaration": {
"type": "Identifier",
"start":15,"end":19,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":19},"identifierName":"from"},
"name": "from"
}
}
],
"directives": []
}
}