Skip to content

Commit 511b658

Browse files
committed
feat: comptime evaluation
`comptime` statements/imports and `-` prefixed tags (`<-if>`, `<-for>`, `<-const>`, `<-log>`, `<-debug>`) evaluate while compiling and leave only their expanded output. Values reach runtime through a resume-style AST serializer; primitive reads fold into eagerly escaped static output. Design doc: comptime.md.
1 parent 0054d69 commit 511b658

178 files changed

Lines changed: 5305 additions & 6 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/comptime-evaluation.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@marko/runtime-tags": minor
3+
"@marko/compiler": minor
4+
---
5+
6+
Add compile time evaluation: `comptime` statements/imports and the `<-if>`, `<-for>`, `<-const>`, `<-log>`, and `<-debug>` tags run while compiling and leave only their expanded output. Values reach runtime through a resume-style AST serializer, and primitive reads fold into eagerly escaped static output. `<-define>`, `<-return>`, and userland comptime tags are reserved.

agent-feedback/bugs.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,19 @@ therefore relied upon for correctness. A real fix needs `isSupersetSources` to
4141
use a strict/proper-superset test (equal sources must not prune each other)
4242
_and_ the corrected arithmetic, then a full snapshot audit — out of scope for a
4343
one-line change.
44+
45+
## Concise `--` without following whitespace silently swallows one text character
46+
47+
`htmljs-parser@5.12.1 dist/index.mjs (BEGIN_DELIMITED_HTML_BLOCK)` | 2026-07-09 | impact:low | effort:low
48+
49+
Upstream htmljs-parser (exercised via `packages/compiler/src/babel-plugin/parser.js`):
50+
in concise mode, a text line whose `--` delimiter is not followed by whitespace
51+
drops the first content character with no error. Verified against the installed
52+
parser with a bare `createParser({ onText })`: `"--foo"` emits text `"oo"`,
53+
`"--foo bar"` emits `"oo bar"`, and `"---foo"` emits `"oo"`, while `"-- foo"`
54+
correctly emits `"foo"`. The delimiter scanner appears to unconditionally skip
55+
one character after the run of hyphens instead of requiring whitespace. Either
56+
the missing-whitespace form should be a parse error or the character should be
57+
kept; silent one-character data loss is the worst of the options. Fix belongs in
58+
marko-js/htmljs-parser (`BEGIN_DELIMITED_HTML_BLOCK` state) with regression
59+
tests for `--foo` / `---foo`.

agent-feedback/dx.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
Friction in builds, tests, tooling, or repo workflows. Format and rules: [README.md](README.md).
44

5+
## `stripTypes` silently unregisters scriptlet import bindings until the transform-exit crawl
6+
7+
`packages/compiler/src/babel-plugin/index.js:444` | 2026-07-09 | impact:med | effort:low
8+
9+
`stripTypes` temporarily hoists `ImportDeclaration`s out of static scriptlets so the TypeScript transform sees them, then puts them back with a raw `scriptlet.body.unshift(importNode)`. The hoist uses `path.remove()`, which unregisters the import's scope bindings (babel's `_removeFromScope`), and the raw re-insert never re-registers them — so from stripTypes until the crawl at transform **exit**, `scope.getBinding()` misses every import that lives inside a static/comptime scriptlet, while `const` bindings in the same scriptlet still resolve (they never went through the remove/re-add dance). Nothing in the tree looks wrong, which makes this a confusing trap for any pass that classifies identifiers during the transform stage (the comptime pass hit it and now re-crawls defensively at `packages/runtime-tags/src/translator/comptime/index.ts:118`). Fix by re-registering the binding when the import is moved back (e.g. `scriptletPath.scope.getProgramParent().registerDeclaration(...)` via a real path op instead of the raw unshift), then the defensive crawl can go.
10+
511
## Fix the broken default translator in `npm run compile`
612

713
`scripts/inspect-compiled-output.ts:22` | 2026-07-02 | impact:med | effort:low

agent-feedback/perf.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,18 @@ Existing TODO: `<${input.component}/>` style dynamic tags always compile against
4949
`scripts/test-parallel.js:40` | 2026-07-02 | impact:low | effort:med
5050

5151
`npm run test:parallel` runs one mocha process per core, but each fixture bundle already drives rolldown's own multi-threaded build (`packages/runtime-tags/src/__tests__/utils/bundle.ts`), so even a serial run uses ~1.4 cores. On a 4-core box the whole suite lands at ~87s vs ~238s serial (2.7×), short of the ~4× the core count implies, because the workers contend for the same native threads. `RAYON_NUM_THREADS=1` in the worker env made no measurable difference, so rolldown isn't honoring it. If rolldown (or its native binding) exposes a per-build thread cap, pinning workers to 1 bundler thread each — so N workers ≈ N threads total — could recover much of the lost efficiency and let the runner scale closer to linearly on higher core counts.
52+
53+
## Prune statically-confident `<if>` branches like `<show>` does
54+
55+
`packages/runtime-tags/src/translator/core/if.ts:583` | 2026-07-09 | impact:med | effort:med
56+
57+
`<show>` consults `evaluate()` and, when `.confident`, collapses to plain markup
58+
(`packages/runtime-tags/src/translator/core/show.ts:80`), but `core/if.ts` never
59+
calls the evaluator: `<if=false><p>never</p></if><else><p>always</p></else>`
60+
compiles (optimized html output) to a literal `if (false) {...} else {...}` with
61+
both branches' `_html` strings, scope ids, and analysis metadata emitted. A
62+
confident condition could instead select the surviving branch in `getBranches`
63+
(if.ts:583) before analysis, dropping the dead branch from both output targets.
64+
Care needed with branch chains (a confident-false head still leaves a live
65+
`else-if`/`else` chain) and with tag variables/attribute tags inside dropped
66+
branches.

0 commit comments

Comments
 (0)