diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 53081d07a..85c186fe1 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -4,7 +4,7 @@ use oxc_ast::ast::*; use oxc_span::GetSpan; use oxc_syntax::{ identifier::{LS, PS}, - keyword::is_keyword, + keyword::is_reserved_keyword_or_global_object, number::NumberBase, operator::{BinaryOperator, UnaryOperator}, precedence::{GetPrecedence, Precedence}, @@ -1735,17 +1735,19 @@ impl<'a, const MINIFY: bool> GenExpr for AssignmentExpression<'a> { let identifier_is_keyword = match &self.left { AssignmentTarget::AssignmentTargetIdentifier(target) => { - is_keyword(target.name.as_str()) + is_reserved_keyword_or_global_object(target.name.as_str()) } AssignmentTarget::ComputedMemberExpression(expression) => match &expression.object { - Expression::Identifier(ident) => is_keyword(ident.name.as_str()), + Expression::Identifier(ident) => { + is_reserved_keyword_or_global_object(ident.name.as_str()) + } _ => false, }, AssignmentTarget::StaticMemberExpression(expression) => { - is_keyword(expression.property.name.as_str()) + is_reserved_keyword_or_global_object(expression.property.name.as_str()) } AssignmentTarget::PrivateFieldExpression(expression) => { - is_keyword(expression.field.name.as_str()) + is_reserved_keyword_or_global_object(expression.field.name.as_str()) } _ => false, }; diff --git a/crates/oxc_minifier/tests/closure/substitute_alternate_syntax.rs b/crates/oxc_minifier/tests/closure/substitute_alternate_syntax.rs index d5d21caef..15b10560a 100644 --- a/crates/oxc_minifier/tests/closure/substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/tests/closure/substitute_alternate_syntax.rs @@ -23,5 +23,5 @@ fn undefined() { test("try {} catch(undefined) {}", "try{}catch(undefined){}"); test("for (undefined in {}) {}", "for(undefined in {}){}"); test("undefined++", "undefined++;"); - test("undefined += undefined;", "(undefined+=void 0);"); + test("undefined += undefined;", "undefined+=void 0;"); } diff --git a/crates/oxc_syntax/src/keyword.rs b/crates/oxc_syntax/src/keyword.rs index 8e48757f1..39f077713 100644 --- a/crates/oxc_syntax/src/keyword.rs +++ b/crates/oxc_syntax/src/keyword.rs @@ -1,13 +1,82 @@ -#[rustfmt::skip] -pub fn is_keyword(s: &str) -> bool { - matches!(s, "as" | "do" | "if" | "in" | "is" | "of" - | "any" | "for" | "get" | "let" | "new" | "out" | "set" | "try" | "var" - | "case" | "else" | "enum" | "from" | "meta" | "null" | "this" | "true" | "type" | "void" | "with" - | "async" | "await" | "break" | "catch" | "class" | "const" | "false" | "infer" | "keyof" | "never" | "super" | "throw" | "using" | "while" | "yield" - | "assert" | "bigint" | "delete" | "export" | "global" | "import" | "module" | "number" | "object" | "public" | "return" | "static" | "string" | "switch" | "symbol" | "target" | "typeof" | "unique" - | "asserts" | "boolean" | "declare" | "default" | "extends" | "finally" | "package" | "private" | "require" | "unknown" | "abstract" - | "accessor" | "continue" | "debugger" | "function" | "override" | "readonly" | "interface" | "intrinsic" | "namespace" | "protected" | "satisfies" | "undefined" - | "implements" | "instanceof" - | "constructor" - ) +use phf::{phf_set, Set}; + +#[inline] +pub fn is_reserved_keyword_or_global_object(s: &str) -> bool { + is_reserved_keyword(s) || is_reserved_keyword(s) } + +#[inline] +pub fn is_reserved_keyword(s: &str) -> bool { + RESERVED_KEYWORDS.contains(s) +} + +/// Checks `Infinity`, `NaN`, `globalThis` and `undefined` +#[inline] +pub fn is_global_object(s: &str) -> bool { + GLOBAL_OBJECTS.contains(s) +} + +/// Value properties of the global object +/// +/// Reference: +pub const GLOBAL_OBJECTS: Set<&'static str> = phf_set! { + "Infinity", + "NaN", + "globalThis", + "undefined", +}; + +/// All reserved keywords, including keywords that are contextually disallowed as identifiers. +/// +/// Reference: +pub const RESERVED_KEYWORDS: Set<&'static str> = phf_set! { + // contextually disallowed as identifiers + "let", + "static", + // future reserved keywords + "implements", + "interface", + "package", + "private", + "protected", + "public", + // reserved word + "await", + "break", + "case", + "catch", + "class", + "const", + "continue", + "debugger", + "default", + "delete", + "do", + "else", + "enum", + "export", + "extends", + "false", + "finally", + "for", + "function", + "if", + "import", + "in", + "instanceof", + "new", + "null", + "return", + "super", + "switch", + "this", + "throw", + "true", + "try", + "typeof", + "var", + "void", + "while", + "with", + "yield", +}; diff --git a/tasks/coverage/codegen_sourcemap.snap b/tasks/coverage/codegen_sourcemap.snap index 1614323b8..ec17cf9ed 100644 --- a/tasks/coverage/codegen_sourcemap.snap +++ b/tasks/coverage/codegen_sourcemap.snap @@ -1132,9 +1132,9 @@ Invalid Character `[` (430:43-430:55) ".prototype =" --> (217:45-217:57) ".prototype =" (430:55-430:59) " new" --> (217:57-217:61) " new" (430:59-431:0) " ComponentDummy();" --> (217:61-218:0) " ComponentDummy();" -(431:0-431:23) "\npureComponentPrototype" --> (218:0-218:26) "\n\t\t(pureComponentPrototype" -(431:23-431:37) ".constructor =" --> (218:26-218:40) ".constructor =" -(431:37-433:0) " PureComponent; // Avoid an extra prototype jump for these methods.\n" --> (218:40-219:0) " PureComponent);" +(431:0-431:23) "\npureComponentPrototype" --> (218:0-218:25) "\n\t\tpureComponentPrototype" +(431:23-431:37) ".constructor =" --> (218:25-218:39) ".constructor =" +(431:37-433:0) " PureComponent; // Avoid an extra prototype jump for these methods.\n" --> (218:39-219:0) " PureComponent;" (433:0-433:8) "\n_assign" --> (219:0-219:10) "\n\t\t_assign" (433:8-433:32) "(pureComponentPrototype," --> (219:10-219:34) "(pureComponentPrototype," (433:32-433:42) " Component" --> (219:34-219:44) " Component" @@ -5045,9 +5045,9 @@ Invalid Character `[` (2246:58-2246:64) "(null," --> (1464:59-1464:65) "(null," (2246:64-2246:69) " type" --> (1464:65-1464:70) " type" (2246:69-2247:2) ");\n " --> (1464:70-1465:0) ");" -(2247:2-2247:19) " validatedFactory" --> (1465:0-1465:21) "\n\t\t\t(validatedFactory" -(2247:19-2247:26) ".type =" --> (1465:21-1465:28) ".type =" -(2247:26-2249:2) " type;\n\n " --> (1465:28-1466:3) " type);\n\t\t" +(2247:2-2247:19) " validatedFactory" --> (1465:0-1465:20) "\n\t\t\tvalidatedFactory" +(2247:19-2247:26) ".type =" --> (1465:20-1465:27) ".type =" +(2247:26-2249:2) " type;\n\n " --> (1465:27-1466:3) " type;\n\t\t" (2249:2-2250:4) " {\n " --> (1466:3-1467:0) "\t{" (2250:4-2250:9) " if (" --> (1467:0-1467:10) "\n\t\t\t\tif ( " (2250:9-2250:46) "!didWarnAboutDeprecatedCreateFactory)" --> (1467:10-1467:47) "!didWarnAboutDeprecatedCreateFactory)" diff --git a/tasks/website/Cargo.toml b/tasks/website/Cargo.toml index 3d545e4fe..45ce8ea8d 100644 --- a/tasks/website/Cargo.toml +++ b/tasks/website/Cargo.toml @@ -14,9 +14,9 @@ test = false [dependencies] oxc_linter = { workspace = true } -oxc_cli = { path = "../../crates/oxc_cli" } +oxc_cli = { path = "../../crates/oxc_cli" } -bpaf = { workspace = true, features = ["docgen"] } +bpaf = { workspace = true, features = ["docgen"] } pico-args = { workspace = true } serde_json = { workspace = true } schemars = { workspace = true }