Skip to content

Commit

Permalink
fix(transformer/react): support emit_full_signatures option in refres…
Browse files Browse the repository at this point in the history
…h plugin
  • Loading branch information
Dunqing authored and Boshen committed Sep 11, 2024
1 parent 36d864a commit 37311d6
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 14 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/oxc_transformer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
13 changes: 13 additions & 0 deletions crates/oxc_transformer/src/react/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
19 changes: 16 additions & 3 deletions crates/oxc_transformer/src/react/refresh.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
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};
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;
Expand All @@ -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<oxc_allocator::Vec<'a, VariableDeclarator<'a>>>,
Expand All @@ -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,
Expand Down Expand Up @@ -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::<Vec<_>>()
.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(
Expand Down
7 changes: 5 additions & 2 deletions tasks/transform_conformance/oxc.snap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
commit: 3bcfee23

Passed: 17/50
Passed: 17/51

# All Passed:
* babel-plugin-transform-nullish-coalescing-operator
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default function Bar () {
useContext(X)
return <Foo />
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"plugins": [
[
"transform-react-jsx",
{
"refresh": {
"emitFullSignatures": false
}
}
]
]
}
Original file line number Diff line number Diff line change
@@ -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");
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
{
"plugins": [["transform-react-jsx", {
"refresh": {}
}]]
"plugins": [
[
"transform-react-jsx",
{
"refresh": {
"emitFullSignatures": true
}
}
]
]
}
Original file line number Diff line number Diff line change
@@ -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
}
}
]
]
}

0 comments on commit 37311d6

Please sign in to comment.