bundleFrameworkComposables (packages/stx/src/framework-composables.ts) globs *.ts
next to itself:
const dir = path.join(import.meta.dir, 'composables') // :97
const glob = new Bun.Glob('*.ts') // :109
files = files.filter(f => !f.endsWith('index.ts')).sort() // :116When stx runs from src/ that finds the composable sources. When it runs from dist/ —
which is every consumer, always — it finds only declaration files:
packages/stx/dist/composables:
.js 37
.d.ts 37
plain .ts (non-.d.ts) 0So every file the bundler processes is type-only and contributes no runtime code. Two things then compound:
The barrel exclusion misses.
'index.d.ts'.endsWith('index.ts')isfalse, soindex.d.ts— the one file the filter on :116 exists to skip — is the one that gets processed.The
exportstrip corrupts re-exports. :143 doescode.replace(/^export\s+(default\s+)?/gm, ''), which turnsexport { useStorage, type StorageType, } from './use-storage'into
{ useStorage, type StorageType, } from './use-storage'— not valid input, sotransformSyncthrows.
Net effect: nothing is ever emitted, so every requested name lands in missing and users
get the warning at :158 telling them their code will break:
[stx] framework composable index.d.ts could not be bundled: 32 | type StorageType,
error: Expected ";" but found "StorageType"
at input.ts:32:8
[stx] could not bundle notify from @stacksjs/stx/composables — calls to them will throw
ReferenceError in the browser.That is from a plain buddy build of a Stacks app against a normal built stx.
Why this matters
This looks like the mechanism behind #1806. Composables that live only in
@stacksjs/stx/composables and are not mirrored into the signals runtime can never be
bundled from a released stx — not because of anything about the composable, but because
the bundler is reading .d.ts files. Anything that does work today (useColorMode,
useDark, useLocalStorage, …) works because it is in the signals runtime on
window.stx, i.e. it never needed this path.
It is also invisible in the stx repo's own test runs, since running from src/ is the
one configuration where the glob finds real code.
Suggested fix
- Glob
*.jswhen running from a built layout (or detect: prefer*.tsexcluding*.d.ts, and fall back to*.js). Excluding.d.tsexplicitly is worth doing regardless — a declaration file can never contribute runtime code. - Make the barrel exclusion extension-agnostic (
path.basename(f).replace(/\.d\.ts$|\.[jt]s$/, '') !== 'index'). - Replace the regex
export-stripping withtranspiler.scan()+ a real transform, or at minimum skip files whose stripped form fails to parse before concluding the exports are missing — right now a parse failure and a genuinely absent export produce the same user-facing message. - A test that exercises this against
dist/rather thansrc/would have caught it; every current path runs from source.
Minor, related
The missing warning does not account for a name the calling file defines itself. In my
app resources/views/accounts.stx:223 has its own const notify = (message, tone, options) => …,
and stx still warned that notify could not be bundled and would ReferenceError. Local
definitions shadow the auto-import, so that instance is a false alarm — worth filtering
out, since a confident warning about working code costs trust (cf. #1815).