view-contracts 0.3.5 → 0.4.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/README.md +10 -3
- package/cli-contract.yaml +40 -0
- package/dist/preview-style-helpers.js +2 -0
- package/dist/preview-style-helpers.js.map +7 -0
- package/dist/preview.mjs +153 -0
- package/dist/preview.mjs.map +7 -0
- package/dist/view-contracts.bundle.mjs +399 -248
- package/dist/view-contracts.bundle.mjs.map +4 -4
- package/docs/cli-reference.md +36 -0
- package/package.json +28 -1
- package/renderers/compose/renderRuntime.ts +40 -0
- package/renderers/react/defaults/theme.yaml +23 -0
- package/renderers/react/defaults/tokens.yaml +15 -0
- package/renderers/react/elementMap.ts +30 -6
- package/renderers/react/elements.yaml +17 -5
- package/renderers/react/lowering/foldStyle.ts +13 -1
- package/renderers/react/lowering/lowerToJsx.ts +15 -34
- package/renderers/react/paths.ts +1 -10
- package/renderers/react/preview/components.ts +23 -0
- package/renderers/react/preview/index.ts +3 -0
- package/renderers/react/preview/start.tsx +42 -0
- package/renderers/react/preview/style-helpers.ts +5 -0
- package/renderers/react/preview/vocabulary.tsx +75 -0
- package/renderers/react/runtime/theme.css +9 -17
- package/renderers/react/style/foldLiterals.ts +11 -0
- package/renderers/swiftui/README.md +48 -10
- package/renderers/swiftui/elementMap.ts +45 -0
- package/renderers/swiftui/elements.yaml +82 -0
- package/renderers/swiftui/index.ts +5 -0
- package/renderers/swiftui/lowering/lowerToSwiftUI.ts +542 -0
- package/renderers/swiftui/lowering/modifiers.ts +142 -0
- package/renderers/swiftui/renderComponents.ts +79 -0
- package/renderers/swiftui/renderRuntime.ts +91 -0
- package/renderers/swiftui/renderTheme.ts +208 -0
- package/spec/ui-dsl.linter-rules.json +0 -7
- package/spec/ui-dsl.meta.json +5 -0
- package/spec/ui-dsl.schema.json +333 -259
- package/src/generated/schema/allowed-components.ts +0 -36
- package/src/generated/schema/dsl-enums.ts +0 -19
- package/src/generated/schema/dsl-properties.ts +0 -64
- package/src/generated/schema/index.ts +0 -4
- package/src/generated/schema/lowering-style-properties.ts +0 -35
- package/src/generated/schema/react-renderer-element-config.ts +0 -27
- package/src/generated/schema/schema-document.ts +0 -33
|
@@ -1,15 +1,53 @@
|
|
|
1
|
-
# SwiftUI
|
|
1
|
+
# SwiftUI Renderer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Compiles View IR into native SwiftUI code at build time.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Capabilities
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
7
|
+
- **Full DSL element coverage** — all core elements (Box, Stack, Row, Table, Form, Button, Text, etc.) map to SwiftUI views via `elements.yaml`
|
|
8
|
+
- **Theme codegen** — `VcTheme.swift` is generated from the design token / theme IR, supporting static and dynamic variant resolution
|
|
9
|
+
- **Modifier cascade** — DSL visual properties are folded at compile time into SwiftUI modifier chains following the correct order: foreground/font → padding → background → clipShape → shadow → overlay → frame → opacity
|
|
10
|
+
- **Padding shorthand** — `"24px 18px"` style shorthand is parsed into `.padding(.vertical, N)` + `.padding(.horizontal, N)`
|
|
11
|
+
- **Dynamic variants** — `IrConcat` expressions (e.g. `` `badge-${row.statusTone}` ``) are lowered to runtime `VcTheme.resolve()` calls with a pre-built theme dictionary
|
|
12
|
+
- **Table layout** — `Table` / `TableHeader` / `TableRow` / `TableColumn` / `TableCell` with fixed widths, flex columns, and configurable `separator` / `separatorColor`
|
|
13
|
+
- **justify="between"** — `Row` elements with `justify="between"` emit `Spacer()` between children
|
|
14
|
+
- **Mock data SSoT** — `mock.yaml` is converted to `SampleData.json` and bundled into the Xcode project; Swift ViewModels decode via `JSONDecoder` (no hand-written sample data)
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
Theme is emitted at build time via `src/design/emit/swiftui.ts` and `templates/theme/*.hbs` — same Token IR + Theme IR as React `theme.css`, not runtime JSON.
|
|
16
|
+
## Files
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
| File | Role |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `elements.yaml` | DSL element → SwiftUI view mapping |
|
|
21
|
+
| `elementMap.ts` | TypeScript loader for elements.yaml |
|
|
22
|
+
| `renderRuntime.ts` | Build orchestrator (theme, components, sample data JSON) |
|
|
23
|
+
| `renderComponents.ts` | Entry point for per-component Swift file generation |
|
|
24
|
+
| `renderTheme.ts` | `VcTheme.swift` generator (variant dictionary + `applyVcStyle`) |
|
|
25
|
+
| `lowering/lowerToSwiftUI.ts` | IR → SwiftUI body code |
|
|
26
|
+
| `lowering/modifiers.ts` | Visual property set → SwiftUI modifier chain |
|
|
27
|
+
| `templates/` | Handlebars templates for boilerplate Swift files |
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
Generated files are placed in `<project>/generated/swiftui/` during `view-contracts build`:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
generated/swiftui/
|
|
35
|
+
├── VcTheme.swift # Theme + variant dictionary
|
|
36
|
+
├── AdminDashboard.generated.swift # Screen view
|
|
37
|
+
├── KpiCard.generated.swift # Partial views
|
|
38
|
+
├── ...
|
|
39
|
+
└── AdminDashboard.SampleData.json # Mock data from YAML SSoT
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## iOS Preview App
|
|
43
|
+
|
|
44
|
+
See `examples/admin-dashboard/ios-preview/` for a working SwiftUI preview app:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cd examples/admin-dashboard/ios-preview
|
|
48
|
+
xcodegen generate # generates .xcodeproj from project.yml
|
|
49
|
+
# Open VcPreview.xcodeproj in Xcode, or build from CLI:
|
|
50
|
+
xcodebuild -scheme VcPreview -destination 'platform=iOS Simulator,...' build
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The preview app loads `SampleData.json` from the bundle and decodes it into typed `Codable` ViewModels.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { packageRoot } from '../../src/lib/packageRoot.js';
|
|
3
|
+
import { loadYamlFile } from '../../src/renderer/elementMapValidation.js';
|
|
4
|
+
|
|
5
|
+
export interface SwiftUIElementConfig {
|
|
6
|
+
container?: 'group' | 'vstack' | 'hstack' | 'navstack' | 'scrollview' | 'field' | 'cellgroup' | 'tablestack';
|
|
7
|
+
leaf?: 'text' | 'button' | 'textfield' | 'texteditor' | 'picker' | 'spacer' | 'divider' | 'image' | 'progressview' | 'toggle';
|
|
8
|
+
spacingProp?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SwiftUIElementMap {
|
|
12
|
+
elements: Record<string, SwiftUIElementConfig>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const SOURCE_LABEL = 'renderers/swiftui/elements.yaml';
|
|
16
|
+
|
|
17
|
+
let cachedMap: SwiftUIElementMap | null = null;
|
|
18
|
+
|
|
19
|
+
function validateSwiftUIElementMap(map: SwiftUIElementMap): void {
|
|
20
|
+
for (const [name, entry] of Object.entries(map.elements)) {
|
|
21
|
+
const hasContainer = entry.container !== undefined && entry.container !== null;
|
|
22
|
+
const hasLeaf = entry.leaf !== undefined && entry.leaf !== null;
|
|
23
|
+
if (hasContainer === hasLeaf) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`${SOURCE_LABEL}: element "${name}" must have exactly one of "container" or "leaf"`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
if ((entry.container === 'vstack' || entry.container === 'hstack') && entry.spacingProp === undefined) {
|
|
29
|
+
// spacingProp is optional for hstack/vstack containers without a DSL gap prop (e.g. TableHeader)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function loadSwiftUIElementMap(customPath?: string): Promise<SwiftUIElementMap> {
|
|
35
|
+
if (!customPath && cachedMap) return cachedMap;
|
|
36
|
+
const mapPath = customPath ?? resolve(packageRoot(), 'renderers/swiftui/elements.yaml');
|
|
37
|
+
const raw = await loadYamlFile<SwiftUIElementMap>(mapPath);
|
|
38
|
+
validateSwiftUIElementMap(raw);
|
|
39
|
+
if (!customPath) cachedMap = raw;
|
|
40
|
+
return raw;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function isSwiftUIVocabulary(name: string, map: SwiftUIElementMap, allowlistExtra: string[] = []): boolean {
|
|
44
|
+
return name in map.elements && !allowlistExtra.includes(name);
|
|
45
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# SwiftUI renderer element map — maps DSL elements to SwiftUI views.
|
|
2
|
+
|
|
3
|
+
elements:
|
|
4
|
+
Page:
|
|
5
|
+
container: navstack
|
|
6
|
+
Box:
|
|
7
|
+
container: group
|
|
8
|
+
Stack:
|
|
9
|
+
container: vstack
|
|
10
|
+
spacingProp: gap
|
|
11
|
+
Row:
|
|
12
|
+
container: hstack
|
|
13
|
+
spacingProp: gap
|
|
14
|
+
Column:
|
|
15
|
+
container: vstack
|
|
16
|
+
spacingProp: gap
|
|
17
|
+
Text:
|
|
18
|
+
leaf: text
|
|
19
|
+
Button:
|
|
20
|
+
leaf: button
|
|
21
|
+
Form:
|
|
22
|
+
container: vstack
|
|
23
|
+
spacingProp: gap
|
|
24
|
+
Field:
|
|
25
|
+
container: field
|
|
26
|
+
TextInput:
|
|
27
|
+
leaf: textfield
|
|
28
|
+
TextArea:
|
|
29
|
+
leaf: texteditor
|
|
30
|
+
Select:
|
|
31
|
+
leaf: picker
|
|
32
|
+
Table:
|
|
33
|
+
container: tablestack
|
|
34
|
+
TableHeader:
|
|
35
|
+
container: hstack
|
|
36
|
+
TableColumn:
|
|
37
|
+
container: cellgroup
|
|
38
|
+
TableRow:
|
|
39
|
+
container: hstack
|
|
40
|
+
TableCell:
|
|
41
|
+
container: cellgroup
|
|
42
|
+
Spacer:
|
|
43
|
+
leaf: spacer
|
|
44
|
+
Divider:
|
|
45
|
+
leaf: divider
|
|
46
|
+
Image:
|
|
47
|
+
leaf: image
|
|
48
|
+
Icon:
|
|
49
|
+
leaf: image
|
|
50
|
+
ScrollArea:
|
|
51
|
+
container: scrollview
|
|
52
|
+
List:
|
|
53
|
+
container: vstack
|
|
54
|
+
spacingProp: gap
|
|
55
|
+
ListItem:
|
|
56
|
+
container: group
|
|
57
|
+
Link:
|
|
58
|
+
leaf: text
|
|
59
|
+
Modal:
|
|
60
|
+
container: group
|
|
61
|
+
Progress:
|
|
62
|
+
leaf: progressview
|
|
63
|
+
Checkbox:
|
|
64
|
+
leaf: toggle
|
|
65
|
+
Switch:
|
|
66
|
+
leaf: toggle
|
|
67
|
+
Radio:
|
|
68
|
+
leaf: picker
|
|
69
|
+
IconButton:
|
|
70
|
+
leaf: button
|
|
71
|
+
Badge:
|
|
72
|
+
leaf: text
|
|
73
|
+
Chip:
|
|
74
|
+
leaf: text
|
|
75
|
+
Avatar:
|
|
76
|
+
leaf: image
|
|
77
|
+
Card:
|
|
78
|
+
container: group
|
|
79
|
+
SafeArea:
|
|
80
|
+
container: group
|
|
81
|
+
App:
|
|
82
|
+
container: group
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { loadSwiftUIElementMap, isSwiftUIVocabulary } from './elementMap.js';
|
|
2
|
+
export type { SwiftUIElementConfig, SwiftUIElementMap } from './elementMap.js';
|
|
3
|
+
export { renderSwiftComponent } from './renderComponents.js';
|
|
4
|
+
export { mergeVisualProps } from '../../src/renderer/style/mergeVisualProps.js';
|
|
5
|
+
export { visualPropSetToModifiers } from './lowering/modifiers.js';
|