@specverse/engines 4.1.30 → 4.2.0
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/assets/examples/manifests/frontend-only.yaml +3 -6
- package/assets/examples/manifests/fullstack-app.yaml +5 -7
- package/assets/examples/manifests/fullstack-monorepo.yaml +3 -6
- package/dist/inference/comprehensive-engine.d.ts.map +1 -1
- package/dist/inference/comprehensive-engine.js +3 -19
- package/dist/inference/comprehensive-engine.js.map +1 -1
- package/dist/inference/core/rule-engine.d.ts +31 -0
- package/dist/inference/core/rule-engine.d.ts.map +1 -1
- package/dist/inference/core/rule-engine.js +117 -33
- package/dist/inference/core/rule-engine.js.map +1 -1
- package/dist/inference/core/rule-file-types.d.ts +0 -2
- package/dist/inference/core/rule-file-types.d.ts.map +1 -1
- package/dist/inference/core/rule-file-types.js +3 -6
- package/dist/inference/core/rule-file-types.js.map +1 -1
- package/dist/inference/core/rule-loader.d.ts +5 -15
- package/dist/inference/core/rule-loader.d.ts.map +1 -1
- package/dist/inference/core/rule-loader.js +43 -132
- package/dist/inference/core/rule-loader.js.map +1 -1
- package/dist/inference/core/types.d.ts +0 -6
- package/dist/inference/core/types.d.ts.map +1 -1
- package/dist/inference/core/types.js +0 -4
- package/dist/inference/core/types.js.map +1 -1
- package/dist/inference/logical/generators/component-type-resolver.d.ts +0 -26
- package/dist/inference/logical/generators/component-type-resolver.d.ts.map +1 -1
- package/dist/inference/logical/generators/component-type-resolver.js +0 -19
- package/dist/inference/logical/generators/component-type-resolver.js.map +1 -1
- package/dist/inference/logical/generators/specialist-view-expander.d.ts +1 -17
- package/dist/inference/logical/generators/specialist-view-expander.d.ts.map +1 -1
- package/dist/inference/logical/generators/specialist-view-expander.js +0 -15
- package/dist/inference/logical/generators/specialist-view-expander.js.map +1 -1
- package/dist/inference/logical/generators/view-generator.d.ts +4 -14
- package/dist/inference/logical/generators/view-generator.d.ts.map +1 -1
- package/dist/inference/logical/generators/view-generator.js +6 -26
- package/dist/inference/logical/generators/view-generator.js.map +1 -1
- package/dist/inference/logical/index.d.ts +2 -2
- package/dist/inference/logical/index.d.ts.map +1 -1
- package/dist/inference/logical/logical-engine.d.ts.map +1 -1
- package/dist/inference/logical/logical-engine.js +17 -80
- package/dist/inference/logical/logical-engine.js.map +1 -1
- package/dist/inference/quint-transpiler.d.ts +5 -3
- package/dist/inference/quint-transpiler.d.ts.map +1 -1
- package/dist/inference/quint-transpiler.js +11 -6
- package/dist/inference/quint-transpiler.js.map +1 -1
- package/dist/libs/instance-factories/applications/templates/react-starter/app-tsx-generator.js +110 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.js +121 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/detail-body-composer.js +78 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/form-body-composer.js +190 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/helpers-emitter.js +45 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/html-to-jsx.js +192 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/list-body-composer.js +46 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/orchestrator.js +30 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/package-json-generator.js +38 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/regen-safety.js +89 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/view-emitter.js +56 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/views-generator.js +66 -0
- package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +14 -11
- package/dist/realize/index.d.ts.map +1 -1
- package/dist/realize/index.js +15 -22
- package/dist/realize/index.js.map +1 -1
- package/dist/registry/utils/manifest-adapter.d.ts +8 -1
- package/dist/registry/utils/manifest-adapter.d.ts.map +1 -1
- package/dist/registry/utils/manifest-adapter.js +8 -1
- package/dist/registry/utils/manifest-adapter.js.map +1 -1
- package/libs/instance-factories/applications/react-app-starter.yaml +150 -0
- package/libs/instance-factories/applications/templates/react-starter/README.md +211 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/dashboard-body-composer.test.ts +153 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/detail-body-composer.test.ts +145 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/form-body-composer.test.ts +175 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/helpers-emitter.test.ts +55 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/html-to-jsx.test.ts +140 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/list-body-composer.test.ts +146 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/orchestrator.test.ts +163 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/parity-p2-factory-imports.test.ts +116 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/parity-p3-rendered-output.test.ts +183 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/regen-safety.test.ts +144 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/starter-generators.test.ts +114 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/view-emitter.test.ts +107 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/views-generator.test.ts +139 -0
- package/libs/instance-factories/applications/templates/react-starter/app-tsx-generator.ts +141 -0
- package/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.ts +174 -0
- package/libs/instance-factories/applications/templates/react-starter/detail-body-composer.ts +135 -0
- package/libs/instance-factories/applications/templates/react-starter/form-body-composer.ts +306 -0
- package/libs/instance-factories/applications/templates/react-starter/helpers-emitter.ts +60 -0
- package/libs/instance-factories/applications/templates/react-starter/html-to-jsx.ts +334 -0
- package/libs/instance-factories/applications/templates/react-starter/list-body-composer.ts +120 -0
- package/libs/instance-factories/applications/templates/react-starter/orchestrator.ts +80 -0
- package/libs/instance-factories/applications/templates/react-starter/package-json-generator.ts +57 -0
- package/libs/instance-factories/applications/templates/react-starter/regen-safety.ts +157 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/dashboard.tsx.template +47 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/detail.tsx.template +94 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/form.tsx.template +114 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/list.tsx.template +72 -0
- package/libs/instance-factories/applications/templates/react-starter/view-emitter.ts +151 -0
- package/libs/instance-factories/applications/templates/react-starter/views-generator.ts +137 -0
- package/libs/instance-factories/cli/templates/commander/command-generator.ts +14 -11
- package/package.json +3 -3
- package/dist/libs/instance-factories/applications/templates/react/_view-components-source.js +0 -530
- package/dist/libs/instance-factories/applications/templates/react/app-tsx-generator.js +0 -73
- package/dist/libs/instance-factories/applications/templates/react/field-helpers-generator.js +0 -99
- package/dist/libs/instance-factories/applications/templates/react/package-json-generator.js +0 -49
- package/dist/libs/instance-factories/applications/templates/react/pattern-adapter-generator.js +0 -156
- package/dist/libs/instance-factories/applications/templates/react/react-pattern-adapter.js +0 -935
- package/dist/libs/instance-factories/applications/templates/react/relationship-field-generator.js +0 -143
- package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.js +0 -646
- package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.js +0 -65
- package/dist/libs/instance-factories/applications/templates/react/view-dashboard-generator.js +0 -143
- package/dist/libs/instance-factories/applications/templates/react/view-detail-generator.js +0 -143
- package/dist/libs/instance-factories/applications/templates/react/view-form-generator.js +0 -355
- package/dist/libs/instance-factories/applications/templates/react/view-list-generator.js +0 -91
- package/dist/libs/instance-factories/applications/templates/react/view-router-generator.js +0 -79
- package/dist/libs/instance-factories/views/index.js +0 -48
- package/dist/libs/instance-factories/views/templates/react/adapters/antd-adapter.js +0 -742
- package/dist/libs/instance-factories/views/templates/react/adapters/mui-adapter.js +0 -824
- package/dist/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.js +0 -719
- package/dist/libs/instance-factories/views/templates/react/app-generator.js +0 -45
- package/dist/libs/instance-factories/views/templates/react/components-generator.js +0 -820
- package/dist/libs/instance-factories/views/templates/react/forms-generator.js +0 -275
- package/dist/libs/instance-factories/views/templates/react/frontend-package-json-generator.js +0 -46
- package/dist/libs/instance-factories/views/templates/react/hooks-generator.js +0 -81
- package/dist/libs/instance-factories/views/templates/react/index-css-generator.js +0 -9
- package/dist/libs/instance-factories/views/templates/react/index-html-generator.js +0 -23
- package/dist/libs/instance-factories/views/templates/react/main-tsx-generator.js +0 -21
- package/dist/libs/instance-factories/views/templates/react/react-component-generator.js +0 -299
- package/dist/libs/instance-factories/views/templates/react/router-generator.js +0 -136
- package/dist/libs/instance-factories/views/templates/react/router-generic-generator.js +0 -107
- package/dist/libs/instance-factories/views/templates/react/shared-utils-generator.js +0 -187
- package/dist/libs/instance-factories/views/templates/react/spec-json-generator.js +0 -7
- package/dist/libs/instance-factories/views/templates/react/types-generator.js +0 -56
- package/dist/libs/instance-factories/views/templates/react/views-metadata-generator.js +0 -27
- package/dist/libs/instance-factories/views/templates/react/vite-config-generator.js +0 -29
- package/dist/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js +0 -261
- package/dist/libs/instance-factories/views/templates/shared/adapter-types.js +0 -34
- package/dist/libs/instance-factories/views/templates/shared/atomic-components-registry.js +0 -800
- package/dist/libs/instance-factories/views/templates/shared/base-generator.js +0 -305
- package/dist/libs/instance-factories/views/templates/shared/component-metadata.js +0 -517
- package/dist/libs/instance-factories/views/templates/shared/composite-pattern-types.js +0 -0
- package/dist/libs/instance-factories/views/templates/shared/composite-patterns.js +0 -445
- package/dist/libs/instance-factories/views/templates/shared/index.js +0 -80
- package/dist/libs/instance-factories/views/templates/shared/pattern-validator.js +0 -210
- package/dist/libs/instance-factories/views/templates/shared/property-mapper.js +0 -492
- package/dist/libs/instance-factories/views/templates/shared/syntax-mapper.js +0 -321
- package/dist/realize/index.js.bak +0 -758
- package/libs/instance-factories/applications/react-app.yaml +0 -186
- package/libs/instance-factories/applications/templates/react/_view-components-source.ts +0 -555
- package/libs/instance-factories/applications/templates/react/app-tsx-generator.ts +0 -94
- package/libs/instance-factories/applications/templates/react/field-helpers-generator.ts +0 -106
- package/libs/instance-factories/applications/templates/react/package-json-generator.ts +0 -57
- package/libs/instance-factories/applications/templates/react/pattern-adapter-generator.ts +0 -179
- package/libs/instance-factories/applications/templates/react/react-pattern-adapter.tsx +0 -1347
- package/libs/instance-factories/applications/templates/react/relationship-field-generator.ts +0 -150
- package/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.ts +0 -704
- package/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.ts +0 -84
- package/libs/instance-factories/applications/templates/react/view-dashboard-generator.ts +0 -150
- package/libs/instance-factories/applications/templates/react/view-detail-generator.ts +0 -150
- package/libs/instance-factories/applications/templates/react/view-form-generator.ts +0 -362
- package/libs/instance-factories/applications/templates/react/view-list-generator.ts +0 -98
- package/libs/instance-factories/applications/templates/react/view-router-generator.ts +0 -89
- package/libs/instance-factories/views/README.md +0 -62
- package/libs/instance-factories/views/index.d.ts +0 -13
- package/libs/instance-factories/views/index.d.ts.map +0 -1
- package/libs/instance-factories/views/index.js +0 -18
- package/libs/instance-factories/views/index.js.map +0 -1
- package/libs/instance-factories/views/index.ts +0 -45
- package/libs/instance-factories/views/react-components.yaml +0 -129
- package/libs/instance-factories/views/templates/ARCHITECTURE.md +0 -198
- package/libs/instance-factories/views/templates/react/adapters/antd-adapter.ts +0 -869
- package/libs/instance-factories/views/templates/react/adapters/mui-adapter.ts +0 -953
- package/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.ts +0 -806
- package/libs/instance-factories/views/templates/react/app-generator.ts +0 -55
- package/libs/instance-factories/views/templates/react/components-generator.ts +0 -938
- package/libs/instance-factories/views/templates/react/forms-generator.ts +0 -325
- package/libs/instance-factories/views/templates/react/frontend-package-json-generator.ts +0 -57
- package/libs/instance-factories/views/templates/react/hooks-generator.ts +0 -106
- package/libs/instance-factories/views/templates/react/index-css-generator.ts +0 -14
- package/libs/instance-factories/views/templates/react/index-html-generator.ts +0 -34
- package/libs/instance-factories/views/templates/react/main-tsx-generator.ts +0 -29
- package/libs/instance-factories/views/templates/react/react-component-generator.d.ts +0 -152
- package/libs/instance-factories/views/templates/react/react-component-generator.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/react/react-component-generator.js +0 -398
- package/libs/instance-factories/views/templates/react/react-component-generator.js.map +0 -1
- package/libs/instance-factories/views/templates/react/react-component-generator.ts +0 -533
- package/libs/instance-factories/views/templates/react/router-generator.ts +0 -197
- package/libs/instance-factories/views/templates/react/router-generic-generator.ts +0 -132
- package/libs/instance-factories/views/templates/react/shared-utils-generator.ts +0 -196
- package/libs/instance-factories/views/templates/react/spec-json-generator.ts +0 -17
- package/libs/instance-factories/views/templates/react/types-generator.ts +0 -76
- package/libs/instance-factories/views/templates/react/views-metadata-generator.ts +0 -42
- package/libs/instance-factories/views/templates/react/vite-config-generator.ts +0 -38
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js.map +0 -1
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.ts +0 -474
- package/libs/instance-factories/views/templates/shared/__tests__/composite-patterns.test.ts +0 -242
- package/libs/instance-factories/views/templates/shared/adapter-types.d.ts +0 -77
- package/libs/instance-factories/views/templates/shared/adapter-types.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/adapter-types.js +0 -47
- package/libs/instance-factories/views/templates/shared/adapter-types.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/adapter-types.ts +0 -142
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts +0 -63
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.js +0 -822
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.ts +0 -908
- package/libs/instance-factories/views/templates/shared/base-generator.d.ts +0 -247
- package/libs/instance-factories/views/templates/shared/base-generator.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/base-generator.js +0 -363
- package/libs/instance-factories/views/templates/shared/base-generator.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/base-generator.ts +0 -608
- package/libs/instance-factories/views/templates/shared/component-metadata.d.ts +0 -254
- package/libs/instance-factories/views/templates/shared/component-metadata.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/component-metadata.js +0 -602
- package/libs/instance-factories/views/templates/shared/component-metadata.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/component-metadata.ts +0 -803
- package/libs/instance-factories/views/templates/shared/composite-pattern-types.ts +0 -250
- package/libs/instance-factories/views/templates/shared/composite-patterns.ts +0 -535
- package/libs/instance-factories/views/templates/shared/index.ts +0 -68
- package/libs/instance-factories/views/templates/shared/pattern-validator.ts +0 -279
- package/libs/instance-factories/views/templates/shared/property-mapper.d.ts +0 -149
- package/libs/instance-factories/views/templates/shared/property-mapper.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/property-mapper.js +0 -580
- package/libs/instance-factories/views/templates/shared/property-mapper.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/property-mapper.ts +0 -700
- package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts +0 -143
- package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/syntax-mapper.js +0 -420
- package/libs/instance-factories/views/templates/shared/syntax-mapper.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/syntax-mapper.ts +0 -539
package/libs/instance-factories/applications/templates/react-starter/skeletons/list.tsx.template
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{MODEL_NAME}}ListView — generated by @specverse/realize (ReactAppStarter)
|
|
3
|
+
*
|
|
4
|
+
* Safe to edit. Edits are preserved across regeneration via content
|
|
5
|
+
* hashing (see .specverse-gen/hashes.json). To accept an upstream
|
|
6
|
+
* regeneration of this file, delete it first, then run `spv realize`.
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useMemo } from 'react';
|
|
9
|
+
import { use{{PLURAL_MODEL}}Query, useDelete{{MODEL_NAME}}Mutation } from '../hooks/useApi';
|
|
10
|
+
import type { {{MODEL_NAME}} } from '../types/api';
|
|
11
|
+
|
|
12
|
+
interface {{MODEL_NAME}}ListViewProps {
|
|
13
|
+
onSelect?: (item: {{MODEL_NAME}}) => void;
|
|
14
|
+
onCreate?: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function {{MODEL_NAME}}ListView({ onSelect, onCreate }: {{MODEL_NAME}}ListViewProps) {
|
|
18
|
+
const { data: items = [], isLoading, error } = use{{PLURAL_MODEL}}Query();
|
|
19
|
+
const deleteItem = useDelete{{MODEL_NAME}}Mutation();
|
|
20
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
21
|
+
|
|
22
|
+
const filtered = useMemo(
|
|
23
|
+
() =>
|
|
24
|
+
items.filter((item: {{MODEL_NAME}}) =>
|
|
25
|
+
// Cast to `any` inside JSX context — `Record<string, unknown>`
|
|
26
|
+
// looks like a JSX opening tag to the TSX parser when this
|
|
27
|
+
// expression ends up inside JSX. `any` is safe here: we only
|
|
28
|
+
// read values for substring-matching against the search term.
|
|
29
|
+
Object.values(item as any).some(v =>
|
|
30
|
+
String(v ?? '').toLowerCase().includes(searchTerm.toLowerCase())
|
|
31
|
+
)
|
|
32
|
+
),
|
|
33
|
+
[items, searchTerm]
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
if (isLoading) return <div className="p-4 text-gray-500">Loading {{PLURAL_LOWER}}…</div>;
|
|
37
|
+
if (error) return <div className="p-4 text-red-600">Error loading {{PLURAL_LOWER}}: {String(error)}</div>;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className="p-6 space-y-4">
|
|
41
|
+
<div className="flex items-center justify-between">
|
|
42
|
+
<input
|
|
43
|
+
type="search"
|
|
44
|
+
placeholder="Search {{PLURAL_LOWER}}…"
|
|
45
|
+
value={searchTerm}
|
|
46
|
+
onChange={e => setSearchTerm(e.target.value)}
|
|
47
|
+
className="w-64 rounded border border-gray-300 px-3 py-2 text-sm"
|
|
48
|
+
/>
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
51
|
+
onClick={onCreate}
|
|
52
|
+
className="rounded bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700"
|
|
53
|
+
>
|
|
54
|
+
+ New {{MODEL_NAME}}
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
{/* Pattern-rendered list body (table + rows). Edit freely. */}
|
|
59
|
+
{{BODY}}
|
|
60
|
+
|
|
61
|
+
{filtered.length === 0 && (
|
|
62
|
+
<div className="p-8 text-center text-gray-400">No {{PLURAL_LOWER}} yet.</div>
|
|
63
|
+
)}
|
|
64
|
+
|
|
65
|
+
{deleteItem.isError && (
|
|
66
|
+
<div className="p-2 text-sm text-red-600">
|
|
67
|
+
Delete failed: {String(deleteItem.error)}
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReactAppStarter view emitter
|
|
3
|
+
*
|
|
4
|
+
* Given a view spec + model spec + expanded spec, emits a complete
|
|
5
|
+
* idiomatic React component source file as a string.
|
|
6
|
+
*
|
|
7
|
+
* Strategy 3 ("skeleton + rendered interior"): a hand-written
|
|
8
|
+
* skeleton template provides the idiomatic React outer structure
|
|
9
|
+
* (imports, hooks, props, layout). The canonical Tailwind adapter
|
|
10
|
+
* renders the pattern's interior as HTML. The html-to-jsx transformer
|
|
11
|
+
* converts the HTML to JSX. The emitter composes these.
|
|
12
|
+
*
|
|
13
|
+
* See README.md in this directory for the architecture.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readFileSync } from 'fs';
|
|
17
|
+
import { join, dirname } from 'path';
|
|
18
|
+
import { fileURLToPath } from 'url';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The minimal shape of a view spec this emitter needs. Matches the
|
|
22
|
+
* inferred spec format — `view.type` drives skeleton selection,
|
|
23
|
+
* `view.model` identifies the primary model, `view.uiComponents` (if
|
|
24
|
+
* present) overrides inference defaults.
|
|
25
|
+
*/
|
|
26
|
+
export interface ViewSpec {
|
|
27
|
+
type: string;
|
|
28
|
+
model?: string;
|
|
29
|
+
uiComponents?: Record<string, unknown>;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The minimal shape of a model spec this emitter needs. Field details
|
|
35
|
+
* flow through to the Tailwind adapter which decides column selection.
|
|
36
|
+
*/
|
|
37
|
+
export interface ModelSpec {
|
|
38
|
+
name: string;
|
|
39
|
+
attributes: Record<string, unknown>;
|
|
40
|
+
relationships?: Record<string, unknown>;
|
|
41
|
+
lifecycles?: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface EmitContext {
|
|
45
|
+
view: ViewSpec;
|
|
46
|
+
viewName: string; // e.g. "PostListView"
|
|
47
|
+
model: ModelSpec;
|
|
48
|
+
modelSchemas: Record<string, ModelSpec>;
|
|
49
|
+
/** Pluggable so we can stub the renderer in unit tests. */
|
|
50
|
+
renderBody: RenderBodyFn;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Render the body of a view as JSX-safe source — the exact string
|
|
55
|
+
* that is dropped at `{{BODY}}` in the skeleton.
|
|
56
|
+
*
|
|
57
|
+
* The renderBody implementation is responsible for the whole body
|
|
58
|
+
* pipeline: invoke the canonical Tailwind adapter for static shell
|
|
59
|
+
* rendering, run that HTML through `htmlToJsx`, and inject any JSX
|
|
60
|
+
* expressions (`.map()`, event handlers). Keeping it here means
|
|
61
|
+
* view-emitter stays a pure orchestrator and can't accidentally
|
|
62
|
+
* double-transform JSX that's already JSX.
|
|
63
|
+
*/
|
|
64
|
+
export type RenderBodyFn = (context: EmitContext) => string;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Emit a complete .tsx file for a single view. Pure function — all
|
|
68
|
+
* inputs come in via the context argument so it's easy to test.
|
|
69
|
+
*/
|
|
70
|
+
export function emitView(context: EmitContext): string {
|
|
71
|
+
const skeleton = loadSkeleton(context.view.type);
|
|
72
|
+
const bodyJsx = context.renderBody(context);
|
|
73
|
+
|
|
74
|
+
const substitutions = buildSubstitutions(context, bodyJsx);
|
|
75
|
+
return applySubstitutions(skeleton, substitutions);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
79
|
+
// Skeleton loading
|
|
80
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
const SKELETON_BY_VIEW_TYPE: Record<string, string> = {
|
|
83
|
+
list: 'list.tsx.template',
|
|
84
|
+
detail: 'detail.tsx.template',
|
|
85
|
+
form: 'form.tsx.template',
|
|
86
|
+
dashboard: 'dashboard.tsx.template',
|
|
87
|
+
// Specialist types (board, timeline, calendar, analytics, workflow,
|
|
88
|
+
// wizard, comparison, settings, map, feed, profile) come online in
|
|
89
|
+
// Phase 2e.
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function loadSkeleton(viewType: string): string {
|
|
93
|
+
const filename = SKELETON_BY_VIEW_TYPE[viewType.toLowerCase()];
|
|
94
|
+
if (!filename) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`No skeleton registered for view type "${viewType}". ` +
|
|
97
|
+
`Known types: ${Object.keys(SKELETON_BY_VIEW_TYPE).join(', ')}.`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
102
|
+
const path = join(here, 'skeletons', filename);
|
|
103
|
+
return readFileSync(path, 'utf8');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
107
|
+
// Substitution
|
|
108
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
interface Substitutions {
|
|
111
|
+
MODEL_NAME: string;
|
|
112
|
+
PLURAL_MODEL: string;
|
|
113
|
+
PLURAL_LOWER: string;
|
|
114
|
+
SINGULAR_LOWER: string;
|
|
115
|
+
BODY: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildSubstitutions(context: EmitContext, body: string): Substitutions {
|
|
119
|
+
const modelName = context.model.name;
|
|
120
|
+
const pluralModel = pluralize(modelName);
|
|
121
|
+
return {
|
|
122
|
+
MODEL_NAME: modelName,
|
|
123
|
+
PLURAL_MODEL: pluralModel,
|
|
124
|
+
PLURAL_LOWER: pluralModel.toLowerCase(),
|
|
125
|
+
SINGULAR_LOWER: modelName.toLowerCase(),
|
|
126
|
+
BODY: body,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function applySubstitutions(template: string, subs: Substitutions): string {
|
|
131
|
+
let out = template;
|
|
132
|
+
for (const [key, value] of Object.entries(subs)) {
|
|
133
|
+
// Replace every occurrence of {{KEY}} — plain string substitution,
|
|
134
|
+
// no expression evaluation. Escape regex special chars in the key
|
|
135
|
+
// (defensive; keys are ASCII constants).
|
|
136
|
+
const pattern = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
|
|
137
|
+
out = out.replace(pattern, value);
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Minimal English pluralizer matching the adapter's conventions.
|
|
144
|
+
* Good enough for generated code — users can edit the output if they
|
|
145
|
+
* want a different pluralization.
|
|
146
|
+
*/
|
|
147
|
+
function pluralize(s: string): string {
|
|
148
|
+
if (/[^aeiou]y$/i.test(s)) return s.slice(0, -1) + 'ies';
|
|
149
|
+
if (/(s|x|z|ch|sh)$/i.test(s)) return s + 'es';
|
|
150
|
+
return s + 's';
|
|
151
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View files generator for ReactAppStarter
|
|
3
|
+
*
|
|
4
|
+
* Called by the realize engine. Iterates over the expanded spec's
|
|
5
|
+
* models × view types, dispatches to the right composer, and returns
|
|
6
|
+
* a filename → source map. Realize writes each file.
|
|
7
|
+
*
|
|
8
|
+
* Also emits the local helpers library (src/lib/entity-display.ts)
|
|
9
|
+
* so generated projects have zero @specverse/runtime runtime deps.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { emitView, type EmitContext, type ModelSpec, type ViewSpec, type RenderBodyFn } from './view-emitter.js';
|
|
13
|
+
import { composeListBody } from './list-body-composer.js';
|
|
14
|
+
import { composeDetailBody } from './detail-body-composer.js';
|
|
15
|
+
import { composeFormBody } from './form-body-composer.js';
|
|
16
|
+
import { composeDashboardBody } from './dashboard-body-composer.js';
|
|
17
|
+
import { emitEntityDisplay } from './helpers-emitter.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The four primary view types Factory B currently emits. Specialist
|
|
21
|
+
* types (board, timeline, calendar, etc.) are not yet implemented —
|
|
22
|
+
* if the expanded spec declares one, we skip it and log a TODO.
|
|
23
|
+
* These users can add composers under Phase 2e.
|
|
24
|
+
*/
|
|
25
|
+
const PRIMARY_VIEW_TYPES = ['list', 'detail', 'form', 'dashboard'] as const;
|
|
26
|
+
type PrimaryViewType = typeof PRIMARY_VIEW_TYPES[number];
|
|
27
|
+
|
|
28
|
+
const COMPOSER_BY_TYPE: Record<PrimaryViewType, RenderBodyFn> = {
|
|
29
|
+
list: composeListBody,
|
|
30
|
+
detail: composeDetailBody,
|
|
31
|
+
form: composeFormBody,
|
|
32
|
+
dashboard: composeDashboardBody,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** Generator contract — the shape realize expects. */
|
|
36
|
+
export interface ViewsGeneratorContext {
|
|
37
|
+
/** The expanded spec (post-inference). */
|
|
38
|
+
spec: ExpandedSpec;
|
|
39
|
+
/** The resolved manifest configuration. Not currently read by this
|
|
40
|
+
* generator but passed by realize for parity with other generators. */
|
|
41
|
+
manifest?: unknown;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ExpandedSpec {
|
|
45
|
+
/** Models keyed by name. */
|
|
46
|
+
models?: Record<string, ModelSpec>;
|
|
47
|
+
/** Views keyed by view name (e.g. "PostListView"). */
|
|
48
|
+
views?: Record<string, ViewSpec & { type: string; model?: string }>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface GeneratedFiles {
|
|
52
|
+
[relativePath: string]: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Generate all view files + local helpers.
|
|
57
|
+
*
|
|
58
|
+
* Paths are RELATIVE to the frontend root (`frontendDir` in the
|
|
59
|
+
* manifest). Realize prepends the absolute output path when writing.
|
|
60
|
+
*/
|
|
61
|
+
export async function generate(context: ViewsGeneratorContext): Promise<GeneratedFiles> {
|
|
62
|
+
const files: GeneratedFiles = {};
|
|
63
|
+
const spec = context.spec;
|
|
64
|
+
const models = spec.models ?? {};
|
|
65
|
+
const viewsBySpec = spec.views ?? {};
|
|
66
|
+
|
|
67
|
+
// Group the spec's views by model+type so we can decide, for each
|
|
68
|
+
// (model, type) pair, whether to use the user's view spec or emit
|
|
69
|
+
// a default.
|
|
70
|
+
const viewIndex = indexViews(viewsBySpec);
|
|
71
|
+
const skippedSpecialists: string[] = [];
|
|
72
|
+
|
|
73
|
+
for (const [modelName, model] of Object.entries(models)) {
|
|
74
|
+
for (const viewType of PRIMARY_VIEW_TYPES) {
|
|
75
|
+
const viewName = `${modelName}${capitalize(viewType)}View`;
|
|
76
|
+
// Prefer a user-defined view of that shape if one exists; fall
|
|
77
|
+
// back to a synthesized default.
|
|
78
|
+
const view = viewIndex.get(viewName) ?? synthesizeDefault(modelName, viewType);
|
|
79
|
+
const source = emitView({
|
|
80
|
+
view,
|
|
81
|
+
viewName,
|
|
82
|
+
model,
|
|
83
|
+
modelSchemas: models,
|
|
84
|
+
renderBody: COMPOSER_BY_TYPE[viewType],
|
|
85
|
+
});
|
|
86
|
+
files[`src/views/${viewName}.tsx`] = source;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Surface any specialist-type views the user declared but we can't
|
|
91
|
+
// emit yet. The runtime (ReactAppRuntime) handles these; in starter
|
|
92
|
+
// kit mode the user has to write them manually for now.
|
|
93
|
+
for (const [name, view] of Object.entries(viewsBySpec)) {
|
|
94
|
+
if (!PRIMARY_VIEW_TYPES.includes(view.type as PrimaryViewType)) {
|
|
95
|
+
skippedSpecialists.push(`${name} (type=${view.type})`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (skippedSpecialists.length > 0) {
|
|
99
|
+
console.warn(
|
|
100
|
+
`[ReactAppStarter] Skipped ${skippedSpecialists.length} specialist view(s) — ` +
|
|
101
|
+
`implement composers for: ${[...new Set(Object.values(viewsBySpec)
|
|
102
|
+
.filter(v => !PRIMARY_VIEW_TYPES.includes(v.type as PrimaryViewType))
|
|
103
|
+
.map(v => v.type))].join(', ')}. ` +
|
|
104
|
+
`Skipped: ${skippedSpecialists.join(', ')}`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Local helpers
|
|
109
|
+
files['src/lib/entity-display.ts'] = emitEntityDisplay();
|
|
110
|
+
|
|
111
|
+
return files;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
115
|
+
// Helpers
|
|
116
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
function indexViews(
|
|
119
|
+
views: Record<string, ViewSpec & { type: string; model?: string }>
|
|
120
|
+
): Map<string, ViewSpec & { type: string; model?: string }> {
|
|
121
|
+
const out = new Map<string, ViewSpec & { type: string; model?: string }>();
|
|
122
|
+
for (const [name, view] of Object.entries(views)) {
|
|
123
|
+
out.set(name, view);
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function synthesizeDefault(modelName: string, viewType: PrimaryViewType): ViewSpec {
|
|
129
|
+
return {
|
|
130
|
+
type: viewType,
|
|
131
|
+
model: modelName,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function capitalize(s: string): string {
|
|
136
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
137
|
+
}
|
|
@@ -393,22 +393,25 @@ import type { ParserEngine, InferenceEngine, RealizeEngine } from '@specverse/ty
|
|
|
393
393
|
process.exit(1);
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
//
|
|
396
|
+
// --static ejects to a standalone starter kit (ReactAppStarter).
|
|
397
|
+
// Default: whatever the manifest declares (ReactAppRuntime in the
|
|
398
|
+
// shipped templates). The swap is a convenience only — users can
|
|
399
|
+
// achieve the same by editing their manifest directly.
|
|
397
400
|
let effectiveManifestPath = manifestPath;
|
|
398
|
-
if (
|
|
401
|
+
if (options.static) {
|
|
399
402
|
const manifestContent = readFileSync(manifestPath, 'utf8');
|
|
400
|
-
if (/instanceFactory:\\s*["']?
|
|
403
|
+
if (/instanceFactory:\\s*["']?ReactAppRuntime["']?\\s*$/m.test(manifestContent)) {
|
|
401
404
|
const { tmpdir } = await import('os');
|
|
402
|
-
const
|
|
403
|
-
/instanceFactory:\\s*["']?
|
|
404
|
-
'instanceFactory: "
|
|
405
|
+
const staticManifest = manifestContent.replace(
|
|
406
|
+
/instanceFactory:\\s*["']?ReactAppRuntime["']?\\s*$/gm,
|
|
407
|
+
'instanceFactory: "ReactAppStarter"'
|
|
405
408
|
);
|
|
406
|
-
effectiveManifestPath = join(tmpdir(), 'specverse-
|
|
407
|
-
writeFileSync(effectiveManifestPath,
|
|
408
|
-
console.log(' Using
|
|
409
|
+
effectiveManifestPath = join(tmpdir(), 'specverse-static-manifest.yaml');
|
|
410
|
+
writeFileSync(effectiveManifestPath, staticManifest, 'utf8');
|
|
411
|
+
console.log(' Using static mode (standalone starter kit — ReactAppStarter)');
|
|
412
|
+
} else {
|
|
413
|
+
console.log(' Using static mode (manifest factory unchanged)');
|
|
409
414
|
}
|
|
410
|
-
} else {
|
|
411
|
-
console.log(' Using static mode (full frontend generation)');
|
|
412
415
|
}
|
|
413
416
|
|
|
414
417
|
const realizeEngine = registry.getEngineForCapability('realize') as RealizeEngine;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@specverse/engines",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "SpecVerse toolchain
|
|
3
|
+
"version": "4.2.0",
|
|
4
|
+
"description": "SpecVerse toolchain — parser, inference, realize, generators, AI, registry",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -65,4 +65,4 @@
|
|
|
65
65
|
"access": "public"
|
|
66
66
|
},
|
|
67
67
|
"license": "MIT"
|
|
68
|
-
}
|
|
68
|
+
}
|