Skip to content

fix(cli): bundle inline HTML module scripts - #1412

Merged
Lorenzobattistela merged 5 commits into
bendlang:mainfrom
oxura:fix/1400-inline-page-modules
Oct 9, 2026
Merged

Lorenzobattistela merged 5 commits into
bendlang:mainfrom
oxura:fix/1400-inline-page-modules

Conversation

@oxura

@oxura oxura commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Bun previously left inline module bodies in bundled HTML without running them through Bend’s existing plugin/asset pipeline.

Change

Closes #1400. Reuses the existing PLUGIN pipeline for inline module scripts. Each inline body gets a stable per-page ordinal virtual path, avoiding UUID-dependent chunk names and old-chunk accumulation. Native HTMLRewriter text capture and inserted markers locate bodies in the actual original page; a whitespace prefix preserves page line/column positions, and diagnostics name the real HTML file plus the inline-module ordinal. No regex HTML parser or separate attribute lexer.

Whitespace/case around type=module is accepted; external scripts, classic scripts, import maps, and nomodule scripts remain outside this inline-module path. The requested GUIDE addition has been removed. The maintainer’s rebase at 3d684c0 is preserved, with no protected core, gates, or token caps changed. New bundler and regression lines respect the 80-column limit and use braced conditionals.

Before/after evidence

  • Before: three identical rebuilds accumulated three different chunks; a syntax error pointed to a nonexistent UUID virtual JS file at line 3. The final permanent regression also fails against main because the emitted inline module cannot resolve ./state.js.
  • After: three identical builds keep the same two files (chunk-9w7qq0dj.js and page.html). Syntax failure points to bad.html:7:16 and inline module 1, including comments, a quoted fake script tag, and a multiline opening tag in the page.
  • Actual Chromium load renders classic|1|2|42 with no recorded page errors. This exercised the bundled inline modules and a default-imported Bend module, not only generated markup.
  • Permanent IO regression: interpreted and emitted-JavaScript executions both print 0. It checks stable filenames/bytes, executed module results, trimmed module type, and real-page error location. Child operations share a 3500ms deadline; the emitted run also passed an external 5s timeout. Combined run: 1.41s.
  • Local repository gate: PASS 54/54.

Limits

Bun 1.3.13 on Linux x86_64. Mini-cluster test/perf/ping was unavailable and is not claimed. A whole-tree TypeScript smoke reports 12 diagnostics outside main.ts, so I do not claim a passing full-tree typecheck. New-head hosted CI is not claimed until it completes.

Release-note policy

Removed my unreleased changelog entry following maintainer review. CHANGELOG.md is now unchanged from main; implementation and previously exercised verification are unchanged.

@Lorenzobattistela Lorenzobattistela self-assigned this Oct 8, 2026
@Lorenzobattistela
Lorenzobattistela force-pushed the fix/1400-inline-page-modules branch from 3367080 to 3d684c0 Compare October 8, 2026 18:33

@Lorenzobattistela Lorenzobattistela left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @oxura. I rebased this onto main as one commit (3d684c0). The #1406 cherry-pick is already on main, so I dropped it.

The fix is right. On main, bend page.html -o out exits 0 and writes an empty chunk for every page with an inline module script, valid pages included, on both Bun 1.4.0 and 1.3.13. With this PR, valid pages bundle and run. A missing or failing .bend import, or a syntax error, now exits 1. Two inline scripts that import the same module share one instance, as they do in the browser. Classic, nomodule, importmap and src scripts are left alone, and no files are left on disk. Gates on main + PR: repo 54/54, ping 48/48, test 1595/1595, perf same as main.

What needs changing:

  1. The random UUID in the virtual file name makes the output nondeterministic. Building the same page twice gives the same JS under a different chunk name (chunk-a71fc3n3.js, then chunk-x98p5hkb.js), and page.html changes with it. Rebuilding into the same -o dir leaves the old chunk behind each time: three builds gave three chunks. Pages with only src scripts get a stable name. The virtual files live in their own namespace and are only matched through the map, so the UUID isn't needed to avoid clashes.

  2. Errors point at a file that doesn't exist, /abs/path/.page.html.bend-inline-<uuid>-0.js:3:7. The line is counted from the start of the script, not from page.html. It would help if the user could tell which script and which page line.

  3. Please drop the guide/GUIDE.md line. The guide is written by hand by the maintainer.

  4. Style, following bend.ts and main.ts: one new line is 116 columns, and the two ifs without braces should have them. main.ts has no braceless ifs.

Minor: type=" module " (with spaces) is treated as a module by browsers but is skipped here. Also, there is no test that fails on main and passes here.

@oxura

oxura commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @Lorenzobattistela for the rebase and real browser/bundler checks. I’ll preserve 3d684c0, remove the UUID from the virtual IDs, make errors identify the page and inline-script location, remove the guide change, and handle the whitespace/style points. I’ll verify repeated builds, shared module identity, error exits and a failing-before/passing-after regression without changing protected gates without approval.

@oxura

oxura commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review in 29e26e7, with source/fixture file-mode preservation in 49a1ad3; your rebased parent 3d684c0 is intact. UUID names are replaced with per-page ordinals, and native HTMLRewriter capture supplies real-page locations without an HTML regex/attribute lexer. Errors now name bad.html:7:16 and the module ordinal. The GUIDE addition is removed; new code is braced and within 80 columns; spaced/case-insensitive module type is handled. The permanent IO regression fails on main (unresolved ./state.js) and passes in both interpreted and emitted-JS modes here, within the external 5s limit. Actual Chromium displays classic|1|2|42 without recorded page errors; three builds retain identical output filenames and bytes. Local gate passed 54/54. The PR body now distinguishes that evidence from unavailable mini-cluster checks and the unrelated whole-tree TypeScript diagnostics.

@Lorenzobattistela Lorenzobattistela left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @oxura, the earlier points are all addressed. I retested 49a1ad3 on main 60fa05d and the gates pass.

One change before merge: please remove the "## Unreleased" entry from CHANGELOG.md. CHANGELOG.md only changes in release commits, where the maintainers write the notes for each release.

@oxura

oxura commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the retest. I will remove the unreleased changelog entry and leave release notes to the maintainers; the CLI fix will remain unchanged. I am applying the same policy to my other open Bend PRs.

@oxura

oxura commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Removed the Unreleased entry in ee0932c. CHANGELOG.md is now unchanged from main; the CLI implementation and permanent regression are untouched. I also removed my unreleased notes from #1434, #1438 and #1441 and updated their descriptions to follow the same release-only policy. No runtime retest is claimed for these documentation-only commits. Thanks for the retest.

@Lorenzobattistela
Lorenzobattistela merged commit 8578b7c into bendlang:main Oct 9, 2026
1 check passed
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.

bend <page>.html -o <dir> exits 0 with a 0-byte JS chunk when the page's .bend import is missing or check-failing (the loader never runs)

2 participants