Skip to content

Commit

Permalink
Parse body of module blocks as Program
Browse files Browse the repository at this point in the history
  • Loading branch information
sosukesuzuki committed Jan 22, 2021
1 parent c1ad143 commit f9173a3
Show file tree
Hide file tree
Showing 20 changed files with 459 additions and 320 deletions.
6 changes: 2 additions & 4 deletions packages/babel-generator/src/generators/expressions.js
Expand Up @@ -274,13 +274,11 @@ export function ModuleExpression(node: Object) {
this.word("module");
this.space();
this.token("{");
if (node.body.length === 0) {
if (node.body.body.length === 0) {
this.token("}");
} else {
this.newline();
this.printSequence(node.body, node, {
indent: true,
});
this.printSequence(node.body.body, node, { indent: true });
this.rightBrace();
}
}
2 changes: 1 addition & 1 deletion packages/babel-parser/ast/spec.md
Expand Up @@ -1092,7 +1092,7 @@ interface DoExpression <: Expression {
```js
interface ModuleExpression <: Expression {
type: "ModuleExpression";
body: [ Statement ];
body: Program
}
```

Expand Down
17 changes: 13 additions & 4 deletions packages/babel-parser/src/parser/expression.js
Expand Up @@ -40,7 +40,6 @@ import {
SCOPE_FUNCTION,
SCOPE_SUPER,
SCOPE_PROGRAM,
SCOPE_OTHER,
} from "../util/scopeflags";
import { ExpressionErrors } from "./util";
import {
Expand All @@ -57,6 +56,10 @@ import {
} from "../util/expression-scope.js";
import { Errors } from "./error";

/*::
import type { SourceType } from "../options";
*/

export default class ExpressionParser extends LValParser {
// Forward-declaration: defined in statement.js
/*::
Expand Down Expand Up @@ -86,6 +89,9 @@ export default class ExpressionParser extends LValParser {
end: TokenType,
afterBlockParse?: (hasStrictModeDirective: boolean) => void
) => void
+parseProgram: (
program: N.Program, end: TokenType, sourceType?: SourceType
) => N.Program
*/

// For object literal, check if property __proto__ has been used more than once.
Expand Down Expand Up @@ -2633,23 +2639,26 @@ export default class ExpressionParser extends LValParser {
parseModuleExpression(): N.ModuleExpression {
this.expectPlugin("moduleBlocks");
const node = this.startNode<N.ModuleExpression>();
node.body = [];
this.next(); // eat "module"
const oldLabels = this.state.labels;
this.state.labels = [];
const oldExportedIdentifiers = this.state.exportedIdentifiers;
this.state.exportedIdentifiers = [];
this.eat(tt.braceL);
this.scope.enter(SCOPE_OTHER);
this.scope.enter(SCOPE_PROGRAM);
const oldUndefinedExports = this.scope.undefinedExports;
this.scope.undefinedExports = new Map();
let paramFlags = PARAM;
if (this.hasPlugin("topLevelAwait")) {
paramFlags |= PARAM_AWAIT;
}
this.prodParam.enter(paramFlags);
const oldInModule = this.inModule;
this.inModule = true;
this.parseBlockOrModuleBlockBody(node.body, undefined, true, tt.braceR);
const program = this.startNode<N.Program>();
node.body = this.parseProgram(program, tt.braceR, "module");
this.scope.exit();
this.scope.undefinedExports = oldUndefinedExports;
this.prodParam.exit();
this.inModule = oldInModule;
this.eat(tt.braceR);
Expand Down
25 changes: 15 additions & 10 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -34,6 +34,7 @@ import {
newExpressionScope,
newParameterDeclarationScope,
} from "../util/expression-scope";
import type { SourceType } from "../options";

const loopLabel = { kind: "loop" },
switchLabel = { kind: "switch" };
Expand All @@ -54,12 +55,22 @@ export default class StatementParser extends ExpressionParser {
// to its body instead of creating a new node.

parseTopLevel(file: N.File, program: N.Program): N.File {
program.sourceType = this.options.sourceType;
file.program = this.parseProgram(program);
file.comments = this.state.comments;

program.interpreter = this.parseInterpreterDirective();
if (this.options.tokens) file.tokens = this.tokens;

this.parseBlockBody(program, true, true, tt.eof);
return this.finishNode(file, "File");
}

parseProgram(
program: N.Program,
end: TokenType = tt.eof,
sourceType: SourceType = this.options.sourceType,
): N.Program {
program.sourceType = sourceType;
program.interpreter = this.parseInterpreterDirective();
this.parseBlockBody(program, true, true, end);
if (
this.inModule &&
!this.options.allowUndeclaredExports &&
Expand All @@ -71,13 +82,7 @@ export default class StatementParser extends ExpressionParser {
this.raise(pos, Errors.ModuleExportUndefined, name);
}
}

file.program = this.finishNode(program, "Program");
file.comments = this.state.comments;

if (this.options.tokens) file.tokens = this.tokens;

return this.finishNode(file, "File");
return this.finishNode<N.Program>(program, "Program");
}

// TODO
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-parser/src/types.js
Expand Up @@ -657,7 +657,7 @@ export type TemplateElement = NodeBase & {

export type ModuleExpression = NodeBase & {
type: "ModuleExpression",
body: Array<Statement>,
body: Program,
};

// Patterns
Expand Down
9 changes: 6 additions & 3 deletions packages/babel-parser/src/util/scope.js
Expand Up @@ -170,13 +170,16 @@ export default class ScopeHandler<IScope: Scope = Scope> {
}

checkLocalExport(id: N.Identifier) {
const currentScope = this.currentScope();
const scope =
currentScope.flags & SCOPE_PROGRAM ? currentScope : this.scopeStack[0];
if (
this.scopeStack[0].lexical.indexOf(id.name) === -1 &&
this.scopeStack[0].var.indexOf(id.name) === -1 &&
scope.lexical.indexOf(id.name) === -1 &&
scope.var.indexOf(id.name) === -1 &&
// In strict mode, scope.functions will always be empty.
// Modules are strict by default, but the `scriptMode` option
// can overwrite this behavior.
this.scopeStack[0].functions.indexOf(id.name) === -1
scope.functions.indexOf(id.name) === -1
) {
this.undefinedExports.set(id.name, id.start);
}
Expand Down
Expand Up @@ -16,30 +16,37 @@
"expression": {
"type": "ModuleExpression",
"start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"body": [
{
"type": "LabeledStatement",
"start":9,"end":17,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":17}},
"body": {
"type": "ExpressionStatement",
"start":16,"end":17,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":17}},
"expression": {
"type": "NumericLiteral",
"body": {
"type": "Program",
"start":9,"end":19,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":19}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "LabeledStatement",
"start":9,"end":17,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":17}},
"body": {
"type": "ExpressionStatement",
"start":16,"end":17,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":17}},
"extra": {
"rawValue": 3,
"raw": "3"
},
"value": 3
"expression": {
"type": "NumericLiteral",
"start":16,"end":17,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":17}},
"extra": {
"rawValue": 3,
"raw": "3"
},
"value": 3
}
},
"label": {
"type": "Identifier",
"start":9,"end":14,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":14},"identifierName":"await"},
"name": "await"
}
},
"label": {
"type": "Identifier",
"start":9,"end":14,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":14},"identifierName":"await"},
"name": "await"
}
}
]
],
"directives": []
}
}
}
],
Expand Down
Expand Up @@ -2,8 +2,7 @@
"type": "File",
"start":0,"end":67,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":2}},
"errors": [
"SyntaxError: `foo` has already been exported. Exported identifiers must be unique. (3:11)",
"SyntaxError: Export 'foo' is not defined (3:11)"
"SyntaxError: `foo` has already been exported. Exported identifiers must be unique. (3:11)"
],
"program": {
"type": "Program",
Expand All @@ -26,61 +25,68 @@
"init": {
"type": "ModuleExpression",
"start":10,"end":66,"loc":{"start":{"line":1,"column":10},"end":{"line":4,"column":1}},
"body": [
{
"type": "ExportNamedDeclaration",
"start":21,"end":46,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":27}},
"specifiers": [],
"source": null,
"declaration": {
"type": "VariableDeclaration",
"start":28,"end":46,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":27}},
"declarations": [
"body": {
"type": "Program",
"start":21,"end":66,"loc":{"start":{"line":2,"column":2},"end":{"line":4,"column":1}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExportNamedDeclaration",
"start":21,"end":46,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":27}},
"specifiers": [],
"source": null,
"declaration": {
"type": "VariableDeclaration",
"start":28,"end":46,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":27}},
"declarations": [
{
"type": "VariableDeclarator",
"start":34,"end":45,"loc":{"start":{"line":2,"column":15},"end":{"line":2,"column":26}},
"id": {
"type": "Identifier",
"start":34,"end":37,"loc":{"start":{"line":2,"column":15},"end":{"line":2,"column":18},"identifierName":"foo"},
"name": "foo"
},
"init": {
"type": "StringLiteral",
"start":40,"end":45,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":26}},
"extra": {
"rawValue": "foo",
"raw": "\"foo\""
},
"value": "foo"
}
}
],
"kind": "const"
}
},
{
"type": "ExportNamedDeclaration",
"start":49,"end":64,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":17}},
"specifiers": [
{
"type": "VariableDeclarator",
"start":34,"end":45,"loc":{"start":{"line":2,"column":15},"end":{"line":2,"column":26}},
"id": {
"type": "ExportSpecifier",
"start":58,"end":61,"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":14}},
"local": {
"type": "Identifier",
"start":34,"end":37,"loc":{"start":{"line":2,"column":15},"end":{"line":2,"column":18},"identifierName":"foo"},
"start":58,"end":61,"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":14},"identifierName":"foo"},
"name": "foo"
},
"init": {
"type": "StringLiteral",
"start":40,"end":45,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":26}},
"extra": {
"rawValue": "foo",
"raw": "\"foo\""
},
"value": "foo"
"exported": {
"type": "Identifier",
"start":58,"end":61,"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":14},"identifierName":"foo"},
"name": "foo"
}
}
],
"kind": "const"
"source": null,
"declaration": null
}
},
{
"type": "ExportNamedDeclaration",
"start":49,"end":64,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":17}},
"specifiers": [
{
"type": "ExportSpecifier",
"start":58,"end":61,"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":14}},
"local": {
"type": "Identifier",
"start":58,"end":61,"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":14},"identifierName":"foo"},
"name": "foo"
},
"exported": {
"type": "Identifier",
"start":58,"end":61,"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":14},"identifierName":"foo"},
"name": "foo"
}
}
],
"source": null,
"declaration": null
}
]
],
"directives": []
}
}
}
],
Expand Down
@@ -0,0 +1,3 @@
module {
export { foo }
}

0 comments on commit f9173a3

Please sign in to comment.