@invariant-ui/cli 0.0.0-stage → 0.2.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 +250 -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/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/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-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/search.js +132 -0
- package/dist/contract-query/system.js +148 -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,446 @@
|
|
|
1
|
+
import { allowedValuesOf, classifyKind, collapse, declarationOf, externalBases, isProjectNode, lineOf, literalValue, relativeFileOf, resolveSymbol, unwrap } from "../compiler-core/index.js";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
const CORE = "@angular/core";
|
|
4
|
+
const SIGNAL_INPUTS = new Set(["input", "model"]);
|
|
5
|
+
/** Local names bound to `@angular/core` exports in a file: `Component`, or `Cmp` for `import { Component as Cmp }`. */
|
|
6
|
+
export function coreBindings(sf) {
|
|
7
|
+
const bindings = new Map();
|
|
8
|
+
for (const statement of sf.statements) {
|
|
9
|
+
if (!ts.isImportDeclaration(statement) ||
|
|
10
|
+
!ts.isStringLiteral(statement.moduleSpecifier) ||
|
|
11
|
+
statement.moduleSpecifier.text !== CORE)
|
|
12
|
+
continue;
|
|
13
|
+
const named = statement.importClause?.namedBindings;
|
|
14
|
+
if (named && ts.isNamedImports(named))
|
|
15
|
+
for (const e of named.elements)
|
|
16
|
+
bindings.set(e.name.text, (e.propertyName ?? e.name).text);
|
|
17
|
+
if (named && ts.isNamespaceImport(named))
|
|
18
|
+
bindings.set(`${named.name.text}.`, "*");
|
|
19
|
+
}
|
|
20
|
+
return bindings;
|
|
21
|
+
}
|
|
22
|
+
/** Reads a class decorated with `@Component`, `@Directive` or `@NgModule`. Nothing is executed. */
|
|
23
|
+
export function readClass(analysis, node) {
|
|
24
|
+
const sf = node.getSourceFile();
|
|
25
|
+
const bindings = coreBindings(sf);
|
|
26
|
+
const decorator = classDecorator(node, bindings);
|
|
27
|
+
if (!decorator || !node.name)
|
|
28
|
+
return undefined;
|
|
29
|
+
const file = relativeFileOf(analysis, node);
|
|
30
|
+
if (!file)
|
|
31
|
+
return undefined;
|
|
32
|
+
const meta = decorator.call.arguments[0] && ts.isObjectLiteralExpression(unwrap(decorator.call.arguments[0]))
|
|
33
|
+
? unwrap(decorator.call.arguments[0])
|
|
34
|
+
: undefined;
|
|
35
|
+
const result = {
|
|
36
|
+
node,
|
|
37
|
+
name: node.name.text,
|
|
38
|
+
file,
|
|
39
|
+
line: lineOf(node),
|
|
40
|
+
kind: decorator.name === "Component" ? "component" : decorator.name === "Directive" ? "directive" : "module",
|
|
41
|
+
imports: [],
|
|
42
|
+
declarations: [],
|
|
43
|
+
exports: [],
|
|
44
|
+
styles: [],
|
|
45
|
+
styleUrls: [],
|
|
46
|
+
host: {},
|
|
47
|
+
hostDirectives: [],
|
|
48
|
+
metadataInputs: [],
|
|
49
|
+
metadataOutputs: [],
|
|
50
|
+
inputs: [],
|
|
51
|
+
outputs: [],
|
|
52
|
+
hostListeners: [],
|
|
53
|
+
hostBindings: [],
|
|
54
|
+
queries: [],
|
|
55
|
+
inheritsFrom: [],
|
|
56
|
+
notes: []
|
|
57
|
+
};
|
|
58
|
+
if (meta)
|
|
59
|
+
readMetadata(analysis, meta, result);
|
|
60
|
+
readMembers(analysis, node, result, undefined, new Set());
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
function classDecorator(node, bindings) {
|
|
64
|
+
for (const d of ts.getDecorators(node) ?? []) {
|
|
65
|
+
if (!ts.isCallExpression(d.expression))
|
|
66
|
+
continue;
|
|
67
|
+
const canonical = coreName(d.expression.expression, bindings);
|
|
68
|
+
if (canonical === "Component" || canonical === "Directive" || canonical === "NgModule")
|
|
69
|
+
return { name: canonical, call: d.expression };
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
/** `Component`, or `core.Component` when `@angular/core` is a namespace import. */
|
|
74
|
+
function coreName(expression, bindings) {
|
|
75
|
+
if (ts.isIdentifier(expression))
|
|
76
|
+
return bindings.get(expression.text);
|
|
77
|
+
if (ts.isPropertyAccessExpression(expression) &&
|
|
78
|
+
ts.isIdentifier(expression.expression) &&
|
|
79
|
+
bindings.has(`${expression.expression.text}.`))
|
|
80
|
+
return expression.name.text;
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
function property(object, key) {
|
|
84
|
+
const p = object.properties.find((x) => ts.isPropertyAssignment(x) &&
|
|
85
|
+
(ts.isIdentifier(x.name) || ts.isStringLiteralLike(x.name)) &&
|
|
86
|
+
x.name.text === key);
|
|
87
|
+
return p && ts.isPropertyAssignment(p) ? p : undefined;
|
|
88
|
+
}
|
|
89
|
+
function stringOf(node) {
|
|
90
|
+
const n = node ? unwrap(node) : undefined;
|
|
91
|
+
return n && ts.isStringLiteralLike(n) ? n.text : undefined;
|
|
92
|
+
}
|
|
93
|
+
function readMetadata(analysis, meta, out) {
|
|
94
|
+
const selector = stringOf(property(meta, "selector")?.initializer);
|
|
95
|
+
if (selector !== undefined)
|
|
96
|
+
out.selector = selector;
|
|
97
|
+
else if (property(meta, "selector"))
|
|
98
|
+
out.notes.push("selector is not a string literal");
|
|
99
|
+
const standalone = property(meta, "standalone")?.initializer;
|
|
100
|
+
if (standalone) {
|
|
101
|
+
const v = unwrap(standalone);
|
|
102
|
+
if (v.kind === ts.SyntaxKind.TrueKeyword)
|
|
103
|
+
out.standalone = true;
|
|
104
|
+
else if (v.kind === ts.SyntaxKind.FalseKeyword)
|
|
105
|
+
out.standalone = false;
|
|
106
|
+
}
|
|
107
|
+
out.imports = nameRefs(analysis, property(meta, "imports")?.initializer);
|
|
108
|
+
out.declarations = nameRefs(analysis, property(meta, "declarations")?.initializer);
|
|
109
|
+
out.exports = nameRefs(analysis, property(meta, "exports")?.initializer);
|
|
110
|
+
out.hostDirectives = nameRefs(analysis, property(meta, "hostDirectives")?.initializer).map((r) => r.name);
|
|
111
|
+
const template = property(meta, "template")?.initializer;
|
|
112
|
+
const templateUrl = stringOf(property(meta, "templateUrl")?.initializer);
|
|
113
|
+
if (template) {
|
|
114
|
+
const t = unwrap(template);
|
|
115
|
+
if (ts.isStringLiteralLike(t))
|
|
116
|
+
out.template = { inline: t.text, line: lineOf(t) };
|
|
117
|
+
else
|
|
118
|
+
out.notes.push("template is not a static string");
|
|
119
|
+
}
|
|
120
|
+
else if (templateUrl)
|
|
121
|
+
out.template = { url: templateUrl };
|
|
122
|
+
const styles = property(meta, "styles")?.initializer;
|
|
123
|
+
if (styles) {
|
|
124
|
+
const s = unwrap(styles);
|
|
125
|
+
const items = ts.isArrayLiteralExpression(s) ? s.elements.map(unwrap) : [s];
|
|
126
|
+
for (const item of items) {
|
|
127
|
+
if (ts.isStringLiteralLike(item))
|
|
128
|
+
out.styles.push({ text: item.text, line: lineOf(item) });
|
|
129
|
+
else
|
|
130
|
+
out.notes.push("a styles entry is not a static string");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const styleUrl = stringOf(property(meta, "styleUrl")?.initializer);
|
|
134
|
+
if (styleUrl)
|
|
135
|
+
out.styleUrls.push(styleUrl);
|
|
136
|
+
const styleUrls = property(meta, "styleUrls")?.initializer;
|
|
137
|
+
if (styleUrls && ts.isArrayLiteralExpression(unwrap(styleUrls))) {
|
|
138
|
+
for (const e of unwrap(styleUrls).elements) {
|
|
139
|
+
const text = stringOf(e);
|
|
140
|
+
if (text)
|
|
141
|
+
out.styleUrls.push(text);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const host = property(meta, "host")?.initializer;
|
|
145
|
+
if (host && ts.isObjectLiteralExpression(unwrap(host))) {
|
|
146
|
+
for (const p of unwrap(host).properties) {
|
|
147
|
+
if (ts.isPropertyAssignment(p) && (ts.isStringLiteralLike(p.name) || ts.isIdentifier(p.name)))
|
|
148
|
+
out.host[p.name.text] = stringOf(p.initializer) ?? collapse(p.initializer.getText()).slice(0, 120);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const lastName = (expr) => expr ? collapse(expr.getText()).split(".").pop() : undefined;
|
|
152
|
+
const change = lastName(property(meta, "changeDetection")?.initializer);
|
|
153
|
+
if (change)
|
|
154
|
+
out.changeDetection = change;
|
|
155
|
+
const encapsulation = lastName(property(meta, "encapsulation")?.initializer);
|
|
156
|
+
if (encapsulation)
|
|
157
|
+
out.encapsulation = encapsulation;
|
|
158
|
+
const exportAs = stringOf(property(meta, "exportAs")?.initializer);
|
|
159
|
+
if (exportAs)
|
|
160
|
+
out.exportAs = exportAs;
|
|
161
|
+
for (const [key, target] of [
|
|
162
|
+
["inputs", out.metadataInputs],
|
|
163
|
+
["outputs", out.metadataOutputs]
|
|
164
|
+
]) {
|
|
165
|
+
const arr = property(meta, key)?.initializer;
|
|
166
|
+
if (arr && ts.isArrayLiteralExpression(unwrap(arr))) {
|
|
167
|
+
for (const e of unwrap(arr).elements) {
|
|
168
|
+
const text = stringOf(e);
|
|
169
|
+
if (text)
|
|
170
|
+
target.push(text);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function nameRefs(analysis, expression) {
|
|
176
|
+
const array = expression ? unwrap(expression) : undefined;
|
|
177
|
+
if (!array || !ts.isArrayLiteralExpression(array))
|
|
178
|
+
return [];
|
|
179
|
+
const refs = [];
|
|
180
|
+
for (const element of array.elements) {
|
|
181
|
+
const e = ts.isSpreadElement(element) ? element.expression : element;
|
|
182
|
+
const name = ts.isObjectLiteralExpression(e) ? collapse(e.getText()).slice(0, 80) : collapse(e.getText());
|
|
183
|
+
const target = ts.isIdentifier(e) || ts.isPropertyAccessExpression(e)
|
|
184
|
+
? declarationOf(resolveSymbol(analysis.checker, analysis.checker.getSymbolAtLocation(e)))
|
|
185
|
+
: undefined;
|
|
186
|
+
const file = target && isProjectNode(analysis, target) ? relativeFileOf(analysis, target) : undefined;
|
|
187
|
+
const localName = target && "name" in target && target.name && ts.isIdentifier(target.name)
|
|
188
|
+
? target.name.text
|
|
189
|
+
: undefined;
|
|
190
|
+
refs.push({ name, ...(file ? { file } : {}), ...(file && localName ? { localName } : {}) });
|
|
191
|
+
}
|
|
192
|
+
return refs;
|
|
193
|
+
}
|
|
194
|
+
function readMembers(analysis, node, out, inheritedFrom, seen) {
|
|
195
|
+
if (seen.has(node))
|
|
196
|
+
return;
|
|
197
|
+
seen.add(node);
|
|
198
|
+
const bindings = coreBindings(node.getSourceFile());
|
|
199
|
+
for (const member of node.members) {
|
|
200
|
+
if (!ts.isPropertyDeclaration(member) &&
|
|
201
|
+
!ts.isSetAccessorDeclaration(member) &&
|
|
202
|
+
!ts.isMethodDeclaration(member) &&
|
|
203
|
+
!ts.isGetAccessorDeclaration(member))
|
|
204
|
+
continue;
|
|
205
|
+
if (!member.name || !(ts.isIdentifier(member.name) || ts.isStringLiteralLike(member.name)))
|
|
206
|
+
continue;
|
|
207
|
+
const property = member.name.text;
|
|
208
|
+
const symbol = analysis.checker.getSymbolAtLocation(member.name);
|
|
209
|
+
const doc = symbol ? ts.displayPartsToString(symbol.getDocumentationComment(analysis.checker)).trim() : "";
|
|
210
|
+
const depTag = symbol?.getJsDocTags(analysis.checker).find((t) => t.name === "deprecated");
|
|
211
|
+
const common = {
|
|
212
|
+
...(doc ? { description: doc } : {}),
|
|
213
|
+
...(depTag
|
|
214
|
+
? {
|
|
215
|
+
deprecated: ts.displayPartsToString(depTag.text).trim()
|
|
216
|
+
? { reason: ts.displayPartsToString(depTag.text).trim() }
|
|
217
|
+
: {}
|
|
218
|
+
}
|
|
219
|
+
: {}),
|
|
220
|
+
...(inheritedFrom ? { inheritedFrom } : {})
|
|
221
|
+
};
|
|
222
|
+
for (const d of ts.getDecorators(member) ?? []) {
|
|
223
|
+
const call = ts.isCallExpression(d.expression) ? d.expression : undefined;
|
|
224
|
+
const callee = call ? call.expression : d.expression;
|
|
225
|
+
const name = coreName(callee, bindings);
|
|
226
|
+
if (name === "Input")
|
|
227
|
+
out.inputs.push(decoratorInput(analysis, member, property, call, common));
|
|
228
|
+
else if (name === "Output")
|
|
229
|
+
out.outputs.push(decoratorOutput(member, property, call, common));
|
|
230
|
+
else if (name === "HostBinding" && call)
|
|
231
|
+
out.hostBindings.push(`${stringOf(call.arguments[0]) ?? property}`);
|
|
232
|
+
else if (name === "HostListener" && call)
|
|
233
|
+
out.hostListeners.push(stringOf(call.arguments[0]) ?? property);
|
|
234
|
+
else if (name && /^(Content|View)Child(ren)?$/.test(name)) {
|
|
235
|
+
const sel = call?.arguments[0]
|
|
236
|
+
? (stringOf(call.arguments[0]) ?? collapse(call.arguments[0].getText()))
|
|
237
|
+
: undefined;
|
|
238
|
+
out.queries.push({ kind: name, property, ...(sel ? { selector: sel } : {}) });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (ts.isPropertyDeclaration(member) && member.initializer)
|
|
242
|
+
readSignalMember(analysis, member, property, bindings, out, common);
|
|
243
|
+
}
|
|
244
|
+
readBase(analysis, node, out, seen);
|
|
245
|
+
}
|
|
246
|
+
function readSignalMember(analysis, member, property, bindings, out, common) {
|
|
247
|
+
const init = unwrap(member.initializer);
|
|
248
|
+
if (!ts.isCallExpression(init))
|
|
249
|
+
return;
|
|
250
|
+
const callee = init.expression;
|
|
251
|
+
const base = ts.isPropertyAccessExpression(callee) && callee.name.text === "required" ? callee.expression : callee;
|
|
252
|
+
const required = ts.isPropertyAccessExpression(callee) && callee.name.text === "required";
|
|
253
|
+
const fn = coreName(base, bindings);
|
|
254
|
+
if (fn && SIGNAL_INPUTS.has(fn)) {
|
|
255
|
+
const optionsArg = required ? init.arguments[0] : init.arguments[1];
|
|
256
|
+
const options = optionsArg && ts.isObjectLiteralExpression(unwrap(optionsArg))
|
|
257
|
+
? unwrap(optionsArg)
|
|
258
|
+
: undefined;
|
|
259
|
+
const alias = options ? stringOf(propertyOf(options, "alias")?.initializer) : undefined;
|
|
260
|
+
const transform = options ? propertyOf(options, "transform")?.initializer : undefined;
|
|
261
|
+
const firstGeneric = init.typeArguments?.[0];
|
|
262
|
+
const secondGeneric = init.typeArguments?.[1];
|
|
263
|
+
const defaultNode = required ? undefined : init.arguments[0];
|
|
264
|
+
const fallback = defaultNode ? literalValue(defaultNode) : undefined;
|
|
265
|
+
const inferred = !firstGeneric ? inferLiteralType(defaultNode) : undefined;
|
|
266
|
+
const typeInfo = firstGeneric
|
|
267
|
+
? typeInfoOf(analysis, firstGeneric)
|
|
268
|
+
: { text: inferred ?? "unknown", kind: kindOfLiteral(inferred), allowedValues: undefined };
|
|
269
|
+
const input = {
|
|
270
|
+
name: alias ?? property,
|
|
271
|
+
property,
|
|
272
|
+
kind: fn === "model" ? "model" : "signal-input",
|
|
273
|
+
typeText: typeInfo.text,
|
|
274
|
+
typeKind: typeInfo.kind,
|
|
275
|
+
required,
|
|
276
|
+
...(fallback !== undefined ? { default: fallback } : {}),
|
|
277
|
+
...(defaultNode && fallback === undefined
|
|
278
|
+
? { defaultExpression: collapse(defaultNode.getText()).slice(0, 200) }
|
|
279
|
+
: {}),
|
|
280
|
+
...(typeInfo.allowedValues ? { allowedValues: typeInfo.allowedValues } : {}),
|
|
281
|
+
...(alias ? { alias } : {}),
|
|
282
|
+
...(transform ? { transform: collapse(transform.getText()) } : {}),
|
|
283
|
+
...(secondGeneric ? { writeType: collapse(secondGeneric.getText()) } : {}),
|
|
284
|
+
...(inferred ? { typeInferred: true } : {}),
|
|
285
|
+
...common,
|
|
286
|
+
node: member
|
|
287
|
+
};
|
|
288
|
+
out.inputs.push(input);
|
|
289
|
+
if (fn === "model")
|
|
290
|
+
out.outputs.push({
|
|
291
|
+
name: `${alias ?? property}Change`,
|
|
292
|
+
property,
|
|
293
|
+
kind: "model",
|
|
294
|
+
payloadText: typeInfo.text,
|
|
295
|
+
...(common.inheritedFrom ? { inheritedFrom: common.inheritedFrom } : {}),
|
|
296
|
+
node: member
|
|
297
|
+
});
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (fn === "output") {
|
|
301
|
+
const optionsArg = init.arguments[0];
|
|
302
|
+
const options = optionsArg && ts.isObjectLiteralExpression(unwrap(optionsArg))
|
|
303
|
+
? unwrap(optionsArg)
|
|
304
|
+
: undefined;
|
|
305
|
+
const alias = options ? stringOf(propertyOf(options, "alias")?.initializer) : undefined;
|
|
306
|
+
out.outputs.push({
|
|
307
|
+
name: alias ?? property,
|
|
308
|
+
property,
|
|
309
|
+
kind: "signal-output",
|
|
310
|
+
...(init.typeArguments?.[0] ? { payloadText: collapse(init.typeArguments[0].getText()) } : {}),
|
|
311
|
+
...(alias ? { alias } : {}),
|
|
312
|
+
...(common.description ? { description: common.description } : {}),
|
|
313
|
+
...(common.inheritedFrom ? { inheritedFrom: common.inheritedFrom } : {}),
|
|
314
|
+
node: member
|
|
315
|
+
});
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (fn === "outputFromObservable")
|
|
319
|
+
out.notes.push(`outputFromObservable (${property}) is not analyzed`);
|
|
320
|
+
if (fn && /^(content|view)Child(ren)?$/.test(fn)) {
|
|
321
|
+
const sel = init.arguments[0]
|
|
322
|
+
? (stringOf(init.arguments[0]) ?? collapse(init.arguments[0].getText()))
|
|
323
|
+
: undefined;
|
|
324
|
+
out.queries.push({ kind: fn, property, ...(sel ? { selector: sel } : {}) });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function decoratorInput(analysis, member, property, call, common) {
|
|
328
|
+
const arg = call?.arguments[0] ? unwrap(call.arguments[0]) : undefined;
|
|
329
|
+
const options = arg && ts.isObjectLiteralExpression(arg) ? arg : undefined;
|
|
330
|
+
const alias = arg && ts.isStringLiteralLike(arg)
|
|
331
|
+
? arg.text
|
|
332
|
+
: options
|
|
333
|
+
? stringOf(propertyOf(options, "alias")?.initializer)
|
|
334
|
+
: undefined;
|
|
335
|
+
const required = options
|
|
336
|
+
? unwrap(propertyOf(options, "required")?.initializer ?? ts.factory.createFalse()).kind ===
|
|
337
|
+
ts.SyntaxKind.TrueKeyword
|
|
338
|
+
: false;
|
|
339
|
+
const transform = options ? propertyOf(options, "transform")?.initializer : undefined;
|
|
340
|
+
const typeNode = ts.isPropertyDeclaration(member)
|
|
341
|
+
? member.type
|
|
342
|
+
: ts.isSetAccessorDeclaration(member)
|
|
343
|
+
? member.parameters[0]?.type
|
|
344
|
+
: undefined;
|
|
345
|
+
const init = ts.isPropertyDeclaration(member) ? member.initializer : undefined;
|
|
346
|
+
const fallback = init ? literalValue(init) : undefined;
|
|
347
|
+
// Without a written type, a literal initializer fixes the type (string, number or boolean), as in TypeScript.
|
|
348
|
+
const literalType = typeNode ? undefined : inferLiteralType(init);
|
|
349
|
+
// Otherwise ask the checker: it can infer a type from the project's own declarations (a config class).
|
|
350
|
+
const checked = !typeNode && !literalType && ts.isPropertyDeclaration(member) ? checkedType(analysis, member) : undefined;
|
|
351
|
+
const inferred = literalType ?? checked?.text;
|
|
352
|
+
const info = typeNode
|
|
353
|
+
? typeInfoOf(analysis, typeNode)
|
|
354
|
+
: checked
|
|
355
|
+
? { text: checked.text, kind: checked.kind, allowedValues: undefined }
|
|
356
|
+
: { text: literalType ?? "unknown", kind: kindOfLiteral(literalType), allowedValues: undefined };
|
|
357
|
+
return {
|
|
358
|
+
name: alias ?? property,
|
|
359
|
+
property,
|
|
360
|
+
kind: "input",
|
|
361
|
+
typeText: info.text,
|
|
362
|
+
typeKind: info.kind,
|
|
363
|
+
required,
|
|
364
|
+
...(fallback !== undefined ? { default: fallback } : {}),
|
|
365
|
+
...(init && fallback === undefined ? { defaultExpression: collapse(init.getText()).slice(0, 200) } : {}),
|
|
366
|
+
...(info.allowedValues ? { allowedValues: info.allowedValues } : {}),
|
|
367
|
+
...(alias ? { alias } : {}),
|
|
368
|
+
...(transform ? { transform: collapse(transform.getText()) } : {}),
|
|
369
|
+
...(inferred ? { typeInferred: true } : {}),
|
|
370
|
+
...common,
|
|
371
|
+
node: member
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function decoratorOutput(member, property, call, common) {
|
|
375
|
+
const alias = call?.arguments[0] ? stringOf(call.arguments[0]) : undefined;
|
|
376
|
+
const init = ts.isPropertyDeclaration(member) && member.initializer ? unwrap(member.initializer) : undefined;
|
|
377
|
+
const payload = init && ts.isNewExpression(init) && init.typeArguments?.[0]
|
|
378
|
+
? collapse(init.typeArguments[0].getText())
|
|
379
|
+
: undefined;
|
|
380
|
+
return {
|
|
381
|
+
name: alias ?? property,
|
|
382
|
+
property,
|
|
383
|
+
kind: "output",
|
|
384
|
+
...(payload ? { payloadText: payload } : {}),
|
|
385
|
+
...(alias ? { alias } : {}),
|
|
386
|
+
...(common.description ? { description: common.description } : {}),
|
|
387
|
+
...(common.inheritedFrom ? { inheritedFrom: common.inheritedFrom } : {}),
|
|
388
|
+
node: member
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function readBase(analysis, node, out, seen) {
|
|
392
|
+
const base = node.heritageClauses?.find((h) => h.token === ts.SyntaxKind.ExtendsKeyword)?.types[0];
|
|
393
|
+
if (!base)
|
|
394
|
+
return;
|
|
395
|
+
const target = declarationOf(resolveSymbol(analysis.checker, analysis.checker.getSymbolAtLocation(base.expression)));
|
|
396
|
+
if (target && ts.isClassDeclaration(target) && isProjectNode(analysis, target)) {
|
|
397
|
+
readMembers(analysis, target, out, target.name?.text, seen);
|
|
398
|
+
if (ts.getDecorators(target)?.length)
|
|
399
|
+
readInheritedMetadata(analysis, target, out);
|
|
400
|
+
}
|
|
401
|
+
else
|
|
402
|
+
out.inheritsFrom.push(...externalBases(analysis, base));
|
|
403
|
+
}
|
|
404
|
+
/** Metadata `inputs`/`outputs` arrays of a decorated base class also apply to its subclasses. */
|
|
405
|
+
function readInheritedMetadata(analysis, base, out) {
|
|
406
|
+
const parent = readClass(analysis, base);
|
|
407
|
+
if (parent) {
|
|
408
|
+
out.metadataInputs.push(...parent.metadataInputs);
|
|
409
|
+
out.metadataOutputs.push(...parent.metadataOutputs);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function propertyOf(object, key) {
|
|
413
|
+
return property(object, key);
|
|
414
|
+
}
|
|
415
|
+
export function typeInfoOf(analysis, node) {
|
|
416
|
+
const { checker } = analysis;
|
|
417
|
+
const text = collapse(node.getText());
|
|
418
|
+
const type = checker.getTypeFromTypeNode(node);
|
|
419
|
+
const kind = classifyKind(checker, type, text);
|
|
420
|
+
return { text, kind, allowedValues: allowedValuesOf(checker, node, type, kind) };
|
|
421
|
+
}
|
|
422
|
+
/** The checker's inferred type of a property, unless it is unresolved (`any`/`unknown`). */
|
|
423
|
+
function checkedType(analysis, member) {
|
|
424
|
+
const { checker } = analysis;
|
|
425
|
+
const type = checker.getTypeAtLocation(member.name);
|
|
426
|
+
if (type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown))
|
|
427
|
+
return undefined;
|
|
428
|
+
const text = checker.typeToString(type);
|
|
429
|
+
return { text, kind: classifyKind(checker, type, text) };
|
|
430
|
+
}
|
|
431
|
+
function inferLiteralType(node) {
|
|
432
|
+
const n = node ? unwrap(node) : undefined;
|
|
433
|
+
if (!n)
|
|
434
|
+
return undefined;
|
|
435
|
+
if (ts.isStringLiteralLike(n))
|
|
436
|
+
return "string";
|
|
437
|
+
if (ts.isNumericLiteral(n))
|
|
438
|
+
return "number";
|
|
439
|
+
if (n.kind === ts.SyntaxKind.TrueKeyword || n.kind === ts.SyntaxKind.FalseKeyword)
|
|
440
|
+
return "boolean";
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
443
|
+
function kindOfLiteral(text) {
|
|
444
|
+
return text === "string" || text === "number" || text === "boolean" ? text : "unknown";
|
|
445
|
+
}
|
|
446
|
+
//# sourceMappingURL=classes.js.map
|