@codefast/cli 0.3.14 → 0.3.16-canary.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 (189) hide show
  1. package/README.md +8 -3
  2. package/dist/arrange/analyze.mjs +24 -0
  3. package/dist/{lib/arrange/presentation/arrange-cli.schema.mjs → arrange/cli-schema.mjs} +10 -1
  4. package/dist/arrange/command.mjs +106 -0
  5. package/dist/{lib/arrange/domain/arrange-analyze.service.mjs → arrange/domain/analyze-service.mjs} +17 -10
  6. package/dist/{lib/arrange/domain/ast/ast-node.model.mjs → arrange/domain/ast/ast-node.mjs} +57 -3
  7. package/dist/{lib/arrange/domain/ast/collectors-cn.collector.mjs → arrange/domain/ast/collectors-cn.mjs} +21 -9
  8. package/dist/{lib/arrange/domain/ast/collectors-jsx.collector.mjs → arrange/domain/ast/collectors-jsx.mjs} +5 -2
  9. package/dist/{lib/arrange/domain/ast/collectors-tv.collector.mjs → arrange/domain/ast/collectors-tv.mjs} +21 -6
  10. package/dist/{lib/arrange/domain/ast/ast-helpers.helper.mjs → arrange/domain/ast/helpers.mjs} +26 -9
  11. package/dist/{lib/arrange/domain/ast/targets.model.mjs → arrange/domain/ast/targets.mjs} +19 -10
  12. package/dist/{lib/arrange/domain/constants.domain.mjs → arrange/domain/constants.mjs} +63 -18
  13. package/dist/{lib/arrange/domain/arrange-grouping.service.mjs → arrange/domain/grouping-service.mjs} +31 -10
  14. package/dist/{lib/arrange/domain/grouping.domain.mjs → arrange/domain/grouping.mjs} +39 -18
  15. package/dist/{lib/arrange/domain/imports.domain.mjs → arrange/domain/imports.mjs} +7 -3
  16. package/dist/{lib/arrange/domain/source-text-formatters.formatter.mjs → arrange/domain/source-text-formatters.mjs} +22 -5
  17. package/dist/{lib/arrange/domain/tailwind-token.value-object.mjs → arrange/domain/tailwind-token.mjs} +12 -3
  18. package/dist/{lib/arrange/domain/tailwind-token-classifier.domain-service.mjs → arrange/domain/token-classifier.mjs} +40 -15
  19. package/dist/arrange/output.mjs +79 -0
  20. package/dist/arrange/process-file.mjs +42 -0
  21. package/dist/arrange/resolve-target.mjs +24 -0
  22. package/dist/arrange/scan-target.mjs +20 -0
  23. package/dist/arrange/source-parse.mjs +18 -0
  24. package/dist/arrange/suggest.mjs +15 -0
  25. package/dist/arrange/sync.mjs +51 -0
  26. package/dist/arrange/typescript-ast-translator.mjs +354 -0
  27. package/dist/arrange/workspace.mjs +31 -0
  28. package/dist/bin.mjs +1 -1
  29. package/dist/{program.mjs → cli.mjs} +14 -19
  30. package/dist/core/cli/format-error.mjs +15 -0
  31. package/dist/core/cli/global-options.mjs +10 -0
  32. package/dist/core/cli/positional.mjs +11 -0
  33. package/dist/core/cli/result-handle.mjs +51 -0
  34. package/dist/core/config/loader.mjs +79 -0
  35. package/dist/{lib/config/infrastructure/config-schema.adapter.mjs → core/config/schema.mjs} +20 -6
  36. package/dist/core/config/warnings.mjs +12 -0
  37. package/dist/core/config.mjs +19 -0
  38. package/dist/{lib/core/domain/caught-unknown-message.value-object.mjs → core/errors.mjs} +20 -4
  39. package/dist/core/exit-codes.mjs +17 -0
  40. package/dist/core/filesystem/node.mjs +32 -0
  41. package/dist/core/logger.mjs +15 -0
  42. package/dist/{lib/core/domain/result.model.mjs → core/result.mjs} +7 -3
  43. package/dist/{lib/core/application/services/schema-validator.service.mjs → core/schema-parse.mjs} +6 -5
  44. package/dist/{lib/shared/source-code/domain/text-edit.model.mjs → core/source-text-edit.mjs} +15 -14
  45. package/dist/core/verbose-diagnostics.mjs +11 -0
  46. package/dist/core/workspace/resolver.mjs +177 -0
  47. package/dist/core/workspace/typescript-walk.mjs +36 -0
  48. package/dist/mirror/cli-result.mjs +21 -0
  49. package/dist/{lib/mirror/presentation/mirror-cli.schema.mjs → mirror/cli-schema.mjs} +4 -4
  50. package/dist/mirror/command.mjs +57 -0
  51. package/dist/mirror/dist-filesystem-impl.mjs +42 -0
  52. package/dist/{lib/mirror/domain/constants.domain.mjs → mirror/domain/constants.mjs} +16 -1
  53. package/dist/{lib/mirror/infrastructure/dirent-list.guard.mjs → mirror/domain/dirent-guard.mjs} +5 -2
  54. package/dist/{lib/mirror/domain/errors.domain.mjs → mirror/domain/errors.mjs} +7 -1
  55. package/dist/{lib/mirror/application/services/generate-mirror-exports.service.mjs → mirror/domain/exports.mjs} +17 -10
  56. package/dist/{lib/mirror/domain/package-display-name.policy.mjs → mirror/domain/package-display-name.mjs} +5 -2
  57. package/dist/{lib/mirror/domain/path-normalizer.value-object.mjs → mirror/domain/path-normalizer.mjs} +4 -1
  58. package/dist/mirror/output.mjs +45 -0
  59. package/dist/mirror/package-path.mjs +32 -0
  60. package/dist/mirror/prepare.mjs +33 -0
  61. package/dist/mirror/sync-reporter.mjs +104 -0
  62. package/dist/mirror/sync-workspace-package.mjs +103 -0
  63. package/dist/mirror/sync.mjs +87 -0
  64. package/dist/{lib/mirror/infrastructure/package-json-exports.repository.mjs → mirror/write-exports.mjs} +35 -31
  65. package/dist/tag/cli-result.mjs +11 -0
  66. package/dist/{lib/tag/presentation/tag-cli.schema.mjs → tag/cli-schema.mjs} +4 -1
  67. package/dist/tag/command.mjs +56 -0
  68. package/dist/{lib/tag/presentation/tag-sync.presenter.mjs → tag/output.mjs} +54 -28
  69. package/dist/tag/prepare.mjs +29 -0
  70. package/dist/tag/resolve-target-path.mjs +12 -0
  71. package/dist/tag/since-writer.mjs +130 -0
  72. package/dist/tag/sync.mjs +141 -0
  73. package/dist/tag/target-candidates.mjs +68 -0
  74. package/dist/tag/target-runner.mjs +26 -0
  75. package/dist/tag/version-resolver.mjs +25 -0
  76. package/package.json +8 -9
  77. package/dist/lib/arrange/adapters/primary/cli/arrange.command.mjs +0 -324
  78. package/dist/lib/arrange/adapters/primary/cli/arrange.presentation.module.mjs +0 -14
  79. package/dist/lib/arrange/adapters/primary/cli/presentation.tokens.mjs +0 -5
  80. package/dist/lib/arrange/adapters/secondary/domain-source-parser.adapter.mjs +0 -239
  81. package/dist/lib/arrange/adapters/secondary/file-walker.adapter.mjs +0 -241
  82. package/dist/lib/arrange/adapters/secondary/workspace-resolver.adapter.mjs +0 -233
  83. package/dist/lib/arrange/application/requests/suggest-groups.request.mjs +0 -1
  84. package/dist/lib/arrange/application/services/arrange-file-processor.service.mjs +0 -267
  85. package/dist/lib/arrange/application/services/arrange-target-path-resolver.service.mjs +0 -21
  86. package/dist/lib/arrange/application/services/arrange-target-scanner.service.mjs +0 -243
  87. package/dist/lib/arrange/application/use-cases/analyze-directory.use-case.mjs +0 -255
  88. package/dist/lib/arrange/application/use-cases/prepare-arrange-workspace.use-case.mjs +0 -263
  89. package/dist/lib/arrange/application/use-cases/run-arrange-sync.use-case.mjs +0 -274
  90. package/dist/lib/arrange/application/use-cases/suggest-cn-groups.use-case.mjs +0 -237
  91. package/dist/lib/arrange/arrange.module.mjs +0 -31
  92. package/dist/lib/arrange/contracts/analyze-report-presenter.contract.mjs +0 -1
  93. package/dist/lib/arrange/contracts/models.mjs +0 -1
  94. package/dist/lib/arrange/contracts/services.contract.mjs +0 -1
  95. package/dist/lib/arrange/contracts/tokens.mjs +0 -15
  96. package/dist/lib/arrange/domain/tailwind-grouping.service.mjs +0 -231
  97. package/dist/lib/arrange/domain/types.domain.mjs +0 -1
  98. package/dist/lib/arrange/infrastructure/typescript-ast-translator.adapter.mjs +0 -355
  99. package/dist/lib/arrange/presentation/arrange-analyze.presenter.mjs +0 -259
  100. package/dist/lib/arrange/presentation/arrange-sync.presenter.mjs +0 -33
  101. package/dist/lib/arrange/presentation/group-file-preview.presenter.mjs +0 -271
  102. package/dist/lib/bootstrap/composition-root.mjs +0 -33
  103. package/dist/lib/config/application/ports/config-loader.port.mjs +0 -1
  104. package/dist/lib/config/application/ports/config-warning-reporter.port.mjs +0 -1
  105. package/dist/lib/config/application/services/load-config.service.mjs +0 -17
  106. package/dist/lib/config/contracts/tokens.mjs +0 -6
  107. package/dist/lib/config/domain/schema.domain.mjs +0 -1
  108. package/dist/lib/config/infrastructure/config-loader.adapter.mjs +0 -306
  109. package/dist/lib/config/infrastructure/config-warning-reporter.adapter.mjs +0 -234
  110. package/dist/lib/core/application/load-codefast-config.use-case.mjs +0 -245
  111. package/dist/lib/core/application/ports/cli-io.port.mjs +0 -1
  112. package/dist/lib/core/application/ports/path.port.mjs +0 -1
  113. package/dist/lib/core/application/ports/repo-root-resolver.port.mjs +0 -1
  114. package/dist/lib/core/application/ports/runtime.port.mjs +0 -1
  115. package/dist/lib/core/application/services/global-cli-options-parser.service.mjs +0 -9
  116. package/dist/lib/core/contracts/tokens.mjs +0 -10
  117. package/dist/lib/core/core.module.mjs +0 -9
  118. package/dist/lib/core/domain/cli-exit-codes.domain.mjs +0 -11
  119. package/dist/lib/core/domain/errors.domain.mjs +0 -17
  120. package/dist/lib/core/infrastructure/infrastructure.module.mjs +0 -30
  121. package/dist/lib/core/infrastructure/path.adapter.mjs +0 -246
  122. package/dist/lib/core/infrastructure/port-telemetry.decorator.mjs +0 -62
  123. package/dist/lib/core/infrastructure/repo-root-resolver.adapter.mjs +0 -233
  124. package/dist/lib/core/infrastructure/verbose-diagnostics.policy.mjs +0 -9
  125. package/dist/lib/core/presentation/cli-executor.presenter.mjs +0 -42
  126. package/dist/lib/core/presentation/cli-schema.parser.mjs +0 -7
  127. package/dist/lib/core/presentation/format-app-error.presenter.mjs +0 -17
  128. package/dist/lib/core/presentation/presentation.module.mjs +0 -5
  129. package/dist/lib/infrastructure/node-io.adapter.mjs +0 -285
  130. package/dist/lib/infrastructure/workspace/repo-root-resolver.service.mjs +0 -21
  131. package/dist/lib/kernel/contracts/cli-command.contract.mjs +0 -1
  132. package/dist/lib/kernel/contracts/tokens.mjs +0 -5
  133. package/dist/lib/mirror/adapters/primary/cli/mirror.command.mjs +0 -272
  134. package/dist/lib/mirror/adapters/secondary/file-system-service.adapter.mjs +0 -261
  135. package/dist/lib/mirror/adapters/secondary/mirror-sync-reporter.adapter.mjs +0 -243
  136. package/dist/lib/mirror/adapters/secondary/package-repository.adapter.mjs +0 -241
  137. package/dist/lib/mirror/adapters/secondary/sync-workspace-package.adapter.mjs +0 -351
  138. package/dist/lib/mirror/adapters/secondary/workspace-service.adapter.mjs +0 -237
  139. package/dist/lib/mirror/application/mirror-sync-json.format.mjs +0 -12
  140. package/dist/lib/mirror/application/ports/file-system-service.port.mjs +0 -1
  141. package/dist/lib/mirror/application/ports/mirror-sync-reporter.port.mjs +0 -1
  142. package/dist/lib/mirror/application/ports/package-repository.port.mjs +0 -1
  143. package/dist/lib/mirror/application/ports/sync-workspace-package.port.mjs +0 -1
  144. package/dist/lib/mirror/application/ports/workspace-service.port.mjs +0 -1
  145. package/dist/lib/mirror/application/requests/mirror-sync.request.mjs +0 -1
  146. package/dist/lib/mirror/application/services/mirror-package-arg-resolver.service.mjs +0 -20
  147. package/dist/lib/mirror/application/use-cases/prepare-mirror-sync.use-case.mjs +0 -264
  148. package/dist/lib/mirror/application/use-cases/run-mirror-sync.use-case.mjs +0 -302
  149. package/dist/lib/mirror/contracts/models.mjs +0 -1
  150. package/dist/lib/mirror/contracts/tokens.mjs +0 -11
  151. package/dist/lib/mirror/domain/types.domain.mjs +0 -1
  152. package/dist/lib/mirror/infrastructure/mirror-console-reporter.presenter.mjs +0 -99
  153. package/dist/lib/mirror/infrastructure/package-filter-resolver.service.mjs +0 -25
  154. package/dist/lib/mirror/infrastructure/workspace-packages.service.mjs +0 -158
  155. package/dist/lib/mirror/mirror.module.mjs +0 -25
  156. package/dist/lib/shared/source-code/infrastructure/typescript-source-file-walker.service.mjs +0 -33
  157. package/dist/lib/tag/adapters/primary/cli/presentation.tokens.mjs +0 -6
  158. package/dist/lib/tag/adapters/primary/cli/tag.command.mjs +0 -285
  159. package/dist/lib/tag/adapters/primary/cli/tag.presentation.module.mjs +0 -13
  160. package/dist/lib/tag/adapters/secondary/tag-since-writer.adapter.mjs +0 -348
  161. package/dist/lib/tag/adapters/secondary/tag-target-resolver.adapter.mjs +0 -233
  162. package/dist/lib/tag/adapters/secondary/tag-version-resolver.adapter.mjs +0 -249
  163. package/dist/lib/tag/adapters/secondary/typescript-tree-walk.adapter.mjs +0 -233
  164. package/dist/lib/tag/application/ports/tag-since-writer.port.mjs +0 -1
  165. package/dist/lib/tag/application/ports/tag-version-resolver.port.mjs +0 -1
  166. package/dist/lib/tag/application/ports/target-resolver.port.mjs +0 -1
  167. package/dist/lib/tag/application/ports/typescript-tree-walk.port.mjs +0 -1
  168. package/dist/lib/tag/application/requests/tag-sync.request.mjs +0 -1
  169. package/dist/lib/tag/application/services/tag-cli-target-path-resolver.service.mjs +0 -9
  170. package/dist/lib/tag/application/services/tag-target-runner.service.mjs +0 -21
  171. package/dist/lib/tag/application/tag-sync-cli-result.mjs +0 -11
  172. package/dist/lib/tag/application/tag-sync-json.format.mjs +0 -14
  173. package/dist/lib/tag/application/use-cases/prepare-tag-sync.use-case.mjs +0 -264
  174. package/dist/lib/tag/application/use-cases/run-tag-sync.use-case.mjs +0 -381
  175. package/dist/lib/tag/contracts/models.mjs +0 -1
  176. package/dist/lib/tag/contracts/tag-sync-result-presenter.contract.mjs +0 -1
  177. package/dist/lib/tag/contracts/tokens.mjs +0 -10
  178. package/dist/lib/tag/domain/types.domain.mjs +0 -1
  179. package/dist/lib/tag/infrastructure/tag-target-candidates.adapter.mjs +0 -170
  180. package/dist/lib/tag/presentation/colors.presenter.mjs +0 -12
  181. package/dist/lib/tag/presentation/present-tag-sync-result.presenter.mjs +0 -233
  182. package/dist/lib/tag/presentation/tag-sync-progress-listener.presenter.mjs +0 -243
  183. package/dist/lib/tag/tag.module.mjs +0 -23
  184. /package/dist/{lib/arrange/application/ports/domain-source-parser.port.mjs → arrange/domain/types.mjs} +0 -0
  185. /package/dist/{lib/arrange/application/ports/file-walker.port.mjs → core/filesystem/port.mjs} +0 -0
  186. /package/dist/{lib/arrange/application/ports/group-file-preview.port.mjs → mirror/domain/dist-filesystem.mjs} +0 -0
  187. /package/dist/{lib/arrange/application/ports/workspace-resolver.port.mjs → mirror/domain/types.mjs} +0 -0
  188. /package/dist/{lib/arrange/application/requests/analyze-directory.request.mjs → mirror/sync-types.mjs} +0 -0
  189. /package/dist/{lib/arrange/application/requests/arrange-sync.request.mjs → tag/domain/types.mjs} +0 -0
@@ -0,0 +1,177 @@
1
+ import { messageFrom } from "../errors.mjs";
2
+ import { logger } from "../logger.mjs";
3
+ import { globSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import picomatch from "picomatch";
7
+ import { parse } from "yaml";
8
+ //#region src/core/workspace/resolver.ts
9
+ /**
10
+ * Resolve the monorepo root (directory containing `pnpm-workspace.yaml`).
11
+ *
12
+ * @since 0.3.16-canary.0
13
+ */
14
+ function findRepoRoot(fromDirectory, fs) {
15
+ const candidates = [path.dirname(fileURLToPath(import.meta.url)), fromDirectory];
16
+ for (const start of candidates) {
17
+ let dir = path.resolve(start);
18
+ for (;;) {
19
+ if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml"))) return dir;
20
+ const parent = path.dirname(dir);
21
+ if (parent === dir) break;
22
+ dir = parent;
23
+ }
24
+ }
25
+ throw new Error(`Could not locate monorepo root (missing pnpm-workspace.yaml). Searched from: ${candidates.join(", ")}`);
26
+ }
27
+ const workspaceYamlFileName = "pnpm-workspace.yaml";
28
+ const defaultIncludePatterns = ["packages/*"];
29
+ const packageJsonFileName = "package.json";
30
+ function toPosix(filePath) {
31
+ return filePath.split(path.sep).join("/");
32
+ }
33
+ function isGlobPermissionError(caughtError) {
34
+ if (typeof caughtError !== "object" || caughtError === null || !("code" in caughtError)) return false;
35
+ const code = caughtError.code;
36
+ return code === "EACCES" || code === "EPERM";
37
+ }
38
+ function workspacePatternToPackageJsonGlob(pattern) {
39
+ const normalizedPattern = toPosix(pattern.trim()).replace(/\/+$/, "");
40
+ if (!normalizedPattern) return `**/${packageJsonFileName}`;
41
+ return `${normalizedPattern}/${packageJsonFileName}`;
42
+ }
43
+ function splitPnpmWorkspacePackagesArray(raw) {
44
+ const include = [];
45
+ const exclude = [];
46
+ if (!Array.isArray(raw)) return {
47
+ include,
48
+ exclude
49
+ };
50
+ for (const entry of raw) {
51
+ if (typeof entry !== "string") continue;
52
+ const trimmed = entry.trim();
53
+ if (!trimmed) continue;
54
+ if (trimmed.startsWith("!")) exclude.push(trimmed.slice(1).trim());
55
+ else include.push(trimmed);
56
+ }
57
+ return {
58
+ include,
59
+ exclude
60
+ };
61
+ }
62
+ function parsePnpmWorkspaceDocument(doc) {
63
+ const wf = workspaceYamlFileName;
64
+ if (doc === null || doc === void 0) throw new Error(`${wf} root document must be an object (got empty or null)`);
65
+ if (typeof doc !== "object" || Array.isArray(doc)) throw new Error(`${wf} root must be a mapping, not an array or scalar`);
66
+ const parsedDoc = doc;
67
+ if (!Object.prototype.hasOwnProperty.call(parsedDoc, "packages")) return {
68
+ include: [],
69
+ exclude: [],
70
+ hasPackagesKey: false,
71
+ isEmptyPackagesArray: false
72
+ };
73
+ const pkgs = parsedDoc.packages;
74
+ if (!Array.isArray(pkgs)) throw new Error(`${wf} field "packages" must be an array`);
75
+ if (pkgs.length === 0) return {
76
+ include: [],
77
+ exclude: [],
78
+ hasPackagesKey: true,
79
+ isEmptyPackagesArray: true
80
+ };
81
+ return {
82
+ ...splitPnpmWorkspacePackagesArray(pkgs),
83
+ hasPackagesKey: true,
84
+ isEmptyPackagesArray: false
85
+ };
86
+ }
87
+ async function readWorkspaceYaml(rootDir, fs) {
88
+ const wf = workspaceYamlFileName;
89
+ const workspaceYamlPath = path.join(rootDir, wf);
90
+ if (!fs.existsSync(workspaceYamlPath)) return { exists: false };
91
+ let raw;
92
+ try {
93
+ raw = await fs.readFile(workspaceYamlPath, "utf8");
94
+ } catch (caughtReadError) {
95
+ throw new Error(`Failed to read ${wf}: ${messageFrom(caughtReadError)}`);
96
+ }
97
+ let doc;
98
+ try {
99
+ doc = parse(raw);
100
+ } catch (caughtYamlParseError) {
101
+ throw new Error(`Failed to parse ${wf}: ${messageFrom(caughtYamlParseError)}`);
102
+ }
103
+ if (doc === null || doc === void 0) throw new Error(`${wf} must define a mapping at the document root`);
104
+ return {
105
+ exists: true,
106
+ doc
107
+ };
108
+ }
109
+ /**
110
+ * @since 0.3.16-canary.0
111
+ */
112
+ async function listWorkspacePackageDirectories(rootDirectoryPathAbsolute, fs, suppressGlobPermissionDiagnostics) {
113
+ const workspaceYaml = await readWorkspaceYaml(rootDirectoryPathAbsolute, fs);
114
+ const defInc = defaultIncludePatterns;
115
+ let include;
116
+ let exclude;
117
+ let layoutSource;
118
+ const hasPnpmWorkspaceYamlFile = workspaceYaml.exists;
119
+ if (!workspaceYaml.exists) {
120
+ include = [...defInc];
121
+ exclude = [];
122
+ layoutSource = "default-patterns";
123
+ } else {
124
+ const parsed = parsePnpmWorkspaceDocument(workspaceYaml.doc);
125
+ if (!parsed.hasPackagesKey) {
126
+ include = [...defInc];
127
+ exclude = [];
128
+ layoutSource = "default-patterns";
129
+ } else if (parsed.isEmptyPackagesArray) return {
130
+ packageDirectoryPathsAbsolute: [],
131
+ layoutSource: "declared-empty",
132
+ hasPnpmWorkspaceYamlFile: true
133
+ };
134
+ else {
135
+ include = parsed.include;
136
+ exclude = parsed.exclude;
137
+ layoutSource = "pnpm-workspace-yaml";
138
+ }
139
+ }
140
+ const foundRelativePosixPackageRoots = /* @__PURE__ */ new Set();
141
+ const globOpts = {
142
+ cwd: rootDirectoryPathAbsolute,
143
+ posix: true,
144
+ ...path.sep === "\\" ? { windowsPathsNoEscape: true } : {}
145
+ };
146
+ for (const pattern of include) {
147
+ const globPat = workspacePatternToPackageJsonGlob(pattern);
148
+ let matches;
149
+ try {
150
+ matches = globSync(globPat, globOpts);
151
+ } catch (caughtGlobError) {
152
+ if (isGlobPermissionError(caughtGlobError)) {
153
+ if (!suppressGlobPermissionDiagnostics) logger.out(`⚠ Skipping workspace glob "${pattern}" (${messageFrom(caughtGlobError)})`);
154
+ continue;
155
+ }
156
+ throw new Error(`Invalid workspace glob "${pattern}": ${messageFrom(caughtGlobError)}`);
157
+ }
158
+ for (const matchedPath of matches) {
159
+ const posix = toPosix(matchedPath);
160
+ const suffix = `/${packageJsonFileName}`;
161
+ if (!posix.endsWith(suffix)) continue;
162
+ const rel = posix.slice(0, -suffix.length);
163
+ if (!rel) continue;
164
+ foundRelativePosixPackageRoots.add(rel);
165
+ }
166
+ }
167
+ const excludeMatchers = exclude.map((pat) => picomatch(pat, { dot: true }));
168
+ const filteredRelative = [...foundRelativePosixPackageRoots].filter((rel) => !excludeMatchers.some((isMatch) => isMatch(rel)));
169
+ filteredRelative.sort((a, b) => a.localeCompare(b));
170
+ return {
171
+ packageDirectoryPathsAbsolute: filteredRelative.map((rel) => path.resolve(rootDirectoryPathAbsolute, rel.split("/").join(path.sep))),
172
+ layoutSource,
173
+ hasPnpmWorkspaceYamlFile
174
+ };
175
+ }
176
+ //#endregion
177
+ export { findRepoRoot, listWorkspacePackageDirectories };
@@ -0,0 +1,36 @@
1
+ import path from "node:path";
2
+ //#region src/core/workspace/typescript-walk.ts
3
+ const defaultSkipDirectoryNames = new Set([
4
+ "node_modules",
5
+ "dist",
6
+ ".git",
7
+ ".turbo",
8
+ ".next",
9
+ ".cache",
10
+ "out",
11
+ "build",
12
+ "coverage",
13
+ ".vercel",
14
+ ".output"
15
+ ]);
16
+ /**
17
+ * @since 0.3.16-canary.0
18
+ */
19
+ function walkTsxFiles(rootDirectoryPath, fs) {
20
+ const result = [];
21
+ visitTsxPaths(result, rootDirectoryPath, fs);
22
+ return result;
23
+ }
24
+ function visitTsxPaths(result, entryPath, fs) {
25
+ if (fs.statSync(entryPath).isDirectory()) {
26
+ for (const childName of fs.readdirSync(entryPath)) {
27
+ if (defaultSkipDirectoryNames.has(childName)) continue;
28
+ visitTsxPaths(result, path.join(entryPath, childName), fs);
29
+ }
30
+ return;
31
+ }
32
+ if (entryPath.endsWith(".d.ts")) return;
33
+ if (entryPath.endsWith(".tsx") || entryPath.endsWith(".ts")) result.push(entryPath);
34
+ }
35
+ //#endregion
36
+ export { walkTsxFiles };
@@ -0,0 +1,21 @@
1
+ import "../core/exit-codes.mjs";
2
+ //#region src/mirror/cli-result.ts
3
+ /**
4
+ * @since 0.3.16-canary.0
5
+ */
6
+ function exitCodeForMirrorSyncResult(stats) {
7
+ return stats.packagesErrored > 0 ? 1 : 0;
8
+ }
9
+ /**
10
+ * @since 0.3.16-canary.0
11
+ */
12
+ function formatMirrorSyncJsonOutput(stats, elapsedSeconds) {
13
+ return JSON.stringify({
14
+ schemaVersion: 1,
15
+ ok: stats.packagesErrored === 0,
16
+ elapsedSeconds,
17
+ stats
18
+ });
19
+ }
20
+ //#endregion
21
+ export { exitCodeForMirrorSyncResult, formatMirrorSyncJsonOutput };
@@ -1,10 +1,10 @@
1
1
  import { z } from "zod";
2
- //#region src/lib/mirror/presentation/mirror-cli.schema.ts
2
+ //#region src/mirror/cli-schema.ts
3
+ /**
4
+ * @since 0.3.16-canary.0
5
+ */
3
6
  const mirrorSyncRunRequestSchema = z.object({
4
7
  rootDir: z.string().min(1),
5
- verbose: z.boolean().optional(),
6
- noColor: z.boolean().optional(),
7
- json: z.boolean().optional(),
8
8
  packageFilter: z.string().optional(),
9
9
  config: z.unknown().optional()
10
10
  });
@@ -0,0 +1,57 @@
1
+ import { parseWithSchema } from "../core/schema-parse.mjs";
2
+ import { logger } from "../core/logger.mjs";
3
+ import { consumeCliAppError } from "../core/cli/result-handle.mjs";
4
+ import { nodeFilesystem } from "../core/filesystem/node.mjs";
5
+ import { readOptionalPositionalArg } from "../core/cli/positional.mjs";
6
+ import { globalCliCommanderOptionsSchema } from "../core/cli/global-options.mjs";
7
+ import { prepareMirrorSync } from "./prepare.mjs";
8
+ import { runMirrorSync } from "./sync.mjs";
9
+ import { mirrorSyncRunRequestSchema } from "./cli-schema.mjs";
10
+ import { exitCodeForMirrorSyncResult, formatMirrorSyncJsonOutput } from "./cli-result.mjs";
11
+ import { MirrorSyncProgressPresenter } from "./output.mjs";
12
+ import process from "node:process";
13
+ import { Command } from "commander";
14
+ //#region src/mirror/command.ts
15
+ /**
16
+ * @since 0.3.16-canary.0
17
+ */
18
+ function createMirrorCommand() {
19
+ const cmd = new Command("mirror").description("Keep package manifests aligned with what you ship");
20
+ cmd.command("sync").description("Write package.json exports from dist/ for workspace packages").argument("[package]", "Optional package path relative to repo root (e.g. packages/ui)").option("-v, --verbose", "Print extra diagnostics", false).option("--json", "Print one JSON summary on stdout (suppresses human progress)", false).action(async (packageArg, opts, command) => {
21
+ const globalOptionsOutcome = parseWithSchema(globalCliCommanderOptionsSchema, command.optsWithGlobals?.() ?? command.opts());
22
+ if (!consumeCliAppError(globalOptionsOutcome)) return;
23
+ const prelude = await prepareMirrorSync(nodeFilesystem, {
24
+ currentWorkingDirectory: process.cwd(),
25
+ packageArg: readOptionalPositionalArg(packageArg),
26
+ globals: globalOptionsOutcome.value
27
+ });
28
+ if (!consumeCliAppError(prelude)) return;
29
+ const { rootDir, config, packageFilter } = prelude.value;
30
+ const parsed = parseWithSchema(mirrorSyncRunRequestSchema, {
31
+ rootDir,
32
+ config: config.mirror ?? {},
33
+ packageFilter
34
+ });
35
+ if (!consumeCliAppError(parsed)) return;
36
+ const json = !!opts.json;
37
+ const noColor = globalOptionsOutcome.value.color === false;
38
+ const verbose = !!opts.verbose;
39
+ const progressPresenter = new MirrorSyncProgressPresenter();
40
+ const listener = json ? void 0 : progressPresenter;
41
+ if (!json) progressPresenter.configure({
42
+ noColor,
43
+ verbose
44
+ });
45
+ const startTime = performance.now();
46
+ const outcome = await runMirrorSync(nodeFilesystem, {
47
+ ...parsed.value,
48
+ listener
49
+ });
50
+ if (!consumeCliAppError(outcome)) return;
51
+ if (json) logger.out(formatMirrorSyncJsonOutput(outcome.value, (performance.now() - startTime) / 1e3));
52
+ process.exitCode = exitCodeForMirrorSyncResult(outcome.value);
53
+ });
54
+ return cmd;
55
+ }
56
+ //#endregion
57
+ export { createMirrorCommand };
@@ -0,0 +1,42 @@
1
+ import { normalizePath } from "./domain/path-normalizer.mjs";
2
+ import { isDirentList } from "./domain/dirent-guard.mjs";
3
+ import path from "node:path";
4
+ //#region src/mirror/dist-filesystem-impl.ts
5
+ /**
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ function createMirrorDistFilesystem(fs) {
9
+ return {
10
+ async listRelativeFilesRecursively(dirPath) {
11
+ try {
12
+ const raw = await fs.readdir(dirPath, {
13
+ recursive: true,
14
+ withFileTypes: true
15
+ });
16
+ if (!isDirentList(raw)) return [];
17
+ return raw.filter((dirent) => dirent.isFile()).map((dirent) => {
18
+ const fullPath = path.join(dirent.parentPath, dirent.name);
19
+ return normalizePath(path.relative(dirPath, fullPath));
20
+ });
21
+ } catch (caughtError) {
22
+ if (isKnownReadDirError(caughtError)) return [];
23
+ throw caughtError;
24
+ }
25
+ },
26
+ async isDirectoryCssOnly(distDir, dirPath) {
27
+ try {
28
+ const raw = await fs.readdir(path.join(distDir, dirPath), { withFileTypes: true });
29
+ if (!isDirentList(raw)) return false;
30
+ if (raw.length === 0) return true;
31
+ return raw.every((dirent) => dirent.isFile() && dirent.name.endsWith(".css"));
32
+ } catch {
33
+ return false;
34
+ }
35
+ }
36
+ };
37
+ }
38
+ function isKnownReadDirError(caughtError) {
39
+ return typeof caughtError === "object" && caughtError !== null && "code" in caughtError && (caughtError.code === "ENOENT" || caughtError.code === "EACCES");
40
+ }
41
+ //#endregion
42
+ export { createMirrorDistFilesystem };
@@ -1,16 +1,31 @@
1
- //#region src/lib/mirror/domain/constants.domain.ts
1
+ //#region src/mirror/domain/constants.ts
2
+ /**
3
+ * @since 0.3.16-canary.0
4
+ */
2
5
  const DIST_DIR = "dist";
6
+ /**
7
+ * @since 0.3.16-canary.0
8
+ */
3
9
  const PACKAGE_JSON = "package.json";
10
+ /**
11
+ * @since 0.3.16-canary.0
12
+ */
4
13
  const VALID_JS_EXTENSIONS = new Set([
5
14
  ".js",
6
15
  ".mjs",
7
16
  ".cjs"
8
17
  ]);
18
+ /**
19
+ * @since 0.3.16-canary.0
20
+ */
9
21
  const VALID_DTS_EXTENSIONS = new Set([
10
22
  ".d.ts",
11
23
  ".d.mts",
12
24
  ".d.cts"
13
25
  ]);
26
+ /**
27
+ * @since 0.3.16-canary.0
28
+ */
14
29
  const PACKAGE_JSON_EXPORT = "./package.json";
15
30
  //#endregion
16
31
  export { DIST_DIR, PACKAGE_JSON, PACKAGE_JSON_EXPORT, VALID_DTS_EXTENSIONS, VALID_JS_EXTENSIONS };
@@ -1,10 +1,13 @@
1
- //#region src/lib/mirror/infrastructure/dirent-list.guard.ts
1
+ //#region src/mirror/domain/dirent-guard.ts
2
2
  /**
3
3
  * Narrow `fs.promises.readdir` overload result to `Dirent[]` when `withFileTypes: true`.
4
4
  * Accepts both historical checks (`isFile` / `isDirectory`) in one guard.
5
5
  * Returning `true` for an empty array is intentional: callers treat empty lists
6
6
  * the same regardless of the element type, so `[]` is safely accepted as `Dirent[]`.
7
- */ function isDirentList(x) {
7
+ *
8
+ * @since 0.3.16-canary.0
9
+ */
10
+ function isDirentList(x) {
8
11
  if (x.length === 0) return true;
9
12
  const first = x[0];
10
13
  return typeof first === "object" && first !== null && "isFile" in first && "isDirectory" in first;
@@ -1,8 +1,14 @@
1
- //#region src/lib/mirror/domain/errors.domain.ts
1
+ //#region src/mirror/domain/errors.ts
2
+ /**
3
+ * @since 0.3.16-canary.0
4
+ */
2
5
  const MirrorErrorCode = {
3
6
  FATAL: "FATAL",
4
7
  PACKAGE_WRITE: "PACKAGE_WRITE"
5
8
  };
9
+ /**
10
+ * @since 0.3.16-canary.0
11
+ */
6
12
  var MirrorError = class extends Error {
7
13
  code;
8
14
  constructor(code, message, options) {
@@ -1,10 +1,11 @@
1
- import { PACKAGE_JSON_EXPORT, VALID_DTS_EXTENSIONS, VALID_JS_EXTENSIONS } from "../../domain/constants.domain.mjs";
2
- //#region src/lib/mirror/application/services/generate-mirror-exports.service.ts
1
+ import { PACKAGE_JSON_EXPORT, VALID_DTS_EXTENSIONS, VALID_JS_EXTENSIONS } from "./constants.mjs";
2
+ import * as nodePath from "node:path";
3
+ //#region src/mirror/domain/exports.ts
3
4
  function resolvePackageScopedConfig(configMap, pkgMeta) {
4
5
  if (!configMap) return;
5
6
  return configMap[pkgMeta.packageName];
6
7
  }
7
- function groupFilesByModule(files, pathService) {
8
+ function groupFilesByModule(files) {
8
9
  const modules = /* @__PURE__ */ new Map();
9
10
  for (const file of files) {
10
11
  let ext;
@@ -14,11 +15,11 @@ function groupFilesByModule(files, pathService) {
14
15
  ext = dtsExt;
15
16
  modulePath = file.slice(0, -dtsExt.length);
16
17
  } else {
17
- ext = pathService.extname(file);
18
+ ext = nodePath.extname(file);
18
19
  if (!VALID_JS_EXTENSIONS.has(ext)) continue;
19
20
  modulePath = file.slice(0, -ext.length);
20
21
  }
21
- if (!pathService.basename(modulePath)) continue;
22
+ if (!nodePath.basename(modulePath)) continue;
22
23
  let distModule = modules.get(modulePath);
23
24
  if (!distModule) {
24
25
  distModule = {
@@ -103,6 +104,9 @@ function getExportGroup(exportPath, pathTransform) {
103
104
  1
104
105
  ];
105
106
  }
107
+ /**
108
+ * @since 0.3.16-canary.0
109
+ */
106
110
  function createPathTransform(config, pkgMeta) {
107
111
  const pathConfig = resolvePackageScopedConfig(config?.pathTransformations, pkgMeta);
108
112
  if (!pathConfig) return null;
@@ -131,7 +135,7 @@ function compareTuples(left, right) {
131
135
  if (left[3] !== right[3]) return left[3] < right[3] ? -1 : 1;
132
136
  return 0;
133
137
  }
134
- async function generateCssExports(pathService, fileSystemService, distDir, cssConfig) {
138
+ async function generateCssExports(fileSystemService, distDir, cssConfig) {
135
139
  if (cssConfig === false) return {};
136
140
  if (cssConfig === true) cssConfig = { enabled: true };
137
141
  if (!cssConfig || cssConfig.enabled === false) return {};
@@ -141,7 +145,7 @@ async function generateCssExports(pathService, fileSystemService, distDir, cssCo
141
145
  const cssByDir = /* @__PURE__ */ new Map();
142
146
  const rootCss = [];
143
147
  for (const file of cssFiles) {
144
- const dirName = pathService.dirname(file).split(pathService.separator).join("/");
148
+ const dirName = nodePath.dirname(file).split(nodePath.sep).join("/");
145
149
  if (dirName === ".") rootCss.push(file);
146
150
  else {
147
151
  const arr = cssByDir.get(dirName) || [];
@@ -164,7 +168,10 @@ async function generateCssExports(pathService, fileSystemService, distDir, cssCo
164
168
  }
165
169
  /**
166
170
  * Compute `package.json#exports` from a built `dist/` tree. No logging; no writes.
167
- */ async function generateExports(pathService, fileSystemService, distDir, pathTransform, cssConfig, customExports) {
171
+ *
172
+ * @since 0.3.16-canary.0
173
+ */
174
+ async function generateExports(fileSystemService, distDir, pathTransform, cssConfig, customExports) {
168
175
  const files = await fileSystemService.listRelativeFilesRecursively(distDir);
169
176
  if (!files.length) return {
170
177
  exports: { [PACKAGE_JSON_EXPORT]: PACKAGE_JSON_EXPORT },
@@ -172,7 +179,7 @@ async function generateCssExports(pathService, fileSystemService, distDir, cssCo
172
179
  jsCount: 0,
173
180
  cssCount: 0
174
181
  };
175
- const modules = groupFilesByModule(files, pathService);
182
+ const modules = groupFilesByModule(files);
176
183
  const validModules = Array.from(modules.values()).filter((moduleEntry) => (moduleEntry.files.js || moduleEntry.files.mjs) && moduleEntry.files.dts);
177
184
  if (!validModules.length) return {
178
185
  exports: { [PACKAGE_JSON_EXPORT]: PACKAGE_JSON_EXPORT },
@@ -198,7 +205,7 @@ async function generateCssExports(pathService, fileSystemService, distDir, cssCo
198
205
  let sortedKeys = Object.keys(exportsObj).sort((pathA, pathB) => compareTuples(getSortTuple(pathA, pathTransform), getSortTuple(pathB, pathTransform)));
199
206
  const sortedExports = {};
200
207
  for (const exportKey of sortedKeys) sortedExports[exportKey] = exportsObj[exportKey];
201
- const cssExports = await generateCssExports(pathService, fileSystemService, distDir, cssConfig ?? { enabled: true });
208
+ const cssExports = await generateCssExports(fileSystemService, distDir, cssConfig ?? { enabled: true });
202
209
  Object.assign(sortedExports, cssExports);
203
210
  for (const [specifier, mappedPath] of Object.entries(customExports || {})) if (specifier !== "./package.json") sortedExports[specifier] = mappedPath;
204
211
  for (const cssSpecifier of Object.keys(cssExports)) if (!(cssSpecifier in originalPathBySpecifier)) originalPathBySpecifier[cssSpecifier] = cssSpecifier;
@@ -1,7 +1,10 @@
1
- //#region src/lib/mirror/domain/package-display-name.policy.ts
1
+ //#region src/mirror/domain/package-display-name.ts
2
2
  /**
3
3
  * Domain rule: prefer `package.json#name` when it is present, otherwise use folder basename.
4
- */ function resolvePackageDisplayName(packageJson, folderBasename) {
4
+ *
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ function resolvePackageDisplayName(packageJson, folderBasename) {
5
8
  const declaredName = packageJson.name;
6
9
  return typeof declaredName === "string" && declaredName.length > 0 ? declaredName : folderBasename;
7
10
  }
@@ -1,5 +1,8 @@
1
1
  import path from "node:path";
2
- //#region src/lib/mirror/domain/path-normalizer.value-object.ts
2
+ //#region src/mirror/domain/path-normalizer.ts
3
+ /**
4
+ * @since 0.3.16-canary.0
5
+ */
3
6
  function normalizePath(pathString) {
4
7
  return pathString.split(path.sep).join("/").replace(/\\/g, "/");
5
8
  }
@@ -0,0 +1,45 @@
1
+ import { logger } from "../core/logger.mjs";
2
+ import { MirrorSyncReporter } from "./sync-reporter.mjs";
3
+ //#region src/mirror/output.ts
4
+ const cliLogger = logger;
5
+ /**
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ var MirrorSyncProgressPresenter = class {
9
+ reporter = new MirrorSyncReporter();
10
+ verbose = false;
11
+ configure(options) {
12
+ this.verbose = options.verbose;
13
+ this.reporter.configureMirrorColors(options.noColor);
14
+ }
15
+ onBanner() {
16
+ this.reporter.mirrorBanner(cliLogger);
17
+ }
18
+ onProcessingMode(mode) {
19
+ this.reporter.mirrorProcessingMode(cliLogger, mode);
20
+ }
21
+ onNoPackages() {
22
+ this.reporter.mirrorNoPackages(cliLogger);
23
+ }
24
+ onPackageComplete(pkgStats, ordinal, total) {
25
+ if (pkgStats.skipped) {
26
+ this.reporter.logSkippedWorkspacePackage(cliLogger, ordinal, total, pkgStats.name, pkgStats.skipReason);
27
+ return;
28
+ }
29
+ if (pkgStats.error !== null) {
30
+ this.reporter.logPackageError(cliLogger, ordinal, total, pkgStats.name, pkgStats.error, this.verbose);
31
+ return;
32
+ }
33
+ for (const exportSpecifier of pkgStats.prunedExportKeys) this.reporter.logPrunedStaleExport(cliLogger, exportSpecifier);
34
+ this.reporter.logPackageSuccess(cliLogger, ordinal, total, pkgStats, {
35
+ jsCount: pkgStats.jsModules,
36
+ cssCount: pkgStats.cssExports
37
+ }, this.verbose);
38
+ }
39
+ onComplete(stats, elapsedSeconds) {
40
+ this.reporter.mirrorSummarySeparator(cliLogger);
41
+ this.reporter.mirrorSummary(cliLogger, stats, elapsedSeconds);
42
+ }
43
+ };
44
+ //#endregion
45
+ export { MirrorSyncProgressPresenter };
@@ -0,0 +1,32 @@
1
+ import { AppError } from "../core/errors.mjs";
2
+ import { err, ok } from "../core/result.mjs";
3
+ import { normalizePath } from "./domain/path-normalizer.mjs";
4
+ import path from "node:path";
5
+ //#region src/mirror/package-path.ts
6
+ /**
7
+ * @since 0.3.16-canary.0
8
+ */
9
+ function resolveMirrorPackageFromCliArg(fs, args) {
10
+ const { rootDir, packageArg, currentWorkingDirectory } = args;
11
+ if (!packageArg) return ok(void 0);
12
+ const rootReal = fs.canonicalPathSync(path.resolve(rootDir));
13
+ const cwdReal = fs.canonicalPathSync(currentWorkingDirectory);
14
+ const resolved = path.isAbsolute(packageArg) ? path.resolve(packageArg) : path.resolve(cwdReal, packageArg);
15
+ const targetReal = fs.canonicalPathSync(resolved);
16
+ const normalized = normalizePath(path.relative(rootReal, targetReal));
17
+ if (normalized.startsWith("..") || path.isAbsolute(normalized) || normalized === "" || normalized === ".") return err(new AppError("VALIDATION_ERROR", `Package path must be a subdirectory under monorepo root: ${rootDir}`));
18
+ return ok(normalized);
19
+ }
20
+ /**
21
+ * @since 0.3.16-canary.0
22
+ */
23
+ function resolvePackageFilterUnderRoot(fs, rootDir, packageFilter) {
24
+ const rootResolved = path.resolve(rootDir);
25
+ const rootReal = fs.canonicalPathSync(rootResolved);
26
+ const resolved = path.isAbsolute(packageFilter) ? fs.canonicalPathSync(path.resolve(packageFilter)) : fs.canonicalPathSync(path.join(rootReal, packageFilter));
27
+ const normalized = normalizePath(path.relative(rootReal, resolved));
28
+ if (normalized.startsWith("..") || path.isAbsolute(normalized) || normalized === "" || normalized === ".") return err(new AppError("VALIDATION_ERROR", `Package path must be a subdirectory under monorepo root: ${rootDir}`));
29
+ return ok(normalized);
30
+ }
31
+ //#endregion
32
+ export { resolveMirrorPackageFromCliArg, resolvePackageFilterUnderRoot };
@@ -0,0 +1,33 @@
1
+ import { AppError, messageFrom } from "../core/errors.mjs";
2
+ import { err, ok } from "../core/result.mjs";
3
+ import { loadCodefastConfig } from "../core/config.mjs";
4
+ import { findRepoRoot } from "../core/workspace/resolver.mjs";
5
+ import { resolveMirrorPackageFromCliArg } from "./package-path.mjs";
6
+ //#region src/mirror/prepare.ts
7
+ /**
8
+ * @since 0.3.16-canary.0
9
+ */
10
+ async function prepareMirrorSync(fs, args) {
11
+ let rootDir;
12
+ try {
13
+ rootDir = findRepoRoot(args.currentWorkingDirectory, fs);
14
+ } catch (caughtError) {
15
+ return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
16
+ }
17
+ const filterOutcome = resolveMirrorPackageFromCliArg(fs, {
18
+ rootDir,
19
+ packageArg: args.packageArg,
20
+ currentWorkingDirectory: args.currentWorkingDirectory
21
+ });
22
+ if (!filterOutcome.ok) return filterOutcome;
23
+ const loadedOutcome = await loadCodefastConfig(rootDir, fs);
24
+ if (!loadedOutcome.ok) return loadedOutcome;
25
+ return ok({
26
+ globals: args.globals,
27
+ rootDir,
28
+ config: loadedOutcome.value.config,
29
+ packageFilter: filterOutcome.value
30
+ });
31
+ }
32
+ //#endregion
33
+ export { prepareMirrorSync };