diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 82cf21a5ca13..cf14262965f8 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2182,9 +2182,14 @@ export default class StatementParser extends ExpressionParser { node.specifiers.push(this.finishNode(specifier, type)); } - parseAssertEntries() { - this.expectPlugin("importAssertions"); - + /** + * parse assert entries + * + * @see {@link https://tc39.es/proposal-import-assertions/#prod-AssertEntries |AssertEntries} + * @returns {N.ImportAttribute[]} + * @memberof StatementParser + */ + parseAssertEntries(): N.ImportAttribute[] { const attrs = []; const attrNames = new Set(); @@ -2193,37 +2198,36 @@ export default class StatementParser extends ExpressionParser { break; } - const node = this.startNode(); + const node = this.startNode(); // parse AssertionKey : IdentifierName, StringLiteral - let assertionKeyNode; + const keyName = this.state.value; if (this.match(tt.string)) { - assertionKeyNode = this.parseLiteral(this.state.value, "StringLiteral"); + node.key = this.parseLiteral(keyName, "StringLiteral"); } else { - assertionKeyNode = this.parseIdentifier(true); + node.key = this.parseIdentifier(true); } - this.next(); - node.key = assertionKeyNode; + this.expect(tt.colon); // for now we are only allowing `type` as the only allowed module attribute - if (node.key.name !== "type") { + if (keyName !== "type") { this.raise( node.key.start, Errors.ModuleAttributeDifferentFromType, - node.key.name, + keyName, ); } // check if we already have an entry for an attribute // if a duplicate entry is found, throw an error // for now this logic will come into play only when someone declares `type` twice - if (attrNames.has(node.key.name)) { + if (attrNames.has(keyName)) { this.raise( node.key.start, Errors.ModuleAttributesWithDuplicateKeys, - node.key.name, + keyName, ); } - attrNames.add(node.key.name); + attrNames.add(keyName); if (!this.match(tt.string)) { throw this.unexpected( @@ -2231,8 +2235,11 @@ export default class StatementParser extends ExpressionParser { Errors.ModuleAttributeInvalidValue, ); } - node.value = this.parseLiteral(this.state.value, "StringLiteral"); - this.finishNode(node, "ImportAttribute"); + node.value = this.parseLiteral( + this.state.value, + "StringLiteral", + ); + this.finishNode(node, "ImportAttribute"); attrs.push(node); } while (this.eat(tt.comma)); @@ -2291,18 +2298,15 @@ export default class StatementParser extends ExpressionParser { } maybeParseImportAssertions() { - if ( - this.match(tt.name) && - this.state.value === "assert" && - !this.hasPrecedingLineBreak() - ) { + // [no LineTerminator here] AssertClause + if (this.isContextual("assert") && !this.hasPrecedingLineBreak()) { this.expectPlugin("importAssertions"); - this.next(); + this.next(); // eat `assert` } else { if (this.hasPlugin("importAssertions")) return []; return null; } - + // https://tc39.es/proposal-import-assertions/#prod-AssertClause this.eat(tt.braceL); const attrs = this.parseAssertEntries(); this.eat(tt.braceR); diff --git a/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-assert-entry-no-colon/input.js b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-assert-entry-no-colon/input.js new file mode 100644 index 000000000000..cfa5d6ec452c --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-assert-entry-no-colon/input.js @@ -0,0 +1 @@ +import "foo" assert { type, "json" }; diff --git a/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-assert-entry-no-colon/options.json b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-assert-entry-no-colon/options.json new file mode 100644 index 000000000000..bd21352ea468 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-assert-entry-no-colon/options.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + [ + "importAssertions" + ] + ], + "sourceType": "module", + "throws": "Unexpected token, expected \":\" (1:26)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-escaped-assert/input.js b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-escaped-assert/input.js new file mode 100644 index 000000000000..e316ef9d4abe --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-escaped-assert/input.js @@ -0,0 +1 @@ +import "foo" \u{61}ssert { type: "json" }; diff --git a/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-escaped-assert/options.json b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-escaped-assert/options.json new file mode 100644 index 000000000000..d534a0e8c87e --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-escaped-assert/options.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + [ + "importAssertions" + ] + ], + "sourceType": "module", + "throws": "Unexpected token, expected \";\" (1:13)" +} diff --git a/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-syntax-with-repeated-type-string/input.js b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-syntax-with-repeated-type-string/input.js new file mode 100644 index 000000000000..2330d9eaed15 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-syntax-with-repeated-type-string/input.js @@ -0,0 +1 @@ +import foo from "foo.json" assert { "type": "json", type: "html", "type": "js" }; diff --git a/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-syntax-with-repeated-type-string/output.json b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-syntax-with-repeated-type-string/output.json new file mode 100644 index 000000000000..b6c67e2e6d94 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-syntax-with-repeated-type-string/output.json @@ -0,0 +1,105 @@ +{ + "type": "File", + "start":0,"end":81,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":81}}, + "errors": [ + "SyntaxError: Duplicate key \"type\" is not allowed in module attributes (1:52)", + "SyntaxError: Duplicate key \"type\" is not allowed in module attributes (1:66)" + ], + "program": { + "type": "Program", + "start":0,"end":81,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":81}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":81,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":81}}, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start":7,"end":10,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":10}}, + "local": { + "type": "Identifier", + "start":7,"end":10,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":10},"identifierName":"foo"}, + "name": "foo" + } + } + ], + "source": { + "type": "StringLiteral", + "start":16,"end":26,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":26}}, + "extra": { + "rawValue": "foo.json", + "raw": "\"foo.json\"" + }, + "value": "foo.json" + }, + "assertions": [ + { + "type": "ImportAttribute", + "start":36,"end":50,"loc":{"start":{"line":1,"column":36},"end":{"line":1,"column":50}}, + "key": { + "type": "StringLiteral", + "start":36,"end":42,"loc":{"start":{"line":1,"column":36},"end":{"line":1,"column":42}}, + "extra": { + "rawValue": "type", + "raw": "\"type\"" + }, + "value": "type" + }, + "value": { + "type": "StringLiteral", + "start":44,"end":50,"loc":{"start":{"line":1,"column":44},"end":{"line":1,"column":50}}, + "extra": { + "rawValue": "json", + "raw": "\"json\"" + }, + "value": "json" + } + }, + { + "type": "ImportAttribute", + "start":52,"end":64,"loc":{"start":{"line":1,"column":52},"end":{"line":1,"column":64}}, + "key": { + "type": "Identifier", + "start":52,"end":56,"loc":{"start":{"line":1,"column":52},"end":{"line":1,"column":56},"identifierName":"type"}, + "name": "type" + }, + "value": { + "type": "StringLiteral", + "start":58,"end":64,"loc":{"start":{"line":1,"column":58},"end":{"line":1,"column":64}}, + "extra": { + "rawValue": "html", + "raw": "\"html\"" + }, + "value": "html" + } + }, + { + "type": "ImportAttribute", + "start":66,"end":78,"loc":{"start":{"line":1,"column":66},"end":{"line":1,"column":78}}, + "key": { + "type": "StringLiteral", + "start":66,"end":72,"loc":{"start":{"line":1,"column":66},"end":{"line":1,"column":72}}, + "extra": { + "rawValue": "type", + "raw": "\"type\"" + }, + "value": "type" + }, + "value": { + "type": "StringLiteral", + "start":74,"end":78,"loc":{"start":{"line":1,"column":74},"end":{"line":1,"column":78}}, + "extra": { + "rawValue": "js", + "raw": "\"js\"" + }, + "value": "js" + } + } + ] + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/import-assertions/valid-syntax-with-repeated-type/input.js b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-syntax-with-repeated-type/input.js similarity index 100% rename from packages/babel-parser/test/fixtures/experimental/import-assertions/valid-syntax-with-repeated-type/input.js rename to packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-syntax-with-repeated-type/input.js diff --git a/packages/babel-parser/test/fixtures/experimental/import-assertions/valid-syntax-with-repeated-type/output.json b/packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-syntax-with-repeated-type/output.json similarity index 100% rename from packages/babel-parser/test/fixtures/experimental/import-assertions/valid-syntax-with-repeated-type/output.json rename to packages/babel-parser/test/fixtures/experimental/import-assertions/invalid-syntax-with-repeated-type/output.json diff --git a/packages/babel-parser/test/fixtures/experimental/import-assertions/valid-string-assertion-key/input.js b/packages/babel-parser/test/fixtures/experimental/import-assertions/valid-string-assertion-key/input.js new file mode 100644 index 000000000000..dcc37efe8067 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/import-assertions/valid-string-assertion-key/input.js @@ -0,0 +1 @@ +import "foo" assert { "type": "json" }; diff --git a/packages/babel-parser/test/fixtures/experimental/import-assertions/valid-string-assertion-key/output.json b/packages/babel-parser/test/fixtures/experimental/import-assertions/valid-string-assertion-key/output.json new file mode 100644 index 000000000000..663266364853 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/import-assertions/valid-string-assertion-key/output.json @@ -0,0 +1,51 @@ +{ + "type": "File", + "start":0,"end":39,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":39}}, + "program": { + "type": "Program", + "start":0,"end":39,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":39}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":39,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":39}}, + "specifiers": [], + "source": { + "type": "StringLiteral", + "start":7,"end":12,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":12}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + }, + "assertions": [ + { + "type": "ImportAttribute", + "start":22,"end":36,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":36}}, + "key": { + "type": "StringLiteral", + "start":22,"end":28,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":28}}, + "extra": { + "rawValue": "type", + "raw": "\"type\"" + }, + "value": "type" + }, + "value": { + "type": "StringLiteral", + "start":30,"end":36,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":36}}, + "extra": { + "rawValue": "json", + "raw": "\"json\"" + }, + "value": "json" + } + } + ] + } + ], + "directives": [] + } +} \ No newline at end of file