← Corpus / flave-ai / issue
The lfm barrel imports node builtins, so it cannot be bundled for a browser
A consumer that only wants `parseMarkdown` still drags `node:crypto`, `node:fs` and `node:path` into the bundle, because the main entry re-exports OGCache. Same class of defect lfm already documented for plantuml — but in the front door.
- Path
- issues/LFM-Barrel-Imports-Node-Builtins.md
- Authors
- Michael Staton
- Augmented with
- Claude Code on Claude Opus 5 (1M context)
- Tags
- Issue · Lossless-Flavored-Markdown · Packaging · Browser-Build · Upstream
The lfm barrel imports node builtins
Symptom
Building @flave/editor (Vite 7, browser target) against @lossless-group/lfm@0.5.1 fails:
"createHash" is not exported by "__vite-browser-external", imported by
.../@lossless-group/lfm/dist/index.js
The editor imports exactly one thing from lfm — parseMarkdown.
Cause
src/index.ts:120 re-exports the OG cache from the package’s main entry:
export { OGCache, loadOGCache, hashUrl } from './utils/og-cache.js';
and src/utils/og-cache.ts:16-18 imports three node builtins at module scope:
import { createHash } from 'node:crypto';
import { promises as fs } from 'node:fs';
import { dirname } from 'node:path';
Because the imports are at module scope rather than behind a lazy call, any
bundler resolving the barrel must resolve them — whether or not the consumer
ever constructs an OGCache.
Why this is already a known shape
lfm’s own [[JSR-Export-Map-Omits-the-Formats-Subpaths]] records the exact reasoning for a sibling module:
plantumlis not re-exported here, because importing a node builtin into this barrel would make it unusable in a browser context. Import it by subpath:@lossless-group/lfm/formats/plantuml.
That discipline was applied to formats/plantuml and not to
utils/og-cache. The rule is right; its application is incomplete.
Blast radius
Every browser consumer of the barrel. Today that is flave’s editor. It would
equally hit any Astro island or client-side component importing parseMarkdown
directly — the astro-knots sites do their parsing at build time, in node, which
is why nobody has hit it yet. Same “why nobody has hit it” shape the sibling
issue documents.
Workaround in place (flave-side, temporary)
apps/editor/vite.config.ts aliases the three builtins to
src/node-builtin-stub.ts, whose exports throw with a named error if anything
ever actually calls them. The OG cache is only constructed when ogFetch is
enabled, which the editor never does — so the stubs are unreachable, and loud
rather than silent if that changes.
This is a consumer-side patch for a package-side problem. It should be deleted, not maintained.
Fix (upstream, lfm)
Move og-cache behind a subpath export, exactly as formats/plantuml was:
- drop the re-export from
src/index.ts - add
"./utils/og-cache"toexportsin bothpackage.jsonanddeno.json - note it in the README beside the existing plantuml caveat
Worth checking lfm-og-fetcher in the same pass — anything reaching the
network or the filesystem probably belongs on the same side of that line.
See also
- [[Phase-0-The-Live-Render-Loop]] — the phase that surfaced this
lfm/context-v/issues/JSR-Export-Map-Omits-the-Formats-Subpaths.md— the sibling issue whose reasoning this extends