@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.
Files changed (190) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +250 -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/behavior.js +75 -0
  84. package/dist/compiler-react/detect.js +558 -0
  85. package/dist/compiler-react/emit.js +188 -0
  86. package/dist/compiler-react/index.js +2 -0
  87. package/dist/compiler-react/props.js +157 -0
  88. package/dist/compiler-react/styling.js +138 -0
  89. package/dist/compiler-react/usage.js +116 -0
  90. package/dist/compiler-react/variants.js +111 -0
  91. package/dist/compiler-storybook/adapter.js +253 -0
  92. package/dist/compiler-storybook/csf.js +521 -0
  93. package/dist/compiler-storybook/emit.js +202 -0
  94. package/dist/compiler-storybook/index-json.js +41 -0
  95. package/dist/compiler-storybook/index.js +5 -0
  96. package/dist/compiler-storybook/mdx.js +120 -0
  97. package/dist/compiler-tokens/adapter.js +158 -0
  98. package/dist/compiler-tokens/css.js +59 -0
  99. package/dist/compiler-tokens/index.js +5 -0
  100. package/dist/compiler-tokens/json-lines.js +106 -0
  101. package/dist/compiler-tokens/json-tokens.js +52 -0
  102. package/dist/compiler-vue/adapter.js +107 -0
  103. package/dist/compiler-vue/analyze.js +344 -0
  104. package/dist/compiler-vue/declared.js +343 -0
  105. package/dist/compiler-vue/emit.js +129 -0
  106. package/dist/compiler-vue/index.js +5 -0
  107. package/dist/compiler-vue/script.js +472 -0
  108. package/dist/compiler-vue/sfc.js +44 -0
  109. package/dist/compiler-vue/template.js +73 -0
  110. package/dist/contract/archetype-checks.js +153 -0
  111. package/dist/contract/components.js +102 -0
  112. package/dist/contract/conflicts.js +39 -0
  113. package/dist/contract/contract.js +46 -0
  114. package/dist/contract/evidence.js +84 -0
  115. package/dist/contract/fact-path.js +39 -0
  116. package/dist/contract/facts.js +62 -0
  117. package/dist/contract/index.js +16 -0
  118. package/dist/contract/integrity.js +21 -0
  119. package/dist/contract/json-schema.js +11 -0
  120. package/dist/contract/knowledge.js +95 -0
  121. package/dist/contract/pattern-checks.js +254 -0
  122. package/dist/contract/patterns.js +225 -0
  123. package/dist/contract/primitives.js +81 -0
  124. package/dist/contract/sources.js +30 -0
  125. package/dist/contract/tokens.js +41 -0
  126. package/dist/contract/validate.js +253 -0
  127. package/dist/contract-query/archetype.js +188 -0
  128. package/dist/contract-query/index.js +6 -0
  129. package/dist/contract-query/patterns.js +298 -0
  130. package/dist/contract-query/search.js +132 -0
  131. package/dist/contract-query/system.js +148 -0
  132. package/dist/contract-query/views.js +288 -0
  133. package/dist/governance/decisions.js +229 -0
  134. package/dist/governance/index.js +5 -0
  135. package/dist/governance/patterns-file.js +156 -0
  136. package/dist/governance/patterns.js +510 -0
  137. package/dist/governance/policies.js +148 -0
  138. package/dist/mcp/index.js +7 -0
  139. package/dist/mcp/loader.js +70 -0
  140. package/dist/mcp/protocol.js +27 -0
  141. package/dist/mcp/resources.js +138 -0
  142. package/dist/mcp/server.js +177 -0
  143. package/dist/mcp/stdio.js +28 -0
  144. package/dist/mcp/tools.js +267 -0
  145. package/dist/reconciliation/code.js +62 -0
  146. package/dist/reconciliation/components.js +401 -0
  147. package/dist/reconciliation/conflicts.js +126 -0
  148. package/dist/reconciliation/facts.js +41 -0
  149. package/dist/reconciliation/index.js +7 -0
  150. package/dist/reconciliation/reconcile.js +134 -0
  151. package/dist/reconciliation/resolve.js +158 -0
  152. package/dist/reconciliation/review.js +81 -0
  153. package/dist/reconciliation/storybook.js +370 -0
  154. package/dist/reconciliation/tokens.js +179 -0
  155. package/dist/reconciliation/util.js +55 -0
  156. package/dist/semantic-engine/additive.js +51 -0
  157. package/dist/semantic-engine/compounds.js +91 -0
  158. package/dist/semantic-engine/documents.js +157 -0
  159. package/dist/semantic-engine/enrich.js +42 -0
  160. package/dist/semantic-engine/index.js +7 -0
  161. package/dist/semantic-engine/infer.js +38 -0
  162. package/dist/semantic-engine/tokens.js +120 -0
  163. package/dist/shared/canonical.js +65 -0
  164. package/dist/shared/diagnostic.js +78 -0
  165. package/dist/shared/glob.js +98 -0
  166. package/dist/shared/index.js +4 -0
  167. package/dist/shared/json.js +2 -0
  168. package/dist/validation/card.js +50 -0
  169. package/dist/validation/discovery/arrangements.js +162 -0
  170. package/dist/validation/discovery/discover.js +70 -0
  171. package/dist/validation/discovery/index.js +8 -0
  172. package/dist/validation/discovery/model.js +127 -0
  173. package/dist/validation/discovery/promote.js +24 -0
  174. package/dist/validation/discovery/reviews.js +63 -0
  175. package/dist/validation/discovery/shapes.js +47 -0
  176. package/dist/validation/discovery/similar-files.js +146 -0
  177. package/dist/validation/discovery/util.js +47 -0
  178. package/dist/validation/index.js +11 -0
  179. package/dist/validation/interaction-facts.js +341 -0
  180. package/dist/validation/interaction-workflows.js +633 -0
  181. package/dist/validation/literals.js +251 -0
  182. package/dist/validation/pattern-rules.js +608 -0
  183. package/dist/validation/policy.js +241 -0
  184. package/dist/validation/report.js +163 -0
  185. package/dist/validation/rules.js +269 -0
  186. package/dist/validation/scan.js +295 -0
  187. package/dist/validation/text.js +34 -0
  188. package/dist/validation/validate-code.js +513 -0
  189. package/dist/validation/validate-usage.js +196 -0
  190. package/package.json +38 -4
@@ -0,0 +1,270 @@
1
+ import ts from "typescript";
2
+ import { listExports } from "./exports.js";
3
+ import { isRecord } from "./packages.js";
4
+ import { cmp, fileExists, posix, readBounded } from "./support.js";
5
+ const CONDITION_ORDER = ["types", "import", "module", "default", "require", "node", "browser"];
6
+ const CONVENTIONAL_ROOT_ENTRIES = [
7
+ "src/index.ts",
8
+ "src/index.tsx",
9
+ "src/public-api.ts",
10
+ "src/public_api.ts",
11
+ "public-api.ts",
12
+ "index.ts",
13
+ "index.tsx"
14
+ ];
15
+ /** Package entry points from `exports` (or main/module/types), resolved to a readable file and enumerated. */
16
+ export async function discoverEntryPoints(env, raw) {
17
+ const dir = raw.info.dir;
18
+ const tsconfig = await readOutRoot(env, dir);
19
+ const declared = declaredEntries(env, raw, tsconfig);
20
+ // ng-packagr libraries declare their entry in `ng-package.json` (or default to a `public-api` file),
21
+ // not in package.json: the source manifest has no entry fields at all.
22
+ if (declared.length === 0)
23
+ declared.push(...(await angularLibraryEntry(env, dir)));
24
+ declared.push(...(await angularSecondaryEntries(env, dir)));
25
+ // Resolve every entry first: an entry may re-export another one through the package's own name.
26
+ const resolvedEntries = [];
27
+ for (const d of declared)
28
+ resolvedEntries.push({
29
+ declared: d,
30
+ resolved: await resolveTarget(env, { dir, subpath: d.subpath, target: d.target, tsconfig })
31
+ });
32
+ const self = new Map();
33
+ const name = raw.info.name;
34
+ if (name)
35
+ for (const { declared: d, resolved } of resolvedEntries)
36
+ if (resolved && !self.has(selfSpecifier(name, d.subpath)))
37
+ self.set(selfSpecifier(name, d.subpath), resolved.file);
38
+ const entries = [];
39
+ for (const { declared: d, resolved } of resolvedEntries) {
40
+ const { subpath, condition, target } = d;
41
+ if (!resolved) {
42
+ env.diagnostics.warning("discovery.entry-unresolved", `Entry ${subpath} (${target}) in ${raw.info.id} could not be resolved to a file`, {
43
+ location: { path: raw.info.manifest.path }
44
+ });
45
+ entries.push({
46
+ subpath,
47
+ condition,
48
+ target,
49
+ resolution: "unresolved",
50
+ exports: [],
51
+ exportsComplete: false,
52
+ parsedFiles: []
53
+ });
54
+ continue;
55
+ }
56
+ const listing = await listExports(env, resolved.file, self);
57
+ entries.push({
58
+ subpath,
59
+ condition,
60
+ target,
61
+ resolution: resolved.resolution,
62
+ file: resolved.file,
63
+ exports: listing.exports,
64
+ exportsComplete: listing.complete,
65
+ parsedFiles: listing.parsedFiles
66
+ });
67
+ }
68
+ return entries.sort((a, b) => cmp(a.subpath, b.subpath));
69
+ }
70
+ /** `primevue` for the root entry, `primevue/accordion` for `./accordion`: how the package imports itself. */
71
+ function selfSpecifier(name, subpath) {
72
+ return subpath === "." ? name : `${name}/${subpath.replace(/^\.\//, "")}`;
73
+ }
74
+ function declaredEntries(env, raw, tsconfig) {
75
+ const out = [];
76
+ const manifest = raw.manifest;
77
+ const add = (subpath, condition, target) => {
78
+ // `"./*": "./*"` maps every file of the package to itself: it is not a list of what the package offers, and expanding
79
+ // it makes each component appear once per build folder (dist/esm, dist/cjs, src) as separate, "ambiguous" copies.
80
+ if (subpath.includes("*") && posix.normalize(subpath) === posix.normalize(target)) {
81
+ env.diagnostics.info("discovery.entry-pattern-identity", `Entry pattern ${subpath} in ${raw.info.id} maps files to themselves and is not expanded`, { location: { path: raw.info.manifest.path } });
82
+ return;
83
+ }
84
+ if (subpath.includes("*") || target.includes("*")) {
85
+ const stars = (t) => t.split("*").length - 1;
86
+ if (stars(subpath) !== 1 || stars(target) !== 1) {
87
+ env.diagnostics.info("discovery.entry-pattern-unsupported", `Entry pattern ${subpath} in ${raw.info.id} is not expanded`, { location: { path: raw.info.manifest.path } });
88
+ return;
89
+ }
90
+ const matches = expandPattern(env, { dir: raw.info.dir, target, tsconfig });
91
+ if (matches.length === 0)
92
+ env.diagnostics.info("discovery.entry-pattern-unmatched", `Entry pattern ${subpath} (${target}) in ${raw.info.id} matches no file`, { location: { path: raw.info.manifest.path } });
93
+ for (const match of matches)
94
+ out.push({ subpath: subpath.replace("*", match), condition, target: target.replace("*", match) });
95
+ return;
96
+ }
97
+ out.push({ subpath, condition, target });
98
+ };
99
+ const walkConditions = (subpath, node, trail) => {
100
+ if (typeof node === "string")
101
+ add(subpath, trail || "default", node);
102
+ else if (Array.isArray(node))
103
+ for (const item of node)
104
+ walkConditions(subpath, item, trail);
105
+ else if (isRecord(node)) {
106
+ const keys = Object.keys(node).sort((a, b) => rank(a) - rank(b));
107
+ for (const key of keys)
108
+ walkConditions(subpath, node[key], trail ? `${trail}.${key}` : key);
109
+ }
110
+ };
111
+ const exportsField = manifest.exports;
112
+ if (typeof exportsField === "string" || Array.isArray(exportsField))
113
+ walkConditions(".", exportsField, "");
114
+ else if (isRecord(exportsField)) {
115
+ const subpathKeys = Object.keys(exportsField).filter((k) => k.startsWith("."));
116
+ if (subpathKeys.length > 0)
117
+ for (const key of subpathKeys)
118
+ walkConditions(key, exportsField[key], "");
119
+ else
120
+ walkConditions(".", exportsField, "");
121
+ }
122
+ else {
123
+ for (const field of ["types", "typings", "module", "main"]) {
124
+ const target = manifest[field];
125
+ if (typeof target === "string") {
126
+ add(".", field, target);
127
+ break;
128
+ }
129
+ }
130
+ }
131
+ // One entry per subpath: the highest-priority condition that resolves is chosen later; keep the
132
+ // first (best-ranked) declaration per subpath to stay deterministic and bounded.
133
+ const seen = new Set();
134
+ return out.filter((e) => {
135
+ if (seen.has(e.subpath))
136
+ return false;
137
+ seen.add(e.subpath);
138
+ return true;
139
+ });
140
+ }
141
+ /**
142
+ * What `*` stands for in a target pattern such as `./dist/*\/index.d.ts`, for every file that fits it. A target in
143
+ * build output is also matched against the sources it was built from (`src/*.ts`), as `resolveTarget` does.
144
+ */
145
+ function expandPattern(env, input) {
146
+ const { dir, target, tsconfig } = input;
147
+ const declaredPath = posix.normalize(posix.join(dir, target));
148
+ const regexes = [globRegExp(declaredPath, "")];
149
+ if (tsconfig.outDir !== undefined) {
150
+ const out = posix.normalize(posix.join(dir, tsconfig.outDir));
151
+ if (declaredPath.startsWith(`${out}/`)) {
152
+ const rootDir = posix.normalize(posix.join(dir, tsconfig.rootDir ?? "src"));
153
+ const rel = declaredPath.slice(out.length + 1).replace(/\.d\.ts$|\.(?:[cm]?js|jsx)$/, "");
154
+ regexes.push(globRegExp(posix.join(rootDir, rel), String.raw `\.(?:[cm]?tsx?)`));
155
+ }
156
+ }
157
+ const found = new Set();
158
+ for (const file of env.files)
159
+ for (const [i, re] of regexes.entries()) {
160
+ // Declaration files are not the sources a build output was made from.
161
+ if (i > 0 && file.endsWith(".d.ts"))
162
+ continue;
163
+ const captured = re.exec(file)?.[1];
164
+ // `*` never stands for an empty string or for a dependency's files.
165
+ if (captured && !captured.split("/").includes("node_modules"))
166
+ found.add(captured);
167
+ }
168
+ return [...found].sort(cmp);
169
+ }
170
+ function globRegExp(pattern, suffix) {
171
+ const [head = "", tail = ""] = pattern.split("*");
172
+ const quote = (t) => t.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
173
+ return new RegExp(`^${quote(head)}(.+)${quote(tail)}${suffix}$`);
174
+ }
175
+ function rank(condition) {
176
+ const i = CONDITION_ORDER.indexOf(condition);
177
+ return i < 0 ? CONDITION_ORDER.length : i;
178
+ }
179
+ async function resolveTarget(env, input) {
180
+ const { dir, subpath, target, tsconfig } = input;
181
+ const declaredPath = posix.normalize(posix.join(dir, target));
182
+ const isSource = /\.(?:tsx?|mts|cts)$/.test(declaredPath) && !declaredPath.endsWith(".d.ts");
183
+ if (isSource && (await fileExists(env, declaredPath)))
184
+ return { file: declaredPath, resolution: "declared" };
185
+ if (tsconfig.outDir !== undefined) {
186
+ const out = posix.normalize(posix.join(dir, tsconfig.outDir));
187
+ if (declaredPath.startsWith(`${out}/`)) {
188
+ const rootDir = posix.normalize(posix.join(dir, tsconfig.rootDir ?? "src"));
189
+ const rel = declaredPath.slice(out.length + 1).replace(/\.d\.ts$|\.(?:[cm]?js|jsx)$/, "");
190
+ for (const ext of [".ts", ".tsx", ".mts", ".cts"]) {
191
+ const candidate = posix.join(rootDir, rel + ext);
192
+ if (await fileExists(env, candidate))
193
+ return { file: candidate, resolution: "mapped" };
194
+ }
195
+ }
196
+ }
197
+ if (await fileExists(env, declaredPath))
198
+ return { file: declaredPath, resolution: "declared" };
199
+ if (subpath === ".") {
200
+ for (const conventional of CONVENTIONAL_ROOT_ENTRIES) {
201
+ const candidate = posix.join(dir, conventional);
202
+ if (await fileExists(env, candidate))
203
+ return { file: candidate, resolution: "conventional" };
204
+ }
205
+ }
206
+ return undefined;
207
+ }
208
+ /** `outDir` / `rootDir` from the package's own tsconfig.json (no `extends` resolution). */
209
+ async function readOutRoot(env, dir) {
210
+ const file = posix.join(dir, "tsconfig.json");
211
+ if (!env.fileSet.has(file))
212
+ return {};
213
+ const text = await readBounded(env, file);
214
+ if (text === undefined)
215
+ return {};
216
+ const parsed = ts.parseConfigFileTextToJson(file, text);
217
+ const options = isRecord(parsed.config) && isRecord(parsed.config.compilerOptions) ? parsed.config.compilerOptions : {};
218
+ return {
219
+ ...(typeof options.outDir === "string" ? { outDir: options.outDir } : {}),
220
+ ...(typeof options.rootDir === "string" ? { rootDir: options.rootDir } : {})
221
+ };
222
+ }
223
+ const PUBLIC_API_FILES = ["src/public-api.ts", "src/public_api.ts", "public-api.ts", "public_api.ts"];
224
+ async function angularLibraryEntry(env, dir) {
225
+ const ngPackage = posix.join(dir, "ng-package.json");
226
+ if (env.fileSet.has(ngPackage)) {
227
+ const text = await readBounded(env, ngPackage);
228
+ if (text !== undefined) {
229
+ const parsed = ts.parseConfigFileTextToJson(ngPackage, text);
230
+ const lib = isRecord(parsed.config) && isRecord(parsed.config.lib) ? parsed.config.lib : undefined;
231
+ if (typeof lib?.entryFile === "string")
232
+ return [{ subpath: ".", condition: "ng-package", target: lib.entryFile }];
233
+ }
234
+ }
235
+ for (const candidate of PUBLIC_API_FILES) {
236
+ if (env.fileSet.has(posix.join(dir, candidate)))
237
+ return [{ subpath: ".", condition: "conventional", target: candidate }];
238
+ }
239
+ return [];
240
+ }
241
+ /**
242
+ * ng-packagr secondary entry points: subfolders with their own `ng-package.json`. Consumers import them
243
+ * as `<package>/<folder>`; the entry file defaults to a `public-api` file in the folder.
244
+ */
245
+ async function angularSecondaryEntries(env, dir) {
246
+ if (!env.fileSet.has(posix.join(dir, "ng-package.json")))
247
+ return [];
248
+ const out = [];
249
+ for (const file of env.files) {
250
+ if (posix.basename(file) !== "ng-package.json" || !file.startsWith(dir === "." ? "" : `${dir}/`))
251
+ continue;
252
+ const folder = posix.dirname(file);
253
+ if (folder === dir)
254
+ continue;
255
+ const relative = posix.relative(dir, folder);
256
+ // A folder with its own package.json is a separate package, not a secondary entry point.
257
+ if (env.fileSet.has(posix.join(folder, "package.json")))
258
+ continue;
259
+ const text = await readBounded(env, file);
260
+ const parsed = text === undefined ? undefined : ts.parseConfigFileTextToJson(file, text);
261
+ const lib = parsed && isRecord(parsed.config) && isRecord(parsed.config.lib) ? parsed.config.lib : undefined;
262
+ const entry = typeof lib?.entryFile === "string"
263
+ ? lib.entryFile
264
+ : PUBLIC_API_FILES.map((f) => posix.basename(f)).find((f) => env.fileSet.has(posix.join(folder, f)));
265
+ if (entry)
266
+ out.push({ subpath: `./${relative}`, condition: "ng-package", target: posix.join(relative, entry) });
267
+ }
268
+ return out;
269
+ }
270
+ //# sourceMappingURL=entries.js.map
@@ -0,0 +1,207 @@
1
+ import ts from "typescript";
2
+ import { cmp, fileExists, hashText, posix, readBounded } from "./support.js";
3
+ const SOURCE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".d.ts", ".js", ".jsx", ".mjs", ".cjs", ".vue"];
4
+ const PARSEABLE = /\.(?:[cm]?[jt]sx?)$/;
5
+ /**
6
+ * Enumerates the names a module exports, syntactically (no type checking, no execution). Follows
7
+ * relative `export *` and `export {…} from` chains. Whether an export is a component is NOT decided here.
8
+ */
9
+ export async function listExports(env, entryFile,
10
+ /** The package's own entry points by import specifier (`primevue/accordion`) to file: followed like relative imports. */
11
+ self = new Map()) {
12
+ const state = {
13
+ env,
14
+ self,
15
+ byName: new Map(),
16
+ parsed: new Map(),
17
+ complete: true,
18
+ visiting: new Set()
19
+ };
20
+ await visitModule(state, entryFile);
21
+ return {
22
+ exports: [...state.byName.values()].sort((a, b) => cmp(a.name, b.name)),
23
+ complete: state.complete,
24
+ parsedFiles: [...state.parsed]
25
+ .map(([path, contentHash]) => ({ path, contentHash }))
26
+ .sort((a, b) => cmp(a.path, b.path))
27
+ };
28
+ }
29
+ async function visitModule(state, file) {
30
+ if (state.visiting.has(file))
31
+ return;
32
+ if (state.parsed.size >= state.env.limits.maxParsedFilesPerEntry) {
33
+ state.complete = false;
34
+ state.env.diagnostics.warning("discovery.export-limit", `Stopped following re-exports after ${state.env.limits.maxParsedFilesPerEntry} files`, { location: { path: file } });
35
+ return;
36
+ }
37
+ state.visiting.add(file);
38
+ const text = await readBounded(state.env, file);
39
+ if (text === undefined) {
40
+ state.complete = false;
41
+ return;
42
+ }
43
+ state.parsed.set(file, hashText(text));
44
+ if (!PARSEABLE.test(file)) {
45
+ // Assets (css, json, .vue) have no enumerable JS exports; the caller must not read "none" as "complete".
46
+ if (state.visiting.size === 1)
47
+ state.complete = false;
48
+ return;
49
+ }
50
+ const sf = ts.createSourceFile(file, text, ts.ScriptTarget.ES2022, false, scriptKind(file));
51
+ const parseErrors = sf.parseDiagnostics ?? [];
52
+ if (parseErrors.length > 0) {
53
+ state.complete = false;
54
+ state.env.diagnostics.warning("discovery.parse-error", `Syntax errors in ${file}; exports may be incomplete`, { location: { path: file } });
55
+ }
56
+ const locals = localDeclarationKinds(sf);
57
+ const stars = [];
58
+ const record = (info, node) => {
59
+ // Explicit exports (collected first, in this file) win over star re-exports; first definition wins.
60
+ if (state.byName.has(info.name))
61
+ return;
62
+ state.byName.set(info.name, {
63
+ ...info,
64
+ file,
65
+ line: sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1
66
+ });
67
+ };
68
+ for (const statement of sf.statements) {
69
+ if (ts.isExportDeclaration(statement)) {
70
+ const spec = statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)
71
+ ? statement.moduleSpecifier.text
72
+ : undefined;
73
+ const external = spec !== undefined && !spec.startsWith(".") && !state.self.has(spec);
74
+ const clause = statement.exportClause;
75
+ if (!clause) {
76
+ if (spec)
77
+ stars.push(spec);
78
+ }
79
+ else if (ts.isNamespaceExport(clause)) {
80
+ record({
81
+ name: clause.name.text,
82
+ kind: "namespace",
83
+ typeOnly: statement.isTypeOnly,
84
+ ...(spec ? { reexportOf: { module: spec, external } } : {})
85
+ }, clause);
86
+ }
87
+ else {
88
+ for (const element of clause.elements) {
89
+ const typeOnly = statement.isTypeOnly || element.isTypeOnly;
90
+ const original = (element.propertyName ?? element.name).text;
91
+ const kind = spec
92
+ ? original === "default"
93
+ ? "default"
94
+ : external
95
+ ? "unknown"
96
+ : await reexportedKind(state, file, spec, original)
97
+ : (locals.get(original) ?? "unknown");
98
+ record({
99
+ name: element.name.text,
100
+ kind: typeOnly && kind === "unknown" ? "type" : kind,
101
+ typeOnly,
102
+ ...(spec ? { reexportOf: { module: spec, external } } : {})
103
+ }, element);
104
+ }
105
+ }
106
+ }
107
+ else if (ts.isExportAssignment(statement)) {
108
+ if (statement.isExportEquals) {
109
+ state.complete = false;
110
+ state.env.diagnostics.info("discovery.unsupported-export-assignment", `'export =' in ${file} is not enumerated`, { location: { path: file } });
111
+ }
112
+ else
113
+ record({ name: "default", kind: "default", typeOnly: false }, statement);
114
+ }
115
+ else if (hasExportModifier(statement)) {
116
+ for (const [name, kind] of declaredNames(statement))
117
+ record({ name, kind, typeOnly: kind === "type" }, statement);
118
+ }
119
+ }
120
+ for (const spec of stars) {
121
+ if (!spec.startsWith(".") && !state.self.has(spec)) {
122
+ state.complete = false;
123
+ state.env.diagnostics.info("discovery.external-reexport", `'export * from "${spec}"' in ${file} is not followed`, { location: { path: file } });
124
+ continue;
125
+ }
126
+ const target = await resolveSpecifier(state, file, spec);
127
+ if (!target) {
128
+ state.complete = false;
129
+ state.env.diagnostics.warning("discovery.unresolved-module", `Could not resolve "${spec}" from ${file}`, { location: { path: file } });
130
+ continue;
131
+ }
132
+ await visitModule(state, target);
133
+ }
134
+ }
135
+ /** A relative path, or one of the package's own entry points imported by its name. */
136
+ async function resolveSpecifier(state, from, spec) {
137
+ return spec.startsWith(".") ? resolveModule(state.env, from, spec) : state.self.get(spec);
138
+ }
139
+ async function resolveModule(env, from, spec) {
140
+ const base = posix.normalize(posix.join(posix.dirname(from), spec));
141
+ const stripped = base.replace(/\.(?:[cm]?jsx?)$/, "");
142
+ const candidates = [
143
+ ...(/\.[a-z]+$/i.test(base) && base === stripped ? [base] : []),
144
+ ...(stripped !== base ? [".ts", ".tsx", ".mts", ".cts", ".d.ts"].map((e) => stripped + e) : []),
145
+ ...(stripped !== base ? [base] : []),
146
+ ...SOURCE_EXTS.map((e) => base + e),
147
+ ...[".ts", ".tsx", ".d.ts", ".js", ".jsx"].map((e) => `${base}/index${e}`)
148
+ ];
149
+ for (const candidate of candidates) {
150
+ if (await fileExists(env, candidate))
151
+ return candidate;
152
+ }
153
+ return undefined;
154
+ }
155
+ /** Looks one level into a relative module to learn whether a re-exported name is a value, type or namespace. */
156
+ async function reexportedKind(state, from, spec, name) {
157
+ const target = await resolveSpecifier(state, from, spec);
158
+ if (!target || !PARSEABLE.test(target))
159
+ return "unknown";
160
+ const text = await readBounded(state.env, target);
161
+ if (text === undefined)
162
+ return "unknown";
163
+ state.parsed.set(target, hashText(text));
164
+ const sf = ts.createSourceFile(target, text, ts.ScriptTarget.ES2022, false, scriptKind(target));
165
+ return localDeclarationKinds(sf).get(name) ?? "unknown";
166
+ }
167
+ function scriptKind(file) {
168
+ if (file.endsWith(".tsx"))
169
+ return ts.ScriptKind.TSX;
170
+ if (file.endsWith(".jsx"))
171
+ return ts.ScriptKind.JSX;
172
+ if (/\.[cm]?js$/.test(file))
173
+ return ts.ScriptKind.JS;
174
+ return ts.ScriptKind.TS;
175
+ }
176
+ function localDeclarationKinds(sf) {
177
+ const kinds = new Map();
178
+ for (const statement of sf.statements) {
179
+ for (const [name, kind] of declaredNames(statement))
180
+ kinds.set(name, kind);
181
+ }
182
+ return kinds;
183
+ }
184
+ function declaredNames(statement) {
185
+ const isDefault = ts.canHaveModifiers(statement) &&
186
+ !!ts.getModifiers(statement)?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword);
187
+ if (ts.isVariableStatement(statement)) {
188
+ return statement.declarationList.declarations.flatMap((d) => ts.isIdentifier(d.name) ? [[d.name.text, "value"]] : []);
189
+ }
190
+ if (ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) {
191
+ if (isDefault)
192
+ return [["default", "default"]];
193
+ return statement.name ? [[statement.name.text, "value"]] : [];
194
+ }
195
+ if (ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement))
196
+ return [[statement.name.text, "type"]];
197
+ if (ts.isEnumDeclaration(statement))
198
+ return [[statement.name.text, "value"]];
199
+ if (ts.isModuleDeclaration(statement) && ts.isIdentifier(statement.name))
200
+ return [[statement.name.text, "namespace"]];
201
+ return [];
202
+ }
203
+ function hasExportModifier(statement) {
204
+ return (ts.canHaveModifiers(statement) &&
205
+ !!ts.getModifiers(statement)?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword));
206
+ }
207
+ //# sourceMappingURL=exports.js.map
@@ -0,0 +1,59 @@
1
+ import { cmp, owningPackage } from "./support.js";
2
+ /** Source relationship graph: sorted nodes and edges, derived only from discovered facts. */
3
+ export function buildGraph(input) {
4
+ const nodes = new Map();
5
+ const edges = new Map();
6
+ const node = (id, type, label) => {
7
+ nodes.set(id, { id, type, label });
8
+ };
9
+ const edge = (from, to, type, weight) => {
10
+ edges.set(`${from}\u0000${type}\u0000${to}`, { from, to, type, ...(weight ? { weight } : {}) });
11
+ };
12
+ const byName = new Map(input.packages.flatMap((p) => (p.name ? [[p.name, p]] : [])));
13
+ for (const pkg of input.packages) {
14
+ node(pkg.id, "package", pkg.name ?? pkg.dir);
15
+ for (const dep of pkg.dependencies) {
16
+ const target = byName.get(dep.name);
17
+ if (target && target.id !== pkg.id)
18
+ edge(pkg.id, target.id, "depends-on");
19
+ }
20
+ for (const fw of pkg.frameworks) {
21
+ node(`framework:${fw.name}`, "framework", fw.name);
22
+ edge(pkg.id, `framework:${fw.name}`, "uses-framework");
23
+ }
24
+ for (const tool of pkg.buildTools) {
25
+ node(`tool:${tool.name}`, "build-tool", tool.name);
26
+ edge(pkg.id, `tool:${tool.name}`, "built-with");
27
+ }
28
+ }
29
+ for (const storybook of input.storybooks) {
30
+ node(storybook.id, "storybook-config", storybook.configPath);
31
+ if (storybook.package)
32
+ edge(storybook.package, storybook.id, "configured-by");
33
+ const counts = new Map();
34
+ for (const file of storybook.storyFiles) {
35
+ const owner = owningPackage(input.packages, file);
36
+ if (owner)
37
+ counts.set(owner.id, (counts.get(owner.id) ?? 0) + 1);
38
+ }
39
+ for (const [pkgId, count] of counts)
40
+ edge(storybook.id, pkgId, "documents", count);
41
+ }
42
+ for (const file of input.tokenFiles) {
43
+ node(`tokens:${file.path}`, "token-file", file.path);
44
+ const owner = owningPackage(input.packages, file.path);
45
+ if (owner)
46
+ edge(owner.id, `tokens:${file.path}`, "contains");
47
+ }
48
+ for (const file of input.docFiles) {
49
+ node(`doc:${file.path}`, "doc-file", file.path);
50
+ const owner = owningPackage(input.packages, file.path);
51
+ if (owner)
52
+ edge(owner.id, `doc:${file.path}`, "contains");
53
+ }
54
+ return {
55
+ nodes: [...nodes.values()].sort((a, b) => cmp(a.id, b.id)),
56
+ edges: [...edges.values()].sort((a, b) => cmp(a.from, b.from) || cmp(a.type, b.type) || cmp(a.to, b.to))
57
+ };
58
+ }
59
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1,103 @@
1
+ import { DiagnosticCollector } from "../../shared/index.js";
2
+ import { DEFAULT_IGNORED_DIRS, walk } from "../walk.js";
3
+ import { discoverDocFiles } from "./docs.js";
4
+ import { discoverEntryPoints } from "./entries.js";
5
+ import { buildGraph } from "./graph.js";
6
+ import { discoverPackages } from "./packages.js";
7
+ import { DISCOVERY_VERSION, projectDiscoverySchema } from "./schema.js";
8
+ import { discoverStorybooks } from "./storybook.js";
9
+ import { DEFAULT_LIMITS, owningPackage } from "./support.js";
10
+ import { discoverTokenFiles } from "./tokens.js";
11
+ import { detectBuildTools, detectFrameworks, detectPackageManager } from "./tooling.js";
12
+ /** Build output folders: ignored in a project, but the content of a published package. */
13
+ const FRAMEWORK_PACKAGE_NAME = { react: "react", vue: "vue", angular: "@angular/core" };
14
+ const BUILD_OUTPUT_DIRS = ["dist", "build", "out"];
15
+ /**
16
+ * Framework-neutral project discovery: packages, workspaces, package manager, frameworks and
17
+ * versions, build tools, entry points and exports, Storybook configs, token files and CSS
18
+ * variables, documentation files, and the relationship graph. Read-only and deterministic.
19
+ */
20
+ export async function discoverProject(options) {
21
+ const limits = { ...DEFAULT_LIMITS, ...options.limits };
22
+ const diagnostics = new DiagnosticCollector();
23
+ const ignore = new Set([...DEFAULT_IGNORED_DIRS, ...(options.ignoreDirs ?? [])]);
24
+ const walked = await walk(options.fs, { ignoreDirs: ignore, maxFiles: limits.maxFiles });
25
+ if (walked.truncated) {
26
+ diagnostics.warning("discovery.walk-truncated", `Stopped after ${limits.maxFiles} files; discovery is incomplete`);
27
+ }
28
+ const roots = new Set((options.declarationRoots ?? []).map((r) => r.replace(/^\.\//, "").replace(/\/+$/, "")));
29
+ const files = new Set(walked.files);
30
+ const packageIgnore = new Set([...ignore].filter((d) => !BUILD_OUTPUT_DIRS.includes(d)));
31
+ for (const root of [...roots].sort()) {
32
+ try {
33
+ await options.fs.readDir(root);
34
+ }
35
+ catch {
36
+ diagnostics.warning("discovery.declaration-root-missing", `${root} was not found; is the package installed?`);
37
+ continue;
38
+ }
39
+ const inside = await walk(options.fs, {
40
+ ignoreDirs: packageIgnore,
41
+ maxFiles: limits.maxFiles,
42
+ start: root
43
+ });
44
+ if (inside.truncated)
45
+ diagnostics.warning("discovery.walk-truncated", `Stopped reading ${root} after ${limits.maxFiles} files`);
46
+ for (const file of inside.files)
47
+ files.add(file);
48
+ }
49
+ const allFiles = [...files].sort();
50
+ const env = {
51
+ fs: options.fs,
52
+ files: allFiles,
53
+ fileSet: files,
54
+ declarationRoots: roots,
55
+ ignoreDirs: ignore,
56
+ diagnostics,
57
+ limits
58
+ };
59
+ const { packages: raws, workspaces } = await discoverPackages(env);
60
+ const root = raws.find((p) => p.info.dir === ".");
61
+ const packageManager = await detectPackageManager(env, root);
62
+ for (const raw of raws) {
63
+ raw.info.frameworks = await detectFrameworks(env, raw);
64
+ const configured = options.declaredFrameworks?.[raw.info.dir];
65
+ // shortcut: a configured framework is recorded as a `*` peer dependency; upgrade if discovery needs to tell them apart.
66
+ if (configured && raw.info.frameworks.length === 0)
67
+ raw.info.frameworks = [
68
+ {
69
+ name: configured,
70
+ package: FRAMEWORK_PACKAGE_NAME[configured],
71
+ dependencyKind: "peerDependencies",
72
+ declaredRange: "*"
73
+ }
74
+ ];
75
+ raw.info.buildTools = detectBuildTools(env, raw);
76
+ raw.info.entryPoints = await discoverEntryPoints(env, raw);
77
+ }
78
+ const packages = raws.map((r) => r.info);
79
+ const storybooks = await discoverStorybooks(env, raws);
80
+ const tokenFiles = (await discoverTokenFiles(env)).map((file) => withOwner(file, packages));
81
+ const docFiles = (await discoverDocFiles(env)).map((file) => withOwner(file, packages));
82
+ const result = {
83
+ discoveryVersion: DISCOVERY_VERSION,
84
+ packageManager,
85
+ workspaces,
86
+ packages,
87
+ storybooks,
88
+ tokenFiles,
89
+ docFiles,
90
+ graph: buildGraph({ packages, storybooks, tokenFiles, docFiles }),
91
+ stats: { filesScanned: allFiles.length, truncated: walked.truncated },
92
+ diagnostics: diagnostics.toArray()
93
+ };
94
+ return projectDiscoverySchema.parse(result);
95
+ }
96
+ function withOwner(file, packages) {
97
+ const owner = owningPackage(packages, file.path);
98
+ return owner ? { ...file, package: owner.id } : file;
99
+ }
100
+ export { parsePnpmWorkspacePackages } from "./packages.js";
101
+ export { DISCOVERY_VERSION, projectDiscoverySchema } from "./schema.js";
102
+ export { parseCssCustomProperties, scanCssCustomProperties } from "./tokens.js";
103
+ //# sourceMappingURL=index.js.map