@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.
- package/LICENSE +105 -0
- package/README.md +248 -2
- package/dist/agent-kit/apply.js +232 -0
- package/dist/agent-kit/guidance.js +236 -0
- package/dist/agent-kit/index.js +5 -0
- package/dist/agent-kit/integrations.js +121 -0
- package/dist/agent-kit/plugin.js +115 -0
- package/dist/artifact-generator/index.js +75 -0
- package/dist/browser-verify/driver.js +2 -0
- package/dist/browser-verify/index.js +4 -0
- package/dist/browser-verify/playwright.js +82 -0
- package/dist/browser-verify/runner.js +175 -0
- package/dist/browser-verify/spec.js +103 -0
- package/dist/cli/args.js +77 -0
- package/dist/cli/baseline.js +64 -0
- package/dist/cli/cli.js +121 -0
- package/dist/cli/commands-browser.js +104 -0
- package/dist/cli/commands-discover.js +312 -0
- package/dist/cli/commands-enforcement.js +35 -0
- package/dist/cli/commands-governance.js +179 -0
- package/dist/cli/commands-hook.js +269 -0
- package/dist/cli/commands-patterns.js +324 -0
- package/dist/cli/commands-setup.js +485 -0
- package/dist/cli/commands-validate.js +157 -0
- package/dist/cli/commands.js +517 -0
- package/dist/cli/config.js +197 -0
- package/dist/cli/diff.js +66 -0
- package/dist/cli/enforcement-log.js +83 -0
- package/dist/cli/enforcement-report.js +96 -0
- package/dist/cli/errors.js +10 -0
- package/dist/cli/format.js +22 -0
- package/dist/cli/index.js +5 -0
- package/dist/cli/io.js +2 -0
- package/dist/cli/main.js +31 -0
- package/dist/cli/pipeline.js +262 -0
- package/dist/cli/publish.js +77 -0
- package/dist/cli/report.js +127 -0
- package/dist/cli/starter-rules.js +57 -0
- package/dist/cli/store.js +60 -0
- package/dist/cli/version.js +5 -0
- package/dist/cli/watch.js +68 -0
- package/dist/cli/workflow.js +52 -0
- package/dist/compiler-angular/adapter.js +115 -0
- package/dist/compiler-angular/analyze.js +222 -0
- package/dist/compiler-angular/classes.js +446 -0
- package/dist/compiler-angular/declared.js +285 -0
- package/dist/compiler-angular/emit.js +183 -0
- package/dist/compiler-angular/index.js +4 -0
- package/dist/compiler-angular/template.js +142 -0
- package/dist/compiler-core/adapter.js +2 -0
- package/dist/compiler-core/adapters/inventory.js +312 -0
- package/dist/compiler-core/compile.js +148 -0
- package/dist/compiler-core/component-facts.js +123 -0
- package/dist/compiler-core/discovery/docs.js +66 -0
- package/dist/compiler-core/discovery/entries.js +270 -0
- package/dist/compiler-core/discovery/exports.js +207 -0
- package/dist/compiler-core/discovery/graph.js +59 -0
- package/dist/compiler-core/discovery/index.js +103 -0
- package/dist/compiler-core/discovery/packages.js +148 -0
- package/dist/compiler-core/discovery/schema.js +144 -0
- package/dist/compiler-core/discovery/storybook.js +232 -0
- package/dist/compiler-core/discovery/support.js +82 -0
- package/dist/compiler-core/discovery/tokens.js +186 -0
- package/dist/compiler-core/discovery/tooling.js +146 -0
- package/dist/compiler-core/fs.js +89 -0
- package/dist/compiler-core/glob.js +3 -0
- package/dist/compiler-core/index.js +16 -0
- package/dist/compiler-core/ir-builder.js +68 -0
- package/dist/compiler-core/json-schema.js +11 -0
- package/dist/compiler-core/testing/index.js +81 -0
- package/dist/compiler-core/ts/ast.js +166 -0
- package/dist/compiler-core/ts/members.js +163 -0
- package/dist/compiler-core/ts/program.js +81 -0
- package/dist/compiler-core/ts/static-eval.js +145 -0
- package/dist/compiler-core/ts/tsconfig-paths.js +60 -0
- package/dist/compiler-core/walk.js +46 -0
- package/dist/compiler-ir/index.js +4 -0
- package/dist/compiler-ir/ir.js +67 -0
- package/dist/compiler-ir/json-schema.js +6 -0
- package/dist/compiler-ir/validate.js +116 -0
- package/dist/compiler-react/adapter.js +147 -0
- package/dist/compiler-react/analyze.js +199 -0
- package/dist/compiler-react/ast.js +105 -0
- package/dist/compiler-react/behavior.js +75 -0
- package/dist/compiler-react/detect.js +558 -0
- package/dist/compiler-react/emit.js +188 -0
- package/dist/compiler-react/index.js +2 -0
- package/dist/compiler-react/program.js +58 -0
- package/dist/compiler-react/props.js +157 -0
- package/dist/compiler-react/styling.js +138 -0
- package/dist/compiler-react/usage.js +116 -0
- package/dist/compiler-react/variants.js +111 -0
- package/dist/compiler-storybook/adapter.js +253 -0
- package/dist/compiler-storybook/csf.js +521 -0
- package/dist/compiler-storybook/emit.js +202 -0
- package/dist/compiler-storybook/index-json.js +41 -0
- package/dist/compiler-storybook/index.js +5 -0
- package/dist/compiler-storybook/mdx.js +120 -0
- package/dist/compiler-storybook/static-eval.js +145 -0
- package/dist/compiler-tokens/adapter.js +158 -0
- package/dist/compiler-tokens/css.js +59 -0
- package/dist/compiler-tokens/index.js +5 -0
- package/dist/compiler-tokens/json-lines.js +106 -0
- package/dist/compiler-tokens/json-tokens.js +52 -0
- package/dist/compiler-vue/adapter.js +107 -0
- package/dist/compiler-vue/analyze.js +344 -0
- package/dist/compiler-vue/declared.js +343 -0
- package/dist/compiler-vue/emit.js +129 -0
- package/dist/compiler-vue/index.js +5 -0
- package/dist/compiler-vue/script.js +472 -0
- package/dist/compiler-vue/sfc.js +44 -0
- package/dist/compiler-vue/template.js +73 -0
- package/dist/contract/archetype-checks.js +153 -0
- package/dist/contract/components.js +102 -0
- package/dist/contract/conflicts.js +39 -0
- package/dist/contract/contract.js +46 -0
- package/dist/contract/evidence.js +84 -0
- package/dist/contract/fact-path.js +39 -0
- package/dist/contract/facts.js +62 -0
- package/dist/contract/index.js +16 -0
- package/dist/contract/integrity.js +21 -0
- package/dist/contract/json-schema.js +11 -0
- package/dist/contract/knowledge.js +95 -0
- package/dist/contract/pattern-checks.js +254 -0
- package/dist/contract/patterns.js +225 -0
- package/dist/contract/primitives.js +81 -0
- package/dist/contract/sources.js +30 -0
- package/dist/contract/tokens.js +41 -0
- package/dist/contract/validate.js +253 -0
- package/dist/contract-query/archetype.js +188 -0
- package/dist/contract-query/index.js +6 -0
- package/dist/contract-query/patterns.js +298 -0
- package/dist/contract-query/scan.js +164 -0
- package/dist/contract-query/search.js +132 -0
- package/dist/contract-query/system.js +148 -0
- package/dist/contract-query/validate-code.js +239 -0
- package/dist/contract-query/validate-usage.js +150 -0
- package/dist/contract-query/views.js +288 -0
- package/dist/governance/decisions.js +229 -0
- package/dist/governance/index.js +5 -0
- package/dist/governance/patterns-file.js +156 -0
- package/dist/governance/patterns.js +510 -0
- package/dist/governance/policies.js +148 -0
- package/dist/mcp/index.js +7 -0
- package/dist/mcp/loader.js +70 -0
- package/dist/mcp/protocol.js +27 -0
- package/dist/mcp/resources.js +138 -0
- package/dist/mcp/server.js +177 -0
- package/dist/mcp/stdio.js +28 -0
- package/dist/mcp/tools.js +267 -0
- package/dist/reconciliation/code.js +62 -0
- package/dist/reconciliation/components.js +401 -0
- package/dist/reconciliation/conflicts.js +126 -0
- package/dist/reconciliation/facts.js +41 -0
- package/dist/reconciliation/index.js +7 -0
- package/dist/reconciliation/reconcile.js +134 -0
- package/dist/reconciliation/resolve.js +158 -0
- package/dist/reconciliation/review.js +81 -0
- package/dist/reconciliation/storybook.js +370 -0
- package/dist/reconciliation/tokens.js +179 -0
- package/dist/reconciliation/util.js +55 -0
- package/dist/semantic-engine/additive.js +51 -0
- package/dist/semantic-engine/compounds.js +91 -0
- package/dist/semantic-engine/documents.js +157 -0
- package/dist/semantic-engine/enrich.js +42 -0
- package/dist/semantic-engine/index.js +7 -0
- package/dist/semantic-engine/infer.js +38 -0
- package/dist/semantic-engine/tokens.js +120 -0
- package/dist/shared/canonical.js +65 -0
- package/dist/shared/diagnostic.js +78 -0
- package/dist/shared/glob.js +98 -0
- package/dist/shared/index.js +4 -0
- package/dist/shared/json.js +2 -0
- package/dist/validation/card.js +50 -0
- package/dist/validation/discovery/arrangements.js +162 -0
- package/dist/validation/discovery/discover.js +70 -0
- package/dist/validation/discovery/index.js +8 -0
- package/dist/validation/discovery/model.js +127 -0
- package/dist/validation/discovery/promote.js +24 -0
- package/dist/validation/discovery/reviews.js +63 -0
- package/dist/validation/discovery/shapes.js +47 -0
- package/dist/validation/discovery/similar-files.js +146 -0
- package/dist/validation/discovery/util.js +47 -0
- package/dist/validation/index.js +11 -0
- package/dist/validation/interaction-facts.js +341 -0
- package/dist/validation/interaction-workflows.js +633 -0
- package/dist/validation/literals.js +251 -0
- package/dist/validation/pattern-rules.js +608 -0
- package/dist/validation/policy.js +241 -0
- package/dist/validation/report.js +163 -0
- package/dist/validation/rules.js +269 -0
- package/dist/validation/scan.js +295 -0
- package/dist/validation/text.js +34 -0
- package/dist/validation/validate-code.js +513 -0
- package/dist/validation/validate-usage.js +196 -0
- package/package.json +38 -4
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { declarationOf, describeMember, literalDomain, ownProperties, resolveSymbol, typeReferenceInfo } from "../compiler-core/index.js";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
const RENDERABLE = /^(?:VNode|VNodeChild|VNodeArrayChildren|Component|DefineComponent)(?:<.*>)?$/;
|
|
4
|
+
const INSTANCE_TYPES = /^(?:CreateComponentPublicInstance(?:WithMixins)?|ComponentPublicInstance)$/;
|
|
5
|
+
const CONSTRUCTORS = {
|
|
6
|
+
StringConstructor: { text: "string", kind: "string" },
|
|
7
|
+
NumberConstructor: { text: "number", kind: "number" },
|
|
8
|
+
BooleanConstructor: { text: "boolean", kind: "boolean" },
|
|
9
|
+
ArrayConstructor: { text: "unknown[]", kind: "array" },
|
|
10
|
+
ObjectConstructor: { text: "object", kind: "object" },
|
|
11
|
+
FunctionConstructor: { text: "Function", kind: "function" },
|
|
12
|
+
DateConstructor: { text: "Date", kind: "object" }
|
|
13
|
+
};
|
|
14
|
+
/** Positions of `DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, Emits, …>`. */
|
|
15
|
+
const PROPS = 0;
|
|
16
|
+
const EMITS = 7;
|
|
17
|
+
/** Helpers that vue-tsc writes around the props type of a `<script setup lang="ts">` component. */
|
|
18
|
+
const PROPS_WRAPPERS = /^__VLS_(?:TypePropsToRuntimeProps|TypePropsToOption|WithDefaults|Prettify)$/;
|
|
19
|
+
const SLOTS_WRAPPER = /^__VLS_WithTemplateSlots$/;
|
|
20
|
+
/**
|
|
21
|
+
* A component a declaration file declares by type: `declare const _default: DefineComponent<Props, …, Emits, …>`
|
|
22
|
+
* (what vue-tsc and vite-plugin-dts write for a `.vue` file or a `defineComponent` call). There is no script
|
|
23
|
+
* to read, so props, events and slots come from the written type arguments. Default values are not part of
|
|
24
|
+
* a declaration file and are not reported.
|
|
25
|
+
*/
|
|
26
|
+
export function analyzeDeclaredComponent(analysis, declaration) {
|
|
27
|
+
if (declaration.initializer || !declaration.type)
|
|
28
|
+
return undefined;
|
|
29
|
+
const found = findDefineComponent(analysis, declaration.type, 0);
|
|
30
|
+
if (!found)
|
|
31
|
+
return undefined;
|
|
32
|
+
const info = {
|
|
33
|
+
props: [],
|
|
34
|
+
emits: [],
|
|
35
|
+
slots: [],
|
|
36
|
+
exposed: [],
|
|
37
|
+
options: {},
|
|
38
|
+
inheritsFrom: [],
|
|
39
|
+
complete: true,
|
|
40
|
+
notes: [],
|
|
41
|
+
optionsApi: false,
|
|
42
|
+
imports: new Map()
|
|
43
|
+
};
|
|
44
|
+
const props = found.args[PROPS];
|
|
45
|
+
if (props)
|
|
46
|
+
readProps(analysis, props, info, 0);
|
|
47
|
+
const emits = found.args[EMITS];
|
|
48
|
+
if (emits)
|
|
49
|
+
readEmits(analysis, emits, info);
|
|
50
|
+
if (found.slots)
|
|
51
|
+
readSlots(analysis, found.slots, info);
|
|
52
|
+
return info;
|
|
53
|
+
}
|
|
54
|
+
function unparen(type) {
|
|
55
|
+
return ts.isParenthesizedTypeNode(type) ? unparen(type.type) : type;
|
|
56
|
+
}
|
|
57
|
+
function targetOf(analysis, name) {
|
|
58
|
+
return declarationOf(resolveSymbol(analysis.checker, analysis.checker.getSymbolAtLocation(name)));
|
|
59
|
+
}
|
|
60
|
+
function findDefineComponent(analysis, type, depth) {
|
|
61
|
+
if (depth > 5)
|
|
62
|
+
return undefined;
|
|
63
|
+
const t = unparen(type);
|
|
64
|
+
if (ts.isIntersectionTypeNode(t)) {
|
|
65
|
+
let found;
|
|
66
|
+
let slots;
|
|
67
|
+
for (const part of t.types) {
|
|
68
|
+
found ??= findDefineComponent(analysis, part, depth + 1);
|
|
69
|
+
slots ??= slotsOfConstructor(part);
|
|
70
|
+
}
|
|
71
|
+
const slot = found?.slots ?? slots;
|
|
72
|
+
return found ? { args: found.args, ...(slot ? { slots: slot } : {}) } : undefined;
|
|
73
|
+
}
|
|
74
|
+
if (ts.isTypeQueryNode(t)) {
|
|
75
|
+
const target = targetOf(analysis, t.exprName);
|
|
76
|
+
return target && ts.isVariableDeclaration(target) && target.type
|
|
77
|
+
? findDefineComponent(analysis, target.type, depth + 1)
|
|
78
|
+
: undefined;
|
|
79
|
+
}
|
|
80
|
+
// `{ new (...args: any[]): CreateComponentPublicInstanceWithMixins<Props, …, Emits, …> }`: what Vuetify and other
|
|
81
|
+
// `defineComponent` libraries write, with the same type-argument positions as `DefineComponent`.
|
|
82
|
+
if (ts.isTypeLiteralNode(t)) {
|
|
83
|
+
for (const member of t.members) {
|
|
84
|
+
if (!ts.isConstructSignatureDeclaration(member) || !member.type)
|
|
85
|
+
continue;
|
|
86
|
+
const instance = typeReferenceInfo(member.type);
|
|
87
|
+
if (instance && INSTANCE_TYPES.test(instance.name))
|
|
88
|
+
return { args: instance.typeArguments };
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const ref = typeReferenceInfo(t);
|
|
93
|
+
if (!ref)
|
|
94
|
+
return undefined;
|
|
95
|
+
if (ref.name === "DefineComponent")
|
|
96
|
+
return { args: ref.typeArguments };
|
|
97
|
+
if (SLOTS_WRAPPER.test(ref.name) && ref.typeArguments[0]) {
|
|
98
|
+
const inner = findDefineComponent(analysis, ref.typeArguments[0], depth + 1);
|
|
99
|
+
return inner ? { ...inner, ...(ref.typeArguments[1] ? { slots: ref.typeArguments[1] } : {}) } : undefined;
|
|
100
|
+
}
|
|
101
|
+
// `type ButtonComponent = DefineComponent<…>`
|
|
102
|
+
if (ts.isTypeReferenceNode(t)) {
|
|
103
|
+
const target = targetOf(analysis, t.typeName);
|
|
104
|
+
if (target && ts.isTypeAliasDeclaration(target) && !target.typeParameters)
|
|
105
|
+
return findDefineComponent(analysis, target.type, depth + 1);
|
|
106
|
+
// `type SFCWithInstall<T> = T & Plugin` (Element Plus): the component is the argument passed for `T`.
|
|
107
|
+
if (target && ts.isTypeAliasDeclaration(target) && target.typeParameters && t.typeArguments) {
|
|
108
|
+
const body = unparen(target.type);
|
|
109
|
+
const members = ts.isIntersectionTypeNode(body) ? body.types : [body];
|
|
110
|
+
for (const member of members) {
|
|
111
|
+
const param = ts.isTypeReferenceNode(member)
|
|
112
|
+
? target.typeParameters.findIndex((p) => p.name.text === member.typeName.getText())
|
|
113
|
+
: -1;
|
|
114
|
+
const arg = param >= 0 ? t.typeArguments[param] : undefined;
|
|
115
|
+
const found = arg ? findDefineComponent(analysis, arg, depth + 1) : undefined;
|
|
116
|
+
if (found)
|
|
117
|
+
return found;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
/** `{ new (): { $slots: S } }`, the way a declaration file attaches slot types to a component. */
|
|
124
|
+
function slotsOfConstructor(type) {
|
|
125
|
+
const t = unparen(type);
|
|
126
|
+
if (!ts.isTypeLiteralNode(t))
|
|
127
|
+
return undefined;
|
|
128
|
+
for (const member of t.members) {
|
|
129
|
+
if (!ts.isConstructSignatureDeclaration(member) || !member.type || !ts.isTypeLiteralNode(member.type))
|
|
130
|
+
continue;
|
|
131
|
+
for (const inner of member.type.members)
|
|
132
|
+
if (ts.isPropertySignature(inner) && inner.name.getText() === "$slots")
|
|
133
|
+
return inner.type;
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
function readProps(analysis, type, info, depth) {
|
|
138
|
+
const t = unparen(type);
|
|
139
|
+
const ref = typeReferenceInfo(t);
|
|
140
|
+
if (depth < 4 && ref && PROPS_WRAPPERS.test(ref.name) && ref.typeArguments[0]) {
|
|
141
|
+
readProps(analysis, ref.typeArguments[0], info, depth + 1);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// `ExtractPropTypes<{ size: { type: PropType<"a" | "b">; default: string } }>`: the runtime options, as a type.
|
|
145
|
+
if (ref?.name === "ExtractPropTypes" && ref.typeArguments[0]) {
|
|
146
|
+
readRuntimeProps(analysis, ref.typeArguments[0], info, 0);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
// `("size" | "tone")[]`: props declared as an array of names.
|
|
150
|
+
if (ts.isArrayTypeNode(t) || ts.isTupleTypeNode(t)) {
|
|
151
|
+
const names = ts.isArrayTypeNode(t)
|
|
152
|
+
? literalDomain(analysis.checker, t.elementType)
|
|
153
|
+
: t.elements.flatMap((e) => literalDomain(analysis.checker, e) ?? []);
|
|
154
|
+
for (const name of names ?? [])
|
|
155
|
+
info.props.push({
|
|
156
|
+
name: String(name),
|
|
157
|
+
typeText: "unknown",
|
|
158
|
+
kind: "unknown",
|
|
159
|
+
required: false,
|
|
160
|
+
source: "array",
|
|
161
|
+
node: t
|
|
162
|
+
});
|
|
163
|
+
if (!names)
|
|
164
|
+
info.complete = false;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const type_ = analysis.checker.getTypeFromTypeNode(t);
|
|
168
|
+
if (type_.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) {
|
|
169
|
+
info.complete = false;
|
|
170
|
+
info.notes.push("the props type did not resolve");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
for (const { symbol, declaration } of ownProperties(analysis, type_)) {
|
|
174
|
+
const member = describeMember(analysis, symbol, declaration, { renderable: RENDERABLE });
|
|
175
|
+
info.props.push({
|
|
176
|
+
name: member.name,
|
|
177
|
+
typeText: member.typeText,
|
|
178
|
+
kind: member.kind,
|
|
179
|
+
required: member.required,
|
|
180
|
+
...(member.allowedValues ? { allowedValues: member.allowedValues } : {}),
|
|
181
|
+
...(member.description ? { description: member.description } : {}),
|
|
182
|
+
...(member.deprecated ? { deprecated: member.deprecated } : {}),
|
|
183
|
+
source: "type",
|
|
184
|
+
node: declaration
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function readRuntimeProps(analysis, type, info, depth) {
|
|
189
|
+
let t = unparen(type);
|
|
190
|
+
// `typeof props`: follow to the declared type of the constant.
|
|
191
|
+
if (ts.isTypeQueryNode(t) && depth < 3) {
|
|
192
|
+
const target = targetOf(analysis, t.exprName);
|
|
193
|
+
if (target && ts.isVariableDeclaration(target) && target.type) {
|
|
194
|
+
readRuntimeProps(analysis, target.type, info, depth + 1);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (!ts.isTypeLiteralNode(t)) {
|
|
199
|
+
info.complete = false;
|
|
200
|
+
info.notes.push("the runtime props type is not an object type");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
for (const member of t.members) {
|
|
204
|
+
if (!ts.isPropertySignature(member) || !member.name || !member.type)
|
|
205
|
+
continue;
|
|
206
|
+
if (!(ts.isIdentifier(member.name) || ts.isStringLiteralLike(member.name)))
|
|
207
|
+
continue;
|
|
208
|
+
t = unparen(member.type);
|
|
209
|
+
const spec = ts.isTypeLiteralNode(t) ? t : undefined;
|
|
210
|
+
const field = (key) => spec?.members.find((m) => ts.isPropertySignature(m) && m.name.getText() === key);
|
|
211
|
+
// A bare constructor (`name: StringConstructor`) or a list of them is the shorthand for `{ type }`.
|
|
212
|
+
const typeNode = spec ? field("type")?.type : t;
|
|
213
|
+
const required = field("required")?.type?.getText() === "true";
|
|
214
|
+
const described = typeNode ? describeRuntimeType(analysis, typeNode) : undefined;
|
|
215
|
+
info.props.push({
|
|
216
|
+
name: member.name.text,
|
|
217
|
+
typeText: described?.text ?? "unknown",
|
|
218
|
+
kind: described?.kind ?? "unknown",
|
|
219
|
+
required,
|
|
220
|
+
...(described?.allowedValues ? { allowedValues: described.allowedValues } : {}),
|
|
221
|
+
source: "runtime",
|
|
222
|
+
node: member
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function describeRuntimeType(analysis, node) {
|
|
227
|
+
const t = unparen(node);
|
|
228
|
+
const ref = typeReferenceInfo(t);
|
|
229
|
+
if (ref?.name === "PropType" && ref.typeArguments[0]) {
|
|
230
|
+
const inner = ref.typeArguments[0];
|
|
231
|
+
const checkerType = analysis.checker.getTypeFromTypeNode(inner);
|
|
232
|
+
const text = inner.getText().replace(/\s+/g, " ");
|
|
233
|
+
const kind = kindOf(analysis, checkerType);
|
|
234
|
+
const allowedValues = kind === "enum" ? literalDomain(analysis.checker, inner) : undefined;
|
|
235
|
+
return { text, kind, ...(allowedValues ? { allowedValues } : {}) };
|
|
236
|
+
}
|
|
237
|
+
if (ref && CONSTRUCTORS[ref.name])
|
|
238
|
+
return CONSTRUCTORS[ref.name];
|
|
239
|
+
if (ts.isUnionTypeNode(t)) {
|
|
240
|
+
const parts = t.types.map((p) => describeRuntimeType(analysis, p));
|
|
241
|
+
if (parts.every((p) => p))
|
|
242
|
+
return { text: parts.map((p) => p?.text).join(" | "), kind: "union" };
|
|
243
|
+
}
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
function kindOf(analysis, type) {
|
|
247
|
+
const text = analysis.checker.typeToString(type);
|
|
248
|
+
if (type.flags & ts.TypeFlags.Any)
|
|
249
|
+
return "unknown";
|
|
250
|
+
if (type.getCallSignatures().length > 0)
|
|
251
|
+
return "function";
|
|
252
|
+
if (type.isUnion()) {
|
|
253
|
+
return type.types.every((x) => x.flags & (ts.TypeFlags.StringLiteral | ts.TypeFlags.NumberLiteral))
|
|
254
|
+
? "enum"
|
|
255
|
+
: "union";
|
|
256
|
+
}
|
|
257
|
+
if (type.flags & ts.TypeFlags.StringLike)
|
|
258
|
+
return "string";
|
|
259
|
+
if (type.flags & ts.TypeFlags.NumberLike)
|
|
260
|
+
return "number";
|
|
261
|
+
if (type.flags & ts.TypeFlags.BooleanLike)
|
|
262
|
+
return "boolean";
|
|
263
|
+
if (analysis.checker.isArrayType(type))
|
|
264
|
+
return "array";
|
|
265
|
+
return text === "unknown" ? "unknown" : "object";
|
|
266
|
+
}
|
|
267
|
+
function readEmits(analysis, type, info) {
|
|
268
|
+
const t = unparen(type);
|
|
269
|
+
// `{}` and `string[]`-like placeholders: Vue's own default for a component with no emits.
|
|
270
|
+
if (ts.isTypeLiteralNode(t) && t.members.length === 0)
|
|
271
|
+
return;
|
|
272
|
+
if (ts.isArrayTypeNode(t) || ts.isTupleTypeNode(t)) {
|
|
273
|
+
const names = ts.isArrayTypeNode(t)
|
|
274
|
+
? literalDomain(analysis.checker, t.elementType)
|
|
275
|
+
: t.elements.flatMap((e) => literalDomain(analysis.checker, e) ?? []);
|
|
276
|
+
for (const name of names ?? [])
|
|
277
|
+
info.emits.push({ name: String(name), source: "array", node: t });
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const checker = analysis.checker;
|
|
281
|
+
const resolved = checker.getTypeFromTypeNode(t);
|
|
282
|
+
// Call-signature form: `(e: "change", id: number): void`
|
|
283
|
+
for (const signature of resolved.getCallSignatures()) {
|
|
284
|
+
const [first, ...rest] = signature.getParameters();
|
|
285
|
+
const eventType = first ? checker.getTypeOfSymbolAtLocation(first, t) : undefined;
|
|
286
|
+
if (!eventType?.isStringLiteral())
|
|
287
|
+
continue;
|
|
288
|
+
const payload = rest
|
|
289
|
+
.map((p) => `${p.name}: ${checker.typeToString(checker.getTypeOfSymbolAtLocation(p, t))}`)
|
|
290
|
+
.join(", ");
|
|
291
|
+
info.emits.push({
|
|
292
|
+
name: eventType.value,
|
|
293
|
+
...(payload ? { payloadText: payload } : {}),
|
|
294
|
+
source: "type",
|
|
295
|
+
node: t
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
// Object form: `change: (id: number) => void` or the named-tuple form `change: [id: number]`.
|
|
299
|
+
for (const property of resolved.getProperties()) {
|
|
300
|
+
const propType = checker.getNonNullableType(checker.getTypeOfSymbolAtLocation(property, t));
|
|
301
|
+
const node = property.valueDeclaration ?? t;
|
|
302
|
+
const signature = propType.getCallSignatures()[0];
|
|
303
|
+
if (signature) {
|
|
304
|
+
const payload = signature
|
|
305
|
+
.getParameters()
|
|
306
|
+
.map((p) => `${p.name}: ${checker.typeToString(checker.getTypeOfSymbolAtLocation(p, t))}`)
|
|
307
|
+
.join(", ");
|
|
308
|
+
info.emits.push({
|
|
309
|
+
name: property.name,
|
|
310
|
+
...(payload ? { payloadText: payload } : {}),
|
|
311
|
+
source: "type",
|
|
312
|
+
node
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
else if (checker.isTupleType(propType) || checker.isArrayType(propType)) {
|
|
316
|
+
const text = checker.typeToString(propType);
|
|
317
|
+
info.emits.push({
|
|
318
|
+
name: property.name,
|
|
319
|
+
...(text !== "[]" ? { payloadText: text } : {}),
|
|
320
|
+
source: "type",
|
|
321
|
+
node
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
else
|
|
325
|
+
info.emits.push({ name: property.name, source: "type", node });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function readSlots(analysis, type, info) {
|
|
329
|
+
const checker = analysis.checker;
|
|
330
|
+
for (const property of checker.getTypeFromTypeNode(type).getProperties()) {
|
|
331
|
+
const propType = checker.getNonNullableType(checker.getTypeOfSymbolAtLocation(property, type));
|
|
332
|
+
const parameter = propType.getCallSignatures()[0]?.getParameters()[0];
|
|
333
|
+
const propsText = parameter
|
|
334
|
+
? checker.typeToString(checker.getTypeOfSymbolAtLocation(parameter, type))
|
|
335
|
+
: undefined;
|
|
336
|
+
info.slots.push({
|
|
337
|
+
name: property.name,
|
|
338
|
+
...(propsText && propsText !== "{}" ? { propsText } : {}),
|
|
339
|
+
node: property.valueDeclaration ?? type
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
//# sourceMappingURL=declared.js.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { ComponentFacts, excerpt } from "../compiler-core/index.js";
|
|
2
|
+
export const CAPABILITIES = [
|
|
3
|
+
"name",
|
|
4
|
+
"implementations.*",
|
|
5
|
+
"props.*",
|
|
6
|
+
"events.*",
|
|
7
|
+
"slots.*",
|
|
8
|
+
"variants.*",
|
|
9
|
+
"accessibility.*",
|
|
10
|
+
"styling.*",
|
|
11
|
+
"composition.*"
|
|
12
|
+
];
|
|
13
|
+
/** Writes one component with the neutral fact paths shared with the other framework adapters. */
|
|
14
|
+
export function emitComponent(builder, c, pkg) {
|
|
15
|
+
const ref = builder.entity(c.ref, "component", c.localName);
|
|
16
|
+
const facts = new ComponentFacts(builder, ref, "vue");
|
|
17
|
+
const at = (node, method = "compiler-api") => {
|
|
18
|
+
const location = c.locate(node);
|
|
19
|
+
return builder.evidence({
|
|
20
|
+
sourceRef: `${location.file}#${c.localName}`,
|
|
21
|
+
location: { type: "file", path: location.file, line: location.line },
|
|
22
|
+
method,
|
|
23
|
+
original: excerpt(node)
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
const block = (line, method = "ast", original) => builder.evidence({
|
|
27
|
+
sourceRef: `${c.file}#${c.localName}`,
|
|
28
|
+
location: { type: "file", path: c.file, line },
|
|
29
|
+
method,
|
|
30
|
+
...(original ? { original } : {})
|
|
31
|
+
});
|
|
32
|
+
const decl = block(c.line);
|
|
33
|
+
facts.put("name", c.localName, decl);
|
|
34
|
+
facts.link(c.file, c.localName, decl);
|
|
35
|
+
if (pkg.name)
|
|
36
|
+
facts.put("implementations.vue.packageName", pkg.name, decl);
|
|
37
|
+
const primary = [...c.exports].sort((a, b) => (a.subpath === "." ? 0 : 1) - (b.subpath === "." ? 0 : 1) ||
|
|
38
|
+
(a.name === "default" ? 1 : 0) - (b.name === "default" ? 1 : 0) ||
|
|
39
|
+
(a.name < b.name ? -1 : 1))[0];
|
|
40
|
+
if (primary) {
|
|
41
|
+
facts.put("implementations.vue.exportName", primary.name, decl);
|
|
42
|
+
if (pkg.name)
|
|
43
|
+
facts.put("implementations.vue.importPath", primary.subpath === "." ? pkg.name : `${pkg.name}/${primary.subpath.replace(/^\.\//, "")}`, decl);
|
|
44
|
+
}
|
|
45
|
+
facts.ext("kind", c.kind, decl);
|
|
46
|
+
facts.ext("exports", c.exports, decl);
|
|
47
|
+
facts.ext("propsComplete", c.propsComplete, decl);
|
|
48
|
+
if (c.sfc) {
|
|
49
|
+
const d = c.sfc.descriptor;
|
|
50
|
+
facts.ext("scriptSetup", d.scriptSetup !== null, decl);
|
|
51
|
+
facts.ext("optionsApi", c.script.optionsApi, decl);
|
|
52
|
+
const lang = d.scriptSetup?.lang ?? d.script?.lang;
|
|
53
|
+
if (lang)
|
|
54
|
+
facts.ext("scriptLang", lang, decl);
|
|
55
|
+
if (d.template?.lang)
|
|
56
|
+
facts.ext("templateLang", d.template.lang, decl);
|
|
57
|
+
}
|
|
58
|
+
if (c.generic)
|
|
59
|
+
facts.ext("generic", c.generic, decl);
|
|
60
|
+
if (c.script.inheritsFrom.length > 0)
|
|
61
|
+
facts.ext("inheritedPropsFrom", [...new Set(c.script.inheritsFrom)].sort(), decl);
|
|
62
|
+
if (c.script.exposed.length > 0)
|
|
63
|
+
facts.ext("exposed", c.script.exposed, decl);
|
|
64
|
+
if (c.script.options.name)
|
|
65
|
+
facts.ext("options.name", c.script.options.name, decl);
|
|
66
|
+
if (c.script.options.inheritAttrs !== undefined)
|
|
67
|
+
facts.ext("options.inheritAttrs", c.script.options.inheritAttrs, decl);
|
|
68
|
+
const dynamicSlots = (c.template?.slots ?? []).filter((s) => s.dynamic).length;
|
|
69
|
+
if (dynamicSlots > 0)
|
|
70
|
+
facts.ext("dynamicSlots", dynamicSlots, decl);
|
|
71
|
+
for (const p of [...c.script.props].sort((a, b) => (a.name < b.name ? -1 : 1))) {
|
|
72
|
+
const evidence = at(p.node, p.source === "type" ? "compiler-api" : "ast");
|
|
73
|
+
facts.prop({
|
|
74
|
+
name: p.name,
|
|
75
|
+
typeText: p.typeText,
|
|
76
|
+
kind: p.kind,
|
|
77
|
+
required: p.required,
|
|
78
|
+
...(p.default !== undefined ? { default: p.default } : {}),
|
|
79
|
+
...(p.defaultExpression !== undefined ? { defaultExpression: p.defaultExpression } : {}),
|
|
80
|
+
...(p.allowedValues ? { allowedValues: p.allowedValues } : {}),
|
|
81
|
+
...(p.description ? { description: p.description } : {}),
|
|
82
|
+
...(p.deprecated ? { deprecated: p.deprecated } : {}),
|
|
83
|
+
extensions: { kind: p.source === "model" ? "model" : "prop", source: p.source }
|
|
84
|
+
}, evidence);
|
|
85
|
+
}
|
|
86
|
+
for (const e of [...c.script.emits].sort((a, b) => (a.name < b.name ? -1 : 1))) {
|
|
87
|
+
facts.event({
|
|
88
|
+
name: e.name,
|
|
89
|
+
...(e.payloadText ? { payloadText: e.payloadText } : {}),
|
|
90
|
+
extensions: { kind: e.source === "model" ? "model" : "emit", source: e.source, emit: e.name }
|
|
91
|
+
}, at(e.node, e.source === "type" ? "compiler-api" : "ast"));
|
|
92
|
+
}
|
|
93
|
+
for (const s of c.slots) {
|
|
94
|
+
const evidence = s.node ? at(s.node) : block(s.line ?? c.templateLine ?? c.line);
|
|
95
|
+
facts.slot({
|
|
96
|
+
name: s.name,
|
|
97
|
+
extensions: {
|
|
98
|
+
source: s.sources.length === 2 ? "both" : s.sources[0],
|
|
99
|
+
...(s.propsText ? { props: s.propsText } : {}),
|
|
100
|
+
...(s.templateProps.length > 0 ? { propNames: s.templateProps } : {})
|
|
101
|
+
}
|
|
102
|
+
}, evidence);
|
|
103
|
+
}
|
|
104
|
+
for (const v of c.variants) {
|
|
105
|
+
const prop = c.script.props.find((p) => p.name === v.name);
|
|
106
|
+
facts.variant({ ...v, extensions: { source: "prop-union" } }, prop ? at(prop.node) : decl);
|
|
107
|
+
}
|
|
108
|
+
const a11y = c.template?.accessibility;
|
|
109
|
+
if (a11y && (a11y.roles.length > 0 || a11y.attributes.length > 0)) {
|
|
110
|
+
const evidence = block(c.templateLine ?? c.line);
|
|
111
|
+
facts.accessibility({ ...(a11y.roles.length === 1 ? { role: a11y.roles[0] } : {}), attributes: a11y.attributes }, evidence);
|
|
112
|
+
}
|
|
113
|
+
const s = c.styling;
|
|
114
|
+
if (s.blocks > 0) {
|
|
115
|
+
const evidence = block(c.line);
|
|
116
|
+
if (s.variables.length > 0) {
|
|
117
|
+
const refs = s.variables.map((v) => block(v.line, "structured-parse", `var(${v.name})`));
|
|
118
|
+
facts.put("styling.cssVariables", s.variables.map((v) => v.name), refs);
|
|
119
|
+
}
|
|
120
|
+
facts.put("styling.scoped", s.scoped, evidence);
|
|
121
|
+
if (s.module)
|
|
122
|
+
facts.put("styling.cssModules", [c.file], evidence);
|
|
123
|
+
if (s.preprocessors.length > 0)
|
|
124
|
+
facts.put("styling.preprocessors", s.preprocessors, evidence);
|
|
125
|
+
}
|
|
126
|
+
if (c.renders.length > 0)
|
|
127
|
+
facts.put("composition.renders", c.renders, block(c.templateLine ?? c.line));
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=emit.js.map
|