Skip to content

Commit

Permalink
Properly parse export default from when exportDefaultFrom is not …
Browse files Browse the repository at this point in the history
…enabled (#11676)

Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
  • Loading branch information
JLHwung and nicolo-ribaudo committed Jun 5, 2020
1 parent 3874470 commit 71d3527
Show file tree
Hide file tree
Showing 22 changed files with 279 additions and 6 deletions.
2 changes: 2 additions & 0 deletions packages/babel-parser/src/parser/error-message.js
Expand Up @@ -42,6 +42,8 @@ export const ErrorMessages = Object.freeze({
DuplicateRegExpFlags: "Duplicate regular expression flag",
ElementAfterRest: "Rest element must be last element",
EscapedCharNotAnIdentifier: "Invalid Unicode escape",
ExportDefaultFromAsIdentifier:
"'from' is not allowed as an identifier after 'export default'",
ForInOfLoopInitializer:
"%0 loop variable declaration may not have an initializer",
GeneratorInSingleStatementContext:
Expand Down
32 changes: 29 additions & 3 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -1857,10 +1857,24 @@ export default class StatementParser extends ExpressionParser {
}
const next = this.nextTokenStart();
return (
const hasFrom = this.isUnparsedContextual(next, "from");
if (
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 {
Expand Down Expand Up @@ -1911,6 +1925,18 @@ export default class StatementParser extends ExpressionParser {
if (isDefault) {
// Default exports
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) {
// Named exports
for (const specifier of node.specifiers) {
Expand Down
17 changes: 17 additions & 0 deletions packages/babel-parser/src/plugins/placeholders.js
Expand Up @@ -251,6 +251,23 @@ export default (superClass: Class<Parser>): Class<Parser> =>
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 {
if (node.specifiers && node.specifiers.length > 0) {
// "export %%NAME%%" has already been parsed by #parseExport.
Expand Down
9 changes: 6 additions & 3 deletions packages/babel-parser/src/tokenizer/index.js
Expand Up @@ -190,11 +190,14 @@ export default class Tokenizer extends ParserErrors {
}

nextTokenStart(): number {
const thisTokEnd = this.state.pos;
skipWhiteSpace.lastIndex = thisTokEnd;
return this.nextTokenStartSince(this.state.pos);
}

nextTokenStartSince(pos: number): number {
skipWhiteSpace.lastIndex = pos;
const skip = skipWhiteSpace.exec(this.input);
// $FlowIgnore: The skipWhiteSpace ensures to match any string
return thisTokEnd + skip[0].length;
return pos + skip[0].length;
}

lookaheadCharCode(): number {
Expand Down
@@ -0,0 +1 @@
export default from (bar);
@@ -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": []
}
}
@@ -0,0 +1 @@
export default from ?? 42;
@@ -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": []
}
}
@@ -0,0 +1 @@
export default from;
@@ -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": []
}
}
@@ -0,0 +1,2 @@
export default from
"bar";
@@ -0,0 +1,4 @@
{
"sourceType": "module",
"plugins": ["exportDefaultFrom"]
}
@@ -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": []
}
}
@@ -0,0 +1 @@
export default \u{66}rom;
@@ -0,0 +1,4 @@
{
"plugins": ["exportDefaultFrom"],
"sourceType": "module"
}
@@ -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": []
}
}
@@ -0,0 +1 @@
export default (from);
@@ -0,0 +1,4 @@
{
"plugins": ["exportDefaultFrom"],
"sourceType": "module"
}
@@ -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": []
}
}
@@ -0,0 +1 @@
export default from;
@@ -0,0 +1,4 @@
{
"plugins": ["exportDefaultFrom"],
"sourceType": "module"
}
@@ -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": []
}
}

0 comments on commit 71d3527

Please sign in to comment.