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.
Files changed (44) hide show
  1. package/README.md +10 -3
  2. package/cli-contract.yaml +40 -0
  3. package/dist/preview-style-helpers.js +2 -0
  4. package/dist/preview-style-helpers.js.map +7 -0
  5. package/dist/preview.mjs +153 -0
  6. package/dist/preview.mjs.map +7 -0
  7. package/dist/view-contracts.bundle.mjs +399 -248
  8. package/dist/view-contracts.bundle.mjs.map +4 -4
  9. package/docs/cli-reference.md +36 -0
  10. package/package.json +28 -1
  11. package/renderers/compose/renderRuntime.ts +40 -0
  12. package/renderers/react/defaults/theme.yaml +23 -0
  13. package/renderers/react/defaults/tokens.yaml +15 -0
  14. package/renderers/react/elementMap.ts +30 -6
  15. package/renderers/react/elements.yaml +17 -5
  16. package/renderers/react/lowering/foldStyle.ts +13 -1
  17. package/renderers/react/lowering/lowerToJsx.ts +15 -34
  18. package/renderers/react/paths.ts +1 -10
  19. package/renderers/react/preview/components.ts +23 -0
  20. package/renderers/react/preview/index.ts +3 -0
  21. package/renderers/react/preview/start.tsx +42 -0
  22. package/renderers/react/preview/style-helpers.ts +5 -0
  23. package/renderers/react/preview/vocabulary.tsx +75 -0
  24. package/renderers/react/runtime/theme.css +9 -17
  25. package/renderers/react/style/foldLiterals.ts +11 -0
  26. package/renderers/swiftui/README.md +48 -10
  27. package/renderers/swiftui/elementMap.ts +45 -0
  28. package/renderers/swiftui/elements.yaml +82 -0
  29. package/renderers/swiftui/index.ts +5 -0
  30. package/renderers/swiftui/lowering/lowerToSwiftUI.ts +542 -0
  31. package/renderers/swiftui/lowering/modifiers.ts +142 -0
  32. package/renderers/swiftui/renderComponents.ts +79 -0
  33. package/renderers/swiftui/renderRuntime.ts +91 -0
  34. package/renderers/swiftui/renderTheme.ts +208 -0
  35. package/spec/ui-dsl.linter-rules.json +0 -7
  36. package/spec/ui-dsl.meta.json +5 -0
  37. package/spec/ui-dsl.schema.json +333 -259
  38. package/src/generated/schema/allowed-components.ts +0 -36
  39. package/src/generated/schema/dsl-enums.ts +0 -19
  40. package/src/generated/schema/dsl-properties.ts +0 -64
  41. package/src/generated/schema/index.ts +0 -4
  42. package/src/generated/schema/lowering-style-properties.ts +0 -35
  43. package/src/generated/schema/react-renderer-element-config.ts +0 -27
  44. package/src/generated/schema/schema-document.ts +0 -33
@@ -1,15 +1,53 @@
1
- # SwiftUI renderer scaffold
1
+ # SwiftUI Renderer
2
2
 
3
- **Not implemented yet** beyond theme / screen stubs. View IR lowering and modifier emission are future work.
3
+ Compiles View IR into native SwiftUI code at build time.
4
4
 
5
- SwiftUI `ViewModifier` chains are **compositional** (not CSS-style override). view-contracts may later introduce a separate **modifier pipeline** concept (wrapper-style transforms, analogous to nested HTML layout shells) — that is **out of scope for the current phase**. When styling is implemented, defaults + variant + instance props will be **folded at compile time** into resolved values, not chained for cascade-like override. See [docs/ARCHITECTURE.md](../../docs/ARCHITECTURE.md) § Native renderers.
5
+ ## Capabilities
6
6
 
7
- - Maps: `elements.yaml`, `properties.yaml` (hand-maintained under this directory)
8
- - Routes: `routes.yaml`
9
- - Templates: `templates/`
10
- - Theme codegen spec: [../NATIVE_THEME_EMIT.md](../NATIVE_THEME_EMIT.md)
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
- Implement SwiftUI code generation by reading View IR + renderer maps.
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
- Extend vocabulary via `spec/ui-dsl.schema.json` (from `ui-dsl-*.csv`), then update renderer maps.
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';