@craft-ts/dev-tools 0.7.0-beta.13

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 (237) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -0
  3. package/generators.json +26 -0
  4. package/package.json +119 -0
  5. package/src/bin/craft-architecture-check.d.ts +2 -0
  6. package/src/bin/craft-architecture-check.js +51 -0
  7. package/src/bin/craft-architecture-check.js.map +1 -0
  8. package/src/bin/craft-brand.d.ts +2 -0
  9. package/src/bin/craft-brand.js +9 -0
  10. package/src/bin/craft-brand.js.map +1 -0
  11. package/src/bin/craft-graph.d.ts +2 -0
  12. package/src/bin/craft-graph.js +72 -0
  13. package/src/bin/craft-graph.js.map +1 -0
  14. package/src/bin/craft-migrate-architecture.d.ts +2 -0
  15. package/src/bin/craft-migrate-architecture.js +64 -0
  16. package/src/bin/craft-migrate-architecture.js.map +1 -0
  17. package/src/bin/craft-migrate-components.d.ts +2 -0
  18. package/src/bin/craft-migrate-components.js +67 -0
  19. package/src/bin/craft-migrate-components.js.map +1 -0
  20. package/src/bin/craft-migrate-primitives.d.ts +2 -0
  21. package/src/bin/craft-migrate-primitives.js +71 -0
  22. package/src/bin/craft-migrate-primitives.js.map +1 -0
  23. package/src/bin/craft-migrate-routes.d.ts +2 -0
  24. package/src/bin/craft-migrate-routes.js +77 -0
  25. package/src/bin/craft-migrate-routes.js.map +1 -0
  26. package/src/bin/craft-migrate-services.d.ts +2 -0
  27. package/src/bin/craft-migrate-services.js +75 -0
  28. package/src/bin/craft-migrate-services.js.map +1 -0
  29. package/src/bin/craft-migrate-template.d.ts +2 -0
  30. package/src/bin/craft-migrate-template.js +68 -0
  31. package/src/bin/craft-migrate-template.js.map +1 -0
  32. package/src/bin/craft-migrate.d.ts +2 -0
  33. package/src/bin/craft-migrate.js +91 -0
  34. package/src/bin/craft-migrate.js.map +1 -0
  35. package/src/bin/craft.d.ts +2 -0
  36. package/src/bin/craft.js +318 -0
  37. package/src/bin/craft.js.map +1 -0
  38. package/src/eslint-rules/anchor-has-href.cjs +35 -0
  39. package/src/eslint-rules/app-start-registry-match.cjs +355 -0
  40. package/src/eslint-rules/button-has-type.cjs +50 -0
  41. package/src/eslint-rules/control-has-accessible-name.cjs +56 -0
  42. package/src/eslint-rules/craft-component-name-match.cjs +8 -0
  43. package/src/eslint-rules/craft-computed-name-match.cjs +63 -0
  44. package/src/eslint-rules/craft-css-token-registry.cjs +17 -0
  45. package/src/eslint-rules/craft-css-var-naming.cjs +18 -0
  46. package/src/eslint-rules/craft-css-vars-contract.cjs +34 -0
  47. package/src/eslint-rules/craft-directive-name-match.cjs +8 -0
  48. package/src/eslint-rules/craft-method-name-match.cjs +8 -0
  49. package/src/eslint-rules/craft-name-match-utils.cjs +179 -0
  50. package/src/eslint-rules/craft-primitive-context.cjs +46 -0
  51. package/src/eslint-rules/craft-signal-source-name-match.cjs +8 -0
  52. package/src/eslint-rules/craft-source-name-match.cjs +8 -0
  53. package/src/eslint-rules/craft-styles-scope-safe.cjs +25 -0
  54. package/src/eslint-rules/css-rule-utils.cjs +109 -0
  55. package/src/eslint-rules/global-exception-registry-match.cjs +562 -0
  56. package/src/eslint-rules/heading-has-content.cjs +31 -0
  57. package/src/eslint-rules/html-helpers.cjs +115 -0
  58. package/src/eslint-rules/hyperscript-walk.cjs +249 -0
  59. package/src/eslint-rules/iframe-has-title.cjs +26 -0
  60. package/src/eslint-rules/img-has-alt.cjs +28 -0
  61. package/src/eslint-rules/index.cjs +209 -0
  62. package/src/eslint-rules/index.d.cts +5 -0
  63. package/src/eslint-rules/label-has-associated-control.cjs +67 -0
  64. package/src/eslint-rules/no-async-await.cjs +46 -0
  65. package/src/eslint-rules/no-craft-computed-side-effects.cjs +301 -0
  66. package/src/eslint-rules/no-craft-use-in-template.cjs +89 -0
  67. package/src/eslint-rules/no-craft-use.cjs +42 -0
  68. package/src/eslint-rules/no-direct-temporal-globals.cjs +73 -0
  69. package/src/eslint-rules/no-effect-in-params.cjs +265 -0
  70. package/src/eslint-rules/no-effect-outside-loaders.cjs +3 -0
  71. package/src/eslint-rules/no-ephemeral-template-form-state.cjs +131 -0
  72. package/src/eslint-rules/no-hardcoded-design-values.cjs +15 -0
  73. package/src/eslint-rules/no-heading-level-skip.cjs +101 -0
  74. package/src/eslint-rules/no-imperative-craft-resource-trigger.cjs +291 -0
  75. package/src/eslint-rules/no-important-in-component-styles.cjs +12 -0
  76. package/src/eslint-rules/no-injection-token.cjs +173 -0
  77. package/src/eslint-rules/no-invalid-insertion-pipe.cjs +162 -0
  78. package/src/eslint-rules/no-manual-route-provider-list.cjs +85 -0
  79. package/src/eslint-rules/no-noninteractive-element-interactions.cjs +66 -0
  80. package/src/eslint-rules/no-positive-tabindex.cjs +44 -0
  81. package/src/eslint-rules/no-redundant-primitive-insertion.cjs +361 -0
  82. package/src/eslint-rules/no-render-writes.cjs +106 -0
  83. package/src/eslint-rules/no-throw.cjs +132 -0
  84. package/src/eslint-rules/no-transition-actions.cjs +133 -0
  85. package/src/eslint-rules/no-type-assertions-in-template.cjs +211 -0
  86. package/src/eslint-rules/no-widened-route-provider-context.cjs +39 -0
  87. package/src/eslint-rules/prefer-browser-boundaries.cjs +92 -0
  88. package/src/eslint-rules/prefer-craft-http-transport.cjs +103 -0
  89. package/src/eslint-rules/prefer-craft-template-blocks.cjs +492 -0
  90. package/src/eslint-rules/prefer-direct-yieldable-callback.cjs +105 -0
  91. package/src/eslint-rules/prefer-named-html-helpers.cjs +33 -0
  92. package/src/eslint-rules/prefer-query-method-over-state-trigger.cjs +282 -0
  93. package/src/eslint-rules/prefer-relative-heading.cjs +43 -0
  94. package/src/eslint-rules/provide-host-name-match-component.cjs +455 -0
  95. package/src/eslint-rules/recommended-config.cjs +77 -0
  96. package/src/eslint-rules/require-assert-exhaustive-route-exceptions.cjs +259 -0
  97. package/src/eslint-rules/require-cascade-route-di-check.cjs +223 -0
  98. package/src/eslint-rules/require-child-route-mount-check.cjs +224 -0
  99. package/src/eslint-rules/require-component-monitoring.cjs +212 -0
  100. package/src/eslint-rules/require-craft-exception-handler.cjs +142 -0
  101. package/src/eslint-rules/require-craft-method-for-yieldable-callback.cjs +236 -0
  102. package/src/eslint-rules/require-craft-resource-trigger-yield.cjs +209 -0
  103. package/src/eslint-rules/require-effect-adapters.cjs +86 -0
  104. package/src/eslint-rules/require-exception-component-di-check.cjs +314 -0
  105. package/src/eslint-rules/require-focus-visible.cjs +36 -0
  106. package/src/eslint-rules/require-interactive-local-name.cjs +105 -0
  107. package/src/eslint-rules/require-lazy-load-with-retry.cjs +148 -0
  108. package/src/eslint-rules/require-outlet-heading-section.cjs +70 -0
  109. package/src/eslint-rules/require-pending-component-di-check.cjs +490 -0
  110. package/src/eslint-rules/require-primitive-context.cjs +156 -0
  111. package/src/eslint-rules/require-primitive-derived-property.cjs +598 -0
  112. package/src/eslint-rules/require-primitive-generator-unwrap.cjs +274 -0
  113. package/src/eslint-rules/require-reactive-template-bindings.cjs +305 -0
  114. package/src/eslint-rules/require-reduced-motion.cjs +38 -0
  115. package/src/eslint-rules/require-route-heading-outline.cjs +166 -0
  116. package/src/eslint-rules/require-yieldable-insertion-write.cjs +98 -0
  117. package/src/eslint-rules/require-yieldable-reactive-read.cjs +102 -0
  118. package/src/eslint-rules/require-yieldable-template-method.cjs +424 -0
  119. package/src/eslint-rules/role-has-required-aria.cjs +38 -0
  120. package/src/eslint-rules/server-function-client-match.cjs +283 -0
  121. package/src/eslint-rules/target-blank-noopener.cjs +31 -0
  122. package/src/eslint-rules/template-element-name-unique.cjs +124 -0
  123. package/src/eslint-rules/valid-aria.cjs +38 -0
  124. package/src/generators/route/compat.d.ts +3 -0
  125. package/src/generators/route/compat.js +6 -0
  126. package/src/generators/route/compat.js.map +1 -0
  127. package/src/generators/route/generator.d.ts +31 -0
  128. package/src/generators/route/generator.js +402 -0
  129. package/src/generators/route/generator.js.map +1 -0
  130. package/src/generators/route/schema.json +50 -0
  131. package/src/generators/route/split-schema.json +19 -0
  132. package/src/index.d.ts +21 -0
  133. package/src/index.js +23 -0
  134. package/src/index.js.map +1 -0
  135. package/src/scripts/angular-brand-codemod.d.ts +139 -0
  136. package/src/scripts/angular-brand-codemod.js +2318 -0
  137. package/src/scripts/angular-brand-codemod.js.map +1 -0
  138. package/src/scripts/angular-brand-codemod.ts +3985 -0
  139. package/src/scripts/architecture/migrate-architecture.d.ts +14 -0
  140. package/src/scripts/architecture/migrate-architecture.js +534 -0
  141. package/src/scripts/architecture/migrate-architecture.js.map +1 -0
  142. package/src/scripts/architecture/migrate-architecture.ts +653 -0
  143. package/src/scripts/architecture-graph.d.ts +368 -0
  144. package/src/scripts/architecture-graph.js +2191 -0
  145. package/src/scripts/architecture-graph.js.map +1 -0
  146. package/src/scripts/architecture-graph.ts +3136 -0
  147. package/src/scripts/components/migrate-components.d.ts +20 -0
  148. package/src/scripts/components/migrate-components.js +163 -0
  149. package/src/scripts/components/migrate-components.js.map +1 -0
  150. package/src/scripts/components/migrate-components.ts +227 -0
  151. package/src/scripts/components/migration-diagnostic.d.ts +7 -0
  152. package/src/scripts/components/migration-diagnostic.js +2 -0
  153. package/src/scripts/components/migration-diagnostic.js.map +1 -0
  154. package/src/scripts/components/migration-diagnostic.ts +8 -0
  155. package/src/scripts/create/create-project.d.ts +20 -0
  156. package/src/scripts/create/create-project.js +783 -0
  157. package/src/scripts/create/create-project.js.map +1 -0
  158. package/src/scripts/create/create-project.ts +849 -0
  159. package/src/scripts/data-flow-graph.d.ts +36 -0
  160. package/src/scripts/data-flow-graph.js +186 -0
  161. package/src/scripts/data-flow-graph.js.map +1 -0
  162. package/src/scripts/data-flow-graph.ts +240 -0
  163. package/src/scripts/dependency-graph.d.ts +152 -0
  164. package/src/scripts/dependency-graph.js +4456 -0
  165. package/src/scripts/dependency-graph.js.map +1 -0
  166. package/src/scripts/dependency-graph.ts +5908 -0
  167. package/src/scripts/effect-dependency-graph.d.ts +89 -0
  168. package/src/scripts/effect-dependency-graph.js +487 -0
  169. package/src/scripts/effect-dependency-graph.js.map +1 -0
  170. package/src/scripts/effect-dependency-graph.ts +630 -0
  171. package/src/scripts/migrate.d.ts +37 -0
  172. package/src/scripts/migrate.js +98 -0
  173. package/src/scripts/migrate.js.map +1 -0
  174. package/src/scripts/migrate.ts +162 -0
  175. package/src/scripts/migration-workspace.d.ts +2 -0
  176. package/src/scripts/migration-workspace.js +67 -0
  177. package/src/scripts/migration-workspace.js.map +1 -0
  178. package/src/scripts/migration-workspace.ts +93 -0
  179. package/src/scripts/primitives/migrate-named-primitives.d.ts +50 -0
  180. package/src/scripts/primitives/migrate-named-primitives.js +487 -0
  181. package/src/scripts/primitives/migrate-named-primitives.js.map +1 -0
  182. package/src/scripts/primitives/migrate-named-primitives.ts +627 -0
  183. package/src/scripts/primitives/migrate-primitive-generators.d.ts +30 -0
  184. package/src/scripts/primitives/migrate-primitive-generators.js +309 -0
  185. package/src/scripts/primitives/migrate-primitive-generators.js.map +1 -0
  186. package/src/scripts/primitives/migrate-primitive-generators.ts +410 -0
  187. package/src/scripts/primitives/migrate-primitives.d.ts +27 -0
  188. package/src/scripts/primitives/migrate-primitives.js +427 -0
  189. package/src/scripts/primitives/migrate-primitives.js.map +1 -0
  190. package/src/scripts/primitives/migrate-primitives.ts +655 -0
  191. package/src/scripts/primitives/migrate-yieldable-reactive-reads.d.ts +23 -0
  192. package/src/scripts/primitives/migrate-yieldable-reactive-reads.js +382 -0
  193. package/src/scripts/primitives/migrate-yieldable-reactive-reads.js.map +1 -0
  194. package/src/scripts/primitives/migrate-yieldable-reactive-reads.ts +499 -0
  195. package/src/scripts/primitives/migration-diagnostic.d.ts +8 -0
  196. package/src/scripts/primitives/migration-diagnostic.js +2 -0
  197. package/src/scripts/primitives/migration-diagnostic.js.map +1 -0
  198. package/src/scripts/primitives/migration-diagnostic.ts +15 -0
  199. package/src/scripts/routes/migrate-routes.d.ts +34 -0
  200. package/src/scripts/routes/migrate-routes.js +657 -0
  201. package/src/scripts/routes/migrate-routes.js.map +1 -0
  202. package/src/scripts/routes/migrate-routes.ts +895 -0
  203. package/src/scripts/routes/migration-diagnostic.d.ts +16 -0
  204. package/src/scripts/routes/migration-diagnostic.js +2 -0
  205. package/src/scripts/routes/migration-diagnostic.js.map +1 -0
  206. package/src/scripts/routes/migration-diagnostic.ts +25 -0
  207. package/src/scripts/routes/route-command.d.ts +75 -0
  208. package/src/scripts/routes/route-command.js +1169 -0
  209. package/src/scripts/routes/route-command.js.map +1 -0
  210. package/src/scripts/routes/route-command.ts +1807 -0
  211. package/src/scripts/routes/verify-routes.d.ts +60 -0
  212. package/src/scripts/routes/verify-routes.js +1121 -0
  213. package/src/scripts/routes/verify-routes.js.map +1 -0
  214. package/src/scripts/routes/verify-routes.ts +1511 -0
  215. package/src/scripts/services/config.d.ts +2 -0
  216. package/src/scripts/services/config.js +38 -0
  217. package/src/scripts/services/config.js.map +1 -0
  218. package/src/scripts/services/config.ts +60 -0
  219. package/src/scripts/services/migrate-services.d.ts +29 -0
  220. package/src/scripts/services/migrate-services.js +924 -0
  221. package/src/scripts/services/migrate-services.js.map +1 -0
  222. package/src/scripts/services/migrate-services.ts +1348 -0
  223. package/src/scripts/services/migration-diagnostic.d.ts +8 -0
  224. package/src/scripts/services/migration-diagnostic.js +2 -0
  225. package/src/scripts/services/migration-diagnostic.js.map +1 -0
  226. package/src/scripts/services/migration-diagnostic.ts +28 -0
  227. package/src/scripts/template/migrate-template.d.ts +16 -0
  228. package/src/scripts/template/migrate-template.js +349 -0
  229. package/src/scripts/template/migrate-template.js.map +1 -0
  230. package/src/scripts/template/migrate-template.ts +418 -0
  231. package/src/template-migration.d.ts +1 -0
  232. package/src/template-migration.js +2 -0
  233. package/src/template-migration.js.map +1 -0
  234. package/src/testing.d.ts +19 -0
  235. package/src/testing.js +2 -0
  236. package/src/testing.js.map +1 -0
  237. package/src/tsconfig.codemod.json +13 -0
@@ -0,0 +1,499 @@
1
+ import {
2
+ ArrowFunction,
3
+ CallExpression,
4
+ FunctionDeclaration,
5
+ FunctionExpression,
6
+ Identifier,
7
+ MethodDeclaration,
8
+ Node,
9
+ SourceFile,
10
+ SyntaxKind,
11
+ } from 'ts-morph';
12
+ import { dirname, relative, resolve } from 'node:path';
13
+
14
+ const CRAFT_USE = 'craftUse';
15
+ const GENERATOR_HOSTS = new Set([
16
+ 'asyncProcess',
17
+ 'craftComponent',
18
+ 'craftComputed',
19
+ 'craftService',
20
+ 'query',
21
+ 'queryParams',
22
+ 'mutation',
23
+ 'state',
24
+ ]);
25
+
26
+ export type YieldableReactiveReadMigrationResult = {
27
+ changed: boolean;
28
+ changedReads: number;
29
+ };
30
+
31
+ /**
32
+ * Converts the old synchronous reader escape hatch into the public yieldable
33
+ * reader contract.
34
+ *
35
+ * A reader used in a generator-capable callback becomes a local binding:
36
+ *
37
+ * ```ts
38
+ * const item = craftUse(state()).find(...)
39
+ * // becomes
40
+ * const _state = yield* state();
41
+ * const item = _state.find(...)
42
+ * ```
43
+ *
44
+ * Ordinary callbacks keep `craftUse(...)`, but never through an alias such as
45
+ * `__craftRead`. This makes the migration output match the public API and
46
+ * keeps non-generator boundaries explicit.
47
+ */
48
+ export function migrateYieldableReactiveReads(
49
+ sourceFile: SourceFile,
50
+ ): YieldableReactiveReadMigrationResult {
51
+ const craftUseAliases = findCraftUseAliases(sourceFile);
52
+
53
+ let changed = false;
54
+ let changedReads = 0;
55
+
56
+ // Promote compatible callbacks before processing their readers. This is
57
+ // intentionally iterative because a reader can be nested in an insertion
58
+ // method inside a primitive factory.
59
+ for (let pass = 0; pass < 4; pass += 1) {
60
+ const wrappers = findCraftUseWrappers(sourceFile, craftUseAliases);
61
+ let promoted = false;
62
+ for (const wrapper of wrappers) {
63
+ if (wrapper.wasForgotten()) continue;
64
+ const argument = unwrap(wrapper.getArguments()[0]);
65
+ if (!argument || !isReactiveReaderInvocation(argument, true)) continue;
66
+ const functionLike = nearestFunction(wrapper);
67
+ if (
68
+ functionLike &&
69
+ !isGenerator(functionLike) &&
70
+ canPromoteToGenerator(functionLike)
71
+ ) {
72
+ promoteToGenerator(functionLike);
73
+ promoted = true;
74
+ changed = true;
75
+ }
76
+ }
77
+ if (!promoted) break;
78
+ }
79
+
80
+ for (const wrapper of findCraftUseWrappers(
81
+ sourceFile,
82
+ craftUseAliases,
83
+ ).sort((left, right) => right.getStart() - left.getStart())) {
84
+ if (wrapper.wasForgotten()) continue;
85
+ const argument = unwrap(wrapper.getArguments()[0]);
86
+ if (!argument || !isReactiveReaderInvocation(argument, true)) continue;
87
+
88
+ const functionLike = nearestFunction(wrapper);
89
+ if (functionLike && isGenerator(functionLike)) {
90
+ const name = createReaderBindingName(functionLike, argument);
91
+ insertReaderBinding(wrapper, name, argument.getText());
92
+ wrapper.replaceWithText(name);
93
+ changed = true;
94
+ changedReads += 1;
95
+ } else {
96
+ wrapper.getExpression().replaceWithText(CRAFT_USE);
97
+ changed = true;
98
+ changedReads += 1;
99
+ }
100
+ }
101
+
102
+ // Handle readers that were not hidden behind craftUse. This is useful for
103
+ // partially migrated files and makes the codemod idempotent across passes.
104
+ for (const call of sourceFile
105
+ .getDescendantsOfKind(SyntaxKind.CallExpression)
106
+ .sort((left, right) => right.getStart() - left.getStart())) {
107
+ if (call.wasForgotten() || !isReactiveReaderInvocation(call, false)) continue;
108
+ if (isConsumedReader(call)) continue;
109
+
110
+ const functionLike = nearestFunction(call);
111
+ if (!functionLike || !isGenerator(functionLike)) continue;
112
+
113
+ const name = createReaderBindingName(functionLike, call);
114
+ insertReaderBinding(call, name, call.getText());
115
+ call.replaceWithText(name);
116
+ changed = true;
117
+ changedReads += 1;
118
+ }
119
+
120
+ changed = normalizeCraftUseImports(sourceFile, craftUseAliases) || changed;
121
+ return { changed, changedReads };
122
+ }
123
+
124
+ function findCraftUseAliases(sourceFile: SourceFile): Set<string> {
125
+ const aliases = new Set<string>();
126
+ for (const declaration of sourceFile.getImportDeclarations()) {
127
+ const specifier = declaration
128
+ .getNamedImports()
129
+ .find((item) => item.getName() === CRAFT_USE);
130
+ if (specifier) aliases.add(specifier.getAliasNode()?.getText() ?? CRAFT_USE);
131
+ }
132
+ return aliases;
133
+ }
134
+
135
+ function findCraftUseWrappers(
136
+ sourceFile: SourceFile,
137
+ aliases: Set<string>,
138
+ ): CallExpression[] {
139
+ return sourceFile
140
+ .getDescendantsOfKind(SyntaxKind.CallExpression)
141
+ .filter((call) => {
142
+ const expression = call.getExpression();
143
+ return (
144
+ Node.isIdentifier(expression) &&
145
+ aliases.has(expression.getText()) &&
146
+ !isShadowed(expression) &&
147
+ call.getArguments().length === 1
148
+ );
149
+ });
150
+ }
151
+
152
+ function isShadowed(identifier: Identifier): boolean {
153
+ const name = identifier.getText();
154
+ let node: Node | undefined = identifier.getParent();
155
+ while (node) {
156
+ if (
157
+ Node.isFunctionDeclaration(node) ||
158
+ Node.isFunctionExpression(node) ||
159
+ Node.isArrowFunction(node) ||
160
+ Node.isMethodDeclaration(node)
161
+ ) {
162
+ for (const parameter of node.getParameters()) {
163
+ const binding = parameter.getNameNode();
164
+ if (binding.getText() === name) return true;
165
+ if (
166
+ Node.isObjectBindingPattern(binding) &&
167
+ binding.getElements().some((element) => element.getName() === name)
168
+ )
169
+ return true;
170
+ }
171
+ }
172
+ node = node.getParent();
173
+ }
174
+ return false;
175
+ }
176
+
177
+ function isReactiveReaderInvocation(
178
+ node: Node,
179
+ allowSyntacticReader: boolean,
180
+ ): node is CallExpression {
181
+ if (!Node.isCallExpression(node) || node.getArguments().length !== 0) {
182
+ return false;
183
+ }
184
+ const expression = node.getExpression();
185
+ if (
186
+ allowSyntacticReader &&
187
+ (Node.isIdentifier(expression) || Node.isPropertyAccessExpression(expression))
188
+ ) {
189
+ return true;
190
+ }
191
+ if (allowSyntacticReader && Node.isElementAccessExpression(expression)) return true;
192
+ return hasReactiveReaderBrand(expression.getType(), new Set());
193
+ }
194
+
195
+ function hasReactiveReaderBrand(type: any, seen: Set<any>): boolean {
196
+ if (!type || seen.has(type)) return false;
197
+ seen.add(type);
198
+ if (type.isUnion?.())
199
+ return type.getUnionTypes().some((part: any) => hasReactiveReaderBrand(part, seen));
200
+ if (type.isIntersection?.())
201
+ return type
202
+ .getIntersectionTypes()
203
+ .some((part: any) => hasReactiveReaderBrand(part, seen));
204
+ return type
205
+ .getProperties?.()
206
+ .some((property: any) => property.getName().includes('RAW_REACTIVE_VALUE'));
207
+ }
208
+
209
+ function isConsumedReader(call: CallExpression): boolean {
210
+ let node: Node = call;
211
+ let parent = node.getParent();
212
+ while (
213
+ parent &&
214
+ (Node.isParenthesizedExpression(parent) ||
215
+ Node.isAsExpression(parent) ||
216
+ Node.isTypeAssertion(parent) ||
217
+ Node.isNonNullExpression(parent))
218
+ ) {
219
+ node = parent;
220
+ parent = node.getParent();
221
+ }
222
+ if (Node.isYieldExpression(parent) && parent.getAsteriskToken()) return true;
223
+ return (
224
+ Node.isCallExpression(parent) &&
225
+ Node.isIdentifier(parent.getExpression()) &&
226
+ parent.getExpression().getText() === CRAFT_USE
227
+ );
228
+ }
229
+
230
+ function nearestFunction(node: Node):
231
+ | ArrowFunction
232
+ | FunctionDeclaration
233
+ | FunctionExpression
234
+ | MethodDeclaration
235
+ | undefined {
236
+ return node.getFirstAncestor(
237
+ (ancestor) =>
238
+ Node.isArrowFunction(ancestor) ||
239
+ Node.isFunctionDeclaration(ancestor) ||
240
+ Node.isFunctionExpression(ancestor) ||
241
+ Node.isMethodDeclaration(ancestor),
242
+ ) as
243
+ | ArrowFunction
244
+ | FunctionDeclaration
245
+ | FunctionExpression
246
+ | MethodDeclaration
247
+ | undefined;
248
+ }
249
+
250
+ function isGenerator(
251
+ node:
252
+ | ArrowFunction
253
+ | FunctionDeclaration
254
+ | FunctionExpression
255
+ | MethodDeclaration,
256
+ ): boolean {
257
+ return !Node.isArrowFunction(node) && node.isGenerator();
258
+ }
259
+
260
+ function canPromoteToGenerator(
261
+ functionLike:
262
+ | ArrowFunction
263
+ | FunctionDeclaration
264
+ | FunctionExpression
265
+ | MethodDeclaration,
266
+ ): boolean {
267
+ if (Node.isFunctionDeclaration(functionLike)) return false;
268
+ let node: Node = functionLike;
269
+ let parent = node.getParent();
270
+ while (parent) {
271
+ if (Node.isCallExpression(parent)) {
272
+ const callee = parent.getExpression().getText();
273
+ return GENERATOR_HOSTS.has(callee) && parent.getArguments().some((arg) => arg === node);
274
+ }
275
+ if (
276
+ Node.isParenthesizedExpression(parent) ||
277
+ Node.isPropertyAssignment(parent) ||
278
+ Node.isObjectLiteralExpression(parent) ||
279
+ Node.isArrayLiteralExpression(parent) ||
280
+ Node.isArrowFunction(parent)
281
+ ) {
282
+ node = parent;
283
+ parent = node.getParent();
284
+ continue;
285
+ }
286
+ return false;
287
+ }
288
+ return false;
289
+ }
290
+
291
+ function promoteToGenerator(
292
+ functionLike:
293
+ | ArrowFunction
294
+ | FunctionDeclaration
295
+ | FunctionExpression
296
+ | MethodDeclaration,
297
+ ): void {
298
+ if (Node.isArrowFunction(functionLike)) {
299
+ const parameters = functionLike
300
+ .getParameters()
301
+ .map((parameter) => parameter.getText())
302
+ .join(', ');
303
+ const body = functionLike.getBody();
304
+ if (!body) return;
305
+ const replacement = Node.isBlock(body)
306
+ ? `function* (${parameters}) ${body.getText()}`
307
+ : `function* (${parameters}) { return ${body.getText()}; }`;
308
+ functionLike.replaceWithText(replacement);
309
+ return;
310
+ }
311
+ if (Node.isMethodDeclaration(functionLike)) {
312
+ functionLike.replaceWithText(`*${functionLike.getText()}`);
313
+ return;
314
+ }
315
+ functionLike.replaceWithText(
316
+ functionLike.getText().replace(/^function\b/, 'function*'),
317
+ );
318
+ }
319
+
320
+ function createReaderBindingName(
321
+ functionLike:
322
+ | ArrowFunction
323
+ | FunctionDeclaration
324
+ | FunctionExpression
325
+ | MethodDeclaration,
326
+ reader: CallExpression,
327
+ ): string {
328
+ const expression = reader.getExpression();
329
+ let base = Node.isIdentifier(expression)
330
+ ? expression.getText()
331
+ : Node.isPropertyAccessExpression(expression)
332
+ ? `${expression.getExpression().getText()}${expression.getName()}`
333
+ : 'value';
334
+ base = base.replace(/[^A-Za-z0-9_$]/g, '');
335
+ if (!base) base = 'value';
336
+ if (/^\d/.test(base)) base = `value${base}`;
337
+ const preferred = `_${base}`;
338
+ const used = new Set(
339
+ functionLike
340
+ .getDescendantsOfKind(SyntaxKind.Identifier)
341
+ .map((identifier) => identifier.getText()),
342
+ );
343
+ if (!used.has(preferred)) return preferred;
344
+ for (let index = 2; ; index += 1) {
345
+ const candidate = `${preferred}${index}`;
346
+ if (!used.has(candidate)) return candidate;
347
+ }
348
+ }
349
+
350
+ function insertReaderBinding(
351
+ reader: CallExpression,
352
+ name: string,
353
+ expression: string,
354
+ ): void {
355
+ const statement = reader.getFirstAncestor((ancestor) =>
356
+ Node.isStatement(ancestor),
357
+ );
358
+ if (!statement) return;
359
+ const parent = statement.getParent();
360
+ if (Node.isBlock(parent)) {
361
+ const index = parent.getStatements().indexOf(statement);
362
+ parent.insertStatements(index, `const ${name} = yield* ${expression};`);
363
+ return;
364
+ }
365
+ statement.replaceWithText(
366
+ `const ${name} = yield* ${expression};\n${statement.getText()}`,
367
+ );
368
+ }
369
+
370
+ function normalizeCraftUseImports(
371
+ sourceFile: SourceFile,
372
+ aliases: Set<string>,
373
+ ): boolean {
374
+ let changed = false;
375
+ for (const declaration of sourceFile.getImportDeclarations()) {
376
+ const specifier = declaration
377
+ .getNamedImports()
378
+ .find((item) => aliases.has(item.getAliasNode()?.getText() ?? item.getName()));
379
+ if (!specifier) continue;
380
+ const localNames = new Set(aliases);
381
+ const references = sourceFile
382
+ .getDescendantsOfKind(SyntaxKind.Identifier)
383
+ .filter(
384
+ (identifier) =>
385
+ localNames.has(identifier.getText()) &&
386
+ identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration) !== declaration,
387
+ );
388
+ const canonicalReferences = sourceFile
389
+ .getDescendantsOfKind(SyntaxKind.Identifier)
390
+ .filter(
391
+ (identifier) =>
392
+ identifier.getText() === CRAFT_USE &&
393
+ !identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration),
394
+ );
395
+ if (references.length > 0) {
396
+ for (const reference of references) {
397
+ if (reference.getText() !== CRAFT_USE) {
398
+ reference.replaceWithText(CRAFT_USE);
399
+ changed = true;
400
+ }
401
+ }
402
+ if (specifier.getAliasNode()) {
403
+ specifier.removeAlias();
404
+ changed = true;
405
+ }
406
+ } else if (canonicalReferences.length > 0) {
407
+ const hasOtherCanonicalImport = sourceFile
408
+ .getImportDeclarations()
409
+ .some(
410
+ (candidate) =>
411
+ candidate !== declaration &&
412
+ candidate
413
+ .getNamedImports()
414
+ .some((item) => item.getName() === CRAFT_USE),
415
+ );
416
+ if (hasOtherCanonicalImport) {
417
+ specifier.remove();
418
+ changed = true;
419
+ } else if (specifier.getAliasNode()) {
420
+ specifier.removeAlias();
421
+ changed = true;
422
+ }
423
+ } else {
424
+ specifier.remove();
425
+ changed = true;
426
+ if (
427
+ declaration.getNamedImports().length === 0 &&
428
+ !declaration.getDefaultImport() &&
429
+ !declaration.getNamespaceImport()
430
+ ) {
431
+ declaration.remove();
432
+ }
433
+ }
434
+ }
435
+ if (
436
+ sourceFile
437
+ .getDescendantsOfKind(SyntaxKind.Identifier)
438
+ .some(
439
+ (identifier) =>
440
+ identifier.getText() === CRAFT_USE &&
441
+ !identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration),
442
+ ) &&
443
+ !sourceFile.getImportDeclarations().some((declaration) =>
444
+ declaration
445
+ .getNamedImports()
446
+ .some((item) => item.getName() === CRAFT_USE),
447
+ )
448
+ ) {
449
+ ensureCraftUseImport(sourceFile);
450
+ changed = true;
451
+ }
452
+ return changed;
453
+ }
454
+
455
+ function ensureCraftUseImport(sourceFile: SourceFile): void {
456
+ const filePath = resolve(sourceFile.getFilePath());
457
+ const workspace = resolve(process.cwd());
458
+ const insideCore = filePath.startsWith(`${workspace}/libs/core/src/lib/`);
459
+ const moduleSpecifier = insideCore
460
+ ? localCraftUseSpecifier(filePath, workspace)
461
+ : '@craft-ts/core';
462
+ const existing = sourceFile
463
+ .getImportDeclarations()
464
+ .find((declaration) => declaration.getModuleSpecifierValue() === moduleSpecifier);
465
+ if (existing) {
466
+ existing.addNamedImport(CRAFT_USE);
467
+ return;
468
+ }
469
+ sourceFile.addImportDeclaration({
470
+ moduleSpecifier,
471
+ namedImports: [CRAFT_USE],
472
+ });
473
+ }
474
+
475
+ function localCraftUseSpecifier(filePath: string, workspace: string): string {
476
+ let specifier = relative(
477
+ dirname(filePath),
478
+ resolve(workspace, 'libs/core/src/lib/craft-use'),
479
+ ).replace(/\\/g, '/');
480
+ if (!specifier.startsWith('.')) specifier = `./${specifier}`;
481
+ return specifier;
482
+ }
483
+
484
+ function unwrap(node: Node | undefined): Node | undefined {
485
+ while (
486
+ node &&
487
+ (Node.isParenthesizedExpression(node) ||
488
+ Node.isAsExpression(node) ||
489
+ Node.isTypeAssertion(node) ||
490
+ Node.isNonNullExpression(node))
491
+ ) {
492
+ node = Node.isParenthesizedExpression(node)
493
+ ? node.getExpression()
494
+ : Node.isAsExpression(node) || Node.isTypeAssertion(node)
495
+ ? node.getExpression()
496
+ : node.getExpression();
497
+ }
498
+ return node;
499
+ }
@@ -0,0 +1,8 @@
1
+ export type PrimitiveMigrationDiagnosticCode = 'PRIMITIVE_NAME_REQUIRES_REVIEW' | 'SIGNAL_FORM_REQUIRES_INSERT_FORM' | 'ASYNC_VALIDATOR_REQUIRES_QUERY' | 'RX_RESOURCE_REQUIRES_QUERY' | 'FORM_TREE_INSERT_EXTRACTION_REQUIRES_REVIEW' | 'IMPERATIVE_WORKFLOW_REQUIRES_REVIEW';
2
+ export type PrimitiveMigrationDiagnostic = {
3
+ code: PrimitiveMigrationDiagnosticCode;
4
+ filePath: string;
5
+ symbol?: string;
6
+ message: string;
7
+ manual: true;
8
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=migration-diagnostic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-diagnostic.js","sourceRoot":"","sources":["../../../../../../libs/dev-tools/src/scripts/primitives/migration-diagnostic.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ export type PrimitiveMigrationDiagnosticCode =
2
+ | 'PRIMITIVE_NAME_REQUIRES_REVIEW'
3
+ | 'SIGNAL_FORM_REQUIRES_INSERT_FORM'
4
+ | 'ASYNC_VALIDATOR_REQUIRES_QUERY'
5
+ | 'RX_RESOURCE_REQUIRES_QUERY'
6
+ | 'FORM_TREE_INSERT_EXTRACTION_REQUIRES_REVIEW'
7
+ | 'IMPERATIVE_WORKFLOW_REQUIRES_REVIEW';
8
+
9
+ export type PrimitiveMigrationDiagnostic = {
10
+ code: PrimitiveMigrationDiagnosticCode;
11
+ filePath: string;
12
+ symbol?: string;
13
+ message: string;
14
+ manual: true;
15
+ };
@@ -0,0 +1,34 @@
1
+ import { SourceFile } from 'ts-morph';
2
+ import { RouteMigrationDiagnostic } from './migration-diagnostic.js';
3
+ export type MigrateRoutesOptions = {
4
+ rootDir?: string;
5
+ tsConfigFilePath?: string;
6
+ files?: readonly string[];
7
+ write?: boolean;
8
+ check?: boolean;
9
+ collectionName?: string;
10
+ parentMount?: string;
11
+ parentNames?: readonly string[];
12
+ jsonFilePath?: string;
13
+ failOnManual?: boolean;
14
+ log?: (message: string) => void;
15
+ };
16
+ export type MigratedRoutesFile = {
17
+ filePath: string;
18
+ changed: boolean;
19
+ collections: string[];
20
+ };
21
+ export type MigrateRoutesResult = {
22
+ changedFiles: string[];
23
+ files: MigratedRoutesFile[];
24
+ diagnostics: RouteMigrationDiagnostic[];
25
+ remainingLegacyCollections: number;
26
+ exitCode: number;
27
+ };
28
+ export declare function runRoutesMigration(options?: MigrateRoutesOptions): Promise<MigrateRoutesResult>;
29
+ export declare function migrateApplicationConfigSourceFile(sourceFile: SourceFile): boolean;
30
+ export declare function migrateBootstrapSourceFile(sourceFile: SourceFile): boolean;
31
+ export declare function migrateRoutesSourceFile(sourceFile: SourceFile, options?: Pick<MigrateRoutesOptions, 'collectionName' | 'parentMount' | 'parentNames'> & {
32
+ diagnostics?: RouteMigrationDiagnostic[];
33
+ generatedComponentFiles?: Set<SourceFile>;
34
+ }): string[];