@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,130 @@
1
+ import { applyEditsDescending, indentOfLineContaining } from "../core/source-text-edit.mjs";
2
+ import ts from "typescript";
3
+ //#region src/tag/since-writer.ts
4
+ /**
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ var TagSinceWriter = class {
8
+ sinceDocumentationTag = "@since";
9
+ constructor(fs) {
10
+ this.fs = fs;
11
+ }
12
+ applySinceTagsToFile(filePath, version, write) {
13
+ const sourceText = this.fs.readFileSync(filePath, "utf8");
14
+ const sf = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true, filePath.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
15
+ const edits = [];
16
+ for (const declaration of this.collectExportedDeclarations(sf)) {
17
+ const edit = this.makeDeclarationSinceLine(declaration, sf, sourceText, version);
18
+ if (edit) edits.push(edit);
19
+ }
20
+ if (edits.length > 0 && write) {
21
+ const updated = applyEditsDescending(sourceText, edits);
22
+ this.fs.writeFileSync(filePath, updated, "utf8");
23
+ }
24
+ return {
25
+ filePath,
26
+ taggedDeclarations: edits.length,
27
+ changed: edits.length > 0
28
+ };
29
+ }
30
+ hasExportModifier(node) {
31
+ return (ts.canHaveModifiers(node) ? ts.getModifiers(node) : void 0)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ?? false;
32
+ }
33
+ isTaggableDeclaration(node) {
34
+ return ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isVariableStatement(node);
35
+ }
36
+ addDeclarationName(registry, name, declaration) {
37
+ if (!registry.has(name)) {
38
+ registry.set(name, new Set([declaration]));
39
+ return;
40
+ }
41
+ const bucket = registry.get(name);
42
+ if (bucket === void 0) throw new Error(`declaration registry missing set for ${name}`);
43
+ bucket.add(declaration);
44
+ }
45
+ collectLocalNamedDeclarations(sf) {
46
+ const declarations = /* @__PURE__ */ new Map();
47
+ for (const statement of sf.statements) {
48
+ if (ts.isFunctionDeclaration(statement) && statement.name) {
49
+ this.addDeclarationName(declarations, statement.name.text, statement);
50
+ continue;
51
+ }
52
+ if (ts.isClassDeclaration(statement) && statement.name) {
53
+ this.addDeclarationName(declarations, statement.name.text, statement);
54
+ continue;
55
+ }
56
+ if (ts.isInterfaceDeclaration(statement)) {
57
+ this.addDeclarationName(declarations, statement.name.text, statement);
58
+ continue;
59
+ }
60
+ if (ts.isTypeAliasDeclaration(statement)) {
61
+ this.addDeclarationName(declarations, statement.name.text, statement);
62
+ continue;
63
+ }
64
+ if (ts.isEnumDeclaration(statement)) {
65
+ this.addDeclarationName(declarations, statement.name.text, statement);
66
+ continue;
67
+ }
68
+ if (!ts.isVariableStatement(statement)) continue;
69
+ for (const declaration of statement.declarationList.declarations) if (ts.isIdentifier(declaration.name)) this.addDeclarationName(declarations, declaration.name.text, statement);
70
+ }
71
+ return declarations;
72
+ }
73
+ collectExportedDeclarations(sf) {
74
+ const exported = /* @__PURE__ */ new Set();
75
+ const localNamed = this.collectLocalNamedDeclarations(sf);
76
+ for (const statement of sf.statements) {
77
+ if (this.isTaggableDeclaration(statement) && this.hasExportModifier(statement)) {
78
+ exported.add(statement);
79
+ continue;
80
+ }
81
+ if (ts.isExportDeclaration(statement) && !statement.moduleSpecifier && statement.exportClause && ts.isNamedExports(statement.exportClause)) {
82
+ for (const element of statement.exportClause.elements) {
83
+ const localName = element.propertyName?.text ?? element.name.text;
84
+ const declarations = localNamed.get(localName);
85
+ if (!declarations) continue;
86
+ for (const declaration of declarations) exported.add(declaration);
87
+ }
88
+ continue;
89
+ }
90
+ if (!ts.isExportAssignment(statement) || !ts.isIdentifier(statement.expression)) continue;
91
+ const declarations = localNamed.get(statement.expression.text);
92
+ if (!declarations) continue;
93
+ for (const declaration of declarations) exported.add(declaration);
94
+ }
95
+ return exported;
96
+ }
97
+ makeJSDocSinceLine(existingComment, sourceText, version) {
98
+ const commentText = sourceText.slice(existingComment.pos, existingComment.end);
99
+ const baseIndent = indentOfLineContaining(sourceText, existingComment.pos);
100
+ const normalizedBodyLines = commentText.replace(/^\/\*\*\s?/, "").replace(/\s*\*\/$/, "").split("\n").map((line) => line.replace(/^\s*\*\s?/, "").replace(/\s+$/, "")).filter((line, lineIndex, lines) => {
101
+ if (line.length > 0) return true;
102
+ const hasNonEmptyBefore = lines.slice(0, lineIndex).some((value) => value.length > 0);
103
+ const hasNonEmptyAfter = lines.slice(lineIndex + 1).some((value) => value.length > 0);
104
+ return hasNonEmptyBefore && hasNonEmptyAfter;
105
+ });
106
+ const replacement = `/**\n${normalizedBodyLines.length > 0 ? `${normalizedBodyLines.map((line) => `${baseIndent} * ${line}`).join("\n")}\n${baseIndent} *\n` : ""}${baseIndent} * ${this.sinceDocumentationTag} ${version}\n${baseIndent} */`;
107
+ return {
108
+ start: existingComment.pos,
109
+ end: existingComment.end,
110
+ replacement
111
+ };
112
+ }
113
+ makeSinceOnlyJSDocBlock(declarationIndent, version) {
114
+ return `/**\n${declarationIndent} * ${this.sinceDocumentationTag} ${version}\n${declarationIndent} */`;
115
+ }
116
+ makeDeclarationSinceLine(declaration, sf, sourceText, version) {
117
+ if (ts.getJSDocTags(declaration).some((tag) => tag.tagName.text === "since")) return;
118
+ const lastJsDoc = ts.getJSDocCommentsAndTags(declaration).filter(ts.isJSDoc).at(-1);
119
+ if (lastJsDoc) return this.makeJSDocSinceLine(lastJsDoc, sourceText, version);
120
+ const start = declaration.getStart(sf);
121
+ const indent = indentOfLineContaining(sourceText, start);
122
+ return {
123
+ start,
124
+ end: start,
125
+ replacement: `${indent}${this.makeSinceOnlyJSDocBlock(indent, version)}\n${indent}`
126
+ };
127
+ }
128
+ };
129
+ //#endregion
130
+ export { TagSinceWriter };
@@ -0,0 +1,141 @@
1
+ import { AppError, messageFrom } from "../core/errors.mjs";
2
+ import { err, ok } from "../core/result.mjs";
3
+ import { resolveTagTargetCandidates } from "./target-candidates.mjs";
4
+ import { runTagOnTarget } from "./target-runner.mjs";
5
+ import path from "node:path";
6
+ //#region src/tag/sync.ts
7
+ /**
8
+ * @since 0.3.16-canary.0
9
+ */
10
+ async function runTagSync(fs, input) {
11
+ try {
12
+ const tagConfig = input.config;
13
+ const { includedCandidates, skippedPackages } = filterSkippedCandidates(await resolveTagTargetCandidates(fs, input.rootDir, input.targetPath), input.skipPackages);
14
+ const selectedTargets = includedCandidates.map((candidate) => resolveTargetSelection(fs, candidate, input.rootDir));
15
+ const targetExecutionResults = await Promise.all(selectedTargets.map((resolvedTarget) => runOnResolvedTarget(fs, resolvedTarget, input.write, input.listener)));
16
+ const allFileResults = targetExecutionResults.flatMap((targetResult) => targetResult.result?.fileResults ?? []);
17
+ const filesScanned = targetExecutionResults.reduce((sum, targetResult) => sum + (targetResult.result?.filesScanned ?? 0), 0);
18
+ const filesChanged = targetExecutionResults.reduce((sum, targetResult) => sum + (targetResult.result?.filesChanged ?? 0), 0);
19
+ const taggedDeclarations = targetExecutionResults.reduce((sum, targetResult) => sum + (targetResult.result?.taggedDeclarations ?? 0), 0);
20
+ const modifiedFiles = allFileResults.filter((entry) => entry.changed).map((entry) => entry.filePath);
21
+ const hookError = input.write && modifiedFiles.length > 0 ? await runTagOnAfterWriteHook(tagConfig?.onAfterWrite, modifiedFiles) : null;
22
+ const versionsSet = extractDistinctVersions(targetExecutionResults);
23
+ const distinctVersions = [...versionsSet].sort((left, right) => left.localeCompare(right));
24
+ return ok({
25
+ mode: input.write ? "applied" : "dry-run",
26
+ selectedTargets,
27
+ skippedPackages,
28
+ targetResults: targetExecutionResults,
29
+ filesScanned,
30
+ filesChanged,
31
+ taggedDeclarations,
32
+ versionSummary: summarizeVersions(versionsSet),
33
+ distinctVersions,
34
+ modifiedFiles,
35
+ hookError
36
+ });
37
+ } catch (caughtError) {
38
+ return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
39
+ }
40
+ }
41
+ async function runTagOnAfterWriteHook(hook, modifiedFiles) {
42
+ if (!hook || modifiedFiles.length === 0) return null;
43
+ try {
44
+ await hook({ files: modifiedFiles });
45
+ return null;
46
+ } catch (caughtHookError) {
47
+ return `[tag] onAfterWrite hook failed: ${messageFrom(caughtHookError)}`;
48
+ }
49
+ }
50
+ function summarizeVersions(distinctVersions) {
51
+ if (distinctVersions.size === 0) return "none";
52
+ if (distinctVersions.size > 1) return "mixed";
53
+ return distinctVersions.values().next().value ?? "none";
54
+ }
55
+ function extractDistinctVersions(targetResults) {
56
+ return new Set(targetResults.map((targetResult) => targetResult.result?.version).filter((version) => typeof version === "string" && version.length > 0));
57
+ }
58
+ function chooseWorkspacePackageTargetPath(fs, candidate) {
59
+ if (candidate.source !== "workspace-package") return {
60
+ targetPath: candidate.candidatePath,
61
+ source: candidate.source
62
+ };
63
+ const preferredSourceDir = path.join(candidate.candidatePath, "src");
64
+ try {
65
+ if (fs.statSync(preferredSourceDir).isDirectory()) return {
66
+ targetPath: preferredSourceDir,
67
+ source: "workspace-package-selected-src"
68
+ };
69
+ } catch {}
70
+ return {
71
+ targetPath: candidate.candidatePath,
72
+ source: "workspace-package-selected-root"
73
+ };
74
+ }
75
+ function resolveTargetSelection(fs, candidate, rootDir) {
76
+ const selectedTarget = chooseWorkspacePackageTargetPath(fs, candidate);
77
+ const rootRelativeTargetPath = path.relative(rootDir, selectedTarget.targetPath).split(path.sep).join("/");
78
+ return {
79
+ targetPath: selectedTarget.targetPath,
80
+ rootRelativeTargetPath: rootRelativeTargetPath || ".",
81
+ source: selectedTarget.source,
82
+ packageDir: candidate.packageDir,
83
+ packageName: candidate.packageName
84
+ };
85
+ }
86
+ function filterSkippedCandidates(targetCandidates, skipPackages) {
87
+ if (!skipPackages || skipPackages.length === 0) return {
88
+ includedCandidates: targetCandidates,
89
+ skippedPackages: []
90
+ };
91
+ const skipPackageSet = new Set(skipPackages);
92
+ const includedCandidates = [];
93
+ const skippedPackages = [];
94
+ for (const candidate of targetCandidates) {
95
+ const packageName = candidate.packageName;
96
+ if (packageName && skipPackageSet.has(packageName)) {
97
+ skippedPackages.push(packageName);
98
+ continue;
99
+ }
100
+ includedCandidates.push(candidate);
101
+ }
102
+ return {
103
+ includedCandidates,
104
+ skippedPackages
105
+ };
106
+ }
107
+ async function runOnResolvedTarget(fs, resolvedTarget, write, listener) {
108
+ listener?.onTargetStarted(resolvedTarget);
109
+ const absoluteTargetPath = path.resolve(resolvedTarget.targetPath);
110
+ if (!fs.existsSync(absoluteTargetPath)) {
111
+ const missingTargetResult = {
112
+ target: resolvedTarget,
113
+ targetExists: false,
114
+ runError: `Not found: ${absoluteTargetPath}`,
115
+ result: null
116
+ };
117
+ listener?.onTargetCompleted(resolvedTarget, missingTargetResult);
118
+ return missingTargetResult;
119
+ }
120
+ try {
121
+ const targetRunResult = {
122
+ target: resolvedTarget,
123
+ targetExists: true,
124
+ runError: null,
125
+ result: runTagOnTarget(fs, absoluteTargetPath, { write })
126
+ };
127
+ listener?.onTargetCompleted(resolvedTarget, targetRunResult);
128
+ return targetRunResult;
129
+ } catch (caughtRunError) {
130
+ const failedTargetRunResult = {
131
+ target: resolvedTarget,
132
+ targetExists: true,
133
+ runError: messageFrom(caughtRunError),
134
+ result: null
135
+ };
136
+ listener?.onTargetCompleted(resolvedTarget, failedTargetRunResult);
137
+ return failedTargetRunResult;
138
+ }
139
+ }
140
+ //#endregion
141
+ export { runTagSync };
@@ -0,0 +1,68 @@
1
+ import { listWorkspacePackageDirectories } from "../core/workspace/resolver.mjs";
2
+ import path from "node:path";
3
+ import { z } from "zod";
4
+ //#region src/tag/target-candidates.ts
5
+ const packageJsonFileName = "package.json";
6
+ const packageJsonNameSchema = z.looseObject({ name: z.string().min(1).optional() });
7
+ function toPosix(filePath) {
8
+ return filePath.split(path.sep).join("/");
9
+ }
10
+ function readPackageName(fs, packageDir) {
11
+ const packageJsonPath = path.join(packageDir, packageJsonFileName);
12
+ if (!fs.existsSync(packageJsonPath)) return null;
13
+ try {
14
+ const rawPackageJson = fs.readFileSync(packageJsonPath, "utf8");
15
+ const parsedPackageJson = JSON.parse(rawPackageJson);
16
+ const parsedPackageName = packageJsonNameSchema.safeParse(parsedPackageJson);
17
+ if (!parsedPackageName.success) return null;
18
+ return parsedPackageName.data.name ?? null;
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+ function toRootRelativePath(rootDir, absolutePath) {
24
+ const relativePath = path.relative(rootDir, absolutePath);
25
+ return relativePath ? toPosix(relativePath) : ".";
26
+ }
27
+ function toPackageTargetCandidate(fs, rootDir, packageDir) {
28
+ const packageName = readPackageName(fs, packageDir);
29
+ return {
30
+ candidatePath: packageDir,
31
+ rootRelativeCandidatePath: toRootRelativePath(rootDir, packageDir),
32
+ source: "workspace-package",
33
+ packageDir,
34
+ packageName
35
+ };
36
+ }
37
+ /**
38
+ * @since 0.3.16-canary.0
39
+ */
40
+ async function resolveTagTargetCandidates(fs, rootDir, explicitTarget) {
41
+ if (explicitTarget) {
42
+ const resolvedExplicitTarget = path.resolve(explicitTarget);
43
+ return [{
44
+ candidatePath: resolvedExplicitTarget,
45
+ rootRelativeCandidatePath: toRootRelativePath(rootDir, resolvedExplicitTarget),
46
+ source: "explicit-target",
47
+ packageDir: null,
48
+ packageName: null
49
+ }];
50
+ }
51
+ const outcome = await listWorkspacePackageDirectories(rootDir, fs, true);
52
+ const packageDirs = [...outcome.packageDirectoryPathsAbsolute];
53
+ const hasWorkspaceFile = outcome.hasPnpmWorkspaceYamlFile;
54
+ if (packageDirs.length === 0) {
55
+ if (hasWorkspaceFile) return [];
56
+ const repoSourceDir = path.join(rootDir, "src");
57
+ return [{
58
+ candidatePath: repoSourceDir,
59
+ rootRelativeCandidatePath: toRootRelativePath(rootDir, repoSourceDir),
60
+ source: "repo-src-fallback",
61
+ packageDir: null,
62
+ packageName: null
63
+ }];
64
+ }
65
+ return packageDirs.map((packageDir) => toPackageTargetCandidate(fs, rootDir, packageDir));
66
+ }
67
+ //#endregion
68
+ export { resolveTagTargetCandidates };
@@ -0,0 +1,26 @@
1
+ import { walkTsxFiles } from "../core/workspace/typescript-walk.mjs";
2
+ import { resolveNearestPackageVersion } from "./version-resolver.mjs";
3
+ import { TagSinceWriter } from "./since-writer.mjs";
4
+ import path from "node:path";
5
+ //#region src/tag/target-runner.ts
6
+ /**
7
+ * @since 0.3.16-canary.0
8
+ */
9
+ function runTagOnTarget(fs, targetPath, opts) {
10
+ const resolvedTarget = path.resolve(targetPath);
11
+ const version = resolveNearestPackageVersion(fs, resolvedTarget);
12
+ const tsFiles = (fs.statSync(resolvedTarget).isDirectory() ? walkTsxFiles(resolvedTarget, fs) : [resolvedTarget]).filter((filePath) => filePath.endsWith(".ts") || filePath.endsWith(".tsx"));
13
+ const sinceWriter = new TagSinceWriter(fs);
14
+ const fileResults = tsFiles.map((filePath) => sinceWriter.applySinceTagsToFile(filePath, version, opts.write));
15
+ const filesChanged = fileResults.filter((result) => result.changed).length;
16
+ const taggedDeclarations = fileResults.reduce((sum, result) => sum + result.taggedDeclarations, 0);
17
+ return {
18
+ version,
19
+ filesScanned: tsFiles.length,
20
+ filesChanged,
21
+ taggedDeclarations,
22
+ fileResults
23
+ };
24
+ }
25
+ //#endregion
26
+ export { runTagOnTarget };
@@ -0,0 +1,25 @@
1
+ import path from "node:path";
2
+ //#region src/tag/version-resolver.ts
3
+ const packageJsonFileName = "package.json";
4
+ /**
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ function resolveNearestPackageVersion(fs, targetPath) {
8
+ const resolved = path.resolve(targetPath);
9
+ let current = fs.statSync(resolved).isDirectory() ? resolved : path.dirname(resolved);
10
+ while (true) {
11
+ const packageJsonPath = path.join(current, packageJsonFileName);
12
+ if (fs.existsSync(packageJsonPath)) {
13
+ const raw = fs.readFileSync(packageJsonPath, "utf8");
14
+ const version = JSON.parse(raw).version;
15
+ if (typeof version === "string" && version.length > 0) return version;
16
+ throw new Error(`Missing or invalid version in ${packageJsonPath}`);
17
+ }
18
+ const parent = path.dirname(current);
19
+ if (parent === current) break;
20
+ current = parent;
21
+ }
22
+ throw new Error(`Unable to locate ${packageJsonFileName} from target: ${targetPath}`);
23
+ }
24
+ //#endregion
25
+ export { resolveNearestPackageVersion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codefast/cli",
3
- "version": "0.3.14",
3
+ "version": "0.3.16-canary.0",
4
4
  "description": "Developer CLI for the Codefast monorepo (arrange, mirror, tag)",
5
5
  "keywords": [
6
6
  "cli",
@@ -55,20 +55,17 @@
55
55
  "jiti": "^2.6.1",
56
56
  "picomatch": "^4.0.4",
57
57
  "typescript": "^6.0.3",
58
- "yaml": "^2.8.3",
59
- "zod": "^4.3.6",
60
- "@codefast/di": "0.3.14"
58
+ "yaml": "^2.8.4",
59
+ "zod": "^4.4.3"
61
60
  },
62
61
  "devDependencies": {
63
62
  "@types/node": "^25.6.0",
64
63
  "@types/picomatch": "^4.0.3",
65
- "@typescript/native-preview": "7.0.0-dev.20260422.1",
64
+ "@typescript/native-preview": "7.0.0-dev.20260504.1",
66
65
  "@vitest/coverage-v8": "^4.1.5",
67
- "esbuild": "^0.28.0",
68
- "unplugin-swc": "^1.5.9",
69
- "vite": "^8.0.9",
66
+ "vite": "^8.0.10",
70
67
  "vitest": "^4.1.5",
71
- "@codefast/typescript-config": "0.3.14"
68
+ "@codefast/typescript-config": "0.3.16-canary.0"
72
69
  },
73
70
  "engines": {
74
71
  "node": ">=22.0.0"
@@ -78,6 +75,8 @@
78
75
  "check-types": "tsgo --noEmit",
79
76
  "clean": "rm -rf dist",
80
77
  "test": "vitest run",
78
+ "test:integration": "vitest run tests/integration",
79
+ "test:unit": "vitest run tests/unit",
81
80
  "test:coverage": "vitest run --coverage",
82
81
  "test:watch": "vitest"
83
82
  }