@metaobjectsdev/codegen-ts-tanstack 0.5.0-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.
Files changed (42) hide show
  1. package/LICENSE +189 -0
  2. package/README.md +41 -0
  3. package/dist/grid-filter-validate.d.ts +20 -0
  4. package/dist/grid-filter-validate.d.ts.map +1 -0
  5. package/dist/grid-filter-validate.js +37 -0
  6. package/dist/grid-filter-validate.js.map +1 -0
  7. package/dist/index.d.ts +4 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +5 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/tanstack-grid-hook.d.ts +15 -0
  12. package/dist/tanstack-grid-hook.d.ts.map +1 -0
  13. package/dist/tanstack-grid-hook.js +38 -0
  14. package/dist/tanstack-grid-hook.js.map +1 -0
  15. package/dist/tanstack-grid.d.ts +13 -0
  16. package/dist/tanstack-grid.d.ts.map +1 -0
  17. package/dist/tanstack-grid.js +36 -0
  18. package/dist/tanstack-grid.js.map +1 -0
  19. package/dist/tanstack-query.d.ts +15 -0
  20. package/dist/tanstack-query.d.ts.map +1 -0
  21. package/dist/tanstack-query.js +31 -0
  22. package/dist/tanstack-query.js.map +1 -0
  23. package/dist/templates/columns-file.d.ts +4 -0
  24. package/dist/templates/columns-file.d.ts.map +1 -0
  25. package/dist/templates/columns-file.js +142 -0
  26. package/dist/templates/columns-file.js.map +1 -0
  27. package/dist/templates/grid-hook-file.d.ts +4 -0
  28. package/dist/templates/grid-hook-file.d.ts.map +1 -0
  29. package/dist/templates/grid-hook-file.js +125 -0
  30. package/dist/templates/grid-hook-file.js.map +1 -0
  31. package/dist/templates/hooks-file.d.ts +20 -0
  32. package/dist/templates/hooks-file.d.ts.map +1 -0
  33. package/dist/templates/hooks-file.js +208 -0
  34. package/dist/templates/hooks-file.js.map +1 -0
  35. package/package.json +48 -0
  36. package/src/index.ts +4 -0
  37. package/src/tanstack-grid-hook.ts +50 -0
  38. package/src/tanstack-grid.ts +44 -0
  39. package/src/tanstack-query.ts +39 -0
  40. package/src/templates/columns-file.ts +192 -0
  41. package/src/templates/grid-hook-file.ts +155 -0
  42. package/src/templates/hooks-file.ts +235 -0
@@ -0,0 +1,142 @@
1
+ import { code, imp, joinCode } from "ts-poet";
2
+ import { LAYOUT_SUBTYPE_DATA_GRID, LAYOUT_DATA_GRID_ATTR_PAGE_SIZE, LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_FIELD, LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_ORDER, LAYOUT_DATA_GRID_ATTR_FILTERABLE, LAYOUT_DATA_GRID_ATTR_FILTER, LAYOUT_DATA_GRID_ATTR_COLUMNS, } from "@metaobjectsdev/metadata";
3
+ import { GENERATED_HEADER, entityModuleSpecifier } from "@metaobjectsdev/codegen-ts";
4
+ function humanize(s) {
5
+ return s
6
+ .replace(/([a-z])([A-Z])/g, "$1 $2")
7
+ .replace(/^./, (c) => c.toUpperCase());
8
+ }
9
+ function fieldViewKind(field) {
10
+ const view = field.ownViews()[0];
11
+ return view?.subType ?? "text";
12
+ }
13
+ function fieldLabel(field) {
14
+ const view = field.ownViews()[0];
15
+ const label = view?.ownAttr("label");
16
+ if (typeof label === "string")
17
+ return label;
18
+ return humanize(field.name);
19
+ }
20
+ /**
21
+ * Extract grid specs from an entity's dataGrid layouts, resolving column
22
+ * metadata against the entity's field list.
23
+ *
24
+ * Column set: read from @columns stringArray attr on the layout. If absent,
25
+ * fall back to all fields on the entity (pre-E-T2 behaviour, kept for
26
+ * backwards compat with metadata not yet migrated by E-T4).
27
+ */
28
+ function extractGrids(entity) {
29
+ // fields() and layouts() are both effective (own + inherited via extends:/super:).
30
+ const fieldsByName = new Map(entity.fields().map((f) => [f.name, f]));
31
+ const grids = [];
32
+ for (const layout of entity.layouts()) {
33
+ if (layout.subType !== LAYOUT_SUBTYPE_DATA_GRID)
34
+ continue;
35
+ // @columns is a stringArray attr on the layout (set by E-T4 migration).
36
+ // Fall back to all entity fields if not present.
37
+ const columnsAttr = layout.ownAttr(LAYOUT_DATA_GRID_ATTR_COLUMNS);
38
+ const columnNames = Array.isArray(columnsAttr)
39
+ ? columnsAttr.filter((x) => typeof x === "string")
40
+ : [...fieldsByName.keys()];
41
+ const columns = columnNames.flatMap((name) => {
42
+ const field = fieldsByName.get(name);
43
+ if (!field)
44
+ return []; // columns ref that doesn't exist on entity; defensive skip
45
+ const spec = {
46
+ id: name,
47
+ header: fieldLabel(field),
48
+ viewKind: fieldViewKind(field),
49
+ };
50
+ return [spec];
51
+ });
52
+ const sortField = layout.ownAttr(LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_FIELD);
53
+ const sortOrder = layout.ownAttr(LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_ORDER);
54
+ const filterAttr = layout.ownAttr(LAYOUT_DATA_GRID_ATTR_FILTER);
55
+ const grid = {
56
+ name: layout.name || "default",
57
+ pageSize: layout.ownAttr(LAYOUT_DATA_GRID_ATTR_PAGE_SIZE) ?? 25,
58
+ filterable: layout.ownAttr(LAYOUT_DATA_GRID_ATTR_FILTERABLE) === true,
59
+ columns,
60
+ };
61
+ if (typeof sortField === "string")
62
+ grid.defaultSortField = sortField;
63
+ if (sortOrder === "asc" || sortOrder === "desc")
64
+ grid.defaultSortOrder = sortOrder;
65
+ if (typeof filterAttr === "object" && filterAttr !== null && !Array.isArray(filterAttr)) {
66
+ grid.filter = filterAttr;
67
+ }
68
+ grids.push(grid);
69
+ }
70
+ return grids;
71
+ }
72
+ function renderColumnDef(col) {
73
+ const parts = [];
74
+ parts.push(` id: ${JSON.stringify(col.id)}`);
75
+ parts.push(` accessorKey: ${JSON.stringify(col.id)}`);
76
+ parts.push(` header: ${JSON.stringify(col.header)}`);
77
+ const meta = [`view: ${JSON.stringify(col.viewKind)}`];
78
+ if (col.sortable !== undefined)
79
+ meta.push(`sortable: ${col.sortable}`);
80
+ if (col.width !== undefined)
81
+ meta.push(`width: ${col.width}`);
82
+ if (col.renderer !== undefined)
83
+ meta.push(`renderer: ${JSON.stringify(col.renderer)}`);
84
+ parts.push(` meta: { ${meta.join(", ")} }`);
85
+ return ` {\n${parts.join(",\n")}\n }`;
86
+ }
87
+ export function renderColumnsFile(entity, ctx) {
88
+ const entityName = entity.name;
89
+ const lcEntity = entityName.charAt(0).toLowerCase() + entityName.slice(1);
90
+ const grids = extractGrids(entity);
91
+ const ColumnDefSym = imp("t:ColumnDef@@tanstack/react-table");
92
+ // Track whether any grid emits a filter const so we know to import <Entity>Filter.
93
+ let hasFilterConst = false;
94
+ const sections = grids.map((grid) => {
95
+ const gridConstName = `${lcEntity}${capitalize(grid.name)}Grid`;
96
+ const columnsConstName = `${lcEntity}${capitalize(grid.name)}Columns`;
97
+ const sortBlock = grid.defaultSortField && grid.defaultSortOrder
98
+ ? ` defaultSort: { field: ${JSON.stringify(grid.defaultSortField)}, order: ${JSON.stringify(grid.defaultSortOrder)} as const },\n`
99
+ : "";
100
+ const gridConst = code `
101
+ export const ${gridConstName} = {
102
+ name: ${JSON.stringify(grid.name)},
103
+ pageSize: ${grid.pageSize},
104
+ ${sortBlock} filterable: ${grid.filterable},
105
+ };
106
+ `;
107
+ const colsLines = grid.columns.map(renderColumnDef).join(",\n");
108
+ const colsConst = code `
109
+ export const ${columnsConstName}: ${ColumnDefSym}<${entityName}Row>[] = [
110
+ ${colsLines},
111
+ ];
112
+ `;
113
+ // Emit per-grid filter const when @filter is set. The value is already a
114
+ // desugared, load-time-validated object — emit it verbatim.
115
+ let filterConstCode = null;
116
+ if (grid.filter !== undefined) {
117
+ const filterConstName = `${lcEntity}${capitalize(grid.name)}Filter`;
118
+ hasFilterConst = true;
119
+ filterConstCode = code `
120
+ export const ${filterConstName}: ${entityName}Filter = ${JSON.stringify(grid.filter, null, 2)};
121
+ `;
122
+ }
123
+ return filterConstCode
124
+ ? code `${gridConst}\n${colsConst}\n${filterConstCode}`
125
+ : code `${gridConst}\n${colsConst}`;
126
+ });
127
+ const header = `// ${GENERATED_HEADER}-tanstack — DO NOT EDIT.\n` +
128
+ `// Source metadata: ${entityName} (${entity.fqn()})\n`;
129
+ // Import the entity's own file. Same target → relative "./Entity"; cross
130
+ // target → importBase-qualified package path.
131
+ const entityModule = entityModuleSpecifier(ctx.selfTarget, ctx.entityModuleTarget, entity.package, entityName, ctx.extStyle);
132
+ // Import <Entity>Row always; import <Entity>Filter only when a filter const is emitted.
133
+ const entityImportCode = hasFilterConst
134
+ ? code `import type { ${entityName} as ${entityName}Row, ${entityName}Filter } from ${JSON.stringify(entityModule)};`
135
+ : code `import type { ${entityName} as ${entityName}Row } from ${JSON.stringify(entityModule)};`;
136
+ const body = joinCode(sections, { on: "\n" });
137
+ return header + entityImportCode.toString() + "\n" + body.toString();
138
+ }
139
+ function capitalize(s) {
140
+ return s.charAt(0).toUpperCase() + s.slice(1);
141
+ }
142
+ //# sourceMappingURL=columns-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"columns-file.js","sourceRoot":"","sources":["../../src/templates/columns-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { MetaObject } from "@metaobjectsdev/metadata";
2
+ import type { RenderContext } from "@metaobjectsdev/codegen-ts";
3
+ export declare function renderGridHookFile(entity: MetaObject, ctx: RenderContext): string;
4
+ //# sourceMappingURL=grid-hook-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid-hook-file.d.ts","sourceRoot":"","sources":["../../src/templates/grid-hook-file.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,0BAA0B,CAAC;AAEvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AA8BhE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CA+GjF"}
@@ -0,0 +1,125 @@
1
+ // Generated grid-hook template — emits one use<Entity><Grid>Grid() per
2
+ // layout[dataGrid] declared on the entity. The hook owns
3
+ // {sorting, pagination, columnFilters, search} state and runs the query
4
+ // against the entity's CRUD list route with withCount=1 (opt-in envelope).
5
+ //
6
+ // The hook returns the controlled-<EntityGrid> prop shape, so a consumer
7
+ // page is just:
8
+ // const grid = useSubscriberDefaultGrid();
9
+ // <EntityGrid {...grid} columns={subscriberDefaultColumns} grid={subscriberDefaultGrid} />
10
+ import { code, imp, joinCode } from "ts-poet";
11
+ import { LAYOUT_SUBTYPE_DATA_GRID } from "@metaobjectsdev/metadata";
12
+ import { GENERATED_HEADER, entityModuleSpecifier, siblingSpecifier } from "@metaobjectsdev/codegen-ts";
13
+ function extractGrids(entity) {
14
+ const out = [];
15
+ for (const l of entity.layouts()) {
16
+ if (l.subType !== LAYOUT_SUBTYPE_DATA_GRID)
17
+ continue;
18
+ out.push({
19
+ name: l.name || "default",
20
+ pageSize: l.pageSize ?? 25,
21
+ hasFilterPreset: l.filter !== undefined,
22
+ defaultSortField: l.defaultSortField,
23
+ defaultSortOrder: l.defaultSortOrder,
24
+ });
25
+ }
26
+ return out;
27
+ }
28
+ function capitalize(s) {
29
+ return s.charAt(0).toUpperCase() + s.slice(1);
30
+ }
31
+ export function renderGridHookFile(entity, ctx) {
32
+ const entityName = entity.name;
33
+ const lcEntity = entityName.charAt(0).toLowerCase() + entityName.slice(1);
34
+ const grids = extractGrids(entity);
35
+ if (grids.length === 0)
36
+ return "";
37
+ // Import the entity's own file. Same target → relative "./Entity"; cross
38
+ // target → importBase-qualified package path.
39
+ const entityModule = entityModuleSpecifier(ctx.selfTarget, ctx.entityModuleTarget, entity.package, entityName, ctx.extStyle);
40
+ // ts-poet symbols (imp()) — typed and deduped on emit.
41
+ const useStateSym = imp("useState@react");
42
+ const useMemoSym = imp("useMemo@react");
43
+ const useQuerySym = imp("useQuery@@tanstack/react-query");
44
+ const SortingStateSym = imp("t:SortingState@@tanstack/react-table");
45
+ const PaginationStateSym = imp("t:PaginationState@@tanstack/react-table");
46
+ const ColumnFiltersStateSym = imp("t:ColumnFiltersState@@tanstack/react-table");
47
+ const useEntityFetcherSym = imp("useEntityFetcher@@metaobjectsdev/tanstack");
48
+ const buildFilterQsSym = imp("buildFilterQs@@metaobjectsdev/runtime-web");
49
+ const entityImports = code `
50
+ import { ${entityName} } from ${JSON.stringify(entityModule)};
51
+ import type { ${entityName} as ${entityName}Row } from ${JSON.stringify(entityModule)};
52
+ `;
53
+ // Collect names of all filter preset consts needed.
54
+ const filterPresetImports = grids
55
+ .filter((g) => g.hasFilterPreset)
56
+ .map((g) => `${lcEntity}${capitalize(g.name)}Filter`);
57
+ // Columns file is a same-target sibling of the grid-hook (both emitted to
58
+ // selfTarget) — always relative, package-layout aware.
59
+ const columnsModule = siblingSpecifier(ctx.selfTarget, entity.package, `${entityName}.columns`, ctx.extStyle);
60
+ const filterPresetImportCode = filterPresetImports.length > 0
61
+ ? code `import { ${filterPresetImports.join(", ")} } from ${JSON.stringify(columnsModule)};\n`
62
+ : code ``;
63
+ const sections = grids.map((grid) => {
64
+ const cap = capitalize(grid.name);
65
+ const hookName = `use${entityName}${cap}Grid`;
66
+ const presetConst = grid.hasFilterPreset ? `${lcEntity}${cap}Filter` : null;
67
+ const initialSorting = grid.defaultSortField
68
+ ? `[{ id: ${JSON.stringify(grid.defaultSortField)}, desc: ${JSON.stringify(grid.defaultSortOrder === "desc")} }]`
69
+ : `[]`;
70
+ const initialPagination = `{ pageIndex: 0, pageSize: ${grid.pageSize} }`;
71
+ return code `
72
+ export function ${hookName}() {
73
+ const [sorting, setSorting] = ${useStateSym}<${SortingStateSym}>(${initialSorting});
74
+ const [pagination, setPagination] = ${useStateSym}<${PaginationStateSym}>(${initialPagination});
75
+ const [columnFilters, setColumnFilters] = ${useStateSym}<${ColumnFiltersStateSym}>([]);
76
+ const [search, setSearch] = ${useStateSym}<string>("");
77
+
78
+ const fetcher = ${useEntityFetcherSym}();
79
+
80
+ const qs = ${useMemoSym}(() => {
81
+ const filterObj: Record<string, unknown> = ${presetConst ? `{ ...${presetConst} }` : `{}`};
82
+ for (const f of columnFilters) {
83
+ filterObj[f.id] = f.value as unknown;
84
+ }
85
+ const sort = sorting.length > 0
86
+ ? \`\${sorting[0].id}:\${sorting[0].desc ? "desc" : "asc"}\`
87
+ : undefined;
88
+ return ${buildFilterQsSym}({
89
+ ...filterObj,
90
+ ...(sort !== undefined ? { sort } : {}),
91
+ limit: pagination.pageSize,
92
+ offset: pagination.pageIndex * pagination.pageSize,
93
+ ...(search !== "" ? { search } : {}),
94
+ withCount: 1,
95
+ });
96
+ }, [sorting, pagination, columnFilters, search]);
97
+
98
+ const query = ${useQuerySym}<{ rows: ${entityName}Row[]; total: number }>({
99
+ queryKey: [${JSON.stringify(lcEntity)}, "grid", ${JSON.stringify(grid.name)}, qs],
100
+ queryFn: () => fetcher<{ rows: ${entityName}Row[]; total: number }>(
101
+ \`\${${entityName}.$apiPrefix}\${${entityName}.$path}?\${qs}\`,
102
+ ),
103
+ });
104
+
105
+ return {
106
+ data: query.data?.rows ?? [],
107
+ rowCount: query.data?.total ?? 0,
108
+ state: { sorting, pagination, columnFilters },
109
+ onSortingChange: setSorting,
110
+ onPaginationChange: setPagination,
111
+ onColumnFiltersChange: setColumnFilters,
112
+ search,
113
+ onSearchChange: setSearch,
114
+ isLoading: query.isLoading,
115
+ error: query.error,
116
+ };
117
+ }
118
+ `;
119
+ });
120
+ const header = `// ${GENERATED_HEADER}-tanstack — DO NOT EDIT.\n` +
121
+ `// Source metadata: ${entityName} (${entity.fqn()})\n`;
122
+ const body = joinCode(sections, { on: "\n" });
123
+ return header + entityImports.toString() + filterPresetImportCode.toString() + body.toString();
124
+ }
125
+ //# sourceMappingURL=grid-hook-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid-hook-file.js","sourceRoot":"","sources":["../../src/templates/grid-hook-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { MetaObject } from "@metaobjectsdev/metadata";
2
+ import type { RenderContext } from "@metaobjectsdev/codegen-ts";
3
+ /**
4
+ * Render <Entity>.hooks.ts — query-key factory + 2 query hooks + (for non-projections) 3 mutation hooks.
5
+ *
6
+ * Projections (view-backed, read-only) emit only:
7
+ * - <camel>Keys query-key factory
8
+ * - use<Entity>(id) — useQuery on GET :id
9
+ * - use<Entities>(filter) — useQuery on list
10
+ *
11
+ * Full (writable) entities additionally emit:
12
+ * - useCreate<Entity>
13
+ * - useUpdate<Entity>
14
+ * - useDelete<Entity>
15
+ *
16
+ * All hooks call useEntityFetcher() (from @metaobjectsdev/tanstack) for
17
+ * the underlying HTTP. Mutations aggressively invalidate <entity>Keys.all().
18
+ */
19
+ export declare function renderHooksFile(entity: MetaObject, ctx: RenderContext): string;
20
+ //# sourceMappingURL=hooks-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-file.d.ts","sourceRoot":"","sources":["../../src/templates/hooks-file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAGhE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAc9E"}
@@ -0,0 +1,208 @@
1
+ import { code, imp, joinCode } from "ts-poet";
2
+ import { GENERATED_HEADER, isProjection, pluralize, entityModuleSpecifier } from "@metaobjectsdev/codegen-ts";
3
+ /**
4
+ * Render <Entity>.hooks.ts — query-key factory + 2 query hooks + (for non-projections) 3 mutation hooks.
5
+ *
6
+ * Projections (view-backed, read-only) emit only:
7
+ * - <camel>Keys query-key factory
8
+ * - use<Entity>(id) — useQuery on GET :id
9
+ * - use<Entities>(filter) — useQuery on list
10
+ *
11
+ * Full (writable) entities additionally emit:
12
+ * - useCreate<Entity>
13
+ * - useUpdate<Entity>
14
+ * - useDelete<Entity>
15
+ *
16
+ * All hooks call useEntityFetcher() (from @metaobjectsdev/tanstack) for
17
+ * the underlying HTTP. Mutations aggressively invalidate <entity>Keys.all().
18
+ */
19
+ export function renderHooksFile(entity, ctx) {
20
+ // Import the entity's own file. Same target → relative "./Entity"; cross
21
+ // target → importBase-qualified package path.
22
+ const entityModule = entityModuleSpecifier(ctx.selfTarget, ctx.entityModuleTarget, entity.package, entity.name, ctx.extStyle);
23
+ if (isProjection(entity)) {
24
+ return renderReadOnlyHooksFile(entity, entityModule);
25
+ }
26
+ return renderFullHooksFile(entity, entityModule);
27
+ }
28
+ // ---------------------------------------------------------------------------
29
+ // Read-only path (projections)
30
+ // ---------------------------------------------------------------------------
31
+ function renderReadOnlyHooksFile(entity, entityModule) {
32
+ const entityName = entity.name;
33
+ const entityNamePlural = pluralize(entityName);
34
+ const lcEntity = entityName.charAt(0).toLowerCase() + entityName.slice(1);
35
+ const keysVar = `${lcEntity}Keys`;
36
+ const useQuerySym = imp("useQuery@@tanstack/react-query");
37
+ const useQueryOptionsSym = imp("t:UseQueryOptions@@tanstack/react-query");
38
+ const useQueryResultSym = imp("t:UseQueryResult@@tanstack/react-query");
39
+ const useEntityFetcherSym = imp("useEntityFetcher@@metaobjectsdev/tanstack");
40
+ const buildFilterQsSym = imp("buildFilterQs@@metaobjectsdev/runtime-web");
41
+ const entityImports = code `
42
+ import {
43
+ ${entityName},
44
+ type ${entityName} as ${entityName}Row,
45
+ type ${entityName}Filter,
46
+ } from ${JSON.stringify(entityModule)};
47
+ `;
48
+ const queryKeys = code `
49
+ export const ${keysVar} = {
50
+ all: () => [${JSON.stringify(lcEntity)}] as const,
51
+ lists: () => [...${keysVar}.all(), "list"] as const,
52
+ list: (filter?: ${entityName}Filter) => [...${keysVar}.lists(), filter ?? {}] as const,
53
+ details: () => [...${keysVar}.all(), "detail"] as const,
54
+ detail: (id: number) => [...${keysVar}.details(), id] as const,
55
+ };
56
+ `;
57
+ const queries = code `
58
+ export function use${entityName}(
59
+ id: number,
60
+ opts?: Omit<${useQueryOptionsSym}<${entityName}Row>, "queryKey" | "queryFn">,
61
+ ): ${useQueryResultSym}<${entityName}Row> {
62
+ const fetcher = ${useEntityFetcherSym}();
63
+ return ${useQuerySym}<${entityName}Row>({
64
+ queryKey: ${keysVar}.detail(id),
65
+ queryFn: () => fetcher<${entityName}Row>(\`\${${entityName}.$apiPrefix}\${${entityName}.$path}/\${id}\`),
66
+ ...opts,
67
+ });
68
+ }
69
+
70
+ export function use${entityNamePlural}(
71
+ filter?: ${entityName}Filter,
72
+ opts?: Omit<${useQueryOptionsSym}<${entityName}Row[]>, "queryKey" | "queryFn">,
73
+ ): ${useQueryResultSym}<${entityName}Row[]> {
74
+ const fetcher = ${useEntityFetcherSym}();
75
+ const qs = filter ? "?" + ${buildFilterQsSym}(filter as Record<string, unknown>) : "";
76
+ return ${useQuerySym}<${entityName}Row[]>({
77
+ queryKey: ${keysVar}.list(filter),
78
+ queryFn: () => fetcher<${entityName}Row[]>(\`\${${entityName}.$apiPrefix}\${${entityName}.$path}\${qs}\`),
79
+ ...opts,
80
+ });
81
+ }
82
+ `;
83
+ const body = joinCode([queryKeys, queries], { on: "\n" });
84
+ const header = `// ${GENERATED_HEADER}-tanstack — DO NOT EDIT.\n` +
85
+ `// Source metadata: ${entityName} (${entity.fqn()})\n`;
86
+ return header + entityImports.toString() + body.toString();
87
+ }
88
+ // ---------------------------------------------------------------------------
89
+ // Full path (writable entities — table-backed or write-through)
90
+ // ---------------------------------------------------------------------------
91
+ function renderFullHooksFile(entity, entityModule) {
92
+ const entityName = entity.name;
93
+ const entityNamePlural = pluralize(entityName);
94
+ const lcEntity = entityName.charAt(0).toLowerCase() + entityName.slice(1);
95
+ const keysVar = `${lcEntity}Keys`;
96
+ const useMutationSym = imp("useMutation@@tanstack/react-query");
97
+ const useQuerySym = imp("useQuery@@tanstack/react-query");
98
+ const useQueryClientSym = imp("useQueryClient@@tanstack/react-query");
99
+ const useQueryOptionsSym = imp("t:UseQueryOptions@@tanstack/react-query");
100
+ const useMutationOptionsSym = imp("t:UseMutationOptions@@tanstack/react-query");
101
+ const useQueryResultSym = imp("t:UseQueryResult@@tanstack/react-query");
102
+ const useMutationResultSym = imp("t:UseMutationResult@@tanstack/react-query");
103
+ const useEntityFetcherSym = imp("useEntityFetcher@@metaobjectsdev/tanstack");
104
+ const buildFilterQsSym = imp("buildFilterQs@@metaobjectsdev/runtime-web");
105
+ const entityImports = code `
106
+ import {
107
+ ${entityName},
108
+ type ${entityName} as ${entityName}Row,
109
+ type ${entityName}Insert,
110
+ type ${entityName}Update,
111
+ type ${entityName}Filter,
112
+ } from ${JSON.stringify(entityModule)};
113
+ `;
114
+ const queryKeys = code `
115
+ export const ${keysVar} = {
116
+ all: () => [${JSON.stringify(lcEntity)}] as const,
117
+ lists: () => [...${keysVar}.all(), "list"] as const,
118
+ list: (filter?: ${entityName}Filter) => [...${keysVar}.lists(), filter ?? {}] as const,
119
+ details: () => [...${keysVar}.all(), "detail"] as const,
120
+ detail: (id: number) => [...${keysVar}.details(), id] as const,
121
+ };
122
+ `;
123
+ const queries = code `
124
+ export function use${entityName}(
125
+ id: number,
126
+ opts?: Omit<${useQueryOptionsSym}<${entityName}Row>, "queryKey" | "queryFn">,
127
+ ): ${useQueryResultSym}<${entityName}Row> {
128
+ const fetcher = ${useEntityFetcherSym}();
129
+ return ${useQuerySym}<${entityName}Row>({
130
+ queryKey: ${keysVar}.detail(id),
131
+ queryFn: () => fetcher<${entityName}Row>(\`\${${entityName}.$apiPrefix}\${${entityName}.$path}/\${id}\`),
132
+ ...opts,
133
+ });
134
+ }
135
+
136
+ export function use${entityNamePlural}(
137
+ filter?: ${entityName}Filter,
138
+ opts?: Omit<${useQueryOptionsSym}<${entityName}Row[]>, "queryKey" | "queryFn">,
139
+ ): ${useQueryResultSym}<${entityName}Row[]> {
140
+ const fetcher = ${useEntityFetcherSym}();
141
+ const qs = filter ? "?" + ${buildFilterQsSym}(filter as Record<string, unknown>) : "";
142
+ return ${useQuerySym}<${entityName}Row[]>({
143
+ queryKey: ${keysVar}.list(filter),
144
+ queryFn: () => fetcher<${entityName}Row[]>(\`\${${entityName}.$apiPrefix}\${${entityName}.$path}\${qs}\`),
145
+ ...opts,
146
+ });
147
+ }
148
+ `;
149
+ const mutations = code `
150
+ export function useCreate${entityName}(
151
+ opts?: Omit<${useMutationOptionsSym}<${entityName}Row, Error, ${entityName}Insert>, "mutationFn">,
152
+ ): ${useMutationResultSym}<${entityName}Row, Error, ${entityName}Insert> {
153
+ const fetcher = ${useEntityFetcherSym}();
154
+ const qc = ${useQueryClientSym}();
155
+ return ${useMutationSym}<${entityName}Row, Error, ${entityName}Insert>({
156
+ mutationFn: (input) => fetcher<${entityName}Row>(\`\${${entityName}.$apiPrefix}\${${entityName}.$path}\`, {
157
+ method: "POST",
158
+ headers: { "Content-Type": "application/json" },
159
+ body: JSON.stringify(input),
160
+ }),
161
+ ...opts,
162
+ onSuccess: (...args) => {
163
+ qc.invalidateQueries({ queryKey: ${keysVar}.all() });
164
+ opts?.onSuccess?.(...args);
165
+ },
166
+ });
167
+ }
168
+
169
+ export function useUpdate${entityName}(
170
+ opts?: Omit<${useMutationOptionsSym}<${entityName}Row, Error, { id: number; input: ${entityName}Update }>, "mutationFn">,
171
+ ): ${useMutationResultSym}<${entityName}Row, Error, { id: number; input: ${entityName}Update }> {
172
+ const fetcher = ${useEntityFetcherSym}();
173
+ const qc = ${useQueryClientSym}();
174
+ return ${useMutationSym}({
175
+ mutationFn: ({ id, input }) => fetcher<${entityName}Row>(\`\${${entityName}.$apiPrefix}\${${entityName}.$path}/\${id}\`, {
176
+ method: "PATCH",
177
+ headers: { "Content-Type": "application/json" },
178
+ body: JSON.stringify(input),
179
+ }),
180
+ ...opts,
181
+ onSuccess: (...args) => {
182
+ qc.invalidateQueries({ queryKey: ${keysVar}.all() });
183
+ opts?.onSuccess?.(...args);
184
+ },
185
+ });
186
+ }
187
+
188
+ export function useDelete${entityName}(
189
+ opts?: Omit<${useMutationOptionsSym}<void, Error, number>, "mutationFn">,
190
+ ): ${useMutationResultSym}<void, Error, number> {
191
+ const fetcher = ${useEntityFetcherSym}();
192
+ const qc = ${useQueryClientSym}();
193
+ return ${useMutationSym}({
194
+ mutationFn: (id) => fetcher<void>(\`\${${entityName}.$apiPrefix}\${${entityName}.$path}/\${id}\`, { method: "DELETE" }),
195
+ ...opts,
196
+ onSuccess: (...args) => {
197
+ qc.invalidateQueries({ queryKey: ${keysVar}.all() });
198
+ opts?.onSuccess?.(...args);
199
+ },
200
+ });
201
+ }
202
+ `;
203
+ const body = joinCode([queryKeys, queries, mutations], { on: "\n" });
204
+ const header = `// ${GENERATED_HEADER}-tanstack — DO NOT EDIT.\n` +
205
+ `// Source metadata: ${entityName} (${entity.fqn()})\n`;
206
+ return header + entityImports.toString() + body.toString();
207
+ }
208
+ //# sourceMappingURL=hooks-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-file.js","sourceRoot":"","sources":["../../src/templates/hooks-file.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@metaobjectsdev/codegen-ts-tanstack",
3
+ "version": "0.5.0-rc.1",
4
+ "description": "TanStack codegen for metaobjects — emits hooks and column definitions for TanStack Query and Table.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "bun": "./src/index.ts",
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": ["dist", "src", "README.md", "LICENSE"],
16
+ "scripts": {
17
+ "build": "tsc -p .",
18
+ "typecheck": "tsc -p tsconfig.typecheck.json"
19
+ },
20
+ "license": "Apache-2.0",
21
+ "author": "Doug Mealing <doug@dougmealing.com>",
22
+ "homepage": "https://metaobjects.dev",
23
+ "bugs": {
24
+ "url": "https://github.com/metaobjectsdev/metaobjects/issues"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/metaobjectsdev/metaobjects.git",
29
+ "directory": "server/typescript/packages/codegen-ts-tanstack"
30
+ },
31
+ "keywords": ["metaobjects", "codegen", "tanstack", "react-query", "react-table"],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "dependencies": {
36
+ "@metaobjectsdev/metadata": "0.5.0",
37
+ "@metaobjectsdev/codegen-ts": "0.5.0",
38
+ "ts-poet": "^6.10.0"
39
+ },
40
+ "peerDependencies": {
41
+ "@biomejs/biome": ">=1.9.0"
42
+ },
43
+ "devDependencies": {
44
+ "@biomejs/biome": "^1.9.0",
45
+ "bun-types": "latest",
46
+ "typescript": "^5.6.0"
47
+ }
48
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ // Public API surface for @metaobjectsdev/codegen-ts-tanstack.
2
+ export { tanstackQuery, type TanstackQueryOpts } from "./tanstack-query.js";
3
+ export { tanstackGrid, type TanstackGridOpts } from "./tanstack-grid.js";
4
+ export { tanstackGridHook, type TanstackGridHookOpts } from "./tanstack-grid-hook.js";
@@ -0,0 +1,50 @@
1
+ import type { MetaObject } from "@metaobjectsdev/metadata";
2
+ import { LAYOUT_SUBTYPE_DATA_GRID } from "@metaobjectsdev/metadata";
3
+ import { perEntity, type Generator, type GeneratorFactory, formatTs, entityOutputPath } from "@metaobjectsdev/codegen-ts";
4
+ import { renderGridHookFile } from "./templates/grid-hook-file.js";
5
+
6
+ export interface TanstackGridHookOpts {
7
+ filter?: (entity: MetaObject) => boolean;
8
+ target?: string;
9
+ }
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
+ /**
17
+ * Per-entity generator that emits <Entity>.grid.ts — one
18
+ * use<Entity><Grid>Grid() hook per layout[dataGrid] declared on the entity.
19
+ *
20
+ * Per-entity opt-out via @emitTanstack: false. Per-entity opt-IN: presence of
21
+ * at least one dataGrid layout on the object (mirrors tanstackGrid).
22
+ */
23
+ export const tanstackGridHook = function tanstackGridHook(opts?: TanstackGridHookOpts): Generator {
24
+ const userFilter = opts?.filter ?? (() => true);
25
+ const generator: Generator = {
26
+ name: "tanstack-grid-hook",
27
+ // AND-composes opt-out, user filter, and dataGrid layout presence.
28
+ filter: (e: MetaObject) =>
29
+ e.ownAttr("emitTanstack") !== false
30
+ && userFilter(e)
31
+ && hasDataGridLayout(e),
32
+ generate: perEntity(async (entity, ctx) => {
33
+ if (!ctx.renderContext) {
34
+ throw new Error("tanstack-grid-hook: renderContext is required (provided by runGen)");
35
+ }
36
+ return {
37
+ path: entityOutputPath(
38
+ ctx.renderContext.outputLayout,
39
+ entity.package,
40
+ `${entity.name}.grid.ts`,
41
+ ),
42
+ content: await formatTs(renderGridHookFile(entity, ctx.renderContext)),
43
+ };
44
+ }),
45
+ };
46
+ if (opts?.target) {
47
+ generator.target = opts.target;
48
+ }
49
+ return generator;
50
+ } as GeneratorFactory<TanstackGridHookOpts | void>;