@facilis.dev/analyze 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 (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -2
  3. package/dist/analyze.d.ts +17 -0
  4. package/dist/analyze.js +99 -0
  5. package/dist/ast/node.d.ts +29 -0
  6. package/dist/ast/node.js +85 -0
  7. package/dist/ast/walk.d.ts +8 -0
  8. package/dist/ast/walk.js +40 -0
  9. package/dist/diagnostics.d.ts +5 -0
  10. package/dist/diagnostics.js +12 -0
  11. package/dist/fs.d.ts +13 -0
  12. package/dist/fs.js +51 -0
  13. package/dist/graph-edges.d.ts +3 -0
  14. package/dist/graph-edges.js +148 -0
  15. package/dist/graph-ids.d.ts +6 -0
  16. package/dist/graph-ids.js +22 -0
  17. package/dist/graph.d.ts +15 -0
  18. package/dist/graph.js +158 -0
  19. package/dist/index.d.ts +23 -0
  20. package/dist/index.js +15 -0
  21. package/dist/json.d.ts +8 -0
  22. package/dist/json.js +31 -0
  23. package/dist/module-graph.d.ts +33 -0
  24. package/dist/module-graph.js +86 -0
  25. package/dist/module-info.d.ts +27 -0
  26. package/dist/module-info.js +143 -0
  27. package/dist/node.d.ts +16 -0
  28. package/dist/node.js +41 -0
  29. package/dist/parser.d.ts +16 -0
  30. package/dist/parser.js +1 -0
  31. package/dist/passes/clients.d.ts +22 -0
  32. package/dist/passes/clients.js +167 -0
  33. package/dist/passes/config.d.ts +16 -0
  34. package/dist/passes/config.js +229 -0
  35. package/dist/passes/file-facts.d.ts +13 -0
  36. package/dist/passes/file-facts.js +130 -0
  37. package/dist/passes/i18n.d.ts +14 -0
  38. package/dist/passes/i18n.js +55 -0
  39. package/dist/passes/job-relations-diagnostics.d.ts +9 -0
  40. package/dist/passes/job-relations-diagnostics.js +38 -0
  41. package/dist/passes/job-relations.d.ts +18 -0
  42. package/dist/passes/job-relations.js +52 -0
  43. package/dist/passes/jobs.d.ts +20 -0
  44. package/dist/passes/jobs.js +133 -0
  45. package/dist/passes/jsx.d.ts +22 -0
  46. package/dist/passes/jsx.js +89 -0
  47. package/dist/passes/path-expression.d.ts +26 -0
  48. package/dist/passes/path-expression.js +265 -0
  49. package/dist/passes/route-config.d.ts +24 -0
  50. package/dist/passes/route-config.js +152 -0
  51. package/dist/passes/route-description.d.ts +13 -0
  52. package/dist/passes/route-description.js +23 -0
  53. package/dist/passes/route-elevation.d.ts +18 -0
  54. package/dist/passes/route-elevation.js +136 -0
  55. package/dist/passes/route-handler.d.ts +27 -0
  56. package/dist/passes/route-handler.js +87 -0
  57. package/dist/passes/route-location.d.ts +16 -0
  58. package/dist/passes/route-location.js +58 -0
  59. package/dist/passes/route-relation-resolve.d.ts +51 -0
  60. package/dist/passes/route-relation-resolve.js +172 -0
  61. package/dist/passes/route-relations-diagnostics.d.ts +15 -0
  62. package/dist/passes/route-relations-diagnostics.js +79 -0
  63. package/dist/passes/route-relations-follow.d.ts +16 -0
  64. package/dist/passes/route-relations-follow.js +116 -0
  65. package/dist/passes/route-relations-usage.d.ts +50 -0
  66. package/dist/passes/route-relations-usage.js +44 -0
  67. package/dist/passes/route-relations-walk.d.ts +25 -0
  68. package/dist/passes/route-relations-walk.js +199 -0
  69. package/dist/passes/route-relations.d.ts +17 -0
  70. package/dist/passes/route-relations.js +43 -0
  71. package/dist/passes/route-table.d.ts +21 -0
  72. package/dist/passes/route-table.js +104 -0
  73. package/dist/passes/route-tables.d.ts +10 -0
  74. package/dist/passes/route-tables.js +42 -0
  75. package/dist/passes/routes.d.ts +18 -0
  76. package/dist/passes/routes.js +152 -0
  77. package/dist/passes/schema-column-types.d.ts +9 -0
  78. package/dist/passes/schema-column-types.js +55 -0
  79. package/dist/passes/schema-columns.d.ts +35 -0
  80. package/dist/passes/schema-columns.js +197 -0
  81. package/dist/passes/schema-dsl.d.ts +24 -0
  82. package/dist/passes/schema-dsl.js +117 -0
  83. package/dist/passes/schema-file-links-ddl.d.ts +18 -0
  84. package/dist/passes/schema-file-links-ddl.js +106 -0
  85. package/dist/passes/schema-file-links.d.ts +19 -0
  86. package/dist/passes/schema-file-links.js +144 -0
  87. package/dist/passes/schema-foreign-keys.d.ts +21 -0
  88. package/dist/passes/schema-foreign-keys.js +80 -0
  89. package/dist/passes/schema-policies.d.ts +15 -0
  90. package/dist/passes/schema-policies.js +201 -0
  91. package/dist/passes/schema-re-exports.d.ts +5 -0
  92. package/dist/passes/schema-re-exports.js +77 -0
  93. package/dist/passes/schema-relations.d.ts +8 -0
  94. package/dist/passes/schema-relations.js +182 -0
  95. package/dist/passes/schema-scope.d.ts +7 -0
  96. package/dist/passes/schema-scope.js +20 -0
  97. package/dist/passes/schema-tables.d.ts +13 -0
  98. package/dist/passes/schema-tables.js +149 -0
  99. package/dist/passes/schema.d.ts +9 -0
  100. package/dist/passes/schema.js +55 -0
  101. package/dist/passes/screen-components.d.ts +22 -0
  102. package/dist/passes/screen-components.js +143 -0
  103. package/dist/passes/screen-preview.d.ts +29 -0
  104. package/dist/passes/screen-preview.js +159 -0
  105. package/dist/passes/screens.d.ts +12 -0
  106. package/dist/passes/screens.js +278 -0
  107. package/dist/passes/static-read.d.ts +47 -0
  108. package/dist/passes/static-read.js +151 -0
  109. package/dist/passes/views.d.ts +20 -0
  110. package/dist/passes/views.js +101 -0
  111. package/dist/passes/workflow-tables.d.ts +18 -0
  112. package/dist/passes/workflow-tables.js +71 -0
  113. package/dist/passes/workflows.d.ts +8 -0
  114. package/dist/passes/workflows.js +160 -0
  115. package/dist/path-resolver.d.ts +20 -0
  116. package/dist/path-resolver.js +134 -0
  117. package/dist/revision.d.ts +2 -0
  118. package/dist/revision.js +23 -0
  119. package/dist/source.d.ts +4 -0
  120. package/dist/source.js +13 -0
  121. package/dist/static-eval.d.ts +36 -0
  122. package/dist/static-eval.js +215 -0
  123. package/dist/types-graph.d.ts +130 -0
  124. package/dist/types-graph.js +4 -0
  125. package/dist/types-jobs.d.ts +28 -0
  126. package/dist/types-jobs.js +14 -0
  127. package/dist/types-relations.d.ts +49 -0
  128. package/dist/types-relations.js +11 -0
  129. package/dist/types-workflows.d.ts +36 -0
  130. package/dist/types-workflows.js +3 -0
  131. package/dist/types.d.ts +226 -0
  132. package/dist/types.js +1 -0
  133. package/package.json +49 -4
  134. package/src/analyze.ts +119 -0
  135. package/src/ast/node.ts +102 -0
  136. package/src/ast/walk.ts +37 -0
  137. package/src/diagnostics.ts +22 -0
  138. package/src/fs.ts +59 -0
  139. package/src/graph-edges.ts +167 -0
  140. package/src/graph-ids.ts +31 -0
  141. package/src/graph.ts +190 -0
  142. package/src/index.ts +24 -0
  143. package/src/json.ts +34 -0
  144. package/src/module-graph.ts +117 -0
  145. package/src/module-info.ts +162 -0
  146. package/src/node.ts +54 -0
  147. package/src/parser.ts +19 -0
  148. package/src/passes/clients.ts +233 -0
  149. package/src/passes/config.ts +278 -0
  150. package/src/passes/file-facts.ts +142 -0
  151. package/src/passes/i18n.ts +66 -0
  152. package/src/passes/job-relations-diagnostics.ts +60 -0
  153. package/src/passes/job-relations.ts +76 -0
  154. package/src/passes/jobs.ts +181 -0
  155. package/src/passes/jsx.ts +92 -0
  156. package/src/passes/path-expression.ts +297 -0
  157. package/src/passes/route-config.ts +188 -0
  158. package/src/passes/route-description.ts +33 -0
  159. package/src/passes/route-elevation.ts +161 -0
  160. package/src/passes/route-handler.ts +105 -0
  161. package/src/passes/route-location.ts +62 -0
  162. package/src/passes/route-relation-resolve.ts +228 -0
  163. package/src/passes/route-relations-diagnostics.ts +115 -0
  164. package/src/passes/route-relations-follow.ts +146 -0
  165. package/src/passes/route-relations-usage.ts +98 -0
  166. package/src/passes/route-relations-walk.ts +255 -0
  167. package/src/passes/route-relations.ts +62 -0
  168. package/src/passes/route-table.ts +123 -0
  169. package/src/passes/route-tables.ts +50 -0
  170. package/src/passes/routes.ts +188 -0
  171. package/src/passes/schema-column-types.ts +62 -0
  172. package/src/passes/schema-columns.ts +287 -0
  173. package/src/passes/schema-dsl.ts +136 -0
  174. package/src/passes/schema-file-links-ddl.ts +125 -0
  175. package/src/passes/schema-file-links.ts +213 -0
  176. package/src/passes/schema-foreign-keys.ts +121 -0
  177. package/src/passes/schema-policies.ts +270 -0
  178. package/src/passes/schema-re-exports.ts +95 -0
  179. package/src/passes/schema-relations.ts +239 -0
  180. package/src/passes/schema-scope.ts +29 -0
  181. package/src/passes/schema-tables.ts +216 -0
  182. package/src/passes/schema.ts +67 -0
  183. package/src/passes/screen-components.ts +162 -0
  184. package/src/passes/screen-preview.ts +268 -0
  185. package/src/passes/screens.ts +291 -0
  186. package/src/passes/static-read.ts +196 -0
  187. package/src/passes/views.ts +125 -0
  188. package/src/passes/workflow-tables.ts +87 -0
  189. package/src/passes/workflows.ts +213 -0
  190. package/src/path-resolver.ts +146 -0
  191. package/src/revision.ts +25 -0
  192. package/src/source.ts +15 -0
  193. package/src/static-eval.ts +242 -0
  194. package/src/types-graph.ts +161 -0
  195. package/src/types-jobs.ts +40 -0
  196. package/src/types-relations.ts +64 -0
  197. package/src/types-workflows.ts +42 -0
  198. package/src/types.ts +299 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * JSX component resolution for the static page preview: find a component's returned
3
+ * expression and resolve a JSX name to a project file through the module graph.
4
+ *
5
+ * The resolver is deliberately one level deep per call; the preview builder owns the depth,
6
+ * cycle and node budgets.
7
+ */
8
+
9
+ import { asNode, nodeArray, type AstNode } from "../ast/node.ts";
10
+ import type { ModuleGraph } from "../module-graph.ts";
11
+ import { defaultExportDeclaration } from "../module-info.ts";
12
+ import { sourceRef } from "../source.ts";
13
+
14
+ export interface ComponentRef {
15
+ readonly file: string;
16
+ readonly name: string;
17
+ }
18
+
19
+ export function lineRef(
20
+ graph: ModuleGraph,
21
+ file: string,
22
+ node: AstNode,
23
+ ): { file: string; line: number } {
24
+ const source = graph.file(file)?.source;
25
+ return source === undefined
26
+ ? { file, line: 1 }
27
+ : sourceRef(file, source, typeof node.start === "number" ? node.start : 0);
28
+ }
29
+
30
+ function componentDeclaration(program: AstNode, name: string): AstNode | undefined {
31
+ const wanted = name === "default" ? null : name;
32
+ for (const statement of nodeArray(program, "body")) {
33
+ const declaration =
34
+ statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration"
35
+ ? asNode(statement.declaration)
36
+ : statement;
37
+ if (declaration === undefined) continue;
38
+ if (declaration.type === "FunctionDeclaration") {
39
+ const id = asNode(declaration.id);
40
+ if (
41
+ id !== undefined &&
42
+ typeof id.name === "string" &&
43
+ (wanted === null || id.name === wanted)
44
+ ) {
45
+ return declaration;
46
+ }
47
+ }
48
+ if (declaration.type !== "VariableDeclaration") continue;
49
+ for (const item of nodeArray(declaration, "declarations")) {
50
+ const id = asNode(item.id);
51
+ const init = asNode(item.init);
52
+ if (id === undefined || init === undefined) continue;
53
+ if (typeof id.name !== "string" || (wanted !== null && id.name !== wanted)) continue;
54
+ if (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression")
55
+ return init;
56
+ }
57
+ }
58
+ if (name === "default") return defaultExportDeclaration(program);
59
+ return undefined;
60
+ }
61
+
62
+ export function returnedExpression(body: AstNode): AstNode | undefined {
63
+ if (body.type === "BlockStatement") {
64
+ for (const statement of nodeArray(body, "body")) {
65
+ const found = returnedExpression(statement);
66
+ if (found !== undefined) return found;
67
+ }
68
+ return undefined;
69
+ }
70
+ if (body.type === "ReturnStatement") return asNode(body.argument);
71
+ if (body.type === "IfStatement") {
72
+ const consequent = asNode(body.consequent);
73
+ const alternate = asNode(body.alternate);
74
+ return (
75
+ (consequent === undefined ? undefined : returnedExpression(consequent)) ??
76
+ (alternate === undefined ? undefined : returnedExpression(alternate))
77
+ );
78
+ }
79
+ if (body.type === "TryStatement") {
80
+ const block = asNode(body.block);
81
+ const handler = asNode(body.handler);
82
+ const finalizer = asNode(body.finalizer);
83
+ const handlerBody = handler === undefined ? undefined : asNode(handler.body);
84
+ return (
85
+ (block === undefined ? undefined : returnedExpression(block)) ??
86
+ (handlerBody === undefined ? undefined : returnedExpression(handlerBody)) ??
87
+ (finalizer === undefined ? undefined : returnedExpression(finalizer))
88
+ );
89
+ }
90
+ return undefined;
91
+ }
92
+
93
+ export function componentExpression(
94
+ graph: ModuleGraph,
95
+ filePath: string,
96
+ name: string,
97
+ ): AstNode | undefined {
98
+ const file = graph.file(filePath);
99
+ if (file === undefined) return undefined;
100
+ const declaration = componentDeclaration(file.program, name);
101
+ if (declaration === undefined) return undefined;
102
+ if (declaration.type === "ArrowFunctionExpression") {
103
+ const body = asNode(declaration.body);
104
+ return body === undefined
105
+ ? undefined
106
+ : body.type === "BlockStatement"
107
+ ? returnedExpression(body)
108
+ : body;
109
+ }
110
+ if (declaration.type === "FunctionDeclaration" || declaration.type === "FunctionExpression") {
111
+ const body = asNode(declaration.body);
112
+ return body === undefined ? undefined : returnedExpression(body);
113
+ }
114
+ return declaration;
115
+ }
116
+
117
+ export function resolveComponent(
118
+ graph: ModuleGraph,
119
+ originFile: string,
120
+ name: string,
121
+ ): ComponentRef | null {
122
+ const local = name.includes(".") ? name.slice(0, name.indexOf(".")) : name;
123
+ const binding = graph.resolveBinding(originFile, local);
124
+ if (binding === null) return null;
125
+ const file = graph.file(binding.file);
126
+ if (file === undefined) return null;
127
+ return { file: file.path, name: binding.name };
128
+ }
129
+
130
+ export function firstComponentName(graph: ModuleGraph, filePath: string): string | null {
131
+ const file = graph.file(filePath);
132
+ if (file === undefined) return null;
133
+ for (const statement of nodeArray(file.program, "body")) {
134
+ const declaration =
135
+ statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration"
136
+ ? asNode(statement.declaration)
137
+ : statement;
138
+ if (declaration === undefined) continue;
139
+ if (declaration.type === "FunctionDeclaration") {
140
+ const id = asNode(declaration.id);
141
+ if (id !== undefined && typeof id.name === "string") return id.name;
142
+ }
143
+ if (declaration.type !== "VariableDeclaration") continue;
144
+ for (const item of nodeArray(declaration, "declarations")) {
145
+ const id = asNode(item.id);
146
+ const init = asNode(item.init);
147
+ if (id === undefined || init === undefined || typeof id.name !== "string") continue;
148
+ if (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression")
149
+ return id.name;
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+
155
+ export function canExpand(file: string): boolean {
156
+ if (file.startsWith("src/components/ui/")) return false;
157
+ return (
158
+ file.startsWith("src/pages/") ||
159
+ file.startsWith("src/layouts/") ||
160
+ file.startsWith("src/components/")
161
+ );
162
+ }
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Bounded static page structure for the page inspector: JSX sections nested one inside the
3
+ * other, with component identifiers resolved through the module graph.
4
+ *
5
+ * This is not a rendering. It deliberately stops at explicit limits (depth, node count,
6
+ * component cycles) and marks conditional/map branches as dynamic; anything outside the
7
+ * bounded subset is omitted rather than guessed.
8
+ */
9
+
10
+ import { asNode, nodeArray, unwrapExpression, type AstNode } from "../ast/node.ts";
11
+ import type { ModuleGraph } from "../module-graph.ts";
12
+ import type { PagePreview, PagePreviewNode } from "../types-graph.ts";
13
+ import {
14
+ elementName,
15
+ isSectionIntrinsic,
16
+ jsxAttributeValue,
17
+ jsxAttributes,
18
+ jsxChildren,
19
+ jsxElementValue,
20
+ } from "./jsx.ts";
21
+ import {
22
+ canExpand,
23
+ componentExpression,
24
+ firstComponentName,
25
+ lineRef,
26
+ resolveComponent,
27
+ returnedExpression,
28
+ } from "./screen-components.ts";
29
+
30
+ const MAX_NODES = 60;
31
+ const MAX_DEPTH = 4;
32
+
33
+ /** A route element, its source file and the child route that replaces its <Outlet>. */
34
+ export interface PreviewRoot {
35
+ readonly element: AstNode;
36
+ readonly originFile: string;
37
+ readonly outletChild: PreviewRoot | null;
38
+ }
39
+
40
+ export interface PagePreviewInput {
41
+ readonly graph: ModuleGraph;
42
+ readonly pagePath: string;
43
+ /** Route-table chain for this page; null for an unrouted page. */
44
+ readonly root: PreviewRoot | null;
45
+ /** Component name used when the page is not reached through the route table. */
46
+ readonly pageName: string | null;
47
+ readonly routePaths: readonly string[];
48
+ }
49
+
50
+ /** Build the bounded static structure for one page. */
51
+ export function buildPagePreview(input: PagePreviewInput): PagePreview {
52
+ const { graph, pagePath } = input;
53
+ const nodes: PagePreviewNode[] = [];
54
+ const active = new Set<string>();
55
+ let truncated = false;
56
+
57
+ const addNode = (
58
+ parentId: string | null,
59
+ name: string,
60
+ kind: PagePreviewNode["kind"],
61
+ file: string | null,
62
+ source: PagePreviewNode["source"],
63
+ dynamic: boolean,
64
+ ): string | null => {
65
+ if (nodes.length >= MAX_NODES) {
66
+ truncated = true;
67
+ return null;
68
+ }
69
+ const id = "p" + nodes.length;
70
+ nodes.push({ id, parentId, name, kind, file, source, dynamic });
71
+ return id;
72
+ };
73
+
74
+ const appendChildren = (
75
+ node: AstNode,
76
+ originFile: string,
77
+ parentId: string | null,
78
+ depth: number,
79
+ dynamic: boolean,
80
+ outletChild: PreviewRoot | null,
81
+ ): void => {
82
+ for (const child of jsxChildren(node)) {
83
+ if (child.type === "JSXExpressionContainer") {
84
+ appendExpression(
85
+ asNode(child.expression),
86
+ originFile,
87
+ parentId,
88
+ depth,
89
+ dynamic,
90
+ outletChild,
91
+ );
92
+ } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
93
+ appendJsx(child, originFile, parentId, depth, dynamic, outletChild);
94
+ }
95
+ }
96
+ };
97
+
98
+ const appendJsx = (
99
+ node: AstNode,
100
+ originFile: string,
101
+ parentId: string | null,
102
+ depth: number,
103
+ dynamic: boolean,
104
+ outletChild: PreviewRoot | null,
105
+ ): void => {
106
+ if (node.type === "JSXFragment") {
107
+ appendChildren(node, originFile, parentId, depth, dynamic, outletChild);
108
+ return;
109
+ }
110
+ const name = elementName(node);
111
+ if (name === null) return;
112
+ if (name === "Outlet") {
113
+ if (outletChild !== null) appendRoot(outletChild, parentId, depth, dynamic);
114
+ else addNode(parentId, "Outlet", "slot", null, lineRef(graph, originFile, node), dynamic);
115
+ return;
116
+ }
117
+ const intrinsic = name[0] !== undefined && name[0] === name[0].toLowerCase();
118
+ if (intrinsic) {
119
+ if (!isSectionIntrinsic(name)) {
120
+ appendChildren(node, originFile, parentId, depth, dynamic, outletChild);
121
+ return;
122
+ }
123
+ const id = addNode(
124
+ parentId,
125
+ name,
126
+ "intrinsic",
127
+ null,
128
+ lineRef(graph, originFile, node),
129
+ dynamic,
130
+ );
131
+ if (id !== null) appendChildren(node, originFile, id, depth + 1, dynamic, outletChild);
132
+ return;
133
+ }
134
+ const ref = resolveComponent(graph, originFile, name);
135
+ const id = addNode(
136
+ parentId,
137
+ name,
138
+ "component",
139
+ ref?.file ?? null,
140
+ lineRef(graph, originFile, node),
141
+ dynamic,
142
+ );
143
+ if (id === null) return;
144
+ // JSX-valued props are real sections too (AuthShell footer, Page actions, ...).
145
+ for (const attribute of jsxAttributes(node)) {
146
+ if (attribute.type !== "JSXAttribute") continue;
147
+ const value = jsxElementValue(jsxAttributeValue(attribute));
148
+ if (value === undefined) continue;
149
+ const attributeName = asNode(attribute.name);
150
+ const slotName =
151
+ attributeName !== undefined && typeof attributeName.name === "string"
152
+ ? attributeName.name
153
+ : "slot";
154
+ const slotId = addNode(id, slotName, "slot", null, lineRef(graph, originFile, node), dynamic);
155
+ if (slotId !== null) appendJsx(value, originFile, slotId, depth + 1, dynamic, null);
156
+ }
157
+ // The component's own JSX children are its content slot; keep them visible even when the
158
+ // component definition is expanded (its `{children}` expression is not JSX we can splice).
159
+ appendChildren(node, originFile, id, depth + 1, dynamic, null);
160
+ const expand = ref !== null && canExpand(ref.file) && depth < MAX_DEPTH;
161
+ if (!expand) return;
162
+ const key = ref.file + "#" + ref.name;
163
+ const expression = active.has(key) ? undefined : componentExpression(graph, ref.file, ref.name);
164
+ if (expression === undefined) return;
165
+ active.add(key);
166
+ appendExpression(expression, ref.file, id, depth + 1, dynamic, outletChild);
167
+ active.delete(key);
168
+ };
169
+
170
+ const appendExpression = (
171
+ node: AstNode | undefined,
172
+ originFile: string,
173
+ parentId: string | null,
174
+ depth: number,
175
+ dynamic: boolean,
176
+ outletChild: PreviewRoot | null,
177
+ ): void => {
178
+ if (node === undefined) return;
179
+ const expression = unwrapExpression(node) ?? node;
180
+ if (expression.type === "JSXElement" || expression.type === "JSXFragment") {
181
+ appendJsx(expression, originFile, parentId, depth, dynamic, outletChild);
182
+ return;
183
+ }
184
+ if (expression.type === "ConditionalExpression") {
185
+ appendExpression(
186
+ asNode(expression.consequent),
187
+ originFile,
188
+ parentId,
189
+ depth,
190
+ true,
191
+ outletChild,
192
+ );
193
+ appendExpression(
194
+ asNode(expression.alternate),
195
+ originFile,
196
+ parentId,
197
+ depth,
198
+ true,
199
+ outletChild,
200
+ );
201
+ return;
202
+ }
203
+ if (expression.type === "LogicalExpression") {
204
+ appendExpression(asNode(expression.right), originFile, parentId, depth, true, outletChild);
205
+ return;
206
+ }
207
+ if (expression.type === "ArrayExpression") {
208
+ for (const element of nodeArray(expression, "elements")) {
209
+ appendExpression(element, originFile, parentId, depth, dynamic, outletChild);
210
+ }
211
+ return;
212
+ }
213
+ if (expression.type === "CallExpression") {
214
+ const callee = asNode(expression.callee);
215
+ const property =
216
+ callee === undefined || callee.type !== "MemberExpression"
217
+ ? undefined
218
+ : asNode(callee.property);
219
+ if (property !== undefined && typeof property.name === "string" && property.name === "map") {
220
+ const callback = nodeArray(expression, "arguments")[0];
221
+ const body = callback === undefined ? undefined : asNode(callback.body);
222
+ const returned =
223
+ body === undefined
224
+ ? undefined
225
+ : body.type === "BlockStatement"
226
+ ? returnedExpression(body)
227
+ : body;
228
+ appendExpression(returned, originFile, parentId, depth, true, outletChild);
229
+ }
230
+ }
231
+ };
232
+
233
+ const appendRoot = (
234
+ root: PreviewRoot | null,
235
+ parentId: string | null,
236
+ depth: number,
237
+ dynamic: boolean,
238
+ ): void => {
239
+ if (root === null) return;
240
+ appendExpression(root.element, root.originFile, parentId, depth, dynamic, root.outletChild);
241
+ };
242
+
243
+ if (input.root !== null) {
244
+ appendRoot(input.root, null, 0, false);
245
+ } else if (input.pageName !== null) {
246
+ const file = graph.file(pagePath);
247
+ if (file !== undefined) {
248
+ const id = addNode(
249
+ null,
250
+ input.pageName,
251
+ "component",
252
+ pagePath,
253
+ lineRef(graph, pagePath, file.program),
254
+ false,
255
+ );
256
+ const expression =
257
+ id === null ? undefined : componentExpression(graph, pagePath, input.pageName);
258
+ if (id !== null && expression !== undefined)
259
+ appendExpression(expression, pagePath, id, 1, false, null);
260
+ }
261
+ }
262
+
263
+ return { routePaths: input.routePaths, nodes, truncated };
264
+ }
265
+
266
+ export function pageComponentName(graph: ModuleGraph, pagePath: string): string | null {
267
+ return firstComponentName(graph, pagePath);
268
+ }