LibJS: Allow full ModuleExportName in namespace

This means we should accept a string after 'export * as '.
This commit is contained in:
davidot 2022-08-28 01:40:51 +02:00 committed by Linus Groh
parent 75ebcf6b4a
commit f75c51b097
Notes: sideshowbarker 2024-07-17 07:34:41 +09:00
3 changed files with 46 additions and 32 deletions

View file

@ -4403,29 +4403,45 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program)
entries_with_location.append({ ExportEntry::named_export(default_string_value, move(local_name)), default_position }); entries_with_location.append({ ExportEntry::named_export(default_string_value, move(local_name)), default_position });
} else { } else {
enum FromSpecifier { enum class FromSpecifier {
NotAllowed, NotAllowed,
Optional, Optional,
Required Required
} check_for_from { NotAllowed }; } check_for_from { FromSpecifier::NotAllowed };
auto parse_module_export_name = [&](bool lhs) -> FlyString {
// https://tc39.es/ecma262/#prod-ModuleExportName
// ModuleExportName :
// IdentifierName
// StringLiteral
if (match_identifier_name()) {
return consume().value();
}
if (match(TokenType::StringLiteral)) {
// It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals.
// Only for export { "a" as "b" }; // <-- no from
if (lhs)
check_for_from = FromSpecifier::Required;
return consume_string_value();
}
expected("ExportSpecifier (string or identifier)");
return {};
};
if (match(TokenType::Asterisk)) { if (match(TokenType::Asterisk)) {
auto asterisk_position = position(); auto asterisk_position = position();
consume(TokenType::Asterisk); consume(TokenType::Asterisk);
if (match_as()) { if (match_as()) {
// * as ModuleExportName
consume(TokenType::Identifier); consume(TokenType::Identifier);
if (match_identifier_name()) {
auto namespace_position = position(); auto namespace_position = position();
auto exported_name = consume().value(); auto exported_name = parse_module_export_name(false);
entries_with_location.append({ ExportEntry::all_module_request(exported_name), namespace_position }); entries_with_location.append({ ExportEntry::all_module_request(exported_name), namespace_position });
} else {
expected("identifier");
}
} else { } else {
entries_with_location.append({ ExportEntry::all_but_default_entry(), asterisk_position }); entries_with_location.append({ ExportEntry::all_but_default_entry(), asterisk_position });
} }
check_for_from = Required; check_for_from = FromSpecifier::Required;
} else if (match_declaration()) { } else if (match_declaration()) {
auto decl_position = position(); auto decl_position = position();
auto declaration = parse_declaration(); auto declaration = parse_declaration();
@ -4471,30 +4487,15 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program)
expression = variable_declaration; expression = variable_declaration;
} else if (match(TokenType::CurlyOpen)) { } else if (match(TokenType::CurlyOpen)) {
consume(TokenType::CurlyOpen); consume(TokenType::CurlyOpen);
check_for_from = Optional; check_for_from = FromSpecifier::Optional;
auto parse_export_specifier = [&](bool lhs) -> FlyString {
if (match_identifier_name()) {
return consume().value();
}
if (match(TokenType::StringLiteral)) {
// It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals.
// Only for export { "a" as "b" }; // <-- no from
if (lhs)
check_for_from = Required;
return consume_string_value();
}
expected("ExportSpecifier (string or identifier)");
return {};
};
while (!done() && !match(TokenType::CurlyClose)) { while (!done() && !match(TokenType::CurlyClose)) {
auto identifier_position = position(); auto identifier_position = position();
auto identifier = parse_export_specifier(true); auto identifier = parse_module_export_name(true);
if (match_as()) { if (match_as()) {
consume(TokenType::Identifier); consume(TokenType::Identifier);
auto export_name = parse_export_specifier(false); auto export_name = parse_module_export_name(false);
entries_with_location.append({ ExportEntry::named_export(move(export_name), move(identifier)), identifier_position }); entries_with_location.append({ ExportEntry::named_export(move(export_name), move(identifier)), identifier_position });
} else { } else {
@ -4513,14 +4514,14 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program)
syntax_error("Unexpected token 'export'", rule_start.position()); syntax_error("Unexpected token 'export'", rule_start.position());
} }
if (check_for_from != NotAllowed && match_from()) { if (check_for_from != FromSpecifier::NotAllowed && match_from()) {
consume(TokenType::Identifier); consume(TokenType::Identifier);
from_specifier = parse_module_request(); from_specifier = parse_module_request();
} else if (check_for_from == Required) { } else if (check_for_from == FromSpecifier::Required) {
expected("from"); expected("from");
} }
if (check_for_from != NotAllowed) if (check_for_from != FromSpecifier::NotAllowed)
consume_or_insert_semicolon(); consume_or_insert_semicolon();
} }

View file

@ -0,0 +1,2 @@
// This is an all export and should thus provide the full namespace.
export * as "viaString" from "./default-and-star-export.mjs";

View file

@ -1,6 +1,17 @@
import * as indirectNs from "./default-and-star-export-indirect.mjs"; import * as indirectNs from "./default-and-star-export-indirect.mjs";
// This is an all-but-default export and should thus only provide "*" and "".
// default-and-star-export-indirect.mjs:
// export * from "./default-and-star-export.mjs";
import * as indirectNsString from "./default-and-star-export-indirect-string.mjs";
// This is an all export and should thus provide the full namespace.
// default-and-star-export-indirect-string.mjs:
// export * as "viaString" from "./default-and-star-export.mjs";
export const passed = export const passed =
indirectNs["*"] === "starExportValue" && indirectNs["*"] === "starExportValue" &&
indirectNs[""] === "empty" && indirectNs[""] === "empty" &&
indirectNs.default === undefined; indirectNs.default === undefined &&
indirectNsString.viaString["*"] === "starExportValue" &&
indirectNsString.viaString[""] === "empty" &&
indirectNsString.viaString.default === "defaultValue";