Summary
An app that has a root stx.config.ts loses any stx config key declared only in config/ui.ts. The root file replaces rather than merges, silently: no warning, and the key that went missing still reads as correct in config/ui.ts.
This cost one app its entire component library without any visible error. plugins was declared in config/ui.ts, the root stx.config.ts did not re-declare it, and so @stacksjs/components was simply never loaded on the serve and build paths.
Environment
@stacksjs/stx0.2.448 (also present on 0.2.401; the version is not the variable)@stacksjs/components0.2.448,bun-plugin-stx0.2.448- macOS, Bun 1.4.3-canary.1
Expected
Either the two sources merge, with the root file overriding only the keys it declares — which is what Partial<StxConfig> implies — or stx says plainly that a key present in one source is being discarded.
Actual
The root file wins wholesale. Keys only in config/ui.ts are dropped with no diagnostic.
Reproduction
config/ui.ts:
export default {
componentsDir: 'components',
plugins: ['./storage/framework/defaults/stx-components-plugin.ts'],
// …
}stx.config.ts at the app root:
import type { StxConfig } from '@stacksjs/stx'
import ui from './config/ui'
const config: Partial<StxConfig> = {
root: 'resources',
layoutsDir: 'views/layouts',
app: ui.app, // forwarded explicitly
// plugins: NOT forwarded
}
export default configThen render any tag from a plugin-provided library, e.g. <Breadcrumb :items="[{ label: 'Home', href: '/' }]" />.
Observed
buddy servenever logs[stx] Plugin loaded: …. An app without a rootstx.config.tslogs it on every boot.- A library-only tag fails to resolve at all:
[Error loading component: ENOENT: no such file or directory, open 'breadcrumb']
Searched paths:
- <app>/resources/views/components/breadcrumb.stx
- <app>/resources/views/components/Breadcrumb.stx
- <app>/node_modules/@stacksjs/stx/dist/components/Breadcrumb.stxThe plugin's component directory is absent from the searched paths.
A tag that also exists under the framework defaults does not error. It silently resolves to the default instead, which is the part that makes this expensive:
<Badge>kept rendering, just from a different component that ignoresclassName, so a set of app-level badge shortcuts became dead code and nobody noticed.Adding
plugins: ui.pluginstostx.config.tsfixes it: the plugin loads and<Breadcrumb>resolves from the library.
Why this is easy to miss
config/ui.ts keeps looking right. Nothing references the dropped key, nothing warns, and for any component name the framework also ships, the page still renders. The only outward sign is a tag that exists only in the plugin's library, and an app may not use one.
Suggested direction
Merge the sources with the root file taking key-level precedence, which matches the Partial<StxConfig> type. If replacement is the intended behaviour, a boot-time warning naming the dropped keys would have turned this into a one-minute fix.
Ruled out while diagnosing
Recorded so nobody re-walks them: the plugin's own precedence order (the default still won with the plugin's other entries removed entirely); an absolute path in the plugin's components array colliding with path.join against the plugin dir (real, but not the cause here); and componentsDir / root in the root config.
Noting one loose end honestly: with plugins forwarded and the library Badge resolving, that component then renders class="" in this app while the identical component with identical props renders correctly in a sibling app on the same version. That looks like a separate problem and I have not isolated it, so it is deliberately not part of this report.