@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,455 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const {
4
+ IndentationText,
5
+ Node,
6
+ Project,
7
+ QuoteKind,
8
+ SyntaxKind,
9
+ } = require('ts-morph');
10
+
11
+ const projectCache = new Map();
12
+
13
+ module.exports = {
14
+ meta: {
15
+ type: 'problem',
16
+ docs: {
17
+ description:
18
+ "Ensure Angular @Component and @Directive classes provide provideHostName('ClassName') in providers.",
19
+ },
20
+ fixable: 'code',
21
+ schema: [],
22
+ },
23
+ create(context) {
24
+ return {
25
+ 'Program:exit'() {
26
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
27
+ const filePath = getFilePath(context);
28
+ if (!filePath || !filePath.endsWith('.ts')) {
29
+ return;
30
+ }
31
+
32
+ const text = sourceCode.getText();
33
+ if (!text.includes('@Component') && !text.includes('@Directive')) {
34
+ return;
35
+ }
36
+
37
+ const sourceFile = getProjectSourceFile(
38
+ getProject(getCwd(context)),
39
+ filePath,
40
+ text,
41
+ );
42
+ const issues = collectHostNameIssues(sourceFile);
43
+ if (issues.length === 0) {
44
+ return;
45
+ }
46
+
47
+ const reportLoc = getNodeLoc(sourceCode, issues[0].reportNode);
48
+
49
+ for (const issue of issues) {
50
+ applyIssueFix(issue);
51
+ }
52
+ ensureProvideHostNameImport(sourceFile);
53
+
54
+ const fixedText = sourceFile.getFullText();
55
+ if (fixedText === text) {
56
+ return;
57
+ }
58
+
59
+ context.report({
60
+ loc: reportLoc,
61
+ message: formatIssueMessage(issues),
62
+ fix(fixer) {
63
+ return fixer.replaceTextRange([0, text.length], fixedText);
64
+ },
65
+ });
66
+ },
67
+ };
68
+ },
69
+ };
70
+
71
+ function getProject(cwd) {
72
+ let project = projectCache.get(cwd);
73
+ if (project) {
74
+ return project;
75
+ }
76
+
77
+ const manipulationSettings = {
78
+ indentationText: IndentationText.TwoSpaces,
79
+ quoteKind: QuoteKind.Single,
80
+ };
81
+ const tsConfigFilePath = path.join(cwd, 'tsconfig.json');
82
+ project = fs.existsSync(tsConfigFilePath)
83
+ ? new Project({
84
+ tsConfigFilePath,
85
+ manipulationSettings,
86
+ })
87
+ : new Project({
88
+ compilerOptions: {
89
+ experimentalDecorators: true,
90
+ target: 9,
91
+ },
92
+ manipulationSettings,
93
+ });
94
+
95
+ projectCache.set(cwd, project);
96
+ return project;
97
+ }
98
+
99
+ function getProjectSourceFile(project, filePath, text) {
100
+ const normalizedPath = path.resolve(filePath);
101
+ const existingSourceFile = project.getSourceFile(normalizedPath);
102
+ if (existingSourceFile) {
103
+ existingSourceFile.replaceWithText(text);
104
+ return existingSourceFile;
105
+ }
106
+
107
+ const sourceFile = project.addSourceFileAtPathIfExists(normalizedPath);
108
+ if (sourceFile) {
109
+ sourceFile.replaceWithText(text);
110
+ return sourceFile;
111
+ }
112
+
113
+ return project.createSourceFile(normalizedPath, text, { overwrite: true });
114
+ }
115
+
116
+ function getFilePath(context) {
117
+ const filePath = context.filename ?? context.getFilename();
118
+ if (!filePath || filePath === '<input>') {
119
+ return undefined;
120
+ }
121
+
122
+ return filePath;
123
+ }
124
+
125
+ function getCwd(context) {
126
+ return context.cwd ?? process.cwd();
127
+ }
128
+
129
+ function getNodeLoc(sourceCode, node) {
130
+ return {
131
+ start: sourceCode.getLocFromIndex(node.getStart()),
132
+ end: sourceCode.getLocFromIndex(node.getEnd()),
133
+ };
134
+ }
135
+
136
+ function collectHostNameIssues(sourceFile) {
137
+ const angularDecorators = collectAngularDecoratorNames(sourceFile);
138
+ if (
139
+ angularDecorators.componentNames.size === 0 &&
140
+ angularDecorators.directiveNames.size === 0 &&
141
+ angularDecorators.namespaceNames.size === 0
142
+ ) {
143
+ return [];
144
+ }
145
+
146
+ const issues = [];
147
+
148
+ for (const classDeclaration of sourceFile.getClasses()) {
149
+ const className = classDeclaration.getName();
150
+ if (!className) {
151
+ continue;
152
+ }
153
+
154
+ const decoratorMatch = findAngularDecorator(
155
+ classDeclaration,
156
+ angularDecorators,
157
+ );
158
+ if (!decoratorMatch) {
159
+ continue;
160
+ }
161
+
162
+ const metadata = getDecoratorMetadata(decoratorMatch.decorator);
163
+ if (!metadata) {
164
+ continue;
165
+ }
166
+
167
+ const providersProperty = getObjectProperty(metadata, 'providers');
168
+ if (!providersProperty) {
169
+ issues.push({
170
+ kind: 'missing-providers',
171
+ decoratorKind: decoratorMatch.kind,
172
+ className,
173
+ metadata,
174
+ reportNode: classDeclaration,
175
+ });
176
+ continue;
177
+ }
178
+
179
+ const providersArray = getArrayInitializer(providersProperty);
180
+ if (!providersArray) {
181
+ continue;
182
+ }
183
+
184
+ const hostNameCalls = findProvideHostNameCalls(providersArray);
185
+ if (hostNameCalls.length === 0) {
186
+ issues.push({
187
+ kind: 'missing-call',
188
+ decoratorKind: decoratorMatch.kind,
189
+ className,
190
+ providersArray,
191
+ reportNode: classDeclaration,
192
+ });
193
+ continue;
194
+ }
195
+
196
+ const expectedHostName = `${getHostNamePrefix(decoratorMatch.kind)}${className}`;
197
+ const matchingCall = hostNameCalls.find((callExpression) =>
198
+ isMatchingHostNameCall(callExpression, expectedHostName),
199
+ );
200
+ if (matchingCall) {
201
+ continue;
202
+ }
203
+
204
+ issues.push({
205
+ kind: 'mismatched-call',
206
+ decoratorKind: decoratorMatch.kind,
207
+ className,
208
+ callExpression: hostNameCalls[0],
209
+ reportNode: classDeclaration,
210
+ });
211
+ }
212
+
213
+ return issues;
214
+ }
215
+
216
+ function collectAngularDecoratorNames(sourceFile) {
217
+ const componentNames = new Set();
218
+ const directiveNames = new Set();
219
+ const namespaceNames = new Set();
220
+
221
+ for (const importDeclaration of sourceFile.getImportDeclarations()) {
222
+ if (importDeclaration.getModuleSpecifierValue() !== '@angular/core') {
223
+ continue;
224
+ }
225
+
226
+ for (const namedImport of importDeclaration.getNamedImports()) {
227
+ const importedName = namedImport.getName();
228
+ const localName = namedImport.getAliasNode()?.getText() ?? importedName;
229
+
230
+ if (importedName === 'Component') {
231
+ componentNames.add(localName);
232
+ }
233
+ if (importedName === 'Directive') {
234
+ directiveNames.add(localName);
235
+ }
236
+ }
237
+
238
+ const namespaceImport = importDeclaration.getNamespaceImport();
239
+ if (namespaceImport) {
240
+ namespaceNames.add(namespaceImport.getText());
241
+ }
242
+ }
243
+
244
+ return {
245
+ componentNames,
246
+ directiveNames,
247
+ namespaceNames,
248
+ };
249
+ }
250
+
251
+ function findAngularDecorator(classDeclaration, angularDecorators) {
252
+ for (const decorator of classDeclaration.getDecorators()) {
253
+ const expression = decorator.getExpression();
254
+ if (!Node.isCallExpression(expression)) {
255
+ continue;
256
+ }
257
+
258
+ const callee = expression.getExpression();
259
+ if (Node.isIdentifier(callee)) {
260
+ const decoratorName = callee.getText();
261
+ if (angularDecorators.componentNames.has(decoratorName)) {
262
+ return { decorator, kind: 'component' };
263
+ }
264
+ if (angularDecorators.directiveNames.has(decoratorName)) {
265
+ return { decorator, kind: 'directive' };
266
+ }
267
+ continue;
268
+ }
269
+
270
+ if (!Node.isPropertyAccessExpression(callee)) {
271
+ continue;
272
+ }
273
+
274
+ const objectExpression = callee.getExpression();
275
+ const propertyName = callee.getName();
276
+ if (
277
+ Node.isIdentifier(objectExpression) &&
278
+ angularDecorators.namespaceNames.has(objectExpression.getText())
279
+ ) {
280
+ if (propertyName === 'Component') {
281
+ return { decorator, kind: 'component' };
282
+ }
283
+ if (propertyName === 'Directive') {
284
+ return { decorator, kind: 'directive' };
285
+ }
286
+ }
287
+ }
288
+
289
+ return undefined;
290
+ }
291
+
292
+ function getHostNamePrefix(kind) {
293
+ return kind === 'directive' ? 'directive:' : 'component:';
294
+ }
295
+
296
+ function getDecoratorMetadata(decorator) {
297
+ const expression = decorator.getExpression();
298
+ if (!Node.isCallExpression(expression)) {
299
+ return undefined;
300
+ }
301
+
302
+ const [metadataArgument] = expression.getArguments();
303
+ if (!metadataArgument || !Node.isObjectLiteralExpression(metadataArgument)) {
304
+ return undefined;
305
+ }
306
+
307
+ return metadataArgument;
308
+ }
309
+
310
+ function getObjectProperty(objectLiteralExpression, name) {
311
+ const property = objectLiteralExpression
312
+ .getProperties()
313
+ .find(
314
+ (candidate) =>
315
+ Node.isPropertyAssignment(candidate) && candidate.getName() === name,
316
+ );
317
+
318
+ return Node.isPropertyAssignment(property) ? property : undefined;
319
+ }
320
+
321
+ function getArrayInitializer(propertyAssignment) {
322
+ const initializer = propertyAssignment.getInitializer();
323
+ if (!initializer || !Node.isArrayLiteralExpression(initializer)) {
324
+ return undefined;
325
+ }
326
+
327
+ return initializer;
328
+ }
329
+
330
+ function findProvideHostNameCalls(arrayLiteralExpression) {
331
+ return arrayLiteralExpression
332
+ .getElements()
333
+ .filter((element) => Node.isCallExpression(element))
334
+ .filter((callExpression) => isProvideHostNameCall(callExpression));
335
+ }
336
+
337
+ function isProvideHostNameCall(callExpression) {
338
+ const expression = callExpression.getExpression();
339
+ if (Node.isIdentifier(expression)) {
340
+ return expression.getText() === 'provideHostName';
341
+ }
342
+
343
+ if (Node.isPropertyAccessExpression(expression)) {
344
+ return expression.getName() === 'provideHostName';
345
+ }
346
+
347
+ return false;
348
+ }
349
+
350
+ function isMatchingHostNameCall(callExpression, expectedHostName) {
351
+ const [nameArgument] = callExpression.getArguments();
352
+ if (
353
+ !nameArgument ||
354
+ (!Node.isStringLiteral(nameArgument) &&
355
+ !Node.isNoSubstitutionTemplateLiteral(nameArgument))
356
+ ) {
357
+ return false;
358
+ }
359
+
360
+ return nameArgument.getLiteralText() === expectedHostName;
361
+ }
362
+
363
+ function applyIssueFix(issue) {
364
+ const expectedHostName = `${getHostNamePrefix(issue.decoratorKind)}${issue.className}`;
365
+ const expectedCall = `provideHostName('${expectedHostName}')`;
366
+
367
+ if (issue.kind === 'missing-providers') {
368
+ issue.metadata.addPropertyAssignment({
369
+ name: 'providers',
370
+ initializer: `[${expectedCall}]`,
371
+ });
372
+ return;
373
+ }
374
+
375
+ if (issue.kind === 'missing-call') {
376
+ issue.providersArray.addElement(expectedCall);
377
+ return;
378
+ }
379
+
380
+ if (issue.kind === 'mismatched-call') {
381
+ issue.callExpression.replaceWithText(expectedCall);
382
+ }
383
+ }
384
+
385
+ function ensureProvideHostNameImport(sourceFile) {
386
+ const coreImports = sourceFile
387
+ .getImportDeclarations()
388
+ .filter(
389
+ (importDeclaration) =>
390
+ importDeclaration.getModuleSpecifierValue() === '@craft-ts/core',
391
+ );
392
+
393
+ for (const importDeclaration of coreImports) {
394
+ const namedImports = importDeclaration.getNamedImports();
395
+ const directImport = namedImports.find(
396
+ (namedImport) =>
397
+ namedImport.getName() === 'provideHostName' &&
398
+ !namedImport.getAliasNode(),
399
+ );
400
+ if (directImport) {
401
+ return;
402
+ }
403
+ }
404
+
405
+ for (const importDeclaration of coreImports) {
406
+ for (const namedImport of importDeclaration.getNamedImports()) {
407
+ if (
408
+ namedImport.getName() === 'provideHostName' &&
409
+ namedImport.getAliasNode()
410
+ ) {
411
+ namedImport.remove();
412
+ }
413
+ }
414
+ }
415
+
416
+ const targetImport = coreImports.find(
417
+ (importDeclaration) => !importDeclaration.getNamespaceImport(),
418
+ );
419
+ if (targetImport) {
420
+ const hasNamedImport = targetImport
421
+ .getNamedImports()
422
+ .some((namedImport) => namedImport.getName() === 'provideHostName');
423
+ if (!hasNamedImport) {
424
+ targetImport.addNamedImport('provideHostName');
425
+ }
426
+ return;
427
+ }
428
+
429
+ const insertIndex = sourceFile
430
+ .getStatements()
431
+ .findIndex(
432
+ (statement) => statement.getKind() !== SyntaxKind.ImportDeclaration,
433
+ );
434
+
435
+ sourceFile.insertImportDeclaration(
436
+ insertIndex < 0 ? sourceFile.getImportDeclarations().length : insertIndex,
437
+ {
438
+ moduleSpecifier: '@craft-ts/core',
439
+ namedImports: ['provideHostName'],
440
+ },
441
+ );
442
+ }
443
+
444
+ function formatIssueMessage(issues) {
445
+ if (issues.length === 1) {
446
+ const issue = issues[0];
447
+ const expectedHostName = `${getHostNamePrefix(issue.decoratorKind)}${issue.className}`;
448
+ return `${issue.className} must include provideHostName('${expectedHostName}') in providers.`;
449
+ }
450
+
451
+ const classNames = [...new Set(issues.map((issue) => issue.className))].join(
452
+ ', ',
453
+ );
454
+ return `Classes must include provideHostName('<kind>:<ClassName>') in providers: ${classNames}.`;
455
+ }
@@ -0,0 +1,77 @@
1
+ module.exports = {
2
+ 'craft-ts/craft-method-name-match': 'warn',
3
+ 'craft-ts/craft-computed-name-match': 'warn',
4
+ 'craft-ts/craft-source-name-match': 'warn',
5
+ 'craft-ts/craft-signal-source-name-match': 'warn',
6
+ 'craft-ts/craft-component-name-match': 'warn',
7
+ 'craft-ts/craft-directive-name-match': 'warn',
8
+ 'craft-ts/no-direct-temporal-globals': 'error',
9
+ 'craft-ts/prefer-craft-template-blocks': 'error',
10
+ 'craft-ts/no-render-writes': 'error',
11
+ 'craft-ts/require-reactive-template-bindings': 'error',
12
+ 'craft-ts/craft-css-vars-contract': 'error',
13
+ 'craft-ts/craft-styles-scope-safe': 'error',
14
+ 'craft-ts/craft-css-var-naming': 'warn',
15
+ 'craft-ts/craft-css-token-registry': 'error',
16
+ 'craft-ts/no-hardcoded-design-values': 'warn',
17
+ 'craft-ts/no-important-in-component-styles': 'error',
18
+ 'craft-ts/no-craft-computed-side-effects': 'error',
19
+ 'craft-ts/provide-host-name-match-component': 'warn',
20
+ 'craft-ts/prefer-craft-http-transport': 'error',
21
+ 'craft-ts/no-imperative-craft-resource-trigger': 'error',
22
+ 'craft-ts/no-transition-actions': 'error',
23
+ 'craft-ts/require-craft-resource-trigger-yield': 'error',
24
+ 'craft-ts/prefer-browser-boundaries': 'warn',
25
+ 'craft-ts/app-start-registry-match': 'warn',
26
+ 'craft-ts/global-exception-registry-match': 'warn',
27
+ 'craft-ts/require-component-monitoring': 'warn',
28
+ 'craft-ts/require-primitive-generator-unwrap': 'warn',
29
+ 'craft-ts/require-yieldable-template-method': 'error',
30
+ 'craft-ts/require-craft-method-for-yieldable-callback': 'error',
31
+ 'craft-ts/require-yieldable-reactive-read': 'error',
32
+ 'craft-ts/require-yieldable-insertion-write': 'error',
33
+ 'craft-ts/prefer-direct-yieldable-callback': 'error',
34
+ 'craft-ts/no-craft-use': 'error',
35
+ 'craft-ts/no-type-assertions-in-template': 'error',
36
+ 'craft-ts/no-ephemeral-template-form-state': 'error',
37
+ 'craft-ts/template-element-name-unique': 'error',
38
+ 'craft-ts/require-primitive-context': 'error',
39
+ 'craft-ts/require-primitive-derived-property': 'error',
40
+ 'craft-ts/no-invalid-insertion-pipe': 'error',
41
+ 'craft-ts/no-redundant-primitive-insertion': 'error',
42
+ 'craft-ts/prefer-query-method-over-state-trigger': 'error',
43
+ 'craft-ts/no-injection-token': 'error',
44
+ 'craft-ts/no-manual-route-provider-list': 'error',
45
+ 'craft-ts/no-widened-route-provider-context': 'error',
46
+ 'craft-ts/no-async-await': 'error',
47
+ 'craft-ts/no-throw': 'error',
48
+ 'craft-ts/require-assert-exhaustive-route-exceptions': 'warn',
49
+ 'craft-ts/require-pending-component-di-check': 'warn',
50
+ 'craft-ts/require-craft-exception-handler': 'warn',
51
+ 'craft-ts/require-exception-component-di-check': 'warn',
52
+ // These aggregate proofs are too expensive for broad app configs and can
53
+ // trigger TS2589 when route graphs are large.
54
+ 'craft-ts/require-child-route-mount-check': 'off',
55
+ 'craft-ts/require-lazy-load-with-retry': 'warn',
56
+ 'craft-ts/require-cascade-route-di-check': 'off',
57
+ 'craft-ts/prefer-named-html-helpers': 'error',
58
+ 'craft-ts/img-has-alt': 'error',
59
+ 'craft-ts/control-has-accessible-name': 'error',
60
+ 'craft-ts/label-has-associated-control': 'error',
61
+ 'craft-ts/no-noninteractive-element-interactions': 'error',
62
+ 'craft-ts/anchor-has-href': 'error',
63
+ 'craft-ts/button-has-type': 'error',
64
+ 'craft-ts/require-interactive-local-name': 'error',
65
+ 'craft-ts/iframe-has-title': 'error',
66
+ 'craft-ts/heading-has-content': 'error',
67
+ 'craft-ts/prefer-relative-heading': 'error',
68
+ 'craft-ts/require-route-heading-outline': 'error',
69
+ 'craft-ts/require-outlet-heading-section': 'error',
70
+ 'craft-ts/no-heading-level-skip': 'error',
71
+ 'craft-ts/no-positive-tabindex': 'error',
72
+ 'craft-ts/valid-aria': 'error',
73
+ 'craft-ts/role-has-required-aria': 'error',
74
+ 'craft-ts/target-blank-noopener': 'error',
75
+ 'craft-ts/require-focus-visible': 'error',
76
+ 'craft-ts/require-reduced-motion': 'error',
77
+ };