@pixel-point/toolcraft 0.0.2

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 (257) hide show
  1. package/LICENSE.md +98 -0
  2. package/README.md +41 -0
  3. package/bin/create-toolcraft-app.mjs +8 -0
  4. package/bin/toolcraft.mjs +8 -0
  5. package/package.json +24 -0
  6. package/scripts/prepare-pack.mjs +29 -0
  7. package/src/cli.mjs +392 -0
  8. package/src/cli.test.mjs +284 -0
  9. package/src/copy-recursive.mjs +86 -0
  10. package/src/generate.mjs +212 -0
  11. package/src/generate.test.mjs +322 -0
  12. package/src/import-map.mjs +14 -0
  13. package/src/package-json.mjs +80 -0
  14. package/src/package-json.test.mjs +67 -0
  15. package/src/rewrite-imports.mjs +85 -0
  16. package/src/rewrite-imports.test.mjs +58 -0
  17. package/templates/runtime/contracts/component-contracts.test.ts +1165 -0
  18. package/templates/runtime/contracts/component-contracts.ts +1340 -0
  19. package/templates/runtime/contracts/decision-contracts.test.ts +206 -0
  20. package/templates/runtime/contracts/decision-contracts.ts +283 -0
  21. package/templates/runtime/contracts/index.test.ts +14 -0
  22. package/templates/runtime/contracts/index.ts +3 -0
  23. package/templates/runtime/contracts/types.ts +56 -0
  24. package/templates/runtime/export/export.test.ts +203 -0
  25. package/templates/runtime/export/export.ts +132 -0
  26. package/templates/runtime/export/index.ts +1 -0
  27. package/templates/runtime/index.ts +14 -0
  28. package/templates/runtime/react/canvas-shell.test.tsx +424 -0
  29. package/templates/runtime/react/canvas-shell.tsx +408 -0
  30. package/templates/runtime/react/control-renderers.ts +31 -0
  31. package/templates/runtime/react/controls-panel.test.tsx +3736 -0
  32. package/templates/runtime/react/controls-panel.tsx +2327 -0
  33. package/templates/runtime/react/curve-geometry.test.ts +70 -0
  34. package/templates/runtime/react/index.ts +15 -0
  35. package/templates/runtime/react/layer-tree.ts +96 -0
  36. package/templates/runtime/react/layers-panel.test.tsx +487 -0
  37. package/templates/runtime/react/layers-panel.tsx +1348 -0
  38. package/templates/runtime/react/media-file.ts +82 -0
  39. package/templates/runtime/react/panel-host-config.ts +80 -0
  40. package/templates/runtime/react/panel-host-geometry.test.ts +66 -0
  41. package/templates/runtime/react/panel-host-geometry.ts +109 -0
  42. package/templates/runtime/react/panel-host-types.ts +74 -0
  43. package/templates/runtime/react/panel-host.test.tsx +102 -0
  44. package/templates/runtime/react/panel-host.tsx +353 -0
  45. package/templates/runtime/react/runtime-public-api.test.tsx +132 -0
  46. package/templates/runtime/react/settings-transfer.test.ts +150 -0
  47. package/templates/runtime/react/settings-transfer.ts +279 -0
  48. package/templates/runtime/react/storage-key-migration.ts +48 -0
  49. package/templates/runtime/react/theme-runtime.tsx +177 -0
  50. package/templates/runtime/react/timeline-panel.test.tsx +668 -0
  51. package/templates/runtime/react/timeline-panel.tsx +2953 -0
  52. package/templates/runtime/react/toolbar-panel.test.tsx +212 -0
  53. package/templates/runtime/react/toolbar-panel.tsx +205 -0
  54. package/templates/runtime/react/toolcraft-app.integration.test.tsx +350 -0
  55. package/templates/runtime/react/toolcraft-app.test.tsx +339 -0
  56. package/templates/runtime/react/toolcraft-app.tsx +81 -0
  57. package/templates/runtime/react/toolcraft-root.test.tsx +347 -0
  58. package/templates/runtime/react/toolcraft-root.tsx +203 -0
  59. package/templates/runtime/react/use-toolcraft.ts +41 -0
  60. package/templates/runtime/schema/define-toolcraft.test.ts +1524 -0
  61. package/templates/runtime/schema/define-toolcraft.ts +1442 -0
  62. package/templates/runtime/schema/keyframe-capability.test.ts +90 -0
  63. package/templates/runtime/schema/keyframe-capability.ts +51 -0
  64. package/templates/runtime/schema/runtime-targets.ts +40 -0
  65. package/templates/runtime/schema/types.ts +370 -0
  66. package/templates/runtime/state/canvas-zoom.ts +8 -0
  67. package/templates/runtime/state/create-template-state.test.ts +242 -0
  68. package/templates/runtime/state/create-template-state.ts +95 -0
  69. package/templates/runtime/state/keyframe-evaluation.test.ts +141 -0
  70. package/templates/runtime/state/keyframe-evaluation.ts +203 -0
  71. package/templates/runtime/state/persistence.test.ts +217 -0
  72. package/templates/runtime/state/persistence.ts +511 -0
  73. package/templates/runtime/state/reducer.test.ts +937 -0
  74. package/templates/runtime/state/reducer.ts +1212 -0
  75. package/templates/runtime/state/timeline-readiness.ts +43 -0
  76. package/templates/runtime/state/types.ts +242 -0
  77. package/templates/runtime/styles.css +125 -0
  78. package/templates/runtime/testing/performance.test.ts +1058 -0
  79. package/templates/runtime/testing/performance.ts +1078 -0
  80. package/templates/starter/AGENTS.md +186 -0
  81. package/templates/starter/LICENSE.md +98 -0
  82. package/templates/starter/NOTICE.md +8 -0
  83. package/templates/starter/docs/toolcraft/README.md +41 -0
  84. package/templates/starter/docs/toolcraft/acceptance-testing.md +205 -0
  85. package/templates/starter/docs/toolcraft/agent-worklog.md +81 -0
  86. package/templates/starter/docs/toolcraft/assembly-workflow.md +206 -0
  87. package/templates/starter/docs/toolcraft/component-rules.md +299 -0
  88. package/templates/starter/docs/toolcraft/custom-controls.md +71 -0
  89. package/templates/starter/docs/toolcraft/decision-contract.md +71 -0
  90. package/templates/starter/docs/toolcraft/performance.md +112 -0
  91. package/templates/starter/docs/toolcraft/renderer-technique.md +48 -0
  92. package/templates/starter/docs/toolcraft/schema-reference.md +265 -0
  93. package/templates/starter/docs/toolcraft/workflow.md +87 -0
  94. package/templates/starter/e2e/app-browser-acceptance.spec.ts +785 -0
  95. package/templates/starter/e2e/app-controls.spec.ts +41 -0
  96. package/templates/starter/e2e/app-performance.spec.ts +326 -0
  97. package/templates/starter/e2e/canvas-handle-helpers.ts +244 -0
  98. package/templates/starter/e2e/performance-helpers.ts +612 -0
  99. package/templates/starter/e2e/product-observable-helpers.ts +170 -0
  100. package/templates/starter/index.html +12 -0
  101. package/templates/starter/package.json +52 -0
  102. package/templates/starter/playwright.config.ts +43 -0
  103. package/templates/starter/scripts/check-ai-skills.mjs +95 -0
  104. package/templates/starter/scripts/check-toolcraft-docs.mjs +159 -0
  105. package/templates/starter/scripts/check-toolcraft-integrity.mjs +232 -0
  106. package/templates/starter/scripts/run-vite-on-free-port.mjs +48 -0
  107. package/templates/starter/scripts/toolcraft-port.mjs +54 -0
  108. package/templates/starter/scripts/toolcraft-port.test.mjs +73 -0
  109. package/templates/starter/src/app/starter-acceptance.test.ts +5959 -0
  110. package/templates/starter/src/app/starter-acceptance.ts +2646 -0
  111. package/templates/starter/src/app/starter-performance.test.ts +1390 -0
  112. package/templates/starter/src/app/starter-performance.ts +12 -0
  113. package/templates/starter/src/app/starter-schema.test.ts +70 -0
  114. package/templates/starter/src/app/starter-schema.ts +15 -0
  115. package/templates/starter/src/main.tsx +18 -0
  116. package/templates/starter/src/router.tsx +16 -0
  117. package/templates/starter/src/routes/index.tsx +7 -0
  118. package/templates/starter/src/routes/root.tsx +19 -0
  119. package/templates/starter/src/styles.css +120 -0
  120. package/templates/starter/tsconfig.json +11 -0
  121. package/templates/starter/vite.config.ts +13 -0
  122. package/templates/ui/components/composites/accordion.tsx +73 -0
  123. package/templates/ui/components/composites/alert-dialog.tsx +190 -0
  124. package/templates/ui/components/composites/alert.tsx +74 -0
  125. package/templates/ui/components/composites/aspect-ratio.tsx +22 -0
  126. package/templates/ui/components/composites/avatar.tsx +98 -0
  127. package/templates/ui/components/composites/badge.tsx +69 -0
  128. package/templates/ui/components/composites/breadcrumb.tsx +106 -0
  129. package/templates/ui/components/composites/card.tsx +91 -0
  130. package/templates/ui/components/composites/combobox.tsx +486 -0
  131. package/templates/ui/components/composites/command.tsx +296 -0
  132. package/templates/ui/components/composites/context-menu.tsx +247 -0
  133. package/templates/ui/components/composites/dialog.tsx +282 -0
  134. package/templates/ui/components/composites/dropdown-menu.tsx +299 -0
  135. package/templates/ui/components/composites/empty.tsx +110 -0
  136. package/templates/ui/components/composites/hover-card.tsx +44 -0
  137. package/templates/ui/components/composites/index.ts +30 -0
  138. package/templates/ui/components/composites/menubar.tsx +214 -0
  139. package/templates/ui/components/composites/navigation-menu.tsx +167 -0
  140. package/templates/ui/components/composites/pagination.tsx +131 -0
  141. package/templates/ui/components/composites/progress.tsx +72 -0
  142. package/templates/ui/components/composites/radio-group.tsx +84 -0
  143. package/templates/ui/components/composites/resizable.tsx +42 -0
  144. package/templates/ui/components/composites/sheet.tsx +153 -0
  145. package/templates/ui/components/composites/sidebar-structural.tsx +310 -0
  146. package/templates/ui/components/composites/sidebar.tsx +431 -0
  147. package/templates/ui/components/composites/sonner.tsx +35 -0
  148. package/templates/ui/components/composites/spinner.tsx +43 -0
  149. package/templates/ui/components/composites/table.tsx +108 -0
  150. package/templates/ui/components/composites/tabs.tsx +83 -0
  151. package/templates/ui/components/control-layout/index.tsx +437 -0
  152. package/templates/ui/components/controls/actions/actions-control.tsx +139 -0
  153. package/templates/ui/components/controls/actions/index.ts +9 -0
  154. package/templates/ui/components/controls/anchor-grid/anchor-grid-control.tsx +107 -0
  155. package/templates/ui/components/controls/anchor-grid/index.ts +4 -0
  156. package/templates/ui/components/controls/boolean/boolean-controls.tsx +79 -0
  157. package/templates/ui/components/controls/boolean/index.ts +4 -0
  158. package/templates/ui/components/controls/channel-mixer/channel-mixer-control.tsx +95 -0
  159. package/templates/ui/components/controls/channel-mixer/index.ts +4 -0
  160. package/templates/ui/components/controls/channel-tabs/channel-tabs.tsx +42 -0
  161. package/templates/ui/components/controls/channel-tabs/index.ts +6 -0
  162. package/templates/ui/components/controls/code-textarea/code-textarea-control.tsx +90 -0
  163. package/templates/ui/components/controls/code-textarea/index.ts +4 -0
  164. package/templates/ui/components/controls/color/color-control.tsx +571 -0
  165. package/templates/ui/components/controls/color/color-picker-popover.tsx +104 -0
  166. package/templates/ui/components/controls/color/index.ts +41 -0
  167. package/templates/ui/components/controls/color/palette-control-data.ts +436 -0
  168. package/templates/ui/components/controls/color/palette-control.tsx +535 -0
  169. package/templates/ui/components/controls/color/style-guide-color-picker-channel-utils.ts +162 -0
  170. package/templates/ui/components/controls/color/style-guide-color-picker-interactions.ts +190 -0
  171. package/templates/ui/components/controls/color/style-guide-color-picker-logic.ts +485 -0
  172. package/templates/ui/components/controls/color/style-guide-color-picker-parts.tsx +710 -0
  173. package/templates/ui/components/controls/color/style-guide-color-picker.tsx +503 -0
  174. package/templates/ui/components/controls/control-types.ts +43 -0
  175. package/templates/ui/components/controls/curves/curve-geometry.ts +355 -0
  176. package/templates/ui/components/controls/curves/curve-graph.tsx +390 -0
  177. package/templates/ui/components/controls/curves/curves-control.tsx +445 -0
  178. package/templates/ui/components/controls/curves/index.ts +6 -0
  179. package/templates/ui/components/controls/file-drop/file-drop-control.tsx +191 -0
  180. package/templates/ui/components/controls/file-drop/index.ts +5 -0
  181. package/templates/ui/components/controls/font-picker/font-catalog.json +15360 -0
  182. package/templates/ui/components/controls/font-picker/font-catalog.ts +116 -0
  183. package/templates/ui/components/controls/font-picker/font-picker-control.tsx +1202 -0
  184. package/templates/ui/components/controls/font-picker/font-preview-loader.ts +336 -0
  185. package/templates/ui/components/controls/font-picker/index.ts +24 -0
  186. package/templates/ui/components/controls/font-picker/use-hover-intent.ts +46 -0
  187. package/templates/ui/components/controls/gradient/gradient-control-utils.ts +190 -0
  188. package/templates/ui/components/controls/gradient/gradient-control.tsx +612 -0
  189. package/templates/ui/components/controls/gradient/gradient-stop-list.tsx +400 -0
  190. package/templates/ui/components/controls/gradient/gradient-toolbar.tsx +152 -0
  191. package/templates/ui/components/controls/gradient/index.ts +4 -0
  192. package/templates/ui/components/controls/image-picker/image-picker-control.tsx +139 -0
  193. package/templates/ui/components/controls/image-picker/index.ts +7 -0
  194. package/templates/ui/components/controls/index.ts +192 -0
  195. package/templates/ui/components/controls/range-input/index.ts +4 -0
  196. package/templates/ui/components/controls/range-input/range-input-control.tsx +173 -0
  197. package/templates/ui/components/controls/range-slider/index.ts +4 -0
  198. package/templates/ui/components/controls/range-slider/range-slider-control.tsx +122 -0
  199. package/templates/ui/components/controls/range-slider/range-slider-value.ts +61 -0
  200. package/templates/ui/components/controls/segmented/index.ts +8 -0
  201. package/templates/ui/components/controls/segmented/segmented-control.tsx +94 -0
  202. package/templates/ui/components/controls/select/index.ts +4 -0
  203. package/templates/ui/components/controls/select/select-control.tsx +223 -0
  204. package/templates/ui/components/controls/slider/index.ts +4 -0
  205. package/templates/ui/components/controls/slider/slider-control.tsx +150 -0
  206. package/templates/ui/components/controls/slider/slider-value.ts +56 -0
  207. package/templates/ui/components/controls/text-input/index.ts +4 -0
  208. package/templates/ui/components/controls/text-input/text-input-control.tsx +158 -0
  209. package/templates/ui/components/controls/use-measured-element-width.ts +42 -0
  210. package/templates/ui/components/controls/vector/index.ts +8 -0
  211. package/templates/ui/components/controls/vector/vector-control.tsx +401 -0
  212. package/templates/ui/components/panel/index.ts +19 -0
  213. package/templates/ui/components/panel/panel-actions.tsx +165 -0
  214. package/templates/ui/components/panel/panel-header.tsx +61 -0
  215. package/templates/ui/components/panel/panel-icon-button.tsx +96 -0
  216. package/templates/ui/components/panel/panel-section.tsx +168 -0
  217. package/templates/ui/components/panel/panel-surface.tsx +206 -0
  218. package/templates/ui/components/panel/panel.tsx +210 -0
  219. package/templates/ui/components/primitives/animated-loader.tsx +61 -0
  220. package/templates/ui/components/primitives/button-group.tsx +134 -0
  221. package/templates/ui/components/primitives/button.tsx +429 -0
  222. package/templates/ui/components/primitives/checkbox.tsx +62 -0
  223. package/templates/ui/components/primitives/editable-slider-value-label.tsx +337 -0
  224. package/templates/ui/components/primitives/field.tsx +225 -0
  225. package/templates/ui/components/primitives/index.ts +82 -0
  226. package/templates/ui/components/primitives/input-group.tsx +298 -0
  227. package/templates/ui/components/primitives/input.tsx +61 -0
  228. package/templates/ui/components/primitives/internal/button-loading.tsx +178 -0
  229. package/templates/ui/components/primitives/label.tsx +16 -0
  230. package/templates/ui/components/primitives/popover.tsx +126 -0
  231. package/templates/ui/components/primitives/portal-layer-context.tsx +33 -0
  232. package/templates/ui/components/primitives/primitive-arrow-icon.tsx +38 -0
  233. package/templates/ui/components/primitives/scroll-fade-logic.ts +441 -0
  234. package/templates/ui/components/primitives/scroll-fade-render.tsx +75 -0
  235. package/templates/ui/components/primitives/scroll-fade-types.ts +41 -0
  236. package/templates/ui/components/primitives/scroll-fade.tsx +72 -0
  237. package/templates/ui/components/primitives/select.tsx +408 -0
  238. package/templates/ui/components/primitives/selection-state.ts +31 -0
  239. package/templates/ui/components/primitives/separator.tsx +21 -0
  240. package/templates/ui/components/primitives/slider/index.ts +4 -0
  241. package/templates/ui/components/primitives/slider/slider-interaction.tsx +96 -0
  242. package/templates/ui/components/primitives/slider/slider-parts.tsx +303 -0
  243. package/templates/ui/components/primitives/slider/slider-reset.ts +152 -0
  244. package/templates/ui/components/primitives/slider/slider-value.ts +114 -0
  245. package/templates/ui/components/primitives/slider/slider.tsx +511 -0
  246. package/templates/ui/components/primitives/switch.tsx +35 -0
  247. package/templates/ui/components/primitives/textarea.tsx +49 -0
  248. package/templates/ui/components/primitives/toggle-group.tsx +114 -0
  249. package/templates/ui/components/primitives/toggle.tsx +46 -0
  250. package/templates/ui/components/primitives/tooltip.tsx +100 -0
  251. package/templates/ui/hooks/use-mobile.ts +21 -0
  252. package/templates/ui/index.ts +31 -0
  253. package/templates/ui/lib/control-outline.ts +3 -0
  254. package/templates/ui/lib/input-control-style.ts +131 -0
  255. package/templates/ui/lib/style-guide-color-utils.ts +111 -0
  256. package/templates/ui/lib/utils.ts +6 -0
  257. package/templates/ui/styles.css +291 -0
@@ -0,0 +1,322 @@
1
+ import assert from "node:assert/strict";
2
+ import { execFile } from "node:child_process";
3
+ import fs from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { promisify } from "node:util";
7
+ import { after, describe, it } from "node:test";
8
+
9
+ import { generateToolcraft } from "./generate.mjs";
10
+
11
+ const tempRoots = [];
12
+ const execFileAsync = promisify(execFile);
13
+
14
+ after(async () => {
15
+ await Promise.all(
16
+ tempRoots.map((tempRoot) => fs.rm(tempRoot, { force: true, recursive: true })),
17
+ );
18
+ });
19
+
20
+ async function readTextFiles(rootDir) {
21
+ const files = [];
22
+
23
+ async function visit(currentDir) {
24
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
25
+
26
+ for (const entry of entries) {
27
+ const filePath = path.join(currentDir, entry.name);
28
+
29
+ if (entry.isDirectory()) {
30
+ if (!["dist", "node_modules"].includes(entry.name)) {
31
+ await visit(filePath);
32
+ }
33
+ continue;
34
+ }
35
+
36
+ if (entry.name === "check-toolcraft-docs.mjs") {
37
+ continue;
38
+ }
39
+
40
+ if (entry.isFile() && /\.(css|html|json|md|mjs|ts|tsx)$/.test(entry.name)) {
41
+ files.push(await fs.readFile(filePath, "utf8"));
42
+ }
43
+ }
44
+ }
45
+
46
+ await visit(rootDir);
47
+ return files.join("\n");
48
+ }
49
+
50
+ describe("generateToolcraft", () => {
51
+ it("creates a standalone app with copied local toolcraft sources", async () => {
52
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "toolcraft-cli-"));
53
+ tempRoots.push(tempRoot);
54
+ const targetDir = path.join(tempRoot, "generated-app");
55
+
56
+ const result = await generateToolcraft({
57
+ cwd: tempRoot,
58
+ force: true,
59
+ name: "Generated App",
60
+ targetDir: "generated-app",
61
+ });
62
+
63
+ assert.equal(result.targetDir, targetDir);
64
+ assert.equal(result.packageName, "generated-app");
65
+ assert.ok(await fs.stat(path.join(targetDir, "src/toolcraft/ui/index.ts")));
66
+ assert.ok(
67
+ await fs.stat(path.join(targetDir, "src/toolcraft/runtime/react/index.ts")),
68
+ );
69
+
70
+ const packageJson = JSON.parse(await fs.readFile(path.join(targetDir, "package.json"), "utf8"));
71
+ assert.equal(packageJson.license, "SEE LICENSE IN LICENSE.md");
72
+ assert.equal(packageJson.dependencies["@repo/ui"], undefined);
73
+ assert.equal(packageJson.dependencies["@repo/toolcraft-runtime"], undefined);
74
+ assert.equal(packageJson.dependencies["@tanstack/react-router"], "1.170.6");
75
+ assert.equal(packageJson.dependencies.cmdk, "^1.1.1");
76
+ assert.equal(packageJson.dependencies["react-resizable-panels"], "^4.10.0");
77
+ assert.equal(packageJson.dependencies.sonner, "^2.0.7");
78
+ assert.equal(packageJson.devDependencies["@repo/typescript-config"], undefined);
79
+ assert.equal(packageJson.devDependencies["@playwright/test"], "^1.51.1");
80
+ assert.equal(packageJson.scripts["ai:check"], "node scripts/check-ai-skills.mjs");
81
+ assert.equal(packageJson.scripts.dev, "node scripts/run-vite-on-free-port.mjs dev");
82
+ assert.equal(packageJson.scripts.preview, "node scripts/run-vite-on-free-port.mjs preview");
83
+ assert.equal(packageJson.scripts["docs:check"], "node scripts/check-toolcraft-docs.mjs");
84
+ assert.equal(
85
+ packageJson.scripts.test,
86
+ "node scripts/check-toolcraft-docs.mjs && node scripts/check-toolcraft-integrity.mjs && node --test scripts/*.test.mjs && vitest run src --passWithNoTests",
87
+ );
88
+ assert.equal(packageJson.scripts["test:browser"], "playwright install chromium && playwright test");
89
+ assert.equal(
90
+ packageJson.scripts["test:browser:perf"],
91
+ "playwright install chromium && playwright test e2e/app-performance.spec.ts --workers=1 && playwright test --grep \"browser perf:\" --workers=1 --pass-with-no-tests",
92
+ );
93
+ assert.equal(packageJson.scripts["verify:quick"], "pnpm ai:check && pnpm test");
94
+ assert.equal(packageJson.scripts["verify:ui"], "pnpm test:browser");
95
+ assert.equal(packageJson.scripts["verify:perf"], "pnpm test:browser:perf");
96
+ assert.equal(
97
+ packageJson.scripts["verify:final"],
98
+ "pnpm ai:check && pnpm test && pnpm build && pnpm test:browser && pnpm test:browser:perf",
99
+ );
100
+
101
+ assert.ok(await fs.stat(path.join(targetDir, "playwright.config.ts")));
102
+ const playwrightConfigSource = await fs.readFile(
103
+ path.join(targetDir, "playwright.config.ts"),
104
+ "utf8",
105
+ );
106
+ assert.match(playwrightConfigSource, /reuseExistingServer:\s*false/);
107
+ assert.ok(await fs.stat(path.join(targetDir, "e2e/app-browser-acceptance.spec.ts")));
108
+ assert.ok(await fs.stat(path.join(targetDir, "e2e/app-controls.spec.ts")));
109
+ assert.ok(await fs.stat(path.join(targetDir, "src/app/app-schema.ts")));
110
+ assert.ok(await fs.stat(path.join(targetDir, "src/app/app-schema.test.ts")));
111
+ assert.ok(await fs.stat(path.join(targetDir, "src/app/app-acceptance.ts")));
112
+ assert.ok(await fs.stat(path.join(targetDir, "src/app/app-acceptance.test.ts")));
113
+ assert.ok(await fs.stat(path.join(targetDir, "src/app/app-performance.ts")));
114
+ assert.ok(await fs.stat(path.join(targetDir, "src/app/app-performance.test.ts")));
115
+ assert.ok(await fs.stat(path.join(targetDir, "e2e/app-performance.spec.ts")));
116
+ assert.ok(await fs.stat(path.join(targetDir, "e2e/performance-helpers.ts")));
117
+ assert.ok(await fs.stat(path.join(targetDir, "e2e/product-observable-helpers.ts")));
118
+ assert.ok(await fs.stat(path.join(targetDir, "e2e/canvas-handle-helpers.ts")));
119
+ assert.ok(await fs.stat(path.join(targetDir, "scripts/check-ai-skills.mjs")));
120
+ assert.ok(await fs.stat(path.join(targetDir, "scripts/toolcraft-port.mjs")));
121
+ assert.ok(await fs.stat(path.join(targetDir, "scripts/toolcraft-port.test.mjs")));
122
+ assert.ok(await fs.stat(path.join(targetDir, "scripts/check-toolcraft-docs.mjs")));
123
+ assert.ok(await fs.stat(path.join(targetDir, "scripts/check-toolcraft-integrity.mjs")));
124
+ assert.ok(await fs.stat(path.join(targetDir, "scripts/run-vite-on-free-port.mjs")));
125
+ assert.ok(await fs.stat(path.join(targetDir, "LICENSE.md")));
126
+ assert.ok(await fs.stat(path.join(targetDir, "NOTICE.md")));
127
+ assert.ok(await fs.stat(path.join(targetDir, "docs/toolcraft/README.md")));
128
+ assert.ok(await fs.stat(path.join(targetDir, "docs/toolcraft/assembly-workflow.md")));
129
+ assert.ok(await fs.stat(path.join(targetDir, "docs/toolcraft/decision-contract.md")));
130
+ assert.ok(await fs.stat(path.join(targetDir, "docs/toolcraft/schema-reference.md")));
131
+ assert.ok(await fs.stat(path.join(targetDir, "docs/toolcraft/acceptance-testing.md")));
132
+ assert.ok(await fs.stat(path.join(targetDir, "docs/toolcraft/performance.md")));
133
+ assert.ok(await fs.stat(path.join(targetDir, "docs/toolcraft/renderer-technique.md")));
134
+ assert.ok(await fs.stat(path.join(targetDir, "docs/toolcraft/custom-controls.md")));
135
+ assert.ok(await fs.stat(path.join(targetDir, "docs/toolcraft/component-rules.md")));
136
+ assert.ok(await fs.stat(path.join(targetDir, "src/toolcraft/.toolcraft-manifest.json")));
137
+
138
+ const appPerformanceSource = await fs.readFile(
139
+ path.join(targetDir, "src/app/app-performance.ts"),
140
+ "utf8",
141
+ );
142
+ assert.match(appPerformanceSource, /defineToolcraftPerformance/);
143
+ assert.doesNotMatch(appPerformanceSource, /function\s+validateToolcraftPerformanceCoverage/);
144
+ assert.doesNotMatch(
145
+ appPerformanceSource,
146
+ /function\s+collectToolcraftPerformanceSensitiveControls/,
147
+ );
148
+ assert.doesNotMatch(appPerformanceSource, /maxPerformanceBudgetCaps/);
149
+
150
+ await execFileAsync(process.execPath, [path.join(targetDir, "scripts/check-toolcraft-integrity.mjs")], {
151
+ cwd: targetDir,
152
+ });
153
+ await execFileAsync(process.execPath, ["--check", path.join(targetDir, "scripts/toolcraft-port.mjs")], {
154
+ cwd: targetDir,
155
+ });
156
+ await execFileAsync(process.execPath, ["--check", path.join(targetDir, "scripts/run-vite-on-free-port.mjs")], {
157
+ cwd: targetDir,
158
+ });
159
+ await execFileAsync(process.execPath, [path.join(targetDir, "scripts/check-toolcraft-docs.mjs")], {
160
+ cwd: targetDir,
161
+ });
162
+
163
+ const routePath = path.join(targetDir, "src/routes/index.tsx");
164
+ const originalRouteSource = await fs.readFile(routePath, "utf8");
165
+ await fs.writeFile(
166
+ routePath,
167
+ 'export function AppHome() { return <iframe src="/reference/index.html" />; }\n',
168
+ );
169
+ await assert.rejects(
170
+ () =>
171
+ execFileAsync(process.execPath, [path.join(targetDir, "scripts/check-toolcraft-integrity.mjs")], {
172
+ cwd: targetDir,
173
+ }),
174
+ /app shell bypass: route files must not render a full-page iframe/,
175
+ );
176
+ await fs.writeFile(routePath, originalRouteSource);
177
+
178
+ const copiedUiIndexPath = path.join(targetDir, "src/toolcraft/ui/index.ts");
179
+ await fs.appendFile(copiedUiIndexPath, "\n// generated app edits should fail integrity checks\n");
180
+ await assert.rejects(
181
+ () =>
182
+ execFileAsync(process.execPath, [path.join(targetDir, "scripts/check-toolcraft-integrity.mjs")], {
183
+ cwd: targetDir,
184
+ }),
185
+ /Toolcraft generated app integrity check failed/,
186
+ );
187
+ const agentsSource = await fs.readFile(path.join(targetDir, "AGENTS.md"), "utf8");
188
+ const licenseSource = await fs.readFile(path.join(targetDir, "LICENSE.md"), "utf8");
189
+ const noticeSource = await fs.readFile(path.join(targetDir, "NOTICE.md"), "utf8");
190
+ const localDocPaths = [
191
+ "assembly-workflow.md",
192
+ "decision-contract.md",
193
+ "schema-reference.md",
194
+ "component-rules.md",
195
+ "acceptance-testing.md",
196
+ "performance.md",
197
+ "renderer-technique.md",
198
+ "custom-controls.md",
199
+ ];
200
+
201
+ assert.match(agentsSource, /Quick Entry Contract/);
202
+ assert.match(agentsSource, /Local Reference Docs/);
203
+ assert.match(agentsSource, /src\/app\/app-schema\.ts/);
204
+ assert.match(agentsSource, /src\/app\/app-acceptance\.ts/);
205
+ assert.match(agentsSource, /src\/app\/app-performance\.ts/);
206
+ assert.match(agentsSource, /Required AI Workflow Skills/);
207
+ assert.match(agentsSource, /Verification Tier Classifier/);
208
+ assert.match(agentsSource, /Verification tier: Tier N/);
209
+ assert.match(agentsSource, /pnpm verify:quick/);
210
+ assert.match(agentsSource, /pnpm verify:final/);
211
+ assert.match(agentsSource, /pnpm ai:check/);
212
+ assert.match(agentsSource, /use `brainstorming`/);
213
+ assert.match(agentsSource, /use `writing-plans`/);
214
+ assert.match(agentsSource, /use `systematic-debugging`/);
215
+ assert.match(agentsSource, /browser` workflow/);
216
+ assert.match(agentsSource, /pnpm test:browser/);
217
+ assert.match(agentsSource, /Do not silently skip required workflow skills/);
218
+ assert.match(agentsSource, /Toolcraft app contract overrides generic brainstorming approval/);
219
+ assert.match(agentsSource, /Do not ask the user to confirm decisions already covered/);
220
+ assert.match(agentsSource, /Do not ask whether to enable a browser companion/);
221
+ assert.match(agentsSource, /not git repositories, save spec\/plan files without asking/);
222
+ assert.match(agentsSource, /defineToolcraft/);
223
+ assert.match(agentsSource, /ToolcraftApp/);
224
+ assert.match(agentsSource, /canvasContent/);
225
+ assert.match(agentsSource, /renderDefaultCanvasMedia=\{false\}/);
226
+ assert.match(agentsSource, /onPanelAction/);
227
+ assert.match(agentsSource, /Toolcraft Designer License/);
228
+ assert.match(agentsSource, /LICENSE\.md/);
229
+ assert.match(agentsSource, /NOTICE\.md/);
230
+ assert.match(licenseSource, /Permitted Designer Use/);
231
+ assert.match(licenseSource, /designer client work/);
232
+ assert.match(licenseSource, /AI-Assisted Development/);
233
+ assert.match(licenseSource, /Codex, Claude, ChatGPT, Cursor/);
234
+ assert.match(licenseSource, /does not, by itself, make your use a prohibited AI software/);
235
+ assert.match(licenseSource, /Prohibited Uses/);
236
+ assert.match(licenseSource, /paid AI software/);
237
+ assert.match(licenseSource, /template marketplaces/);
238
+ assert.match(noticeSource, /generated with Toolcraft/);
239
+ assert.match(noticeSource, /governed by `LICENSE\.md`/);
240
+
241
+ for (const localDocPath of localDocPaths) {
242
+ assert.match(agentsSource, new RegExp(`docs/toolcraft/${localDocPath.replace(".", "\\.")}`));
243
+ }
244
+
245
+ const componentRulesSource = await fs.readFile(
246
+ path.join(targetDir, "docs/toolcraft/component-rules.md"),
247
+ "utf8",
248
+ );
249
+ assert.match(componentRulesSource, /Slider `step` means numeric snapping only/);
250
+ assert.match(componentRulesSource, /variant: "discrete"/);
251
+ assert.match(componentRulesSource, /Keep large or precision stepped ranges visually continuous/);
252
+ assert.match(componentRulesSource, /Segmented Controls/);
253
+ assert.match(componentRulesSource, /Never generate a section titled `Color` or `Colors`/);
254
+ assert.match(componentRulesSource, /Use `fileDrop` for source material uploads/);
255
+ assert.match(componentRulesSource, /Do not show Layers for a single-layer app/);
256
+ assert.match(componentRulesSource, /Use playback timeline/);
257
+ assert.match(componentRulesSource, /Still-output product apps include one primary `Export PNG` action/);
258
+ assert.match(componentRulesSource, /Animated product apps include `Export Video`/);
259
+ assert.match(componentRulesSource, /Copy never replaces export/);
260
+
261
+ const assemblyDocsSource = await fs.readFile(
262
+ path.join(targetDir, "docs/toolcraft/assembly-workflow.md"),
263
+ "utf8",
264
+ );
265
+ assert.match(assemblyDocsSource, /@\/toolcraft\/runtime/);
266
+ assert.match(assemblyDocsSource, /transferMode: "reference-runtime-clone"/);
267
+ assert.match(assemblyDocsSource, /`canvasContent` must not contain app UI/);
268
+ assert.doesNotMatch(assemblyDocsSource, /@repo\/toolcraft-runtime/);
269
+
270
+ const acceptanceDocsSource = await fs.readFile(
271
+ path.join(targetDir, "docs/toolcraft/acceptance-testing.md"),
272
+ "utf8",
273
+ );
274
+ assert.match(acceptanceDocsSource, /Every visible product entity must prove it works/);
275
+ assert.match(acceptanceDocsSource, /automatedTestName/);
276
+ assert.match(acceptanceDocsSource, /browserTestName/);
277
+
278
+ const rendererDocsSource = await fs.readFile(
279
+ path.join(targetDir, "docs/toolcraft/renderer-technique.md"),
280
+ "utf8",
281
+ );
282
+ assert.match(rendererDocsSource, /rendererTechnique/);
283
+ assert.match(rendererDocsSource, /sourceRepresentation/);
284
+ assert.match(rendererDocsSource, /whyNotAlternativeStrategies/);
285
+ await assert.rejects(
286
+ () =>
287
+ fs.stat(path.join(targetDir, "src/toolcraft/runtime/react/canvas-shell.test.tsx")),
288
+ /ENOENT/,
289
+ );
290
+
291
+ const routeSource = await fs.readFile(path.join(targetDir, "src/routes/index.tsx"), "utf8");
292
+ assert.match(routeSource, /@\/toolcraft\/runtime\/react/);
293
+ assert.match(routeSource, /\.\.\/app\/app-schema/);
294
+
295
+ const stylesSource = await fs.readFile(path.join(targetDir, "src/styles.css"), "utf8");
296
+ assert.match(stylesSource, /@source "\.\/toolcraft\/ui";/);
297
+ assert.match(stylesSource, /@import "@\/toolcraft\/ui\/styles\.css";/);
298
+
299
+ const allText = await readTextFiles(targetDir);
300
+ assert.doesNotMatch(allText, /@repo\/ui|@repo\/toolcraft-runtime|workspace:/);
301
+ assert.doesNotMatch(allText, new RegExp("template" + "-runtime"));
302
+ assert.doesNotMatch(allText, /packages\/typescript-config/);
303
+ assert.doesNotMatch(
304
+ allText,
305
+ /starterSchema|starter-schema|starterAcceptance|starter-acceptance|starterProductReadiness|starterTransferMode|starterPerformance|starter-performance|StarterHome|Toolcraft Starter/,
306
+ );
307
+ });
308
+
309
+ it("refuses to write into a non-empty directory without force", async () => {
310
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "toolcraft-cli-"));
311
+ tempRoots.push(tempRoot);
312
+ await fs.writeFile(path.join(tempRoot, "README.md"), "keep me");
313
+
314
+ await assert.rejects(
315
+ () =>
316
+ generateToolcraft({
317
+ cwd: tempRoot,
318
+ }),
319
+ /Target directory is not empty/,
320
+ );
321
+ });
322
+ });
@@ -0,0 +1,14 @@
1
+ export const TOOLCRAFT_IMPORT_REWRITES = Object.freeze([
2
+ [
3
+ "@repo/toolcraft-runtime/contracts",
4
+ "@/toolcraft/runtime/contracts/component-contracts",
5
+ ],
6
+ ["@repo/toolcraft-runtime/react", "@/toolcraft/runtime/react"],
7
+ ["@repo/toolcraft-runtime/styles.css", "@/toolcraft/runtime/styles.css"],
8
+ ["@repo/toolcraft-runtime", "@/toolcraft/runtime"],
9
+ ["@repo/ui/controls", "@/toolcraft/ui/components/controls"],
10
+ ["@repo/ui/panel", "@/toolcraft/ui/components/panel"],
11
+ ["@repo/ui/primitives", "@/toolcraft/ui/components/primitives"],
12
+ ["@repo/ui/styles.css", "@/toolcraft/ui/styles.css"],
13
+ ["@repo/ui", "@/toolcraft/ui"],
14
+ ]);
@@ -0,0 +1,80 @@
1
+ export function sanitizePackageName(value) {
2
+ const sanitized = String(value ?? "")
3
+ .trim()
4
+ .toLowerCase()
5
+ .replace(/^@/, "")
6
+ .replace(/[^a-z0-9._/-]+/g, "-")
7
+ .replace(/^-+|-+$/g, "");
8
+
9
+ return sanitized || "toolcraft-app";
10
+ }
11
+
12
+ function isWorkspaceDependency(name, specifier) {
13
+ return name.startsWith("@repo/") || String(specifier).startsWith("workspace:");
14
+ }
15
+
16
+ function createStandaloneDependencies(dependencies = {}) {
17
+ return Object.fromEntries(
18
+ Object.entries(dependencies).filter(
19
+ ([name, specifier]) => !isWorkspaceDependency(name, specifier),
20
+ ),
21
+ );
22
+ }
23
+
24
+ function addDependencyGroup(packageJson, groupName, dependencies) {
25
+ const standaloneDependencies = createStandaloneDependencies(dependencies);
26
+
27
+ if (Object.keys(standaloneDependencies).length > 0) {
28
+ packageJson[groupName] = standaloneDependencies;
29
+ }
30
+ }
31
+
32
+ export function createGeneratedPackageJson({ name, starterPackageJson }) {
33
+ if (!starterPackageJson || typeof starterPackageJson !== "object") {
34
+ throw new Error("starterPackageJson is required to create a generated package manifest.");
35
+ }
36
+
37
+ const packageJson = {
38
+ name: sanitizePackageName(name),
39
+ private: true,
40
+ license: starterPackageJson.license,
41
+ type: starterPackageJson.type ?? "module",
42
+ scripts: starterPackageJson.scripts ?? {},
43
+ };
44
+
45
+ addDependencyGroup(packageJson, "dependencies", starterPackageJson.dependencies);
46
+ addDependencyGroup(packageJson, "devDependencies", starterPackageJson.devDependencies);
47
+ addDependencyGroup(packageJson, "peerDependencies", starterPackageJson.peerDependencies);
48
+ addDependencyGroup(packageJson, "optionalDependencies", starterPackageJson.optionalDependencies);
49
+
50
+ return packageJson;
51
+ }
52
+
53
+ export function createGeneratedTsConfig() {
54
+ return {
55
+ $schema: "https://json.schemastore.org/tsconfig",
56
+ compilerOptions: {
57
+ target: "ES2022",
58
+ useDefineForClassFields: true,
59
+ lib: ["ES2022", "DOM", "DOM.Iterable"],
60
+ allowJs: false,
61
+ skipLibCheck: true,
62
+ esModuleInterop: true,
63
+ allowSyntheticDefaultImports: true,
64
+ strict: true,
65
+ forceConsistentCasingInFileNames: true,
66
+ module: "ESNext",
67
+ moduleResolution: "Bundler",
68
+ resolveJsonModule: true,
69
+ isolatedModules: true,
70
+ jsx: "react-jsx",
71
+ noEmit: true,
72
+ types: ["vite/client", "node"],
73
+ paths: {
74
+ "@/*": ["./src/*"],
75
+ "#/*": ["./src/*"],
76
+ },
77
+ },
78
+ include: ["src", "vite.config.ts"],
79
+ };
80
+ }
@@ -0,0 +1,67 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ import { createGeneratedPackageJson, sanitizePackageName } from "./package-json.mjs";
5
+
6
+ describe("sanitizePackageName", () => {
7
+ it("normalizes package names for generated apps", () => {
8
+ assert.equal(sanitizePackageName(" Mesh Gradient "), "mesh-gradient");
9
+ assert.equal(sanitizePackageName("@Pixel Point/Mesh Gradient"), "pixel-point/mesh-gradient");
10
+ assert.equal(sanitizePackageName(""), "toolcraft-app");
11
+ });
12
+ });
13
+
14
+ describe("createGeneratedPackageJson", () => {
15
+ it("uses the starter package manifest as source of truth", () => {
16
+ const packageJson = createGeneratedPackageJson({
17
+ name: "Generated App",
18
+ starterPackageJson: {
19
+ license: "SEE LICENSE IN LICENSE.md",
20
+ type: "module",
21
+ scripts: {
22
+ dev: "vite dev",
23
+ test: "vitest run",
24
+ },
25
+ dependencies: {
26
+ "@repo/toolcraft-runtime": "workspace:*",
27
+ "@repo/ui": "workspace:*",
28
+ "@tanstack/react-router": "1.170.6",
29
+ cmdk: "^1.1.1",
30
+ react: "^19.2.0",
31
+ },
32
+ devDependencies: {
33
+ "@repo/typescript-config": "workspace:*",
34
+ typescript: "^6.0.3",
35
+ vitest: "^3.0.5",
36
+ },
37
+ },
38
+ });
39
+
40
+ assert.deepEqual(packageJson, {
41
+ name: "generated-app",
42
+ private: true,
43
+ license: "SEE LICENSE IN LICENSE.md",
44
+ type: "module",
45
+ scripts: {
46
+ dev: "vite dev",
47
+ test: "vitest run",
48
+ },
49
+ dependencies: {
50
+ "@tanstack/react-router": "1.170.6",
51
+ cmdk: "^1.1.1",
52
+ react: "^19.2.0",
53
+ },
54
+ devDependencies: {
55
+ typescript: "^6.0.3",
56
+ vitest: "^3.0.5",
57
+ },
58
+ });
59
+ });
60
+
61
+ it("rejects missing starter package manifests", () => {
62
+ assert.throws(
63
+ () => createGeneratedPackageJson({ name: "Generated App" }),
64
+ /starterPackageJson is required/,
65
+ );
66
+ });
67
+ });
@@ -0,0 +1,85 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import { TOOLCRAFT_IMPORT_REWRITES } from "./import-map.mjs";
5
+
6
+ const TEXT_FILE_EXTENSIONS = new Set([
7
+ ".css",
8
+ ".html",
9
+ ".json",
10
+ ".js",
11
+ ".jsx",
12
+ ".md",
13
+ ".mdx",
14
+ ".mjs",
15
+ ".ts",
16
+ ".tsx",
17
+ ]);
18
+
19
+ const IGNORED_DIRECTORY_NAMES = new Set([".git", "dist", "node_modules"]);
20
+
21
+ export function escapeRegExp(value) {
22
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
23
+ }
24
+
25
+ export function rewriteImportSpecifiers(source, importMap = TOOLCRAFT_IMPORT_REWRITES) {
26
+ let output = source;
27
+
28
+ for (const [from, to] of importMap) {
29
+ const exactQuotedSpecifier = new RegExp(`(["'])${escapeRegExp(from)}\\1`, "g");
30
+ output = output.replace(exactQuotedSpecifier, (_match, quote) => `${quote}${to}${quote}`);
31
+ }
32
+
33
+ return output;
34
+ }
35
+
36
+ export function rewriteStarterCssSources(source) {
37
+ return source
38
+ .replaceAll('@source "../../packages/ui/src";', '@source "./toolcraft/ui";')
39
+ .replaceAll(
40
+ '@source "../../packages/toolcraft-runtime/src";',
41
+ '@source "./toolcraft/runtime";',
42
+ );
43
+ }
44
+
45
+ export function rewriteGeneratedText(source) {
46
+ return rewriteStarterCssSources(rewriteImportSpecifiers(source));
47
+ }
48
+
49
+ export function isTextFile(filePath) {
50
+ return TEXT_FILE_EXTENSIONS.has(path.extname(filePath));
51
+ }
52
+
53
+ export async function rewriteTextFiles(rootDir, transform = rewriteGeneratedText) {
54
+ const changedFiles = [];
55
+
56
+ async function visit(currentDir) {
57
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
58
+
59
+ for (const entry of entries) {
60
+ const filePath = path.join(currentDir, entry.name);
61
+
62
+ if (entry.isDirectory()) {
63
+ if (!IGNORED_DIRECTORY_NAMES.has(entry.name)) {
64
+ await visit(filePath);
65
+ }
66
+ continue;
67
+ }
68
+
69
+ if (!entry.isFile() || !isTextFile(filePath)) {
70
+ continue;
71
+ }
72
+
73
+ const current = await fs.readFile(filePath, "utf8");
74
+ const next = transform(current, filePath);
75
+
76
+ if (next !== current) {
77
+ await fs.writeFile(filePath, next);
78
+ changedFiles.push(filePath);
79
+ }
80
+ }
81
+ }
82
+
83
+ await visit(rootDir);
84
+ return changedFiles;
85
+ }
@@ -0,0 +1,58 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ import { rewriteGeneratedText, rewriteImportSpecifiers } from "./rewrite-imports.mjs";
5
+
6
+ describe("rewriteImportSpecifiers", () => {
7
+ it("rewrites exact workspace package imports to local toolcraft imports", () => {
8
+ const source = `
9
+ import { defineToolcraft } from "@repo/toolcraft-runtime";
10
+ import { ToolcraftApp } from "@repo/toolcraft-runtime/react";
11
+ import { Button } from '@repo/ui';
12
+ import { Tooltip } from "@repo/ui/primitives";
13
+ import { Panel } from "@repo/ui/panel";
14
+ import { Slider } from "@repo/ui/controls";
15
+ import { getToolcraftComponentContract } from "@repo/toolcraft-runtime/contracts";
16
+ import "@repo/ui/styles.css";
17
+ `;
18
+
19
+ assert.equal(
20
+ rewriteImportSpecifiers(source),
21
+ `
22
+ import { defineToolcraft } from "@/toolcraft/runtime";
23
+ import { ToolcraftApp } from "@/toolcraft/runtime/react";
24
+ import { Button } from '@/toolcraft/ui';
25
+ import { Tooltip } from "@/toolcraft/ui/components/primitives";
26
+ import { Panel } from "@/toolcraft/ui/components/panel";
27
+ import { Slider } from "@/toolcraft/ui/components/controls";
28
+ import { getToolcraftComponentContract } from "@/toolcraft/runtime/contracts/component-contracts";
29
+ import "@/toolcraft/ui/styles.css";
30
+ `,
31
+ );
32
+ });
33
+
34
+ it("does not rewrite package names that only share a prefix", () => {
35
+ const source = `import value from "@repo/ui-extra";`;
36
+
37
+ assert.equal(rewriteImportSpecifiers(source), source);
38
+ });
39
+ });
40
+
41
+ describe("rewriteGeneratedText", () => {
42
+ it("rewrites starter css package sources", () => {
43
+ const source = `
44
+ @source "../../packages/ui/src";
45
+ @source "../../packages/toolcraft-runtime/src";
46
+ @import "@repo/toolcraft-runtime/styles.css";
47
+ `;
48
+
49
+ assert.equal(
50
+ rewriteGeneratedText(source),
51
+ `
52
+ @source "./toolcraft/ui";
53
+ @source "./toolcraft/runtime";
54
+ @import "@/toolcraft/runtime/styles.css";
55
+ `,
56
+ );
57
+ });
58
+ });