Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/compose-extensions-vike-only-guard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@gemstack/framework': patch
---

Enforce the Vike-only constraint on `--compose-extensions`

`--compose-extensions` is documented as Vike-only, but `runFramework` applied the vike-* composer personas regardless of the detected preset, so a Next project would be framed with `nextPageBuilder` plus composers telling the agent to install vike-auth/vike-crud/etc.: an incoherent prompt. It now gates compose on the Vike preset and, on any other preset, falls back to the hand-rolled + Prisma path and emits a log explaining why.
22 changes: 22 additions & 0 deletions packages/framework/src/run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,28 @@ test('--compose-extensions frames the agent with vike-auth, not hand-rolled auth
assert.match(system(), /npm install vike-auth/)
})

test('--compose-extensions is ignored on a non-Vike preset and falls back with a log (#202)', async () => {
const events: FrameworkEvent[] = []
const { driver, system } = recordingDriver()
const { detection } = await runFramework({
intent: FAKE_INTENT,
driver,
cwd: '/tmp/ws',
signals: { dependencies: ['next'] }, // detect Next, not Vike
composeExtensions: true,
onEvent: e => events.push(e),
})

// The vike-* extensions are Vike-only, so a Next project does not get them.
assert.equal(detection.framework, 'Next.js')
assert.doesNotMatch(system(), /vike-auth/)
// And we say why, rather than silently framing Next with vike composers.
assert.ok(
events.some(e => e.kind === 'log' && /--compose-extensions ignored/.test(e.message)),
'expected a log explaining the compose fallback',
)
})

test('without --compose-extensions the default framing has no vike-auth (publish-safe)', async () => {
const { driver, system } = recordingDriver()
await runFramework({
Expand Down
18 changes: 14 additions & 4 deletions packages/framework/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,21 @@ export async function runFramework(opts: RunFrameworkOptions): Promise<RunFramew
}

// 1. Preset: detect the framework and turn its personas into prompt-framing.
// With --compose-extensions, swap the shared personas for the vike-extension
// set (compose vike-auth/vike-rbac/vike-crud/vike-themes + the universal-orm
// data layer instead of hand-rolling them); default keeps Prisma.
// --compose-extensions swaps the shared personas for the vike-extension set
// (compose vike-auth/vike-rbac/vike-crud/vike-themes + the universal-orm data
// layer instead of hand-rolling them); default keeps Prisma. The extensions
// are Vike-only and resolve only in the vike-data workspace, so guard it: on a
// non-Vike preset, fall back to the hand-rolled + Prisma path and say why
// rather than framing (e.g.) Next with vike composers.
const { preset, detection } = builtinPresetRegistry().select(opts.signals ?? {})
const personas = opts.composeExtensions
const composeExtensions = opts.composeExtensions === true && preset.name === 'vike'
if (opts.composeExtensions && !composeExtensions) {
emit({
kind: 'log',
message: `--compose-extensions ignored: the vike-* extensions are Vike-only, but the detected preset is "${preset.name}". Using the hand-rolled + Prisma path.`,
})
}
const personas = composeExtensions
? presetPersonas(preset, vikeExtensionPersonas)
: presetPersonas(preset)
const system = personas.map(personaInstructions).join('\n\n')
Expand Down
Loading