From 37311d6b2c00b83fa8992ac0fbe703db6aed1914 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Sun, 8 Sep 2024 15:28:40 +0800 Subject: [PATCH] fix(transformer/react): support emit_full_signatures option in refresh plugin --- Cargo.lock | 13 +++++++++++++ crates/oxc_transformer/Cargo.toml | 2 ++ crates/oxc_transformer/src/react/options.rs | 13 +++++++++++++ crates/oxc_transformer/src/react/refresh.rs | 19 ++++++++++++++++--- tasks/transform_conformance/oxc.snap.md | 7 +++++-- .../emit-full-signatures-option/input.jsx | 4 ++++ .../emit-full-signatures-option/options.json | 12 ++++++++++++ .../emit-full-signatures-option/output.js | 12 ++++++++++++ .../test/fixtures/refresh/options.json | 13 ++++++++++--- .../options.json | 18 ++++++++++++------ 10 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/input.jsx create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/options.json create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/output.js diff --git a/Cargo.lock b/Cargo.lock index 2f9de16a666e71..713acfaf8034d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1957,6 +1957,7 @@ dependencies = [ name = "oxc_transformer" version = "0.27.0" dependencies = [ + "base64", "dashmap 6.0.1", "indexmap", "oxc-browserslist", @@ -1975,6 +1976,7 @@ dependencies = [ "rustc-hash", "serde", "serde_json", + "sha1", ] [[package]] @@ -2701,6 +2703,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index 6f705179e89c9d..5c9be2e348f44d 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -37,6 +37,8 @@ ropey = { workspace = true } rustc-hash = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } +base64 = { workspace = true } +sha1 = { version = "0.10.6" } [dev-dependencies] oxc_codegen = { workspace = true } diff --git a/crates/oxc_transformer/src/react/options.rs b/crates/oxc_transformer/src/react/options.rs index 5b9c590342d688..845ebe3fcc98ea 100644 --- a/crates/oxc_transformer/src/react/options.rs +++ b/crates/oxc_transformer/src/react/options.rs @@ -195,15 +195,28 @@ impl ReactOptions { #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ReactRefreshOptions { /// Specify the identifier of the refresh registration variable. + /// /// Defaults to `$RefreshReg$`. #[serde(default = "default_refresh_reg")] pub refresh_reg: String, /// Specify the identifier of the refresh signature variable. + /// /// Defaults to `$RefreshSig$`. #[serde(default = "default_refresh_sig")] pub refresh_sig: String, + /// Controls whether to emit full signatures or use a more compact representation. + /// + /// When set to `true`, this option causes this plugin to emit full, readable signatures + /// for React components and hooks. This can be useful for debugging and development purposes. + /// + /// When set to `false` (default), the transformer will use a more compact representation. + /// Specifically, it generates a SHA-1 hash of the signature and then encodes it using Base64. + /// This process produces a deterministic, compact representation that's suitable for + /// production builds while still uniquely identifying components. + /// + /// Defaults to `false`. #[serde(default)] pub emit_full_signatures: bool, } diff --git a/crates/oxc_transformer/src/react/refresh.rs b/crates/oxc_transformer/src/react/refresh.rs index 80846895974dfc..125eef51b219ae 100644 --- a/crates/oxc_transformer/src/react/refresh.rs +++ b/crates/oxc_transformer/src/react/refresh.rs @@ -1,5 +1,6 @@ use std::{cell::Cell, iter::once}; +use base64::prelude::{Engine, BASE64_STANDARD}; use oxc_allocator::CloneIn; use oxc_ast::{ast::*, match_expression, match_member_expression}; use oxc_semantic::{Reference, ReferenceFlags, ScopeId, SymbolFlags, SymbolId}; @@ -7,6 +8,7 @@ use oxc_span::{Atom, GetSpan, SPAN}; use oxc_syntax::operator::AssignmentOperator; use oxc_traverse::{Ancestor, Traverse, TraverseCtx}; use rustc_hash::FxHashMap; +use sha1::{Digest, Sha1}; use super::options::ReactRefreshOptions; use crate::context::Ctx; @@ -22,7 +24,7 @@ use crate::context::Ctx; pub struct ReactRefresh<'a> { refresh_reg: Atom<'a>, refresh_sig: Atom<'a>, - _emit_full_signatures: bool, + emit_full_signatures: bool, registrations: Vec<(SymbolId, Atom<'a>)>, ctx: Ctx<'a>, signature_declarator_items: Vec>>, @@ -41,7 +43,7 @@ impl<'a> ReactRefresh<'a> { Self { refresh_reg: ctx.ast.atom(&options.refresh_reg), refresh_sig: ctx.ast.atom(&options.refresh_sig), - _emit_full_signatures: options.emit_full_signatures, + emit_full_signatures: options.emit_full_signatures, signature_declarator_items: Vec::new(), registrations: Vec::default(), ctx, @@ -542,12 +544,23 @@ impl<'a> ReactRefresh<'a> { ) -> Option<(BindingIdentifier<'a>, oxc_allocator::Vec<'a, Argument<'a>>)> { let fn_hook_calls = self.hook_calls.remove(&scope_id)?; - let key = fn_hook_calls + let mut key = fn_hook_calls .into_iter() .map(|(hook_name, hook_key)| format!("{hook_name}{{{hook_key}}}")) .collect::>() .join("\\n"); + if !self.emit_full_signatures { + // Prefer to hash when we can (e.g. outside of ASTExplorer). + // This makes it deterministically compact, even if there's + // e.g. a useState initializer with some code inside. + // We also need it for www that has transforms like cx() + // that don't understand if something is part of a string. + let mut hasher = Sha1::new(); + hasher.update(key); + key = BASE64_STANDARD.encode(hasher.finalize()); + } + let callee_list = self.non_builtin_hooks_callee.remove(&scope_id).unwrap_or_default(); let callee_len = callee_list.len(); let custom_hooks_in_scope = ctx.ast.vec_from_iter( diff --git a/tasks/transform_conformance/oxc.snap.md b/tasks/transform_conformance/oxc.snap.md index 67c0f5f9fbc73a..8dd15ff407c6ff 100644 --- a/tasks/transform_conformance/oxc.snap.md +++ b/tasks/transform_conformance/oxc.snap.md @@ -1,6 +1,6 @@ commit: 3bcfee23 -Passed: 17/50 +Passed: 17/51 # All Passed: * babel-plugin-transform-nullish-coalescing-operator @@ -167,7 +167,7 @@ rebuilt : SymbolId(2): [] x Output mismatch -# babel-plugin-transform-react-jsx (3/28) +# babel-plugin-transform-react-jsx (3/29) * refresh/can-handle-implicit-arrow-returns/input.jsx Symbol reference IDs mismatch: after transform: SymbolId(9): [ReferenceId(23), ReferenceId(24), ReferenceId(25)] @@ -268,6 +268,9 @@ rebuilt : ["$RefreshSig$", "item", "useFoo"] * refresh/does-not-transform-it-because-it-is-not-used-in-the-AST/input.jsx x Output mismatch +* refresh/emit-full-signatures-option/input.jsx +x Output mismatch + * refresh/generates-signatures-for-function-declarations-calling-hooks/input.jsx x Output mismatch diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/input.jsx b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/input.jsx new file mode 100644 index 00000000000000..7e871cb8dc04d0 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/input.jsx @@ -0,0 +1,4 @@ +export default function Bar () { + useContext(X) + return +}; \ No newline at end of file diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/options.json b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/options.json new file mode 100644 index 00000000000000..b3692da53a7631 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/options.json @@ -0,0 +1,12 @@ +{ + "plugins": [ + [ + "transform-react-jsx", + { + "refresh": { + "emitFullSignatures": false + } + } + ] + ] +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/output.js new file mode 100644 index 00000000000000..15638c1b2282f6 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/emit-full-signatures-option/output.js @@ -0,0 +1,12 @@ +import { jsx as _jsx } from "react/jsx-runtime"; +var _s = $RefreshSig$(); +export default function Bar() { + _s(); + useContext(X); + return _jsx(Foo, {}); +} +_s(Bar, "gDsCjeeItUuvgOWf1v4qoK9RF6k="); +_c = Bar; +; +var _c; +$RefreshReg$(_c, "Bar"); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/options.json b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/options.json index ab1dae10ca5aeb..9e510a659005c2 100644 --- a/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/options.json +++ b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/options.json @@ -1,5 +1,12 @@ { - "plugins": [["transform-react-jsx", { - "refresh": {} - }]] + "plugins": [ + [ + "transform-react-jsx", + { + "refresh": { + "emitFullSignatures": true + } + } + ] + ] } diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/uses-custom-identifiers-for-refresh-reg-and-refresh-sig/options.json b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/uses-custom-identifiers-for-refresh-reg-and-refresh-sig/options.json index be726ca7ef2933..8fa24088726aef 100644 --- a/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/uses-custom-identifiers-for-refresh-reg-and-refresh-sig/options.json +++ b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/refresh/uses-custom-identifiers-for-refresh-reg-and-refresh-sig/options.json @@ -1,8 +1,14 @@ { - "plugins": [["transform-react-jsx", { - "refresh": { - "refreshReg": "import.meta.refreshReg", - "refreshSig": "import.meta.refreshSig" - } - }]] + "plugins": [ + [ + "transform-react-jsx", + { + "refresh": { + "refreshReg": "import.meta.refreshReg", + "refreshSig": "import.meta.refreshSig", + "emitFullSignatures": true + } + } + ] + ] }