Skip to content

Commit 0db23a4

Browse files
authored
Merge pull request #3 from RandomCodeSpace/feat/drop-babel-standalone
Drop Babel-standalone from component pages (~600 KB per load)
2 parents d089049 + ff5ebd7 commit 0db23a4

1 file changed

Lines changed: 53 additions & 23 deletions

File tree

scripts/build-docs.mjs

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
* <outDir>/dist/styles.css (concat colors_and_type.css + src/styles.css)
1212
* <outDir>/docs/index.html (component reference, card grid)
1313
* <outDir>/docs/docs.css (per-component page styles)
14-
* <outDir>/docs/runner.js (Babel + render helper)
15-
* <outDir>/docs/playground/index.html (editable playground)
14+
* <outDir>/docs/runner.js (render helper — no in-browser compiler)
15+
* <outDir>/docs/playground/index.html (editable playground; uses Babel)
1616
* <outDir>/docs/<Name>/index.html (one per runtime export)
1717
*
1818
* Source of truth (parsed from the repo):
@@ -22,12 +22,16 @@
2222
* src/charts/index.ts (charts subpath exports — "Charts" category)
2323
*
2424
* Every component page renders the live React component via the IIFE
25-
* bundle plus Babel-standalone. There are no hand-written HTML cards.
25+
* bundle. JSX in @example blocks is pre-transformed to JS at build
26+
* time via esbuild, so component pages do not load Babel-standalone in
27+
* the browser. The editable playground is the one exception — it keeps
28+
* Babel-standalone since users edit JSX live there.
2629
*
27-
* Pure Node built-ins.
30+
* Pure Node built-ins + esbuild (already a dev-dep for the IIFE bundle).
2831
*/
2932

3033
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
34+
import { transformSync } from "esbuild";
3135
import { join, resolve } from "node:path";
3236
import { generateDemos } from "./component-examples.mjs";
3337
import { extractInterfaces, extractTypeAliases, parseRuntimeExports, parseChartsExports } from "./parse-source.mjs";
@@ -629,9 +633,34 @@ html { scroll-behavior: smooth; }
629633
`;
630634
}
631635

636+
// Pre-transform a JSX expression string into a JS expression at build
637+
// time. The runner splices this into "return (...)" inside a factory
638+
// scoped to `var { React, Button, ... } = R`, so the transformed output
639+
// references React.createElement / React.Fragment, both of which the
640+
// bundle exports. No Babel-standalone needed in the browser.
641+
function compileDemo(jsxCode) {
642+
if (!jsxCode) return "";
643+
// esbuild requires statement-shape input — wrap as `(EXPR)` so the
644+
// single JSX expression survives as a single expression on output.
645+
const result = transformSync(`(${jsxCode})`, {
646+
loader: "tsx",
647+
jsx: "transform",
648+
jsxFactory: "React.createElement",
649+
jsxFragment: "React.Fragment",
650+
target: "es2020",
651+
minify: false,
652+
});
653+
// esbuild emits the expression as a statement: "(...);\n". Strip the
654+
// trailing punctuation so the result drops cleanly into "return ( … )".
655+
return result.code.replace(/;\s*$/, "").trimEnd();
656+
}
657+
632658
function renderRunner() {
633-
return `// Shared runtime for component live previews, the playground, and the
634-
// charts subpath. Loaded after the IIFE bundle(s) and Babel-standalone.
659+
return `// Shared runtime for component live previews and chart docs pages.
660+
// Loaded after the IIFE bundle(s). Demo code is pre-transformed to JS
661+
// at build time, so this runner only needs to eval and render — there
662+
// is no JSX compiler in the page. (The editable playground keeps
663+
// Babel-standalone because it transforms whatever the user types.)
635664
//
636665
// runExample(code, targetId) — core components, uses window.RCS
637666
// runChartExample(code, targetId) — chart components, uses window.RCSCharts
@@ -642,7 +671,7 @@ function renderRunner() {
642671
function showError(target, msg) {
643672
target.innerHTML = '<div class="err">' + escapeHtml(msg) + '</div>';
644673
}
645-
function transformExample(R, code) {
674+
function buildFactorySrc(R, code) {
646675
var names = Object.keys(R || {}).filter(function (n) { return n !== "default"; });
647676
return (
648677
"(function() { var R = arguments[0]; " +
@@ -655,12 +684,9 @@ function renderRunner() {
655684
var target = document.getElementById(targetId || "preview");
656685
if (!target) return;
657686
if (!global.RCS) return showError(target, "window.RCS not loaded");
658-
if (!global.Babel) return showError(target, "Babel standalone not loaded");
659-
var wrapped = transformExample(global.RCS, code);
660-
var transformed, factory, result;
661-
try { transformed = global.Babel.transform(wrapped, { presets: ["env", "react"] }).code; }
662-
catch (e) { return showError(target, "Compile error: " + (e && e.message || e)); }
663-
try { factory = (0, eval)(transformed); result = factory(global.RCS); }
687+
var src = buildFactorySrc(global.RCS, code);
688+
var factory, result;
689+
try { factory = (0, eval)(src); result = factory(global.RCS); }
664690
catch (e) { return showError(target, "Runtime error: " + (e && e.message || e)); }
665691
try {
666692
var R = global.RCS;
@@ -675,12 +701,9 @@ function renderRunner() {
675701
var target = document.getElementById(targetId || "preview");
676702
if (!target) return;
677703
if (!global.RCSCharts) return showError(target, "window.RCSCharts not loaded");
678-
if (!global.Babel) return showError(target, "Babel standalone not loaded");
679-
var wrapped = transformExample(global.RCSCharts, code);
680-
var transformed, factory, result;
681-
try { transformed = global.Babel.transform(wrapped, { presets: ["env", "react"] }).code; }
682-
catch (e) { return showError(target, "Compile error: " + (e && e.message || e)); }
683-
try { factory = (0, eval)(transformed); result = factory(global.RCSCharts); }
704+
var src = buildFactorySrc(global.RCSCharts, code);
705+
var factory, result;
706+
try { factory = (0, eval)(src); result = factory(global.RCSCharts); }
684707
catch (e) { return showError(target, "Runtime error: " + (e && e.message || e)); }
685708
try {
686709
var R = global.RCSCharts;
@@ -1072,14 +1095,20 @@ function renderComponentPage(name) {
10721095
// The runner functions themselves verify their bundle is loaded (window.RCS
10731096
// for runExample, window.RCSCharts for runChartExample) and surface the
10741097
// failure inline, so we only need to guard against runner.js itself failing.
1098+
// JSX is pre-transformed at build time via compileDemo() — the runtime
1099+
// string passed to runFn is plain JS (References React.createElement /
1100+
// React.Fragment, both destructured from R inside the runner factory).
1101+
// The displayed code panel and copy-button still receive the original JSX
1102+
// via DEMO_CODES below.
10751103
const runFn = chart ? "runChartExample" : "runExample";
1076-
const runCalls = demos.map((d, i) => `${runFn}(${JSON.stringify(d.code)}, "demo-render-${i}");`).join("\n ");
1104+
const compiledDemos = demos.map((d) => compileDemo(d.code));
1105+
const runCalls = compiledDemos.map((js, i) => `${runFn}(${JSON.stringify(js)}, "demo-render-${i}");`).join("\n ");
10771106
const codeJson = JSON.stringify(demos.map((d) => d.code));
1107+
const bundleGlobal = chart ? "window.RCSCharts" : "window.RCS";
10781108

10791109
const tail = `
10801110
<script src="../bundle/rcs.iife.js"></script>${chart ? `
10811111
<script src="../bundle/rcs-charts.iife.js"></script>` : ""}
1082-
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js"></script>
10831112
<script src="../runner.js"></script>
10841113
<script>
10851114
(function () {
@@ -1149,8 +1178,9 @@ function renderComponentPage(name) {
11491178
li.style.display = t.indexOf(q) >= 0 ? '' : 'none';
11501179
});
11511180
});
1152-
// Run demos as soon as bundle + babel are ready.
1153-
if (window.RCS && window.Babel) runAll();
1181+
// Run demos as soon as the IIFE bundle for this page has loaded.
1182+
// No JSX compiler needed in the browser — demos are pre-transformed.
1183+
if (${bundleGlobal}) runAll();
11541184
else window.addEventListener('load', runAll);
11551185
})();
11561186
</script>`;

0 commit comments

Comments
 (0)