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
19 changes: 19 additions & 0 deletions .changeset/init-template-descriptions-match-emission.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
---
"@objectstack/cli": patch
---

fix(cli): `os init` template descriptions no longer advertise metadata kinds they never emit (#9737)

The `app` and `plugin` templates' `description` strings — shown by `printKV('Template', …)`
right after `os init -t <template>` runs, and mirrored in `content/docs/deployment/cli.mdx` —
claimed views, actions, and extensions that neither template's `srcFiles` map ever writes.
Both templates emit objects only (`src/objects/index.ts` + `src/objects/{namespace}_item.ts`).

- `app`: `'Full application with objects, views, and actions'` → `'Full application with objects'`
- `plugin`: `'Reusable plugin with objects and extensions'` → `'Reusable plugin with objects'`
- `content/docs/deployment/cli.mdx`'s template table drops the same false `views` claim from the
`app` row.

A new pin (`packages/cli/test/init.test.ts`) asserts every template's description only claims a
metadata kind (`objects`/`views`/`actions`/`extensions`) its `srcFiles` map actually has an entry
under, so a future template can't drift the same way.
2 changes: 1 addition & 1 deletion content/docs/deployment/cli.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -101,7 +101,7 @@ os init my-app --no-install # Skip dependency installation

| Template | What it creates |
|----------|-----------------|
| `app` | Full application with objects, views, barrel imports |
| `app` | Full application with objects, barrel imports |
| `plugin` | Reusable plugin package with objects |
| `empty` | Minimal project with just `objectstack.config.ts` |

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/init.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -102,7 +102,7 @@ export const TEMPLATES: Record<string, {
srcFiles: Record<string, (name: string, namespace: string) => string>;
}> = {
app: {
description: 'Full application with objects, views, and actions',
description: 'Full application with objects',
get dependencies() {
const v = pkgVersion();
// No driver is listed on purpose. `@objectstack/runtime` already depends
Expand DownExpand Up@@ -195,7 +195,7 @@ export default ${toCamelCase(namespace)}Item;
},

plugin: {
description: 'Reusable plugin with objects and extensions',
description: 'Reusable plugin with objects',
get dependencies() {
return {
'@objectstack/spec': pkgVersion(),
Expand Down
32 changes: 32 additions & 0 deletions packages/cli/test/init.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -52,6 +52,38 @@ describe('init command — published scaffold', () => {
});
});

describe('template description accuracy — matches what srcFiles emits (#9737)', () => {
// A template's `description` is what `printKV('Template', …)` and
// `content/docs/deployment/cli.mdx` show a user at the moment they pick
// `-t <template>` — so a description claiming a metadata kind the template
// never writes (the `app`/`plugin` "views"/"actions"/"extensions" claims
// this pin exists for) is a load-bearing lie, not cosmetic copy. Each kind
// maps to the `src/` subdirectory its files would live under; a claim with
// no matching `srcFiles` entry, in either direction, is the drift #9737
// found.
const KIND_CLAIMS: Array<{ kind: string; claimPattern: RegExp; dirPrefix: string }> = [
{ kind: 'objects', claimPattern: /\bobjects?\b/i, dirPrefix: 'src/objects/' },
{ kind: 'views', claimPattern: /\bviews?\b/i, dirPrefix: 'src/views/' },
{ kind: 'actions', claimPattern: /\bactions?\b/i, dirPrefix: 'src/actions/' },
{ kind: 'extensions', claimPattern: /\bextensions?\b/i, dirPrefix: 'src/extensions/' },
];

it.each(Object.keys(TEMPLATES))('template "%s" only claims metadata kinds it actually writes', (key) => {
const t = TEMPLATES[key];
const emittedPaths = Object.keys(t.srcFiles);
for (const { kind, claimPattern, dirPrefix } of KIND_CLAIMS) {
const claims = claimPattern.test(t.description);
const emits = emittedPaths.some((p) => p.startsWith(dirPrefix));
if (claims) {
expect(
emits,
`template "${key}" description claims "${kind}" but srcFiles has no ${dirPrefix}* entry (description: "${t.description}")`,
).toBe(true);
}
}
});
});

describe('native build allowlist (pnpm-workspace.yaml)', () => {
// pnpm 10+ blocks dependency build scripts by default. Without an allowlist,
// the scaffold installs but `serve` crashes with "Could not locate the
Expand Down
Loading