@codefast/cli 0.3.13-canary.0 → 0.3.13-canary.1

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 (112) hide show
  1. package/dist/bin.mjs +1 -1
  2. package/dist/commands/arrange.mjs +106 -1
  3. package/dist/commands/mirror.mjs +65 -1
  4. package/dist/commands/tag.mjs +57 -1
  5. package/dist/lib/arrange/application/analyze.mjs +91 -1
  6. package/dist/lib/arrange/application/group-file.mjs +112 -1
  7. package/dist/lib/arrange/application/run-target.mjs +64 -1
  8. package/dist/lib/arrange/domain/ast/ast-helpers.mjs +114 -1
  9. package/dist/lib/arrange/domain/ast/collectors-cn.mjs +60 -1
  10. package/dist/lib/arrange/domain/ast/collectors-jsx.mjs +19 -1
  11. package/dist/lib/arrange/domain/ast/collectors-tv.mjs +169 -1
  12. package/dist/lib/arrange/domain/ast/targets.mjs +105 -1
  13. package/dist/lib/arrange/domain/constants.mjs +168 -1
  14. package/dist/lib/arrange/domain/errors.mjs +18 -1
  15. package/dist/lib/arrange/domain/grouping.mjs +234 -1
  16. package/dist/lib/arrange/domain/imports.mjs +53 -1
  17. package/dist/lib/arrange/domain/tokenizer.mjs +215 -1
  18. package/dist/lib/arrange/index.mjs +14 -15
  19. package/dist/lib/arrange/infra/walk.mjs +32 -1
  20. package/dist/lib/arrange/presentation/formatters.mjs +51 -1
  21. package/dist/lib/arrange/presentation/report.mjs +23 -1
  22. package/dist/lib/config/domain/schema.mjs +24 -1
  23. package/dist/lib/config/index.mjs +2 -3
  24. package/dist/lib/config/infra/loader.mjs +77 -1
  25. package/dist/lib/infra/caught-unknown-message.mjs +19 -1
  26. package/dist/lib/infra/config-reporter.mjs +9 -1
  27. package/dist/lib/infra/node-io.mjs +29 -1
  28. package/dist/lib/infra/workspace/repo-root.mjs +21 -1
  29. package/dist/lib/mirror/application/engine.mjs +247 -1
  30. package/dist/lib/mirror/application/sync.mjs +161 -1
  31. package/dist/lib/mirror/domain/constants.mjs +11 -1
  32. package/dist/lib/mirror/domain/errors.mjs +14 -1
  33. package/dist/lib/mirror/index.mjs +1 -2
  34. package/dist/lib/mirror/infra/dirent-list.mjs +13 -1
  35. package/dist/lib/mirror/infra/package-filter.mjs +25 -1
  36. package/dist/lib/mirror/infra/path-normalizer.mjs +6 -1
  37. package/dist/lib/mirror/infra/update-pkg.mjs +104 -1
  38. package/dist/lib/mirror/infra/workspace-packages.mjs +158 -1
  39. package/dist/lib/mirror/presentation/reporter.mjs +111 -2
  40. package/dist/lib/tag/application/engine.mjs +286 -1
  41. package/dist/lib/tag/index.mjs +3 -4
  42. package/dist/lib/tag/infra/target-resolver.mjs +169 -1
  43. package/dist/lib/tag/presentation/colors.mjs +11 -1
  44. package/dist/lib/tag/presentation/tag-presenter.mjs +62 -1
  45. package/dist/program.mjs +33 -1
  46. package/package.json +2 -2
  47. package/dist/analyze-DvtJ3j9m.mjs +0 -92
  48. package/dist/arrange-Bbh62DrU.mjs +0 -107
  49. package/dist/arrange-BqP2DI3w.mjs +0 -8
  50. package/dist/ast-helpers-MDRYuIAk.mjs +0 -115
  51. package/dist/caught-unknown-message-CRhkDWO7.mjs +0 -20
  52. package/dist/collectors-cn-BS8TTQ8O.mjs +0 -61
  53. package/dist/collectors-jsx-D9tL2ZPM.mjs +0 -20
  54. package/dist/collectors-tv-D2PXZaRt.mjs +0 -170
  55. package/dist/colors-BjdkCfGK.mjs +0 -12
  56. package/dist/commands/mirror.test.mjs +0 -48
  57. package/dist/config-DVia_f9a.mjs +0 -3
  58. package/dist/config-reporter-SCcWDZc1.mjs +0 -10
  59. package/dist/constants-AtPhF-Ct.mjs +0 -169
  60. package/dist/constants-Bq5wdyLC.mjs +0 -8
  61. package/dist/dirent-list-BSDeRG5i.mjs +0 -14
  62. package/dist/engine-CcWEHSCq.mjs +0 -287
  63. package/dist/engine-DRTdkc5u.mjs +0 -236
  64. package/dist/errors-CFH4w6ic.mjs +0 -15
  65. package/dist/errors-CpU1ogfA.mjs +0 -19
  66. package/dist/formatters-DGE8jJ53.mjs +0 -52
  67. package/dist/group-file-ClW7XdD6.mjs +0 -113
  68. package/dist/grouping-c1grpCQT.mjs +0 -235
  69. package/dist/imports-DSVq1xhB.mjs +0 -54
  70. package/dist/lib/arrange/application/analyze.test.mjs +0 -171
  71. package/dist/lib/arrange/application/group-file.test.mjs +0 -273
  72. package/dist/lib/arrange/application/run-target.test.mjs +0 -97
  73. package/dist/lib/arrange/domain/ast/ast-helpers.test.mjs +0 -148
  74. package/dist/lib/arrange/domain/ast/collectors-cn.test.mjs +0 -58
  75. package/dist/lib/arrange/domain/ast/collectors-jsx.test.mjs +0 -20
  76. package/dist/lib/arrange/domain/ast/collectors-tv.test.mjs +0 -145
  77. package/dist/lib/arrange/domain/ast/targets.test.mjs +0 -50
  78. package/dist/lib/arrange/domain/grouping.test.mjs +0 -186
  79. package/dist/lib/arrange/domain/imports.test.mjs +0 -48
  80. package/dist/lib/arrange/domain/tokenizer.test.mjs +0 -165
  81. package/dist/lib/arrange/infra/walk.test.mjs +0 -50
  82. package/dist/lib/arrange/presentation/formatters.test.mjs +0 -73
  83. package/dist/lib/config/infra/loader.test.mjs +0 -116
  84. package/dist/lib/infra/workspace/repo-root.test.mjs +0 -45
  85. package/dist/lib/mirror/application/sync.test.mjs +0 -482
  86. package/dist/lib/mirror/infra/dirent-list.test.mjs +0 -18
  87. package/dist/lib/mirror/infra/package-filter.test.mjs +0 -32
  88. package/dist/lib/mirror/infra/workspace-packages.test.mjs +0 -167
  89. package/dist/lib/tag/application/engine.test.mjs +0 -159
  90. package/dist/lib/tag/presentation/tag-presenter.test.mjs +0 -29
  91. package/dist/loader-DpN-zl5g.mjs +0 -78
  92. package/dist/mirror-CbGiLXKT.mjs +0 -2
  93. package/dist/mirror-DqJE3ejL.mjs +0 -66
  94. package/dist/node-io-DFRbHO6m.mjs +0 -30
  95. package/dist/package-filter-C_8ryqvz.mjs +0 -26
  96. package/dist/path-normalizer-D4ceBO3l.mjs +0 -7
  97. package/dist/program-DR-H4tH3.mjs +0 -34
  98. package/dist/repo-root-HmtIEm6J.mjs +0 -22
  99. package/dist/report-DYxvw34T.mjs +0 -24
  100. package/dist/reporter-CwIe_tAM.mjs +0 -108
  101. package/dist/run-target-CJWrqNT4.mjs +0 -65
  102. package/dist/schema-Or8RAARd.mjs +0 -25
  103. package/dist/sync-e1Sp-shi.mjs +0 -157
  104. package/dist/tag-Cd9JujU5.mjs +0 -2
  105. package/dist/tag-hEKVZ_-7.mjs +0 -58
  106. package/dist/tag-presenter-Cs66UBsH.mjs +0 -63
  107. package/dist/target-resolver-CR3FXMTy.mjs +0 -170
  108. package/dist/targets-pCTslnFh.mjs +0 -106
  109. package/dist/tokenizer-CeZx73Zu.mjs +0 -216
  110. package/dist/update-pkg-CnGfjWBy.mjs +0 -37
  111. package/dist/walk-DjgQn66n.mjs +0 -33
  112. package/dist/workspace-packages-K2NYTHJq.mjs +0 -159
@@ -1,2 +1,287 @@
1
- import { n as runTagOnTarget, r as runTagSync, t as resolveNearestPackageVersion } from "../../../engine-CcWEHSCq.mjs";
1
+ import { messageFromCaughtUnknown } from "../../infra/caught-unknown-message.mjs";
2
+ import { applyEditsDescending, indentOfLineContaining } from "../../arrange/domain/ast/ast-helpers.mjs";
3
+ import { walkTsxFiles } from "../../arrange/infra/walk.mjs";
4
+ import "../../arrange/index.mjs";
5
+ import { resolveTagTargetCandidates } from "../infra/target-resolver.mjs";
6
+ import path from "node:path";
7
+ import ts from "typescript";
8
+ //#region src/lib/tag/application/engine.ts
9
+ const PACKAGE_JSON = "package.json";
10
+ const VERSION_TAG = "@since";
11
+ function hasExportModifier(node) {
12
+ return (ts.canHaveModifiers(node) ? ts.getModifiers(node) : void 0)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ?? false;
13
+ }
14
+ function isTaggableDeclaration(node) {
15
+ return ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isVariableStatement(node);
16
+ }
17
+ function addDeclarationName(registry, name, declaration) {
18
+ if (!registry.has(name)) {
19
+ registry.set(name, new Set([declaration]));
20
+ return;
21
+ }
22
+ registry.get(name).add(declaration);
23
+ }
24
+ function collectLocalNamedDeclarations(sf) {
25
+ const declarations = /* @__PURE__ */ new Map();
26
+ for (const statement of sf.statements) {
27
+ if (ts.isFunctionDeclaration(statement) && statement.name) {
28
+ addDeclarationName(declarations, statement.name.text, statement);
29
+ continue;
30
+ }
31
+ if (ts.isClassDeclaration(statement) && statement.name) {
32
+ addDeclarationName(declarations, statement.name.text, statement);
33
+ continue;
34
+ }
35
+ if (ts.isInterfaceDeclaration(statement)) {
36
+ addDeclarationName(declarations, statement.name.text, statement);
37
+ continue;
38
+ }
39
+ if (ts.isTypeAliasDeclaration(statement)) {
40
+ addDeclarationName(declarations, statement.name.text, statement);
41
+ continue;
42
+ }
43
+ if (ts.isEnumDeclaration(statement)) {
44
+ addDeclarationName(declarations, statement.name.text, statement);
45
+ continue;
46
+ }
47
+ if (!ts.isVariableStatement(statement)) continue;
48
+ for (const declaration of statement.declarationList.declarations) if (ts.isIdentifier(declaration.name)) addDeclarationName(declarations, declaration.name.text, statement);
49
+ }
50
+ return declarations;
51
+ }
52
+ function collectExportedDeclarations(sf) {
53
+ const exported = /* @__PURE__ */ new Set();
54
+ const localNamed = collectLocalNamedDeclarations(sf);
55
+ for (const statement of sf.statements) {
56
+ if (isTaggableDeclaration(statement) && hasExportModifier(statement)) {
57
+ exported.add(statement);
58
+ continue;
59
+ }
60
+ if (ts.isExportDeclaration(statement) && !statement.moduleSpecifier && statement.exportClause && ts.isNamedExports(statement.exportClause)) {
61
+ for (const element of statement.exportClause.elements) {
62
+ const localName = element.propertyName?.text ?? element.name.text;
63
+ const declarations = localNamed.get(localName);
64
+ if (!declarations) continue;
65
+ for (const declaration of declarations) exported.add(declaration);
66
+ }
67
+ continue;
68
+ }
69
+ if (!ts.isExportAssignment(statement) || !ts.isIdentifier(statement.expression)) continue;
70
+ const declarations = localNamed.get(statement.expression.text);
71
+ if (!declarations) continue;
72
+ for (const declaration of declarations) exported.add(declaration);
73
+ }
74
+ return exported;
75
+ }
76
+ function makeJSDocSinceLine(existingComment, sourceText, version) {
77
+ const commentText = sourceText.slice(existingComment.pos, existingComment.end);
78
+ const baseIndent = indentOfLineContaining(sourceText, existingComment.pos);
79
+ const normalizedBodyLines = commentText.replace(/^\/\*\*\s?/, "").replace(/\s*\*\/$/, "").split("\n").map((line) => line.replace(/^\s*\*\s?/, "").replace(/\s+$/, "")).filter((line, lineIndex, lines) => {
80
+ if (line.length > 0) return true;
81
+ const hasNonEmptyBefore = lines.slice(0, lineIndex).some((value) => value.length > 0);
82
+ const hasNonEmptyAfter = lines.slice(lineIndex + 1).some((value) => value.length > 0);
83
+ return hasNonEmptyBefore && hasNonEmptyAfter;
84
+ });
85
+ const replacement = `/**\n${normalizedBodyLines.length > 0 ? `${normalizedBodyLines.map((line) => `${baseIndent} * ${line}`).join("\n")}\n${baseIndent} *\n` : ""}${baseIndent} * ${VERSION_TAG} ${version}\n${baseIndent} */`;
86
+ return {
87
+ start: existingComment.pos,
88
+ end: existingComment.end,
89
+ replacement
90
+ };
91
+ }
92
+ function makeSinceOnlyJSDocBlock(declarationIndent, version) {
93
+ return `/**\n${declarationIndent} * ${VERSION_TAG} ${version}\n${declarationIndent} */`;
94
+ }
95
+ function makeDeclarationSinceLine(declaration, sf, sourceText, version) {
96
+ if (ts.getJSDocTags(declaration).some((tag) => tag.tagName.text === "since")) return;
97
+ const lastJsDoc = ts.getJSDocCommentsAndTags(declaration).filter(ts.isJSDoc).at(-1);
98
+ if (lastJsDoc) return makeJSDocSinceLine(lastJsDoc, sourceText, version);
99
+ const start = declaration.getStart(sf);
100
+ const indent = indentOfLineContaining(sourceText, start);
101
+ return {
102
+ start,
103
+ end: start,
104
+ replacement: `${indent}${makeSinceOnlyJSDocBlock(indent, version)}\n${indent}`
105
+ };
106
+ }
107
+ function applySinceTagsToFile(filePath, version, fs, write) {
108
+ const sourceText = fs.readFileSync(filePath, "utf8");
109
+ const sf = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true, filePath.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
110
+ const edits = [];
111
+ for (const declaration of collectExportedDeclarations(sf)) {
112
+ const edit = makeDeclarationSinceLine(declaration, sf, sourceText, version);
113
+ if (edit) edits.push(edit);
114
+ }
115
+ if (edits.length > 0 && write) {
116
+ const updated = applyEditsDescending(sourceText, edits);
117
+ fs.writeFileSync(filePath, updated, "utf8");
118
+ }
119
+ return {
120
+ filePath,
121
+ taggedDeclarations: edits.length,
122
+ changed: edits.length > 0
123
+ };
124
+ }
125
+ function resolveNearestPackageVersion(targetPath, fs) {
126
+ const resolved = path.resolve(targetPath);
127
+ let current = fs.statSync(resolved).isDirectory() ? resolved : path.dirname(resolved);
128
+ while (true) {
129
+ const packageJsonPath = path.join(current, PACKAGE_JSON);
130
+ if (fs.existsSync(packageJsonPath)) {
131
+ const raw = fs.readFileSync(packageJsonPath, "utf8");
132
+ const version = JSON.parse(raw).version;
133
+ if (typeof version === "string" && version.length > 0) return version;
134
+ throw new Error(`Missing or invalid version in ${packageJsonPath}`);
135
+ }
136
+ const parent = path.dirname(current);
137
+ if (parent === current) break;
138
+ current = parent;
139
+ }
140
+ throw new Error(`Unable to locate ${PACKAGE_JSON} from target: ${targetPath}`);
141
+ }
142
+ function runTagOnTarget(targetPath, opts, fs) {
143
+ const resolvedTarget = path.resolve(targetPath);
144
+ const version = resolveNearestPackageVersion(resolvedTarget, fs);
145
+ const tsFiles = (fs.statSync(resolvedTarget).isDirectory() ? walkTsxFiles(resolvedTarget, fs) : [resolvedTarget]).filter((filePath) => filePath.endsWith(".ts") || filePath.endsWith(".tsx"));
146
+ const fileResults = tsFiles.map((filePath) => applySinceTagsToFile(filePath, version, fs, opts.write));
147
+ const filesChanged = fileResults.filter((result) => result.changed).length;
148
+ const taggedDeclarations = fileResults.reduce((sum, result) => sum + result.taggedDeclarations, 0);
149
+ return {
150
+ version,
151
+ filesScanned: tsFiles.length,
152
+ filesChanged,
153
+ taggedDeclarations,
154
+ fileResults
155
+ };
156
+ }
157
+ async function runTagOnAfterWriteHook(hook, modifiedFiles) {
158
+ if (!hook || modifiedFiles.length === 0) return null;
159
+ try {
160
+ await hook({ files: modifiedFiles });
161
+ return null;
162
+ } catch (caughtHookError) {
163
+ return `[tag] onAfterWrite hook failed: ${messageFromCaughtUnknown(caughtHookError)}`;
164
+ }
165
+ }
166
+ function summarizeVersions(targetResults) {
167
+ const distinctVersions = extractDistinctVersions(targetResults);
168
+ if (distinctVersions.size === 0) return "none";
169
+ if (distinctVersions.size > 1) return "mixed";
170
+ return [...distinctVersions][0];
171
+ }
172
+ function extractDistinctVersions(targetResults) {
173
+ return new Set(targetResults.map((targetResult) => targetResult.result?.version).filter((version) => typeof version === "string" && version.length > 0));
174
+ }
175
+ function chooseWorkspacePackageTargetPath(candidate, fs) {
176
+ if (candidate.source !== "workspace-package") return {
177
+ targetPath: candidate.candidatePath,
178
+ source: candidate.source
179
+ };
180
+ const preferredSourceDir = path.join(candidate.candidatePath, "src");
181
+ if (fs.existsSync(preferredSourceDir) && fs.statSync(preferredSourceDir).isDirectory()) return {
182
+ targetPath: preferredSourceDir,
183
+ source: "workspace-package-selected-src"
184
+ };
185
+ return {
186
+ targetPath: candidate.candidatePath,
187
+ source: "workspace-package-selected-root"
188
+ };
189
+ }
190
+ function resolveTargetSelection(candidate, rootDir, fs) {
191
+ const selectedTarget = chooseWorkspacePackageTargetPath(candidate, fs);
192
+ const rootRelativeTargetPath = path.relative(rootDir, selectedTarget.targetPath).split(path.sep).join("/");
193
+ return {
194
+ targetPath: selectedTarget.targetPath,
195
+ rootRelativeTargetPath: rootRelativeTargetPath || ".",
196
+ source: selectedTarget.source,
197
+ packageDir: candidate.packageDir,
198
+ packageName: candidate.packageName
199
+ };
200
+ }
201
+ function filterSkippedCandidates(targetCandidates, skipPackages) {
202
+ if (!skipPackages || skipPackages.length === 0) return {
203
+ includedCandidates: targetCandidates,
204
+ skippedPackages: []
205
+ };
206
+ const skipPackageSet = new Set(skipPackages);
207
+ const includedCandidates = [];
208
+ const skippedPackages = [];
209
+ for (const candidate of targetCandidates) {
210
+ const packageName = candidate.packageName;
211
+ if (packageName && skipPackageSet.has(packageName)) {
212
+ skippedPackages.push(packageName);
213
+ continue;
214
+ }
215
+ includedCandidates.push(candidate);
216
+ }
217
+ return {
218
+ includedCandidates,
219
+ skippedPackages
220
+ };
221
+ }
222
+ async function runOnResolvedTarget(resolvedTarget, write, fs, listener) {
223
+ listener?.onTargetStarted(resolvedTarget);
224
+ const absoluteTargetPath = path.resolve(resolvedTarget.targetPath);
225
+ if (!fs.existsSync(absoluteTargetPath)) {
226
+ const missingTargetResult = {
227
+ target: resolvedTarget,
228
+ targetExists: false,
229
+ runError: `Not found: ${absoluteTargetPath}`,
230
+ result: null
231
+ };
232
+ listener?.onTargetCompleted(resolvedTarget, missingTargetResult);
233
+ return missingTargetResult;
234
+ }
235
+ try {
236
+ const targetRunResult = {
237
+ target: resolvedTarget,
238
+ targetExists: true,
239
+ runError: null,
240
+ result: runTagOnTarget(absoluteTargetPath, { write }, fs)
241
+ };
242
+ listener?.onTargetCompleted(resolvedTarget, targetRunResult);
243
+ return targetRunResult;
244
+ } catch (caughtRunError) {
245
+ const failedTargetRunResult = {
246
+ target: resolvedTarget,
247
+ targetExists: true,
248
+ runError: messageFromCaughtUnknown(caughtRunError),
249
+ result: null
250
+ };
251
+ listener?.onTargetCompleted(resolvedTarget, failedTargetRunResult);
252
+ return failedTargetRunResult;
253
+ }
254
+ }
255
+ /**
256
+ * CLI entry: run tagging and optional `onAfterWrite` using config injected by the command layer.
257
+ * Returns structured execution data; presentation/logging belongs to command layer.
258
+ */
259
+ async function runTagSync(opts) {
260
+ const { fs } = opts;
261
+ const { includedCandidates, skippedPackages } = filterSkippedCandidates(await resolveTagTargetCandidates(opts.rootDir, opts.targetPath, fs), opts.skipPackages);
262
+ const selectedTargets = includedCandidates.map((candidate) => resolveTargetSelection(candidate, opts.rootDir, fs));
263
+ const targetResults = await Promise.all(selectedTargets.map((resolvedTarget) => runOnResolvedTarget(resolvedTarget, opts.write, fs, opts.listener)));
264
+ const allFileResults = targetResults.flatMap((targetResult) => targetResult.result?.fileResults ?? []);
265
+ const filesScanned = targetResults.reduce((sum, targetResult) => sum + (targetResult.result?.filesScanned ?? 0), 0);
266
+ const filesChanged = targetResults.reduce((sum, targetResult) => sum + (targetResult.result?.filesChanged ?? 0), 0);
267
+ const taggedDeclarations = targetResults.reduce((sum, targetResult) => sum + (targetResult.result?.taggedDeclarations ?? 0), 0);
268
+ const modifiedFiles = allFileResults.filter((entry) => entry.changed).map((entry) => entry.filePath);
269
+ const hookError = opts.write && modifiedFiles.length > 0 ? await runTagOnAfterWriteHook(opts.config?.onAfterWrite, modifiedFiles) : null;
270
+ const distinctVersions = [...extractDistinctVersions(targetResults)].sort((left, right) => left.localeCompare(right));
271
+ return {
272
+ mode: opts.write ? "applied" : "dry-run",
273
+ selectedTargets,
274
+ resolvedTargets: selectedTargets,
275
+ skippedPackages,
276
+ targetResults,
277
+ filesScanned,
278
+ filesChanged,
279
+ taggedDeclarations,
280
+ versionSummary: summarizeVersions(targetResults),
281
+ distinctVersions,
282
+ modifiedFiles,
283
+ hookError
284
+ };
285
+ }
286
+ //#endregion
2
287
  export { resolveNearestPackageVersion, runTagOnTarget, runTagSync };
@@ -1,5 +1,4 @@
1
- import { n as createNodeCliLogger, t as createNodeCliFs } from "../../node-io-DFRbHO6m.mjs";
2
- import { n as runTagOnTarget, r as runTagSync, t as resolveNearestPackageVersion } from "../../engine-CcWEHSCq.mjs";
3
- import { a as formatWarningsAndErrors, i as formatTargetTable, n as formatProgress, r as formatSummary, t as createTagProgressListener } from "../../tag-presenter-Cs66UBsH.mjs";
4
- import "../../tag-Cd9JujU5.mjs";
1
+ import { createNodeCliFs, createNodeCliLogger } from "../infra/node-io.mjs";
2
+ import { resolveNearestPackageVersion, runTagOnTarget, runTagSync } from "./application/engine.mjs";
3
+ import { createTagProgressListener, formatProgress, formatSummary, formatTargetTable, formatWarningsAndErrors } from "./presentation/tag-presenter.mjs";
5
4
  export { createNodeCliFs, createNodeCliLogger, createTagProgressListener, formatProgress, formatSummary, formatTargetTable, formatWarningsAndErrors, resolveNearestPackageVersion, runTagOnTarget, runTagSync };
@@ -1,2 +1,170 @@
1
- import { t as resolveTagTargetCandidates } from "../../../target-resolver-CR3FXMTy.mjs";
1
+ import { messageFromCaughtUnknown } from "../../infra/caught-unknown-message.mjs";
2
+ import { globSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { z } from "zod";
5
+ import picomatch from "picomatch";
6
+ import { parse } from "yaml";
7
+ //#region src/lib/tag/infra/target-resolver.ts
8
+ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
9
+ const PACKAGE_JSON_FILE = "package.json";
10
+ const DEFAULT_WORKSPACE_PACKAGE_PATTERNS = ["packages/*"];
11
+ const PnpmWorkspaceSchema = z.object({ packages: z.array(z.string()).optional() }).passthrough();
12
+ const PackageNameSchema = z.object({ name: z.string().min(1).optional() }).passthrough();
13
+ function toPosix(filePath) {
14
+ return filePath.split(path.sep).join("/");
15
+ }
16
+ function workspacePatternToPackageJsonGlob(pattern) {
17
+ const normalizedPattern = toPosix(pattern.trim()).replace(/\/+$/, "");
18
+ if (!normalizedPattern) return `**/${PACKAGE_JSON_FILE}`;
19
+ return `${normalizedPattern}/${PACKAGE_JSON_FILE}`;
20
+ }
21
+ function splitWorkspacePackagePatterns(rawPatterns) {
22
+ const include = [];
23
+ const exclude = [];
24
+ for (const rawPattern of rawPatterns) {
25
+ const trimmedPattern = rawPattern.trim();
26
+ if (!trimmedPattern) continue;
27
+ if (trimmedPattern.startsWith("!")) {
28
+ exclude.push(trimmedPattern.slice(1).trim());
29
+ continue;
30
+ }
31
+ include.push(trimmedPattern);
32
+ }
33
+ return {
34
+ include,
35
+ exclude
36
+ };
37
+ }
38
+ async function readWorkspacePackagePatterns(rootDir, fs) {
39
+ const workspaceYamlPath = path.join(rootDir, PNPM_WORKSPACE_FILE);
40
+ if (!fs.existsSync(workspaceYamlPath)) return {
41
+ patterns: [...DEFAULT_WORKSPACE_PACKAGE_PATTERNS],
42
+ hasWorkspaceFile: false,
43
+ declaredEmptyPackages: false
44
+ };
45
+ let workspaceYamlRaw = "";
46
+ try {
47
+ workspaceYamlRaw = await fs.readFile(workspaceYamlPath, "utf8");
48
+ } catch (caughtReadError) {
49
+ throw new Error(`Failed to read ${PNPM_WORKSPACE_FILE}: ${messageFromCaughtUnknown(caughtReadError)}`);
50
+ }
51
+ let parsedWorkspaceDoc;
52
+ try {
53
+ parsedWorkspaceDoc = parse(workspaceYamlRaw);
54
+ } catch (caughtParseError) {
55
+ throw new Error(`Failed to parse ${PNPM_WORKSPACE_FILE}: ${messageFromCaughtUnknown(caughtParseError)}`);
56
+ }
57
+ const parsedWorkspaceResult = PnpmWorkspaceSchema.safeParse(parsedWorkspaceDoc);
58
+ if (!parsedWorkspaceResult.success) {
59
+ const issuePath = parsedWorkspaceResult.error.issues[0]?.path.join(".") ?? "<root>";
60
+ const issueMessage = parsedWorkspaceResult.error.issues[0]?.message ?? "Invalid value";
61
+ throw new Error(`Invalid ${PNPM_WORKSPACE_FILE} schema at "${issuePath}": ${issueMessage}`);
62
+ }
63
+ const hasPackagesKey = typeof parsedWorkspaceDoc === "object" && parsedWorkspaceDoc !== null && Object.prototype.hasOwnProperty.call(parsedWorkspaceDoc, "packages");
64
+ const workspacePackages = parsedWorkspaceResult.data.packages;
65
+ if (!workspacePackages) return {
66
+ patterns: [...DEFAULT_WORKSPACE_PACKAGE_PATTERNS],
67
+ hasWorkspaceFile: true,
68
+ declaredEmptyPackages: false
69
+ };
70
+ if (workspacePackages.length === 0 && hasPackagesKey) return {
71
+ patterns: [],
72
+ hasWorkspaceFile: true,
73
+ declaredEmptyPackages: true
74
+ };
75
+ return {
76
+ patterns: workspacePackages,
77
+ hasWorkspaceFile: true,
78
+ declaredEmptyPackages: false
79
+ };
80
+ }
81
+ async function discoverWorkspacePackageDirs(rootDir, fs) {
82
+ const workspacePackages = await readWorkspacePackagePatterns(rootDir, fs);
83
+ const { include, exclude } = splitWorkspacePackagePatterns(workspacePackages.patterns);
84
+ const includePatterns = include.length > 0 ? include : [...DEFAULT_WORKSPACE_PACKAGE_PATTERNS];
85
+ const foundPackageDirs = /* @__PURE__ */ new Set();
86
+ const globOptions = {
87
+ cwd: rootDir,
88
+ posix: true,
89
+ ...path.sep === "\\" ? { windowsPathsNoEscape: true } : {}
90
+ };
91
+ for (const pattern of includePatterns) {
92
+ const matches = globSync(workspacePatternToPackageJsonGlob(pattern), globOptions);
93
+ for (const matchedPath of matches) {
94
+ if (!matchedPath.endsWith(`/${PACKAGE_JSON_FILE}`)) continue;
95
+ const packageRelDir = matchedPath.slice(0, -`/${PACKAGE_JSON_FILE}`.length);
96
+ if (!packageRelDir) continue;
97
+ foundPackageDirs.add(path.resolve(rootDir, packageRelDir));
98
+ }
99
+ }
100
+ if (exclude.length === 0) return {
101
+ packageDirs: [...foundPackageDirs].sort((leftDir, rightDir) => leftDir.localeCompare(rightDir)),
102
+ hasWorkspaceFile: workspacePackages.hasWorkspaceFile,
103
+ declaredEmptyPackages: workspacePackages.declaredEmptyPackages
104
+ };
105
+ const excludeMatchers = exclude.map((excludePattern) => picomatch(excludePattern, { dot: true }));
106
+ const discoveredDirs = [...foundPackageDirs].filter((packageDir) => {
107
+ const relativePackagePath = toPosix(path.relative(rootDir, packageDir));
108
+ return !excludeMatchers.some((excludeMatcher) => excludeMatcher(relativePackagePath));
109
+ });
110
+ discoveredDirs.sort((leftDir, rightDir) => leftDir.localeCompare(rightDir));
111
+ return {
112
+ packageDirs: discoveredDirs,
113
+ hasWorkspaceFile: workspacePackages.hasWorkspaceFile,
114
+ declaredEmptyPackages: workspacePackages.declaredEmptyPackages
115
+ };
116
+ }
117
+ function readPackageName(packageDir, fs) {
118
+ const packageJsonPath = path.join(packageDir, PACKAGE_JSON_FILE);
119
+ if (!fs.existsSync(packageJsonPath)) return null;
120
+ try {
121
+ const rawPackageJson = fs.readFileSync(packageJsonPath, "utf8");
122
+ const parsedPackageJson = JSON.parse(rawPackageJson);
123
+ const parsedPackageName = PackageNameSchema.safeParse(parsedPackageJson);
124
+ if (!parsedPackageName.success) return null;
125
+ return parsedPackageName.data.name ?? null;
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+ function toRootRelativePath(rootDir, absolutePath) {
131
+ const relativePath = path.relative(rootDir, absolutePath);
132
+ return relativePath ? toPosix(relativePath) : ".";
133
+ }
134
+ function toPackageTargetCandidate(rootDir, packageDir, fs) {
135
+ const packageName = readPackageName(packageDir, fs);
136
+ return {
137
+ candidatePath: packageDir,
138
+ rootRelativeCandidatePath: toRootRelativePath(rootDir, packageDir),
139
+ source: "workspace-package",
140
+ packageDir,
141
+ packageName
142
+ };
143
+ }
144
+ async function resolveTagTargetCandidates(rootDir, explicitTarget, fs) {
145
+ if (explicitTarget) {
146
+ const resolvedExplicitTarget = path.resolve(explicitTarget);
147
+ return [{
148
+ candidatePath: resolvedExplicitTarget,
149
+ rootRelativeCandidatePath: toRootRelativePath(rootDir, resolvedExplicitTarget),
150
+ source: "explicit-target",
151
+ packageDir: null,
152
+ packageName: null
153
+ }];
154
+ }
155
+ const workspaceDiscovery = await discoverWorkspacePackageDirs(rootDir, fs);
156
+ if (workspaceDiscovery.packageDirs.length === 0) {
157
+ if (workspaceDiscovery.hasWorkspaceFile) return [];
158
+ const repoSourceDir = path.join(rootDir, "src");
159
+ return [{
160
+ candidatePath: repoSourceDir,
161
+ rootRelativeCandidatePath: toRootRelativePath(rootDir, repoSourceDir),
162
+ source: "repo-src-fallback",
163
+ packageDir: null,
164
+ packageName: null
165
+ }];
166
+ }
167
+ return workspaceDiscovery.packageDirs.map((packageDir) => toPackageTargetCandidate(rootDir, packageDir, fs));
168
+ }
169
+ //#endregion
2
170
  export { resolveTagTargetCandidates };
@@ -1,2 +1,12 @@
1
- import { n as withTagColor, t as TAG_COLORS } from "../../../colors-BjdkCfGK.mjs";
1
+ //#region src/lib/tag/presentation/colors.ts
2
+ const TAG_COLORS = {
3
+ RESET: "\x1B[0m",
4
+ RED: "\x1B[31m",
5
+ GREEN: "\x1B[32m",
6
+ YELLOW: "\x1B[33m"
7
+ };
8
+ function withTagColor(line, colorCode) {
9
+ return `${colorCode}${line}${TAG_COLORS.RESET}`;
10
+ }
11
+ //#endregion
2
12
  export { TAG_COLORS, withTagColor };
@@ -1,2 +1,63 @@
1
- import { a as formatWarningsAndErrors, i as formatTargetTable, n as formatProgress, r as formatSummary, t as createTagProgressListener } from "../../../tag-presenter-Cs66UBsH.mjs";
1
+ import { TAG_COLORS, withTagColor } from "./colors.mjs";
2
+ //#region src/lib/tag/presentation/tag-presenter.ts
3
+ function targetDisplayName(target) {
4
+ return target.packageName ?? target.rootRelativeTargetPath;
5
+ }
6
+ function warningsAndErrorsFromResult(result) {
7
+ const entries = [];
8
+ for (const targetResult of result.targetResults) if (targetResult.runError) entries.push(targetResult.runError);
9
+ if (result.hookError) entries.push(result.hookError);
10
+ return entries;
11
+ }
12
+ function formatProgress(event) {
13
+ if (event.type === "target-started") return `[tag] Processing ${targetDisplayName(event.target)}...`;
14
+ const changedFiles = event.result.result?.filesChanged ?? 0;
15
+ return `[tag] Done ${targetDisplayName(event.target)} (${changedFiles} changes)`;
16
+ }
17
+ function formatTargetTable(targets, rootDir) {
18
+ const packageColumnWidth = Math.max(7, ...targets.map((target) => (target.packageName ?? "-").length));
19
+ const pathColumnWidth = Math.max(4, ...targets.map((target) => target.rootRelativeTargetPath.length));
20
+ const lines = [];
21
+ lines.push(`[tag] Root: ${rootDir}`);
22
+ lines.push(`[tag] Resolved targets: ${targets.length}`);
23
+ lines.push("[tag] Targets:");
24
+ lines.push(` ${"package".padEnd(packageColumnWidth)} ${"path".padEnd(pathColumnWidth)} source`);
25
+ lines.push(` ${"-".repeat(packageColumnWidth)} ${"-".repeat(pathColumnWidth)} ${"-".repeat(6)}`);
26
+ for (const target of targets) lines.push(` ${(target.packageName ?? "-").padEnd(packageColumnWidth)} ${target.rootRelativeTargetPath.padEnd(pathColumnWidth)} ${target.source}`);
27
+ return lines.join("\n");
28
+ }
29
+ function formatWarningsAndErrors(result) {
30
+ const entries = warningsAndErrorsFromResult(result);
31
+ if (entries.length === 0) return null;
32
+ const lines = [withTagColor("⚠️ Warnings & Errors", TAG_COLORS.YELLOW)];
33
+ for (const entry of entries) lines.push(withTagColor(`- ${entry}`, TAG_COLORS.RED));
34
+ return lines.join("\n");
35
+ }
36
+ function formatSummary(result) {
37
+ const isDryRun = result.mode === "dry-run";
38
+ const summaryPrefix = isDryRun ? "[tag:dry-run]" : "[tag]";
39
+ const versionSuffix = result.versionSummary === "mixed" && result.distinctVersions.length > 0 ? ` [${result.distinctVersions.join(", ")}]` : "";
40
+ const summaryColor = result.targetResults.some((targetResult) => targetResult.runError !== null) || result.hookError !== null ? TAG_COLORS.RED : isDryRun ? TAG_COLORS.YELLOW : TAG_COLORS.GREEN;
41
+ const lines = [withTagColor(`${summaryPrefix} version=${result.versionSummary}${versionSuffix} files=${result.filesChanged}/${result.filesScanned} declarations=${result.taggedDeclarations}`, summaryColor)];
42
+ if (result.skippedPackages.length > 0) lines.push(`[tag] Skipped: ${result.skippedPackages.length} package(s)`);
43
+ return lines.join("\n");
44
+ }
45
+ function createTagProgressListener(onProgressLine) {
46
+ return {
47
+ onTargetStarted: (target) => {
48
+ onProgressLine(formatProgress({
49
+ type: "target-started",
50
+ target
51
+ }));
52
+ },
53
+ onTargetCompleted: (target, result) => {
54
+ onProgressLine(formatProgress({
55
+ type: "target-completed",
56
+ target,
57
+ result
58
+ }));
59
+ }
60
+ };
61
+ }
62
+ //#endregion
2
63
  export { createTagProgressListener, formatProgress, formatSummary, formatTargetTable, formatWarningsAndErrors };
package/dist/program.mjs CHANGED
@@ -1,2 +1,34 @@
1
- import { n as runCli, t as createProgram } from "./program-DR-H4tH3.mjs";
1
+ import { registerArrangeCommand } from "./commands/arrange.mjs";
2
+ import { registerMirrorCommand } from "./commands/mirror.mjs";
3
+ import { registerTagCommand } from "./commands/tag.mjs";
4
+ import process from "node:process";
5
+ import { readFileSync } from "node:fs";
6
+ import path from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { Command } from "commander";
9
+ //#region src/program.ts
10
+ function readVersion() {
11
+ try {
12
+ const dir = path.dirname(fileURLToPath(import.meta.url));
13
+ const raw = readFileSync(path.join(dir, "..", "package.json"), "utf-8");
14
+ const pkgVersion = JSON.parse(raw).version;
15
+ return typeof pkgVersion === "string" && pkgVersion.length > 0 ? pkgVersion : "unknown";
16
+ } catch {
17
+ return "unknown";
18
+ }
19
+ }
20
+ function createProgram() {
21
+ const program = new Command();
22
+ program.name("codefast").description("Codefast monorepo developer CLI").version(readVersion()).option("--no-color", "Disable ANSI color output").configureHelp({ sortSubcommands: true }).showHelpAfterError("(use --help for usage)");
23
+ registerMirrorCommand(program);
24
+ registerArrangeCommand(program);
25
+ registerTagCommand(program);
26
+ return program;
27
+ }
28
+ async function runCli(argv) {
29
+ await createProgram().parseAsync(argv, { from: "node" });
30
+ const code = process.exitCode ?? 0;
31
+ return typeof code === "number" ? code : Number(code) || 0;
32
+ }
33
+ //#endregion
2
34
  export { createProgram, runCli };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codefast/cli",
3
- "version": "0.3.13-canary.0",
3
+ "version": "0.3.13-canary.1",
4
4
  "description": "Developer CLI for the Codefast monorepo (mirror, arrange)",
5
5
  "keywords": [
6
6
  "cli",
@@ -60,7 +60,7 @@
60
60
  "@typescript/native-preview": "7.0.0-dev.20260411.1",
61
61
  "jest": "^30.3.0",
62
62
  "tsdown": "^0.21.8",
63
- "@codefast/typescript-config": "0.3.13-canary.0"
63
+ "@codefast/typescript-config": "0.3.13-canary.1"
64
64
  },
65
65
  "engines": {
66
66
  "node": ">=22.0.0"