Skip to content

Simplify Ono: remove bundler, fix JSX runtime, enable uno.config.js#21

Merged
hashrock merged 9 commits into
mainfrom
claude/design-simplification-plan-gi98o2
Jul 8, 2026
Merged

Simplify Ono: remove bundler, fix JSX runtime, enable uno.config.js#21
hashrock merged 9 commits into
mainfrom
claude/design-simplification-plan-gi98o2

Conversation

@hashrock

@hashrock hashrock commented Jul 8, 2026

Copy link
Copy Markdown
Owner

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

  • Removed custom bundler (src/bundler.js, src/resolver.js refactored): 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.
  • Unified JSX runtime: Consolidated three divergent implementations (runtime file, inlined string, and inline h function) into a single source. The real jsx-runtime.js is now injected via import statement into compiled pages, fixing the Fragment ReferenceError that broke <>...</> syntax in CLI builds.
  • Fixed uno.config.js loading: loadUnoConfig() now receives the correct config path from CLI commands instead of undefined, enabling user theme customization and shortcuts that were previously silently ignored.

Server & Live Reload

  • Replaced WebSocket with Server-Sent Events: Removed ws dependency and WebSocket server implementation. Live reload now uses native HTTP EventSource with a simple script injection, reducing complexity and eliminating a core dependency.
  • Removed h3 dependency: Replaced h3 framework with native Node.js http.createServer(), reducing core dependencies from 5 to 4 (typescript, @unocss/core, @unocss/preset-uno, reset).

Testing & Snapshots

  • Migrated to Node.js native snapshot testing: Replaced custom snapshot utilities with Node's built-in t.assert.snapshot(). Snapshot files now use standard .snapshot format instead of individual .snap files.
  • Removed snapshot test files: Consolidated renderer.snapshot.test.js, transformer.snapshot.test.js, unocss.snapshot.test.js, and cli.snapshot.test.js into inline snapshot tests within existing test files.

Cleanup

  • Removed ono-lp package: LLM-based landing page generator with heavy dependencies (ai, zod, dotenv, commander, node-html-markdown) is no longer part of the core distribution.
  • Removed example pages and components: Cleaned up packages/ono/pages/, packages/ono/example/, and packages/ono/public/ to reduce repository bloat.
  • Removed unused exports: Removed ./bundler export from package.json.

Implementation Details

  • Pages are compiled with transformJSX() and the JSX runtime import prepended, then evaluated in Node with proper module context
  • The resolver now only collects the local import graph; package imports and evaluation order are handled by Node's module resolution
  • Live reload script is injected into HTML responses via regex replacement of `

https://claude.ai/code/session_01L5ggPYmgrEz4Yd9V3D8fbK

claude added 9 commits July 8, 2026 10:26
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
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
@hashrock
hashrock merged commit 4d28fa9 into main Jul 8, 2026
5 checks passed
@hashrock
hashrock deleted the claude/design-simplification-plan-gi98o2 branch July 8, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants