view-contracts 0.1.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/LICENSE +21 -0
- package/README.md +297 -0
- package/cli-contract.yaml +201 -0
- package/dist/view-contracts.bundle.mjs +808 -0
- package/dist/view-contracts.bundle.mjs.map +7 -0
- package/docs/cli-reference.md +239 -0
- package/package.json +94 -0
- package/renderers/compose/README.md +8 -0
- package/renderers/react/README.md +10 -0
- package/renderers/react/routes.yaml +27 -0
- package/renderers/swiftui/README.md +8 -0
- package/spec/ui-dsl.linter-rules.json +90 -0
- package/spec/ui-dsl.meta.json +113 -0
- package/spec/ui-dsl.schema.json +738 -0
- package/src/generated/schema/allowed-components.ts +36 -0
- package/src/generated/schema/dsl-properties.ts +63 -0
- package/src/generated/schema/index.ts +4 -0
- package/src/generated/schema/schema-document.ts +33 -0
- package/src/generated/schema/tokens.ts +19 -0
- package/src/renderer/react/runtime/actionRuntime.ts +26 -0
- package/src/renderer/react/runtime/components.tsx +216 -0
- package/src/renderer/react/runtime/index.ts +4 -0
- package/src/renderer/react/runtime/shared.tsx +53 -0
- package/src/renderer/react/runtime/style.ts +69 -0
- package/src/renderer/react/runtime/theme.css +68 -0
- package/src/renderer/react/runtime/types.ts +46 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# view-contracts CLI
|
|
2
|
+
|
|
3
|
+
Contract-first UI design toolchain. Compiles restricted view-contract TSX to language-neutral View IR and renders platform targets (React, SwiftUI, Compose).
|
|
4
|
+
|
|
5
|
+
**Version:** 0.1.0
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [view-contracts](#view-contracts)
|
|
10
|
+
- [compile](#view-contracts-compile)
|
|
11
|
+
- [render.react](#view-contracts-render-react)
|
|
12
|
+
- [render.bridge](#view-contracts-render-bridge)
|
|
13
|
+
- [build](#view-contracts-build)
|
|
14
|
+
- [contracts.generate](#view-contracts-contracts-generate)
|
|
15
|
+
- [contracts.check](#view-contracts-contracts-check)
|
|
16
|
+
- [catalog.build](#view-contracts-catalog-build)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## view-contracts
|
|
21
|
+
|
|
22
|
+
View contract compiler and renderer toolchain.
|
|
23
|
+
|
|
24
|
+
### Global Options
|
|
25
|
+
|
|
26
|
+
| Option | Aliases | Required | Default | Description |
|
|
27
|
+
|---|---|---|---|---|
|
|
28
|
+
| `--help` | -h | No | | Show help and exit. |
|
|
29
|
+
| `--version` | -V | No | | Print version and exit. |
|
|
30
|
+
|
|
31
|
+
### compile
|
|
32
|
+
|
|
33
|
+
Compile .view.tsx files to View IR.
|
|
34
|
+
|
|
35
|
+
Parses restricted view-contract TSX via the TypeScript AST, validates allowed components and expressions, and holds View IR in memory.
|
|
36
|
+
|
|
37
|
+
**Usage:**
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
view-contracts compile
|
|
41
|
+
```
|
|
42
|
+
```
|
|
43
|
+
view-contracts compile -c view-contracts.config.yaml
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### Options
|
|
47
|
+
|
|
48
|
+
| Option | Aliases | Required | Default | Description |
|
|
49
|
+
|---|---|---|---|---|
|
|
50
|
+
| `--config` | -c | No | | Path to view-contracts.config.yaml (discovered upward from cwd when omitted). |
|
|
51
|
+
|
|
52
|
+
#### Exit Codes
|
|
53
|
+
|
|
54
|
+
**Exit 0:** Compilation succeeded.
|
|
55
|
+
|
|
56
|
+
**Exit 1:** Compilation failed.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
### render.react
|
|
61
|
+
|
|
62
|
+
Generate React components from View IR.
|
|
63
|
+
|
|
64
|
+
Compiles views in memory and emits production React to generated/react/.
|
|
65
|
+
|
|
66
|
+
**Usage:**
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
view-contracts render react
|
|
70
|
+
```
|
|
71
|
+
```
|
|
72
|
+
view-contracts render react -c view-contracts.config.yaml
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Options
|
|
76
|
+
|
|
77
|
+
| Option | Aliases | Required | Default | Description |
|
|
78
|
+
|---|---|---|---|---|
|
|
79
|
+
| `--config` | -c | No | | Path to view-contracts.config.yaml (discovered upward from cwd when omitted). |
|
|
80
|
+
|
|
81
|
+
#### Exit Codes
|
|
82
|
+
|
|
83
|
+
**Exit 0:** Render succeeded.
|
|
84
|
+
|
|
85
|
+
**Exit 1:** Render failed.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### render.bridge
|
|
90
|
+
|
|
91
|
+
Generate view ↔ ViewModel bridge registry.
|
|
92
|
+
|
|
93
|
+
Emits src/generated/view-bridge.generated.ts from view-bindings.yaml.
|
|
94
|
+
|
|
95
|
+
**Usage:**
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
view-contracts render bridge
|
|
99
|
+
```
|
|
100
|
+
```
|
|
101
|
+
view-contracts render bridge -c view-contracts.config.yaml
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### Options
|
|
105
|
+
|
|
106
|
+
| Option | Aliases | Required | Default | Description |
|
|
107
|
+
|---|---|---|---|---|
|
|
108
|
+
| `--config` | -c | No | | Path to view-contracts.config.yaml (discovered upward from cwd when omitted). |
|
|
109
|
+
|
|
110
|
+
#### Exit Codes
|
|
111
|
+
|
|
112
|
+
**Exit 0:** Bridge registry generated.
|
|
113
|
+
|
|
114
|
+
**Exit 1:** Generation failed.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### build
|
|
119
|
+
|
|
120
|
+
Full view pipeline (compile + render runtime + render react + bridge).
|
|
121
|
+
|
|
122
|
+
Compiles views in memory, copies React runtime to generated/, emits React components and bridge registry, and scaffolds .vite/ dev files.
|
|
123
|
+
|
|
124
|
+
**Usage:**
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
view-contracts build
|
|
128
|
+
```
|
|
129
|
+
```
|
|
130
|
+
view-contracts build -c view-contracts.config.yaml
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### Options
|
|
134
|
+
|
|
135
|
+
| Option | Aliases | Required | Default | Description |
|
|
136
|
+
|---|---|---|---|---|
|
|
137
|
+
| `--config` | -c | No | | Path to view-contracts.config.yaml (discovered upward from cwd when omitted). |
|
|
138
|
+
|
|
139
|
+
#### Exit Codes
|
|
140
|
+
|
|
141
|
+
**Exit 0:** Build succeeded.
|
|
142
|
+
|
|
143
|
+
**Exit 1:** Build failed.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### contracts.generate
|
|
148
|
+
|
|
149
|
+
Generate ViewModel types from OpenAPI via micro-contracts.
|
|
150
|
+
|
|
151
|
+
Delegates to micro-contracts generate using micro-contracts.config.yaml.
|
|
152
|
+
|
|
153
|
+
**Usage:**
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
view-contracts contracts generate
|
|
157
|
+
```
|
|
158
|
+
```
|
|
159
|
+
view-contracts contracts generate -c view-contracts.config.yaml
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Options
|
|
163
|
+
|
|
164
|
+
| Option | Aliases | Required | Default | Description |
|
|
165
|
+
|---|---|---|---|---|
|
|
166
|
+
| `--config` | -c | No | | Path to view-contracts.config.yaml (discovered upward from cwd when omitted). |
|
|
167
|
+
|
|
168
|
+
#### Exit Codes
|
|
169
|
+
|
|
170
|
+
**Exit 0:** Generation succeeded.
|
|
171
|
+
|
|
172
|
+
**Exit 1:** Generation failed.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### contracts.check
|
|
177
|
+
|
|
178
|
+
Lint OpenAPI screen contracts via micro-contracts.
|
|
179
|
+
|
|
180
|
+
Delegates to micro-contracts lint.
|
|
181
|
+
|
|
182
|
+
**Usage:**
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
view-contracts contracts check
|
|
186
|
+
```
|
|
187
|
+
```
|
|
188
|
+
view-contracts contracts check -c view-contracts.config.yaml
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### Options
|
|
192
|
+
|
|
193
|
+
| Option | Aliases | Required | Default | Description |
|
|
194
|
+
|---|---|---|---|---|
|
|
195
|
+
| `--config` | -c | No | | Path to view-contracts.config.yaml (discovered upward from cwd when omitted). |
|
|
196
|
+
|
|
197
|
+
#### Exit Codes
|
|
198
|
+
|
|
199
|
+
**Exit 0:** Lint passed.
|
|
200
|
+
|
|
201
|
+
**Exit 1:** Lint failed.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### catalog.build
|
|
206
|
+
|
|
207
|
+
Build static catalog HTML for design review.
|
|
208
|
+
|
|
209
|
+
Renders all registered views with mock ViewModels into a single HTML file for design review, screenshot baselines, and accessibility checks.
|
|
210
|
+
|
|
211
|
+
**Usage:**
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
view-contracts catalog build
|
|
215
|
+
```
|
|
216
|
+
```
|
|
217
|
+
view-contracts catalog build -c view-contracts.config.yaml
|
|
218
|
+
```
|
|
219
|
+
```
|
|
220
|
+
view-contracts catalog build -o dist/catalog.html
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Options
|
|
224
|
+
|
|
225
|
+
| Option | Aliases | Required | Default | Description |
|
|
226
|
+
|---|---|---|---|---|
|
|
227
|
+
| `--config` | -c | No | | Path to view-contracts.config.yaml (discovered upward from cwd when omitted). |
|
|
228
|
+
| `--output` | -o | No | | Output HTML path (defaults to dist/catalog.html next to bindings). |
|
|
229
|
+
|
|
230
|
+
#### Exit Codes
|
|
231
|
+
|
|
232
|
+
**Exit 0:** Catalog built.
|
|
233
|
+
|
|
234
|
+
- **Generated files:**
|
|
235
|
+
- `dist/catalog.html` (text/html) *(optional)*
|
|
236
|
+
|
|
237
|
+
**Exit 1:** Build failed.
|
|
238
|
+
|
|
239
|
+
---
|
package/package.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "view-contracts",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Contract-first UI design toolchain — restricted TSX to View IR to platform renderers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"workspaces": [
|
|
7
|
+
"packages/*"
|
|
8
|
+
],
|
|
9
|
+
"bin": {
|
|
10
|
+
"view-contracts": "dist/view-contracts.bundle.mjs"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/view-contracts.bundle.mjs",
|
|
14
|
+
"dist/view-contracts.bundle.mjs.map",
|
|
15
|
+
"src/renderer/react/runtime",
|
|
16
|
+
"src/generated/schema",
|
|
17
|
+
"spec",
|
|
18
|
+
"renderers",
|
|
19
|
+
"cli-contract.yaml",
|
|
20
|
+
"docs/cli-reference.md",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"generate": "cli-contracts generate",
|
|
26
|
+
"dev": "npm run build:views && npm run dev:both --prefix examples/admin-dashboard",
|
|
27
|
+
"dev:preview": "npm run dev:preview --prefix examples/admin-dashboard",
|
|
28
|
+
"dev:app": "npm run dev:app --prefix examples/admin-dashboard",
|
|
29
|
+
"dev:both": "npm run build:views && npm run dev:both --prefix examples/admin-dashboard",
|
|
30
|
+
"check:both": "npm run check:both --prefix examples/admin-dashboard",
|
|
31
|
+
"serve:built": "npm run serve:built --prefix examples/admin-dashboard",
|
|
32
|
+
"build": "npm run build:cli && npm run build:views && tsc -b",
|
|
33
|
+
"build:cli": "npm run generate && tsc -p tsconfig.cli.json",
|
|
34
|
+
"build:publish": "npm run build:cli && npm run bundle:min",
|
|
35
|
+
"bundle": "node esbuild.bundle.mjs",
|
|
36
|
+
"bundle:min": "node esbuild.bundle.mjs --minify",
|
|
37
|
+
"build:views": "tsx src/cli.ts build -c examples/admin-dashboard/view-contracts.config.yaml",
|
|
38
|
+
"build:catalog": "tsx src/cli.ts catalog build -c examples/admin-dashboard/view-contracts.config.yaml",
|
|
39
|
+
"build:example": "vite build --config examples/admin-dashboard/.vite/vite.config.ts",
|
|
40
|
+
"compile": "tsx src/cli.ts compile -c examples/admin-dashboard/view-contracts.config.yaml",
|
|
41
|
+
"render:react": "tsx src/cli.ts render react -c examples/admin-dashboard/view-contracts.config.yaml",
|
|
42
|
+
"contracts:generate": "tsx src/cli.ts contracts generate -c examples/admin-dashboard/view-contracts.config.yaml",
|
|
43
|
+
"contracts:check": "tsx src/cli.ts contracts check -c examples/admin-dashboard/view-contracts.config.yaml",
|
|
44
|
+
"schema:generate": "tsx scripts/schema-generate.ts",
|
|
45
|
+
"cli-contracts:validate": "cli-contracts validate",
|
|
46
|
+
"test:ci": "node dist/view-contracts.bundle.mjs --version && node dist/view-contracts.bundle.mjs --help",
|
|
47
|
+
"version": "node -e \"const fs=require('fs');const f='cli-contract.yaml';fs.writeFileSync(f,fs.readFileSync(f,'utf8').replace(/^ version: .*/m,' version: '+process.env.npm_package_version))\" && git add cli-contract.yaml",
|
|
48
|
+
"prepublishOnly": "npm run build:publish && npm run test:ci"
|
|
49
|
+
},
|
|
50
|
+
"keywords": [
|
|
51
|
+
"ui",
|
|
52
|
+
"contract-first",
|
|
53
|
+
"tsx",
|
|
54
|
+
"view-model",
|
|
55
|
+
"code-generation",
|
|
56
|
+
"react",
|
|
57
|
+
"openapi",
|
|
58
|
+
"design-system",
|
|
59
|
+
"ai-development"
|
|
60
|
+
],
|
|
61
|
+
"author": "foo-log-inc",
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"repository": {
|
|
64
|
+
"type": "git",
|
|
65
|
+
"url": "git+https://github.com/foo-log-inc/view-contracts.git"
|
|
66
|
+
},
|
|
67
|
+
"homepage": "https://github.com/foo-log-inc/view-contracts#readme",
|
|
68
|
+
"bugs": {
|
|
69
|
+
"url": "https://github.com/foo-log-inc/view-contracts/issues"
|
|
70
|
+
},
|
|
71
|
+
"engines": {
|
|
72
|
+
"node": ">=20.0.0"
|
|
73
|
+
},
|
|
74
|
+
"dependencies": {
|
|
75
|
+
"commander": "^14.0.3",
|
|
76
|
+
"js-yaml": "^4.1.1",
|
|
77
|
+
"micro-contracts": "^0.17.10",
|
|
78
|
+
"react": "^19.2.0",
|
|
79
|
+
"react-dom": "^19.2.0",
|
|
80
|
+
"typescript": "^5.9.3"
|
|
81
|
+
},
|
|
82
|
+
"devDependencies": {
|
|
83
|
+
"@types/js-yaml": "^4.0.9",
|
|
84
|
+
"@types/node": "^22.19.21",
|
|
85
|
+
"@types/react": "^19.2.17",
|
|
86
|
+
"@types/react-dom": "^19.2.3",
|
|
87
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
88
|
+
"ajv": "^8.18.0",
|
|
89
|
+
"cli-contracts": "^0.33.13",
|
|
90
|
+
"esbuild": "^0.28.0",
|
|
91
|
+
"tsx": "^4.21.0",
|
|
92
|
+
"vite": "^6.4.1"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Compose renderer scaffold
|
|
2
|
+
|
|
3
|
+
- Maps: `elements.yaml`, `properties.yaml` (hand-maintained under this directory)
|
|
4
|
+
- Routes: `routes.yaml`
|
|
5
|
+
- Templates: `templates/`
|
|
6
|
+
|
|
7
|
+
Implement Compose code generation by reading View IR + renderer maps.
|
|
8
|
+
Vocabulary: `spec/ui-dsl.schema.json` (from `ui-dsl-*.csv`).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# React renderer
|
|
2
|
+
|
|
3
|
+
Per-target mapping for view-contracts UI DSL → React.
|
|
4
|
+
|
|
5
|
+
- `routes.yaml` — generated output file routing
|
|
6
|
+
- `elements.yaml` — element → React component (hand-maintained)
|
|
7
|
+
- `properties.yaml` — property → style/attr (hand-maintained)
|
|
8
|
+
|
|
9
|
+
Vocabulary (elements/properties) is defined in `spec/ui-dsl.schema.json` (from `ui-dsl-*.csv`).
|
|
10
|
+
Sample-only preview helpers (Sidebar, Card, etc.) are **not** part of the vocabulary.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Renderer output routing — separate from element/property mapping.
|
|
2
|
+
# Vocabulary: spec/ui-dsl.schema.json. Per-target maps: renderers/{target}/elements.yaml
|
|
3
|
+
|
|
4
|
+
react:
|
|
5
|
+
description: Production / preview React components via project componentsModule
|
|
6
|
+
importFrom: '@view-contracts/components'
|
|
7
|
+
themeCss: theme.css
|
|
8
|
+
outputs:
|
|
9
|
+
screen:
|
|
10
|
+
pattern: '{{projectRoot}}/generated/react/{{screenName}}.generated.tsx'
|
|
11
|
+
preview:
|
|
12
|
+
entry: views/preview.entry.tsx
|
|
13
|
+
generatedEntry: src/main.tsx
|
|
14
|
+
|
|
15
|
+
swiftui:
|
|
16
|
+
description: SwiftUI renderer (scaffold — implement using elements.map.yaml + properties.map.yaml)
|
|
17
|
+
outputs:
|
|
18
|
+
screen:
|
|
19
|
+
pattern: '{{projectRoot}}/generated/swiftui/{{screenName}}.swift'
|
|
20
|
+
template: templates/Screen.swift.hbs
|
|
21
|
+
|
|
22
|
+
compose:
|
|
23
|
+
description: Jetpack Compose renderer (scaffold)
|
|
24
|
+
outputs:
|
|
25
|
+
screen:
|
|
26
|
+
pattern: '{{projectRoot}}/generated/compose/{{screenName}}Screen.kt'
|
|
27
|
+
template: templates/Screen.kt.hbs
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# SwiftUI renderer scaffold
|
|
2
|
+
|
|
3
|
+
- Maps: `elements.yaml`, `properties.yaml` (hand-maintained under this directory)
|
|
4
|
+
- Routes: `routes.yaml`
|
|
5
|
+
- Templates: `templates/`
|
|
6
|
+
|
|
7
|
+
Implement SwiftUI code generation by reading View IR + renderer maps.
|
|
8
|
+
Extend vocabulary via `spec/ui-dsl.schema.json` (from `ui-dsl-*.csv`), then update renderer maps.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "view-contracts UI DSL linter rules",
|
|
4
|
+
"description": "Structural constraints not expressible in ui-dsl.schema.json alone. Validated by schema linter, not JSON Schema.",
|
|
5
|
+
"rules": [
|
|
6
|
+
{
|
|
7
|
+
"id": "spacer-parent",
|
|
8
|
+
"severity": "error",
|
|
9
|
+
"description": "Spacer must be a direct child of Row or Column",
|
|
10
|
+
"element": "Spacer",
|
|
11
|
+
"parent": ["Row", "Column"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "scroll-area-single-child",
|
|
15
|
+
"severity": "error",
|
|
16
|
+
"description": "ScrollArea must contain exactly one child",
|
|
17
|
+
"element": "ScrollArea",
|
|
18
|
+
"children": { "count": 1 }
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "list-listitem-only",
|
|
22
|
+
"severity": "error",
|
|
23
|
+
"description": "List children must be ListItem elements only",
|
|
24
|
+
"element": "List",
|
|
25
|
+
"children": { "allowed": ["ListItem"] }
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "icon-button-label-required",
|
|
29
|
+
"severity": "error",
|
|
30
|
+
"description": "IconButton requires label for accessibility",
|
|
31
|
+
"element": "IconButton",
|
|
32
|
+
"requiredProperties": ["label"]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "image-src-required",
|
|
36
|
+
"severity": "error",
|
|
37
|
+
"description": "Image requires src",
|
|
38
|
+
"element": "Image",
|
|
39
|
+
"requiredProperties": ["src"]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "icon-name-required",
|
|
43
|
+
"severity": "error",
|
|
44
|
+
"description": "Icon requires name (icon token)",
|
|
45
|
+
"element": "Icon",
|
|
46
|
+
"requiredProperties": ["name"]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "heading-level-required",
|
|
50
|
+
"severity": "error",
|
|
51
|
+
"description": "Heading requires headingLevel for semantic hierarchy",
|
|
52
|
+
"element": "Heading",
|
|
53
|
+
"requiredProperties": ["headingLevel"]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "form-no-onclick",
|
|
57
|
+
"severity": "error",
|
|
58
|
+
"description": "Form must use onSubmit, not onClick",
|
|
59
|
+
"element": "Form",
|
|
60
|
+
"forbiddenProperties": ["onClick"]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "field-single-input",
|
|
64
|
+
"severity": "error",
|
|
65
|
+
"description": "Field must contain exactly one input element",
|
|
66
|
+
"element": "Field",
|
|
67
|
+
"children": {
|
|
68
|
+
"allowed": ["TextInput", "TextArea", "Select", "Checkbox", "Radio", "Switch"],
|
|
69
|
+
"count": 1
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": "modal-placement",
|
|
74
|
+
"severity": "error",
|
|
75
|
+
"description": "Modal must be a direct child of Page or an overlay host",
|
|
76
|
+
"element": "Modal",
|
|
77
|
+
"parent": ["Page", "App"]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "button-label-or-children",
|
|
81
|
+
"severity": "error",
|
|
82
|
+
"description": "Button requires label or text/icon children; when both exist, label is the accessibility name",
|
|
83
|
+
"element": "Button",
|
|
84
|
+
"oneOf": [
|
|
85
|
+
{ "requiredProperties": ["label"] },
|
|
86
|
+
{ "hasChildren": true }
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://view-contracts.dev/schema/ui-dsl.meta.json",
|
|
4
|
+
"title": "view-contracts UI DSL vocabulary document",
|
|
5
|
+
"description": "JSON Schema for spec/ui-dsl.schema.json. Vocabulary is derived from ui-dsl-*.csv.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["tokens", "properties", "elements"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"$schema": { "type": "string" },
|
|
11
|
+
"tokens": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"additionalProperties": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": { "type": "string" },
|
|
16
|
+
"minItems": 1
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"properties": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"description": "Shared DSL attributes (from ui-dsl-properties.csv).",
|
|
22
|
+
"additionalProperties": { "$ref": "#/$defs/propertyDef" }
|
|
23
|
+
},
|
|
24
|
+
"elements": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"description": "DSL elements (from ui-dsl-elements.csv).",
|
|
27
|
+
"additionalProperties": { "$ref": "#/$defs/elementDef" }
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"$defs": {
|
|
31
|
+
"propertyDef": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"required": ["type", "x-category"],
|
|
34
|
+
"additionalProperties": false,
|
|
35
|
+
"properties": {
|
|
36
|
+
"type": {
|
|
37
|
+
"oneOf": [
|
|
38
|
+
{ "type": "string", "enum": ["string", "number", "boolean", "array", "action"] },
|
|
39
|
+
{ "type": "array", "items": { "type": "string" }, "minItems": 1 }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"enum": { "type": "string", "description": "Token set name in tokens" },
|
|
43
|
+
"description": { "type": "string" },
|
|
44
|
+
"x-category": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"enum": [
|
|
47
|
+
"identity", "state", "size", "spacing", "alignment", "direction", "overflow",
|
|
48
|
+
"visual", "text", "image", "interaction", "data", "validation"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"x-appliesTo": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": { "type": "string" }
|
|
54
|
+
},
|
|
55
|
+
"x-notes": { "type": "string" }
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"propertyRef": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"required": ["$ref"],
|
|
61
|
+
"additionalProperties": false,
|
|
62
|
+
"properties": {
|
|
63
|
+
"$ref": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"pattern": "^#/properties/[A-Za-z][A-Za-z0-9]*$"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"elementProperty": {
|
|
70
|
+
"oneOf": [
|
|
71
|
+
{ "$ref": "#/$defs/propertyDef" },
|
|
72
|
+
{ "$ref": "#/$defs/propertyRef" }
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
"elementDef": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"required": ["x-tier", "x-category", "description", "properties", "_children"],
|
|
78
|
+
"additionalProperties": false,
|
|
79
|
+
"properties": {
|
|
80
|
+
"description": { "type": "string" },
|
|
81
|
+
"x-tier": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"enum": ["core", "optional", "extension"]
|
|
84
|
+
},
|
|
85
|
+
"x-category": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"enum": ["structure", "layout", "scroll", "text", "media", "input", "form", "overlay", "feedback"]
|
|
88
|
+
},
|
|
89
|
+
"x-notes": { "type": "string" },
|
|
90
|
+
"properties": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"additionalProperties": { "$ref": "#/$defs/elementProperty" }
|
|
93
|
+
},
|
|
94
|
+
"_children": { "$ref": "#/$defs/childrenConstraint" }
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"childrenConstraint": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"required": ["x-kind"],
|
|
100
|
+
"additionalProperties": false,
|
|
101
|
+
"properties": {
|
|
102
|
+
"x-kind": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"enum": ["any", "none", "single", "text", "list"]
|
|
105
|
+
},
|
|
106
|
+
"x-items": {
|
|
107
|
+
"type": "array",
|
|
108
|
+
"items": { "type": "string" }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|