[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. |
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. |
|
Pedro Fachetti Carvalho (@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. |
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.