@nifrajs/cli 4.0.2 → 5.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 (364) hide show
  1. package/README.md +10 -2
  2. package/dist/agent-files.d.ts +107 -0
  3. package/dist/agent-files.d.ts.map +1 -0
  4. package/dist/agent-files.js +162 -0
  5. package/dist/agent-files.js.map +1 -0
  6. package/dist/app-files.d.ts +6 -0
  7. package/dist/app-files.d.ts.map +1 -1
  8. package/dist/app-files.js +27 -2
  9. package/dist/app-files.js.map +1 -1
  10. package/dist/assure-hydration.d.ts.map +1 -1
  11. package/dist/assure-hydration.js +9 -2
  12. package/dist/assure-hydration.js.map +1 -1
  13. package/dist/assure.d.ts +12 -2
  14. package/dist/assure.d.ts.map +1 -1
  15. package/dist/assure.js +59 -8
  16. package/dist/assure.js.map +1 -1
  17. package/dist/capabilities-tool.d.ts.map +1 -1
  18. package/dist/capabilities-tool.js +36 -9
  19. package/dist/capabilities-tool.js.map +1 -1
  20. package/dist/check-diagnostics.d.ts +22 -2
  21. package/dist/check-diagnostics.d.ts.map +1 -1
  22. package/dist/check-diagnostics.js +54 -7
  23. package/dist/check-diagnostics.js.map +1 -1
  24. package/dist/check-scan.d.ts +83 -24
  25. package/dist/check-scan.d.ts.map +1 -1
  26. package/dist/check-scan.js +1516 -318
  27. package/dist/check-scan.js.map +1 -1
  28. package/dist/check.d.ts +2 -2
  29. package/dist/check.d.ts.map +1 -1
  30. package/dist/check.js +203 -70
  31. package/dist/check.js.map +1 -1
  32. package/dist/cli.d.ts +9 -0
  33. package/dist/cli.d.ts.map +1 -1
  34. package/dist/cli.js +215 -50
  35. package/dist/cli.js.map +1 -1
  36. package/dist/client-module.d.ts +20 -0
  37. package/dist/client-module.d.ts.map +1 -0
  38. package/dist/client-module.js +73 -0
  39. package/dist/client-module.js.map +1 -0
  40. package/dist/command-catalog.d.ts +24 -5
  41. package/dist/command-catalog.d.ts.map +1 -1
  42. package/dist/command-catalog.js +194 -45
  43. package/dist/command-catalog.js.map +1 -1
  44. package/dist/component-check.d.ts +13 -0
  45. package/dist/component-check.d.ts.map +1 -0
  46. package/dist/component-check.js +59 -0
  47. package/dist/component-check.js.map +1 -0
  48. package/dist/contract-proof.d.ts +1 -1
  49. package/dist/contract-proof.d.ts.map +1 -1
  50. package/dist/contract-proof.js +2 -24
  51. package/dist/contract-proof.js.map +1 -1
  52. package/dist/contracts.d.ts +19 -0
  53. package/dist/contracts.d.ts.map +1 -1
  54. package/dist/contracts.js +50 -3
  55. package/dist/contracts.js.map +1 -1
  56. package/dist/db-tool.d.ts.map +1 -1
  57. package/dist/db-tool.js +4 -3
  58. package/dist/db-tool.js.map +1 -1
  59. package/dist/dev-bun-config.d.ts +5 -1
  60. package/dist/dev-bun-config.d.ts.map +1 -1
  61. package/dist/dev-bun-config.js +44 -11
  62. package/dist/dev-bun-config.js.map +1 -1
  63. package/dist/dev-feed-tool.d.ts +18 -1
  64. package/dist/dev-feed-tool.d.ts.map +1 -1
  65. package/dist/dev-feed-tool.js +93 -0
  66. package/dist/dev-feed-tool.js.map +1 -1
  67. package/dist/diagnostics.d.ts +3 -0
  68. package/dist/diagnostics.d.ts.map +1 -1
  69. package/dist/diagnostics.js.map +1 -1
  70. package/dist/diff-tool.d.ts +9 -8
  71. package/dist/diff-tool.d.ts.map +1 -1
  72. package/dist/diff-tool.js +28 -24
  73. package/dist/diff-tool.js.map +1 -1
  74. package/dist/doctor.d.ts +13 -0
  75. package/dist/doctor.d.ts.map +1 -1
  76. package/dist/doctor.js +23 -15
  77. package/dist/doctor.js.map +1 -1
  78. package/dist/env-file.d.ts +6 -0
  79. package/dist/env-file.d.ts.map +1 -1
  80. package/dist/env-file.js +6 -0
  81. package/dist/env-file.js.map +1 -1
  82. package/dist/fix-recipes.d.ts +2 -0
  83. package/dist/fix-recipes.d.ts.map +1 -1
  84. package/dist/fix-recipes.js +102 -2
  85. package/dist/fix-recipes.js.map +1 -1
  86. package/dist/hand-built-manifest.d.ts +27 -0
  87. package/dist/hand-built-manifest.d.ts.map +1 -0
  88. package/dist/hand-built-manifest.js +124 -0
  89. package/dist/hand-built-manifest.js.map +1 -0
  90. package/dist/hooks-report.d.ts +23 -0
  91. package/dist/hooks-report.d.ts.map +1 -0
  92. package/dist/hooks-report.js +104 -0
  93. package/dist/hooks-report.js.map +1 -0
  94. package/dist/init-agents.d.ts +18 -2
  95. package/dist/init-agents.d.ts.map +1 -1
  96. package/dist/init-agents.js +20 -13
  97. package/dist/init-agents.js.map +1 -1
  98. package/dist/internal/config-paths.d.ts +16 -0
  99. package/dist/internal/config-paths.d.ts.map +1 -0
  100. package/dist/internal/config-paths.js +124 -0
  101. package/dist/internal/config-paths.js.map +1 -0
  102. package/dist/internal/installed-package.d.ts +4 -0
  103. package/dist/internal/installed-package.d.ts.map +1 -0
  104. package/dist/internal/installed-package.js +17 -0
  105. package/dist/internal/installed-package.js.map +1 -0
  106. package/dist/internal/nifra-project.d.ts +41 -0
  107. package/dist/internal/nifra-project.d.ts.map +1 -0
  108. package/dist/internal/nifra-project.js +183 -0
  109. package/dist/internal/nifra-project.js.map +1 -0
  110. package/dist/internal/project-specifiers.d.ts +29 -0
  111. package/dist/internal/project-specifiers.d.ts.map +1 -0
  112. package/dist/internal/project-specifiers.js +435 -0
  113. package/dist/internal/project-specifiers.js.map +1 -0
  114. package/dist/internal/script-kind.d.ts.map +1 -1
  115. package/dist/internal/script-kind.js +2 -1
  116. package/dist/internal/script-kind.js.map +1 -1
  117. package/dist/internal/source-facts.d.ts +8 -1
  118. package/dist/internal/source-facts.d.ts.map +1 -1
  119. package/dist/internal/source-facts.js +22 -5
  120. package/dist/internal/source-facts.js.map +1 -1
  121. package/dist/internal/typescript-import.d.ts +1 -1
  122. package/dist/internal/typescript-import.d.ts.map +1 -1
  123. package/dist/internal/typescript-import.js +10 -6
  124. package/dist/internal/typescript-import.js.map +1 -1
  125. package/dist/introspect.d.ts +15 -6
  126. package/dist/introspect.d.ts.map +1 -1
  127. package/dist/introspect.js +87 -25
  128. package/dist/introspect.js.map +1 -1
  129. package/dist/levels-tool.d.ts.map +1 -1
  130. package/dist/levels-tool.js +98 -23
  131. package/dist/levels-tool.js.map +1 -1
  132. package/dist/load.d.ts +33 -10
  133. package/dist/load.d.ts.map +1 -1
  134. package/dist/load.js +60 -10
  135. package/dist/load.js.map +1 -1
  136. package/dist/mcp-context.d.ts +5 -5
  137. package/dist/mcp-context.d.ts.map +1 -1
  138. package/dist/mcp-context.js +8 -23
  139. package/dist/mcp-context.js.map +1 -1
  140. package/dist/mcp-delegate.d.ts +2 -2
  141. package/dist/mcp-delegate.d.ts.map +1 -1
  142. package/dist/mcp-delegate.js +19 -7
  143. package/dist/mcp-delegate.js.map +1 -1
  144. package/dist/mcp-exec.d.ts +13 -4
  145. package/dist/mcp-exec.d.ts.map +1 -1
  146. package/dist/mcp-exec.js +56 -14
  147. package/dist/mcp-exec.js.map +1 -1
  148. package/dist/mcp-http.js +1 -1
  149. package/dist/mcp-io.d.ts +30 -0
  150. package/dist/mcp-io.d.ts.map +1 -1
  151. package/dist/mcp-io.js +63 -0
  152. package/dist/mcp-io.js.map +1 -1
  153. package/dist/mcp-isolate.d.ts.map +1 -1
  154. package/dist/mcp-isolate.js +3 -2
  155. package/dist/mcp-isolate.js.map +1 -1
  156. package/dist/mcp-path.d.ts +2 -1
  157. package/dist/mcp-path.d.ts.map +1 -1
  158. package/dist/mcp-path.js +9 -24
  159. package/dist/mcp-path.js.map +1 -1
  160. package/dist/mcp-project-child.js +1 -1
  161. package/dist/mcp-project-child.js.map +1 -1
  162. package/dist/mcp-root.d.ts.map +1 -1
  163. package/dist/mcp-root.js +50 -10
  164. package/dist/mcp-root.js.map +1 -1
  165. package/dist/mcp-ws.d.ts.map +1 -1
  166. package/dist/mcp-ws.js +3 -1
  167. package/dist/mcp-ws.js.map +1 -1
  168. package/dist/mcp.d.ts.map +1 -1
  169. package/dist/mcp.js +48 -19
  170. package/dist/mcp.js.map +1 -1
  171. package/dist/migrate-layout.d.ts +47 -1
  172. package/dist/migrate-layout.d.ts.map +1 -1
  173. package/dist/migrate-layout.js +1972 -170
  174. package/dist/migrate-layout.js.map +1 -1
  175. package/dist/output-schemas.d.ts +128 -0
  176. package/dist/output-schemas.d.ts.map +1 -0
  177. package/dist/output-schemas.js +975 -0
  178. package/dist/output-schemas.js.map +1 -0
  179. package/dist/port.d.ts.map +1 -1
  180. package/dist/port.js +3 -2
  181. package/dist/port.js.map +1 -1
  182. package/dist/project-facts.d.ts +15 -4
  183. package/dist/project-facts.d.ts.map +1 -1
  184. package/dist/project-facts.js +1 -0
  185. package/dist/project-facts.js.map +1 -1
  186. package/dist/project-path.d.ts +6 -0
  187. package/dist/project-path.d.ts.map +1 -1
  188. package/dist/project-path.js +48 -14
  189. package/dist/project-path.js.map +1 -1
  190. package/dist/recipes/5.0.0.d.ts +11 -0
  191. package/dist/recipes/5.0.0.d.ts.map +1 -0
  192. package/dist/recipes/5.0.0.js +34 -0
  193. package/dist/recipes/5.0.0.js.map +1 -0
  194. package/dist/recipes/index.d.ts +7 -0
  195. package/dist/recipes/index.d.ts.map +1 -1
  196. package/dist/recipes/index.js +2 -0
  197. package/dist/recipes/index.js.map +1 -1
  198. package/dist/release-verification.d.ts +5 -0
  199. package/dist/release-verification.d.ts.map +1 -1
  200. package/dist/release-verification.js +14 -0
  201. package/dist/release-verification.js.map +1 -1
  202. package/dist/replay.d.ts.map +1 -1
  203. package/dist/replay.js +12 -3
  204. package/dist/replay.js.map +1 -1
  205. package/dist/review.d.ts +3 -1
  206. package/dist/review.d.ts.map +1 -1
  207. package/dist/review.js +43 -7
  208. package/dist/review.js.map +1 -1
  209. package/dist/rules/codes.d.ts +12 -0
  210. package/dist/rules/codes.d.ts.map +1 -1
  211. package/dist/rules/codes.js +12 -0
  212. package/dist/rules/codes.js.map +1 -1
  213. package/dist/rules/comment-markers.d.ts +16 -7
  214. package/dist/rules/comment-markers.d.ts.map +1 -1
  215. package/dist/rules/comment-markers.js +58 -19
  216. package/dist/rules/comment-markers.js.map +1 -1
  217. package/dist/rules/contract-visibility.d.ts +3 -0
  218. package/dist/rules/contract-visibility.d.ts.map +1 -0
  219. package/dist/rules/contract-visibility.js +337 -0
  220. package/dist/rules/contract-visibility.js.map +1 -0
  221. package/dist/rules/data-guard.d.ts +13 -0
  222. package/dist/rules/data-guard.d.ts.map +1 -1
  223. package/dist/rules/data-guard.js +356 -37
  224. package/dist/rules/data-guard.js.map +1 -1
  225. package/dist/rules/layout.d.ts +4 -0
  226. package/dist/rules/layout.d.ts.map +1 -0
  227. package/dist/rules/layout.js +19 -0
  228. package/dist/rules/layout.js.map +1 -0
  229. package/dist/rules/legacy.d.ts.map +1 -1
  230. package/dist/rules/legacy.js +178 -44
  231. package/dist/rules/legacy.js.map +1 -1
  232. package/dist/rules/pages.d.ts +50 -0
  233. package/dist/rules/pages.d.ts.map +1 -1
  234. package/dist/rules/pages.js +436 -4
  235. package/dist/rules/pages.js.map +1 -1
  236. package/dist/rules/routes.d.ts +6 -0
  237. package/dist/rules/routes.d.ts.map +1 -1
  238. package/dist/rules/routes.js +111 -16
  239. package/dist/rules/routes.js.map +1 -1
  240. package/dist/rules/secrets.js +2 -2
  241. package/dist/rules/secrets.js.map +1 -1
  242. package/dist/rules/security.d.ts +13 -1
  243. package/dist/rules/security.d.ts.map +1 -1
  244. package/dist/rules/security.js +313 -69
  245. package/dist/rules/security.js.map +1 -1
  246. package/dist/rules/zones.d.ts +13 -0
  247. package/dist/rules/zones.d.ts.map +1 -1
  248. package/dist/rules/zones.js +218 -44
  249. package/dist/rules/zones.js.map +1 -1
  250. package/dist/scaffold.d.ts.map +1 -1
  251. package/dist/scaffold.js +6 -4
  252. package/dist/scaffold.js.map +1 -1
  253. package/dist/sdk.d.ts.map +1 -1
  254. package/dist/sdk.js +14 -5
  255. package/dist/sdk.js.map +1 -1
  256. package/dist/stylex-migrate.d.ts.map +1 -1
  257. package/dist/stylex-migrate.js +290 -24
  258. package/dist/stylex-migrate.js.map +1 -1
  259. package/dist/test-tool.d.ts.map +1 -1
  260. package/dist/test-tool.js +6 -3
  261. package/dist/test-tool.js.map +1 -1
  262. package/dist/typecheck-coverage.d.ts +20 -0
  263. package/dist/typecheck-coverage.d.ts.map +1 -0
  264. package/dist/typecheck-coverage.js +129 -0
  265. package/dist/typecheck-coverage.js.map +1 -0
  266. package/dist/upgrade.d.ts +84 -0
  267. package/dist/upgrade.d.ts.map +1 -1
  268. package/dist/upgrade.js +498 -78
  269. package/dist/upgrade.js.map +1 -1
  270. package/dist/verification-plan.d.ts.map +1 -1
  271. package/dist/verification-plan.js +16 -2
  272. package/dist/verification-plan.js.map +1 -1
  273. package/dist/version-drift.d.ts +22 -0
  274. package/dist/version-drift.d.ts.map +1 -0
  275. package/dist/version-drift.js +94 -0
  276. package/dist/version-drift.js.map +1 -0
  277. package/dist/web-app-options.d.ts +1 -1
  278. package/dist/web-app-options.d.ts.map +1 -1
  279. package/dist/web-app-options.js +1 -1
  280. package/dist/web-app-options.js.map +1 -1
  281. package/dist/work-graph.d.ts.map +1 -1
  282. package/dist/work-graph.js +20 -8
  283. package/dist/work-graph.js.map +1 -1
  284. package/docs/examples.json +55 -7
  285. package/docs/llms-full.txt +2043 -222
  286. package/docs/types.json +1573 -246
  287. package/package.json +11 -12
  288. package/src/agent-files.ts +203 -0
  289. package/src/app-files.ts +34 -4
  290. package/src/assure-hydration.ts +18 -2
  291. package/src/assure.ts +88 -10
  292. package/src/capabilities-tool.ts +47 -9
  293. package/src/check-diagnostics.ts +75 -9
  294. package/src/check-scan.ts +1816 -377
  295. package/src/check.ts +258 -81
  296. package/src/cli.ts +242 -37
  297. package/src/client-module.ts +97 -0
  298. package/src/command-catalog.ts +237 -52
  299. package/src/component-check.ts +79 -0
  300. package/src/contract-proof.ts +3 -25
  301. package/src/contracts.ts +60 -3
  302. package/src/db-tool.ts +5 -2
  303. package/src/dev-bun-config.ts +51 -10
  304. package/src/dev-feed-tool.ts +112 -1
  305. package/src/diagnostics.ts +3 -0
  306. package/src/diff-tool.ts +33 -26
  307. package/src/doctor.ts +43 -21
  308. package/src/env-file.ts +7 -0
  309. package/src/fix-recipes.ts +116 -2
  310. package/src/hand-built-manifest.ts +144 -0
  311. package/src/hooks-report.ts +135 -0
  312. package/src/init-agents.ts +21 -14
  313. package/src/internal/config-paths.ts +154 -0
  314. package/src/internal/installed-package.ts +15 -0
  315. package/src/internal/nifra-project.ts +191 -0
  316. package/src/internal/project-specifiers.ts +477 -0
  317. package/src/internal/script-kind.ts +2 -1
  318. package/src/internal/source-facts.ts +48 -5
  319. package/src/internal/typescript-import.ts +15 -6
  320. package/src/introspect.ts +104 -28
  321. package/src/levels-tool.ts +124 -29
  322. package/src/load.ts +107 -17
  323. package/src/mcp-context.ts +12 -25
  324. package/src/mcp-delegate.ts +22 -10
  325. package/src/mcp-exec.ts +68 -18
  326. package/src/mcp-http.ts +1 -1
  327. package/src/mcp-io.ts +74 -0
  328. package/src/mcp-isolate.ts +9 -2
  329. package/src/mcp-path.ts +8 -22
  330. package/src/mcp-project-child.ts +1 -1
  331. package/src/mcp-root.ts +58 -8
  332. package/src/mcp-ws.ts +4 -2
  333. package/src/mcp.ts +56 -22
  334. package/src/migrate-layout.ts +2257 -176
  335. package/src/output-schemas.ts +1240 -0
  336. package/src/port.ts +3 -2
  337. package/src/project-facts.ts +17 -3
  338. package/src/project-path.ts +44 -13
  339. package/src/recipes/5.0.0.ts +35 -0
  340. package/src/recipes/index.ts +10 -0
  341. package/src/release-verification.ts +20 -0
  342. package/src/replay.ts +14 -3
  343. package/src/review.ts +44 -10
  344. package/src/rules/codes.ts +12 -0
  345. package/src/rules/comment-markers.ts +57 -21
  346. package/src/rules/contract-visibility.ts +383 -0
  347. package/src/rules/data-guard.ts +391 -42
  348. package/src/rules/layout.ts +22 -0
  349. package/src/rules/legacy.ts +206 -52
  350. package/src/rules/pages.ts +481 -4
  351. package/src/rules/routes.ts +124 -16
  352. package/src/rules/secrets.ts +2 -2
  353. package/src/rules/security.ts +391 -72
  354. package/src/rules/zones.ts +232 -42
  355. package/src/scaffold.ts +6 -4
  356. package/src/sdk.ts +14 -5
  357. package/src/stylex-migrate.ts +251 -24
  358. package/src/test-tool.ts +12 -3
  359. package/src/typecheck-coverage.ts +134 -0
  360. package/src/upgrade.ts +576 -72
  361. package/src/verification-plan.ts +33 -2
  362. package/src/version-drift.ts +118 -0
  363. package/src/web-app-options.ts +2 -2
  364. package/src/work-graph.ts +17 -12
@@ -0,0 +1,183 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join, relative } from "node:path";
3
+ import { CONFIG_FILE, FRAMEWORK_FILE } from "../app-files.js";
4
+ const NIFRA_PACKAGE = /^(?:@nifrajs\/|nifra$)/;
5
+ const DEPENDENCY_FIELDS = [
6
+ "dependencies",
7
+ "devDependencies",
8
+ "peerDependencies",
9
+ "optionalDependencies",
10
+ ];
11
+ /** Whether a package.json text names `nifra` or a `@nifrajs/` package, or carries a `nifra` setting. */
12
+ function declaresNifra(manifest) {
13
+ let parsed;
14
+ try {
15
+ parsed = JSON.parse(manifest);
16
+ }
17
+ catch {
18
+ // A manifest nobody can read proves nothing either way, so the directory stays checked.
19
+ return true;
20
+ }
21
+ if (typeof parsed !== "object" || parsed === null)
22
+ return true;
23
+ if ("nifra" in parsed)
24
+ return true;
25
+ return DEPENDENCY_FIELDS.some((field) => {
26
+ const deps = Reflect.get(parsed, field);
27
+ return (typeof deps === "object" &&
28
+ deps !== null &&
29
+ Object.keys(deps).some((name) => NIFRA_PACKAGE.test(name)));
30
+ });
31
+ }
32
+ /**
33
+ * Whether a project-relative directory ("" for the root) belongs to a nifra project, read statically.
34
+ * The checked root always does: running the check there says so. Below it, walking up, the first
35
+ * directory with a nifra config decides yes, and the first package.json decides by naming `nifra` or
36
+ * a `@nifrajs/` package, or carrying a `nifra` setting; a nested package that does none of that is
37
+ * another project, such as another framework's app kept beside this one.
38
+ */
39
+ export function createNifraProjectProbe(root) {
40
+ const cache = new Map();
41
+ const probe = (dir) => {
42
+ const cached = cache.get(dir);
43
+ if (cached !== undefined)
44
+ return cached;
45
+ const at = join(root, dir);
46
+ let nifra;
47
+ if (dir === "" || existsSync(join(at, CONFIG_FILE)) || existsSync(join(at, FRAMEWORK_FILE)))
48
+ nifra = true;
49
+ else if (existsSync(join(at, "package.json"))) {
50
+ let manifest;
51
+ try {
52
+ manifest = readFileSync(join(at, "package.json"), "utf8");
53
+ }
54
+ catch {
55
+ manifest = "";
56
+ }
57
+ nifra = declaresNifra(manifest);
58
+ }
59
+ else
60
+ nifra = probe(dir.slice(0, Math.max(dir.lastIndexOf("/"), 0)));
61
+ cache.set(dir, nifra);
62
+ return nifra;
63
+ };
64
+ return probe;
65
+ }
66
+ function readJson(path) {
67
+ try {
68
+ return JSON.parse(readFileSync(path, "utf8"));
69
+ }
70
+ catch {
71
+ return undefined;
72
+ }
73
+ }
74
+ const recordOf = (value) => typeof value === "object" && value !== null && !Array.isArray(value)
75
+ ? Object.fromEntries(Object.entries(value))
76
+ : undefined;
77
+ /**
78
+ * The `"nifra": { "kind" }` that governs a project-relative directory: read from the nearest
79
+ * package.json at or above it, the way the zone setting is read, with no app code run.
80
+ */
81
+ export function createKindProbe(root) {
82
+ const cache = new Map();
83
+ const probe = (absolute) => {
84
+ const cached = cache.get(absolute);
85
+ if (cached !== undefined)
86
+ return cached;
87
+ let declaration;
88
+ const manifestPath = join(absolute, "package.json");
89
+ if (existsSync(manifestPath)) {
90
+ const manifest = relative(root, manifestPath).replaceAll("\\", "/");
91
+ const kind = recordOf(recordOf(readJson(manifestPath))?.nifra)?.kind;
92
+ if (kind === undefined)
93
+ declaration = { kind: undefined, manifest };
94
+ else if (kind === "api" || kind === "web")
95
+ declaration = { kind, manifest };
96
+ else
97
+ declaration = {
98
+ kind: undefined,
99
+ manifest,
100
+ invalid: `[nifra] ${manifest} "nifra.kind" must be "api" or "web", not ${JSON.stringify(kind)}`,
101
+ };
102
+ }
103
+ else {
104
+ const parent = dirname(absolute);
105
+ declaration = parent === absolute ? { kind: undefined, manifest: undefined } : probe(parent);
106
+ }
107
+ cache.set(absolute, declaration);
108
+ return declaration;
109
+ };
110
+ return (dir) => probe(join(root, dir));
111
+ }
112
+ /** Whether a project-relative directory holds web config: a `nifra.config.ts` exporting `adapter`, or
113
+ * `backend/framework.ts`. */
114
+ export function hasWebConfig(root, dir) {
115
+ if (existsSync(join(root, dir, FRAMEWORK_FILE)))
116
+ return true;
117
+ const config = join(root, dir, CONFIG_FILE);
118
+ if (!existsSync(config))
119
+ return false;
120
+ let text;
121
+ try {
122
+ text = readFileSync(config, "utf8");
123
+ }
124
+ catch {
125
+ return true;
126
+ }
127
+ return /\bexport\s+(?:const|let|var|function)\s+adapter\b|\bexport\s*\{[^}]*\badapter\b/.test(text);
128
+ }
129
+ const WEB_PACKAGE = /^@nifrajs\/web(?:-[a-z0-9-]+)?$/;
130
+ /**
131
+ * Whether a project-relative directory ("" for the root) is inside a nifra web app. Walking up, the
132
+ * first directory with `nifra.config.ts` or `backend/framework.ts` makes it one; the first package.json
133
+ * decides instead: `"nifra": { "kind": "api" }` says no, otherwise naming `@nifrajs/web` or an adapter
134
+ * says yes. Nothing up to the root means no.
135
+ */
136
+ export function createWebAppProbe(root) {
137
+ const cache = new Map();
138
+ const probe = (dir) => {
139
+ const cached = cache.get(dir);
140
+ if (cached !== undefined)
141
+ return cached;
142
+ const at = join(root, dir);
143
+ let web;
144
+ if (existsSync(join(at, CONFIG_FILE)) || existsSync(join(at, FRAMEWORK_FILE)))
145
+ web = true;
146
+ else if (existsSync(join(at, "package.json"))) {
147
+ const manifest = recordOf(readJson(join(at, "package.json")));
148
+ web =
149
+ recordOf(manifest?.nifra)?.kind !== "api" &&
150
+ DEPENDENCY_FIELDS.some((field) => {
151
+ const deps = recordOf(manifest?.[field]);
152
+ return deps !== undefined && Object.keys(deps).some((name) => WEB_PACKAGE.test(name));
153
+ });
154
+ }
155
+ else
156
+ web = dir !== "" && probe(dir.slice(0, Math.max(dir.lastIndexOf("/"), 0)));
157
+ cache.set(dir, web);
158
+ return web;
159
+ };
160
+ return probe;
161
+ }
162
+ /** A zone or server-only finding outside any web app, with the API-only setting named. */
163
+ export const withApiKindHint = (message) => `${message}${message.endsWith(".") ? "" : "."} If this project serves no pages, declare it in package.json: "nifra": { "kind": "api" }`;
164
+ /**
165
+ * Whether `"nifra": { "kind": "api" }` takes a project-relative directory out of the zone checks. It
166
+ * does only where no web config sits at or above the directory, up to the checked root: an app that
167
+ * serves pages keeps its zone checks whatever its package.json says (NF-C037 reports the clash).
168
+ */
169
+ export function createApiOnlyProbe(root) {
170
+ const kindOf = createKindProbe(root);
171
+ const web = new Map();
172
+ const webConfigUp = (dir) => {
173
+ const cached = web.get(dir);
174
+ if (cached !== undefined)
175
+ return cached;
176
+ const found = hasWebConfig(root, dir) ||
177
+ (dir !== "" && webConfigUp(dir.slice(0, Math.max(dir.lastIndexOf("/"), 0))));
178
+ web.set(dir, found);
179
+ return found;
180
+ };
181
+ return (dir) => kindOf(dir).kind === "api" && !webConfigUp(dir);
182
+ }
183
+ //# sourceMappingURL=nifra-project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nifra-project.js","sourceRoot":"","sources":["../../src/internal/nifra-project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /** `(absolute importing file, specifier)` to the real path of the file it loads, or `undefined`. */
2
+ export type SpecifierResolver = (fromFile: string, specifier: string) => string | undefined;
3
+ export interface ProjectResolver extends SpecifierResolver {
4
+ /**
5
+ * `specifier`, an alias as `fromFile` writes it, rewritten through the same alias to name `file`
6
+ * instead, in the same style (extension, index). `undefined` when `specifier` is not an alias or no
7
+ * target of its alias covers `file`. Paths are absolute and unresolved, as `fromFile` is spelled.
8
+ */
9
+ readonly respell: (fromFile: string, specifier: string, file: string) => string | undefined;
10
+ /** The `paths` patterns of the nearest tsconfig at or above `dir`, after its `extends` chain. */
11
+ readonly pathPatterns: (dir: string) => readonly string[];
12
+ /**
13
+ * Whether the compiler settings for `file` keep an import whose named specifiers are all
14
+ * type-only, as `import {} from "x"` (`verbatimModuleSyntax`). `true` when its tsconfig chain
15
+ * cannot be read, so a scan that trusts the answer keeps the edge.
16
+ */
17
+ readonly keepsTypeOnlyImports: (file: string) => boolean;
18
+ }
19
+ /** JSON with comments and trailing commas, as tsconfig files are written. Throws on anything else. */
20
+ export declare function parseJsonc(text: string): unknown;
21
+ /** Whether `path` is a file. A path through a file (`a.ts/index.ts`) is none, not an error. */
22
+ export declare const isFile: (path: string) => boolean;
23
+ export interface SpecifierResolverOptions {
24
+ /** Whether a module file exists, for resolving against a planned layout rather than the disk.
25
+ * Config files (tsconfig, package.json) are still read from the disk. */
26
+ readonly moduleExists?: (path: string) => boolean;
27
+ }
28
+ export declare function createSpecifierResolver(options?: SpecifierResolverOptions): ProjectResolver;
29
+ //# sourceMappingURL=project-specifiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-specifiers.d.ts","sourceRoot":"","sources":["../../src/internal/project-specifiers.ts"],"names":[],"mappings":"AAYA,oGAAoG;AACpG,MAAM,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;AAE3F,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IAC3F,iGAAiG;IACjG,QAAQ,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,CAAA;IACzD;;;;OAIG;IACH,QAAQ,CAAC,oBAAoB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CACzD;AAkDD,sGAAsG;AACtG,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAgChD;AAOD,+FAA+F;AAC/F,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,KAAG,OAMrC,CAAA;AA8DD,MAAM,WAAW,wBAAwB;IACvC;6EACyE;IACzE,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAClD;AAED,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,wBAA6B,GAAG,eAAe,CAyR/F"}
@@ -0,0 +1,435 @@
1
+ /**
2
+ * The first-party file an import specifier names, found the way a bundler finds it: a relative or
3
+ * absolute path, a tsconfig `paths` alias or `baseUrl` name (following `extends`), or a package.json
4
+ * `imports` entry (`#x`). A bare package specifier, a file under `node_modules` and a name that matches
5
+ * nothing resolve to `undefined`, so a check never mistakes a dependency for project code.
6
+ *
7
+ * Never `Bun.resolveSync`: it can install a missing package and remembers answers for the life of the
8
+ * process, and the MCP server is long-lived. A resolver caches what it reads for one check run only.
9
+ */
10
+ import { readFileSync, realpathSync, statSync } from "node:fs";
11
+ import { basename, dirname, extname, isAbsolute, join } from "node:path";
12
+ const STAR = "\u0000";
13
+ const EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
14
+ // TypeScript's ESM convention: `./x.js` names `x.ts` when only the source exists.
15
+ const SOURCE_FOR = {
16
+ ".js": [".ts", ".tsx"],
17
+ ".jsx": [".tsx"],
18
+ ".mjs": [".mts"],
19
+ ".cjs": [".cts"],
20
+ };
21
+ // The package.json `imports` conditions a server-side bundle of this project matches, in no order:
22
+ // the target object's own key order decides, as in Node.
23
+ const CONDITIONS = new Set(["bun", "import", "default"]);
24
+ const MAX_EXTENDS = 16;
25
+ // The options that decide whether an import left with only type specifiers is emitted. The last two
26
+ // are removed in TypeScript 6 but still read by older compilers and bundlers.
27
+ const IMPORT_EMIT_OPTIONS = [
28
+ "verbatimModuleSyntax",
29
+ "importsNotUsedAsValues",
30
+ "preserveValueImports",
31
+ ];
32
+ /** JSON with comments and trailing commas, as tsconfig files are written. Throws on anything else. */
33
+ export function parseJsonc(text) {
34
+ let code = "";
35
+ for (let i = 0; i < text.length;) {
36
+ const ch = text[i];
37
+ if (ch === '"') {
38
+ let end = i + 1;
39
+ while (end < text.length && text[end] !== '"')
40
+ end += text[end] === "\\" ? 2 : 1;
41
+ code += text.slice(i, end + 1);
42
+ i = end + 1;
43
+ }
44
+ else if (ch === "/" && text[i + 1] === "/") {
45
+ const newline = text.indexOf("\n", i);
46
+ i = newline === -1 ? text.length : newline;
47
+ }
48
+ else if (ch === "/" && text[i + 1] === "*") {
49
+ const close = text.indexOf("*/", i + 2);
50
+ i = close === -1 ? text.length : close + 2;
51
+ }
52
+ else {
53
+ code += ch;
54
+ i += 1;
55
+ }
56
+ }
57
+ let json = "";
58
+ for (let i = 0; i < code.length; i += 1) {
59
+ const ch = code[i];
60
+ if (ch === '"') {
61
+ let end = i + 1;
62
+ while (end < code.length && code[end] !== '"')
63
+ end += code[end] === "\\" ? 2 : 1;
64
+ json += code.slice(i, end + 1);
65
+ i = end;
66
+ }
67
+ else if (ch === "," && /^\s*[}\]]/.test(code.slice(i + 1))) {
68
+ }
69
+ else
70
+ json += ch;
71
+ }
72
+ return JSON.parse(json.replace(/^\uFEFF/, ""));
73
+ }
74
+ const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value)
75
+ ? Object.fromEntries(Object.entries(value))
76
+ : undefined;
77
+ /** Whether `path` is a file. A path through a file (`a.ts/index.ts`) is none, not an error. */
78
+ export const isFile = (path) => {
79
+ try {
80
+ return statSync(path, { throwIfNoEntry: false })?.isFile() ?? false;
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ };
86
+ const real = (path) => {
87
+ try {
88
+ return realpathSync(path);
89
+ }
90
+ catch {
91
+ return path;
92
+ }
93
+ };
94
+ /** The file a path names: as written, its source for a `.js` name, with an extension, or an index. */
95
+ function probe(base, exists) {
96
+ if (exists(base))
97
+ return base;
98
+ const ext = extname(base);
99
+ for (const source of SOURCE_FOR[ext] ?? []) {
100
+ const candidate = `${base.slice(0, -ext.length)}${source}`;
101
+ if (exists(candidate))
102
+ return candidate;
103
+ }
104
+ for (const extension of EXTENSIONS)
105
+ if (exists(`${base}${extension}`))
106
+ return `${base}${extension}`;
107
+ for (const extension of EXTENSIONS) {
108
+ const index = join(base, `index${extension}`);
109
+ if (exists(index))
110
+ return index;
111
+ }
112
+ return undefined;
113
+ }
114
+ /**
115
+ * `file` written the way `written` names `found`: as is, without its extension, as its folder when
116
+ * `found` is an index, or with the `.js` name of a `.ts` source.
117
+ */
118
+ function sameStyle(written, found, file) {
119
+ if (written === found)
120
+ return file;
121
+ const ext = extname(file);
122
+ const bare = file.slice(0, file.length - ext.length);
123
+ if (found.startsWith(`${written}/index.`) || found.startsWith(`${written}\\index.`))
124
+ return basename(file).startsWith("index.") ? dirname(file) : bare;
125
+ if (found.startsWith(written))
126
+ return bare;
127
+ const writtenExt = extname(written);
128
+ return SOURCE_FOR[writtenExt] === undefined ? file : `${bare}${writtenExt}`;
129
+ }
130
+ /** Longest-prefix `*` match, TypeScript's rule for `paths` and Node's for `imports`. */
131
+ function matchPattern(patterns, specifier) {
132
+ if (patterns.includes(specifier))
133
+ return { pattern: specifier, star: undefined };
134
+ let best;
135
+ for (const pattern of patterns) {
136
+ const at = pattern.indexOf("*");
137
+ if (at === -1 || pattern.indexOf("*", at + 1) !== -1)
138
+ continue;
139
+ const prefix = pattern.slice(0, at);
140
+ const suffix = pattern.slice(at + 1);
141
+ if (specifier.length < prefix.length + suffix.length)
142
+ continue;
143
+ if (!specifier.startsWith(prefix) || !specifier.endsWith(suffix))
144
+ continue;
145
+ if (best !== undefined && prefix.length <= best.pattern.indexOf("*"))
146
+ continue;
147
+ best = { pattern, star: specifier.slice(prefix.length, specifier.length - suffix.length) };
148
+ }
149
+ return best;
150
+ }
151
+ export function createSpecifierResolver(options = {}) {
152
+ const exists = options.moduleExists ?? isFile;
153
+ const configs = new Map();
154
+ const tsconfigByDir = new Map();
155
+ const manifests = new Map();
156
+ const packageByDir = new Map();
157
+ const readJsonc = (path) => {
158
+ try {
159
+ return record(parseJsonc(readFileSync(path, "utf8")));
160
+ }
161
+ catch {
162
+ return undefined;
163
+ }
164
+ };
165
+ /** The nearest `tsconfig.json` (or `jsconfig.json`) at or above `dir`, as Bun's resolver picks it. */
166
+ const tsconfigFor = (dir) => {
167
+ if (tsconfigByDir.has(dir))
168
+ return tsconfigByDir.get(dir);
169
+ const own = ["tsconfig.json", "jsconfig.json"].map((name) => join(dir, name)).find(isFile);
170
+ const parent = dirname(dir);
171
+ const found = own ?? (parent === dir ? undefined : tsconfigFor(parent));
172
+ tsconfigByDir.set(dir, found);
173
+ return found;
174
+ };
175
+ /** An `extends` entry's file: a path relative to the config, or a package found in `node_modules`. */
176
+ const extendedConfig = (from, name) => {
177
+ const withJson = (path) => isFile(path) ? path : isFile(`${path}.json`) ? `${path}.json` : undefined;
178
+ if (name.startsWith("./") || name.startsWith("../") || isAbsolute(name))
179
+ return withJson(join(dirname(from), name));
180
+ for (let dir = dirname(from);; dir = dirname(dir)) {
181
+ const target = join(dir, "node_modules", name);
182
+ const direct = withJson(target);
183
+ if (direct !== undefined)
184
+ return direct;
185
+ const manifest = readJsonc(join(target, "package.json"));
186
+ const field = manifest?.tsconfig;
187
+ const nested = typeof field === "string"
188
+ ? withJson(join(target, field))
189
+ : withJson(join(target, "tsconfig"));
190
+ if (nested !== undefined)
191
+ return nested;
192
+ if (dirname(dir) === dir)
193
+ return undefined;
194
+ }
195
+ };
196
+ /** The `paths`, `baseUrl` and import-emit options a config ends up with after its `extends` chain. */
197
+ const pathsConfig = (file, seen = new Set()) => {
198
+ if (configs.has(file))
199
+ return configs.get(file);
200
+ if (seen.has(file) || seen.size >= MAX_EXTENDS)
201
+ return undefined;
202
+ const config = readJsonc(file);
203
+ let inherited;
204
+ let importEmit = {};
205
+ const extended = config?.extends;
206
+ for (const name of typeof extended === "string"
207
+ ? [extended]
208
+ : Array.isArray(extended)
209
+ ? extended
210
+ : []) {
211
+ if (typeof name !== "string")
212
+ continue;
213
+ const base = extendedConfig(file, name);
214
+ const parent = base === undefined ? undefined : pathsConfig(base, new Set([...seen, file]));
215
+ importEmit =
216
+ importEmit === undefined || parent?.importEmit === undefined
217
+ ? undefined
218
+ : { ...importEmit, ...parent.importEmit };
219
+ if (parent === undefined)
220
+ continue;
221
+ inherited = {
222
+ paths: parent.paths.length > 0 ? parent.paths : (inherited?.paths ?? []),
223
+ baseUrl: parent.baseUrl ?? inherited?.baseUrl,
224
+ pathsBase: parent.paths.length > 0 ? parent.pathsBase : (inherited?.pathsBase ?? parent.pathsBase),
225
+ importEmit,
226
+ };
227
+ }
228
+ const options = record(config?.compilerOptions);
229
+ if (importEmit !== undefined) {
230
+ for (const key of IMPORT_EMIT_OPTIONS)
231
+ if (options?.[key] !== undefined)
232
+ importEmit = { ...importEmit, [key]: options[key] };
233
+ }
234
+ const baseUrl = typeof options?.baseUrl === "string"
235
+ ? join(dirname(file), options.baseUrl)
236
+ : inherited?.baseUrl;
237
+ const ownPaths = record(options?.paths);
238
+ const paths = ownPaths === undefined
239
+ ? (inherited?.paths ?? [])
240
+ : Object.entries(ownPaths).map(([pattern, targets]) => [
241
+ pattern,
242
+ Array.isArray(targets)
243
+ ? targets.filter((t) => typeof t === "string")
244
+ : [],
245
+ ]);
246
+ const pathsBase = baseUrl ?? (ownPaths === undefined ? (inherited?.pathsBase ?? dirname(file)) : dirname(file));
247
+ const resolved = config === undefined ? undefined : { paths, baseUrl, pathsBase, importEmit };
248
+ configs.set(file, resolved);
249
+ return resolved;
250
+ };
251
+ // Keyed by config, not directory: every package name reaches here, from every folder of the project.
252
+ const aliases = new Map();
253
+ const viaTsconfig = (fromFile, specifier) => {
254
+ const tsconfig = tsconfigFor(dirname(fromFile));
255
+ if (tsconfig === undefined)
256
+ return undefined;
257
+ const key = `${tsconfig}\0${specifier}`;
258
+ if (aliases.has(key))
259
+ return aliases.get(key);
260
+ const found = aliasTarget(tsconfig, specifier);
261
+ aliases.set(key, found);
262
+ return found;
263
+ };
264
+ const aliasTarget = (tsconfig, specifier) => pathsRule(tsconfig, specifier)?.found;
265
+ /** The nearest package.json at or above `dir`: the package scope Node reads `imports` from. */
266
+ const packageFor = (dir) => {
267
+ if (packageByDir.has(dir))
268
+ return packageByDir.get(dir);
269
+ const own = join(dir, "package.json");
270
+ const parent = dirname(dir);
271
+ const found = isFile(own) ? own : parent === dir ? undefined : packageFor(parent);
272
+ packageByDir.set(dir, found);
273
+ return found;
274
+ };
275
+ const importTarget = (target, star) => {
276
+ if (typeof target === "string")
277
+ return target.startsWith("./") ? target.replaceAll("*", star ?? "") : undefined;
278
+ if (Array.isArray(target)) {
279
+ for (const entry of target) {
280
+ const found = importTarget(entry, star);
281
+ if (found !== undefined)
282
+ return found;
283
+ }
284
+ return undefined;
285
+ }
286
+ const conditions = record(target);
287
+ if (conditions === undefined)
288
+ return undefined;
289
+ for (const [condition, value] of Object.entries(conditions)) {
290
+ if (CONDITIONS.has(condition))
291
+ return importTarget(value, star);
292
+ }
293
+ return undefined;
294
+ };
295
+ const importsRule = (fromFile, specifier) => {
296
+ const manifestPath = packageFor(dirname(fromFile));
297
+ if (manifestPath === undefined)
298
+ return undefined;
299
+ if (!manifests.has(manifestPath))
300
+ manifests.set(manifestPath, readJsonc(manifestPath));
301
+ const imports = record(manifests.get(manifestPath)?.imports);
302
+ if (imports === undefined)
303
+ return undefined;
304
+ const keys = Object.keys(imports);
305
+ const match = matchPattern(keys, specifier);
306
+ if (match === undefined)
307
+ return undefined;
308
+ const template = importTarget(imports[match.pattern], STAR);
309
+ if (template === undefined)
310
+ return undefined;
311
+ const base = dirname(manifestPath);
312
+ const written = join(base, template.replaceAll(STAR, match.star ?? ""));
313
+ const found = probe(written, exists);
314
+ return found === undefined
315
+ ? undefined
316
+ : { key: match.pattern, keys, base, templates: [template], written, found };
317
+ };
318
+ const viaImports = (fromFile, specifier) => importsRule(fromFile, specifier)?.found;
319
+ const tsconfigRule = (fromFile, specifier) => {
320
+ const tsconfig = tsconfigFor(dirname(fromFile));
321
+ return tsconfig === undefined ? undefined : pathsRule(tsconfig, specifier);
322
+ };
323
+ const pathsRule = (tsconfig, specifier) => {
324
+ const config = pathsConfig(tsconfig);
325
+ if (config === undefined)
326
+ return undefined;
327
+ const keys = config.paths.map(([pattern]) => pattern);
328
+ const match = matchPattern(keys, specifier);
329
+ if (match === undefined) {
330
+ if (config.baseUrl === undefined)
331
+ return undefined;
332
+ const written = join(config.baseUrl, specifier);
333
+ const found = probe(written, exists);
334
+ return found === undefined
335
+ ? undefined
336
+ : { key: undefined, keys, base: config.baseUrl, templates: [STAR], written, found };
337
+ }
338
+ // TypeScript permits at most one wildcard in each substitution, as in the alias key.
339
+ const targets = (config.paths.find(([pattern]) => pattern === match.pattern)?.[1] ?? []).filter((target) => target.indexOf("*") === target.lastIndexOf("*"));
340
+ for (const target of targets) {
341
+ const written = join(config.pathsBase, target.replaceAll("*", match.star ?? ""));
342
+ const found = probe(written, exists);
343
+ if (found === undefined)
344
+ continue;
345
+ const templates = targets.map((t) => t.replaceAll("*", STAR));
346
+ return { key: match.pattern, keys, base: config.pathsBase, templates, written, found };
347
+ }
348
+ return undefined;
349
+ };
350
+ const respell = (fromFile, specifier, file) => {
351
+ if (/^\.\.?(?:\/|$)/.test(specifier) || isAbsolute(specifier))
352
+ return undefined;
353
+ const rule = (specifier.startsWith("#") ? importsRule(fromFile, specifier) : undefined) ??
354
+ tsconfigRule(fromFile, specifier);
355
+ if (rule === undefined)
356
+ return undefined;
357
+ const spelled = sameStyle(rule.written, rule.found, file);
358
+ for (const template of rule.templates) {
359
+ const at = template.indexOf(STAR);
360
+ if (at === -1)
361
+ continue;
362
+ // An empty head still ends at a folder boundary: `src2/x` is not under a `baseUrl` of `src`.
363
+ const prefix = join(rule.base, template.slice(0, at) || "./");
364
+ const suffix = template.slice(at + STAR.length);
365
+ if (!spelled.startsWith(prefix) || !spelled.endsWith(suffix))
366
+ continue;
367
+ if (spelled.length < prefix.length + suffix.length)
368
+ continue;
369
+ const star = spelled
370
+ .slice(prefix.length, spelled.length - suffix.length)
371
+ .replaceAll("\\", "/");
372
+ if (star === "" || star.startsWith("/"))
373
+ continue;
374
+ const keyAt = rule.key?.indexOf("*") ?? 0;
375
+ if (keyAt === -1)
376
+ continue;
377
+ const next = rule.key === undefined
378
+ ? star
379
+ : `${rule.key.slice(0, keyAt)}${star}${rule.key.slice(keyAt + 1)}`;
380
+ // The rewritten name must take the same route: another, longer pattern would claim it.
381
+ if (matchPattern(rule.keys, next)?.pattern !== rule.key)
382
+ continue;
383
+ return next;
384
+ }
385
+ return undefined;
386
+ };
387
+ const resolved = new Map();
388
+ const resolve = (fromFile, specifier) => {
389
+ // Every rule below reads only the importing file's directory, so the answer is the directory's.
390
+ const key = `${dirname(fromFile)}\0${specifier}`;
391
+ if (resolved.has(key))
392
+ return resolved.get(key);
393
+ const path = resolveFrom(fromFile, specifier);
394
+ resolved.set(key, path);
395
+ return path;
396
+ };
397
+ const pathPatterns = (dir) => {
398
+ const tsconfig = tsconfigFor(dir);
399
+ const config = tsconfig === undefined ? undefined : pathsConfig(tsconfig);
400
+ return (config?.paths ?? []).map(([pattern]) => pattern);
401
+ };
402
+ const keepsTypeOnlyImports = (file) => {
403
+ const tsconfig = tsconfigFor(dirname(file));
404
+ if (tsconfig === undefined)
405
+ return false;
406
+ const emit = pathsConfig(tsconfig)?.importEmit;
407
+ if (emit === undefined)
408
+ return true;
409
+ return (emit.verbatimModuleSyntax === true ||
410
+ emit.preserveValueImports === true ||
411
+ emit.importsNotUsedAsValues === "preserve" ||
412
+ emit.importsNotUsedAsValues === "error");
413
+ };
414
+ return Object.assign(resolve, { respell, pathPatterns, keepsTypeOnlyImports });
415
+ function resolveFrom(fromFile, specifier) {
416
+ let found;
417
+ if (specifier.startsWith("./") ||
418
+ specifier.startsWith("../") ||
419
+ specifier === "." ||
420
+ specifier === "..")
421
+ found = probe(join(dirname(fromFile), specifier), exists);
422
+ else if (isAbsolute(specifier))
423
+ found = probe(specifier, exists);
424
+ // A `#` name is a package.json import first; a tsconfig may still map it.
425
+ else if (specifier.startsWith("#"))
426
+ found = viaImports(fromFile, specifier) ?? viaTsconfig(fromFile, specifier);
427
+ else
428
+ found = viaTsconfig(fromFile, specifier);
429
+ if (found === undefined)
430
+ return undefined;
431
+ const path = real(found);
432
+ return /[\\/]node_modules[\\/]/.test(path) ? undefined : path;
433
+ }
434
+ }
435
+ //# sourceMappingURL=project-specifiers.js.map