Simplify Ono: remove bundler, fix JSX runtime, enable uno.config.js#21
Merged
Conversation
Reviews the whole monorepo and lays out a phased simplification plan: unify the triplicated JSX runtime (fixes Fragment ReferenceError in CLI builds), fix uno.config.js never being loaded, preserve nested page paths, drop the Node-side bundler in favor of native module resolution, remove h3/ws dependencies, migrate to Node's built-in snapshot testing, and consolidate examples/scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK
Phase 1 of the simplification plan — three bugs fixed by removing
duplication:
- jsx-runtime.js is now self-contained and the single source of truth;
the builder injects its real source instead of the drifted
INLINE_JSX_RUNTIME copy (which lacked Fragment, so CLI builds of pages
using <>...</> crashed). barrels' third inline runtime copy is gone too.
- loadUnoConfig() now defaults to uno.config.js in the project root;
it previously threw on its undefined path argument and silently
returned {}, so user configs were never applied. The always-empty
unocssConfig plumbing through builder/watcher/commands is removed;
generateUnoCSS loads the config itself.
- buildFile() preserves directory structure relative to the input root:
pages/blog/first-post.jsx now outputs dist/blog/first-post.html
instead of flattening (and colliding) at dist/. This also fixes .tsx
pages being emitted as *.tsx.html.
The example's uno.config.js is now a plain object (no unocss import,
which wasn't resolvable from the example package anyway).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK
Phase 2 of the simplification plan: - Pages are now compiled file-by-file into a per-build temp directory (.ono/) that mirrors the project layout, relative .jsx/.tsx/.ts specifiers are rewritten to .js, and the entry is imported normally. Evaluation order, circular imports, and package imports are handled by Node itself instead of a hand-rolled bundler, and failed builds no longer leave _temp_*.js files inside dist/. - bundler.js is deleted; resolver.js shrinks to import scanning only (no topological sort, no import/export text surgery). - barrels' meta loading reuses the same importJSXModule() helper. - Dead code removed: transformJSXWithImports(), the unused plain debounce(), and the utils.js/utils.browser.js re-export split (flattenChildren lives in jsx-runtime.js, toCamelCase in barrels.js). - parseBuildArgs/parseDevArgs are unified into one parseCommandArgs() built on node:util's parseArgs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK
Phase 3 of the simplification plan: - The dev server is now plain node:http (~100 lines): static files, directory index support for the new nested output, and a simple path-traversal guard. - Live reload moves from WebSocket to Server-Sent Events on the same port. The server injects the EventSource snippet into served HTML — previously no client script was ever injected anywhere, so the ws broadcast on port 35729 had no listeners and live reload silently did nothing. It works end-to-end now, and the extra port is gone. - UnoCSS dependencies are narrowed to what is actually used: @unocss/core + @unocss/preset-uno + @unocss/reset instead of the unocss meta package (@unocss/core was previously an undeclared transitive dependency of the browser compiler). The reset CSS is resolved with createRequire instead of a ../node_modules path hack that failed under pnpm and silently produced an empty reset. - Runtime dependencies are now typescript + @unocss/* only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK
…hots Phase 4 of the simplification plan: - The 139-line hand-rolled snapshot-utils.js and its 37 .snap files are replaced by t.assert.snapshot (stable in Node 22), with snapshots stored next to each test file. Update via 'npm run test:update' (--test-update-snapshots) instead of an env var. SNAPSHOT_TESTING.md is no longer needed. - Snapshot cases are folded into the matching unit test files (transformer, renderer); cases that only restated an existing unit assertion are dropped. unocss and cli snapshot suites become unocss.test.js / cli.test.js. The cli suite also drops tests for the 'serve' and 'watch' commands, which no longer exist. - engines is raised to node >=22.3.0 (required for built-in snapshots; the test scripts already assumed a modern runner). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK
Two latent bugs surfaced by adding a real barrel consumer to the example:
- The resolver only scanned 'import ... from' statements, so the
're-export from' lines in generated barrels never pulled the post
files into the build.
- The generated barrel's 'posts' map referenced names bound by
'export { default as x } from ...', but re-exports do not create
local bindings in ES modules, so evaluating the barrel threw
ReferenceError. Barrels now import first and re-export the locals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK
Phase 5 of the simplification plan: - packages/ono-lp (LLM landing-page generator) is removed from the workspace. It shares no code path with the SSG core and carried the heaviest dependency set in the repo (three AI SDKs, commander, dotenv, zod). The code remains available in git history for extraction into its own repository. - Samples are consolidated into packages/example: the barrels demo moves there with a new pages/blog.jsx that renders posts through the generated barrel, and the duplicated packages/ono/example, packages/ono/pages, packages/ono/public trees (including committed build output) are deleted, along with the leftover root uno.config.js. - packages/ono/.npmignore is removed — the package.json 'files' whitelist already controls what gets published. - Docs updated: root README package table, ONO.md API example (the bundler subpath no longer exists). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK
Replaces the Node-native module resolution (and the REPL's regex-based module rewriting) with one browser-compatible mini bundler, per owner direction on PR #21. - src/parser.js: small parser-combinator core plus an ES module-syntax parser. Only import/export declarations (and top-level function names) are parsed; the surrounding code is skipped by a scanner that understands strings, template literals, comments, and regex literals, so 'import' inside a string or comment is never misparsed. No regular expressions over source code. - src/bundler.js: pure, host-agnostic bundle({entry, load, resolve}). Modules are wrapped in factories and linked with a tiny lazy require, which removes the need for topological sorting and gives CommonJS-like cycle tolerance (function exports are hoisted to survive cycles, like ESM). Package imports are either hoisted to the bundle top (Node) or rejected with a clear error (browser). - builder.js bundles to a single temp file under .ono/ and imports it; the temp-directory tree mirror and .jsx->.js specifier rewriting are gone. resolver.js is superseded and removed. - browser/compiler.js now delegates to the shared bundler, deleting its ~150 lines of regex import/export rewriting and manual topo sort. The render-candidate heuristic no longer needs to regex the entry source. - New test suites for the parser (string/comment/regex-literal traps, every import/export form) and the bundler (cycles, barrels, hoisting), plus browser-compiler tests that now run in Node since the module has no Node dependencies. Verified: 152 unit tests, example + barrels build, dev server SSE reload, and the built REPL compiling in headless Chromium. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements the full simplification plan for Ono SSG, addressing five critical bugs and removing unnecessary complexity from the codebase. The core changes eliminate the custom bundler in favor of Node's native module resolution, unify the fragmented JSX runtime implementations, fix silent configuration failures, and migrate to snapshot-based testing.
Key Changes
Architecture & Build System
src/bundler.js,src/resolver.jsrefactored): Pages are now compiled file-by-file into a per-build temp directory that mirrors project layout, then imported with Node's own module resolution. This eliminates 81 lines of bundling code and all associated complexity.hfunction) into a single source. The realjsx-runtime.jsis now injected via import statement into compiled pages, fixing theFragmentReferenceError that broke<>...</>syntax in CLI builds.uno.config.jsloading:loadUnoConfig()now receives the correct config path from CLI commands instead ofundefined, enabling user theme customization and shortcuts that were previously silently ignored.Server & Live Reload
wsdependency and WebSocket server implementation. Live reload now uses native HTTP EventSource with a simple script injection, reducing complexity and eliminating a core dependency.http.createServer(), reducing core dependencies from 5 to 4 (typescript, @unocss/core, @unocss/preset-uno, reset).Testing & Snapshots
t.assert.snapshot(). Snapshot files now use standard.snapshotformat instead of individual.snapfiles.renderer.snapshot.test.js,transformer.snapshot.test.js,unocss.snapshot.test.js, andcli.snapshot.test.jsinto inline snapshot tests within existing test files.Cleanup
ono-lppackage: LLM-based landing page generator with heavy dependencies (ai, zod, dotenv, commander, node-html-markdown) is no longer part of the core distribution.packages/ono/pages/,packages/ono/example/, andpackages/ono/public/to reduce repository bloat../bundlerexport from package.json.Implementation Details
transformJSX()and the JSX runtime import prepended, then evaluated in Node with proper module contexthttps://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK