From b075982eaa6d11838a676d9129703f4d1070d7e8 Mon Sep 17 00:00:00 2001 From: ottomated <31470743+ottomated@users.noreply.github.com> Date: Thu, 24 Oct 2024 20:04:09 +0000 Subject: [PATCH 1/5] fix(types): Change @oxc/types package name (#6874) Closes #6862. Possible options: - `oxc-types` - `@oxc-project/types` - `@oxc-ast/types` - `oxc-ast-types` --- .github/workflows/release_types.yml | 6 +++--- crates/oxc/src/napi/parse.rs | 2 +- crates/oxc_wasm/package.json | 2 +- crates/oxc_wasm/src/lib.rs | 4 ++-- napi/parser/index.d.ts | 2 +- npm/oxc-parser/package.json | 4 ++-- npm/oxc-types/package.json | 2 +- pnpm-lock.yaml | 8 ++++---- wasm/parser/package.json | 2 +- wasm/parser/src/lib.rs | 4 ++-- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/release_types.yml b/.github/workflows/release_types.yml index 5ba137eae5050..796d5a3eb15d8 100644 --- a/.github/workflows/release_types.yml +++ b/.github/workflows/release_types.yml @@ -1,4 +1,4 @@ -name: Release @oxc/types +name: Release @oxc-project/types on: workflow_dispatch: @@ -27,7 +27,7 @@ jobs: id: version with: static-checking: localIsNew - file-url: https://unpkg.com/@oxc/types/package.json + file-url: https://unpkg.com/@oxc-project/types/package.json file-name: npm/oxc-types/package.json - name: Set version name @@ -38,7 +38,7 @@ jobs: build: needs: check if: needs.check.outputs.version_changed == 'true' - name: Release @oxc/types + name: Release @oxc-project/types runs-on: ubuntu-latest permissions: id-token: write # for `pnpm publish --provenance` diff --git a/crates/oxc/src/napi/parse.rs b/crates/oxc/src/napi/parse.rs index 9a3fc3044736c..6ab5d3ce8e3df 100644 --- a/crates/oxc/src/napi/parse.rs +++ b/crates/oxc/src/napi/parse.rs @@ -21,7 +21,7 @@ pub struct ParserOptions { #[napi(object)] pub struct ParseResult { - #[napi(ts_type = "import(\"@oxc/types\").Program")] + #[napi(ts_type = "import(\"@oxc-project/types\").Program")] pub program: String, pub comments: Vec, pub errors: Vec, diff --git a/crates/oxc_wasm/package.json b/crates/oxc_wasm/package.json index 1df1e71d9a79b..9f0966b7a1623 100644 --- a/crates/oxc_wasm/package.json +++ b/crates/oxc_wasm/package.json @@ -13,7 +13,7 @@ "oxc_wasm.d.ts" ], "devDependencies": { - "@oxc/types": "workspace:^" + "@oxc-project/types": "workspace:^" }, "main": "oxc_wasm.js", "types": "oxc_wasm.d.ts", diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index e63257ac9af3b..10097dbe2eaca 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -34,8 +34,8 @@ use crate::options::{OxcOptions, OxcRunOptions}; #[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)] const TS_APPEND_CONTENT: &'static str = r#" -import type { Program, Span } from "@oxc/types"; -export * from "@oxc/types"; +import type { Program, Span } from "@oxc-project/types"; +export * from "@oxc-project/types"; "#; #[wasm_bindgen(getter_with_clone)] diff --git a/napi/parser/index.d.ts b/napi/parser/index.d.ts index 790372d240cf3..77423644b5bbe 100644 --- a/napi/parser/index.d.ts +++ b/napi/parser/index.d.ts @@ -91,7 +91,7 @@ export declare function moduleLexerSync(sourceText: string, options?: ParserOpti export declare function parseAsync(sourceText: string, options?: ParserOptions | undefined | null): Promise export interface ParseResult { - program: import("@oxc/types").Program + program: import("@oxc-project/types").Program comments: Array errors: Array } diff --git a/npm/oxc-parser/package.json b/npm/oxc-parser/package.json index 0de44788cf50b..bb76fe7a2a938 100644 --- a/npm/oxc-parser/package.json +++ b/npm/oxc-parser/package.json @@ -24,6 +24,6 @@ "bindings.js" ], "devDependencies": { - "@oxc/types": "workspace:^" + "@oxc-project/types": "workspace:^" } -} \ No newline at end of file +} diff --git a/npm/oxc-types/package.json b/npm/oxc-types/package.json index 05ca8ba3e1eac..0feffc60f7d19 100644 --- a/npm/oxc-types/package.json +++ b/npm/oxc-types/package.json @@ -1,5 +1,5 @@ { - "name": "@oxc/types", + "name": "@oxc-project/types", "version": "0.32.0", "description": "Types for Oxc AST nodes", "keywords": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba34384b77550..5c90fa94031fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,7 +60,7 @@ importers: npm/oxc-parser: devDependencies: - '@oxc/types': + '@oxc-project/types': specifier: workspace:^ version: link:../oxc-types @@ -70,7 +70,7 @@ importers: npm/oxc-wasm: devDependencies: - '@oxc/types': + '@oxc-project/types': specifier: workspace:^ version: link:../oxc-types @@ -78,7 +78,7 @@ importers: npm/parser-wasm: devDependencies: - '@oxc/types': + '@oxc-project/types': specifier: workspace:^ version: link:../oxc-types @@ -96,7 +96,7 @@ importers: wasm/parser: devDependencies: - '@oxc/types': + '@oxc-project/types': specifier: workspace:^ version: link:../../npm/oxc-types diff --git a/wasm/parser/package.json b/wasm/parser/package.json index fe3a50a5a0c06..0d53340d39581 100644 --- a/wasm/parser/package.json +++ b/wasm/parser/package.json @@ -27,7 +27,7 @@ "web" ], "devDependencies": { - "@oxc/types": "workspace:^" + "@oxc-project/types": "workspace:^" }, "scripts": { "build": "pnpm run build-node && pnpm run build-web && pnpm run copy-files && pnpm run clean-files", diff --git a/wasm/parser/src/lib.rs b/wasm/parser/src/lib.rs index 0a850cb3e5852..7a26a4706b244 100644 --- a/wasm/parser/src/lib.rs +++ b/wasm/parser/src/lib.rs @@ -9,8 +9,8 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)] const TS_APPEND_CONTENT: &'static str = r#" -import type { Program } from "@oxc/types"; -export * from "@oxc/types"; +import type { Program } from "@oxc-project/types"; +export * from "@oxc-project/types"; "#; #[derive(Debug, Default, Clone, Deserialize, Tsify)] From fccf82e4dffbb1313599eda107a9afb0b192fa0a Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Fri, 25 Oct 2024 01:29:56 +0000 Subject: [PATCH 2/5] feat(minifier): implement folding `substring` string fns (#6869) --- crates/oxc_ecmascript/src/lib.rs | 6 +- crates/oxc_ecmascript/src/string_substring.rs | 37 ++++++++++++ .../peephole_replace_known_methods.rs | 58 +++++++++++++++++-- 3 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 crates/oxc_ecmascript/src/string_substring.rs diff --git a/crates/oxc_ecmascript/src/lib.rs b/crates/oxc_ecmascript/src/lib.rs index ebc3612fa1d13..1c3d635c7623a 100644 --- a/crates/oxc_ecmascript/src/lib.rs +++ b/crates/oxc_ecmascript/src/lib.rs @@ -11,6 +11,7 @@ mod string_char_at; mod string_char_code_at; mod string_index_of; mod string_last_index_of; +mod string_substring; mod string_to_big_int; mod string_to_number; mod to_big_int; @@ -30,6 +31,7 @@ pub use self::{ private_bound_identifiers::PrivateBoundIdentifiers, prop_name::PropName, string_char_at::StringCharAt, string_char_code_at::StringCharCodeAt, string_index_of::StringIndexOf, string_last_index_of::StringLastIndexOf, - string_to_big_int::StringToBigInt, string_to_number::StringToNumber, to_big_int::ToBigInt, - to_boolean::ToBoolean, to_int_32::ToInt32, to_number::ToNumber, to_string::ToJsString, + string_substring::StringSubstring, string_to_big_int::StringToBigInt, + string_to_number::StringToNumber, to_big_int::ToBigInt, to_boolean::ToBoolean, + to_int_32::ToInt32, to_number::ToNumber, to_string::ToJsString, }; diff --git a/crates/oxc_ecmascript/src/string_substring.rs b/crates/oxc_ecmascript/src/string_substring.rs new file mode 100644 index 0000000000000..1518e688ba667 --- /dev/null +++ b/crates/oxc_ecmascript/src/string_substring.rs @@ -0,0 +1,37 @@ +use crate::ToInt32; + +pub trait StringSubstring { + /// `String.prototype.substring ( start , end ] )` + /// + fn substring(&self, start: Option, end: Option) -> String; +} + +impl StringSubstring for &str { + #[expect(clippy::cast_sign_loss)] + fn substring(&self, start: Option, end: Option) -> String { + let start = start.map_or(0, |x| x.to_int_32().max(0) as usize); + let end = end.map_or(usize::MAX, |x| x.to_int_32().max(0) as usize); + let start = start.min(self.len()); + let end = end.min(self.len()); + if start > end { + return String::new(); + } + self.chars().skip(start).take(end - start).collect() + } +} + +#[cfg(test)] +mod test { + #[test] + fn test_prototype_last_index_of() { + use super::StringSubstring; + assert_eq!("foo".substring(Some(1.0), None), "oo"); + assert_eq!("foo".substring(Some(1.0), Some(2.0)), "o"); + assert_eq!("foo".substring(Some(1.0), Some(1.0)), ""); + assert_eq!("foo".substring(Some(1.0), Some(0.0)), ""); + assert_eq!("foo".substring(Some(0.0), Some(0.0)), ""); + assert_eq!("foo".substring(Some(0.0), Some(1.0)), "f"); + assert_eq!("abc".substring(Some(0.0), Some(2.0)), "ab"); + assert_eq!("abcde".substring(Some(0.0), Some(2.0)), "ab"); + } +} diff --git a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs index 00a845a2d8048..94238776cd42e 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs @@ -2,7 +2,7 @@ use cow_utils::CowUtils; use oxc_ast::ast::*; use oxc_ecmascript::{ constant_evaluation::ConstantEvaluation, StringCharAt, StringCharCodeAt, StringIndexOf, - StringLastIndexOf, + StringLastIndexOf, StringSubstring, }; use oxc_traverse::{Traverse, TraverseCtx}; @@ -76,7 +76,12 @@ impl PeepholeReplaceKnownMethods { ctx, ), // TODO: Implement the rest of the string methods - "substring" | "slice" => None, + "substring" | "slice" => Self::try_fold_string_substring_or_slice( + call_expr.span, + call_expr, + string_lit, + ctx, + ), "charAt" => { Self::try_fold_string_char_at(call_expr.span, call_expr, string_lit, ctx) } @@ -131,6 +136,53 @@ impl PeepholeReplaceKnownMethods { ))); } + fn try_fold_string_substring_or_slice<'a>( + span: Span, + call_expr: &CallExpression<'a>, + string_lit: &StringLiteral<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + if call_expr.arguments.len() > 2 { + return None; + } + + let start_idx = if let Some(v) = call_expr.arguments.first() { + let val = match v { + Argument::SpreadElement(_) => None, + _ => Ctx(ctx).get_side_free_number_value(v.to_expression()), + }?; + Some(val) + } else { + None + }; + let end_idx = if let Some(v) = call_expr.arguments.get(1) { + let val = match v { + Argument::SpreadElement(_) => None, + _ => Ctx(ctx).get_side_free_number_value(v.to_expression()), + }?; + Some(val) + } else { + None + }; + + #[expect(clippy::cast_precision_loss)] + if start_idx.is_some_and(|start| start > string_lit.value.len() as f64 || start < 0.0) + || end_idx.is_some_and(|end| end > string_lit.value.len() as f64 || end < 0.0) + { + return None; + } + + if let (Some(start), Some(end)) = (start_idx, end_idx) { + if start > end { + return None; + } + }; + + return Some(ctx.ast.expression_from_string_literal( + ctx.ast.string_literal(span, string_lit.value.as_str().substring(start_idx, end_idx)), + )); + } + fn try_fold_string_char_at<'a>( span: Span, call_expr: &CallExpression<'a>, @@ -393,7 +445,6 @@ mod test { } #[test] - #[ignore] fn test_fold_string_substring() { fold("x = 'abcde'.substring(0,2)", "x = 'ab'"); fold("x = 'abcde'.substring(1,2)", "x = 'b'"); @@ -412,7 +463,6 @@ mod test { } #[test] - #[ignore] fn test_fold_string_slice() { fold("x = 'abcde'.slice(0,2)", "x = 'ab'"); fold("x = 'abcde'.slice(1,2)", "x = 'b'"); From 50744f341be8ed3b1c1f1310f6de339b4c7b9a7d Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Fri, 25 Oct 2024 01:29:56 +0000 Subject: [PATCH 3/5] feat(minifier): implement folding String.prototype.replace (#6870) --- .../peephole_replace_known_methods.rs | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs index 94238776cd42e..d92853f35baa5 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs @@ -88,7 +88,9 @@ impl PeepholeReplaceKnownMethods { "charCodeAt" => { Self::try_fold_string_char_code_at(call_expr.span, call_expr, string_lit, ctx) } - "replace" => None, + "replace" => { + Self::try_fold_string_replace(call_expr.span, call_expr, string_lit, ctx) + } "replaceAll" => None, _ => None, }; @@ -244,6 +246,40 @@ impl PeepholeReplaceKnownMethods { NumberBase::Decimal, ))) } + fn try_fold_string_replace<'a>( + span: Span, + call_expr: &CallExpression<'a>, + string_lit: &StringLiteral<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + if call_expr.arguments.len() != 2 { + return None; + } + + let search_value = call_expr.arguments.first().unwrap(); + let search_value = match search_value { + Argument::SpreadElement(_) => return None, + match_expression!(Argument) => { + Ctx(ctx).get_side_free_string_value(search_value.to_expression())? + } + }; + let replace_value = call_expr.arguments.get(1).unwrap(); + let replace_value = match replace_value { + Argument::SpreadElement(_) => return None, + match_expression!(Argument) => { + Ctx(ctx).get_side_free_string_value(replace_value.to_expression())? + } + }; + + if replace_value.contains('$') { + return None; + } + + let result = + string_lit.value.as_str().cow_replacen(search_value.as_ref(), &replace_value, 1); + + Some(ctx.ast.expression_from_string_literal(ctx.ast.string_literal(span, result))) + } } /// Port from: @@ -394,7 +430,6 @@ mod test { } #[test] - #[ignore] fn test_fold_string_replace() { fold("'c'.replace('c','x')", "'x'"); fold("'ac'.replace('c','x')", "'ax'"); From c26020e553c041a9dc8391b1ddf4c570bfab0845 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Fri, 25 Oct 2024 01:29:57 +0000 Subject: [PATCH 4/5] feat(minifier): implement folding String.prototype.replaceAll (#6871) --- .../peephole_replace_known_methods.rs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs index d92853f35baa5..6ab3fb650567b 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs @@ -45,7 +45,6 @@ impl PeepholeReplaceKnownMethods { let Expression::StaticMemberExpression(member) = &call_expr.callee else { return }; if let Expression::StringLiteral(string_lit) = &member.object { - #[expect(clippy::match_same_arms)] let replacement = match member.property.name.as_str() { "toLowerCase" | "toUpperCase" | "trim" => { let transformed_value = @@ -75,7 +74,6 @@ impl PeepholeReplaceKnownMethods { string_lit, ctx, ), - // TODO: Implement the rest of the string methods "substring" | "slice" => Self::try_fold_string_substring_or_slice( call_expr.span, call_expr, @@ -88,10 +86,13 @@ impl PeepholeReplaceKnownMethods { "charCodeAt" => { Self::try_fold_string_char_code_at(call_expr.span, call_expr, string_lit, ctx) } - "replace" => { - Self::try_fold_string_replace(call_expr.span, call_expr, string_lit, ctx) - } - "replaceAll" => None, + "replace" | "replaceAll" => Self::try_fold_string_replace_or_string_replace_all( + call_expr.span, + call_expr, + member, + string_lit, + ctx, + ), _ => None, }; @@ -246,9 +247,10 @@ impl PeepholeReplaceKnownMethods { NumberBase::Decimal, ))) } - fn try_fold_string_replace<'a>( + fn try_fold_string_replace_or_string_replace_all<'a>( span: Span, call_expr: &CallExpression<'a>, + member: &StaticMemberExpression<'a>, string_lit: &StringLiteral<'a>, ctx: &mut TraverseCtx<'a>, ) -> Option> { @@ -275,8 +277,15 @@ impl PeepholeReplaceKnownMethods { return None; } - let result = - string_lit.value.as_str().cow_replacen(search_value.as_ref(), &replace_value, 1); + let result = match member.property.name.as_str() { + "replace" => { + string_lit.value.as_str().cow_replacen(search_value.as_ref(), &replace_value, 1) + } + "replaceAll" => { + string_lit.value.as_str().cow_replace(search_value.as_ref(), &replace_value) + } + _ => unreachable!(), + }; Some(ctx.ast.expression_from_string_literal(ctx.ast.string_literal(span, result))) } @@ -456,7 +465,6 @@ mod test { } #[test] - #[ignore] fn test_fold_string_replace_all() { fold("x = 'abcde'.replaceAll('bcd','c')", "x = 'ace'"); fold("x = 'abcde'.replaceAll('c','xxx')", "x = 'abxxxde'"); From f49b3e20880adf02b6d778674a75bca3c85e3572 Mon Sep 17 00:00:00 2001 From: DonIsaac <22823424+DonIsaac@users.noreply.github.com> Date: Fri, 25 Oct 2024 01:43:55 +0000 Subject: [PATCH 5/5] fix(linter): `react/iframe-missing-sandbox` ignores vanilla JS APIs (#6872) > Closes #6750 Fixes a false positive in `react/iframe-missing-sandbox` on `document.createElement`, which is not react and has no way of passing a sandbox prop/attribute on creation. --- .../src/rules/react/iframe_missing_sandbox.rs | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/crates/oxc_linter/src/rules/react/iframe_missing_sandbox.rs b/crates/oxc_linter/src/rules/react/iframe_missing_sandbox.rs index 0fcd3f4300beb..7d46bfca5aee1 100644 --- a/crates/oxc_linter/src/rules/react/iframe_missing_sandbox.rs +++ b/crates/oxc_linter/src/rules/react/iframe_missing_sandbox.rs @@ -8,6 +8,7 @@ use oxc_macros::declare_oxc_lint; use oxc_span::Span; use phf::{phf_set, Set}; +use crate::ast_util::is_method_call; use crate::utils::{get_prop_value, has_jsx_prop_ignore_case, is_create_element_call}; use crate::{context::LintContext, rule::Rule, AstNode}; @@ -57,12 +58,19 @@ declare_oxc_lint!( /// /// ### Why is this bad? /// - /// The sandbox attribute enables an extra set of restrictions for the content in the iframe. Using sandbox attribute is considered a good security practice. - /// To learn more about sandboxing, see [MDN's documentation on the `sandbox` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox). - + /// The sandbox attribute enables an extra set of restrictions for the + /// content in the iframe. Using sandbox attribute is considered a good + /// security practice. To learn more about sandboxing, see [MDN's + /// documentation on the `sandbox` + /// attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox). + /// + /// This rule checks all React `