@codefast/cli 0.8.0 → 0.9.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 (230) hide show
  1. package/CHANGELOG.md +568 -0
  2. package/LICENSE +1 -1
  3. package/README.md +150 -50
  4. package/dist/arrange/analyze.js +1 -2
  5. package/dist/arrange/cli-schema.js +1 -2
  6. package/dist/arrange/command.js +1 -2
  7. package/dist/arrange/domain/analyze-service.js +1 -2
  8. package/dist/arrange/domain/ast/ast-node.js +1 -2
  9. package/dist/arrange/domain/ast/collectors-cn.js +1 -2
  10. package/dist/arrange/domain/ast/collectors-jsx.js +1 -2
  11. package/dist/arrange/domain/ast/collectors-tv.js +1 -2
  12. package/dist/arrange/domain/ast/helpers.js +1 -2
  13. package/dist/arrange/domain/ast/simplify-targets.js +1 -2
  14. package/dist/arrange/domain/ast/targets.js +1 -2
  15. package/dist/arrange/domain/constants.js +1 -2
  16. package/dist/arrange/domain/grouping-service.js +1 -2
  17. package/dist/arrange/domain/grouping.js +1 -2
  18. package/dist/arrange/domain/imports.js +1 -2
  19. package/dist/arrange/domain/source-text-formatters.js +1 -2
  20. package/dist/arrange/domain/tailwind-token.js +1 -2
  21. package/dist/arrange/domain/token-classifier.js +1 -2
  22. package/dist/arrange/domain/types.js +1 -2
  23. package/dist/arrange/output.js +1 -2
  24. package/dist/arrange/process-file.js +1 -2
  25. package/dist/arrange/resolve-target.js +1 -2
  26. package/dist/arrange/scan-target.js +1 -2
  27. package/dist/arrange/simplify-process-file.js +1 -2
  28. package/dist/arrange/simplify-sync.js +1 -2
  29. package/dist/arrange/source-parse.js +1 -2
  30. package/dist/arrange/suggest.js +1 -2
  31. package/dist/arrange/sync.js +1 -2
  32. package/dist/arrange/typescript-ast-translator.js +1 -2
  33. package/dist/arrange/workspace.js +1 -2
  34. package/dist/audit/cli-schema.js +12 -2
  35. package/dist/audit/command.js +44 -5
  36. package/dist/audit/domain/audit-file.js +2 -3
  37. package/dist/audit/domain/comment-content.js +1 -2
  38. package/dist/audit/domain/comment-dividers.js +49 -22
  39. package/dist/audit/domain/display-names.js +71 -0
  40. package/dist/audit/domain/link-references.js +1 -2
  41. package/dist/audit/domain/mappings.js +1 -2
  42. package/dist/audit/domain/markdown-links.js +1 -2
  43. package/dist/audit/domain/react-imports.js +1 -2
  44. package/dist/audit/domain/since-versions.js +1 -2
  45. package/dist/audit/domain/tokenize.js +2 -3
  46. package/dist/audit/domain/tsdoc-syntax.js +1 -2
  47. package/dist/audit/domain/types.js +1 -2
  48. package/dist/audit/output.js +41 -1
  49. package/dist/audit/prepare.js +31 -1
  50. package/dist/audit/run-comments.js +23 -20
  51. package/dist/audit/run-display-names.js +60 -0
  52. package/dist/audit/run-links.js +1 -2
  53. package/dist/audit/run-react.js +1 -2
  54. package/dist/audit/run.js +1 -2
  55. package/dist/bin.js +1 -2
  56. package/dist/cli.js +3 -2
  57. package/dist/core/cli/format-error.js +1 -2
  58. package/dist/core/cli/global-options.js +1 -2
  59. package/dist/core/cli/positional.js +1 -2
  60. package/dist/core/cli/result-handle.js +1 -22
  61. package/dist/core/config/loader.js +1 -2
  62. package/dist/core/config/schema.js +19 -10
  63. package/dist/core/config/warnings.js +1 -2
  64. package/dist/core/config.js +1 -2
  65. package/dist/core/errors.js +1 -2
  66. package/dist/core/exit-codes.js +1 -2
  67. package/dist/core/filesystem/node.js +1 -2
  68. package/dist/core/filesystem/port.js +1 -2
  69. package/dist/core/glob.js +1 -2
  70. package/dist/core/logger.js +1 -2
  71. package/dist/core/result.js +1 -2
  72. package/dist/core/schema-parse.js +1 -2
  73. package/dist/core/source-text-edit.js +1 -2
  74. package/dist/core/verbose-diagnostics.js +1 -2
  75. package/dist/core/workspace/markdown-walk.js +1 -2
  76. package/dist/core/workspace/package-version.js +1 -2
  77. package/dist/core/workspace/resolver.js +1 -2
  78. package/dist/core/workspace/skip-directories.js +1 -2
  79. package/dist/core/workspace/source-walk.js +14 -4
  80. package/dist/core/workspace/typescript-walk.js +1 -2
  81. package/dist/mirror/cli-result.js +1 -2
  82. package/dist/mirror/cli-schema.js +1 -2
  83. package/dist/mirror/command.js +1 -2
  84. package/dist/mirror/dist-filesystem-impl.js +1 -2
  85. package/dist/mirror/domain/constants.js +1 -2
  86. package/dist/mirror/domain/dirent-guard.js +1 -2
  87. package/dist/mirror/domain/dist-filesystem.js +1 -2
  88. package/dist/mirror/domain/errors.js +1 -2
  89. package/dist/mirror/domain/exports.js +1 -2
  90. package/dist/mirror/domain/package-display-name.js +1 -2
  91. package/dist/mirror/domain/path-normalizer.js +1 -2
  92. package/dist/mirror/domain/types.js +1 -2
  93. package/dist/mirror/output.js +1 -2
  94. package/dist/mirror/package-path.js +1 -2
  95. package/dist/mirror/prepare.js +1 -2
  96. package/dist/mirror/supplement-exports.js +1 -2
  97. package/dist/mirror/sync-reporter.js +1 -2
  98. package/dist/mirror/sync-types.js +1 -2
  99. package/dist/mirror/sync-workspace-package.js +1 -2
  100. package/dist/mirror/sync.js +1 -2
  101. package/dist/mirror/write-exports.js +1 -2
  102. package/dist/pack-slim/cli-result.js +23 -0
  103. package/dist/pack-slim/cli-schema.js +11 -0
  104. package/dist/pack-slim/command.js +80 -0
  105. package/dist/pack-slim/domain/transform.js +221 -0
  106. package/dist/pack-slim/domain/types.js +2 -0
  107. package/dist/pack-slim/output.js +62 -0
  108. package/dist/pack-slim/sync.js +157 -0
  109. package/dist/pack-slim/working-tree.js +45 -0
  110. package/dist/tag/cli-result.js +1 -2
  111. package/dist/tag/cli-schema.js +1 -2
  112. package/dist/tag/command.js +1 -2
  113. package/dist/tag/domain/types.js +1 -2
  114. package/dist/tag/output.js +1 -2
  115. package/dist/tag/prepare.js +1 -2
  116. package/dist/tag/resolve-target-path.js +1 -2
  117. package/dist/tag/since-writer.js +1 -2
  118. package/dist/tag/sync.js +1 -2
  119. package/dist/tag/target-candidates.js +1 -2
  120. package/dist/tag/target-runner.js +1 -2
  121. package/dist/tag/version-resolver.js +1 -2
  122. package/package.json +8 -43
  123. package/dist/arrange/analyze.js.map +0 -1
  124. package/dist/arrange/cli-schema.js.map +0 -1
  125. package/dist/arrange/command.js.map +0 -1
  126. package/dist/arrange/domain/analyze-service.js.map +0 -1
  127. package/dist/arrange/domain/ast/ast-node.js.map +0 -1
  128. package/dist/arrange/domain/ast/collectors-cn.js.map +0 -1
  129. package/dist/arrange/domain/ast/collectors-jsx.js.map +0 -1
  130. package/dist/arrange/domain/ast/collectors-tv.js.map +0 -1
  131. package/dist/arrange/domain/ast/helpers.js.map +0 -1
  132. package/dist/arrange/domain/ast/simplify-targets.js.map +0 -1
  133. package/dist/arrange/domain/ast/targets.js.map +0 -1
  134. package/dist/arrange/domain/constants.js.map +0 -1
  135. package/dist/arrange/domain/grouping-service.js.map +0 -1
  136. package/dist/arrange/domain/grouping.js.map +0 -1
  137. package/dist/arrange/domain/imports.js.map +0 -1
  138. package/dist/arrange/domain/source-text-formatters.js.map +0 -1
  139. package/dist/arrange/domain/tailwind-token.js.map +0 -1
  140. package/dist/arrange/domain/token-classifier.js.map +0 -1
  141. package/dist/arrange/domain/types.js.map +0 -1
  142. package/dist/arrange/output.js.map +0 -1
  143. package/dist/arrange/process-file.js.map +0 -1
  144. package/dist/arrange/resolve-target.js.map +0 -1
  145. package/dist/arrange/scan-target.js.map +0 -1
  146. package/dist/arrange/simplify-process-file.js.map +0 -1
  147. package/dist/arrange/simplify-sync.js.map +0 -1
  148. package/dist/arrange/source-parse.js.map +0 -1
  149. package/dist/arrange/suggest.js.map +0 -1
  150. package/dist/arrange/sync.js.map +0 -1
  151. package/dist/arrange/typescript-ast-translator.js.map +0 -1
  152. package/dist/arrange/workspace.js.map +0 -1
  153. package/dist/audit/cli-schema.js.map +0 -1
  154. package/dist/audit/command.js.map +0 -1
  155. package/dist/audit/domain/audit-file.js.map +0 -1
  156. package/dist/audit/domain/comment-content.js.map +0 -1
  157. package/dist/audit/domain/comment-dividers.js.map +0 -1
  158. package/dist/audit/domain/link-references.js.map +0 -1
  159. package/dist/audit/domain/mappings.js.map +0 -1
  160. package/dist/audit/domain/markdown-links.js.map +0 -1
  161. package/dist/audit/domain/react-imports.js.map +0 -1
  162. package/dist/audit/domain/since-versions.js.map +0 -1
  163. package/dist/audit/domain/tokenize.js.map +0 -1
  164. package/dist/audit/domain/tsdoc-syntax.js.map +0 -1
  165. package/dist/audit/domain/types.js.map +0 -1
  166. package/dist/audit/output.js.map +0 -1
  167. package/dist/audit/prepare.js.map +0 -1
  168. package/dist/audit/run-comments.js.map +0 -1
  169. package/dist/audit/run-links.js.map +0 -1
  170. package/dist/audit/run-react.js.map +0 -1
  171. package/dist/audit/run.js.map +0 -1
  172. package/dist/bin.js.map +0 -1
  173. package/dist/cli.js.map +0 -1
  174. package/dist/core/cli/format-error.js.map +0 -1
  175. package/dist/core/cli/global-options.js.map +0 -1
  176. package/dist/core/cli/positional.js.map +0 -1
  177. package/dist/core/cli/result-handle.js.map +0 -1
  178. package/dist/core/config/loader.js.map +0 -1
  179. package/dist/core/config/schema.js.map +0 -1
  180. package/dist/core/config/warnings.js.map +0 -1
  181. package/dist/core/config.js.map +0 -1
  182. package/dist/core/errors.js.map +0 -1
  183. package/dist/core/exit-codes.js.map +0 -1
  184. package/dist/core/filesystem/node.js.map +0 -1
  185. package/dist/core/filesystem/port.js.map +0 -1
  186. package/dist/core/glob.js.map +0 -1
  187. package/dist/core/logger.js.map +0 -1
  188. package/dist/core/result.js.map +0 -1
  189. package/dist/core/schema-parse.js.map +0 -1
  190. package/dist/core/source-text-edit.js.map +0 -1
  191. package/dist/core/verbose-diagnostics.js.map +0 -1
  192. package/dist/core/workspace/markdown-walk.js.map +0 -1
  193. package/dist/core/workspace/package-version.js.map +0 -1
  194. package/dist/core/workspace/resolver.js.map +0 -1
  195. package/dist/core/workspace/skip-directories.js.map +0 -1
  196. package/dist/core/workspace/source-walk.js.map +0 -1
  197. package/dist/core/workspace/typescript-walk.js.map +0 -1
  198. package/dist/mirror/cli-result.js.map +0 -1
  199. package/dist/mirror/cli-schema.js.map +0 -1
  200. package/dist/mirror/command.js.map +0 -1
  201. package/dist/mirror/dist-filesystem-impl.js.map +0 -1
  202. package/dist/mirror/domain/constants.js.map +0 -1
  203. package/dist/mirror/domain/dirent-guard.js.map +0 -1
  204. package/dist/mirror/domain/dist-filesystem.js.map +0 -1
  205. package/dist/mirror/domain/errors.js.map +0 -1
  206. package/dist/mirror/domain/exports.js.map +0 -1
  207. package/dist/mirror/domain/package-display-name.js.map +0 -1
  208. package/dist/mirror/domain/path-normalizer.js.map +0 -1
  209. package/dist/mirror/domain/types.js.map +0 -1
  210. package/dist/mirror/output.js.map +0 -1
  211. package/dist/mirror/package-path.js.map +0 -1
  212. package/dist/mirror/prepare.js.map +0 -1
  213. package/dist/mirror/supplement-exports.js.map +0 -1
  214. package/dist/mirror/sync-reporter.js.map +0 -1
  215. package/dist/mirror/sync-types.js.map +0 -1
  216. package/dist/mirror/sync-workspace-package.js.map +0 -1
  217. package/dist/mirror/sync.js.map +0 -1
  218. package/dist/mirror/write-exports.js.map +0 -1
  219. package/dist/tag/cli-result.js.map +0 -1
  220. package/dist/tag/cli-schema.js.map +0 -1
  221. package/dist/tag/command.js.map +0 -1
  222. package/dist/tag/domain/types.js.map +0 -1
  223. package/dist/tag/output.js.map +0 -1
  224. package/dist/tag/prepare.js.map +0 -1
  225. package/dist/tag/resolve-target-path.js.map +0 -1
  226. package/dist/tag/since-writer.js.map +0 -1
  227. package/dist/tag/sync.js.map +0 -1
  228. package/dist/tag/target-candidates.js.map +0 -1
  229. package/dist/tag/target-runner.js.map +0 -1
  230. package/dist/tag/version-resolver.js.map +0 -1
@@ -0,0 +1,221 @@
1
+ /** Pure transforms that slim a package's manifest and dist for publish. */
2
+ /**
3
+ * The emitted-file extensions that may carry a `sourceMappingURL` directive.
4
+ */
5
+ const MAP_ANNOTATED_EXTENSIONS = new Set([".js", ".mjs", ".cjs", ".d.ts", ".d.mts", ".d.cts"]);
6
+ /**
7
+ * The script names npm and pnpm run on their own, on a consumer's install or on this package's publish.
8
+ */
9
+ const LIFECYCLE_SCRIPT_STEMS = new Set([
10
+ "install",
11
+ "prepare",
12
+ "prepublish",
13
+ "prepublishOnly",
14
+ "pack",
15
+ "publish",
16
+ ]);
17
+ /**
18
+ * The characters that make a `files` entry a glob rather than a path.
19
+ */
20
+ const GLOB_CHARACTERS = /[*?[\]{}]/;
21
+ /**
22
+ * Whether a dist entry is a source-map sidecar (`*.map`).
23
+ *
24
+ * @since 0.8.1
25
+ */
26
+ export function isSourceMapFile(fileName) {
27
+ return fileName.endsWith(".map");
28
+ }
29
+ /**
30
+ * Whether a dist entry is an emitted file that may carry a `sourceMappingURL` directive.
31
+ *
32
+ * @since 0.8.1
33
+ */
34
+ export function isMapAnnotatedFile(fileName) {
35
+ for (const extension of MAP_ANNOTATED_EXTENSIONS) {
36
+ if (fileName.endsWith(extension)) {
37
+ return true;
38
+ }
39
+ }
40
+ return false;
41
+ }
42
+ /**
43
+ * Whether a script name is an install or publish lifecycle hook, with or without its `pre`/`post` prefix.
44
+ *
45
+ * @remarks Publish hooks stay because `pnpm publish` runs them right after pack-slim; install hooks stay because a
46
+ * consumer's package manager runs them.
47
+ *
48
+ * @since 0.9.0
49
+ */
50
+ export function isLifecycleScript(name) {
51
+ if (LIFECYCLE_SCRIPT_STEMS.has(name)) {
52
+ return true;
53
+ }
54
+ const stem = name.replace(/^(?:pre|post)/, "");
55
+ return stem !== name && LIFECYCLE_SCRIPT_STEMS.has(stem);
56
+ }
57
+ /**
58
+ * Produces a publish manifest carrying only what a consumer's `tsc` and Node read.
59
+ *
60
+ * @remarks Operates on a structural clone, so the caller's manifest is left intact. A consumer never enables `source`
61
+ * and resolves `#/` through `types`/`default` to `dist`, so dropping the source lane, the `imports` left pointing
62
+ * outside `files`, the scripts that are not lifecycle hooks, and `devDependencies` leaves the published surface whole.
63
+ *
64
+ * @since 0.8.1
65
+ */
66
+ export function slimPublishManifest(manifest) {
67
+ const draft = structuredClone(manifest);
68
+ const filesSrcRemoved = removeSrcFromFiles(draft);
69
+ const exportsSourceRemoved = deleteSourceConditions(draft.exports);
70
+ const importsSourceRemoved = deleteSourceConditions(draft.imports);
71
+ const importsUnshippedRemoved = deleteUnshippedImports(draft);
72
+ const scriptsRemoved = deleteDevOnlyScripts(draft);
73
+ const devDependenciesRemoved = deleteDevDependencies(draft);
74
+ const changed = JSON.stringify(draft) !== JSON.stringify(manifest);
75
+ return {
76
+ manifest: draft,
77
+ report: {
78
+ filesSrcRemoved,
79
+ exportsSourceRemoved,
80
+ importsSourceRemoved,
81
+ importsUnshippedRemoved,
82
+ scriptsRemoved,
83
+ devDependenciesRemoved,
84
+ changed,
85
+ },
86
+ };
87
+ }
88
+ /**
89
+ * Strips every `sourceMappingURL` directive line from an emitted file's text.
90
+ *
91
+ * @since 0.8.1
92
+ */
93
+ export function stripSourceMappingComment(text) {
94
+ const lines = text.split("\n");
95
+ const kept = lines.filter((line) => !/^\s*\/\/# sourceMappingURL=/.test(line));
96
+ if (kept.length === lines.length) {
97
+ return { text, stripped: false };
98
+ }
99
+ return { text: kept.join("\n"), stripped: true };
100
+ }
101
+ function removeSrcFromFiles(manifest) {
102
+ const files = manifest.files;
103
+ if (!Array.isArray(files)) {
104
+ return false;
105
+ }
106
+ const next = files.filter((entry) => entry !== "src");
107
+ if (next.length === files.length) {
108
+ return false;
109
+ }
110
+ manifest.files = next;
111
+ return true;
112
+ }
113
+ // Walks a conditions tree deleting every `source` key. A subpath key always starts with ".", so only real condition
114
+ // objects match; an `imports` entry this leaves empty is what the unshipped pass removes.
115
+ function deleteSourceConditions(node) {
116
+ if (Array.isArray(node)) {
117
+ let removed = 0;
118
+ for (const item of node) {
119
+ removed += deleteSourceConditions(item);
120
+ }
121
+ return removed;
122
+ }
123
+ if (!isRecord(node)) {
124
+ return 0;
125
+ }
126
+ let removed = 0;
127
+ if ("source" in node) {
128
+ delete node.source;
129
+ removed += 1;
130
+ }
131
+ for (const key of Object.keys(node)) {
132
+ removed += deleteSourceConditions(node[key]);
133
+ }
134
+ return removed;
135
+ }
136
+ // Keeps an `imports` entry only while one of its targets lands inside a path `files` ships. `files` is read after the
137
+ // `src` drop, so a lane still pointing at `./src/*` counts as unshipped; with no `files` at all npm ships everything.
138
+ function deleteUnshippedImports(manifest) {
139
+ const imports = manifest.imports;
140
+ const files = manifest.files;
141
+ if (!isRecord(imports) || !Array.isArray(files)) {
142
+ return 0;
143
+ }
144
+ const shippedEntries = files.filter((entry) => typeof entry === "string");
145
+ let removed = 0;
146
+ for (const specifier of Object.keys(imports)) {
147
+ const targets = collectTargets(imports[specifier]);
148
+ if (targets.some((target) => isShippedTarget(target, shippedEntries))) {
149
+ continue;
150
+ }
151
+ delete imports[specifier];
152
+ removed += 1;
153
+ }
154
+ if (Object.keys(imports).length === 0) {
155
+ delete manifest.imports;
156
+ }
157
+ return removed;
158
+ }
159
+ function collectTargets(node, into = []) {
160
+ if (typeof node === "string") {
161
+ into.push(node);
162
+ }
163
+ else if (Array.isArray(node)) {
164
+ for (const item of node) {
165
+ collectTargets(item, into);
166
+ }
167
+ }
168
+ else if (isRecord(node)) {
169
+ for (const value of Object.values(node)) {
170
+ collectTargets(value, into);
171
+ }
172
+ }
173
+ return into;
174
+ }
175
+ // A glob entry is left to npm, so any target counts as shipped under it; a negated entry ships nothing.
176
+ function isShippedTarget(target, shippedEntries) {
177
+ const relativeTarget = stripDotSlash(target);
178
+ return shippedEntries.some((entry) => {
179
+ if (entry.startsWith("!")) {
180
+ return false;
181
+ }
182
+ if (GLOB_CHARACTERS.test(entry)) {
183
+ return true;
184
+ }
185
+ const shipped = stripDotSlash(entry).replace(/\/+$/, "");
186
+ return relativeTarget === shipped || relativeTarget.startsWith(`${shipped}/`);
187
+ });
188
+ }
189
+ function stripDotSlash(filePath) {
190
+ return filePath.startsWith("./") ? filePath.slice(2) : filePath;
191
+ }
192
+ // Drops every script that is not a lifecycle hook, and the field itself once nothing is left in it.
193
+ function deleteDevOnlyScripts(manifest) {
194
+ const scripts = manifest.scripts;
195
+ if (!isRecord(scripts)) {
196
+ return 0;
197
+ }
198
+ let removed = 0;
199
+ for (const name of Object.keys(scripts)) {
200
+ if (isLifecycleScript(name)) {
201
+ continue;
202
+ }
203
+ delete scripts[name];
204
+ removed += 1;
205
+ }
206
+ if (Object.keys(scripts).length === 0) {
207
+ delete manifest.scripts;
208
+ }
209
+ return removed;
210
+ }
211
+ function deleteDevDependencies(manifest) {
212
+ const devDependencies = manifest.devDependencies;
213
+ if (!isRecord(devDependencies)) {
214
+ return 0;
215
+ }
216
+ delete manifest.devDependencies;
217
+ return Object.keys(devDependencies).length;
218
+ }
219
+ function isRecord(value) {
220
+ return value !== null && typeof value === "object" && !Array.isArray(value);
221
+ }
@@ -0,0 +1,2 @@
1
+ /** Stats and progress types for a pack-slim run. */
2
+ export {};
@@ -0,0 +1,62 @@
1
+ import { logger } from "#/core/logger";
2
+ /**
3
+ * A progress listener that renders pack-slim run events as human-readable CLI lines.
4
+ *
5
+ * @since 0.8.1
6
+ */
7
+ export class PackSlimProgressPresenter {
8
+ dryRun = false;
9
+ configure(options) {
10
+ this.dryRun = options.dryRun;
11
+ }
12
+ onBanner() {
13
+ logger.out(this.dryRun ? "pack-slim (dry run) — no files will change" : "pack-slim — slimming publish artifacts");
14
+ }
15
+ onPackageComplete(stats, ordinal, total) {
16
+ const position = `[${ordinal}/${total}]`;
17
+ if (stats.skipped) {
18
+ logger.out(`${position} skip ${stats.name} (${stats.skipReason})`);
19
+ return;
20
+ }
21
+ if (stats.error !== null) {
22
+ logger.err(`${position} error ${stats.name}: ${stats.error}`);
23
+ return;
24
+ }
25
+ if (!stats.changed) {
26
+ logger.out(`${position} ok ${stats.name} (already slim)`);
27
+ return;
28
+ }
29
+ const verb = this.dryRun ? "would drop" : "dropped";
30
+ const parts = [];
31
+ if (stats.filesSrcRemoved) {
32
+ parts.push("files:src");
33
+ }
34
+ if (stats.exportsSourceRemoved > 0) {
35
+ parts.push(`exports:source×${stats.exportsSourceRemoved}`);
36
+ }
37
+ if (stats.importsSourceRemoved > 0) {
38
+ parts.push(`imports:source×${stats.importsSourceRemoved}`);
39
+ }
40
+ if (stats.importsUnshippedRemoved > 0) {
41
+ parts.push(`imports:unshipped×${stats.importsUnshippedRemoved}`);
42
+ }
43
+ if (stats.scriptsRemoved > 0) {
44
+ parts.push(`scripts×${stats.scriptsRemoved}`);
45
+ }
46
+ if (stats.devDependenciesRemoved > 0) {
47
+ parts.push(`devDependencies×${stats.devDependenciesRemoved}`);
48
+ }
49
+ if (stats.mapFilesDeleted > 0) {
50
+ parts.push(`maps×${stats.mapFilesDeleted}`);
51
+ }
52
+ if (stats.sourceCommentsStripped > 0) {
53
+ parts.push(`sourceMappingURL×${stats.sourceCommentsStripped}`);
54
+ }
55
+ logger.out(`${position} ok ${stats.name} — ${verb} ${parts.join(", ")}`);
56
+ }
57
+ onComplete(stats, elapsedSeconds) {
58
+ logger.out(`pack-slim done in ${elapsedSeconds.toFixed(2)}s — ${stats.packagesChanged} changed, ` +
59
+ `${stats.packagesSkipped} skipped, ${stats.totalMapFilesDeleted} maps ${this.dryRun ? "to drop" : "dropped"}` +
60
+ (stats.packagesErrored > 0 ? `, ${stats.packagesErrored} errored` : ""));
61
+ }
62
+ }
@@ -0,0 +1,157 @@
1
+ import path from "node:path";
2
+ import { AppError, messageFrom } from "#/core/errors";
3
+ import { err, ok } from "#/core/result";
4
+ import { listWorkspacePackageDirectories } from "#/core/workspace/resolver";
5
+ import { DIST_DIR, PACKAGE_JSON } from "#/mirror/domain/constants";
6
+ import { isMapAnnotatedFile, isSourceMapFile, slimPublishManifest, stripSourceMappingComment, } from "#/pack-slim/domain/transform";
7
+ /**
8
+ * Slims every published package's manifest and build output for publish and returns the aggregate stats.
9
+ *
10
+ * @remarks Meant to run on an ephemeral CI checkout right before `changeset publish`: it rewrites `package.json` down to
11
+ * what a consumer reads, drops `dist` source maps, and clears their now-dangling `sourceMappingURL` directives, so the
12
+ * tarball ships only `dist` runtime and types. Private packages are skipped because `changeset publish` never publishes them.
13
+ *
14
+ * @since 0.8.1
15
+ */
16
+ export async function runPackSlim(fs, input) {
17
+ const write = input.write ?? true;
18
+ const { listener } = input;
19
+ listener?.onBanner?.();
20
+ const startTime = performance.now();
21
+ try {
22
+ const targets = await resolveTargets(fs, input.rootDir, input.packageFilter);
23
+ const stats = {
24
+ packagesFound: targets.length,
25
+ packagesProcessed: 0,
26
+ packagesSkipped: 0,
27
+ packagesErrored: 0,
28
+ packagesChanged: 0,
29
+ totalMapFilesDeleted: 0,
30
+ packageDetails: [],
31
+ };
32
+ let ordinal = 1;
33
+ for (const packageDir of targets) {
34
+ const pkgStats = await slimWorkspacePackage(fs, packageDir, write);
35
+ stats.packageDetails.push(pkgStats);
36
+ accumulate(stats, pkgStats);
37
+ listener?.onPackageComplete?.(pkgStats, ordinal, targets.length);
38
+ ordinal += 1;
39
+ }
40
+ listener?.onComplete?.(stats, (performance.now() - startTime) / 1000);
41
+ return ok(stats);
42
+ }
43
+ catch (caughtError) {
44
+ return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
45
+ }
46
+ }
47
+ async function resolveTargets(fs, rootDir, packageFilter) {
48
+ if (packageFilter !== undefined) {
49
+ const packageDir = path.resolve(rootDir, packageFilter);
50
+ if (!fs.existsSync(path.join(packageDir, PACKAGE_JSON))) {
51
+ throw new Error(`No package.json under "${packageFilter}"`);
52
+ }
53
+ return [packageDir];
54
+ }
55
+ const layout = await listWorkspacePackageDirectories(rootDir, fs, false);
56
+ return [...layout.packageDirectoryPathsAbsolute].sort((left, right) => left.localeCompare(right));
57
+ }
58
+ async function slimWorkspacePackage(fs, packageDir, write) {
59
+ const packageJsonPath = path.join(packageDir, PACKAGE_JSON);
60
+ const pkgStats = {
61
+ name: path.basename(packageDir),
62
+ path: packageDir,
63
+ skipped: false,
64
+ skipReason: "",
65
+ error: null,
66
+ filesSrcRemoved: false,
67
+ exportsSourceRemoved: 0,
68
+ importsSourceRemoved: 0,
69
+ importsUnshippedRemoved: 0,
70
+ scriptsRemoved: 0,
71
+ devDependenciesRemoved: 0,
72
+ mapFilesDeleted: 0,
73
+ sourceCommentsStripped: 0,
74
+ changed: false,
75
+ };
76
+ if (!fs.existsSync(packageJsonPath)) {
77
+ pkgStats.skipped = true;
78
+ pkgStats.skipReason = "package.json not found";
79
+ return pkgStats;
80
+ }
81
+ try {
82
+ const raw = await fs.readFile(packageJsonPath, "utf8");
83
+ const parsed = JSON.parse(raw);
84
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
85
+ throw new SyntaxError("package.json root must be a JSON object");
86
+ }
87
+ const manifest = parsed;
88
+ if (typeof manifest.name === "string") {
89
+ pkgStats.name = manifest.name;
90
+ }
91
+ if (manifest.private === true) {
92
+ pkgStats.skipped = true;
93
+ pkgStats.skipReason = "private package";
94
+ return pkgStats;
95
+ }
96
+ const { manifest: slimmed, report } = slimPublishManifest(manifest);
97
+ pkgStats.filesSrcRemoved = report.filesSrcRemoved;
98
+ pkgStats.exportsSourceRemoved = report.exportsSourceRemoved;
99
+ pkgStats.importsSourceRemoved = report.importsSourceRemoved;
100
+ pkgStats.importsUnshippedRemoved = report.importsUnshippedRemoved;
101
+ pkgStats.scriptsRemoved = report.scriptsRemoved;
102
+ pkgStats.devDependenciesRemoved = report.devDependenciesRemoved;
103
+ if (report.changed && write) {
104
+ await fs.writeFile(packageJsonPath, `${JSON.stringify(slimmed, null, 2)}\n`, "utf8");
105
+ }
106
+ await pruneDist(fs, path.join(packageDir, DIST_DIR), write, pkgStats);
107
+ pkgStats.changed = report.changed || pkgStats.mapFilesDeleted > 0 || pkgStats.sourceCommentsStripped > 0;
108
+ }
109
+ catch (caughtError) {
110
+ pkgStats.error = messageFrom(caughtError);
111
+ }
112
+ return pkgStats;
113
+ }
114
+ async function pruneDist(fs, distDir, write, pkgStats) {
115
+ if (!fs.existsSync(distDir)) {
116
+ return;
117
+ }
118
+ const entries = (await fs.readdir(distDir, { recursive: true, withFileTypes: true }));
119
+ for (const entry of entries) {
120
+ if (!entry.isFile()) {
121
+ continue;
122
+ }
123
+ const filePath = path.join(entry.parentPath, entry.name);
124
+ if (isSourceMapFile(entry.name)) {
125
+ if (write) {
126
+ await fs.unlink(filePath);
127
+ }
128
+ pkgStats.mapFilesDeleted += 1;
129
+ continue;
130
+ }
131
+ if (isMapAnnotatedFile(entry.name)) {
132
+ const contents = await fs.readFile(filePath, "utf8");
133
+ const { text, stripped } = stripSourceMappingComment(contents);
134
+ if (stripped) {
135
+ if (write) {
136
+ await fs.writeFile(filePath, text, "utf8");
137
+ }
138
+ pkgStats.sourceCommentsStripped += 1;
139
+ }
140
+ }
141
+ }
142
+ }
143
+ function accumulate(stats, pkgStats) {
144
+ if (pkgStats.skipped) {
145
+ stats.packagesSkipped += 1;
146
+ return;
147
+ }
148
+ if (pkgStats.error !== null) {
149
+ stats.packagesErrored += 1;
150
+ return;
151
+ }
152
+ stats.packagesProcessed += 1;
153
+ stats.totalMapFilesDeleted += pkgStats.mapFilesDeleted;
154
+ if (pkgStats.changed) {
155
+ stats.packagesChanged += 1;
156
+ }
157
+ }
@@ -0,0 +1,45 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { AppError, messageFrom } from "#/core/errors";
3
+ import { err, ok } from "#/core/result";
4
+ /**
5
+ * Whether git's tracked-only porcelain output reports any uncommitted change.
6
+ *
7
+ * @remarks The output is read with `--untracked-files=no`, so untracked and gitignored paths (like the freshly built
8
+ * `dist`) never appear — any non-blank line is a staged or unstaged change to a tracked file.
9
+ *
10
+ * @since 0.8.1
11
+ */
12
+ export function hasUncommittedTrackedChanges(porcelainOutput) {
13
+ return porcelainOutput.split("\n").some((line) => line.trim().length > 0);
14
+ }
15
+ /**
16
+ * Reads git's porcelain status for tracked files only, under the given root.
17
+ */
18
+ function readTrackedWorkingTreeStatus(rootDir) {
19
+ return execFileSync("git", ["status", "--porcelain", "--untracked-files=no"], {
20
+ cwd: rootDir,
21
+ encoding: "utf8",
22
+ });
23
+ }
24
+ /**
25
+ * Refuses when the git working tree carries uncommitted tracked changes, so a destructive slim never lands on real work.
26
+ *
27
+ * @remarks pack-slim rewrites tracked `package.json` files and deletes build output; its result must never be committed.
28
+ * A caller passing `--force` skips this, and `--dry-run` never reaches it because it writes nothing.
29
+ *
30
+ * @since 0.8.1
31
+ */
32
+ export function ensureWorkingTreeClean(rootDir) {
33
+ let porcelain;
34
+ try {
35
+ porcelain = readTrackedWorkingTreeStatus(rootDir);
36
+ }
37
+ catch (caughtError) {
38
+ return err(new AppError("INFRA_FAILURE", `Could not check the git working tree: ${messageFrom(caughtError)}. Pass --force to skip this check.`, caughtError));
39
+ }
40
+ if (hasUncommittedTrackedChanges(porcelain)) {
41
+ return err(new AppError("VALIDATION_ERROR", "pack-slim refuses to run: the git working tree has uncommitted tracked changes. " +
42
+ "Commit or stash them, use --dry-run to preview, or pass --force to override."));
43
+ }
44
+ return ok(undefined);
45
+ }
@@ -10,5 +10,4 @@ export function exitCodeForTagSyncResult(result) {
10
10
  }
11
11
  const hasRunErrors = result.targetResults.some((targetResult) => targetResult.runError !== null);
12
12
  return hasRunErrors || result.hookError !== null ? CLI_EXIT_GENERAL_ERROR : CLI_EXIT_SUCCESS;
13
- }
14
- //# sourceMappingURL=cli-result.js.map
13
+ }
@@ -11,5 +11,4 @@ export const tagSyncRunRequestSchema = z.object({
11
11
  targetPath: z.string().optional(),
12
12
  skipPackages: z.array(z.string()).optional(),
13
13
  config: z.unknown().optional(),
14
- });
15
- //# sourceMappingURL=cli-schema.js.map
14
+ });
@@ -67,5 +67,4 @@ function formatTagSyncJsonOutput(result, rootDir) {
67
67
  cwd: rootDir,
68
68
  result,
69
69
  });
70
- }
71
- //# sourceMappingURL=command.js.map
70
+ }
@@ -1,2 +1 @@
1
- export {};
2
- //# sourceMappingURL=types.js.map
1
+ export {};
@@ -101,5 +101,4 @@ function formatSummary(result) {
101
101
  lines.push(`[tag] Skipped: ${result.skippedPackages.length} package(s)`);
102
102
  }
103
103
  return lines.join("\n");
104
- }
105
- //# sourceMappingURL=output.js.map
104
+ }
@@ -28,5 +28,4 @@ export async function prepareTagSync(fs, args) {
28
28
  config: loadedOutcome.value.config,
29
29
  resolvedTargetPath,
30
30
  });
31
- }
32
- //# sourceMappingURL=prepare.js.map
31
+ }
@@ -12,5 +12,4 @@ export function resolveProvidedTagTargetPath(fs, args) {
12
12
  ? path.resolve(args.rawTarget)
13
13
  : path.resolve(args.currentWorkingDirectory, args.rawTarget);
14
14
  return fs.canonicalPathSync(candidate);
15
- }
16
- //# sourceMappingURL=resolve-target-path.js.map
15
+ }
@@ -229,5 +229,4 @@ export class TagSinceWriter {
229
229
  replacement: `${indent}${this.makeSinceOnlyJSDocBlock(indent, version)}\n${indent}`,
230
230
  };
231
231
  }
232
- }
233
- //# sourceMappingURL=since-writer.js.map
232
+ }
package/dist/tag/sync.js CHANGED
@@ -164,5 +164,4 @@ async function runOnResolvedTarget(fs, resolvedTarget, write, listener) {
164
164
  listener?.onTargetCompleted(resolvedTarget, failedTargetRunResult);
165
165
  return failedTargetRunResult;
166
166
  }
167
- }
168
- //# sourceMappingURL=sync.js.map
167
+ }
@@ -77,5 +77,4 @@ export async function resolveTagTargetCandidates(fs, rootDir, explicitTarget) {
77
77
  ];
78
78
  }
79
79
  return packageDirs.map((packageDir) => toPackageTargetCandidate(fs, rootDir, packageDir));
80
- }
81
- //# sourceMappingURL=target-candidates.js.map
80
+ }
@@ -23,5 +23,4 @@ export function runTagOnTarget(fs, targetPath, opts) {
23
23
  taggedDeclarations,
24
24
  fileResults,
25
25
  };
26
- }
27
- //# sourceMappingURL=target-runner.js.map
26
+ }
@@ -10,5 +10,4 @@ export function resolveNearestPackageVersion(fs, targetPath) {
10
10
  throw new Error(`Unable to resolve a package version from target: ${targetPath}`);
11
11
  }
12
12
  return version;
13
- }
14
- //# sourceMappingURL=version-resolver.js.map
13
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codefast/cli",
3
- "version": "0.8.0",
4
- "description": "Developer CLI for the Codefast monorepo (arrange, audit, mirror, tag)",
3
+ "version": "0.9.0",
4
+ "description": "Developer CLI for the Codefast monorepo (arrange, audit, mirror, pack-slim, tag)",
5
5
  "keywords": [
6
6
  "cli",
7
7
  "codefast",
@@ -15,7 +15,7 @@
15
15
  "url": "https://github.com/codefastlabs/codefast/issues"
16
16
  },
17
17
  "license": "MIT",
18
- "author": "Vuong Phan <mr.thevuong@gmail.com>",
18
+ "author": "Vuong Phan <mr.thevuong@gmail.com> (https://github.com/thevuong)",
19
19
  "repository": {
20
20
  "type": "git",
21
21
  "url": "git+https://github.com/codefastlabs/codefast.git",
@@ -26,27 +26,13 @@
26
26
  },
27
27
  "files": [
28
28
  "dist",
29
- "LICENSE",
29
+ "CHANGELOG.md",
30
30
  "README.md",
31
- "package.json"
31
+ "LICENSE"
32
32
  ],
33
33
  "type": "module",
34
34
  "imports": {
35
- "#/tests/*": [
36
- "./tests/*",
37
- "./tests/*.ts",
38
- "./tests/*.tsx",
39
- "./tests/*/index.ts",
40
- "./tests/*/index.tsx"
41
- ],
42
35
  "#/*": {
43
- "source": [
44
- "./src/*",
45
- "./src/*.ts",
46
- "./src/*.tsx",
47
- "./src/*/index.ts",
48
- "./src/*/index.tsx"
49
- ],
50
36
  "default": "./dist/*.js"
51
37
  }
52
38
  },
@@ -57,33 +43,12 @@
57
43
  "@microsoft/tsdoc": "^0.16.0",
58
44
  "commander": "^15.0.0",
59
45
  "jiti": "^2.7.0",
60
- "oxc-parser": "^0.146.0",
61
- "picomatch": "^4.0.5",
46
+ "oxc-parser": "^0.148.0",
47
+ "picomatch": "^4.0.7",
62
48
  "yaml": "^2.9.0",
63
- "zod": "^4.4.3"
64
- },
65
- "devDependencies": {
66
- "@types/node": "^26.2.0",
67
- "@types/picomatch": "^4.0.3",
68
- "@vitest/coverage-v8": "^4.1.11",
69
- "typescript": "^7.0.2",
70
- "vite": "^8.2.2",
71
- "vitest": "^4.1.11",
72
- "@codefast/typescript-config": "0.8.0"
49
+ "zod": "^4.5.4"
73
50
  },
74
51
  "engines": {
75
52
  "node": ">=24.0.0"
76
- },
77
- "scripts": {
78
- "build": "rm -rf dist && tsc -p tsconfig.build.json",
79
- "check-types": "tsc --noEmit",
80
- "clean": "rm -rf dist",
81
- "test": "vitest run",
82
- "test:coverage": "vitest run --coverage",
83
- "test:e2e": "vitest run tests/e2e",
84
- "test:integration": "vitest run tests/integration",
85
- "test:type": "vitest run tests/types",
86
- "test:unit": "vitest run tests/unit",
87
- "test:watch": "vitest"
88
53
  }
89
54
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/arrange/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oCAAoC,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAElH,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAExC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAkB,EAAE,eAAuB;IAC1E,MAAM,MAAM,GAAG,wBAAwB,EAAE,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,kBAAkB,CAAC,EAAE,EAAE,eAAe,CAAC,CAAC;QACtD,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;YAC7B,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACrD,MAAM,QAAQ,GAAG,qBAAqB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YAC7D,oCAAoC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;IACpB,CAAC;IAAC,OAAO,WAAoB,EAAE,CAAC;QAC9B,OAAO,GAAG,CAAC,IAAI,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;IACnF,CAAC;AACH,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli-schema.js","sourceRoot":"","sources":["../../src/arrange/cli-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoCxB;;;;GAIG;AACH,MAAM,CAAC,MAAM,oCAAoC,GAA8C,CAAC,CAAC,MAAM,CAAC;IACtG,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,6BAA6B,CAAC;CAClE,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAqC,CAAC,CAAC,MAAM,CAAC;IACpF,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7B,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE;IAClB,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACrC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAA2C,CAAC,CAAC,MAAM,CAAC;IAChG,aAAa,EAAE,CAAC;SACb,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,sFAAsF,CAAC;IACjG,gBAAgB,EAAE,CAAC,CAAC,OAAO,EAAE;IAC7B,iBAAiB,EAAE,CAAC,CAAC,OAAO,EAAE;CAC/B,CAAC,CAAC"}