@metaobjectsdev/codegen-ts-react 0.9.0 → 0.11.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.
@@ -1 +1 @@
1
- {"version":3,"file":"form-file.d.ts","sourceRoot":"","sources":["../src/form-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAA6B,KAAK,gBAAgB,EAAyC,MAAM,4BAA4B,CAAC;AAGrI,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,EAuBhB,gBAAgB,CAAC,YAAY,CAAC,CAAC"}
1
+ {"version":3,"file":"form-file.d.ts","sourceRoot":"","sources":["../src/form-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,EAA6B,KAAK,gBAAgB,EAA+E,MAAM,4BAA4B,CAAC;AAG3K,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,EAiChB,gBAAgB,CAAC,YAAY,CAAC,CAAC"}
package/dist/form-file.js CHANGED
@@ -1,4 +1,5 @@
1
- import { perEntity, entityOutputPath, emitsWriteArtifacts } from "@metaobjectsdev/codegen-ts";
1
+ import { OBJECT_ATTR_DISCRIMINATOR } from "@metaobjectsdev/metadata";
2
+ import { perEntity, entityOutputPath, emitsWriteArtifacts, isTphSubtype, CODEGEN_ATTR_EMIT_FORM } from "@metaobjectsdev/codegen-ts";
2
3
  import { renderFormFile } from "./templates/form-file.js";
3
4
  /**
4
5
  * Forms are opt-in: a user must add `formFile()` to config.generators.
@@ -13,7 +14,21 @@ export const formFile = function formFile(opts) {
13
14
  // (skips abstract types — no instance — and read-only projections —
14
15
  // instantiable for read, never for write), the metadata opt-out, and
15
16
  // the optional user filter.
16
- filter: (e) => emitsWriteArtifacts(e) && e.ownAttr("emitForm") !== false && userFilter(e),
17
+ // FR-017 Tier 3: forms are ALWAYS per-subtype. The discriminator base gets
18
+ // NO form (you can't create an abstract base), but each concrete subtype
19
+ // does — even though it has no own writable source (it inherits the base's).
20
+ filter: (e) => {
21
+ if (e.ownAttr(CODEGEN_ATTR_EMIT_FORM) === false)
22
+ return false;
23
+ if (!userFilter(e))
24
+ return false;
25
+ if (isTphSubtype(e))
26
+ return true; // per-subtype form
27
+ // A discriminator base is never form-rendered directly.
28
+ if (typeof e.ownAttr(OBJECT_ATTR_DISCRIMINATOR) === "string")
29
+ return false;
30
+ return emitsWriteArtifacts(e);
31
+ },
17
32
  generate: perEntity((entity, ctx) => {
18
33
  if (!ctx.renderContext) {
19
34
  throw new Error("form-file: renderContext is required (provided by runGen)");
@@ -1 +1 @@
1
- {"version":3,"file":"form-file.js","sourceRoot":"","sources":["../src/form-file.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAyC,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACrI,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAO1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,SAAS,QAAQ,CAAC,IAAmB;IAC3D,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,WAAW;QACjB,8DAA8D;QAC9D,oEAAoE;QACpE,qEAAqE;QACrE,4BAA4B;QAC5B,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,KAAK,IAAI,UAAU,CAAC,CAAC,CAAC;QACrG,QAAQ,EAAE,SAAS,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;YAClC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;YAC/E,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,WAAW,CAAC;gBACpG,OAAO,EAAE,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC;aACnD,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAmC,CAAC"}
1
+ {"version":3,"file":"form-file.js","sourceRoot":"","sources":["../src/form-file.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,SAAS,EAAyC,gBAAgB,EAAE,mBAAmB,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAC3K,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAO1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,SAAS,QAAQ,CAAC,IAAmB;IAC3D,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,WAAW;QACjB,8DAA8D;QAC9D,oEAAoE;QACpE,qEAAqE;QACrE,4BAA4B;QAC5B,2EAA2E;QAC3E,yEAAyE;QACzE,6EAA6E;QAC7E,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE;YACxB,IAAI,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,KAAK,KAAK;gBAAE,OAAO,KAAK,CAAC;YAC9D,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;YACjC,IAAI,YAAY,CAAC,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC,CAAC,mBAAmB;YACrD,wDAAwD;YACxD,IAAI,OAAO,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,KAAK,QAAQ;gBAAE,OAAO,KAAK,CAAC;YAC3E,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC;QACD,QAAQ,EAAE,SAAS,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;YAClC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;YAC/E,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,WAAW,CAAC;gBACpG,OAAO,EAAE,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC;aACnD,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAmC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"form-file.d.ts","sourceRoot":"","sources":["../../src/templates/form-file.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAa,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAMjE,OAAO,EAAE,KAAK,aAAa,EAA2C,MAAM,4BAA4B,CAAC;AAqCzG,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAwF7E"}
1
+ {"version":3,"file":"form-file.d.ts","sourceRoot":"","sources":["../../src/templates/form-file.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAa,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAMjE,OAAO,EAAE,KAAK,aAAa,EAAgE,MAAM,4BAA4B,CAAC;AAwC9H,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAgG7E"}
@@ -18,7 +18,7 @@
18
18
  import { code, imp } from "ts-poet";
19
19
  import { MetaField, MetaObject } from "@metaobjectsdev/metadata";
20
20
  import { IDENTITY_SUBTYPE_PRIMARY, IDENTITY_ATTR_FIELDS, FIELD_ATTR_DEFAULT, } from "@metaobjectsdev/metadata";
21
- import { entityModuleSpecifier, GENERATED_HEADER } from "@metaobjectsdev/codegen-ts";
21
+ import { entityModuleSpecifier, GENERATED_HEADER, tphDiscriminatorPin } from "@metaobjectsdev/codegen-ts";
22
22
  function primaryFieldNames(entity) {
23
23
  const set = new Set();
24
24
  // identities() returns effective identities, so inherited identities (from extends:/super:) are included.
@@ -42,8 +42,10 @@ function isAutoManaged(field) {
42
42
  }
43
43
  return false;
44
44
  }
45
- /** Visible form fields = all fields minus PK and DB-auto-defaulted. */
46
- function visibleFields(entity) {
45
+ /** Visible form fields = all fields minus PK, DB-auto-defaulted, and (for a TPH
46
+ * subtype) the discriminator — which is implicit (the form is subtype-specific)
47
+ * and injected server-side, never rendered. */
48
+ function visibleFields(entity, discField) {
47
49
  const pkNames = primaryFieldNames(entity);
48
50
  const names = [];
49
51
  // fields() returns effective fields, so inherited fields (from extends:/super:) are included in forms.
@@ -54,6 +56,8 @@ function visibleFields(entity) {
54
56
  continue;
55
57
  if (isAutoManaged(child))
56
58
  continue;
59
+ if (discField !== undefined && child.name === discField)
60
+ continue;
57
61
  names.push(child.name);
58
62
  }
59
63
  return names;
@@ -63,7 +67,15 @@ export function renderFormFile(entity, ctx) {
63
67
  // Import the entity's own file. Same target → relative "./Entity"; cross
64
68
  // target → importBase-qualified package path.
65
69
  const entityFileSpec = entityModuleSpecifier(ctx.selfTarget, ctx.entityModuleTarget, entity.package, entityName, ctx.extStyle);
66
- const fields = visibleFields(entity);
70
+ // FR-017 Tier 3: a TPH subtype's form omits the discriminator field (it's
71
+ // implicit — the form is subtype-specific — and injected server-side). The
72
+ // create schema correspondingly drops the pinned literal so RHF validation
73
+ // doesn't require a field the user never fills.
74
+ const tphPin = tphDiscriminatorPin(entity);
75
+ const fields = visibleFields(entity, tphPin?.fieldName);
76
+ const formSchema = tphPin !== undefined
77
+ ? `${entityName}InsertSchema.omit({ ${tphPin.fieldName}: true })`
78
+ : `${entityName}InsertSchema`;
67
79
  const ReactElementSym = imp("t:ReactElement@react");
68
80
  const SubmitHandlerSym = imp("t:SubmitHandler@react-hook-form");
69
81
  const useEntityFormSym = imp("useEntityForm@@metaobjectsdev/react");
@@ -110,7 +122,7 @@ export interface ${entityName}FormProps {
110
122
  export function ${entityName}Form(props: ${entityName}FormProps): ${ReactElementSym} {
111
123
  const form = ${useEntityFormSym}(
112
124
  ${entityName},
113
- ${entityName}InsertSchema,
125
+ ${formSchema},
114
126
  props.defaultValues !== undefined ? { defaultValues: props.defaultValues } : {},
115
127
  );
116
128
  return (
@@ -1 +1 @@
1
- {"version":3,"file":"form-file.js","sourceRoot":"","sources":["../../src/templates/form-file.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"form-file.js","sourceRoot":"","sources":["../../src/templates/form-file.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/codegen-ts-react",
3
- "version": "0.9.0",
3
+ "version": "0.11.0-rc.1",
4
4
  "description": "React codegen for metaobjects — emits <Entity>.form.tsx files using react-hook-form and @metaobjectsdev/react helpers.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,7 +12,12 @@
12
12
  "default": "./dist/index.js"
13
13
  }
14
14
  },
15
- "files": ["dist", "src", "README.md", "LICENSE"],
15
+ "files": [
16
+ "dist",
17
+ "src",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
16
21
  "scripts": {
17
22
  "build": "tsc -p .",
18
23
  "typecheck": "tsc -p tsconfig.typecheck.json",
@@ -22,28 +27,34 @@
22
27
  "author": "Doug Mealing <doug@dougmealing.com>",
23
28
  "homepage": "https://metaobjects.dev",
24
29
  "bugs": {
25
- "url": "https://github.com/metaobjectsdev/metaobjects/issues"
26
- },
27
- "repository": {
28
- "type": "git",
29
- "url": "https://github.com/metaobjectsdev/metaobjects.git",
30
- "directory": "server/typescript/packages/codegen-ts-react"
31
- },
32
- "keywords": ["metaobjects", "codegen", "react", "forms", "react-hook-form"],
33
- "publishConfig": {
34
- "access": "public"
35
- },
36
- "dependencies": {
37
- "@metaobjectsdev/metadata": "0.9.0",
38
- "@metaobjectsdev/codegen-ts": "0.9.0",
39
- "ts-poet": "^6.10.0"
40
- },
41
- "peerDependencies": {
42
- "@biomejs/biome": ">=1.9.0"
43
- },
44
- "devDependencies": {
45
- "@biomejs/biome": "^1.9.0",
46
- "bun-types": "latest",
47
- "typescript": "^5.6.0"
48
- }
30
+ "url": "https://github.com/metaobjectsdev/metaobjects/issues"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/metaobjectsdev/metaobjects.git",
35
+ "directory": "server/typescript/packages/codegen-ts-react"
36
+ },
37
+ "keywords": [
38
+ "metaobjects",
39
+ "codegen",
40
+ "react",
41
+ "forms",
42
+ "react-hook-form"
43
+ ],
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "dependencies": {
48
+ "@metaobjectsdev/metadata": "0.11.0-rc.1",
49
+ "@metaobjectsdev/codegen-ts": "0.11.0-rc.1",
50
+ "ts-poet": "^6.10.0"
51
+ },
52
+ "peerDependencies": {
53
+ "@biomejs/biome": ">=1.9.0"
54
+ },
55
+ "devDependencies": {
56
+ "@biomejs/biome": "^1.9.0",
57
+ "bun-types": "latest",
58
+ "typescript": "^5.6.0"
59
+ }
49
60
  }
package/src/form-file.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { MetaObject } from "@metaobjectsdev/metadata";
2
- import { perEntity, type Generator, type GeneratorFactory, entityOutputPath, emitsWriteArtifacts } from "@metaobjectsdev/codegen-ts";
2
+ import { OBJECT_ATTR_DISCRIMINATOR } from "@metaobjectsdev/metadata";
3
+ import { perEntity, type Generator, type GeneratorFactory, entityOutputPath, emitsWriteArtifacts, isTphSubtype, CODEGEN_ATTR_EMIT_FORM } from "@metaobjectsdev/codegen-ts";
3
4
  import { renderFormFile } from "./templates/form-file.js";
4
5
 
5
6
  export interface FormFileOpts {
@@ -20,7 +21,17 @@ export const formFile = function formFile(opts?: FormFileOpts): Generator {
20
21
  // (skips abstract types — no instance — and read-only projections —
21
22
  // instantiable for read, never for write), the metadata opt-out, and
22
23
  // the optional user filter.
23
- filter: (e: MetaObject) => emitsWriteArtifacts(e) && e.ownAttr("emitForm") !== false && userFilter(e),
24
+ // FR-017 Tier 3: forms are ALWAYS per-subtype. The discriminator base gets
25
+ // NO form (you can't create an abstract base), but each concrete subtype
26
+ // does — even though it has no own writable source (it inherits the base's).
27
+ filter: (e: MetaObject) => {
28
+ if (e.ownAttr(CODEGEN_ATTR_EMIT_FORM) === false) return false;
29
+ if (!userFilter(e)) return false;
30
+ if (isTphSubtype(e)) return true; // per-subtype form
31
+ // A discriminator base is never form-rendered directly.
32
+ if (typeof e.ownAttr(OBJECT_ATTR_DISCRIMINATOR) === "string") return false;
33
+ return emitsWriteArtifacts(e);
34
+ },
24
35
  generate: perEntity((entity, ctx) => {
25
36
  if (!ctx.renderContext) {
26
37
  throw new Error("form-file: renderContext is required (provided by runGen)");
@@ -23,7 +23,7 @@ import {
23
23
  IDENTITY_ATTR_FIELDS,
24
24
  FIELD_ATTR_DEFAULT,
25
25
  } from "@metaobjectsdev/metadata";
26
- import { type RenderContext, entityModuleSpecifier, GENERATED_HEADER } from "@metaobjectsdev/codegen-ts";
26
+ import { type RenderContext, entityModuleSpecifier, GENERATED_HEADER, tphDiscriminatorPin } from "@metaobjectsdev/codegen-ts";
27
27
 
28
28
  function primaryFieldNames(entity: MetaObject): Set<string> {
29
29
  const set = new Set<string>();
@@ -46,8 +46,10 @@ function isAutoManaged(field: MetaField): boolean {
46
46
  return false;
47
47
  }
48
48
 
49
- /** Visible form fields = all fields minus PK and DB-auto-defaulted. */
50
- function visibleFields(entity: MetaObject): string[] {
49
+ /** Visible form fields = all fields minus PK, DB-auto-defaulted, and (for a TPH
50
+ * subtype) the discriminator — which is implicit (the form is subtype-specific)
51
+ * and injected server-side, never rendered. */
52
+ function visibleFields(entity: MetaObject, discField?: string): string[] {
51
53
  const pkNames = primaryFieldNames(entity);
52
54
  const names: string[] = [];
53
55
  // fields() returns effective fields, so inherited fields (from extends:/super:) are included in forms.
@@ -55,6 +57,7 @@ function visibleFields(entity: MetaObject): string[] {
55
57
  if (child.ownAttr("formExclude") === true) continue;
56
58
  if (pkNames.has(child.name)) continue;
57
59
  if (isAutoManaged(child)) continue;
60
+ if (discField !== undefined && child.name === discField) continue;
58
61
  names.push(child.name);
59
62
  }
60
63
  return names;
@@ -71,7 +74,15 @@ export function renderFormFile(entity: MetaObject, ctx: RenderContext): string {
71
74
  entityName,
72
75
  ctx.extStyle,
73
76
  );
74
- const fields = visibleFields(entity);
77
+ // FR-017 Tier 3: a TPH subtype's form omits the discriminator field (it's
78
+ // implicit — the form is subtype-specific — and injected server-side). The
79
+ // create schema correspondingly drops the pinned literal so RHF validation
80
+ // doesn't require a field the user never fills.
81
+ const tphPin = tphDiscriminatorPin(entity);
82
+ const fields = visibleFields(entity, tphPin?.fieldName);
83
+ const formSchema = tphPin !== undefined
84
+ ? `${entityName}InsertSchema.omit({ ${tphPin.fieldName}: true })`
85
+ : `${entityName}InsertSchema`;
75
86
 
76
87
  const ReactElementSym = imp("t:ReactElement@react");
77
88
  const SubmitHandlerSym = imp("t:SubmitHandler@react-hook-form");
@@ -124,7 +135,7 @@ export interface ${entityName}FormProps {
124
135
  export function ${entityName}Form(props: ${entityName}FormProps): ${ReactElementSym} {
125
136
  const form = ${useEntityFormSym}(
126
137
  ${entityName},
127
- ${entityName}InsertSchema,
138
+ ${formSchema},
128
139
  props.defaultValues !== undefined ? { defaultValues: props.defaultValues } : {},
129
140
  );
130
141
  return (