ReviewOS

stacks/stx

bug(config): a root stx.config.ts silently replaces config/ui.ts, dropping keys it does not re-declare

#2056
Closed glennmichael123 opened this 9 days ago ·
9 days ago

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/stx 0.2.448 (also present on 0.2.401; the version is not the variable)
  • @stacksjs/components 0.2.448, bun-plugin-stx 0.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 config

Then render any tag from a plugin-provided library, e.g. <Breadcrumb :items="[{ label: 'Home', href: '/' }]" />.

Observed

  • buddy serve never logs [stx] Plugin loaded: …. An app without a root stx.config.ts logs 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.stx

The 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 ignores className, so a set of app-level badge shortcuts became dead code and nobody noticed.

  • Adding plugins: ui.plugins to stx.config.ts fixes 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.

9 days ago

Fixed in 7dda50e (fix(config): merge root and UI config files). When both sources exist, config/ui is the base and root stx.config takes key-level precedence, preserving UI-only plugins and settings. Added regression coverage in packages/stx/test/config-dir-resolution.test.ts; focused tests pass (5/5).

Sign in to comment on this issue.