view-contracts 0.3.3 → 0.3.5

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 (37) hide show
  1. package/README.md +6 -6
  2. package/cli-contract.yaml +1 -1
  3. package/dist/view-contracts.bundle.mjs +169 -153
  4. package/dist/view-contracts.bundle.mjs.map +4 -4
  5. package/docs/cli-reference.md +1 -1
  6. package/package.json +1 -1
  7. package/renderers/NATIVE_THEME_EMIT.md +73 -0
  8. package/renderers/compose/README.md +7 -0
  9. package/renderers/compose/templates/Screen.kt.hbs +3 -5
  10. package/renderers/compose/templates/VcTheme.kt.hbs +7 -13
  11. package/renderers/react/defaults/theme.yaml +7 -0
  12. package/renderers/react/defaults/tokens.yaml +11 -0
  13. package/renderers/react/lowering/foldStyle.ts +87 -0
  14. package/renderers/react/lowering/lowerToJsx.ts +58 -69
  15. package/renderers/react/renderComponents.ts +180 -24
  16. package/renderers/react/renderRuntime.ts +36 -43
  17. package/renderers/react/routes.yaml +2 -2
  18. package/renderers/react/runtime/index.ts +1 -1
  19. package/renderers/react/runtime/style.ts +3 -55
  20. package/renderers/react/runtime/theme.css +1 -1
  21. package/renderers/react/runtime/types.ts +0 -24
  22. package/renderers/react/style/foldLiterals.ts +129 -0
  23. package/renderers/react/syncGeneratedRuntime.ts +35 -47
  24. package/renderers/react/templates/components-index.ts.hbs +2 -2
  25. package/renderers/react/templates/runtime.ts.hbs +10 -0
  26. package/renderers/react/templates/style-helpers.ts.hbs +2 -2
  27. package/renderers/swiftui/README.md +7 -0
  28. package/renderers/swiftui/templates/Screen.swift.hbs +3 -5
  29. package/renderers/swiftui/templates/VcTheme.swift.hbs +8 -16
  30. package/spec/ui-dsl.schema.json +1331 -345
  31. package/src/generated/schema/allowed-components.ts +1 -1
  32. package/src/generated/schema/dsl-enums.ts +1 -1
  33. package/src/generated/schema/dsl-properties.ts +1 -1
  34. package/src/generated/schema/index.ts +1 -1
  35. package/src/generated/schema/lowering-style-properties.ts +9 -2
  36. package/src/generated/schema/react-renderer-element-config.ts +1 -1
  37. package/src/generated/schema/schema-document.ts +1 -1
@@ -1,4 +1,4 @@
1
- import { readFile, writeFile, mkdir, stat, rm } from 'node:fs/promises';
1
+ import { readFile, writeFile, mkdir, stat, rm, unlink } from 'node:fs/promises';
2
2
  import { resolve } from 'node:path';
3
3
  import type { ViewContractsConfig } from '../../src/config.js';
4
4
  import { projectRoot } from '../../src/config.js';
@@ -14,64 +14,52 @@ async function pathExists(target: string): Promise<boolean> {
14
14
  }
15
15
  }
16
16
 
17
- async function removeStaleSchemaDir(componentsDir: string): Promise<void> {
18
- const schemaDest = resolve(componentsDir, 'schema');
19
- if (await pathExists(schemaDest)) {
20
- await rm(schemaDest, { recursive: true, force: true });
17
+ /** Remove legacy generated layout under generated/react/. */
18
+ export async function removeLegacyReactRuntimeLayout(reactDir: string): Promise<void> {
19
+ const generatedRoot = resolve(reactDir, '..');
20
+ for (const dir of [resolve(reactDir, 'runtime'), resolve(generatedRoot, 'components'), resolve(generatedRoot, 'runtime')]) {
21
+ if (await pathExists(dir)) {
22
+ await rm(dir, { recursive: true, force: true });
23
+ }
24
+ }
25
+ for (const name of ['extensions.tsx', 'extensions.ts', 'contracts.ts'] as const) {
26
+ const stale = resolve(reactDir, name);
27
+ if (await pathExists(stale)) {
28
+ await unlink(stale);
29
+ }
21
30
  }
22
31
  }
23
32
 
24
- async function copyStyleBundle(componentsDir: string): Promise<void> {
25
- const runtimeSrc = reactRuntimeDir();
26
- await writeFile(resolve(componentsDir, 'style.ts'), await readFile(resolve(runtimeSrc, 'style.ts'), 'utf-8'), 'utf-8');
27
- await writeFile(resolve(componentsDir, 'types.ts'), await readFile(resolve(runtimeSrc, 'types.ts'), 'utf-8'), 'utf-8');
33
+ function stripDispatchImport(dispatchSource: string): string {
34
+ return dispatchSource.replace(/^import type \{ DispatchableAction \} from [^;]+;\n/gm, '');
28
35
  }
29
36
 
30
- /** Copy package runtime (style, types, dispatch) into generated/ standalone output, no renderers/ imports. */
31
- export async function syncGeneratedRuntime(
37
+ /** Emit generated/react/runtime.ts fixed system runtime only (dispatch + style helpers). */
38
+ export async function writeGeneratedRuntimeBundle(
32
39
  configPath: string,
33
40
  config: ViewContractsConfig,
34
41
  templatesDir?: string,
35
- ): Promise<{ componentsDir: string; runtimeDir: string }> {
42
+ ): Promise<{ runtimePath: string }> {
36
43
  const root = projectRoot(configPath);
44
+ const reactDir = config.generated.reactDir;
37
45
  const templates = reactTemplatesDir(templatesDir ?? (config.rendererTemplatesDir ? resolve(root, config.rendererTemplatesDir) : undefined));
38
- const componentsDir = resolve(config.generated.reactDir, '../components');
39
- const runtimeDir = resolve(config.generated.reactDir, '../runtime');
46
+ const runtimeSrc = reactRuntimeDir();
40
47
 
41
- await mkdir(componentsDir, { recursive: true });
42
- await mkdir(runtimeDir, { recursive: true });
48
+ await mkdir(reactDir, { recursive: true });
49
+ await removeLegacyReactRuntimeLayout(reactDir);
43
50
 
44
- await removeStaleSchemaDir(componentsDir);
45
- await copyStyleBundle(componentsDir);
46
- await writeFile(
47
- resolve(componentsDir, 'style-helpers.ts'),
48
- await renderTemplate(templates, 'style-helpers.ts.hbs', {}),
49
- 'utf-8',
50
- );
51
- await writeFile(
52
- resolve(componentsDir, 'index.ts'),
53
- await renderTemplate(templates, 'components-index.ts.hbs', {}),
54
- 'utf-8',
55
- );
56
- await writeFile(
57
- resolve(runtimeDir, 'types.ts'),
58
- await renderTemplate(templates, 'runtime-types.ts.hbs', {}),
59
- 'utf-8',
60
- );
61
- await writeFile(
62
- resolve(runtimeDir, 'dispatch.ts'),
63
- await renderTemplate(templates, 'dispatch.ts.hbs', {}),
64
- 'utf-8',
65
- );
51
+ const typesBody = await readFile(resolve(runtimeSrc, 'types.ts'), 'utf-8');
52
+ const styleBody = await readFile(resolve(runtimeSrc, 'style.ts'), 'utf-8');
53
+ const dispatchRaw = await renderTemplate(templates, 'dispatch.ts.hbs', {});
54
+ const dispatchBody = stripDispatchImport(dispatchRaw);
66
55
 
67
- return { componentsDir, runtimeDir };
68
- }
56
+ const content = await renderTemplate(templates, 'runtime.ts.hbs', {
57
+ typesBody: typesBody.trim(),
58
+ styleBody: styleBody.trim(),
59
+ dispatchBody: dispatchBody.trim(),
60
+ });
69
61
 
70
- /** Rewrite preview virtual imports to generated/components-relative paths. */
71
- export function rewriteExtensionImportsForGenerated(body: string): string {
72
- return body
73
- .replace(/from 'view-contracts:runtime\/dispatch'/g, "from '../runtime/dispatch.js'")
74
- .replace(/from "view-contracts:runtime\/dispatch"/g, 'from "../runtime/dispatch.js"')
75
- .replace(/from 'view-contracts:runtime\/types'/g, "from './types.js'")
76
- .replace(/from "view-contracts:runtime\/types"/g, 'from "./types.js"');
62
+ const runtimePath = resolve(reactDir, 'runtime.ts');
63
+ await writeFile(runtimePath, content, 'utf-8');
64
+ return { runtimePath };
77
65
  }
@@ -1,5 +1,5 @@
1
1
  /** Auto-generated component barrel — DO NOT EDIT */
2
2
  export * from './extensions.js';
3
3
  export type { TableColumn } from './extensions.js';
4
- export { commonStyle, mapAlign, mapJustify, isHidden, toneClass } from './style.js';
5
- export type { Action, Option, LoweredStyleInput } from './types.js';
4
+ export { extensionClassName, resolveExtensionVariant, isHidden, toneClass } from './style.js';
5
+ export type { Action, Option } from './types.js';
@@ -0,0 +1,10 @@
1
+ /** Auto-generated React runtime bundle — DO NOT EDIT */
2
+
3
+ // --- types ---
4
+ {{{typesBody}}}
5
+
6
+ // --- style helpers ---
7
+ {{{styleBody}}}
8
+
9
+ // --- action dispatch ---
10
+ {{{dispatchBody}}}
@@ -1,2 +1,2 @@
1
- /** Auto-generated style helpers for lowered JSX — DO NOT EDIT */
2
- export { commonStyle, extensionClassName, mapAlign, mapJustify, resolveExtensionVariant } from './style.js';
1
+ /** Auto-generated style helpers for extensions — DO NOT EDIT */
2
+ export { extensionClassName, resolveExtensionVariant } from './style.js';
@@ -1,8 +1,15 @@
1
1
  # SwiftUI renderer scaffold
2
2
 
3
+ **Not implemented yet** beyond theme / screen stubs. View IR lowering and modifier emission are future work.
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.
6
+
3
7
  - Maps: `elements.yaml`, `properties.yaml` (hand-maintained under this directory)
4
8
  - Routes: `routes.yaml`
5
9
  - Templates: `templates/`
10
+ - Theme codegen spec: [../NATIVE_THEME_EMIT.md](../NATIVE_THEME_EMIT.md)
6
11
 
7
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.
14
+
8
15
  Extend vocabulary via `spec/ui-dsl.schema.json` (from `ui-dsl-*.csv`), then update renderer maps.
@@ -1,8 +1,6 @@
1
- // {{screenName}} — SwiftUI scaffold (Phase 5)
2
- // Theme IR: {{themeJsonPath}}
3
- // Token IR: {{tokensJsonPath}}
4
- //
5
- // TODO(Phase B): Load theme.json and emit PrimaryButtonStyle(), vcTextMuted(), etc.
1
+ // {{screenName}} — SwiftUI scaffold
2
+ // View IR lowering: renderers/swiftui/lowerToSwiftUI.ts (planned)
3
+ // Theme: renderers/NATIVE_THEME_EMIT.md
6
4
 
7
5
  import SwiftUI
8
6
 
@@ -1,21 +1,13 @@
1
- // VcTheme — SwiftUI theme scaffold (Phase 5)
2
- // Load Token IR + Theme IR from bundle resources:
3
- // {{tokensJsonPath}}
4
- // {{themeJsonPath}}
1
+ // VcTheme — SwiftUI theme (scaffold placeholder)
5
2
  //
6
- // TODO(Phase B): Map Theme IR variants to ButtonStyle / ViewModifier implementations.
3
+ // Theme is emitted at build time from Token IR + Theme IR via Handlebars templates.
4
+ // See renderers/NATIVE_THEME_EMIT.md — not loaded from JSON at runtime.
5
+ //
6
+ // Implementation: src/design/emit/swiftui.ts + renderers/swiftui/templates/theme/*.hbs
7
+ // Output target: generated/swiftui/Theme/
7
8
 
8
- import Foundation
9
+ import SwiftUI
9
10
 
10
11
  enum VcTheme {
11
- static let tokensResource = "{{tokensJsonPath}}"
12
- static let themeResource = "{{themeJsonPath}}"
13
-
14
- /// Placeholder — decode theme.json into Swift types in Phase B.
15
- static func loadThemeJson() -> Data? {
16
- guard let url = Bundle.main.url(forResource: themeResource, withExtension: nil) else {
17
- return nil
18
- }
19
- return try? Data(contentsOf: url)
20
- }
12
+ // Populated by emitSwiftUITheme() when swiftui target is enabled.
21
13
  }