Code-heavy fixture
Prose is interleaved with fenced code blocks and inline code. The walker's policy (per plan/01_renderer.md) is that text inside <code> and <pre> is not a mark target — keeps syntax-highlighter overlays clean. This fixture stresses that policy: the same word appears inside and outside code, and only the outside occurrence should get marked.
Closures and the event loop
A closure captures variables from its enclosing scope. Outside a closure, hoisting still applies: variable declarations using var are hoisted; declarations using let and const are subject to the TDZ.
function makeCounter() {
// closure example — the inner function captures `count`
let count = 0;
return () => ++count;
}
// the word closure appears in this comment too — but it's inside <code>
// so the walker should NOT mark it.
After the fence, the term closure in prose is a mark target again.
A Promise wraps an eventual value. Anything with a .then method is a thenable. Resolved Promises queue microtasks; setTimeout queues macrotasks; the event loop alternates between them.
// `Promise` inline — should NOT mark
const p = new Promise(resolve => resolve(1));
p.then(v => console.log(v)); // microtask runs after current task
setTimeout(() => {}, 0); // macrotask
Inline code in prose: the Promise constructor is on the platform. Inside the backticks, "Promise" should NOT be marked. In the surrounding prose, Promise should be.
Iterators and generators
A generator function returns an iterator. An async iterator returns Promises. WeakMap and WeakRef are GC-friendly references.
function* range(n) {
for (let i = 0; i < n; i++) yield i;
}
const it = range(3); // iterator inside the fence — no mark
it.next();
// async iterator and generator both appear here
async function* gen() {
for await (const chunk of stream) yield chunk;
}
Workers and shared memory
Web Workers are background threads. Service Workers proxy network. SharedArrayBuffer plus Atomics enables lock-free shared state. structured clone is the default cross-realm copy; transferable objects skip the clone.
const w = new Worker('worker.js');
const buf = new SharedArrayBuffer(1024);
w.postMessage(buf); // shared
// the words SharedArrayBuffer, Atomics, transferable inside <code> — no marks
Modules
ESM files use the .mjs extension or "type": "module". CJS uses require(). An import map lets a bare specifier resolve to a URL. dynamic import is a runtime expression. top-level await waits during module evaluation.
// import map example, inside <script type="importmap">
{
"imports": {
"lodash": "https://cdn.skypack.dev/lodash"
}
}
const m = await import('./lazy.js'); // dynamic import
// top-level await is fine in ESM
// CJS example
const x = require('./x');
module.exports = { x };
Bundling
tree-shaking removes unused exports. code-splitting emits lazy chunks. source map files map minified code to source via the sourceMappingURL comment:
//# sourceMappingURL=app.js.map
That trailing comment must NOT cause the walker to mark "sourceMappingURL" — it's inside a fenced code block. The same term sourceMappingURL appears once in this paragraph and should be marked there.
Late syntax highlighter caveat
Some hosts run Prism / Shiki / highlight.js *after* the page renders. The widget mounts on DOMContentLoaded and may have already injected marks before the highlighter walks the <pre> content. Since the walker skips <code> content, no marks landed inside fences in the first place — the highlighter is free to do its thing. This fixture is the proof.
If a host wraps inline <code> in additional spans during highlighting (e.g. <code><span class="token">closure</span></code>), the walker still sees a <code> ancestor and skips. No marks inside, regardless of how the host rewrites the inner DOM.