From 9ea4e9dead015ebf710b58fdefd2b3bc0e50c1ca Mon Sep 17 00:00:00 2001 From: Jeff Morrison Date: Tue, 3 Jan 2017 04:52:18 -0500 Subject: [PATCH] Add support for Flow shorthand import type (#267) --- src/parser/statement.js | 14 +- src/plugins/flow.js | 52 ++ .../import-type-shorthand/actual.js | 8 + .../import-type-shorthand/expected.json | 650 ++++++++++++++++++ .../flow/type-imports/import-type/actual.js | 3 + .../type-imports/import-type/expected.json | 310 +++++++++ 6 files changed, 1032 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/flow/type-imports/import-type-shorthand/actual.js create mode 100644 test/fixtures/flow/type-imports/import-type-shorthand/expected.json create mode 100644 test/fixtures/flow/type-imports/import-type/actual.js create mode 100644 test/fixtures/flow/type-imports/import-type/expected.json diff --git a/src/parser/statement.js b/src/parser/statement.js index 40bcb56882..053241298b 100644 --- a/src/parser/statement.js +++ b/src/parser/statement.js @@ -1061,14 +1061,18 @@ pp.parseImportSpecifiers = function (node) { if (this.eat(tt.braceR)) break; } - let specifier = this.startNode(); - specifier.imported = this.parseIdentifier(true); - specifier.local = this.eatContextual("as") ? this.parseIdentifier() : specifier.imported.__clone(); - this.checkLVal(specifier.local, true, undefined, "import specifier"); - node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")); + this.parseImportSpecifier(node); } }; +pp.parseImportSpecifier = function (node) { + let specifier = this.startNode(); + specifier.imported = this.parseIdentifier(true); + specifier.local = this.eatContextual("as") ? this.parseIdentifier() : specifier.imported.__clone(); + this.checkLVal(specifier.local, true, undefined, "import specifier"); + node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")); +}; + pp.parseImportSpecifierDefault = function (id, startPos, startLoc) { let node = this.startNodeAt(startPos, startLoc); node.local = id; diff --git a/src/plugins/flow.js b/src/plugins/flow.js index 8b9c2048dc..0c0486b1c9 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -1185,6 +1185,58 @@ export default function (instance) { }; }); + // parse import-type/typeof shorthand + instance.extend("parseImportSpecifier", function () { + return function (node) { + let specifier = this.startNode(); + const firstIdentLoc = this.state.start; + const firstIdent = this.parseIdentifier(true); + + let specifierTypeKind = null; + if (firstIdent.name === "type") { + specifierTypeKind = "type"; + } else if (firstIdent.name === "typeof") { + specifierTypeKind = "typeof"; + } + + if (this.isContextual("as")) { + const as_ident = this.parseIdentifier(true); + if (specifierTypeKind !== null && !this.match(tt.name)) { + // `import {type as ,` or `import {type as }` + specifier.imported = as_ident; + specifier.importKind = specifierTypeKind; + specifier.local = as_ident.__clone(); + } else { + // `import {type as foo` + specifier.imported = firstIdent; + specifier.importKind = null; + specifier.local = this.parseIdentifier(false); + } + } else if (specifierTypeKind !== null && this.match(tt.name)) { + // `import {type foo` + specifier.imported = this.parseIdentifier(true); + specifier.importKind = specifierTypeKind; + specifier.local = + this.eatContextual("as") + ? this.parseIdentifier(false) + : specifier.imported.__clone(); + } else { + if (firstIdent.name === "typeof") { + this.unexpected( + firstIdentLoc, + "Cannot import a variable named `typeof`" + ); + } + specifier.imported = firstIdent; + specifier.importKind = null; + specifier.local = specifier.imported.__clone(); + } + + this.checkLVal(specifier.local, true, undefined, "import specifier"); + node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")); + }; + }); + // parse function type parameters - function foo() {} instance.extend("parseFunctionParams", function (inner) { return function (node) { diff --git a/test/fixtures/flow/type-imports/import-type-shorthand/actual.js b/test/fixtures/flow/type-imports/import-type-shorthand/actual.js new file mode 100644 index 0000000000..fbb21d87cf --- /dev/null +++ b/test/fixtures/flow/type-imports/import-type-shorthand/actual.js @@ -0,0 +1,8 @@ +import {type} from "foo"; +import {type t} from "foo"; +import {type as} from "foo"; +import {type t as u} from "foo"; + +import {typeof t} from "foo"; +import {typeof as} from "foo"; +import {typeof t as u} from "foo"; diff --git a/test/fixtures/flow/type-imports/import-type-shorthand/expected.json b/test/fixtures/flow/type-imports/import-type-shorthand/expected.json new file mode 100644 index 0000000000..7b3d80f439 --- /dev/null +++ b/test/fixtures/flow/type-imports/import-type-shorthand/expected.json @@ -0,0 +1,650 @@ +{ + "type": "File", + "start": 0, + "end": 212, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 8, + "column": 34 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 212, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 8, + "column": 34 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ImportDeclaration", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "start": 8, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "imported": { + "type": "Identifier", + "start": 8, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 12 + }, + "identifierName": "type" + }, + "name": "type" + }, + "importKind": null, + "local": { + "type": "Identifier", + "start": 8, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 12 + }, + "identifierName": "type" + }, + "name": "type" + } + } + ], + "importKind": "value", + "source": { + "type": "StringLiteral", + "start": 19, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 19 + }, + "end": { + "line": 1, + "column": 24 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + }, + { + "type": "ImportDeclaration", + "start": 26, + "end": 53, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "start": 34, + "end": 40, + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 14 + } + }, + "imported": { + "type": "Identifier", + "start": 39, + "end": 40, + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 14 + }, + "identifierName": "t" + }, + "name": "t" + }, + "importKind": "type", + "local": { + "type": "Identifier", + "start": 39, + "end": 40, + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 14 + }, + "identifierName": "t" + }, + "name": "t" + } + } + ], + "importKind": "value", + "source": { + "type": "StringLiteral", + "start": 47, + "end": 52, + "loc": { + "start": { + "line": 2, + "column": 21 + }, + "end": { + "line": 2, + "column": 26 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + }, + { + "type": "ImportDeclaration", + "start": 54, + "end": 82, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 28 + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "start": 62, + "end": 69, + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 15 + } + }, + "imported": { + "type": "Identifier", + "start": 67, + "end": 69, + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 15 + }, + "identifierName": "as" + }, + "name": "as" + }, + "importKind": "type", + "local": { + "type": "Identifier", + "start": 67, + "end": 69, + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 15 + }, + "identifierName": "as" + }, + "name": "as" + } + } + ], + "importKind": "value", + "source": { + "type": "StringLiteral", + "start": 76, + "end": 81, + "loc": { + "start": { + "line": 3, + "column": 22 + }, + "end": { + "line": 3, + "column": 27 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + }, + { + "type": "ImportDeclaration", + "start": 83, + "end": 115, + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 32 + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "start": 91, + "end": 102, + "loc": { + "start": { + "line": 4, + "column": 8 + }, + "end": { + "line": 4, + "column": 19 + } + }, + "imported": { + "type": "Identifier", + "start": 96, + "end": 97, + "loc": { + "start": { + "line": 4, + "column": 13 + }, + "end": { + "line": 4, + "column": 14 + }, + "identifierName": "t" + }, + "name": "t" + }, + "importKind": "type", + "local": { + "type": "Identifier", + "start": 101, + "end": 102, + "loc": { + "start": { + "line": 4, + "column": 18 + }, + "end": { + "line": 4, + "column": 19 + }, + "identifierName": "u" + }, + "name": "u" + } + } + ], + "importKind": "value", + "source": { + "type": "StringLiteral", + "start": 109, + "end": 114, + "loc": { + "start": { + "line": 4, + "column": 26 + }, + "end": { + "line": 4, + "column": 31 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + }, + { + "type": "ImportDeclaration", + "start": 117, + "end": 146, + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 6, + "column": 29 + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "start": 125, + "end": 133, + "loc": { + "start": { + "line": 6, + "column": 8 + }, + "end": { + "line": 6, + "column": 16 + } + }, + "imported": { + "type": "Identifier", + "start": 132, + "end": 133, + "loc": { + "start": { + "line": 6, + "column": 15 + }, + "end": { + "line": 6, + "column": 16 + }, + "identifierName": "t" + }, + "name": "t" + }, + "importKind": "typeof", + "local": { + "type": "Identifier", + "start": 132, + "end": 133, + "loc": { + "start": { + "line": 6, + "column": 15 + }, + "end": { + "line": 6, + "column": 16 + }, + "identifierName": "t" + }, + "name": "t" + } + } + ], + "importKind": "value", + "source": { + "type": "StringLiteral", + "start": 140, + "end": 145, + "loc": { + "start": { + "line": 6, + "column": 23 + }, + "end": { + "line": 6, + "column": 28 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + }, + { + "type": "ImportDeclaration", + "start": 147, + "end": 177, + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 30 + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "start": 155, + "end": 164, + "loc": { + "start": { + "line": 7, + "column": 8 + }, + "end": { + "line": 7, + "column": 17 + } + }, + "imported": { + "type": "Identifier", + "start": 162, + "end": 164, + "loc": { + "start": { + "line": 7, + "column": 15 + }, + "end": { + "line": 7, + "column": 17 + }, + "identifierName": "as" + }, + "name": "as" + }, + "importKind": "typeof", + "local": { + "type": "Identifier", + "start": 162, + "end": 164, + "loc": { + "start": { + "line": 7, + "column": 15 + }, + "end": { + "line": 7, + "column": 17 + }, + "identifierName": "as" + }, + "name": "as" + } + } + ], + "importKind": "value", + "source": { + "type": "StringLiteral", + "start": 171, + "end": 176, + "loc": { + "start": { + "line": 7, + "column": 24 + }, + "end": { + "line": 7, + "column": 29 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + }, + { + "type": "ImportDeclaration", + "start": 178, + "end": 212, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 34 + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "start": 186, + "end": 199, + "loc": { + "start": { + "line": 8, + "column": 8 + }, + "end": { + "line": 8, + "column": 21 + } + }, + "imported": { + "type": "Identifier", + "start": 193, + "end": 194, + "loc": { + "start": { + "line": 8, + "column": 15 + }, + "end": { + "line": 8, + "column": 16 + }, + "identifierName": "t" + }, + "name": "t" + }, + "importKind": "typeof", + "local": { + "type": "Identifier", + "start": 198, + "end": 199, + "loc": { + "start": { + "line": 8, + "column": 20 + }, + "end": { + "line": 8, + "column": 21 + }, + "identifierName": "u" + }, + "name": "u" + } + } + ], + "importKind": "value", + "source": { + "type": "StringLiteral", + "start": 206, + "end": 211, + "loc": { + "start": { + "line": 8, + "column": 28 + }, + "end": { + "line": 8, + "column": 33 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + } + ], + "directives": [] + } +} diff --git a/test/fixtures/flow/type-imports/import-type/actual.js b/test/fixtures/flow/type-imports/import-type/actual.js new file mode 100644 index 0000000000..7dae1e81dc --- /dev/null +++ b/test/fixtures/flow/type-imports/import-type/actual.js @@ -0,0 +1,3 @@ +import type Def from "M"; +import type {named} from "M"; +import type Def, {named} from "M"; diff --git a/test/fixtures/flow/type-imports/import-type/expected.json b/test/fixtures/flow/type-imports/import-type/expected.json new file mode 100644 index 0000000000..2b23f221b3 --- /dev/null +++ b/test/fixtures/flow/type-imports/import-type/expected.json @@ -0,0 +1,310 @@ +{ + "type": "File", + "start": 0, + "end": 90, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 34 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 90, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 34 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ImportDeclaration", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start": 12, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 15 + } + }, + "local": { + "type": "Identifier", + "start": 12, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 15 + }, + "identifierName": "Def" + }, + "name": "Def" + } + } + ], + "importKind": "type", + "source": { + "type": "StringLiteral", + "start": 21, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 21 + }, + "end": { + "line": 1, + "column": 24 + } + }, + "extra": { + "rawValue": "M", + "raw": "\"M\"" + }, + "value": "M" + } + }, + { + "type": "ImportDeclaration", + "start": 26, + "end": 55, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 29 + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "start": 39, + "end": 44, + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 18 + } + }, + "imported": { + "type": "Identifier", + "start": 39, + "end": 44, + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 18 + }, + "identifierName": "named" + }, + "name": "named" + }, + "local": { + "type": "Identifier", + "start": 39, + "end": 44, + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 18 + }, + "identifierName": "named" + }, + "name": "named" + } + } + ], + "importKind": "type", + "source": { + "type": "StringLiteral", + "start": 51, + "end": 54, + "loc": { + "start": { + "line": 2, + "column": 25 + }, + "end": { + "line": 2, + "column": 28 + } + }, + "extra": { + "rawValue": "M", + "raw": "\"M\"" + }, + "value": "M" + } + }, + { + "type": "ImportDeclaration", + "start": 56, + "end": 90, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 34 + } + }, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start": 68, + "end": 71, + "loc": { + "start": { + "line": 3, + "column": 12 + }, + "end": { + "line": 3, + "column": 15 + } + }, + "local": { + "type": "Identifier", + "start": 68, + "end": 71, + "loc": { + "start": { + "line": 3, + "column": 12 + }, + "end": { + "line": 3, + "column": 15 + }, + "identifierName": "Def" + }, + "name": "Def" + } + }, + { + "type": "ImportSpecifier", + "start": 74, + "end": 79, + "loc": { + "start": { + "line": 3, + "column": 18 + }, + "end": { + "line": 3, + "column": 23 + } + }, + "imported": { + "type": "Identifier", + "start": 74, + "end": 79, + "loc": { + "start": { + "line": 3, + "column": 18 + }, + "end": { + "line": 3, + "column": 23 + }, + "identifierName": "named" + }, + "name": "named" + }, + "local": { + "type": "Identifier", + "start": 74, + "end": 79, + "loc": { + "start": { + "line": 3, + "column": 18 + }, + "end": { + "line": 3, + "column": 23 + }, + "identifierName": "named" + }, + "name": "named" + } + } + ], + "importKind": "type", + "source": { + "type": "StringLiteral", + "start": 86, + "end": 89, + "loc": { + "start": { + "line": 3, + "column": 30 + }, + "end": { + "line": 3, + "column": 33 + } + }, + "extra": { + "rawValue": "M", + "raw": "\"M\"" + }, + "value": "M" + } + } + ], + "directives": [] + } +} \ No newline at end of file