[Question] Plugin-injected imports renamed by hygiene pass - how to preserve identifier names? #11269
-
Establishing ContextI am migrating our internal build tool from Babel to SWC, and internally, we have our own UI framework, and we need to support both our renderers for React and the internal framework. Practically speaking, I need some arbitrary Pragma to be called for the JSX transformation and add an import statement like so: Currently, I am adding this import statement with a custom Rust-based SWC plugin. Now let me explain what is my problem below. Problem StatementMy SWC plugin injects the JSX pragma imports, but the hygiene pass automatically renames the imported identifiers by adding numeric suffixes (e.g., This breaks the JSX transformation, since the pragma configuration expects the original identifier name. Broken transformation exampleOriginal source code: import styles from './styles.scss';
const componentView = () => (
<div>
<h1>Nani</h1>
</div>
);Expected transformed code: import { createElement as jsxPragma_createElement } from "my-renderer-library";
import styles from './styles.scss';
const componentView = ()=>{
return /*#__PURE__*/ jsxPragma_createElement("div", null, /*#__PURE__*/ jsxPragma_createElement("h1", null, "Nani"));
};
Actual transformed code: import { createElement as jsxPragma_createElement1 } from "my-renderer-library";
import styles from './styles.scss';
const componentView = ()=>{
return /*#__PURE__*/ jsxPragma_createElement("div", null, /*#__PURE__*/ jsxPragma_createElement("h1", null, "Nani"));
};Current SetupSWC Config: {
jsc: {
parser: {
syntax: hasTS ? "typescript" : "ecmascript",
...(hasTS ? { tsx: true } : { jsx: true }),
externalHelpers: true,
},
transform: {
react: {
runtime: "classic",
pragma: "jsxPragma_createElement",
pragmaFrag: "jsxPragma_Fragment",
},
},
experimental: {
plugins: [["my-jsx-plugin", { /* config */ }]],
},
module: {
type: "es6"
}
},
}Final QuestionIs there any known way to go around this problem? I'm new to compilers, Babel and SWC, so I'm learning along the way. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 8 replies
-
|
If you want to override how JSX classic runtime works, you need https://swc.rs/docs/contributing/es-commons/variable-management#syntax-context But not sure if the top-level mark is passed to the Wasm plugin. |
Beta Was this translation helpful? Give feedback.
-
|
@pedrofcarvalho the hygiene pass renames your injected identifiers because they get a fresh // in your plugin
fn visit_mut_module(&mut self, module: &mut Module) {
let ctxt = SyntaxContext::empty().apply_mark(self.unresolved_mark);
// use this ctxt for BOTH the import binding and JSX references
let pragma = Ident::new("jsxPragma_createElement".into(), DUMMY_SP, ctxt);
// inject: import { createElement as jsxPragma_createElement } from "..."
// and reference `pragma` everywhere in JSX
}the key insight: without this, your import gets one context and your usage gets another, so the hygiene pass thinks they're different identifiers and renames one of them. ref: SWC plugin cheatsheet covers how marks work in the transform pipeline. |
Beta Was this translation helpful? Give feedback.
If you want to override how JSX classic runtime works, you need
top_level_mark.https://swc.rs/docs/contributing/es-commons/variable-management#syntax-context
But not sure if the top-level mark is passed to the Wasm plugin.