Skip to content

Commit 54ab649

Browse files
committed
feat: support JSX preserve
1 parent 007c5f0 commit 54ab649

File tree

19 files changed

+1804
-72
lines changed

19 files changed

+1804
-72
lines changed

packages/core/src/config.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,8 @@ export function composeMinifyConfig(config: LibConfig): EnvironmentConfig {
397397
js: true,
398398
css: false,
399399
jsOptions: {
400+
// Tweaked based on https://github.com/web-infra-dev/rspack/blob/e350b76163c976ee48da54c29bbb9d72153738d7/crates/rspack_plugin_swc_js_minimizer/src/lib.rs#L40.
401+
test: /\.[cm]?jsx?(\?.*)?$/,
400402
minimizerOptions: {
401403
mangle: false,
402404
// MF assets are loaded over the network, which means they will not be compressed by the project. Therefore, minifying them is necessary.
@@ -838,6 +840,40 @@ const fixJsModuleTypePlugin = (): RsbuildPlugin => ({
838840
},
839841
});
840842

843+
const BundlePlugin = (): RsbuildPlugin => ({
844+
name: 'rslib:bundle',
845+
setup(api) {
846+
api.onBeforeBuild({
847+
order: 'post',
848+
handler: ({ bundlerConfigs }) => {
849+
if (bundlerConfigs) {
850+
for (const config of bundlerConfigs) {
851+
if (config?.module?.parser?.javascript?.jsx === true) {
852+
throw new Error(
853+
'Bundle mode does not support preserving JSX syntax. Set `bundle` to `false` or change the JSX runtime to `automatic` or `classic`.',
854+
);
855+
}
856+
}
857+
}
858+
},
859+
});
860+
},
861+
});
862+
863+
const composeBundleConfig = (
864+
bundle: LibConfig['bundle'],
865+
): { rsbuildConfig: EnvironmentConfig } => {
866+
if (bundle) {
867+
return {
868+
rsbuildConfig: {
869+
plugins: [BundlePlugin()],
870+
},
871+
};
872+
}
873+
874+
return { rsbuildConfig: {} };
875+
};
876+
841877
const composeShimsConfig = (
842878
format: Format,
843879
shims?: Shims,
@@ -1676,6 +1712,7 @@ async function composeLibRsbuildConfig(
16761712
redirect = {},
16771713
umdName,
16781714
} = config;
1715+
const { rsbuildConfig: bundleConfig } = composeBundleConfig(bundle);
16791716
const { rsbuildConfig: shimsConfig, enabledShims } = composeShimsConfig(
16801717
format,
16811718
shims,
@@ -1761,6 +1798,7 @@ async function composeLibRsbuildConfig(
17611798
const printFileSizeConfig = composePrintFileSizeConfig(bundle, target);
17621799

17631800
return mergeRsbuildConfig(
1801+
bundleConfig,
17641802
formatConfig,
17651803
// outputConfig,
17661804
shimsConfig,

pnpm-lock.yaml

Lines changed: 991 additions & 69 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ onlyBuiltDependencies:
1818

1919
overrides:
2020
'zx>@types/node': '-'
21+
'@rsbuild/core': 'link:../rsbuild/packages/core'
22+
'@rsbuild/plugin-react': 'link:../rsbuild/packages/plugin-react'
2123

2224
strictPeerDependencies: false
2325
autoInstallPeers: false
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
"use strict";
2+
var __webpack_modules__ = {
3+
"./App1": function(module) {
4+
module.exports = require("./App1.cjs");
5+
}
6+
};
7+
var __webpack_module_cache__ = {};
8+
function __webpack_require__(moduleId) {
9+
var cachedModule = __webpack_module_cache__[moduleId];
10+
if (void 0 !== cachedModule) return cachedModule.exports;
11+
var module = __webpack_module_cache__[moduleId] = {
12+
exports: {}
13+
};
14+
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
15+
return module.exports;
16+
}
17+
(()=>{
18+
var getProto = Object.getPrototypeOf ? (obj)=>Object.getPrototypeOf(obj) : (obj)=>obj.__proto__;
19+
var leafPrototypes;
20+
__webpack_require__.t = function(value, mode) {
21+
if (1 & mode) value = this(value);
22+
if (8 & mode) return value;
23+
if ('object' == typeof value && value) {
24+
if (4 & mode && value.__esModule) return value;
25+
if (16 & mode && 'function' == typeof value.then) return value;
26+
}
27+
var ns = Object.create(null);
28+
__webpack_require__.r(ns);
29+
var def = {};
30+
leafPrototypes = leafPrototypes || [
31+
null,
32+
getProto({}),
33+
getProto([]),
34+
getProto(getProto)
35+
];
36+
for(var current = 2 & mode && value; 'object' == typeof current && !~leafPrototypes.indexOf(current); current = getProto(current))Object.getOwnPropertyNames(current).forEach((key)=>{
37+
def[key] = ()=>value[key];
38+
});
39+
def['default'] = ()=>value;
40+
__webpack_require__.d(ns, def);
41+
return ns;
42+
};
43+
})();
44+
(()=>{
45+
__webpack_require__.d = (exports1, definition)=>{
46+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
47+
enumerable: true,
48+
get: definition[key]
49+
});
50+
};
51+
})();
52+
(()=>{
53+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
54+
})();
55+
(()=>{
56+
__webpack_require__.r = (exports1)=>{
57+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
58+
value: 'Module'
59+
});
60+
Object.defineProperty(exports1, '__esModule', {
61+
value: true
62+
});
63+
};
64+
})();
65+
var __webpack_exports__ = {};
66+
(()=>{
67+
__webpack_require__.r(__webpack_exports__);
68+
__webpack_require__.d(__webpack_exports__, {
69+
default: ()=>Root
70+
});
71+
var external_App1_cjs_ = __webpack_require__("./App1");
72+
const external_App2_cjs_namespaceObject = require("./App2.cjs");
73+
const DynamicComponent = ()=>{
74+
const Component = Math.random() > 0.5 ? external_App1_cjs_.App1A : external_App1_cjs_.App1C;
75+
return Promise.resolve().then(__webpack_require__.t.bind(__webpack_require__, "./App1", 23)).then((mod)=>{
76+
const Dynamic = mod[Component === external_App1_cjs_.App1A ? 'App1A' : 'App1C'];
77+
return <Dynamic/>;
78+
});
79+
};
80+
const SectionWithSpread = (props)=><section {...props}/>;
81+
const spreadChildren = [
82+
<span key="first">First</span>,
83+
<span key="second">Second</span>
84+
];
85+
function Root() {
86+
return <>
87+
<React.Fragment>
88+
<>
89+
<external_App1_cjs_.App1A/>
90+
<React.Suspense fallback={<div>Loading...</div>}>
91+
<DynamicComponent/>
92+
</React.Suspense>
93+
</>
94+
</React.Fragment>
95+
<external_App1_cjs_.App1C>
96+
{x ? <external_App1_cjs_.App1A>
97+
<external_App1_cjs_.App1B>
98+
<external_App2_cjs_namespaceObject.App2 props={external_App2_cjs_namespaceObject.app2Props}>
99+
<external_App1_cjs_.App1C props={external_App1_cjs_.app1cProps}/>
100+
</external_App2_cjs_namespaceObject.App2>
101+
</external_App1_cjs_.App1B>
102+
</external_App1_cjs_.App1A> : <external_App1_cjs_.App1B/>}
103+
</external_App1_cjs_.App1C>
104+
<external_App1_cjs_.App1C className="c"/>
105+
<external_App1_cjs_.App1C className="app1c"></external_App1_cjs_.App1C>
106+
<external_App1_cjs_.App1B/>
107+
<NamespaceComponents.Button label="Namespace button" {...{
108+
title: 'extra',
109+
['data-role']: 'primary'
110+
}} data-count={3} icon={<external_App1_cjs_.App1A/>} fragmentContent={<>
111+
<span>Nested</span>
112+
<span>Fragment</span>
113+
</>}/>
114+
<div className="wrapper">
115+
{[
116+
<section key="namespace-import" data-index="0">
117+
<external_App1_cjs_.App data-dynamic="registry" data-item="one"/>
118+
<foo:bar value="namespaced"/>
119+
<svg:path d="M0,0 L10,10" xlink:href="#one"/>
120+
<span>{'item-one'.toUpperCase()}</span>
121+
{}
122+
</section>,
123+
<section key="legacy-widget" data-index="1">
124+
<external_App2_cjs_namespaceObject.App2 data-dynamic="registry" data-item="two" {...external_App2_cjs_namespaceObject.app2Props}/>
125+
app2
126+
<external_App2_cjs_namespaceObject.App2/>
127+
<foo:bar value="namespaced-two"/>
128+
<svg:path d="M10,10 L20,20" xlink:href="#two"/>
129+
<external_App1_cjs_.App1A/>
130+
{}
131+
</section>,
132+
<section key="external-app" data-index="2">
133+
<external_App1_cjs_.App1A data-dynamic="registry" data-item="fallback"/>
134+
<foo:bar value="namespaced-three"/>
135+
<svg:path d="M20,20 L30,30" xlink:href="#three"/>
136+
{(()=><NamespaceComponents.Button label="Inline child"/>)()}
137+
{}
138+
</section>
139+
]}
140+
<group-container>{...spreadChildren}</group-container>
141+
<text-block dangerouslySetInnerHTML={{
142+
__html: '<strong>bold</strong>'
143+
}}/>
144+
<SectionWithSpread {...{
145+
'data-testid': 'component-with-spread',
146+
role: 'region'
147+
}}/>
148+
</div>
149+
</>;
150+
}
151+
})();
152+
exports["default"] = __webpack_exports__["default"];
153+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
154+
"default"
155+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
156+
Object.defineProperty(exports, '__esModule', {
157+
value: true
158+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { App, App1A, App1B, App1C, app1cProps } from "./App1.jsx";
2+
import { App2, app2Props } from "./App2.jsx";
3+
const DynamicComponent = ()=>{
4+
const Component = Math.random() > 0.5 ? App1A : App1C;
5+
return import("./App1.jsx").then((mod)=>{
6+
const Dynamic = mod[Component === App1A ? 'App1A' : 'App1C'];
7+
return <Dynamic/>;
8+
});
9+
};
10+
const SectionWithSpread = (props)=><section {...props}/>;
11+
const spreadChildren = [
12+
<span key="first">First</span>,
13+
<span key="second">Second</span>
14+
];
15+
function Root() {
16+
return <>
17+
<React.Fragment>
18+
<>
19+
<App1A/>
20+
<React.Suspense fallback={<div>Loading...</div>}>
21+
<DynamicComponent/>
22+
</React.Suspense>
23+
</>
24+
</React.Fragment>
25+
<App1C>
26+
{x ? <App1A>
27+
<App1B>
28+
<App2 props={app2Props}>
29+
<App1C props={app1cProps}/>
30+
</App2>
31+
</App1B>
32+
</App1A> : <App1B/>}
33+
</App1C>
34+
<App1C className="c"/>
35+
<App1C className="app1c"></App1C>
36+
<App1B/>
37+
<NamespaceComponents.Button label="Namespace button" {...{
38+
title: 'extra',
39+
['data-role']: 'primary'
40+
}} data-count={3} icon={<App1A/>} fragmentContent={<>
41+
<span>Nested</span>
42+
<span>Fragment</span>
43+
</>}/>
44+
<div className="wrapper">
45+
{[
46+
<section key="namespace-import" data-index="0">
47+
<App data-dynamic="registry" data-item="one"/>
48+
<foo:bar value="namespaced"/>
49+
<svg:path d="M0,0 L10,10" xlink:href="#one"/>
50+
<span>{'item-one'.toUpperCase()}</span>
51+
{}
52+
</section>,
53+
<section key="legacy-widget" data-index="1">
54+
<App2 data-dynamic="registry" data-item="two" {...app2Props}/>
55+
app2
56+
<App2/>
57+
<foo:bar value="namespaced-two"/>
58+
<svg:path d="M10,10 L20,20" xlink:href="#two"/>
59+
<App1A/>
60+
{}
61+
</section>,
62+
<section key="external-app" data-index="2">
63+
<App1A data-dynamic="registry" data-item="fallback"/>
64+
<foo:bar value="namespaced-three"/>
65+
<svg:path d="M20,20 L30,30" xlink:href="#three"/>
66+
{(()=><NamespaceComponents.Button label="Inline child"/>)()}
67+
{}
68+
</section>
69+
]}
70+
<group-container>{...spreadChildren}</group-container>
71+
<text-block dangerouslySetInnerHTML={{
72+
__html: '<strong>bold</strong>'
73+
}}/>
74+
<SectionWithSpread {...{
75+
'data-testid': 'component-with-spread',
76+
role: 'region'
77+
}}/>
78+
</div>
79+
</>;
80+
}
81+
export { Root as default };
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { App, App1A, App1B, App1C, app1cProps } from "./App1.js";
2+
import { App2, app2Props } from "./App2.js";
3+
const DynamicComponent = ()=>{
4+
const Component = Math.random() > 0.5 ? App1A : App1C;
5+
return import("./App1.js").then((mod)=>{
6+
const Dynamic = mod[Component === App1A ? 'App1A' : 'App1C'];
7+
return <Dynamic/>;
8+
});
9+
};
10+
const SectionWithSpread = (props)=><section {...props}/>;
11+
const spreadChildren = [
12+
<span key="first">First</span>,
13+
<span key="second">Second</span>
14+
];
15+
function Root() {
16+
return <>
17+
<React.Fragment>
18+
<>
19+
<App1A/>
20+
<React.Suspense fallback={<div>Loading...</div>}>
21+
<DynamicComponent/>
22+
</React.Suspense>
23+
</>
24+
</React.Fragment>
25+
<App1C>
26+
{x ? <App1A>
27+
<App1B>
28+
<App2 props={app2Props}>
29+
<App1C props={app1cProps}/>
30+
</App2>
31+
</App1B>
32+
</App1A> : <App1B/>}
33+
</App1C>
34+
<App1C className="c"/>
35+
<App1C className="app1c"></App1C>
36+
<App1B/>
37+
<NamespaceComponents.Button label="Namespace button" {...{
38+
title: 'extra',
39+
['data-role']: 'primary'
40+
}} data-count={3} icon={<App1A/>} fragmentContent={<>
41+
<span>Nested</span>
42+
<span>Fragment</span>
43+
</>}/>
44+
<div className="wrapper">
45+
{[
46+
<section key="namespace-import" data-index="0">
47+
<App data-dynamic="registry" data-item="one"/>
48+
<foo:bar value="namespaced"/>
49+
<svg:path d="M0,0 L10,10" xlink:href="#one"/>
50+
<span>{'item-one'.toUpperCase()}</span>
51+
{}
52+
</section>,
53+
<section key="legacy-widget" data-index="1">
54+
<App2 data-dynamic="registry" data-item="two" {...app2Props}/>
55+
app2
56+
<App2/>
57+
<foo:bar value="namespaced-two"/>
58+
<svg:path d="M10,10 L20,20" xlink:href="#two"/>
59+
<App1A/>
60+
{}
61+
</section>,
62+
<section key="external-app" data-index="2">
63+
<App1A data-dynamic="registry" data-item="fallback"/>
64+
<foo:bar value="namespaced-three"/>
65+
<svg:path d="M20,20 L30,30" xlink:href="#three"/>
66+
{(()=><NamespaceComponents.Button label="Inline child"/>)()}
67+
{}
68+
</section>
69+
]}
70+
<group-container>{...spreadChildren}</group-container>
71+
<text-block dangerouslySetInnerHTML={{
72+
__html: '<strong>bold</strong>'
73+
}}/>
74+
<SectionWithSpread {...{
75+
'data-testid': 'component-with-spread',
76+
role: 'region'
77+
}}/>
78+
</div>
79+
</>;
80+
}
81+
export { Root as default };

0 commit comments

Comments
 (0)