Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support string assertion key in assert entries #12281

Merged
merged 2 commits into from Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 27 additions & 23 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -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();

Expand All @@ -2193,46 +2198,48 @@ export default class StatementParser extends ExpressionParser {
break;
}

const node = this.startNode();
const node = this.startNode<N.ImportAttribute>();

// 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<N.StringLiteral>(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(
this.state.start,
Errors.ModuleAttributeInvalidValue,
);
}
node.value = this.parseLiteral(this.state.value, "StringLiteral");
this.finishNode(node, "ImportAttribute");
node.value = this.parseLiteral<N.StringLiteral>(
this.state.value,
"StringLiteral",
);
this.finishNode<N.ImportAttribute>(node, "ImportAttribute");
attrs.push(node);
} while (this.eat(tt.comma));

Expand Down Expand Up @@ -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);
Expand Down
@@ -0,0 +1 @@
import "foo" assert { type, "json" };
@@ -0,0 +1,9 @@
{
"plugins": [
[
"importAssertions"
]
],
"sourceType": "module",
"throws": "Unexpected token, expected \":\" (1:26)"
}
@@ -0,0 +1 @@
import "foo" \u{61}ssert { type: "json" };
@@ -0,0 +1,9 @@
{
"plugins": [
[
"importAssertions"
]
],
"sourceType": "module",
"throws": "Unexpected token, expected \";\" (1:13)"
}
@@ -0,0 +1 @@
import foo from "foo.json" assert { "type": "json", type: "html", "type": "js" };
@@ -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": []
}
}
@@ -0,0 +1 @@
import "foo" assert { "type": "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": []
}
}