Skip to content

Commit

Permalink
basic support for import assertions. Closes #1095
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiosantoscode committed Nov 13, 2021
1 parent f10938e commit 8539751
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 16 deletions.
8 changes: 5 additions & 3 deletions lib/ast.js
Expand Up @@ -889,12 +889,13 @@ var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", {
},
});

var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
var AST_Import = DEFNODE("Import", "imported_name imported_names module_name assert_clause", {
$documentation: "An `import` statement",
$propdoc: {
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
imported_names: "[AST_NameMapping*] The names of non-default imported variables",
module_name: "[AST_String] String literal describing where this module came from",
assert_clause: "[AST_Object?] The import assertion"
},
_walk: function(visitor) {
return visitor._visit(this, function() {
Expand Down Expand Up @@ -923,14 +924,15 @@ var AST_ImportMeta = DEFNODE("ImportMeta", null, {
$documentation: "A reference to import.meta",
});

var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name", {
var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name assert_clause", {
$documentation: "An `export` statement",
$propdoc: {
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
exported_value: "[AST_Node?] An exported value",
exported_names: "[AST_NameMapping*?] List of exported names",
module_name: "[AST_String?] Name of the file to load exports from",
is_default: "[Boolean] Whether this is the default exported value of this module"
is_default: "[Boolean] Whether this is the default exported value of this module",
assert_clause: "[AST_Object?] The import assertion"
},
_walk: function (visitor) {
return visitor._visit(this, function () {
Expand Down
54 changes: 48 additions & 6 deletions lib/mozilla-ast.js
Expand Up @@ -158,6 +158,7 @@ import {
AST_With,
AST_Yield,
} from "./ast.js";
import { is_basic_identifier_string } from "./parse.js";

(function() {

Expand All @@ -179,6 +180,24 @@ import {
return body;
};

const assert_clause_from_moz = (assertions) => {
if (assertions && assertions.length > 0) {
return new AST_Object({
start: my_start_token(assertions),
end: my_end_token(assertions),
properties: assertions.map((assertion_kv) =>
new AST_ObjectKeyVal({
start: my_start_token(assertion_kv),
end: my_end_token(assertion_kv),
key: assertion_kv.key.name || assertion_kv.key.value,
value: from_moz(assertion_kv.value)
})
)
})
}
return null;
}

var MOZ_TO_ME = {
Program: function(M) {
return new AST_Toplevel({
Expand Down Expand Up @@ -499,7 +518,8 @@ import {
end : my_end_token(M),
imported_name: imported_name,
imported_names : imported_names,
module_name : from_moz(M.source)
module_name : from_moz(M.source),
assert_clause: assert_clause_from_moz(M.assertions)
});
},
ExportAllDeclaration: function(M) {
Expand All @@ -512,7 +532,8 @@ import {
foreign_name: new AST_SymbolExportForeign({ name: "*" })
})
],
module_name: from_moz(M.source)
module_name: from_moz(M.source),
assert_clause: assert_clause_from_moz(M.assertions)
});
},
ExportNamedDeclaration: function(M) {
Expand All @@ -526,7 +547,8 @@ import {
name: from_moz(specifier.local)
});
}) : null,
module_name: from_moz(M.source)
module_name: from_moz(M.source),
assert_clause: assert_clause_from_moz(M.assertions)
});
},
ExportDefaultDeclaration: function(M) {
Expand Down Expand Up @@ -818,12 +840,30 @@ import {
};
});

const assert_clause_to_moz = assert_clause => {
const assertions = [];
if (assert_clause) {
for (const { key, value } of assert_clause.properties) {
const key_moz = is_basic_identifier_string(key)
? { type: 'Identifier', name: key }
: { type: 'Literal', value: key, raw: JSON.stringify(key) };
assertions.push({
type: 'ImportAttribute',
key: key_moz,
value: to_moz(value)
});
}
}
return assertions;
}

def_to_moz(AST_Export, function To_Moz_ExportDeclaration(M) {
if (M.exported_names) {
if (M.exported_names[0].name.name === "*") {
return {
type: "ExportAllDeclaration",
source: to_moz(M.module_name)
source: to_moz(M.module_name),
assertions: assert_clause_to_moz(M.assert_clause)
};
}
return {
Expand All @@ -836,7 +876,8 @@ import {
};
}),
declaration: to_moz(M.exported_definition),
source: to_moz(M.module_name)
source: to_moz(M.module_name),
assertions: assert_clause_to_moz(M.assert_clause)
};
}
return {
Expand Down Expand Up @@ -870,7 +911,8 @@ import {
return {
type: "ImportDeclaration",
specifiers: specifiers,
source: to_moz(M.module_name)
source: to_moz(M.module_name),
assertions: assert_clause_to_moz(M.assert_clause)
};
});

Expand Down
8 changes: 8 additions & 0 deletions lib/output.js
Expand Up @@ -1637,6 +1637,10 @@ function OutputStream(options) {
output.space();
}
self.module_name.print(output);
if (self.assert_clause) {
output.print("assert");
self.assert_clause.print(output);
}
output.semicolon();
});
DEFPRINT(AST_ImportMeta, function(self, output) {
Expand Down Expand Up @@ -1702,6 +1706,10 @@ function OutputStream(options) {
output.space();
self.module_name.print(output);
}
if (self.assert_clause) {
output.print("assert");
self.assert_clause.print(output);
}
if (self.exported_value
&& !(self.exported_value instanceof AST_Defun ||
self.exported_value instanceof AST_Function ||
Expand Down
30 changes: 23 additions & 7 deletions lib/parse.js
Expand Up @@ -1222,7 +1222,7 @@ function parse($TEXT, options) {
}
if (S.token.value == "import" && !is_token(peek(), "punc", "(") && !is_token(peek(), "punc", ".")) {
next();
var node = import_();
var node = import_statement();
semicolon();
return node;
}
Expand Down Expand Up @@ -1374,7 +1374,7 @@ function parse($TEXT, options) {
case "export":
if (!is_token(peek(), "punc", "(")) {
next();
var node = export_();
var node = export_statement();
if (is("punc", ";")) semicolon();
return node;
}
Expand Down Expand Up @@ -2666,7 +2666,15 @@ function parse($TEXT, options) {
}
}

function import_() {
function maybe_import_assertion() {
if (is("name", "assert") && !has_newline_before(S.token)) {
next();
return object_or_destructuring_();
}
return null
}

function import_statement() {
var start = prev();

var imported_name;
Expand All @@ -2689,16 +2697,20 @@ function parse($TEXT, options) {
unexpected();
}
next();

const assert_clause = maybe_import_assertion();

return new AST_Import({
start: start,
imported_name: imported_name,
imported_names: imported_names,
start,
imported_name,
imported_names,
module_name: new AST_String({
start: mod_str,
value: mod_str.value,
quote: mod_str.quote,
end: mod_str,
}),
assert_clause,
end: S.token,
});
}
Expand Down Expand Up @@ -2806,7 +2818,7 @@ function parse($TEXT, options) {
return names;
}

function export_() {
function export_statement() {
var start = S.token;
var is_default;
var exported_names;
Expand All @@ -2824,6 +2836,8 @@ function parse($TEXT, options) {
}
next();

const assert_clause = maybe_import_assertion();

return new AST_Export({
start: start,
is_default: is_default,
Expand All @@ -2835,6 +2849,7 @@ function parse($TEXT, options) {
end: mod_str,
}),
end: prev(),
assert_clause
});
} else {
return new AST_Export({
Expand Down Expand Up @@ -2880,6 +2895,7 @@ function parse($TEXT, options) {
exported_value: exported_value,
exported_definition: exported_definition,
end: prev(),
assert_clause: null
});
}

Expand Down
28 changes: 28 additions & 0 deletions test/compress/harmony.js
Expand Up @@ -303,6 +303,34 @@ export_from_statement: {
expect_exact: 'export*from"a.js";export{A}from"a.js";export{A,B}from"a.js";export{C};'
}

import_assertions: {
input: {
import "hello" assert { key: 'value' };
}
expect_exact: 'import"hello"assert{key:"value"};'
}

import_assertions_with_spaces_in_obj: {
input: {
import "hello" assert { 'k e y': 'value' };
}
expect_exact: 'import"hello"assert{"k e y":"value"};'
}

export_from_assertions: {
input: {
export * from "hello" assert { key: 'value' };
}
expect_exact: 'export*from"hello"assert{key:"value"};'
}

export_named_from_assertions: {
input: {
export { x } from "hello" assert { key: 'value' };
}
expect_exact: 'export{x}from"hello"assert{key:"value"};'
}

import_statement_mangling: {
mangle = { toplevel: true };
input: {
Expand Down

0 comments on commit 8539751

Please sign in to comment.