@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,75 @@
1
+ import { walkSameFunction } from "../compiler-core/index.js";
2
+ import ts from "typescript";
3
+ /**
4
+ * Slots are derived from props: `children` is the default slot, any other prop whose declared type is
5
+ * renderable (ReactNode, ReactElement, JSX.Element) is a named slot, and a function prop returning
6
+ * renderable output is a render-prop slot.
7
+ */
8
+ export function slotsOf(props) {
9
+ return props
10
+ .filter((p) => p.role === "children" || p.role === "slot" || p.role === "render-prop")
11
+ .map((p) => ({
12
+ name: p.role === "children" ? "default" : p.name,
13
+ required: p.required,
14
+ prop: p.name,
15
+ typeText: p.typeText,
16
+ renderProp: p.role === "render-prop"
17
+ }));
18
+ }
19
+ /** Static `role` and `aria-*` attributes on intrinsic (lowercase) JSX elements in the component body. */
20
+ export function accessibilityOf(candidate) {
21
+ const impl = candidate.impl;
22
+ if (!impl)
23
+ return undefined;
24
+ const roles = new Set();
25
+ const attributes = new Set();
26
+ const visit = (n) => {
27
+ const element = ts.isJsxSelfClosingElement(n) ? n : ts.isJsxOpeningElement(n) ? n : undefined;
28
+ if (!element || !/^[a-z]/.test(element.tagName.getText()))
29
+ return;
30
+ for (const attribute of element.attributes.properties) {
31
+ if (!ts.isJsxAttribute(attribute))
32
+ continue;
33
+ const name = attribute.name.getText();
34
+ if (name === "role" && attribute.initializer && ts.isStringLiteral(attribute.initializer))
35
+ roles.add(attribute.initializer.text);
36
+ else if (name.startsWith("aria-"))
37
+ attributes.add(name);
38
+ }
39
+ };
40
+ const walk = (n) => {
41
+ visit(n);
42
+ ts.forEachChild(n, walk);
43
+ };
44
+ // Nested component definitions inside the body are not this component's markup.
45
+ if (ts.isFunctionLike(impl) && impl.body)
46
+ walkSameFunction(impl, visit);
47
+ else if (ts.isClassLike(impl))
48
+ walk(impl);
49
+ if (roles.size === 0 && attributes.size === 0)
50
+ return undefined;
51
+ const only = roles.size === 1 ? [...roles][0] : undefined;
52
+ return { ...(only ? { role: only } : {}), attributes: [...attributes].sort() };
53
+ }
54
+ /** JSDoc on the component declaration: description, `@deprecated`, and `@example` blocks. */
55
+ export function docsOf(analysis, candidate) {
56
+ const { checker } = analysis;
57
+ const nameNode = candidate.decl.name;
58
+ const symbol = nameNode ? checker.getSymbolAtLocation(nameNode) : undefined;
59
+ if (!symbol)
60
+ return { examples: [] };
61
+ const description = ts.displayPartsToString(symbol.getDocumentationComment(checker)).trim();
62
+ const tags = symbol.getJsDocTags(checker);
63
+ const deprecated = tags.find((t) => t.name === "deprecated");
64
+ const examples = tags
65
+ .filter((t) => t.name === "example")
66
+ .map((t) => ts.displayPartsToString(t.text).trim())
67
+ .filter((t) => t.length > 0);
68
+ const reason = deprecated ? ts.displayPartsToString(deprecated.text).trim() : undefined;
69
+ return {
70
+ ...(description ? { description } : {}),
71
+ ...(deprecated ? { deprecated: reason ? { reason } : {} } : {}),
72
+ examples
73
+ };
74
+ }
75
+ //# sourceMappingURL=behavior.js.map
@@ -0,0 +1,558 @@
1
+ import { declarationOf, resolveSymbol, topLevelAssignments, unwrap, walkSameFunction } from "../compiler-core/index.js";
2
+ import ts from "typescript";
3
+ /** Component types of other packages that declare components by wrapping a type map: see `declaredComponent`. */
4
+ const POLYMORPHIC_WRAPPERS = new Set(["OverridableComponent", "ExtendButtonBase"]);
5
+ const PASCAL = /^[A-Z]/;
6
+ const COMPONENT_BASES = new Set(["Component", "React.Component", "PureComponent", "React.PureComponent"]);
7
+ const FC_TYPES = new Set([
8
+ "FC",
9
+ "React.FC",
10
+ "FunctionComponent",
11
+ "React.FunctionComponent",
12
+ "VFC",
13
+ "React.VFC"
14
+ ]);
15
+ /** Types a declaration file gives a component constant, and whether the first type argument is its props. */
16
+ const DECLARED_COMPONENT_TYPES = {
17
+ ForwardRefExoticComponent: { kind: "forwardRef" },
18
+ ExoticComponent: { kind: "function" },
19
+ NamedExoticComponent: { kind: "function" },
20
+ ComponentType: { kind: "function" },
21
+ ComponentClass: { kind: "function" },
22
+ FC: { kind: "function" },
23
+ FunctionComponent: { kind: "function" },
24
+ VFC: { kind: "function" }
25
+ };
26
+ const REACT_PREFIX = String.raw `(?:React\.|import\(["']react["']\)\.)?`;
27
+ const JSX_RETURN = new RegExp(`^(?:${REACT_PREFIX}JSX\\.Element|${REACT_PREFIX}ReactElement(?:<[\\s\\S]*>)?|${REACT_PREFIX}ReactNode)$`);
28
+ const STYLED_MODULES = {
29
+ "styled-components": "styled-components",
30
+ "@emotion/styled": "emotion"
31
+ };
32
+ /**
33
+ * Decides whether a declaration is a React component using syntax only: a Pascal-case function (or
34
+ * class extending Component/PureComponent) that returns JSX, optionally wrapped in forwardRef, memo or
35
+ * lazy, a styled-components/emotion tagged template, or `Object.assign(Root, { Child })`. Anything
36
+ * that returns a component (a higher-order component) is reported, not analyzed.
37
+ */
38
+ export function classifyDeclaration(checker, decl, displayHint, depth = 0) {
39
+ if (depth > 6)
40
+ return undefined;
41
+ if (ts.isFunctionDeclaration(decl)) {
42
+ const name = decl.name?.text ?? displayHint;
43
+ if (isComponentFunction(decl, name))
44
+ return finish({ decl, name, kind: "function", wrappers: [], impl: decl, propsUnknown: false, subcomponents: [] }, decl);
45
+ // `export declare function Card(props: P): JSX.Element`: a declaration file has no body to read JSX from.
46
+ if (isDeclaredComponentFunction(decl, name))
47
+ return finish({ decl, name, kind: "function", wrappers: [], impl: decl, propsUnknown: false, subcomponents: [] }, decl);
48
+ return returnsComponentFunction(decl) ? { hoc: true, name, node: decl } : undefined;
49
+ }
50
+ if (ts.isVariableDeclaration(decl) && ts.isIdentifier(decl.name)) {
51
+ const name = decl.name.text;
52
+ let fromInit = decl.initializer
53
+ ? classifyExpression(checker, decl.initializer, { name, decl, depth })
54
+ : undefined;
55
+ // A Pascal-case function declared as `React.FC<P>` is a component even when its return value is not literal JSX.
56
+ const init = decl.initializer ? unwrap(decl.initializer) : undefined;
57
+ if (!fromInit &&
58
+ PASCAL.test(name) &&
59
+ fcTypeArgument(decl.type) &&
60
+ init &&
61
+ (ts.isArrowFunction(init) || ts.isFunctionExpression(init))) {
62
+ fromInit = {
63
+ decl,
64
+ name,
65
+ kind: "function",
66
+ wrappers: [],
67
+ impl: init,
68
+ propsUnknown: false,
69
+ subcomponents: []
70
+ };
71
+ }
72
+ if (fromInit && "hoc" in fromInit)
73
+ return fromInit;
74
+ if (fromInit) {
75
+ const fcProps = fcTypeArgument(decl.type);
76
+ if (fcProps && !fromInit.propsTypeNode)
77
+ fromInit.propsTypeNode = fcProps;
78
+ return finish(fromInit, decl);
79
+ }
80
+ // `export declare const Button: ForwardRefExoticComponent<P & RefAttributes<T>>`
81
+ const declared = !decl.initializer && decl.type ? declaredComponent(checker, decl.type, name, decl) : undefined;
82
+ return declared ? finish(declared, decl) : undefined;
83
+ }
84
+ // A member of a declared compound object: `Item: (props: P) => JSX.Element`.
85
+ if (ts.isPropertySignature(decl) && decl.type) {
86
+ if (ts.isTypeQueryNode(decl.type)) {
87
+ const query = decl.type.exprName;
88
+ const symbol = checker.getSymbolAtLocation(ts.isQualifiedName(query) ? query.right : query);
89
+ const target = declarationOf(resolveSymbol(checker, symbol));
90
+ return target && target !== decl
91
+ ? classifyDeclaration(checker, target, displayHint, depth + 1)
92
+ : undefined;
93
+ }
94
+ return declaredComponent(checker, decl.type, displayHint, decl);
95
+ }
96
+ if (ts.isClassDeclaration(decl)) {
97
+ const base = componentBase(decl);
98
+ if (!base)
99
+ return undefined;
100
+ const name = decl.name?.text ?? displayHint;
101
+ return finish({
102
+ decl,
103
+ name,
104
+ kind: "class",
105
+ wrappers: [],
106
+ impl: decl,
107
+ ...(base.typeArguments?.[0] ? { propsTypeNode: base.typeArguments[0] } : {}),
108
+ propsUnknown: !base.typeArguments?.[0],
109
+ subcomponents: []
110
+ }, decl);
111
+ }
112
+ if (ts.isExportAssignment(decl) && !decl.isExportEquals) {
113
+ const expression = unwrap(decl.expression);
114
+ if (ts.isIdentifier(expression)) {
115
+ // `export default Link`: the component is whatever `Link` declares, under its own name.
116
+ const target = declarationOf(resolveSymbol(checker, checker.getSymbolAtLocation(expression)));
117
+ return target && target !== decl
118
+ ? classifyDeclaration(checker, target, expression.text, depth + 1)
119
+ : undefined;
120
+ }
121
+ return classifyExpression(checker, expression, { name: displayHint, decl, depth });
122
+ }
123
+ return undefined;
124
+ }
125
+ function classifyExpression(checker, expression, ctx) {
126
+ const node = unwrap(expression);
127
+ const { name, decl, depth } = ctx;
128
+ if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
129
+ if (isComponentFunction(node, name))
130
+ return {
131
+ decl,
132
+ name,
133
+ kind: "function",
134
+ wrappers: [],
135
+ impl: node,
136
+ propsUnknown: false,
137
+ subcomponents: []
138
+ };
139
+ return returnsComponentFunction(node) ? { hoc: true, name, node } : undefined;
140
+ }
141
+ if (ts.isIdentifier(node)) {
142
+ const target = declarationOf(resolveSymbol(checker, checker.getSymbolAtLocation(node)));
143
+ if (!target || target === decl)
144
+ return undefined;
145
+ const inner = classifyDeclaration(checker, target, name, depth + 1);
146
+ return inner && !("hoc" in inner) ? { ...inner, decl, name } : inner;
147
+ }
148
+ if (ts.isTaggedTemplateExpression(node)) {
149
+ const library = styledLibrary(node.tag);
150
+ if (!library)
151
+ return undefined;
152
+ return {
153
+ decl,
154
+ name,
155
+ kind: "styled",
156
+ wrappers: [],
157
+ styled: { library },
158
+ ...(node.typeArguments?.[0] ? { propsTypeNode: node.typeArguments[0] } : {}),
159
+ propsUnknown: !node.typeArguments?.[0],
160
+ subcomponents: []
161
+ };
162
+ }
163
+ if (ts.isCallExpression(node)) {
164
+ const callee = node.expression.getText();
165
+ const arg = node.arguments[0];
166
+ if ((callee === "forwardRef" || callee === "React.forwardRef") && arg) {
167
+ const inner = classifyExpression(checker, arg, { name, decl, depth: depth + 1 });
168
+ const impl = inner && !("hoc" in inner) ? inner.impl : undefined;
169
+ const refType = node.typeArguments?.[0];
170
+ return {
171
+ decl,
172
+ name,
173
+ kind: "forwardRef",
174
+ wrappers: ["forwardRef"],
175
+ ...(impl ? { impl } : {}),
176
+ ...(node.typeArguments?.[1] ? { propsTypeNode: node.typeArguments[1] } : {}),
177
+ ...(refType ? { refTypeText: refType.getText() } : {}),
178
+ propsUnknown: false,
179
+ subcomponents: []
180
+ };
181
+ }
182
+ if ((callee === "memo" || callee === "React.memo") && arg) {
183
+ const inner = classifyExpression(checker, arg, { name, decl, depth: depth + 1 });
184
+ if (!inner || "hoc" in inner)
185
+ return inner;
186
+ return { ...inner, decl, kind: "memo", wrappers: ["memo", ...inner.wrappers] };
187
+ }
188
+ if ((callee === "lazy" || callee === "React.lazy") && arg) {
189
+ return { decl, name, kind: "lazy", wrappers: ["lazy"], propsUnknown: true, subcomponents: [] };
190
+ }
191
+ if (callee === "Object.assign" && node.arguments.length >= 2) {
192
+ const root = classifyExpression(checker, node.arguments[0], {
193
+ name,
194
+ decl,
195
+ depth: depth + 1
196
+ });
197
+ if (!root || "hoc" in root)
198
+ return root;
199
+ const extras = unwrap(node.arguments[1]);
200
+ const subcomponents = [];
201
+ if (ts.isObjectLiteralExpression(extras)) {
202
+ for (const property of extras.properties) {
203
+ const key = property.name && (ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name))
204
+ ? property.name.text
205
+ : undefined;
206
+ if (key && (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property)))
207
+ subcomponents.push({ key, target: property });
208
+ }
209
+ }
210
+ return { ...root, decl, subcomponents: [...root.subcomponents, ...subcomponents] };
211
+ }
212
+ }
213
+ return undefined;
214
+ }
215
+ /**
216
+ * A declaration file's compound object, `declare const Dialog: { Root: typeof DialogRoot; Title: typeof DialogTitle }`.
217
+ * It is a namespace, not a component, so only its members are returned (named `Dialog.Root` by the caller).
218
+ */
219
+ export function classifyCompound(checker, decl, name) {
220
+ if (!ts.isVariableDeclaration(decl) || decl.initializer || !decl.type || !ts.isTypeLiteralNode(decl.type))
221
+ return undefined;
222
+ if (!PASCAL.test(name))
223
+ return undefined;
224
+ const out = [];
225
+ for (const member of decl.type.members) {
226
+ if (!ts.isPropertySignature(member) || !member.type)
227
+ continue;
228
+ if (!member.name || !(ts.isIdentifier(member.name) || ts.isStringLiteralLike(member.name)))
229
+ continue;
230
+ const key = member.name.text;
231
+ if (!PASCAL.test(key))
232
+ continue;
233
+ let candidate;
234
+ if (ts.isTypeQueryNode(member.type)) {
235
+ const query = member.type.exprName;
236
+ const symbol = checker.getSymbolAtLocation(ts.isQualifiedName(query) ? query.right : query);
237
+ const target = declarationOf(resolveSymbol(checker, symbol));
238
+ candidate = target ? classifyDeclaration(checker, target, key) : undefined;
239
+ }
240
+ else
241
+ candidate = declaredComponent(checker, member.type, key, member);
242
+ if (candidate && !("hoc" in candidate))
243
+ out.push({ key, candidate });
244
+ }
245
+ return out.length > 0 ? out : undefined;
246
+ }
247
+ function unparen(type) {
248
+ return ts.isParenthesizedTypeNode(type) ? unparen(type.type) : type;
249
+ }
250
+ /** The name and type arguments of `Foo<P>`, `React.Foo<P>` and `import('react').Foo<P>`. */
251
+ function refInfo(type) {
252
+ if (ts.isTypeReferenceNode(type)) {
253
+ const n = type.typeName;
254
+ return {
255
+ name: ts.isQualifiedName(n) ? n.right.text : n.text,
256
+ ...(type.typeArguments ? { typeArguments: type.typeArguments } : {})
257
+ };
258
+ }
259
+ if (ts.isImportTypeNode(type) && type.qualifier) {
260
+ const q = type.qualifier;
261
+ return {
262
+ name: ts.isQualifiedName(q) ? q.right.text : q.text,
263
+ ...(type.typeArguments ? { typeArguments: type.typeArguments } : {})
264
+ };
265
+ }
266
+ return undefined;
267
+ }
268
+ function returnsJsxType(type) {
269
+ if (!type)
270
+ return false;
271
+ return JSX_RETURN.test(type.getText().replace(/\s*\|\s*(?:null|undefined)/g, ""));
272
+ }
273
+ function isDeclaredComponentFunction(fn, name) {
274
+ return PASCAL.test(name) && !fn.body && fn.parameters.length <= 1 && returnsJsxType(fn.type);
275
+ }
276
+ /**
277
+ * A component declared by its type in a declaration file, where there is no body to read JSX from:
278
+ * `React.ForwardRefExoticComponent<P & RefAttributes<T>>`, `React.FC<P>`, an arrow type `(props: P) => JSX.Element`,
279
+ * a callable object `{ (props: P): JSX.Element; Item: … }` (its Pascal-case members are subcomponents), or an alias of those.
280
+ */
281
+ export function declaredComponent(checker, type, name, decl, depth = 0) {
282
+ if (depth > 4 || !PASCAL.test(name))
283
+ return undefined;
284
+ const t = unparen(type);
285
+ const make = (extra) => ({
286
+ decl,
287
+ name,
288
+ kind: "function",
289
+ wrappers: [],
290
+ propsUnknown: false,
291
+ subcomponents: [],
292
+ ...extra
293
+ });
294
+ if (ts.isFunctionTypeNode(t)) {
295
+ if (t.parameters.length > 1 || !returnsJsxType(t.type))
296
+ return undefined;
297
+ const props = t.parameters[0]?.type;
298
+ return make(props ? { propsTypeNode: props } : {});
299
+ }
300
+ if (ts.isTypeLiteralNode(t)) {
301
+ const call = t.members.find(ts.isCallSignatureDeclaration);
302
+ if (!call || call.parameters.length > 1 || !returnsJsxType(call.type))
303
+ return undefined;
304
+ const props = call.parameters[0]?.type;
305
+ const subcomponents = [];
306
+ for (const member of t.members) {
307
+ if (!ts.isPropertySignature(member) || !member.type || !member.name)
308
+ continue;
309
+ if (!(ts.isIdentifier(member.name) || ts.isStringLiteralLike(member.name)))
310
+ continue;
311
+ const key = member.name.text;
312
+ if (PASCAL.test(key) && declaredComponent(checker, member.type, key, member, depth + 1))
313
+ subcomponents.push({ key, target: member });
314
+ }
315
+ return make({ ...(props ? { propsTypeNode: props } : {}), subcomponents });
316
+ }
317
+ // `NamedExoticComponent<P> & ThemeExtend<…>`: the component type is one member, the rest are static properties.
318
+ if (ts.isIntersectionTypeNode(t)) {
319
+ for (const member of t.types) {
320
+ const found = declaredComponent(checker, member, name, decl, depth + 1);
321
+ if (found)
322
+ return found;
323
+ }
324
+ }
325
+ const ref = refInfo(t);
326
+ if (!ref)
327
+ return callableComponent(checker, t, make);
328
+ // Checked first: the wrapper's own declaration is an alias whose first call signature (`href` required) is a trap.
329
+ const polymorphic = POLYMORPHIC_WRAPPERS.has(ref.name);
330
+ if (polymorphic) {
331
+ // `OverridableComponent<ButtonTypeMap>`: the component's own props are the type map's `props`. Its call signatures
332
+ // are no help: `ExtendButtonBase` lists an `href`-required overload first, and the `OverridableComponent` parameter
333
+ // names the generic `M`. The wrapper also adds a `component` prop and system props, so the list is never closed.
334
+ // Without a readable type map (it comes from a package that is not installed) there are no props to report.
335
+ const typeMapProps = propsOfTypeMap(checker, ref.typeArguments?.[0]);
336
+ return make({
337
+ openProps: true,
338
+ ...(typeMapProps ? { propsTypeNode: typeMapProps } : { propsUnknown: true })
339
+ });
340
+ }
341
+ const known = DECLARED_COMPONENT_TYPES[ref.name];
342
+ const fromKnown = known ? knownComponent(known, ref.typeArguments, make) : undefined;
343
+ if (fromKnown)
344
+ return fromKnown;
345
+ if (!known && (ts.isTypeReferenceNode(t) || (ts.isImportTypeNode(t) && t.qualifier))) {
346
+ const reference = ts.isTypeReferenceNode(t) ? t.typeName : t.qualifier;
347
+ const target = declarationOf(resolveSymbol(checker, checker.getSymbolAtLocation(reference)));
348
+ // `type TableSelectComponent = { (props: P): JSX.Element; Option: … }`
349
+ if (target && ts.isTypeAliasDeclaration(target)) {
350
+ const aliased = declaredComponent(checker, target.type, name, decl, depth + 1);
351
+ // Mantine's `MantineComponent<{ props: AlertProps; … }>`: the alias's own props are `Payload['props']`, so read the
352
+ // concrete type from the payload object at the use site.
353
+ const payload = t.typeArguments?.[0];
354
+ const payloadProps = payload && ts.isTypeLiteralNode(payload)
355
+ ? payload.members.find((m) => ts.isPropertySignature(m) && m.name.getText() === "props")?.type
356
+ : undefined;
357
+ if (aliased && payloadProps)
358
+ aliased.propsTypeNode = payloadProps;
359
+ if (aliased)
360
+ return aliased;
361
+ }
362
+ // `interface X extends React.ForwardRefExoticComponent<P> { Item: typeof Item }`
363
+ if (target && ts.isInterfaceDeclaration(target)) {
364
+ for (const heritage of target.heritageClauses ?? []) {
365
+ if (heritage.token !== ts.SyntaxKind.ExtendsKeyword)
366
+ continue;
367
+ for (const base of heritage.types) {
368
+ const baseKnown = DECLARED_COMPONENT_TYPES[base.expression.getText().split(".").pop() ?? ""];
369
+ const candidate = baseKnown ? knownComponent(baseKnown, base.typeArguments, make) : undefined;
370
+ if (!candidate)
371
+ continue;
372
+ for (const member of target.members) {
373
+ if (!ts.isPropertySignature(member) || !member.type || !member.name)
374
+ continue;
375
+ if (!(ts.isIdentifier(member.name) || ts.isStringLiteralLike(member.name)))
376
+ continue;
377
+ const key = member.name.text;
378
+ if (!PASCAL.test(key))
379
+ continue;
380
+ if (ts.isTypeQueryNode(member.type) ||
381
+ declaredComponent(checker, member.type, key, member, depth + 1))
382
+ candidate.subcomponents.push({ key, target: member });
383
+ }
384
+ return candidate;
385
+ }
386
+ }
387
+ }
388
+ }
389
+ return callableComponent(checker, t, make);
390
+ }
391
+ /** The `props` member of a type map declared in the analyzed package (`interface ButtonTypeMap { props: P; … }`). */
392
+ function propsOfTypeMap(checker, arg) {
393
+ if (!arg || !ts.isTypeReferenceNode(arg))
394
+ return undefined;
395
+ const target = declarationOf(resolveSymbol(checker, checker.getSymbolAtLocation(arg.typeName)));
396
+ // `type AccordionTypeMap<…> = ExtendPaperTypeMap<{ props: …; defaultComponent: … }>`: the map is the wrapper's argument.
397
+ const literal = (t) => {
398
+ if (!t)
399
+ return undefined;
400
+ if (ts.isTypeLiteralNode(t))
401
+ return t;
402
+ const first = ts.isTypeReferenceNode(t) ? t.typeArguments?.[0] : undefined;
403
+ return first && ts.isTypeLiteralNode(first) ? first : undefined;
404
+ };
405
+ const members = target
406
+ ? ts.isInterfaceDeclaration(target)
407
+ ? target.members
408
+ : ts.isTypeAliasDeclaration(target)
409
+ ? literal(target.type)?.members
410
+ : undefined
411
+ : undefined;
412
+ const prop = members?.find((m) => ts.isPropertySignature(m) && m.name.getText() === "props");
413
+ return prop?.type;
414
+ }
415
+ /**
416
+ * A component whose declared type is not one of the known React component types but is still callable with props and
417
+ * returns JSX: MUI's `OverridableComponent<M>` and `ExtendButtonBase<M>`, Mantine's `((props: P) => JSX.Element) & ThemeExtend<…>`.
418
+ * The type checker finds the call signatures, so library-specific wrapper names need no list.
419
+ */
420
+ function callableComponent(checker, node, make) {
421
+ const signatures = checker.getSignaturesOfType(checker.getTypeFromTypeNode(node), ts.SignatureKind.Call);
422
+ const jsx = signatures.filter((s) => s.parameters.length <= 1 && signatureReturnsJsx(checker, s));
423
+ // An overload without type parameters has concrete props; generic ones are `component`-polymorphic variants.
424
+ const sig = jsx.find((s) => !s.typeParameters?.length) ?? jsx[0];
425
+ if (!sig)
426
+ return undefined;
427
+ const param = sig.parameters[0]?.valueDeclaration;
428
+ const props = param && ts.isParameter(param) ? param.type : undefined;
429
+ return make(props ? { propsTypeNode: props } : {});
430
+ }
431
+ function signatureReturnsJsx(checker, sig) {
432
+ const declared = sig.declaration?.type;
433
+ if (declared && ts.isTypeNode(declared))
434
+ return returnsJsxType(declared);
435
+ return JSX_RETURN.test(checker.typeToString(sig.getReturnType()).replace(/\s*\|\s*(?:null|undefined)/g, ""));
436
+ }
437
+ /** A component typed as one of the React component types, with its props from the first type argument. */
438
+ function knownComponent(known, typeArguments, make) {
439
+ const props = typeArguments?.[0];
440
+ if (!props)
441
+ return undefined;
442
+ // `P & RefAttributes<T>` is how forwardRef components declare their ref: it is not part of the props.
443
+ let propsTypeNode = props;
444
+ let refTypeText;
445
+ if (ts.isIntersectionTypeNode(props)) {
446
+ const isRef = (n) => refInfo(n)?.name === "RefAttributes";
447
+ const refs = props.types.filter(isRef);
448
+ const rest = props.types.filter((n) => !isRef(n));
449
+ if (refs.length === 1 && rest.length === 1) {
450
+ propsTypeNode = rest[0];
451
+ refTypeText = refInfo(refs[0])?.typeArguments?.[0]?.getText();
452
+ }
453
+ }
454
+ return make({
455
+ kind: known.kind,
456
+ wrappers: known.kind === "forwardRef" ? ["forwardRef"] : [],
457
+ propsTypeNode,
458
+ ...(refTypeText ? { refTypeText } : {})
459
+ });
460
+ }
461
+ /** Adds `Root.displayName = "…"` and `Root.Item = Item` information found next to the declaration. */
462
+ function finish(candidate, decl) {
463
+ const assignments = topLevelAssignments(decl.getSourceFile());
464
+ const display = assignments.get(`${candidate.name}.displayName`);
465
+ if (display && ts.isStringLiteralLike(unwrap(display)))
466
+ candidate.reactDisplayName = unwrap(display).text;
467
+ for (const [key, value] of assignments) {
468
+ if (!key.startsWith(`${candidate.name}.`))
469
+ continue;
470
+ const member = key.slice(candidate.name.length + 1);
471
+ if (member === "displayName" || member === "defaultProps" || !PASCAL.test(member))
472
+ continue;
473
+ candidate.subcomponents.push({ key: member, target: value });
474
+ }
475
+ return candidate;
476
+ }
477
+ function isComponentFunction(fn, name) {
478
+ return PASCAL.test(name) && returnsJsx(fn);
479
+ }
480
+ export function returnsJsx(fn) {
481
+ if (!fn.body)
482
+ return false;
483
+ if (!ts.isBlock(fn.body))
484
+ return yieldsJsx(fn.body);
485
+ let found = false;
486
+ walkSameFunction(fn, (n) => {
487
+ if (!found && ts.isReturnStatement(n) && n.expression && yieldsJsx(n.expression))
488
+ found = true;
489
+ });
490
+ return found;
491
+ }
492
+ function yieldsJsx(expression) {
493
+ const node = unwrap(expression);
494
+ if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node))
495
+ return true;
496
+ if (ts.isConditionalExpression(node))
497
+ return yieldsJsx(node.whenTrue) || yieldsJsx(node.whenFalse);
498
+ if (ts.isBinaryExpression(node)) {
499
+ const op = node.operatorToken.kind;
500
+ if (op === ts.SyntaxKind.AmpersandAmpersandToken ||
501
+ op === ts.SyntaxKind.BarBarToken ||
502
+ op === ts.SyntaxKind.QuestionQuestionToken) {
503
+ return yieldsJsx(node.left) || yieldsJsx(node.right);
504
+ }
505
+ }
506
+ if (ts.isCallExpression(node))
507
+ return /(?:^|\.)(?:_?jsxs?|createElement|cloneElement|createPortal)$/.test(node.expression.getText());
508
+ return false;
509
+ }
510
+ function returnsComponentFunction(fn) {
511
+ if (!fn.body)
512
+ return false;
513
+ const isInner = (e) => {
514
+ const inner = unwrap(e);
515
+ return (ts.isArrowFunction(inner) || ts.isFunctionExpression(inner)) && returnsJsx(inner);
516
+ };
517
+ if (!ts.isBlock(fn.body))
518
+ return isInner(fn.body);
519
+ let found = false;
520
+ walkSameFunction(fn, (n) => {
521
+ if (!found && ts.isReturnStatement(n) && n.expression && isInner(n.expression))
522
+ found = true;
523
+ });
524
+ return found;
525
+ }
526
+ function componentBase(cls) {
527
+ const extendsClause = cls.heritageClauses?.find((h) => h.token === ts.SyntaxKind.ExtendsKeyword);
528
+ const base = extendsClause?.types[0];
529
+ return base && COMPONENT_BASES.has(base.expression.getText()) ? base : undefined;
530
+ }
531
+ function fcTypeArgument(type) {
532
+ if (type && ts.isTypeReferenceNode(type) && FC_TYPES.has(type.typeName.getText()))
533
+ return type.typeArguments?.[0];
534
+ return undefined;
535
+ }
536
+ function styledLibrary(tag) {
537
+ let root = tag;
538
+ while (ts.isPropertyAccessExpression(root) || ts.isCallExpression(root))
539
+ root = ts.isCallExpression(root) ? root.expression : root.expression;
540
+ if (!ts.isIdentifier(root))
541
+ return undefined;
542
+ for (const statement of root.getSourceFile().statements) {
543
+ if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier))
544
+ continue;
545
+ const library = STYLED_MODULES[statement.moduleSpecifier.text];
546
+ const clause = statement.importClause;
547
+ if (!library || !clause)
548
+ continue;
549
+ if (clause.name?.text === root.text)
550
+ return library;
551
+ if (clause.namedBindings &&
552
+ ts.isNamedImports(clause.namedBindings) &&
553
+ clause.namedBindings.elements.some((e) => e.name.text === root.text))
554
+ return library;
555
+ }
556
+ return undefined;
557
+ }
558
+ //# sourceMappingURL=detect.js.map