Skip to content

T-OBF9-ui-layer-prepare-for-consumers

State: closed/done · Impact: medium · Complexity: medium

packages/ts/ui/tsconfig.json extended ./.nuxt/tsconfig.json, a file nuxt prepare generates. A consuming app’s vitest transforms a file from the layer, walks up to the nearest tsconfig — that one — and dies resolving the extends, because on the consumer’s checkout the layer was never prepared. The layer’s own gate never noticed: ui:check ran nuxi typecheck, which regenerates .nuxt before invoking vue-tsc. A layer green while a consumer is red is how this reached main and stayed there.

The file a consumer resolves and the file the layer typechecks itself with had been the same file, serving two incompatible requirements. They are now two files.

LocationRole
packages/ts/ui/tsconfig.jsonConsumer-facing. Extends ../tsconfig/presets/vue.json by relative path. Resolves on a checkout where this package was never installed.
packages/ts/ui/tsconfig.nuxt.jsonThe layer’s own typecheck. Extends the generated ./.nuxt/tsconfig.json.
packages/ts/ui/package.jsontypecheck is nuxt prepare && vue-tsc --noEmit -p tsconfig.nuxt.json. vue-tsc was already a devDependency.
apps/agentpants/src-nuxt/tests/useDTheme.test.tsPasses. Had never run green in CI since it landed in #1508; the job died earlier on every run.

Three things were established by reproducing the failure locally — delete packages/ts/ui/.nuxt and, where noted, hide packages/ts/ui/node_modules, which is the state an agentpants runner is actually in.

The recorded cause was wrong. This task previously said CI installs with --ignore-scripts, so the layer’s postinstall: nuxt prepare never runs. agentpants-ci.yml runs bun install --frozen-lockfile with no --ignore-scripts. The postinstall is not what makes the difference.

Preparing the layer on a consumer’s runner is not available. nuxt prepare resolves @nuxt/kit relative to the rootDir it is preparing, so it needs packages/ts/ui/node_modules, which agentpants’ scoped install does not create:

ERROR Cannot resolve module "@nuxt/kit" (from: /packages/ts/ui/)

This is unchanged by using a pinned binary instead of bunx. The earlier attempt (381a91289, reverted by 9a648f2d2) was read as a bunx-pulls- nuxt@latest problem; that was real but secondary. Running apps/agentpants/src-nuxt/node_modules/.bin/nuxt prepare ../../../packages/ts/ui resolves the CLI correctly and still fails on kit. Closing this route means installing a full Nuxt dependency tree on every agentpants CI run.

The layer genuinely needs the generated config. Dropping the ./.nuxt/tsconfig.json extends outright fixes every consumer and produces 33 errors in the layer’s own source, mostly Nuxt auto-imports:

app/composables/useAppearance.ts: Cannot find name 'useState'
app/composables/useDTheme.ts: Cannot find name 'useColorMode'
nuxt.config.ts: Cannot find name 'defineNuxtConfig'

So the generated config has to stay for the layer, and cannot be on the path a consumer walks. Hence the split.

packages/ts/app-frame was checked as a precedent and rejected: it is the other Nuxt layer these apps consume and never trips this, because it ships neither a tsconfig nor a typecheck. Its own moon.yml documents that as a gap, not a pattern.

  • AC-1: agentpants:frontend-test passes with packages/ts/ui/.nuxt and packages/ts/ui/node_modules both absent — 6 test files, 35 tests, including useDTheme.test.ts.
  • AC-2: agentpants:frontend-typecheck passes in the same state.
  • AC-3: ui:check still passes, and still typechecks the layer against Nuxt’s auto-import types.
  • AC-4: sdf, zoo and pumice stay green and gain no new dependency — 6, 4 and 9 test files respectively, with the layer’s .nuxt absent.
  • The --ignore-scripts policy. It exists because the nuxt prepare postinstall fan-out OOMs the 8 GB runner (see .github/workflows/ci.yml).
  • Giving packages/ts/app-frame a typecheck of its own. Its moon.yml already records what that would take.
  • none

Surfaced on #1642, a Rust CI caching PR, which fixed the failures ahead of this one and let agentpants-ci reach agentpants:frontend-test for the first time.

The consumer-facing config uses a relative path rather than the @sksizer/tsconfig/vue specifier for the same reason #1650 moved the root config off a specifier: an extends specifier resolves from the directory of the file spelling it, so it lands in the repo-root node_modules, which a scoped install never creates.


← Back to Tasks