@metaobjectsdev/codegen-ts-tanstack 0.21.3 → 0.21.4-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -3
- package/dist/data-grid-gate.d.ts +32 -0
- package/dist/data-grid-gate.d.ts.map +1 -0
- package/dist/data-grid-gate.js +45 -0
- package/dist/data-grid-gate.js.map +1 -0
- package/dist/tanstack-grid-hook.d.ts.map +1 -1
- package/dist/tanstack-grid-hook.js +34 -22
- package/dist/tanstack-grid-hook.js.map +1 -1
- package/dist/tanstack-grid.d.ts.map +1 -1
- package/dist/tanstack-grid.js +28 -26
- package/dist/tanstack-grid.js.map +1 -1
- package/package.json +4 -4
- package/src/data-grid-gate.ts +54 -0
- package/src/tanstack-grid-hook.ts +39 -28
- package/src/tanstack-grid.ts +29 -28
package/README.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# @metaobjectsdev/codegen-ts-tanstack
|
|
2
2
|
|
|
3
|
-
TanStack codegen for MetaObjects. Provides `tanstackQuery()` (per-entity `<Entity>.hooks.ts` — 5 React Query hooks, plus a `use<Source><Relation>(sourceId, opts?)` collection hook per many-to-many relationship), `tanstackGrid()` (`<Entity>.columns.tsx` for `@tanstack/react-table`), and `tanstackGridHook()
|
|
3
|
+
TanStack codegen for MetaObjects. Provides `tanstackQuery()` (per-entity `<Entity>.hooks.ts` — 5 React Query hooks, plus a `use<Source><Relation>(sourceId, opts?)` collection hook per many-to-many relationship), `tanstackGrid()` (`<Entity>.columns.tsx` for `@tanstack/react-table`), and `tanstackGridHook()` (`<Entity>.grid.ts` — the controlled grid state + query).
|
|
4
|
+
|
|
5
|
+
### Grids are opt-in per entity
|
|
6
|
+
|
|
7
|
+
`tanstackQuery()` emits hooks for **every** entity. `tanstackGrid()` and `tanstackGridHook()` emit **only for an entity that declares a `layout.dataGrid` child** — declaring one is how you say "this entity is displayed in a grid". If you wire the grid generators and get no `.columns.tsx`/`.grid.ts`, that is the reason, and `meta gen` says so in its warnings. Opt an entity in with:
|
|
8
|
+
|
|
9
|
+
```jsonc
|
|
10
|
+
{ "layout.dataGrid": { "name": "default", "@columns": ["name", "email"] } }
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`@columns` is an ordered list; omit it to get every field. Each `layout.dataGrid` yields a `<entity><Grid>Columns` + `<entity><Grid>Grid` pair from `tanstackGrid()` and a `use<Entity><Grid>Grid()` from `tanstackGridHook()`.
|
|
4
14
|
|
|
5
15
|
### M:N collection hooks (FR-018)
|
|
6
16
|
|
|
@@ -18,13 +28,28 @@ In your `metaobjects.config.ts`:
|
|
|
18
28
|
|
|
19
29
|
```ts
|
|
20
30
|
import { defineConfig } from "@metaobjectsdev/cli";
|
|
21
|
-
import { tanstackQuery, tanstackGrid } from "@metaobjectsdev/codegen-ts-tanstack";
|
|
31
|
+
import { tanstackQuery, tanstackGrid, tanstackGridHook } from "@metaobjectsdev/codegen-ts-tanstack";
|
|
22
32
|
|
|
23
33
|
export default defineConfig({
|
|
24
|
-
generators: [tanstackQuery(), tanstackGrid()],
|
|
34
|
+
generators: [tanstackQuery(), tanstackGrid(), tanstackGridHook()],
|
|
25
35
|
});
|
|
26
36
|
```
|
|
27
37
|
|
|
38
|
+
Then render. `<EntityGrid>` is fully controlled — beyond the columns it needs
|
|
39
|
+
`rowCount`, a `state` object and three `onChange` callbacks — so pair it with the
|
|
40
|
+
generated grid hook, which returns exactly that prop shape:
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { EntityGrid } from "@metaobjectsdev/tanstack";
|
|
44
|
+
import { authorDefaultColumns, authorDefaultGrid } from "./generated/Author.columns";
|
|
45
|
+
import { useAuthorDefaultGrid } from "./generated/Author.grid";
|
|
46
|
+
|
|
47
|
+
export function AuthorList() {
|
|
48
|
+
const grid = useAuthorDefaultGrid(); // owns sorting/pagination/filters + the query
|
|
49
|
+
return <EntityGrid {...grid} columns={authorDefaultColumns} grid={authorDefaultGrid} />;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
28
53
|
`tanstackQuery`/`tanstackGrid`/`tanstackGridHook` each accept `{ target }` to route
|
|
29
54
|
their output (hooks/columns/grids) to a named target such as the browser app — see
|
|
30
55
|
`@metaobjectsdev/cli` README, "Multiple output targets". The generated files import
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
2
|
+
import type { GenContext } from "@metaobjectsdev/codegen-ts";
|
|
3
|
+
/** True when the entity declares (or inherits) at least one `layout.dataGrid`. */
|
|
4
|
+
export declare function hasDataGridLayout(entity: MetaObject): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Grid artifacts are opt-IN per entity: an entity produces one only when it declares
|
|
7
|
+
* a `layout.dataGrid` child. That is intended — but nothing used to say so. An
|
|
8
|
+
* adopter who wired `tanstackQuery() + tanstackGrid()` got `.hooks.ts` for every
|
|
9
|
+
* entity and `.columns.tsx` for none, and reasonably concluded codegen was broken
|
|
10
|
+
* (#287). The package description promising "hooks and column definitions"
|
|
11
|
+
* unconditionally made it worse.
|
|
12
|
+
*
|
|
13
|
+
* So the run itself now says it, following the repo's tell-at-`meta gen`-time
|
|
14
|
+
* precedent (#226 / #258) rather than a doc line that gets missed the same way this
|
|
15
|
+
* one was. Warning only: the exit code is untouched, and `--dry-run` reports it too,
|
|
16
|
+
* since generators run before the write phase branches.
|
|
17
|
+
*
|
|
18
|
+
* It fires ONLY when the generator emitted **nothing at all** — which is the reported
|
|
19
|
+
* condition ("no columns file for ANY entity") and makes the note self-extinguishing:
|
|
20
|
+
* declare one `layout.dataGrid` anywhere in the model and it goes quiet forever. A
|
|
21
|
+
* 50-entity model with 3 grids therefore stays silent, because that author plainly
|
|
22
|
+
* knows how grids work and a permanent 47-name nag is exactly the cry-wolf failure
|
|
23
|
+
* that got the old `timestampMode` warning deleted from `runner.ts` rather than left
|
|
24
|
+
* to fire forever.
|
|
25
|
+
*
|
|
26
|
+
* `passesOtherGates` is the generator's own filter MINUS the layout check, so the
|
|
27
|
+
* named set is exactly "objects the layout gate — and nothing else — held back".
|
|
28
|
+
* `object.value`s are excluded from it: a value object is a payload/DTO shape, never
|
|
29
|
+
* a grid candidate, so naming one would be advice worth ignoring.
|
|
30
|
+
*/
|
|
31
|
+
export declare function warnMissingDataGridLayout(ctx: GenContext, passesOtherGates: (entity: MetaObject) => boolean, artifact: string): void;
|
|
32
|
+
//# sourceMappingURL=data-grid-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-grid-gate.d.ts","sourceRoot":"","sources":["../src/data-grid-gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAG7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,UAAU,EACf,gBAAgB,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,EACjD,QAAQ,EAAE,MAAM,GACf,IAAI,CAaN"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { LAYOUT_SUBTYPE_DATA_GRID, OBJECT_SUBTYPE_VALUE } from "@metaobjectsdev/metadata";
|
|
2
|
+
/** True when the entity declares (or inherits) at least one `layout.dataGrid`. */
|
|
3
|
+
export function hasDataGridLayout(entity) {
|
|
4
|
+
// layouts() is effective — own + inherited layouts (from extends:/super:).
|
|
5
|
+
return entity.layouts().some((l) => l.subType === LAYOUT_SUBTYPE_DATA_GRID);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Grid artifacts are opt-IN per entity: an entity produces one only when it declares
|
|
9
|
+
* a `layout.dataGrid` child. That is intended — but nothing used to say so. An
|
|
10
|
+
* adopter who wired `tanstackQuery() + tanstackGrid()` got `.hooks.ts` for every
|
|
11
|
+
* entity and `.columns.tsx` for none, and reasonably concluded codegen was broken
|
|
12
|
+
* (#287). The package description promising "hooks and column definitions"
|
|
13
|
+
* unconditionally made it worse.
|
|
14
|
+
*
|
|
15
|
+
* So the run itself now says it, following the repo's tell-at-`meta gen`-time
|
|
16
|
+
* precedent (#226 / #258) rather than a doc line that gets missed the same way this
|
|
17
|
+
* one was. Warning only: the exit code is untouched, and `--dry-run` reports it too,
|
|
18
|
+
* since generators run before the write phase branches.
|
|
19
|
+
*
|
|
20
|
+
* It fires ONLY when the generator emitted **nothing at all** — which is the reported
|
|
21
|
+
* condition ("no columns file for ANY entity") and makes the note self-extinguishing:
|
|
22
|
+
* declare one `layout.dataGrid` anywhere in the model and it goes quiet forever. A
|
|
23
|
+
* 50-entity model with 3 grids therefore stays silent, because that author plainly
|
|
24
|
+
* knows how grids work and a permanent 47-name nag is exactly the cry-wolf failure
|
|
25
|
+
* that got the old `timestampMode` warning deleted from `runner.ts` rather than left
|
|
26
|
+
* to fire forever.
|
|
27
|
+
*
|
|
28
|
+
* `passesOtherGates` is the generator's own filter MINUS the layout check, so the
|
|
29
|
+
* named set is exactly "objects the layout gate — and nothing else — held back".
|
|
30
|
+
* `object.value`s are excluded from it: a value object is a payload/DTO shape, never
|
|
31
|
+
* a grid candidate, so naming one would be advice worth ignoring.
|
|
32
|
+
*/
|
|
33
|
+
export function warnMissingDataGridLayout(ctx, passesOtherGates, artifact) {
|
|
34
|
+
const candidates = ctx.entities.filter((e) => e.subType !== OBJECT_SUBTYPE_VALUE && passesOtherGates(e));
|
|
35
|
+
if (candidates.length === 0)
|
|
36
|
+
return;
|
|
37
|
+
if (candidates.some(hasDataGridLayout))
|
|
38
|
+
return; // the model uses grids — say nothing
|
|
39
|
+
ctx.warn(`no ${artifact} emitted for ${candidates.map((e) => e.name).join(", ")} — grid ` +
|
|
40
|
+
`artifacts are opt-in per entity via a \`layout.dataGrid\` child, and no object in ` +
|
|
41
|
+
`this model declares one. Add e.g. ` +
|
|
42
|
+
`{"layout.dataGrid": {"name": "default", "@columns": ["id", "name"]}} to an entity ` +
|
|
43
|
+
`to emit its grid.`);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=data-grid-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-grid-gate.js","sourceRoot":"","sources":["../src/data-grid-gate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAG1F,kFAAkF;AAClF,MAAM,UAAU,iBAAiB,CAAC,MAAkB;IAClD,2EAA2E;IAC3E,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,wBAAwB,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,yBAAyB,CACvC,GAAe,EACf,gBAAiD,EACjD,QAAgB;IAEhB,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CACpC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,oBAAoB,IAAI,gBAAgB,CAAC,CAAC,CAAC,CACjE,CAAC;IACF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACpC,IAAI,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC;QAAE,OAAO,CAAG,qCAAqC;IACvF,GAAG,CAAC,IAAI,CACN,MAAM,QAAQ,gBAAgB,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU;QAC9E,oFAAoF;QACpF,oCAAoC;QACpC,oFAAoF;QACpF,mBAAmB,CACtB,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tanstack-grid-hook.d.ts","sourceRoot":"","sources":["../src/tanstack-grid-hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"tanstack-grid-hook.d.ts","sourceRoot":"","sources":["../src/tanstack-grid-hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAA6B,KAAK,gBAAgB,EAAwH,MAAM,4BAA4B,CAAC;AAIpN,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EA8CxB,gBAAgB,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC"}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { perEntity, formatTs, entityOutputPath, emitsInstanceArtifacts, isTphSubtype, CODEGEN_ATTR_EMIT_TANSTACK, CODEGEN_ATTR_EMIT_GRID } from "@metaobjectsdev/codegen-ts";
|
|
2
|
+
import { hasDataGridLayout, warnMissingDataGridLayout } from "./data-grid-gate.js";
|
|
3
3
|
import { renderGridHookFile } from "./templates/grid-hook-file.js";
|
|
4
|
-
function hasDataGridLayout(entity) {
|
|
5
|
-
// layouts() is effective — own + inherited layouts (from extends:/super:).
|
|
6
|
-
return entity.layouts().some((l) => l.subType === LAYOUT_SUBTYPE_DATA_GRID);
|
|
7
|
-
}
|
|
8
4
|
/**
|
|
9
5
|
* Per-entity generator that emits <Entity>.grid.ts — one
|
|
10
6
|
* use<Entity><Grid>Grid() hook per layout[dataGrid] declared on the entity.
|
|
@@ -14,24 +10,40 @@ function hasDataGridLayout(entity) {
|
|
|
14
10
|
*/
|
|
15
11
|
export const tanstackGridHook = function tanstackGridHook(opts) {
|
|
16
12
|
const userFilter = opts?.filter ?? (() => true);
|
|
13
|
+
// Every gate EXCEPT the dataGrid-layout opt-in: the framework instance-artifact
|
|
14
|
+
// guard (skips abstract types), the metadata opt-out, and the user filter. Split
|
|
15
|
+
// out so the discoverability note can name exactly the entities the LAYOUT gate
|
|
16
|
+
// alone held back (#287).
|
|
17
|
+
//
|
|
18
|
+
// The TPH clause must MATCH tanstackGrid's exactly. A TPH subtype inherits its base's
|
|
19
|
+
// dataGrid layout via extends, so `hasDataGridLayout` is true for it — but tanstackGrid
|
|
20
|
+
// deliberately emits no per-subtype columns without an own `@emitGrid: true` (the base's
|
|
21
|
+
// polymorphic grid is the single source of truth). Without the same clause here, a TPH
|
|
22
|
+
// subtype got a `<Sub>.grid.ts` whose sibling `<Sub>.columns.tsx` is never emitted:
|
|
23
|
+
// a dangling `use<Sub>DefaultGrid()` with nothing to pair it with, and an outright
|
|
24
|
+
// TS2307 when the inherited layout carries an `@filter` preset (the hook then imports
|
|
25
|
+
// `<sub>DefaultFilter` from the missing columns module).
|
|
26
|
+
const passesOtherGates = (e) => emitsInstanceArtifacts(e)
|
|
27
|
+
// ADR-0039: resolving — a concrete entity may inherit its @emit* opt-out flag via extends.
|
|
28
|
+
&& e.attr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
|
|
29
|
+
&& userFilter(e)
|
|
30
|
+
&& (!isTphSubtype(e) || e.attr(CODEGEN_ATTR_EMIT_GRID) === true);
|
|
31
|
+
const emit = perEntity(async (entity, ctx) => {
|
|
32
|
+
if (!ctx.renderContext) {
|
|
33
|
+
throw new Error("tanstack-grid-hook: renderContext is required (provided by runGen)");
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
path: entityOutputPath(ctx.renderContext.outputLayout, entity.package, `${entity.name}.grid.ts`),
|
|
37
|
+
content: await formatTs(renderGridHookFile(entity, ctx.renderContext)),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
17
40
|
const generator = {
|
|
18
41
|
name: "tanstack-grid-hook",
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
&& userFilter(e)
|
|
25
|
-
&& hasDataGridLayout(e),
|
|
26
|
-
generate: perEntity(async (entity, ctx) => {
|
|
27
|
-
if (!ctx.renderContext) {
|
|
28
|
-
throw new Error("tanstack-grid-hook: renderContext is required (provided by runGen)");
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
path: entityOutputPath(ctx.renderContext.outputLayout, entity.package, `${entity.name}.grid.ts`),
|
|
32
|
-
content: await formatTs(renderGridHookFile(entity, ctx.renderContext)),
|
|
33
|
-
};
|
|
34
|
-
}),
|
|
42
|
+
filter: (e) => passesOtherGates(e) && hasDataGridLayout(e),
|
|
43
|
+
generate: async (ctx) => {
|
|
44
|
+
warnMissingDataGridLayout(ctx, passesOtherGates, "<Entity>.grid.ts");
|
|
45
|
+
return emit(ctx);
|
|
46
|
+
},
|
|
35
47
|
};
|
|
36
48
|
if (opts?.target) {
|
|
37
49
|
generator.target = opts.target;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tanstack-grid-hook.js","sourceRoot":"","sources":["../src/tanstack-grid-hook.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"tanstack-grid-hook.js","sourceRoot":"","sources":["../src/tanstack-grid-hook.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAyC,QAAQ,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,YAAY,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpN,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAOnE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,gBAAgB,CAAC,IAA2B;IACnF,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,gFAAgF;IAChF,iFAAiF;IACjF,gFAAgF;IAChF,0BAA0B;IAC1B,EAAE;IACF,sFAAsF;IACtF,wFAAwF;IACxF,yFAAyF;IACzF,uFAAuF;IACvF,oFAAoF;IACpF,mFAAmF;IACnF,sFAAsF;IACtF,yDAAyD;IACzD,MAAM,gBAAgB,GAAG,CAAC,CAAa,EAAW,EAAE,CAClD,sBAAsB,CAAC,CAAC,CAAC;QACzB,2FAA2F;WACxF,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,KAAK,KAAK;WAC5C,UAAU,CAAC,CAAC,CAAC;WACb,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,KAAK,IAAI,CAAC,CAAC;IACnE,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,MAAkB,EAAE,GAAG,EAAE,EAAE;QACvD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;QACxF,CAAC;QACD,OAAO;YACL,IAAI,EAAE,gBAAgB,CACpB,GAAG,CAAC,aAAa,CAAC,YAAY,EAC9B,MAAM,CAAC,OAAO,EACd,GAAG,MAAM,CAAC,IAAI,UAAU,CACzB;YACD,OAAO,EAAE,MAAM,QAAQ,CAAC,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;SACvE,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,oBAAoB;QAC1B,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC;QACtE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACtB,yBAAyB,CAAC,GAAG,EAAE,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;YACrE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;KACF,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAkD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tanstack-grid.d.ts","sourceRoot":"","sources":["../src/tanstack-grid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"tanstack-grid.d.ts","sourceRoot":"","sources":["../src/tanstack-grid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAA6B,KAAK,gBAAgB,EAAwH,MAAM,4BAA4B,CAAC;AAIpN,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAqCpB,gBAAgB,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC"}
|
package/dist/tanstack-grid.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { LAYOUT_SUBTYPE_DATA_GRID } from "@metaobjectsdev/metadata";
|
|
2
1
|
import { perEntity, formatTs, entityOutputPath, emitsInstanceArtifacts, isTphSubtype, CODEGEN_ATTR_EMIT_TANSTACK, CODEGEN_ATTR_EMIT_GRID } from "@metaobjectsdev/codegen-ts";
|
|
2
|
+
import { hasDataGridLayout, warnMissingDataGridLayout } from "./data-grid-gate.js";
|
|
3
3
|
import { renderColumnsFile } from "./templates/columns-file.js";
|
|
4
|
-
function hasDataGridLayout(entity) {
|
|
5
|
-
// layouts() is effective — own + inherited layouts (from extends:/super:).
|
|
6
|
-
return entity.layouts().some((l) => l.subType === LAYOUT_SUBTYPE_DATA_GRID);
|
|
7
|
-
}
|
|
8
4
|
/**
|
|
9
5
|
* Per-entity opt-out via `@emitTanstack: false`. Per-entity opt-IN: presence of
|
|
10
6
|
* at least one `dataGrid` layout on the object. If both pass and the user-supplied
|
|
@@ -12,29 +8,35 @@ function hasDataGridLayout(entity) {
|
|
|
12
8
|
*/
|
|
13
9
|
export const tanstackGrid = function tanstackGrid(opts) {
|
|
14
10
|
const userFilter = opts?.filter ?? (() => true);
|
|
11
|
+
// Every gate EXCEPT the dataGrid-layout opt-in: the framework instance-artifact
|
|
12
|
+
// guard (skips abstract types), the metadata opt-out, and the user filter.
|
|
13
|
+
// FR-017 Tier 3: a TPH discriminator base emits ONE polymorphic grid. Its
|
|
14
|
+
// subtypes inherit the base's dataGrid layout via extends, but per-subtype grids
|
|
15
|
+
// are opt-IN only (own `@emitGrid: true`) — otherwise the polymorphic grid is the
|
|
16
|
+
// single source of truth.
|
|
17
|
+
// Split out so the discoverability note can name exactly the entities the LAYOUT
|
|
18
|
+
// gate alone held back (#287) — an opted-out or abstract type is not a surprise.
|
|
19
|
+
const passesOtherGates = (e) => emitsInstanceArtifacts(e)
|
|
20
|
+
// ADR-0039: resolving — a concrete entity may inherit its @emit* opt-out flag via extends.
|
|
21
|
+
&& e.attr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
|
|
22
|
+
&& userFilter(e)
|
|
23
|
+
&& (!isTphSubtype(e) || e.attr(CODEGEN_ATTR_EMIT_GRID) === true);
|
|
24
|
+
const emit = perEntity(async (entity, ctx) => {
|
|
25
|
+
if (!ctx.renderContext) {
|
|
26
|
+
throw new Error("tanstack-grid: renderContext is required (provided by runGen)");
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
path: entityOutputPath(ctx.renderContext.outputLayout, entity.package, `${entity.name}.columns.tsx`),
|
|
30
|
+
content: await formatTs(renderColumnsFile(entity, ctx.renderContext)),
|
|
31
|
+
};
|
|
32
|
+
});
|
|
15
33
|
const generator = {
|
|
16
34
|
name: "tanstack-grid",
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// grid is the single source of truth.
|
|
23
|
-
filter: (e) => emitsInstanceArtifacts(e)
|
|
24
|
-
// ADR-0039: resolving — a concrete entity may inherit its @emit* opt-out flag via extends.
|
|
25
|
-
&& e.attr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
|
|
26
|
-
&& userFilter(e)
|
|
27
|
-
&& hasDataGridLayout(e)
|
|
28
|
-
&& (!isTphSubtype(e) || e.attr(CODEGEN_ATTR_EMIT_GRID) === true),
|
|
29
|
-
generate: perEntity(async (entity, ctx) => {
|
|
30
|
-
if (!ctx.renderContext) {
|
|
31
|
-
throw new Error("tanstack-grid: renderContext is required (provided by runGen)");
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
path: entityOutputPath(ctx.renderContext.outputLayout, entity.package, `${entity.name}.columns.tsx`),
|
|
35
|
-
content: await formatTs(renderColumnsFile(entity, ctx.renderContext)),
|
|
36
|
-
};
|
|
37
|
-
}),
|
|
35
|
+
filter: (e) => passesOtherGates(e) && hasDataGridLayout(e),
|
|
36
|
+
generate: async (ctx) => {
|
|
37
|
+
warnMissingDataGridLayout(ctx, passesOtherGates, "<Entity>.columns.tsx");
|
|
38
|
+
return emit(ctx);
|
|
39
|
+
},
|
|
38
40
|
};
|
|
39
41
|
if (opts?.target) {
|
|
40
42
|
generator.target = opts.target;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tanstack-grid.js","sourceRoot":"","sources":["../src/tanstack-grid.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"tanstack-grid.js","sourceRoot":"","sources":["../src/tanstack-grid.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAyC,QAAQ,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,YAAY,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpN,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAOhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,YAAY,CAAC,IAAuB;IACvE,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,gFAAgF;IAChF,2EAA2E;IAC3E,0EAA0E;IAC1E,iFAAiF;IACjF,kFAAkF;IAClF,0BAA0B;IAC1B,iFAAiF;IACjF,iFAAiF;IACjF,MAAM,gBAAgB,GAAG,CAAC,CAAa,EAAW,EAAE,CAClD,sBAAsB,CAAC,CAAC,CAAC;QACzB,2FAA2F;WACxF,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,KAAK,KAAK;WAC5C,UAAU,CAAC,CAAC,CAAC;WACb,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,KAAK,IAAI,CAAC,CAAC;IACnE,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,MAAkB,EAAE,GAAG,EAAE,EAAE;QACvD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QACnF,CAAC;QACD,OAAO;YACL,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,cAAc,CAAC;YACpG,OAAO,EAAE,MAAM,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;SACtE,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC;QACtE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACtB,yBAAyB,CAAC,GAAG,EAAE,gBAAgB,EAAE,sBAAsB,CAAC,CAAC;YACzE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;KACF,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAA8C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/codegen-ts-tanstack",
|
|
3
|
-
"version": "0.21.
|
|
4
|
-
"description": "TanStack codegen for metaobjects —
|
|
3
|
+
"version": "0.21.4-rc.1",
|
|
4
|
+
"description": "TanStack codegen for metaobjects — TanStack Query hooks for every entity, plus TanStack Table column definitions and grid-state hooks for entities declaring a layout.dataGrid.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@metaobjectsdev/metadata": "0.21.
|
|
48
|
-
"@metaobjectsdev/codegen-ts": "0.21.
|
|
47
|
+
"@metaobjectsdev/metadata": "0.21.4-rc.1",
|
|
48
|
+
"@metaobjectsdev/codegen-ts": "0.21.4-rc.1",
|
|
49
49
|
"ts-poet": "^6.10.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
2
|
+
import { LAYOUT_SUBTYPE_DATA_GRID, OBJECT_SUBTYPE_VALUE } from "@metaobjectsdev/metadata";
|
|
3
|
+
import type { GenContext } from "@metaobjectsdev/codegen-ts";
|
|
4
|
+
|
|
5
|
+
/** True when the entity declares (or inherits) at least one `layout.dataGrid`. */
|
|
6
|
+
export function hasDataGridLayout(entity: MetaObject): boolean {
|
|
7
|
+
// layouts() is effective — own + inherited layouts (from extends:/super:).
|
|
8
|
+
return entity.layouts().some((l) => l.subType === LAYOUT_SUBTYPE_DATA_GRID);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Grid artifacts are opt-IN per entity: an entity produces one only when it declares
|
|
13
|
+
* a `layout.dataGrid` child. That is intended — but nothing used to say so. An
|
|
14
|
+
* adopter who wired `tanstackQuery() + tanstackGrid()` got `.hooks.ts` for every
|
|
15
|
+
* entity and `.columns.tsx` for none, and reasonably concluded codegen was broken
|
|
16
|
+
* (#287). The package description promising "hooks and column definitions"
|
|
17
|
+
* unconditionally made it worse.
|
|
18
|
+
*
|
|
19
|
+
* So the run itself now says it, following the repo's tell-at-`meta gen`-time
|
|
20
|
+
* precedent (#226 / #258) rather than a doc line that gets missed the same way this
|
|
21
|
+
* one was. Warning only: the exit code is untouched, and `--dry-run` reports it too,
|
|
22
|
+
* since generators run before the write phase branches.
|
|
23
|
+
*
|
|
24
|
+
* It fires ONLY when the generator emitted **nothing at all** — which is the reported
|
|
25
|
+
* condition ("no columns file for ANY entity") and makes the note self-extinguishing:
|
|
26
|
+
* declare one `layout.dataGrid` anywhere in the model and it goes quiet forever. A
|
|
27
|
+
* 50-entity model with 3 grids therefore stays silent, because that author plainly
|
|
28
|
+
* knows how grids work and a permanent 47-name nag is exactly the cry-wolf failure
|
|
29
|
+
* that got the old `timestampMode` warning deleted from `runner.ts` rather than left
|
|
30
|
+
* to fire forever.
|
|
31
|
+
*
|
|
32
|
+
* `passesOtherGates` is the generator's own filter MINUS the layout check, so the
|
|
33
|
+
* named set is exactly "objects the layout gate — and nothing else — held back".
|
|
34
|
+
* `object.value`s are excluded from it: a value object is a payload/DTO shape, never
|
|
35
|
+
* a grid candidate, so naming one would be advice worth ignoring.
|
|
36
|
+
*/
|
|
37
|
+
export function warnMissingDataGridLayout(
|
|
38
|
+
ctx: GenContext,
|
|
39
|
+
passesOtherGates: (entity: MetaObject) => boolean,
|
|
40
|
+
artifact: string,
|
|
41
|
+
): void {
|
|
42
|
+
const candidates = ctx.entities.filter(
|
|
43
|
+
(e) => e.subType !== OBJECT_SUBTYPE_VALUE && passesOtherGates(e),
|
|
44
|
+
);
|
|
45
|
+
if (candidates.length === 0) return;
|
|
46
|
+
if (candidates.some(hasDataGridLayout)) return; // the model uses grids — say nothing
|
|
47
|
+
ctx.warn(
|
|
48
|
+
`no ${artifact} emitted for ${candidates.map((e) => e.name).join(", ")} — grid ` +
|
|
49
|
+
`artifacts are opt-in per entity via a \`layout.dataGrid\` child, and no object in ` +
|
|
50
|
+
`this model declares one. Add e.g. ` +
|
|
51
|
+
`{"layout.dataGrid": {"name": "default", "@columns": ["id", "name"]}} to an entity ` +
|
|
52
|
+
`to emit its grid.`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { perEntity, type Generator, type GeneratorFactory, formatTs, entityOutputPath, emitsInstanceArtifacts, isTphSubtype, CODEGEN_ATTR_EMIT_TANSTACK, CODEGEN_ATTR_EMIT_GRID } from "@metaobjectsdev/codegen-ts";
|
|
3
|
+
import { hasDataGridLayout, warnMissingDataGridLayout } from "./data-grid-gate.js";
|
|
4
4
|
import { renderGridHookFile } from "./templates/grid-hook-file.js";
|
|
5
5
|
|
|
6
6
|
export interface TanstackGridHookOpts {
|
|
@@ -8,11 +8,6 @@ export interface TanstackGridHookOpts {
|
|
|
8
8
|
target?: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
function hasDataGridLayout(entity: MetaObject): boolean {
|
|
12
|
-
// layouts() is effective — own + inherited layouts (from extends:/super:).
|
|
13
|
-
return entity.layouts().some((l) => l.subType === LAYOUT_SUBTYPE_DATA_GRID);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
11
|
/**
|
|
17
12
|
* Per-entity generator that emits <Entity>.grid.ts — one
|
|
18
13
|
* use<Entity><Grid>Grid() hook per layout[dataGrid] declared on the entity.
|
|
@@ -22,29 +17,45 @@ function hasDataGridLayout(entity: MetaObject): boolean {
|
|
|
22
17
|
*/
|
|
23
18
|
export const tanstackGridHook = function tanstackGridHook(opts?: TanstackGridHookOpts): Generator {
|
|
24
19
|
const userFilter = opts?.filter ?? (() => true);
|
|
20
|
+
// Every gate EXCEPT the dataGrid-layout opt-in: the framework instance-artifact
|
|
21
|
+
// guard (skips abstract types), the metadata opt-out, and the user filter. Split
|
|
22
|
+
// out so the discoverability note can name exactly the entities the LAYOUT gate
|
|
23
|
+
// alone held back (#287).
|
|
24
|
+
//
|
|
25
|
+
// The TPH clause must MATCH tanstackGrid's exactly. A TPH subtype inherits its base's
|
|
26
|
+
// dataGrid layout via extends, so `hasDataGridLayout` is true for it — but tanstackGrid
|
|
27
|
+
// deliberately emits no per-subtype columns without an own `@emitGrid: true` (the base's
|
|
28
|
+
// polymorphic grid is the single source of truth). Without the same clause here, a TPH
|
|
29
|
+
// subtype got a `<Sub>.grid.ts` whose sibling `<Sub>.columns.tsx` is never emitted:
|
|
30
|
+
// a dangling `use<Sub>DefaultGrid()` with nothing to pair it with, and an outright
|
|
31
|
+
// TS2307 when the inherited layout carries an `@filter` preset (the hook then imports
|
|
32
|
+
// `<sub>DefaultFilter` from the missing columns module).
|
|
33
|
+
const passesOtherGates = (e: MetaObject): boolean =>
|
|
34
|
+
emitsInstanceArtifacts(e)
|
|
35
|
+
// ADR-0039: resolving — a concrete entity may inherit its @emit* opt-out flag via extends.
|
|
36
|
+
&& e.attr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
|
|
37
|
+
&& userFilter(e)
|
|
38
|
+
&& (!isTphSubtype(e) || e.attr(CODEGEN_ATTR_EMIT_GRID) === true);
|
|
39
|
+
const emit = perEntity(async (entity: MetaObject, ctx) => {
|
|
40
|
+
if (!ctx.renderContext) {
|
|
41
|
+
throw new Error("tanstack-grid-hook: renderContext is required (provided by runGen)");
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
path: entityOutputPath(
|
|
45
|
+
ctx.renderContext.outputLayout,
|
|
46
|
+
entity.package,
|
|
47
|
+
`${entity.name}.grid.ts`,
|
|
48
|
+
),
|
|
49
|
+
content: await formatTs(renderGridHookFile(entity, ctx.renderContext)),
|
|
50
|
+
};
|
|
51
|
+
});
|
|
25
52
|
const generator: Generator = {
|
|
26
53
|
name: "tanstack-grid-hook",
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
&& e.attr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
|
|
33
|
-
&& userFilter(e)
|
|
34
|
-
&& hasDataGridLayout(e),
|
|
35
|
-
generate: perEntity(async (entity, ctx) => {
|
|
36
|
-
if (!ctx.renderContext) {
|
|
37
|
-
throw new Error("tanstack-grid-hook: renderContext is required (provided by runGen)");
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
path: entityOutputPath(
|
|
41
|
-
ctx.renderContext.outputLayout,
|
|
42
|
-
entity.package,
|
|
43
|
-
`${entity.name}.grid.ts`,
|
|
44
|
-
),
|
|
45
|
-
content: await formatTs(renderGridHookFile(entity, ctx.renderContext)),
|
|
46
|
-
};
|
|
47
|
-
}),
|
|
54
|
+
filter: (e: MetaObject) => passesOtherGates(e) && hasDataGridLayout(e),
|
|
55
|
+
generate: async (ctx) => {
|
|
56
|
+
warnMissingDataGridLayout(ctx, passesOtherGates, "<Entity>.grid.ts");
|
|
57
|
+
return emit(ctx);
|
|
58
|
+
},
|
|
48
59
|
};
|
|
49
60
|
if (opts?.target) {
|
|
50
61
|
generator.target = opts.target;
|
package/src/tanstack-grid.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
2
|
-
import { LAYOUT_SUBTYPE_DATA_GRID } from "@metaobjectsdev/metadata";
|
|
3
2
|
import { perEntity, type Generator, type GeneratorFactory, formatTs, entityOutputPath, emitsInstanceArtifacts, isTphSubtype, CODEGEN_ATTR_EMIT_TANSTACK, CODEGEN_ATTR_EMIT_GRID } from "@metaobjectsdev/codegen-ts";
|
|
3
|
+
import { hasDataGridLayout, warnMissingDataGridLayout } from "./data-grid-gate.js";
|
|
4
4
|
import { renderColumnsFile } from "./templates/columns-file.js";
|
|
5
5
|
|
|
6
6
|
export interface TanstackGridOpts {
|
|
@@ -8,11 +8,6 @@ export interface TanstackGridOpts {
|
|
|
8
8
|
target?: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
function hasDataGridLayout(entity: MetaObject): boolean {
|
|
12
|
-
// layouts() is effective — own + inherited layouts (from extends:/super:).
|
|
13
|
-
return entity.layouts().some((l) => l.subType === LAYOUT_SUBTYPE_DATA_GRID);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
11
|
/**
|
|
17
12
|
* Per-entity opt-out via `@emitTanstack: false`. Per-entity opt-IN: presence of
|
|
18
13
|
* at least one `dataGrid` layout on the object. If both pass and the user-supplied
|
|
@@ -20,30 +15,36 @@ function hasDataGridLayout(entity: MetaObject): boolean {
|
|
|
20
15
|
*/
|
|
21
16
|
export const tanstackGrid = function tanstackGrid(opts?: TanstackGridOpts): Generator {
|
|
22
17
|
const userFilter = opts?.filter ?? (() => true);
|
|
18
|
+
// Every gate EXCEPT the dataGrid-layout opt-in: the framework instance-artifact
|
|
19
|
+
// guard (skips abstract types), the metadata opt-out, and the user filter.
|
|
20
|
+
// FR-017 Tier 3: a TPH discriminator base emits ONE polymorphic grid. Its
|
|
21
|
+
// subtypes inherit the base's dataGrid layout via extends, but per-subtype grids
|
|
22
|
+
// are opt-IN only (own `@emitGrid: true`) — otherwise the polymorphic grid is the
|
|
23
|
+
// single source of truth.
|
|
24
|
+
// Split out so the discoverability note can name exactly the entities the LAYOUT
|
|
25
|
+
// gate alone held back (#287) — an opted-out or abstract type is not a surprise.
|
|
26
|
+
const passesOtherGates = (e: MetaObject): boolean =>
|
|
27
|
+
emitsInstanceArtifacts(e)
|
|
28
|
+
// ADR-0039: resolving — a concrete entity may inherit its @emit* opt-out flag via extends.
|
|
29
|
+
&& e.attr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
|
|
30
|
+
&& userFilter(e)
|
|
31
|
+
&& (!isTphSubtype(e) || e.attr(CODEGEN_ATTR_EMIT_GRID) === true);
|
|
32
|
+
const emit = perEntity(async (entity: MetaObject, ctx) => {
|
|
33
|
+
if (!ctx.renderContext) {
|
|
34
|
+
throw new Error("tanstack-grid: renderContext is required (provided by runGen)");
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
path: entityOutputPath(ctx.renderContext.outputLayout, entity.package, `${entity.name}.columns.tsx`),
|
|
38
|
+
content: await formatTs(renderColumnsFile(entity, ctx.renderContext)),
|
|
39
|
+
};
|
|
40
|
+
});
|
|
23
41
|
const generator: Generator = {
|
|
24
42
|
name: "tanstack-grid",
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// grid is the single source of truth.
|
|
31
|
-
filter: (e: MetaObject) =>
|
|
32
|
-
emitsInstanceArtifacts(e)
|
|
33
|
-
// ADR-0039: resolving — a concrete entity may inherit its @emit* opt-out flag via extends.
|
|
34
|
-
&& e.attr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
|
|
35
|
-
&& userFilter(e)
|
|
36
|
-
&& hasDataGridLayout(e)
|
|
37
|
-
&& (!isTphSubtype(e) || e.attr(CODEGEN_ATTR_EMIT_GRID) === true),
|
|
38
|
-
generate: perEntity(async (entity, ctx) => {
|
|
39
|
-
if (!ctx.renderContext) {
|
|
40
|
-
throw new Error("tanstack-grid: renderContext is required (provided by runGen)");
|
|
41
|
-
}
|
|
42
|
-
return {
|
|
43
|
-
path: entityOutputPath(ctx.renderContext.outputLayout, entity.package, `${entity.name}.columns.tsx`),
|
|
44
|
-
content: await formatTs(renderColumnsFile(entity, ctx.renderContext)),
|
|
45
|
-
};
|
|
46
|
-
}),
|
|
43
|
+
filter: (e: MetaObject) => passesOtherGates(e) && hasDataGridLayout(e),
|
|
44
|
+
generate: async (ctx) => {
|
|
45
|
+
warnMissingDataGridLayout(ctx, passesOtherGates, "<Entity>.columns.tsx");
|
|
46
|
+
return emit(ctx);
|
|
47
|
+
},
|
|
47
48
|
};
|
|
48
49
|
if (opts?.target) {
|
|
49
50
|
generator.target = opts.target;
|