@invariant-ui/cli 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +248 -2
  3. package/dist/agent-kit/apply.js +232 -0
  4. package/dist/agent-kit/guidance.js +236 -0
  5. package/dist/agent-kit/index.js +5 -0
  6. package/dist/agent-kit/integrations.js +121 -0
  7. package/dist/agent-kit/plugin.js +115 -0
  8. package/dist/artifact-generator/index.js +75 -0
  9. package/dist/browser-verify/driver.js +2 -0
  10. package/dist/browser-verify/index.js +4 -0
  11. package/dist/browser-verify/playwright.js +82 -0
  12. package/dist/browser-verify/runner.js +175 -0
  13. package/dist/browser-verify/spec.js +103 -0
  14. package/dist/cli/args.js +77 -0
  15. package/dist/cli/baseline.js +64 -0
  16. package/dist/cli/cli.js +121 -0
  17. package/dist/cli/commands-browser.js +104 -0
  18. package/dist/cli/commands-discover.js +312 -0
  19. package/dist/cli/commands-enforcement.js +35 -0
  20. package/dist/cli/commands-governance.js +179 -0
  21. package/dist/cli/commands-hook.js +269 -0
  22. package/dist/cli/commands-patterns.js +324 -0
  23. package/dist/cli/commands-setup.js +485 -0
  24. package/dist/cli/commands-validate.js +157 -0
  25. package/dist/cli/commands.js +517 -0
  26. package/dist/cli/config.js +197 -0
  27. package/dist/cli/diff.js +66 -0
  28. package/dist/cli/enforcement-log.js +83 -0
  29. package/dist/cli/enforcement-report.js +96 -0
  30. package/dist/cli/errors.js +10 -0
  31. package/dist/cli/format.js +22 -0
  32. package/dist/cli/index.js +5 -0
  33. package/dist/cli/io.js +2 -0
  34. package/dist/cli/main.js +31 -0
  35. package/dist/cli/pipeline.js +262 -0
  36. package/dist/cli/publish.js +77 -0
  37. package/dist/cli/report.js +127 -0
  38. package/dist/cli/starter-rules.js +57 -0
  39. package/dist/cli/store.js +60 -0
  40. package/dist/cli/version.js +5 -0
  41. package/dist/cli/watch.js +68 -0
  42. package/dist/cli/workflow.js +52 -0
  43. package/dist/compiler-angular/adapter.js +115 -0
  44. package/dist/compiler-angular/analyze.js +222 -0
  45. package/dist/compiler-angular/classes.js +446 -0
  46. package/dist/compiler-angular/declared.js +285 -0
  47. package/dist/compiler-angular/emit.js +183 -0
  48. package/dist/compiler-angular/index.js +4 -0
  49. package/dist/compiler-angular/template.js +142 -0
  50. package/dist/compiler-core/adapter.js +2 -0
  51. package/dist/compiler-core/adapters/inventory.js +312 -0
  52. package/dist/compiler-core/compile.js +148 -0
  53. package/dist/compiler-core/component-facts.js +123 -0
  54. package/dist/compiler-core/discovery/docs.js +66 -0
  55. package/dist/compiler-core/discovery/entries.js +270 -0
  56. package/dist/compiler-core/discovery/exports.js +207 -0
  57. package/dist/compiler-core/discovery/graph.js +59 -0
  58. package/dist/compiler-core/discovery/index.js +103 -0
  59. package/dist/compiler-core/discovery/packages.js +148 -0
  60. package/dist/compiler-core/discovery/schema.js +144 -0
  61. package/dist/compiler-core/discovery/storybook.js +232 -0
  62. package/dist/compiler-core/discovery/support.js +82 -0
  63. package/dist/compiler-core/discovery/tokens.js +186 -0
  64. package/dist/compiler-core/discovery/tooling.js +146 -0
  65. package/dist/compiler-core/fs.js +89 -0
  66. package/dist/compiler-core/glob.js +3 -0
  67. package/dist/compiler-core/index.js +16 -0
  68. package/dist/compiler-core/ir-builder.js +68 -0
  69. package/dist/compiler-core/json-schema.js +11 -0
  70. package/dist/compiler-core/testing/index.js +81 -0
  71. package/dist/compiler-core/ts/ast.js +166 -0
  72. package/dist/compiler-core/ts/members.js +163 -0
  73. package/dist/compiler-core/ts/program.js +81 -0
  74. package/dist/compiler-core/ts/static-eval.js +145 -0
  75. package/dist/compiler-core/ts/tsconfig-paths.js +60 -0
  76. package/dist/compiler-core/walk.js +46 -0
  77. package/dist/compiler-ir/index.js +4 -0
  78. package/dist/compiler-ir/ir.js +67 -0
  79. package/dist/compiler-ir/json-schema.js +6 -0
  80. package/dist/compiler-ir/validate.js +116 -0
  81. package/dist/compiler-react/adapter.js +147 -0
  82. package/dist/compiler-react/analyze.js +199 -0
  83. package/dist/compiler-react/ast.js +105 -0
  84. package/dist/compiler-react/behavior.js +75 -0
  85. package/dist/compiler-react/detect.js +558 -0
  86. package/dist/compiler-react/emit.js +188 -0
  87. package/dist/compiler-react/index.js +2 -0
  88. package/dist/compiler-react/program.js +58 -0
  89. package/dist/compiler-react/props.js +157 -0
  90. package/dist/compiler-react/styling.js +138 -0
  91. package/dist/compiler-react/usage.js +116 -0
  92. package/dist/compiler-react/variants.js +111 -0
  93. package/dist/compiler-storybook/adapter.js +253 -0
  94. package/dist/compiler-storybook/csf.js +521 -0
  95. package/dist/compiler-storybook/emit.js +202 -0
  96. package/dist/compiler-storybook/index-json.js +41 -0
  97. package/dist/compiler-storybook/index.js +5 -0
  98. package/dist/compiler-storybook/mdx.js +120 -0
  99. package/dist/compiler-storybook/static-eval.js +145 -0
  100. package/dist/compiler-tokens/adapter.js +158 -0
  101. package/dist/compiler-tokens/css.js +59 -0
  102. package/dist/compiler-tokens/index.js +5 -0
  103. package/dist/compiler-tokens/json-lines.js +106 -0
  104. package/dist/compiler-tokens/json-tokens.js +52 -0
  105. package/dist/compiler-vue/adapter.js +107 -0
  106. package/dist/compiler-vue/analyze.js +344 -0
  107. package/dist/compiler-vue/declared.js +343 -0
  108. package/dist/compiler-vue/emit.js +129 -0
  109. package/dist/compiler-vue/index.js +5 -0
  110. package/dist/compiler-vue/script.js +472 -0
  111. package/dist/compiler-vue/sfc.js +44 -0
  112. package/dist/compiler-vue/template.js +73 -0
  113. package/dist/contract/archetype-checks.js +153 -0
  114. package/dist/contract/components.js +102 -0
  115. package/dist/contract/conflicts.js +39 -0
  116. package/dist/contract/contract.js +46 -0
  117. package/dist/contract/evidence.js +84 -0
  118. package/dist/contract/fact-path.js +39 -0
  119. package/dist/contract/facts.js +62 -0
  120. package/dist/contract/index.js +16 -0
  121. package/dist/contract/integrity.js +21 -0
  122. package/dist/contract/json-schema.js +11 -0
  123. package/dist/contract/knowledge.js +95 -0
  124. package/dist/contract/pattern-checks.js +254 -0
  125. package/dist/contract/patterns.js +225 -0
  126. package/dist/contract/primitives.js +81 -0
  127. package/dist/contract/sources.js +30 -0
  128. package/dist/contract/tokens.js +41 -0
  129. package/dist/contract/validate.js +253 -0
  130. package/dist/contract-query/archetype.js +188 -0
  131. package/dist/contract-query/index.js +6 -0
  132. package/dist/contract-query/patterns.js +298 -0
  133. package/dist/contract-query/scan.js +164 -0
  134. package/dist/contract-query/search.js +132 -0
  135. package/dist/contract-query/system.js +148 -0
  136. package/dist/contract-query/validate-code.js +239 -0
  137. package/dist/contract-query/validate-usage.js +150 -0
  138. package/dist/contract-query/views.js +288 -0
  139. package/dist/governance/decisions.js +229 -0
  140. package/dist/governance/index.js +5 -0
  141. package/dist/governance/patterns-file.js +156 -0
  142. package/dist/governance/patterns.js +510 -0
  143. package/dist/governance/policies.js +148 -0
  144. package/dist/mcp/index.js +7 -0
  145. package/dist/mcp/loader.js +70 -0
  146. package/dist/mcp/protocol.js +27 -0
  147. package/dist/mcp/resources.js +138 -0
  148. package/dist/mcp/server.js +177 -0
  149. package/dist/mcp/stdio.js +28 -0
  150. package/dist/mcp/tools.js +267 -0
  151. package/dist/reconciliation/code.js +62 -0
  152. package/dist/reconciliation/components.js +401 -0
  153. package/dist/reconciliation/conflicts.js +126 -0
  154. package/dist/reconciliation/facts.js +41 -0
  155. package/dist/reconciliation/index.js +7 -0
  156. package/dist/reconciliation/reconcile.js +134 -0
  157. package/dist/reconciliation/resolve.js +158 -0
  158. package/dist/reconciliation/review.js +81 -0
  159. package/dist/reconciliation/storybook.js +370 -0
  160. package/dist/reconciliation/tokens.js +179 -0
  161. package/dist/reconciliation/util.js +55 -0
  162. package/dist/semantic-engine/additive.js +51 -0
  163. package/dist/semantic-engine/compounds.js +91 -0
  164. package/dist/semantic-engine/documents.js +157 -0
  165. package/dist/semantic-engine/enrich.js +42 -0
  166. package/dist/semantic-engine/index.js +7 -0
  167. package/dist/semantic-engine/infer.js +38 -0
  168. package/dist/semantic-engine/tokens.js +120 -0
  169. package/dist/shared/canonical.js +65 -0
  170. package/dist/shared/diagnostic.js +78 -0
  171. package/dist/shared/glob.js +98 -0
  172. package/dist/shared/index.js +4 -0
  173. package/dist/shared/json.js +2 -0
  174. package/dist/validation/card.js +50 -0
  175. package/dist/validation/discovery/arrangements.js +162 -0
  176. package/dist/validation/discovery/discover.js +70 -0
  177. package/dist/validation/discovery/index.js +8 -0
  178. package/dist/validation/discovery/model.js +127 -0
  179. package/dist/validation/discovery/promote.js +24 -0
  180. package/dist/validation/discovery/reviews.js +63 -0
  181. package/dist/validation/discovery/shapes.js +47 -0
  182. package/dist/validation/discovery/similar-files.js +146 -0
  183. package/dist/validation/discovery/util.js +47 -0
  184. package/dist/validation/index.js +11 -0
  185. package/dist/validation/interaction-facts.js +341 -0
  186. package/dist/validation/interaction-workflows.js +633 -0
  187. package/dist/validation/literals.js +251 -0
  188. package/dist/validation/pattern-rules.js +608 -0
  189. package/dist/validation/policy.js +241 -0
  190. package/dist/validation/report.js +163 -0
  191. package/dist/validation/rules.js +269 -0
  192. package/dist/validation/scan.js +295 -0
  193. package/dist/validation/text.js +34 -0
  194. package/dist/validation/validate-code.js +513 -0
  195. package/dist/validation/validate-usage.js +196 -0
  196. package/package.json +38 -4
@@ -0,0 +1,472 @@
1
+ import { classifyKind, collapse, describeMember, externalBases, literalDomain, literalValue, ownProperties, unwrap } from "../compiler-core/index.js";
2
+ import ts from "typescript";
3
+ const RENDERABLE = /^(?:VNode|VNodeChild|VNodeArrayChildren|Component|DefineComponent)(?:<.*>)?$/;
4
+ const RUNTIME_KINDS = {
5
+ String: { text: "string", kind: "string" },
6
+ Number: { text: "number", kind: "number" },
7
+ Boolean: { text: "boolean", kind: "boolean" },
8
+ Array: { text: "unknown[]", kind: "array" },
9
+ Object: { text: "object", kind: "object" },
10
+ Function: { text: "Function", kind: "function" },
11
+ Symbol: { text: "symbol", kind: "unknown" },
12
+ Date: { text: "Date", kind: "object" }
13
+ };
14
+ /** Reads the component declaration of a (virtual) script module. Never evaluates or runs anything. */
15
+ export function analyzeScript(analysis, sf) {
16
+ const info = {
17
+ props: [],
18
+ emits: [],
19
+ slots: [],
20
+ exposed: [],
21
+ options: {},
22
+ inheritsFrom: [],
23
+ complete: true,
24
+ notes: [],
25
+ optionsApi: false,
26
+ imports: readImports(sf)
27
+ };
28
+ const state = { analysis, info };
29
+ for (const statement of sf.statements) {
30
+ if (ts.isVariableStatement(statement)) {
31
+ for (const d of statement.declarationList.declarations)
32
+ if (d.initializer)
33
+ macro(state, unwrap(d.initializer), d);
34
+ }
35
+ else if (ts.isExpressionStatement(statement))
36
+ macro(state, unwrap(statement.expression), undefined);
37
+ else if (ts.isExportAssignment(statement) && !statement.isExportEquals) {
38
+ const options = optionsObject(unwrap(statement.expression));
39
+ if (options)
40
+ readOptions(state, options);
41
+ }
42
+ }
43
+ return info;
44
+ }
45
+ /** Analyzes `defineComponent({...})`/options-object declarations in a TS file (for non-SFC components). */
46
+ export function analyzeOptionsDeclaration(analysis, declaration) {
47
+ if (!declaration.initializer)
48
+ return undefined;
49
+ const options = optionsObject(unwrap(declaration.initializer));
50
+ if (!options)
51
+ return undefined;
52
+ const info = {
53
+ props: [],
54
+ emits: [],
55
+ slots: [],
56
+ exposed: [],
57
+ options: {},
58
+ inheritsFrom: [],
59
+ complete: true,
60
+ notes: [],
61
+ optionsApi: true,
62
+ imports: readImports(declaration.getSourceFile())
63
+ };
64
+ readOptions({ analysis, info }, options);
65
+ return info;
66
+ }
67
+ function macro(state, call, declaration) {
68
+ if (!ts.isCallExpression(call) || !ts.isIdentifier(call.expression))
69
+ return;
70
+ const name = call.expression.text;
71
+ const { info } = state;
72
+ if (name === "withDefaults") {
73
+ const inner = call.arguments[0] ? unwrap(call.arguments[0]) : undefined;
74
+ if (inner &&
75
+ ts.isCallExpression(inner) &&
76
+ ts.isIdentifier(inner.expression) &&
77
+ inner.expression.text === "defineProps") {
78
+ definePropsMacro(state, inner, defaultsFromObject(call.arguments[1]), declaration);
79
+ }
80
+ }
81
+ else if (name === "defineProps")
82
+ definePropsMacro(state, call, new Map(), declaration);
83
+ else if (name === "defineEmits")
84
+ defineEmitsMacro(state, call);
85
+ else if (name === "defineSlots")
86
+ defineSlotsMacro(state, call);
87
+ else if (name === "defineModel")
88
+ defineModelMacro(state, call);
89
+ else if (name === "defineExpose") {
90
+ const arg = call.arguments[0] ? unwrap(call.arguments[0]) : undefined;
91
+ if (arg && ts.isObjectLiteralExpression(arg))
92
+ info.exposed = arg.properties
93
+ .flatMap((p) => p.name && (ts.isIdentifier(p.name) || ts.isStringLiteralLike(p.name)) ? [p.name.text] : [])
94
+ .sort();
95
+ else if (arg)
96
+ info.notes.push("defineExpose argument is not an object literal");
97
+ }
98
+ else if (name === "defineOptions") {
99
+ const arg = call.arguments[0] ? unwrap(call.arguments[0]) : undefined;
100
+ if (arg && ts.isObjectLiteralExpression(arg))
101
+ readOptionMetadata(info, arg);
102
+ }
103
+ }
104
+ function definePropsMacro(state, call, withDefaults, declaration) {
105
+ const { analysis, info } = state;
106
+ const defaults = new Map(withDefaults);
107
+ // Reactive props destructure (Vue 3.5+): `const { size = "md" } = defineProps<…>()`.
108
+ if (declaration && ts.isObjectBindingPattern(declaration.name)) {
109
+ for (const element of declaration.name.elements) {
110
+ if (!element.initializer || element.dotDotDotToken)
111
+ continue;
112
+ const key = element.propertyName ?? element.name;
113
+ if (ts.isIdentifier(key) || ts.isStringLiteralLike(key))
114
+ defaults.set(key.text, specOf(element.initializer));
115
+ }
116
+ }
117
+ const typeArgument = call.typeArguments?.[0];
118
+ if (typeArgument) {
119
+ const type = analysis.checker.getTypeFromTypeNode(typeArgument);
120
+ if (type.flags & ts.TypeFlags.Any) {
121
+ info.complete = false;
122
+ info.notes.push("defineProps type did not resolve");
123
+ }
124
+ info.inheritsFrom.push(...externalBases(analysis, typeArgument));
125
+ for (const { symbol, declaration: decl } of ownProperties(analysis, type)) {
126
+ const member = describeMember(analysis, symbol, decl, { renderable: RENDERABLE });
127
+ const fallback = defaults.get(member.name);
128
+ info.props.push({
129
+ name: member.name,
130
+ typeText: member.typeText,
131
+ kind: member.kind,
132
+ required: member.required,
133
+ ...(fallback?.value !== undefined ? { default: fallback.value } : {}),
134
+ ...(fallback && fallback.value === undefined ? { defaultExpression: fallback.expression } : {}),
135
+ ...(member.allowedValues ? { allowedValues: member.allowedValues } : {}),
136
+ ...(member.description ? { description: member.description } : {}),
137
+ ...(member.deprecated ? { deprecated: member.deprecated } : {}),
138
+ source: "type",
139
+ node: decl
140
+ });
141
+ }
142
+ if (info.inheritsFrom.length > 0)
143
+ info.complete = false;
144
+ return;
145
+ }
146
+ const arg = call.arguments[0] ? unwrap(call.arguments[0]) : undefined;
147
+ if (arg)
148
+ readRuntimeProps(state, arg);
149
+ }
150
+ function specOf(expression) {
151
+ const value = literalValue(expression);
152
+ return {
153
+ ...(value !== undefined ? { value } : {}),
154
+ expression: collapse(expression.getText()).slice(0, 200)
155
+ };
156
+ }
157
+ function defaultsFromObject(node) {
158
+ const out = new Map();
159
+ const object = node ? unwrap(node) : undefined;
160
+ if (!object || !ts.isObjectLiteralExpression(object))
161
+ return out;
162
+ for (const p of object.properties) {
163
+ if (!p.name || !(ts.isIdentifier(p.name) || ts.isStringLiteralLike(p.name)))
164
+ continue;
165
+ if (ts.isPropertyAssignment(p))
166
+ out.set(p.name.text, specOf(p.initializer));
167
+ else if (ts.isMethodDeclaration(p))
168
+ out.set(p.name.text, { expression: collapse(p.getText()).slice(0, 200) });
169
+ }
170
+ return out;
171
+ }
172
+ /** `props: ['a', 'b']` and `props: { a: String, b: { type: Number, default: 1 } }` (also the `defineProps(...)` argument). */
173
+ function readRuntimeProps(state, node) {
174
+ const { analysis, info } = state;
175
+ if (ts.isArrayLiteralExpression(node)) {
176
+ for (const element of node.elements) {
177
+ if (ts.isStringLiteralLike(element))
178
+ info.props.push({
179
+ name: element.text,
180
+ typeText: "unknown",
181
+ kind: "unknown",
182
+ required: false,
183
+ source: "array",
184
+ node: element
185
+ });
186
+ else
187
+ info.complete = false;
188
+ }
189
+ return;
190
+ }
191
+ if (!ts.isObjectLiteralExpression(node)) {
192
+ info.complete = false;
193
+ info.notes.push("props declaration is not an object or array literal");
194
+ return;
195
+ }
196
+ for (const property of node.properties) {
197
+ if (!property.name ||
198
+ !(ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name)) ||
199
+ ts.isSpreadAssignment(property)) {
200
+ info.complete = false;
201
+ continue;
202
+ }
203
+ const name = property.name.text;
204
+ // Keep `as PropType<…>` casts: they carry the type. Only parentheses are stripped.
205
+ const value = ts.isPropertyAssignment(property) ? withoutParens(property.initializer) : undefined;
206
+ const spec = value
207
+ ? runtimeSpec(analysis, value)
208
+ : { typeText: "unknown", kind: "unknown", required: false };
209
+ info.props.push({ name, ...spec, source: "runtime", node: property });
210
+ }
211
+ }
212
+ function withoutParens(node) {
213
+ let current = node;
214
+ while (ts.isParenthesizedExpression(current))
215
+ current = current.expression;
216
+ return current;
217
+ }
218
+ function runtimeSpec(analysis, value) {
219
+ const base = { typeText: "unknown", kind: "unknown", required: false };
220
+ if (ts.isIdentifier(value))
221
+ return {
222
+ ...base,
223
+ ...(RUNTIME_KINDS[value.text]
224
+ ? {
225
+ typeText: RUNTIME_KINDS[value.text].text,
226
+ kind: RUNTIME_KINDS[value.text].kind
227
+ }
228
+ : {})
229
+ };
230
+ if (ts.isAsExpression(value))
231
+ return typeFromCast(analysis, value, base);
232
+ if (ts.isArrayLiteralExpression(value)) {
233
+ const names = value.elements.flatMap((e) => ts.isIdentifier(e) && RUNTIME_KINDS[e.text] ? [RUNTIME_KINDS[e.text].text] : []);
234
+ return {
235
+ ...base,
236
+ typeText: names.join(" | ") || "unknown",
237
+ kind: names.length > 1
238
+ ? "union"
239
+ : names.length === 1
240
+ ? RUNTIME_KINDS[value.elements[0].text].kind
241
+ : "unknown"
242
+ };
243
+ }
244
+ if (!ts.isObjectLiteralExpression(value))
245
+ return base;
246
+ const get = (key) => {
247
+ const p = value.properties.find((x) => ts.isPropertyAssignment(x) && x.name.getText() === key);
248
+ return p && ts.isPropertyAssignment(p) ? unwrap(p.initializer) : undefined;
249
+ };
250
+ // `type: String as PropType<…>`: the cast carries the type, so it must not be unwrapped.
251
+ const typeProperty = value.properties.find((x) => ts.isPropertyAssignment(x) && x.name.getText() === "type");
252
+ const typeExpression = typeProperty && ts.isPropertyAssignment(typeProperty) ? typeProperty.initializer : undefined;
253
+ const typed = typeExpression ? runtimeSpec(analysis, typeExpression) : base;
254
+ const requiredNode = get("required");
255
+ const defaultNode = get("default");
256
+ const fallback = defaultNode ? specOf(defaultNode) : undefined;
257
+ return {
258
+ ...typed,
259
+ required: requiredNode?.kind === ts.SyntaxKind.TrueKeyword,
260
+ ...(fallback?.value !== undefined ? { default: fallback.value } : {}),
261
+ ...(fallback && fallback.value === undefined ? { defaultExpression: fallback.expression } : {})
262
+ };
263
+ }
264
+ /** `String as PropType<"a" | "b">`. */
265
+ function typeFromCast(analysis, cast, base) {
266
+ const target = cast.type;
267
+ if (ts.isTypeReferenceNode(target) &&
268
+ target.typeName.getText().endsWith("PropType") &&
269
+ target.typeArguments?.[0]) {
270
+ const inner = target.typeArguments[0];
271
+ const text = collapse(inner.getText());
272
+ const checker = analysis.checker;
273
+ const type = checker.getTypeFromTypeNode(inner);
274
+ const allowed = literalDomain(checker, inner);
275
+ const kind = allowed && allowed.length > 0 && !allowed.every((v) => typeof v === "boolean")
276
+ ? "enum"
277
+ : classifyKind(checker, type, text, RENDERABLE);
278
+ return {
279
+ ...base,
280
+ typeText: text,
281
+ kind,
282
+ ...(kind === "enum" && allowed && allowed.length > 0 ? { allowedValues: allowed } : {})
283
+ };
284
+ }
285
+ return base;
286
+ }
287
+ function defineEmitsMacro(state, call) {
288
+ const { analysis, info } = state;
289
+ const typeArgument = call.typeArguments?.[0];
290
+ if (typeArgument) {
291
+ const checker = analysis.checker;
292
+ const type = checker.getTypeFromTypeNode(typeArgument);
293
+ // Call-signature form: (e: 'change', id: number): void
294
+ for (const signature of type.getCallSignatures()) {
295
+ const [first, ...rest] = signature.getParameters();
296
+ const eventType = first ? checker.getTypeOfSymbolAtLocation(first, typeArgument) : undefined;
297
+ if (!eventType?.isStringLiteral())
298
+ continue;
299
+ const payload = rest
300
+ .map((p) => `${p.name}: ${checker.typeToString(checker.getTypeOfSymbolAtLocation(p, typeArgument))}`)
301
+ .join(", ");
302
+ info.emits.push({
303
+ name: eventType.value,
304
+ ...(payload ? { payloadText: payload } : {}),
305
+ source: "type",
306
+ node: typeArgument
307
+ });
308
+ }
309
+ // Named-tuple form (Vue 3.3+): change: [id: number]
310
+ for (const property of type.getProperties()) {
311
+ const propType = checker.getTypeOfSymbolAtLocation(property, typeArgument);
312
+ if (!checker.isTupleType(propType) && !checker.isArrayType(propType))
313
+ continue;
314
+ const text = checker.typeToString(propType);
315
+ info.emits.push({
316
+ name: property.name,
317
+ ...(text !== "[]" ? { payloadText: text } : {}),
318
+ source: "type",
319
+ node: property.valueDeclaration ?? typeArgument
320
+ });
321
+ }
322
+ return;
323
+ }
324
+ const arg = call.arguments[0] ? unwrap(call.arguments[0]) : undefined;
325
+ if (arg)
326
+ readRuntimeEmits(info, arg);
327
+ }
328
+ function readRuntimeEmits(info, node) {
329
+ if (ts.isArrayLiteralExpression(node)) {
330
+ for (const e of node.elements) {
331
+ if (ts.isStringLiteralLike(e))
332
+ info.emits.push({ name: e.text, source: "array", node: e });
333
+ else
334
+ info.complete = false;
335
+ }
336
+ }
337
+ else if (ts.isObjectLiteralExpression(node)) {
338
+ for (const p of node.properties) {
339
+ if (p.name && (ts.isIdentifier(p.name) || ts.isStringLiteralLike(p.name)))
340
+ info.emits.push({ name: p.name.text, source: "runtime", node: p });
341
+ }
342
+ }
343
+ else
344
+ info.notes.push("emits declaration is not an array or object literal");
345
+ }
346
+ function defineSlotsMacro(state, call) {
347
+ const { analysis, info } = state;
348
+ const typeArgument = call.typeArguments?.[0];
349
+ if (!typeArgument)
350
+ return;
351
+ const checker = analysis.checker;
352
+ const type = checker.getTypeFromTypeNode(typeArgument);
353
+ for (const property of type.getProperties()) {
354
+ const propType = checker.getTypeOfSymbolAtLocation(property, typeArgument);
355
+ const parameter = propType.getCallSignatures()[0]?.getParameters()[0];
356
+ const propsText = parameter
357
+ ? checker.typeToString(checker.getTypeOfSymbolAtLocation(parameter, typeArgument))
358
+ : undefined;
359
+ info.slots.push({
360
+ name: property.name,
361
+ ...(propsText ? { propsText } : {}),
362
+ node: property.valueDeclaration ?? typeArgument
363
+ });
364
+ }
365
+ }
366
+ /** `defineModel()` is a prop named `modelValue` (or the given name) plus an `update:<name>` event. */
367
+ function defineModelMacro(state, call) {
368
+ const { analysis, info } = state;
369
+ const first = call.arguments[0] ? unwrap(call.arguments[0]) : undefined;
370
+ const named = first && ts.isStringLiteralLike(first);
371
+ const name = named ? first.text : "modelValue";
372
+ const options = (named ? call.arguments[1] : call.arguments[0]);
373
+ const object = options ? unwrap(options) : undefined;
374
+ const checker = analysis.checker;
375
+ const typeArgument = call.typeArguments?.[0];
376
+ const typeText = typeArgument ? collapse(typeArgument.getText()) : "unknown";
377
+ let kind = "unknown";
378
+ let allowedValues;
379
+ if (typeArgument) {
380
+ const allowed = literalDomain(checker, typeArgument);
381
+ kind =
382
+ allowed && allowed.length > 0 && !allowed.every((v) => typeof v === "boolean")
383
+ ? "enum"
384
+ : classifyKind(checker, checker.getTypeFromTypeNode(typeArgument), typeText, RENDERABLE);
385
+ if (kind === "enum")
386
+ allowedValues = allowed;
387
+ }
388
+ else if (object && ts.isObjectLiteralExpression(object)) {
389
+ const type = object.properties.find((p) => ts.isPropertyAssignment(p) && p.name.getText() === "type");
390
+ if (type && ts.isPropertyAssignment(type))
391
+ kind = runtimeSpec(analysis, unwrap(type.initializer)).kind;
392
+ }
393
+ let required = false;
394
+ let fallback;
395
+ if (object && ts.isObjectLiteralExpression(object)) {
396
+ for (const p of object.properties) {
397
+ if (!ts.isPropertyAssignment(p))
398
+ continue;
399
+ if (p.name.getText() === "required")
400
+ required = unwrap(p.initializer).kind === ts.SyntaxKind.TrueKeyword;
401
+ if (p.name.getText() === "default")
402
+ fallback = specOf(p.initializer);
403
+ }
404
+ }
405
+ info.props.push({
406
+ name,
407
+ typeText,
408
+ kind,
409
+ required,
410
+ ...(fallback?.value !== undefined ? { default: fallback.value } : {}),
411
+ ...(fallback && fallback.value === undefined ? { defaultExpression: fallback.expression } : {}),
412
+ ...(allowedValues ? { allowedValues } : {}),
413
+ source: "model",
414
+ node: call
415
+ });
416
+ info.emits.push({ name: `update:${name}`, payloadText: typeText, source: "model", node: call });
417
+ }
418
+ /** `export default { … }` and `defineComponent({ … })`. */
419
+ function optionsObject(node) {
420
+ if (ts.isObjectLiteralExpression(node))
421
+ return node;
422
+ if (ts.isCallExpression(node) &&
423
+ ts.isIdentifier(node.expression) &&
424
+ node.expression.text === "defineComponent" &&
425
+ node.arguments[0]) {
426
+ const arg = unwrap(node.arguments[0]);
427
+ return ts.isObjectLiteralExpression(arg) ? arg : undefined;
428
+ }
429
+ return undefined;
430
+ }
431
+ function readOptions(state, options) {
432
+ const { info } = state;
433
+ info.optionsApi = true;
434
+ readOptionMetadata(info, options);
435
+ for (const p of options.properties) {
436
+ if (!ts.isPropertyAssignment(p) || !p.name)
437
+ continue;
438
+ const key = p.name.getText().replace(/^["']|["']$/g, "");
439
+ if (key === "props")
440
+ readRuntimeProps(state, unwrap(p.initializer));
441
+ else if (key === "emits")
442
+ readRuntimeEmits(info, unwrap(p.initializer));
443
+ }
444
+ }
445
+ function readOptionMetadata(info, options) {
446
+ for (const p of options.properties) {
447
+ if (!ts.isPropertyAssignment(p) || !p.name)
448
+ continue;
449
+ const key = p.name.getText().replace(/^["']|["']$/g, "");
450
+ const value = unwrap(p.initializer);
451
+ if (key === "name" && ts.isStringLiteralLike(value))
452
+ info.options.name = value.text;
453
+ if (key === "inheritAttrs")
454
+ info.options.inheritAttrs = value.kind === ts.SyntaxKind.TrueKeyword;
455
+ }
456
+ }
457
+ function readImports(sf) {
458
+ const imports = new Map();
459
+ for (const statement of sf.statements) {
460
+ if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier))
461
+ continue;
462
+ const clause = statement.importClause;
463
+ if (clause?.name)
464
+ imports.set(clause.name.text, statement.moduleSpecifier.text);
465
+ if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
466
+ for (const e of clause.namedBindings.elements)
467
+ imports.set(e.name.text, `${statement.moduleSpecifier.text}#${(e.propertyName ?? e.name).text}`);
468
+ }
469
+ }
470
+ return imports;
471
+ }
472
+ //# sourceMappingURL=script.js.map
@@ -0,0 +1,44 @@
1
+ import { componentNameFromFile } from "../compiler-core/index.js";
2
+ import { parse } from "@vue/compiler-sfc";
3
+ export function parseSfc(path, text) {
4
+ const { descriptor, errors } = parse(text, { filename: path, sourceMap: false });
5
+ const script = buildScript(path, descriptor);
6
+ return {
7
+ path,
8
+ descriptor,
9
+ errors: errors.map((e) => ("message" in e ? e.message : String(e))),
10
+ ...(script ? { script } : {})
11
+ };
12
+ }
13
+ function buildScript(path, descriptor) {
14
+ const blocks = [descriptor.script, descriptor.scriptSetup].filter((b) => b !== null);
15
+ // Blocks that load their content from another file (`<script src>`) are not read here.
16
+ const readable = blocks.filter((b) => !b.src);
17
+ if (readable.length === 0)
18
+ return undefined;
19
+ const segments = [];
20
+ let text = "";
21
+ let line = 1;
22
+ for (const block of readable) {
23
+ const lines = block.content.split("\n").length;
24
+ segments.push({ virtualStart: line, sfcStart: block.loc.start.line, lines });
25
+ text += `${block.content}\n`;
26
+ line += lines;
27
+ }
28
+ const lang = readable.map((b) => b.lang).find((l) => l === "tsx" || l === "jsx");
29
+ const ext = lang ? ".tsx" : ".ts";
30
+ return {
31
+ path: `${path}${ext}`,
32
+ text,
33
+ tsx: lang !== undefined,
34
+ lineOf(virtualLine) {
35
+ const segment = [...segments].reverse().find((s) => virtualLine >= s.virtualStart) ?? segments[0];
36
+ return segment ? segment.sfcStart + (virtualLine - segment.virtualStart) : virtualLine;
37
+ }
38
+ };
39
+ }
40
+ /** Display and link name of an SFC component: the PascalCase file name (`button-group.vue` is `ButtonGroup`). */
41
+ export function sfcLocalName(path) {
42
+ return componentNameFromFile(path);
43
+ }
44
+ //# sourceMappingURL=sfc.js.map
@@ -0,0 +1,73 @@
1
+ // Numeric node and element kinds of the Vue template AST (stable constants of the compiler).
2
+ const ELEMENT = 1;
3
+ const ATTRIBUTE = 6;
4
+ const DIRECTIVE = 7;
5
+ const TAG_ELEMENT = 0;
6
+ const TAG_SLOT = 2;
7
+ /** Reads slot outlets, component tags and static accessibility attributes from the parsed template. */
8
+ export function analyzeTemplate(descriptor) {
9
+ const info = { slots: [], components: [], accessibility: { roles: [], attributes: [] } };
10
+ const template = descriptor.template;
11
+ if (!template)
12
+ return info;
13
+ if (template.src)
14
+ return { ...info, skipped: "template loaded from an external file (src)" };
15
+ if (template.lang && template.lang !== "html")
16
+ return { ...info, skipped: `template language ${template.lang} is not analyzed` };
17
+ const root = template.ast;
18
+ if (!root)
19
+ return { ...info, skipped: "template did not parse" };
20
+ const components = new Map();
21
+ const roles = new Set();
22
+ const attributes = new Set();
23
+ const visit = (node) => {
24
+ if (node.type === ELEMENT && node.tag !== undefined) {
25
+ const props = node.props ?? [];
26
+ if (node.tagType === TAG_SLOT) {
27
+ const named = props.find((p) => p.type === ATTRIBUTE && p.name === "name");
28
+ const bound = props.find((p) => p.type === DIRECTIVE && p.name === "bind" && p.arg?.content === "name");
29
+ const slotProps = props
30
+ .flatMap((p) => p.type === ATTRIBUTE
31
+ ? [p.name]
32
+ : p.type === DIRECTIVE && p.name === "bind" && p.arg?.isStatic
33
+ ? [p.arg.content]
34
+ : [])
35
+ .filter((n) => n !== "name");
36
+ info.slots.push({
37
+ ...(named?.value ? { name: named.value.content } : bound ? {} : { name: "default" }),
38
+ dynamic: bound !== undefined,
39
+ line: node.loc.start.line,
40
+ props: [...new Set(slotProps)].sort()
41
+ });
42
+ }
43
+ else if (node.tagType === TAG_ELEMENT) {
44
+ for (const p of props) {
45
+ if (p.type === ATTRIBUTE && p.name === "role" && p.value)
46
+ roles.add(p.value.content);
47
+ const name = p.type === ATTRIBUTE
48
+ ? p.name
49
+ : p.type === DIRECTIVE && p.name === "bind" && p.arg?.isStatic
50
+ ? p.arg.content
51
+ : undefined;
52
+ if (name?.startsWith("aria-"))
53
+ attributes.add(name);
54
+ }
55
+ }
56
+ else if (node.tagType !== undefined && node.tagType !== 3) {
57
+ const entry = components.get(node.tag) ?? { count: 0, line: node.loc.start.line };
58
+ entry.count++;
59
+ components.set(node.tag, entry);
60
+ }
61
+ }
62
+ for (const child of node.children ?? [])
63
+ if (typeof child === "object" && child !== null)
64
+ visit(child);
65
+ };
66
+ visit(root);
67
+ info.components = [...components]
68
+ .map(([tag, v]) => ({ tag, ...v }))
69
+ .sort((a, b) => (a.tag < b.tag ? -1 : 1));
70
+ info.accessibility = { roles: [...roles].sort(), attributes: [...attributes].sort() };
71
+ return info;
72
+ }
73
+ //# sourceMappingURL=template.js.map