@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.
- package/dist/form-file.d.ts.map +1 -1
- package/dist/form-file.js +17 -2
- package/dist/form-file.js.map +1 -1
- package/dist/templates/form-file.d.ts.map +1 -1
- package/dist/templates/form-file.js +17 -5
- package/dist/templates/form-file.js.map +1 -1
- package/package.json +37 -26
- package/src/form-file.ts +13 -2
- package/src/templates/form-file.ts +16 -5
package/dist/form-file.d.ts.map
CHANGED
|
@@ -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;
|
|
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 {
|
|
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
|
-
|
|
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)");
|
package/dist/form-file.js.map
CHANGED
|
@@ -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;
|
|
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,
|
|
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
|
|
46
|
-
|
|
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
|
-
|
|
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
|
-
${
|
|
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":"AAAA,yEAAyE;AACzE,kEAAkE;AAClE,uEAAuE;AACvE,mDAAmD;AACnD,EAAE;AACF,qBAAqB;AACrB,0EAA0E;AAC1E,gDAAgD;AAChD,oEAAoE;AACpE,EAAE;AACF,4CAA4C;AAC5C,6CAA6C;AAC7C,qDAAqD;AACrD,2DAA2D;AAC3D,EAAE;AACF,oEAAoE;AACpE,sEAAsE;AAEtE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAsB,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;
|
|
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.
|
|
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": [
|
|
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
|
-
|
|
26
|
-
},
|
|
27
|
-
"repository": {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
},
|
|
32
|
-
"keywords": [
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"
|
|
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 {
|
|
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
|
-
|
|
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
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
${
|
|
138
|
+
${formSchema},
|
|
128
139
|
props.defaultValues !== undefined ? { defaultValues: props.defaultValues } : {},
|
|
129
140
|
);
|
|
130
141
|
return (
|