Refactor import and export parsing (#9326)
* [parser] Refactor import parsing * [parser] Refactor export parsing * Fix types
This commit is contained in:
parent
f77c450cda
commit
65febdd13a
@ -1470,35 +1470,109 @@ export default class StatementParser extends ExpressionParser {
|
||||
|
||||
// Parses module export declaration.
|
||||
|
||||
// TODO: better type. Node is an N.AnyExport.
|
||||
parseExport(node: N.Node): N.Node {
|
||||
// export * from '...'
|
||||
if (this.shouldParseExportStar()) {
|
||||
this.parseExportStar(node);
|
||||
if (node.type === "ExportAllDeclaration") return node;
|
||||
} else if (this.isExportDefaultSpecifier()) {
|
||||
this.expectPlugin("exportDefaultFrom");
|
||||
const specifier = this.startNode();
|
||||
specifier.exported = this.parseIdentifier(true);
|
||||
const specifiers = [this.finishNode(specifier, "ExportDefaultSpecifier")];
|
||||
node.specifiers = specifiers;
|
||||
if (this.match(tt.comma) && this.lookahead().type === tt.star) {
|
||||
this.expect(tt.comma);
|
||||
const specifier = this.startNode();
|
||||
this.expect(tt.star);
|
||||
this.expectContextual("as");
|
||||
specifier.exported = this.parseIdentifier();
|
||||
specifiers.push(this.finishNode(specifier, "ExportNamespaceSpecifier"));
|
||||
} else {
|
||||
this.parseExportSpecifiersMaybe(node);
|
||||
}
|
||||
parseExport(node: N.Node): N.AnyExport {
|
||||
const hasDefault = this.maybeParseExportDefaultSpecifier(node);
|
||||
const parseAfterDefault = !hasDefault || this.eat(tt.comma);
|
||||
const hasStar = parseAfterDefault && this.eatExportStar(node);
|
||||
const hasNamespace =
|
||||
hasStar && this.maybeParseExportNamespaceSpecifier(node);
|
||||
const parseAfterNamespace =
|
||||
parseAfterDefault && (!hasNamespace || this.eat(tt.comma));
|
||||
const isFromRequired = hasDefault || hasStar;
|
||||
|
||||
if (hasStar && !hasNamespace) {
|
||||
if (hasDefault) this.unexpected();
|
||||
this.parseExportFrom(node, true);
|
||||
} else if (this.eat(tt._default)) {
|
||||
|
||||
return this.finishNode(node, "ExportAllDeclaration");
|
||||
}
|
||||
|
||||
const hasSpecifiers = this.maybeParseExportNamedSpecifiers(node);
|
||||
|
||||
if (
|
||||
(hasDefault && parseAfterDefault && !hasStar && !hasSpecifiers) ||
|
||||
(hasNamespace && parseAfterNamespace && !hasSpecifiers)
|
||||
) {
|
||||
throw this.unexpected(null, tt.braceL);
|
||||
}
|
||||
|
||||
let hasDeclaration;
|
||||
if (isFromRequired || hasSpecifiers) {
|
||||
hasDeclaration = false;
|
||||
this.parseExportFrom(node, isFromRequired);
|
||||
} else {
|
||||
hasDeclaration = this.maybeParseExportDeclaration(node);
|
||||
}
|
||||
|
||||
if (isFromRequired || hasSpecifiers || hasDeclaration) {
|
||||
this.checkExport(node, true);
|
||||
return this.finishNode(node, "ExportNamedDeclaration");
|
||||
}
|
||||
|
||||
if (this.eat(tt._default)) {
|
||||
// export default ...
|
||||
node.declaration = this.parseExportDefaultExpression();
|
||||
this.checkExport(node, true, true);
|
||||
|
||||
return this.finishNode(node, "ExportDefaultDeclaration");
|
||||
} else if (this.shouldParseExportDeclaration()) {
|
||||
}
|
||||
|
||||
throw this.unexpected(null, tt.braceL);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
eatExportStar(node: N.Node): boolean {
|
||||
return this.eat(tt.star);
|
||||
}
|
||||
|
||||
maybeParseExportDefaultSpecifier(node: N.Node): boolean {
|
||||
if (this.isExportDefaultSpecifier()) {
|
||||
// export defaultObj ...
|
||||
this.expectPlugin("exportDefaultFrom");
|
||||
const specifier = this.startNode();
|
||||
specifier.exported = this.parseIdentifier(true);
|
||||
node.specifiers = [this.finishNode(specifier, "ExportDefaultSpecifier")];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
maybeParseExportNamespaceSpecifier(node: N.Node): boolean {
|
||||
if (this.isContextual("as")) {
|
||||
if (!node.specifiers) node.specifiers = [];
|
||||
this.expectPlugin("exportNamespaceFrom");
|
||||
|
||||
const specifier = this.startNodeAt(
|
||||
this.state.lastTokStart,
|
||||
this.state.lastTokStartLoc,
|
||||
);
|
||||
|
||||
this.next();
|
||||
|
||||
specifier.exported = this.parseIdentifier(true);
|
||||
node.specifiers.push(
|
||||
this.finishNode(specifier, "ExportNamespaceSpecifier"),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
maybeParseExportNamedSpecifiers(node: N.Node): boolean {
|
||||
if (this.match(tt.braceL)) {
|
||||
if (!node.specifiers) node.specifiers = [];
|
||||
node.specifiers.push(...this.parseExportSpecifiers());
|
||||
|
||||
node.source = null;
|
||||
node.declaration = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
maybeParseExportDeclaration(node: N.Node): boolean {
|
||||
if (this.shouldParseExportDeclaration()) {
|
||||
if (this.isContextual("async")) {
|
||||
const next = this.lookahead();
|
||||
|
||||
@ -1511,14 +1585,10 @@ export default class StatementParser extends ExpressionParser {
|
||||
node.specifiers = [];
|
||||
node.source = null;
|
||||
node.declaration = this.parseExportDeclaration(node);
|
||||
} else {
|
||||
// export { x, y as z } [from '...']
|
||||
node.declaration = null;
|
||||
node.specifiers = this.parseExportSpecifiers();
|
||||
this.parseExportFrom(node);
|
||||
|
||||
return true;
|
||||
}
|
||||
this.checkExport(node, true);
|
||||
return this.finishNode(node, "ExportNamedDeclaration");
|
||||
return false;
|
||||
}
|
||||
|
||||
isAsyncFunction() {
|
||||
@ -1605,17 +1675,9 @@ export default class StatementParser extends ExpressionParser {
|
||||
);
|
||||
}
|
||||
|
||||
parseExportSpecifiersMaybe(node: N.ExportNamedDeclaration): void {
|
||||
if (this.eat(tt.comma)) {
|
||||
node.specifiers = node.specifiers.concat(this.parseExportSpecifiers());
|
||||
}
|
||||
}
|
||||
|
||||
parseExportFrom(node: N.ExportNamedDeclaration, expect?: boolean): void {
|
||||
if (this.eatContextual("from")) {
|
||||
node.source = this.match(tt.string)
|
||||
? this.parseExprAtom()
|
||||
: this.unexpected();
|
||||
node.source = this.parseImportSource();
|
||||
this.checkExport(node);
|
||||
} else {
|
||||
if (expect) {
|
||||
@ -1628,39 +1690,6 @@ export default class StatementParser extends ExpressionParser {
|
||||
this.semicolon();
|
||||
}
|
||||
|
||||
shouldParseExportStar(): boolean {
|
||||
return this.match(tt.star);
|
||||
}
|
||||
|
||||
parseExportStar(node: N.ExportNamedDeclaration): void {
|
||||
this.expect(tt.star);
|
||||
|
||||
if (this.isContextual("as")) {
|
||||
this.parseExportNamespace(node);
|
||||
} else {
|
||||
this.parseExportFrom(node, true);
|
||||
this.finishNode(node, "ExportAllDeclaration");
|
||||
}
|
||||
}
|
||||
|
||||
parseExportNamespace(node: N.ExportNamedDeclaration): void {
|
||||
this.expectPlugin("exportNamespaceFrom");
|
||||
|
||||
const specifier = this.startNodeAt(
|
||||
this.state.lastTokStart,
|
||||
this.state.lastTokStartLoc,
|
||||
);
|
||||
|
||||
this.next();
|
||||
|
||||
specifier.exported = this.parseIdentifier(true);
|
||||
|
||||
node.specifiers = [this.finishNode(specifier, "ExportNamespaceSpecifier")];
|
||||
|
||||
this.parseExportSpecifiersMaybe(node);
|
||||
this.parseExportFrom(node, true);
|
||||
}
|
||||
|
||||
shouldParseExportDeclaration(): boolean {
|
||||
if (this.match(tt.at)) {
|
||||
this.expectOnePlugin(["decorators", "decorators-legacy"]);
|
||||
@ -1760,27 +1789,24 @@ export default class StatementParser extends ExpressionParser {
|
||||
}
|
||||
|
||||
checkDuplicateExports(
|
||||
node: N.Identifier | N.ExportNamedDeclaration | N.ExportSpecifier,
|
||||
node:
|
||||
| N.Identifier
|
||||
| N.ExportNamedDeclaration
|
||||
| N.ExportSpecifier
|
||||
| N.ExportDefaultSpecifier,
|
||||
name: string,
|
||||
): void {
|
||||
if (this.state.exportedIdentifiers.indexOf(name) > -1) {
|
||||
this.raiseDuplicateExportError(node, name);
|
||||
throw this.raise(
|
||||
node.start,
|
||||
name === "default"
|
||||
? "Only one default export allowed per module."
|
||||
: `\`${name}\` has already been exported. Exported identifiers must be unique.`,
|
||||
);
|
||||
}
|
||||
this.state.exportedIdentifiers.push(name);
|
||||
}
|
||||
|
||||
raiseDuplicateExportError(
|
||||
node: N.Identifier | N.ExportNamedDeclaration | N.ExportSpecifier,
|
||||
name: string,
|
||||
): empty {
|
||||
throw this.raise(
|
||||
node.start,
|
||||
name === "default"
|
||||
? "Only one default export allowed per module."
|
||||
: `\`${name}\` has already been exported. Exported identifiers must be unique.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Parses a comma-separated list of module exports.
|
||||
|
||||
parseExportSpecifiers(): Array<N.ExportSpecifier> {
|
||||
@ -1820,23 +1846,26 @@ export default class StatementParser extends ExpressionParser {
|
||||
|
||||
// Parses import declaration.
|
||||
|
||||
parseImport(node: N.Node): N.ImportDeclaration | N.TsImportEqualsDeclaration {
|
||||
parseImport(node: N.Node): N.AnyImport {
|
||||
// import '...'
|
||||
if (this.match(tt.string)) {
|
||||
node.specifiers = [];
|
||||
node.source = this.parseExprAtom();
|
||||
} else {
|
||||
node.specifiers = [];
|
||||
this.parseImportSpecifiers(node);
|
||||
node.specifiers = [];
|
||||
if (!this.match(tt.string)) {
|
||||
const hasDefault = this.maybeParseDefaultImportSpecifier(node);
|
||||
const parseNext = !hasDefault || this.eat(tt.comma);
|
||||
const hasStar = parseNext && this.maybeParseStarImportSpecifier(node);
|
||||
if (parseNext && !hasStar) this.parseNamedImportSpecifiers(node);
|
||||
this.expectContextual("from");
|
||||
node.source = this.match(tt.string)
|
||||
? this.parseExprAtom()
|
||||
: this.unexpected();
|
||||
}
|
||||
node.source = this.parseImportSource();
|
||||
this.semicolon();
|
||||
return this.finishNode(node, "ImportDeclaration");
|
||||
}
|
||||
|
||||
parseImportSource(): N.StringLiteral {
|
||||
if (!this.match(tt.string)) this.unexpected();
|
||||
return this.parseExprAtom();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
shouldParseDefaultImport(node: N.ImportDeclaration): boolean {
|
||||
return this.match(tt.name);
|
||||
@ -1853,9 +1882,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
node.specifiers.push(this.finishNode(specifier, type));
|
||||
}
|
||||
|
||||
// Parses a comma-separated list of module imports.
|
||||
parseImportSpecifiers(node: N.ImportDeclaration): void {
|
||||
let first = true;
|
||||
maybeParseDefaultImportSpecifier(node: N.ImportDeclaration): boolean {
|
||||
if (this.shouldParseDefaultImport(node)) {
|
||||
// import defaultObj, { x, y as z } from '...'
|
||||
this.parseImportSpecifierLocal(
|
||||
@ -1864,10 +1891,12 @@ export default class StatementParser extends ExpressionParser {
|
||||
"ImportDefaultSpecifier",
|
||||
"default import specifier",
|
||||
);
|
||||
|
||||
if (!this.eat(tt.comma)) return;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
maybeParseStarImportSpecifier(node: N.ImportDeclaration): boolean {
|
||||
if (this.match(tt.star)) {
|
||||
const specifier = this.startNode();
|
||||
this.next();
|
||||
@ -1879,10 +1908,13 @@ export default class StatementParser extends ExpressionParser {
|
||||
"ImportNamespaceSpecifier",
|
||||
"import namespace specifier",
|
||||
);
|
||||
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
parseNamedImportSpecifiers(node: N.ImportDeclaration) {
|
||||
let first = true;
|
||||
this.expect(tt.braceL);
|
||||
while (!this.eat(tt.braceR)) {
|
||||
if (first) {
|
||||
|
||||
@ -1847,17 +1847,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
super.assertModuleNodeAllowed(node);
|
||||
}
|
||||
|
||||
parseExport(
|
||||
node: N.ExportNamedDeclaration | N.ExportAllDeclaration,
|
||||
): N.ExportNamedDeclaration | N.ExportAllDeclaration {
|
||||
node = super.parseExport(node);
|
||||
parseExport(node: N.Node): N.AnyExport {
|
||||
const decl = super.parseExport(node);
|
||||
if (
|
||||
node.type === "ExportNamedDeclaration" ||
|
||||
node.type === "ExportAllDeclaration"
|
||||
decl.type === "ExportNamedDeclaration" ||
|
||||
decl.type === "ExportAllDeclaration"
|
||||
) {
|
||||
node.exportKind = node.exportKind || "value";
|
||||
decl.exportKind = decl.exportKind || "value";
|
||||
}
|
||||
return node;
|
||||
return decl;
|
||||
}
|
||||
|
||||
parseExportDeclaration(node: N.ExportNamedDeclaration): ?N.Declaration {
|
||||
@ -1893,27 +1891,26 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
}
|
||||
}
|
||||
|
||||
shouldParseExportStar(): boolean {
|
||||
return (
|
||||
super.shouldParseExportStar() ||
|
||||
(this.isContextual("type") && this.lookahead().type === tt.star)
|
||||
);
|
||||
}
|
||||
eatExportStar(node: N.Node): boolean {
|
||||
if (super.eatExportStar(...arguments)) return true;
|
||||
|
||||
parseExportStar(node: N.ExportNamedDeclaration): void {
|
||||
if (this.eatContextual("type")) {
|
||||
if (this.isContextual("type") && this.lookahead().type === tt.star) {
|
||||
node.exportKind = "type";
|
||||
this.next();
|
||||
this.next();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.parseExportStar(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
parseExportNamespace(node: N.ExportNamedDeclaration) {
|
||||
if (node.exportKind === "type") {
|
||||
this.unexpected();
|
||||
maybeParseExportNamespaceSpecifier(node: N.Node): boolean {
|
||||
const pos = this.state.start;
|
||||
const hasNamespace = super.maybeParseExportNamespaceSpecifier(node);
|
||||
if (hasNamespace && node.exportKind === "type") {
|
||||
this.unexpected(pos);
|
||||
}
|
||||
|
||||
return super.parseExportNamespace(node);
|
||||
return hasNamespace;
|
||||
}
|
||||
|
||||
parseClassId(node: N.Class, isStatement: boolean, optionalId: ?boolean) {
|
||||
@ -2225,7 +2222,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
}
|
||||
|
||||
// parse typeof and type imports
|
||||
parseImportSpecifiers(node: N.ImportDeclaration): void {
|
||||
maybeParseDefaultImportSpecifier(node: N.ImportDeclaration): boolean {
|
||||
node.importKind = "value";
|
||||
|
||||
let kind = null;
|
||||
@ -2252,7 +2249,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
}
|
||||
}
|
||||
|
||||
super.parseImportSpecifiers(node);
|
||||
return super.maybeParseDefaultImportSpecifier(node);
|
||||
}
|
||||
|
||||
// parse import-type/typeof shorthand
|
||||
|
||||
@ -1615,16 +1615,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
*/
|
||||
checkDuplicateExports() {}
|
||||
|
||||
parseImport(
|
||||
node: N.Node,
|
||||
): N.ImportDeclaration | N.TsImportEqualsDeclaration {
|
||||
parseImport(node: N.Node): N.AnyImport {
|
||||
if (this.match(tt.name) && this.lookahead().type === tt.eq) {
|
||||
return this.tsParseImportEqualsDeclaration(node);
|
||||
}
|
||||
return super.parseImport(node);
|
||||
}
|
||||
|
||||
parseExport(node: N.Node): N.Node {
|
||||
parseExport(node: N.Node): N.AnyExport {
|
||||
if (this.match(tt._import)) {
|
||||
// `export import A = B;`
|
||||
this.expect(tt._import);
|
||||
|
||||
@ -783,7 +783,9 @@ export type AnyExport =
|
||||
| ExportNamedDeclaration
|
||||
| ExportDefaultDeclaration
|
||||
| ExportAllDeclaration
|
||||
| TsExportAssignment;
|
||||
| TsExportAssignment
|
||||
| TsImportEqualsDeclaration
|
||||
| TsNamespaceExportDeclaration;
|
||||
|
||||
export type ModuleSpecifier = NodeBase & {
|
||||
local: Identifier,
|
||||
@ -820,7 +822,7 @@ export type ImportNamespaceSpecifier = ModuleSpecifier & {
|
||||
export type ExportNamedDeclaration = NodeBase & {
|
||||
type: "ExportNamedDeclaration",
|
||||
declaration: ?Declaration,
|
||||
specifiers: $ReadOnlyArray<ExportSpecifier>,
|
||||
specifiers: $ReadOnlyArray<ExportSpecifier | ExportDefaultSpecifier>,
|
||||
source: ?Literal,
|
||||
|
||||
exportKind?: "type" | "value", // TODO: Not in spec
|
||||
@ -831,6 +833,11 @@ export type ExportSpecifier = NodeBase & {
|
||||
exported: Identifier,
|
||||
};
|
||||
|
||||
export type ExportDefaultSpecifier = NodeBase & {
|
||||
type: "ExportDefaultSpecifier",
|
||||
exported: Identifier,
|
||||
};
|
||||
|
||||
export type ExportDefaultDeclaration = NodeBase & {
|
||||
type: "ExportDefaultDeclaration",
|
||||
declaration:
|
||||
|
||||
@ -145,7 +145,8 @@
|
||||
"raw": "\"bar\""
|
||||
},
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
"declaration": null
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
|
||||
@ -145,7 +145,8 @@
|
||||
"raw": "\"bar\""
|
||||
},
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
"declaration": null
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user