mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
LibJS: Allow full ModuleExportName in namespace
This means we should accept a string after 'export * as '.
This commit is contained in:
parent
75ebcf6b4a
commit
f75c51b097
Notes:
sideshowbarker
2024-07-17 07:34:41 +09:00
Author: https://github.com/davidot Commit: https://github.com/SerenityOS/serenity/commit/f75c51b097 Pull-request: https://github.com/SerenityOS/serenity/pull/15100 Reviewed-by: https://github.com/linusg
3 changed files with 46 additions and 32 deletions
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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";
|
|
@ -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";
|
||||||
|
|
Loading…
Reference in a new issue