@mongez/pkgist 1.0.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 (49) hide show
  1. package/.prettierignore +4 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +320 -0
  4. package/builder.ts +183 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +1125 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/index.d.ts +202 -0
  9. package/dist/index.js +824 -0
  10. package/dist/index.js.map +1 -0
  11. package/eslint.config.js +98 -0
  12. package/package.json +49 -0
  13. package/src/build/family-builder.ts +76 -0
  14. package/src/build/index.ts +4 -0
  15. package/src/build/package-builder.ts +155 -0
  16. package/src/build/parallel-builder.ts +36 -0
  17. package/src/cli.ts +51 -0
  18. package/src/commands/build-all.ts +88 -0
  19. package/src/commands/build-family.ts +55 -0
  20. package/src/commands/build.ts +84 -0
  21. package/src/commands/index.ts +5 -0
  22. package/src/commands/list.ts +74 -0
  23. package/src/commands/validate.ts +125 -0
  24. package/src/compile/index.ts +1 -0
  25. package/src/compile/tsdown-compiler.ts +344 -0
  26. package/src/config/define-config.ts +9 -0
  27. package/src/config/index.ts +3 -0
  28. package/src/config/load-config.ts +103 -0
  29. package/src/files/clone-files.ts +45 -0
  30. package/src/files/file-manager.ts +109 -0
  31. package/src/files/index.ts +3 -0
  32. package/src/files/package-json.ts +191 -0
  33. package/src/git/index.ts +1 -0
  34. package/src/git/operations.ts +87 -0
  35. package/src/index.ts +26 -0
  36. package/src/publish/index.ts +1 -0
  37. package/src/publish/npm-publisher.ts +36 -0
  38. package/src/types/config.ts +33 -0
  39. package/src/types/index.ts +2 -0
  40. package/src/types/package.ts +81 -0
  41. package/src/utils/errors.ts +40 -0
  42. package/src/utils/exec.ts +58 -0
  43. package/src/utils/index.ts +4 -0
  44. package/src/utils/logger.ts +44 -0
  45. package/src/utils/paths.ts +48 -0
  46. package/src/version/increment.ts +51 -0
  47. package/src/version/index.ts +1 -0
  48. package/tsconfig.json +20 -0
  49. package/tsup.config.ts +34 -0
package/dist/cli.js ADDED
@@ -0,0 +1,1125 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { Command } from "commander";
5
+ import { readFileSync } from "fs";
6
+ import { fileURLToPath } from "url";
7
+ import path5 from "path";
8
+
9
+ // src/config/load-config.ts
10
+ import path2 from "path";
11
+ import { pathToFileURL } from "url";
12
+ import fs from "fs";
13
+
14
+ // src/utils/paths.ts
15
+ import path from "path";
16
+ function toForwardSlash(p) {
17
+ return p.replace(/\\/g, "/");
18
+ }
19
+ function joinPath(...segments) {
20
+ return toForwardSlash(path.join(...segments));
21
+ }
22
+ function resolvePath(...segments) {
23
+ return toForwardSlash(path.resolve(...segments));
24
+ }
25
+ function scopelessName(packageName) {
26
+ const slash = packageName.indexOf("/");
27
+ return slash === -1 ? packageName : packageName.slice(slash + 1);
28
+ }
29
+ function buildOutputPath(buildDir, packageName, version) {
30
+ return joinPath(buildDir, scopelessName(packageName), version);
31
+ }
32
+ function sourceSnapshotPath(sourcesDir, packageName) {
33
+ return joinPath(sourcesDir, scopelessName(packageName));
34
+ }
35
+
36
+ // src/utils/errors.ts
37
+ var BundlerError = class extends Error {
38
+ constructor(step, packageName, message, cause) {
39
+ super(message);
40
+ this.step = step;
41
+ this.packageName = packageName;
42
+ this.cause = cause;
43
+ this.name = "BundlerError";
44
+ if (cause instanceof Error && cause.stack) {
45
+ this.stack = `${this.stack}
46
+ Caused by: ${cause.stack}`;
47
+ }
48
+ }
49
+ step;
50
+ packageName;
51
+ cause;
52
+ };
53
+ function wrapError(step, packageName, error) {
54
+ if (error instanceof BundlerError) return error;
55
+ const msg = error instanceof Error ? error.message : typeof error === "string" ? error : JSON.stringify(error);
56
+ return new BundlerError(step, packageName, `[${step}] ${packageName}: ${msg}`, error);
57
+ }
58
+
59
+ // src/config/load-config.ts
60
+ async function loadConfig(configFilePath) {
61
+ const absolute = resolvePath(configFilePath);
62
+ if (!fs.existsSync(absolute)) {
63
+ throw new Error(`Config file not found: ${absolute}`);
64
+ }
65
+ let mod;
66
+ try {
67
+ const fileUrl = pathToFileURL(absolute).href;
68
+ mod = await import(fileUrl);
69
+ } catch (err) {
70
+ throw wrapError("load-config", absolute, err);
71
+ }
72
+ const config = mod.default;
73
+ if (!config || typeof config !== "object") {
74
+ throw new Error(
75
+ `Config file ${absolute} must export a default object from defineConfig(). Got: ${typeof config}`
76
+ );
77
+ }
78
+ validateConfig(config, absolute);
79
+ return {
80
+ config,
81
+ configPath: absolute,
82
+ configDir: resolvePath(path2.dirname(absolute))
83
+ };
84
+ }
85
+ function validateConfig(config, filePath) {
86
+ if (!config.settings) {
87
+ throw new Error(`Config "${filePath}" must have a "settings" object.`);
88
+ }
89
+ if (!config.settings.buildDir) {
90
+ throw new Error(`Config "${filePath}" settings.buildDir is required.`);
91
+ }
92
+ const names = /* @__PURE__ */ new Set();
93
+ for (const pkg of config.standalone ?? []) {
94
+ if (!pkg.name) throw new Error(`Standalone package is missing "name" in ${filePath}`);
95
+ if (!pkg.root) throw new Error(`Standalone package "${pkg.name}" is missing "root"`);
96
+ if (names.has(pkg.name)) throw new Error(`Duplicate package name: "${pkg.name}"`);
97
+ names.add(pkg.name);
98
+ }
99
+ for (const family of config.families ?? []) {
100
+ if (!family.name) throw new Error(`A family is missing "name" in ${filePath}`);
101
+ for (const pkg of family.packages) {
102
+ if (!pkg.name) throw new Error(`Package inside family "${family.name}" is missing "name"`);
103
+ if (!pkg.root) throw new Error(`Package "${pkg.name}" in family "${family.name}" is missing "root"`);
104
+ if (names.has(pkg.name)) throw new Error(`Duplicate package name: "${pkg.name}"`);
105
+ names.add(pkg.name);
106
+ }
107
+ }
108
+ }
109
+ function findDefaultConfigPath(cwd) {
110
+ const candidates = ["builder.ts", "builder.js", "mongez.ts", "mongez.js"];
111
+ for (const candidate of candidates) {
112
+ const full = path2.join(cwd, candidate);
113
+ if (fs.existsSync(full)) return full;
114
+ }
115
+ throw new Error(
116
+ `No config file found in ${cwd}. Tried: ${candidates.join(", ")}. Use --config <path> to specify a custom location.`
117
+ );
118
+ }
119
+
120
+ // src/files/file-manager.ts
121
+ import fs2 from "fs";
122
+ import path3 from "path";
123
+ function ensureDir(dir) {
124
+ try {
125
+ fs2.mkdirSync(dir, { recursive: true });
126
+ } catch (err) {
127
+ throw wrapError("ensure-dir", dir, err);
128
+ }
129
+ }
130
+ function copyFile(src, dest) {
131
+ ensureDir(path3.dirname(dest));
132
+ try {
133
+ fs2.copyFileSync(src, dest);
134
+ } catch (err) {
135
+ throw wrapError("copy-file", src, err);
136
+ }
137
+ }
138
+ var COPY_EXCLUDES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".turbo", ".cache"]);
139
+ function copyDir(src, dest) {
140
+ ensureDir(dest);
141
+ const entries = fs2.readdirSync(src, { withFileTypes: true });
142
+ for (const entry of entries) {
143
+ if (COPY_EXCLUDES.has(entry.name)) continue;
144
+ const srcPath = joinPath(src, entry.name);
145
+ const destPath = joinPath(dest, entry.name);
146
+ if (entry.isDirectory()) {
147
+ copyDir(srcPath, destPath);
148
+ } else {
149
+ copyFile(srcPath, destPath);
150
+ }
151
+ }
152
+ }
153
+ function readFile(filePath, packageName = filePath) {
154
+ try {
155
+ return fs2.readFileSync(filePath, "utf-8");
156
+ } catch (err) {
157
+ throw wrapError("read-file", packageName, err);
158
+ }
159
+ }
160
+ function writeFile(filePath, content, packageName = filePath) {
161
+ ensureDir(path3.dirname(filePath));
162
+ try {
163
+ fs2.writeFileSync(filePath, content, "utf-8");
164
+ } catch (err) {
165
+ throw wrapError("write-file", packageName, err);
166
+ }
167
+ }
168
+ function moveFile(src, dest, packageName = src) {
169
+ ensureDir(path3.dirname(dest));
170
+ try {
171
+ fs2.renameSync(src, dest);
172
+ } catch (err) {
173
+ try {
174
+ fs2.copyFileSync(src, dest);
175
+ fs2.unlinkSync(src);
176
+ } catch (fallbackErr) {
177
+ throw wrapError("move-file", packageName, fallbackErr);
178
+ }
179
+ }
180
+ }
181
+ function pathExists(p) {
182
+ return fs2.existsSync(p);
183
+ }
184
+
185
+ // src/files/package-json.ts
186
+ var KEPT_FIELDS = [
187
+ "name",
188
+ "description",
189
+ "keywords",
190
+ "author",
191
+ "license",
192
+ "repository",
193
+ "homepage",
194
+ "bugs",
195
+ "dependencies",
196
+ "peerDependencies",
197
+ "sideEffects",
198
+ "bin",
199
+ "engines"
200
+ ];
201
+ function readSourcePackageJson(packageRoot, packageName) {
202
+ const pkgPath = joinPath(packageRoot, "package.json");
203
+ let raw;
204
+ try {
205
+ raw = readFile(pkgPath, packageName);
206
+ } catch {
207
+ throw wrapError(
208
+ "read-package-json",
209
+ packageName,
210
+ new Error(`package.json not found at ${pkgPath}`)
211
+ );
212
+ }
213
+ try {
214
+ const parsed = JSON.parse(raw);
215
+ if (!parsed.version) {
216
+ throw new Error(`package.json at ${pkgPath} is missing "version" field`);
217
+ }
218
+ return parsed;
219
+ } catch (err) {
220
+ throw wrapError("parse-package-json", packageName, err);
221
+ }
222
+ }
223
+ function writeSourceVersion(packageRoot, packageName, newVersion) {
224
+ const pkgPath = joinPath(packageRoot, "package.json");
225
+ const raw = readFile(pkgPath, packageName);
226
+ const parsed = JSON.parse(raw);
227
+ parsed.version = newVersion;
228
+ writeFile(pkgPath, JSON.stringify(parsed, null, 2) + "\n", packageName);
229
+ }
230
+ function writeBuildPackageJson(pkg, sourceJson, buildPath, newVersion) {
231
+ const formats = pkg.formats ?? ["esm", "cjs"];
232
+ const hasEsm = formats.includes("esm");
233
+ const hasCjs = formats.includes("cjs");
234
+ const esmOnly = hasEsm && !hasCjs;
235
+ const preserve = pkg.preserveModules !== false;
236
+ const esmExt = preserve ? ".mjs" : ".js";
237
+ const cjsExt = preserve ? ".cjs" : ".js";
238
+ const esmDts = preserve ? ".d.mts" : ".d.ts";
239
+ const cjsDts = preserve ? ".d.cts" : ".d.ts";
240
+ const rawEntries = pkg.entries ?? ["index.ts"];
241
+ const entryList = Array.isArray(rawEntries) ? rawEntries : [rawEntries];
242
+ function entryPaths(entry) {
243
+ const basePath = entry.replace(/\.tsx?$/, "");
244
+ const parts = basePath.split("/");
245
+ const last = parts[parts.length - 1];
246
+ let exportPath;
247
+ if (last === "index") {
248
+ const dir = parts.slice(0, -1).join("/");
249
+ exportPath = dir ? `./${dir}` : ".";
250
+ } else {
251
+ exportPath = `./${basePath}`;
252
+ }
253
+ return { basePath, exportPath };
254
+ }
255
+ const { basePath: primaryBase } = entryPaths(entryList[0]);
256
+ const exportsMap = {};
257
+ for (const entry of entryList) {
258
+ const { basePath, exportPath } = entryPaths(entry);
259
+ const conditions = {};
260
+ if (hasEsm) {
261
+ conditions["import"] = {
262
+ types: `./esm/${basePath}${esmDts}`,
263
+ default: `./esm/${basePath}${esmExt}`
264
+ };
265
+ }
266
+ if (hasCjs) {
267
+ const cjsTypes = preserve && hasEsm ? `./esm/${basePath}${esmDts}` : `./cjs/${basePath}${cjsDts}`;
268
+ conditions["require"] = {
269
+ types: cjsTypes,
270
+ default: `./cjs/${basePath}${cjsExt}`
271
+ };
272
+ }
273
+ exportsMap[exportPath] = conditions;
274
+ }
275
+ const output = {};
276
+ for (const field of KEPT_FIELDS) {
277
+ if (field in sourceJson && sourceJson[field] !== void 0) {
278
+ output[field] = sourceJson[field];
279
+ }
280
+ }
281
+ output["name"] = pkg.name;
282
+ output["version"] = newVersion;
283
+ if (esmOnly) {
284
+ output["type"] = "module";
285
+ }
286
+ const mainType = pkg.mainType ?? "cjs";
287
+ if (mainType === "esm" || esmOnly) {
288
+ output["main"] = `./esm/${primaryBase}${esmExt}`;
289
+ } else {
290
+ output["main"] = `./cjs/${primaryBase}${cjsExt}`;
291
+ }
292
+ if (hasEsm) {
293
+ output["module"] = `./esm/${primaryBase}${esmExt}`;
294
+ }
295
+ output["types"] = `./esm/${primaryBase}${esmDts}`;
296
+ output["exports"] = exportsMap;
297
+ const pkgPath = joinPath(buildPath, "package.json");
298
+ writeFile(pkgPath, JSON.stringify(output, null, 2) + "\n", pkg.name);
299
+ }
300
+
301
+ // src/files/clone-files.ts
302
+ import fs3 from "fs";
303
+
304
+ // src/utils/logger.ts
305
+ import chalk from "chalk";
306
+ var _verbose = false;
307
+ function setVerbose(v) {
308
+ _verbose = v;
309
+ }
310
+ function log(level, message) {
311
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace("Z", "");
312
+ switch (level) {
313
+ case "info":
314
+ console.log(chalk.cyan(`[${ts}] INFO ${message}`));
315
+ break;
316
+ case "success":
317
+ console.log(chalk.green(`[${ts}] OK ${message}`));
318
+ break;
319
+ case "warn":
320
+ console.warn(chalk.yellow(`[${ts}] WARN ${message}`));
321
+ break;
322
+ case "error":
323
+ console.error(chalk.red(`[${ts}] ERROR ${message}`));
324
+ break;
325
+ case "step":
326
+ console.log(chalk.blueBright(`[${ts}] STEP ${message}`));
327
+ break;
328
+ case "debug":
329
+ if (_verbose) {
330
+ console.log(chalk.gray(`[${ts}] DEBUG ${message}`));
331
+ }
332
+ break;
333
+ }
334
+ }
335
+ var logger = {
336
+ info: (msg) => log("info", msg),
337
+ success: (msg) => log("success", msg),
338
+ warn: (msg) => log("warn", msg),
339
+ error: (msg) => log("error", msg),
340
+ step: (msg) => log("step", msg),
341
+ debug: (msg) => log("debug", msg)
342
+ };
343
+
344
+ // src/files/clone-files.ts
345
+ function cloneFiles(packageRoot, buildDir, cloneList, packageName, dryRun) {
346
+ for (const entry of cloneList) {
347
+ const [srcRel, destRel] = typeof entry === "string" ? [entry, entry] : entry;
348
+ const src = joinPath(packageRoot, srcRel);
349
+ const dest = joinPath(buildDir, destRel);
350
+ if (!pathExists(src)) {
351
+ logger.warn(`[clone-files] ${packageName}: source file not found, skipping \u2014 ${src}`);
352
+ continue;
353
+ }
354
+ if (dryRun) {
355
+ logger.info(`[dry-run] clone ${src} \u2192 ${dest}`);
356
+ continue;
357
+ }
358
+ const stat = fs3.statSync(src);
359
+ if (stat.isDirectory()) {
360
+ copyDir(src, dest);
361
+ } else {
362
+ copyFile(src, dest);
363
+ }
364
+ logger.debug(`Cloned ${src} \u2192 ${dest}`);
365
+ }
366
+ }
367
+
368
+ // src/compile/tsdown-compiler.ts
369
+ import path4 from "path";
370
+ import fs4 from "fs";
371
+ import { build } from "tsdown";
372
+ function collectExternals(sourceJson) {
373
+ const deps = Object.keys(sourceJson["dependencies"] ?? {});
374
+ const peers = Object.keys(sourceJson["peerDependencies"] ?? {});
375
+ return [.../* @__PURE__ */ new Set([...deps, ...peers])];
376
+ }
377
+ function resolveEntries(pkg, packageRoot) {
378
+ const srcDir = pkg.srcDir ?? "src";
379
+ const rawEntries = pkg.entries ?? ["index.ts"];
380
+ const entryList = Array.isArray(rawEntries) ? rawEntries : [rawEntries];
381
+ return entryList.map((e) => toForwardSlash(resolvePath(packageRoot, srcDir, e)));
382
+ }
383
+ function findTsconfig(packageRoot) {
384
+ const local = joinPath(packageRoot, "tsconfig.json");
385
+ return pathExists(local) ? local : void 0;
386
+ }
387
+ async function compilePackage(pkg, packageRoot, buildPath, sourceJson, dryRun) {
388
+ if (dryRun) {
389
+ logger.info(`[dry-run] compile ${pkg.name} \u2192 ${buildPath}`);
390
+ return;
391
+ }
392
+ const entries = resolveEntries(pkg, packageRoot);
393
+ const formats = pkg.formats ?? ["esm", "cjs"];
394
+ const externals = collectExternals(sourceJson);
395
+ const tsconfig = findTsconfig(packageRoot);
396
+ const shouldPreserveModules = pkg.preserveModules !== false;
397
+ const srcDir = toForwardSlash(resolvePath(packageRoot, pkg.srcDir ?? "src"));
398
+ logger.step(`Compiling ${pkg.name} (${formats.join(", ")}) \u2192 ${buildPath}`);
399
+ logger.debug(` entries: ${entries.join(", ")}`);
400
+ logger.debug(` externals: ${externals.slice(0, 8).join(", ")}${externals.length > 8 ? "\u2026" : ""}`);
401
+ for (const subDir of ["esm", "cjs"]) {
402
+ const dir = resolvePath(buildPath, subDir);
403
+ if (fs4.existsSync(dir)) {
404
+ fs4.rmSync(dir, { recursive: true, force: true });
405
+ }
406
+ }
407
+ const rolldownInputOptions = pkg.type === "react" ? {
408
+ transform: {
409
+ jsx: "react-jsx"
410
+ // automatic runtime — works with React 17+
411
+ }
412
+ } : void 0;
413
+ const sharedOptions = {
414
+ entry: entries,
415
+ deps: { neverBundle: externals },
416
+ sourcemap: pkg.sourcemap !== false,
417
+ clean: true,
418
+ minify: pkg.minify ?? false,
419
+ tsconfig: tsconfig ?? true,
420
+ ...rolldownInputOptions ? { inputOptions: rolldownInputOptions } : {}
421
+ };
422
+ try {
423
+ if (shouldPreserveModules && formats.includes("esm") && formats.includes("cjs")) {
424
+ const tmpEsm = toForwardSlash(resolvePath(buildPath, "_tmp_esm"));
425
+ const tmpCjs = toForwardSlash(resolvePath(buildPath, "_tmp_cjs"));
426
+ ensureDir(tmpEsm);
427
+ ensureDir(tmpCjs);
428
+ await Promise.all([
429
+ build({
430
+ ...sharedOptions,
431
+ format: ["esm"],
432
+ dts: pkg.dts !== false,
433
+ outDir: tmpEsm,
434
+ outputOptions: {
435
+ preserveModules: true,
436
+ preserveModulesRoot: srcDir
437
+ }
438
+ }),
439
+ build({
440
+ ...sharedOptions,
441
+ format: ["cjs"],
442
+ // CJS skips DTS — types are served from ESM declarations (see package-json.ts).
443
+ dts: false,
444
+ outDir: tmpCjs
445
+ })
446
+ ]);
447
+ reorganiseOutput(tmpEsm, buildPath, ["esm"], pkg, true);
448
+ reorganiseOutput(tmpCjs, buildPath, ["cjs"], pkg, false, true);
449
+ rmTmp(tmpEsm);
450
+ rmTmp(tmpCjs);
451
+ } else {
452
+ const tmpDir = toForwardSlash(resolvePath(buildPath, "_tmp"));
453
+ ensureDir(tmpDir);
454
+ await build({
455
+ ...sharedOptions,
456
+ format: formats,
457
+ dts: pkg.dts !== false,
458
+ outDir: tmpDir,
459
+ ...shouldPreserveModules && formats.includes("esm") ? { outputOptions: { preserveModules: true, preserveModulesRoot: srcDir } } : {}
460
+ });
461
+ reorganiseOutput(tmpDir, buildPath, formats, pkg, shouldPreserveModules);
462
+ rmTmp(tmpDir);
463
+ }
464
+ } catch (err) {
465
+ throw wrapError("tsdown-build", pkg.name, err);
466
+ }
467
+ logger.success(`Compiled ${pkg.name}`);
468
+ }
469
+ function rmTmp(dir) {
470
+ try {
471
+ fs4.rmSync(dir, { recursive: true, force: true });
472
+ } catch {
473
+ }
474
+ }
475
+ function reorganiseOutput(tmpDir, buildPath, formats, pkg, preserveModules, keepNativeExtension = false) {
476
+ const esmDir = joinPath(buildPath, "esm");
477
+ const cjsDir = joinPath(buildPath, "cjs");
478
+ if (formats.includes("esm")) ensureDir(esmDir);
479
+ if (formats.includes("cjs")) ensureDir(cjsDir);
480
+ const mainType = pkg.mainType ?? (formats.includes("cjs") ? "cjs" : "esm");
481
+ processDirectory(tmpDir, { esmDir, cjsDir, formats, mainType, pkg, tmpDir, preserveModules, keepNativeExtension });
482
+ }
483
+ function processDirectory(dir, ctx) {
484
+ if (!fs4.existsSync(dir)) return;
485
+ const entries = fs4.readdirSync(dir, { withFileTypes: true });
486
+ for (const entry of entries) {
487
+ const fullPath = joinPath(dir, entry.name);
488
+ if (entry.isDirectory()) {
489
+ processDirectory(fullPath, ctx);
490
+ } else {
491
+ processFile(fullPath, entry.name, ctx);
492
+ }
493
+ }
494
+ }
495
+ function processFile(filePath, name, ctx) {
496
+ const { esmDir, cjsDir, formats, mainType, pkg, tmpDir, preserveModules } = ctx;
497
+ const relDir = toForwardSlash(path4.relative(tmpDir, path4.dirname(filePath)));
498
+ if (preserveModules) {
499
+ if (isEsmFile(name)) {
500
+ if (formats.includes("esm")) {
501
+ moveFile(filePath, joinPath(esmDir, relDir, name), pkg.name);
502
+ }
503
+ } else if (isCjsFile(name)) {
504
+ if (formats.includes("cjs")) {
505
+ moveFile(filePath, joinPath(cjsDir, relDir, name), pkg.name);
506
+ }
507
+ } else if (isDtsFile(name)) {
508
+ const targetDir = formats.includes("esm") ? esmDir : cjsDir;
509
+ moveFile(filePath, joinPath(targetDir, relDir, name), pkg.name);
510
+ }
511
+ return;
512
+ }
513
+ const { keepNativeExtension } = ctx;
514
+ if (isEsmFile(name)) {
515
+ if (formats.includes("esm")) {
516
+ const destName = keepNativeExtension ? name : normaliseEsmName(name);
517
+ moveFile(filePath, joinPath(esmDir, relDir, destName), pkg.name);
518
+ }
519
+ } else if (isCjsFile(name)) {
520
+ if (formats.includes("cjs")) {
521
+ const destName = keepNativeExtension ? name : normaliseCjsName(name);
522
+ moveFile(filePath, joinPath(cjsDir, relDir, destName), pkg.name);
523
+ }
524
+ } else if (isDtsFile(name)) {
525
+ const targetDir = formats.includes("esm") ? esmDir : cjsDir;
526
+ moveFile(filePath, joinPath(targetDir, relDir, name), pkg.name);
527
+ } else if (isJsFile(name)) {
528
+ const targetDir = mainType === "esm" ? esmDir : cjsDir;
529
+ if (formats.includes(mainType)) {
530
+ const destName = targetDir === cjsDir ? name.replace(/\.js\.map$/, ".cjs.map").replace(/\.js$/, ".cjs") : name;
531
+ moveFile(filePath, joinPath(targetDir, relDir, destName), pkg.name);
532
+ }
533
+ }
534
+ }
535
+ function isEsmFile(name) {
536
+ return name.endsWith(".mjs") || name.endsWith(".mjs.map") || name.endsWith(".d.mts") || name.endsWith(".d.mts.map");
537
+ }
538
+ function isCjsFile(name) {
539
+ return name.endsWith(".cjs") || name.endsWith(".cjs.map") || name.endsWith(".d.cts") || name.endsWith(".d.cts.map");
540
+ }
541
+ function isDtsFile(name) {
542
+ return name.endsWith(".d.ts") || name.endsWith(".d.ts.map");
543
+ }
544
+ function isJsFile(name) {
545
+ return (name.endsWith(".js") || name.endsWith(".js.map")) && !name.endsWith(".d.ts") && !name.endsWith(".d.ts.map");
546
+ }
547
+ function normaliseEsmName(name) {
548
+ return name.replace(/\.mjs\.map$/, ".js.map").replace(/\.mjs$/, ".js").replace(/\.d\.mts\.map$/, ".d.ts.map").replace(/\.d\.mts$/, ".d.ts");
549
+ }
550
+ function normaliseCjsName(name) {
551
+ return name.replace(/\.cjs\.map$/, ".js.map").replace(/\.cjs$/, ".js").replace(/\.d\.cts\.map$/, ".d.ts.map").replace(/\.d\.cts$/, ".d.ts");
552
+ }
553
+
554
+ // src/version/increment.ts
555
+ import semver from "semver";
556
+ function resolveVersion(currentVersion, strategy = "auto", packageName) {
557
+ const bumpType = strategy === "auto" || strategy === "patch" ? "patch" : strategy === "minor" ? "minor" : strategy === "major" ? "major" : null;
558
+ if (bumpType) {
559
+ const bumped = semver.inc(currentVersion, bumpType);
560
+ if (!bumped) {
561
+ throw wrapError(
562
+ "version-increment",
563
+ packageName,
564
+ new Error(
565
+ `Cannot ${bumpType}-bump invalid semver version "${currentVersion}" for package "${packageName}"`
566
+ )
567
+ );
568
+ }
569
+ return bumped;
570
+ }
571
+ const cleaned = semver.valid(semver.coerce(strategy) ?? strategy);
572
+ if (!cleaned) {
573
+ throw wrapError(
574
+ "version-increment",
575
+ packageName,
576
+ new Error(
577
+ `Explicit version "${strategy}" is not a valid semver string for package "${packageName}"`
578
+ )
579
+ );
580
+ }
581
+ return cleaned;
582
+ }
583
+
584
+ // src/git/operations.ts
585
+ import simpleGit from "simple-git";
586
+ async function isGitRepo(dir) {
587
+ try {
588
+ const git = simpleGit(dir);
589
+ const result = await git.checkIsRepo();
590
+ return result;
591
+ } catch {
592
+ return false;
593
+ }
594
+ }
595
+ async function gitCommitTagPush(packageRoot, packageName, version, commitMessage, branch, dryRun) {
596
+ const isRepo = await isGitRepo(packageRoot);
597
+ if (!isRepo) {
598
+ logger.warn(`[git] ${packageName}: directory is not a git repo \u2014 skipping git operations`);
599
+ return;
600
+ }
601
+ const tag = `v${version}`;
602
+ logger.step(`[git] ${packageName}: commit "${commitMessage}", tag ${tag}, push to ${branch}`);
603
+ if (dryRun) {
604
+ logger.info(`[dry-run] git add + commit "${commitMessage}" + tag ${tag} + push`);
605
+ return;
606
+ }
607
+ const git = simpleGit(packageRoot);
608
+ try {
609
+ await git.add("-A");
610
+ await git.commit(commitMessage);
611
+ await git.push("origin", branch);
612
+ await git.addTag(tag);
613
+ await git.pushTags("origin");
614
+ logger.success(`[git] ${packageName}: pushed ${branch}, tagged ${tag}`);
615
+ } catch (err) {
616
+ throw wrapError("git-operations", packageName, err);
617
+ }
618
+ }
619
+ async function currentBranch(dir) {
620
+ try {
621
+ const git = simpleGit(dir);
622
+ const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
623
+ return branch.trim() || "main";
624
+ } catch {
625
+ return "main";
626
+ }
627
+ }
628
+
629
+ // src/utils/exec.ts
630
+ import { execa } from "execa";
631
+ async function exec(step, packageName, command, args, cwd) {
632
+ try {
633
+ const result = await execa(command, args, {
634
+ cwd,
635
+ all: true,
636
+ reject: false
637
+ });
638
+ if (result.exitCode !== 0) {
639
+ const combined = result.all ?? result.stderr ?? "";
640
+ throw new Error(
641
+ `Command "${command} ${args.join(" ")}" exited with code ${result.exitCode}.
642
+ ${combined}`
643
+ );
644
+ }
645
+ return {
646
+ stdout: result.stdout ?? "",
647
+ stderr: result.stderr ?? "",
648
+ exitCode: result.exitCode
649
+ };
650
+ } catch (err) {
651
+ throw wrapError(step, packageName, err);
652
+ }
653
+ }
654
+
655
+ // src/publish/npm-publisher.ts
656
+ async function publishPackage(pkg, buildPath, dryRun) {
657
+ if (pkg.publish === false) {
658
+ logger.info(`[publish] ${pkg.name}: publish=false, skipping`);
659
+ return;
660
+ }
661
+ const access = pkg.access ?? "public";
662
+ if (dryRun) {
663
+ logger.info(`[dry-run] npm publish --access ${access} from ${buildPath}`);
664
+ return;
665
+ }
666
+ logger.step(`Publishing ${pkg.name}@... to npm (access: ${access})`);
667
+ await exec(
668
+ "npm-publish",
669
+ pkg.name,
670
+ "npm",
671
+ ["publish", "--access", access],
672
+ buildPath
673
+ );
674
+ logger.success(`Published ${pkg.name}`);
675
+ }
676
+
677
+ // src/build/package-builder.ts
678
+ async function buildPackage(pkg, versionStrategy = "auto", settings, options, configDir, overrideCommit, forcedVersion) {
679
+ const step = "package-builder";
680
+ try {
681
+ const packageRoot = resolvePath(configDir, pkg.root);
682
+ const sourceJson = readSourcePackageJson(packageRoot, pkg.name);
683
+ const currentVersion = sourceJson.version;
684
+ const newVersion = forcedVersion ?? resolveVersion(currentVersion, versionStrategy, pkg.name);
685
+ logger.info(`Building ${pkg.name}: ${currentVersion} \u2192 ${newVersion}`);
686
+ const absoluteBuildDir = resolvePath(configDir, settings.buildDir);
687
+ const buildPath = buildOutputPath(absoluteBuildDir, pkg.name, newVersion);
688
+ if (!options.dryRun) {
689
+ ensureDir(buildPath);
690
+ }
691
+ if (settings.sourcesDir) {
692
+ const absoluteSourcesDir = resolvePath(configDir, settings.sourcesDir);
693
+ const snapshotPath = sourceSnapshotPath(absoluteSourcesDir, pkg.name);
694
+ if (options.dryRun) {
695
+ logger.info(`[dry-run] snapshot ${packageRoot} \u2192 ${snapshotPath}`);
696
+ } else {
697
+ ensureDir(snapshotPath);
698
+ copyDir(packageRoot, snapshotPath);
699
+ logger.debug(`Snapshot: ${packageRoot} \u2192 ${snapshotPath}`);
700
+ }
701
+ }
702
+ await compilePackage(pkg, packageRoot, buildPath, sourceJson, options.dryRun);
703
+ if (pkg.clone && pkg.clone.length > 0) {
704
+ cloneFiles(packageRoot, buildPath, pkg.clone, pkg.name, options.dryRun);
705
+ }
706
+ if (!options.dryRun) {
707
+ writeBuildPackageJson(pkg, sourceJson, buildPath, newVersion);
708
+ logger.debug(`Wrote build package.json for ${pkg.name}@${newVersion}`);
709
+ } else {
710
+ logger.info(`[dry-run] write build package.json for ${pkg.name}@${newVersion}`);
711
+ }
712
+ if (!options.dryRun) {
713
+ writeSourceVersion(packageRoot, pkg.name, newVersion);
714
+ logger.debug(`Updated source version ${pkg.name} \u2192 ${newVersion}`);
715
+ } else {
716
+ logger.info(`[dry-run] update source package.json version \u2192 ${newVersion}`);
717
+ }
718
+ const commitMessage = overrideCommit ?? pkg.commit;
719
+ if (commitMessage && !options.noGit) {
720
+ const branch = pkg.branch ?? await currentBranch(packageRoot);
721
+ await gitCommitTagPush(
722
+ packageRoot,
723
+ pkg.name,
724
+ newVersion,
725
+ commitMessage,
726
+ branch,
727
+ options.dryRun
728
+ );
729
+ } else if (!commitMessage) {
730
+ logger.debug(`[git] ${pkg.name}: no commit message set \u2014 skipping git`);
731
+ }
732
+ if (!options.noPublish) {
733
+ await publishPackage(pkg, buildPath, options.dryRun);
734
+ } else {
735
+ logger.info(`[publish] ${pkg.name}: --no-publish, skipping`);
736
+ }
737
+ return { packageName: pkg.name, version: newVersion, buildPath, success: true };
738
+ } catch (err) {
739
+ const wrapped = wrapError(step, pkg.name, err);
740
+ logger.error(`Failed to build ${pkg.name}: ${wrapped.message}`);
741
+ return {
742
+ packageName: pkg.name,
743
+ version: "",
744
+ buildPath: "",
745
+ success: false,
746
+ error: wrapped
747
+ };
748
+ }
749
+ }
750
+
751
+ // src/build/parallel-builder.ts
752
+ import pLimit from "p-limit";
753
+ async function runParallel(tasks, concurrency) {
754
+ if (tasks.length === 0) return [];
755
+ const limit = pLimit(Math.max(1, concurrency));
756
+ logger.info(`Running ${tasks.length} task(s) with concurrency=${concurrency}`);
757
+ const results = await Promise.all(
758
+ tasks.map((task) => limit(task))
759
+ );
760
+ const succeeded = results.filter((r) => r.success).length;
761
+ const failed = results.filter((r) => !r.success).length;
762
+ if (failed > 0) {
763
+ logger.warn(`${succeeded} succeeded, ${failed} failed`);
764
+ for (const r of results.filter((r2) => !r2.success)) {
765
+ logger.error(` FAILED: ${r.packageName} \u2014 ${r.error?.message ?? "unknown error"}`);
766
+ }
767
+ } else {
768
+ logger.success(`All ${succeeded} package(s) built successfully`);
769
+ }
770
+ return results;
771
+ }
772
+
773
+ // src/commands/build.ts
774
+ function registerBuildCommand(program2) {
775
+ program2.command("build [packages...]").description("Build standalone packages by name. Use --all to build all standalone packages.").option("--all", "Build all standalone packages").option("--dry-run", "Print what would happen without making any changes").option("--no-publish", "Skip npm publish").option("--no-git", "Skip git operations").option("--config <path>", "Path to config file").option("--concurrency <n>", "Override concurrency").action(async (packageNames, opts) => {
776
+ const configPath = opts.config ?? findDefaultConfigPath(process.cwd());
777
+ const { config, configDir } = await loadConfig(configPath);
778
+ const buildOptions = {
779
+ dryRun: opts.dryRun ?? false,
780
+ noPublish: !opts.noPublish,
781
+ // commander flips --no-publish to noPublish=false
782
+ noGit: opts.noGit ?? false,
783
+ concurrency: opts.concurrency ? parseInt(opts.concurrency, 10) : void 0,
784
+ configPath
785
+ };
786
+ const rawOpts = opts;
787
+ const shouldPublish = rawOpts["publish"] !== false;
788
+ buildOptions.noPublish = !shouldPublish;
789
+ const standalone = config.standalone ?? [];
790
+ let targets = standalone;
791
+ if (!opts.all && packageNames.length > 0) {
792
+ targets = standalone.filter((p) => packageNames.includes(p.name));
793
+ const missing = packageNames.filter(
794
+ (n) => !standalone.some((p) => p.name === n)
795
+ );
796
+ if (missing.length > 0) {
797
+ logger.warn(`Unknown package names: ${missing.join(", ")}`);
798
+ }
799
+ }
800
+ if (targets.length === 0) {
801
+ logger.warn("No standalone packages matched. Use --all or specify package names.");
802
+ process.exit(0);
803
+ }
804
+ const concurrency = buildOptions.concurrency ?? config.settings.concurrency ?? 4;
805
+ const tasks = targets.map(
806
+ (pkg) => () => buildPackage(
807
+ pkg,
808
+ pkg.version ?? "auto",
809
+ config.settings,
810
+ buildOptions,
811
+ configDir
812
+ )
813
+ );
814
+ const results = await runParallel(tasks, concurrency);
815
+ const anyFailed = results.some((r) => !r.success);
816
+ process.exit(anyFailed ? 1 : 0);
817
+ });
818
+ }
819
+
820
+ // src/build/family-builder.ts
821
+ import semver2 from "semver";
822
+ async function buildFamily(family, settings, options, configDir) {
823
+ if (family.packages.length === 0) {
824
+ logger.warn(`Family "${family.name}" has no packages \u2014 nothing to build`);
825
+ return [];
826
+ }
827
+ let newVersion;
828
+ try {
829
+ const strategy = family.version ?? "auto";
830
+ const isBumpKeyword = strategy === "auto" || strategy === "patch" || strategy === "minor" || strategy === "major";
831
+ if (!isBumpKeyword && semver2.valid(semver2.coerce(strategy) ?? strategy)) {
832
+ newVersion = strategy;
833
+ } else {
834
+ let highestVersion = "0.0.0";
835
+ for (const pkg of family.packages) {
836
+ const root = resolvePath(configDir, pkg.root);
837
+ const sourceJson = readSourcePackageJson(root, pkg.name);
838
+ const v = sourceJson.version ?? "0.0.0";
839
+ if (semver2.gt(v, highestVersion)) {
840
+ highestVersion = v;
841
+ }
842
+ }
843
+ newVersion = resolveVersion(highestVersion, strategy, family.name);
844
+ }
845
+ } catch (err) {
846
+ throw wrapError("family-version", family.name, err);
847
+ }
848
+ logger.info(`Family "${family.name}": shared version \u2192 ${newVersion} (${family.packages.length} packages)`);
849
+ const concurrency = options.concurrency ?? settings.concurrency ?? 4;
850
+ const familyCommit = family.commit;
851
+ const tasks = family.packages.map(
852
+ (pkg) => () => buildPackage(
853
+ pkg,
854
+ "auto",
855
+ // not used — forcedVersion overrides
856
+ settings,
857
+ options,
858
+ configDir,
859
+ familyCommit ?? pkg.commit,
860
+ newVersion
861
+ // all packages use the same version
862
+ )
863
+ );
864
+ return runParallel(tasks, concurrency);
865
+ }
866
+
867
+ // src/commands/build-family.ts
868
+ function registerBuildFamilyCommand(program2) {
869
+ program2.command("build:family <name>").description("Build all packages in a named family with a single shared version.").option("--dry-run", "Print what would happen without making any changes").option("--no-publish", "Skip npm publish").option("--no-git", "Skip git operations").option("--config <path>", "Path to config file").option("--concurrency <n>", "Override concurrency").action(async (familyName, opts) => {
870
+ const configPath = opts.config ?? findDefaultConfigPath(process.cwd());
871
+ const { config, configDir } = await loadConfig(configPath);
872
+ const rawOpts = opts;
873
+ const shouldPublish = rawOpts["publish"] !== false;
874
+ const buildOptions = {
875
+ dryRun: opts.dryRun ?? false,
876
+ noPublish: !shouldPublish,
877
+ noGit: opts.noGit ?? false,
878
+ concurrency: opts.concurrency ? parseInt(opts.concurrency, 10) : void 0,
879
+ configPath
880
+ };
881
+ const family = (config.families ?? []).find((f) => f.name === familyName);
882
+ if (!family) {
883
+ const available = (config.families ?? []).map((f) => f.name).join(", ");
884
+ logger.error(
885
+ `Family "${familyName}" not found. Available families: ${available || "(none)"}`
886
+ );
887
+ process.exit(1);
888
+ }
889
+ const results = await buildFamily(family, config.settings, buildOptions, configDir);
890
+ const anyFailed = results.some((r) => !r.success);
891
+ process.exit(anyFailed ? 1 : 0);
892
+ });
893
+ }
894
+
895
+ // src/commands/build-all.ts
896
+ function registerBuildAllCommand(program2) {
897
+ program2.command("build:all").description("Build all standalone packages and all families.").option("--dry-run", "Print what would happen without making any changes").option("--no-publish", "Skip npm publish").option("--no-git", "Skip git operations").option("--config <path>", "Path to config file").option("--concurrency <n>", "Override concurrency").action(async (opts) => {
898
+ const configPath = opts.config ?? findDefaultConfigPath(process.cwd());
899
+ const { config, configDir } = await loadConfig(configPath);
900
+ const rawOpts = opts;
901
+ const shouldPublish = rawOpts["publish"] !== false;
902
+ const buildOptions = {
903
+ dryRun: opts.dryRun ?? false,
904
+ noPublish: !shouldPublish,
905
+ noGit: opts.noGit ?? false,
906
+ concurrency: opts.concurrency ? parseInt(opts.concurrency, 10) : void 0,
907
+ configPath
908
+ };
909
+ const concurrency = buildOptions.concurrency ?? config.settings.concurrency ?? 4;
910
+ const allResults = [];
911
+ const standalone = config.standalone ?? [];
912
+ if (standalone.length > 0) {
913
+ logger.info(`Building ${standalone.length} standalone package(s)\u2026`);
914
+ const standaloneTasks = standalone.map(
915
+ (pkg) => () => buildPackage(
916
+ pkg,
917
+ pkg.version ?? "auto",
918
+ config.settings,
919
+ buildOptions,
920
+ configDir
921
+ )
922
+ );
923
+ const standaloneResults = await runParallel(standaloneTasks, concurrency);
924
+ allResults.push(...standaloneResults);
925
+ }
926
+ const families = config.families ?? [];
927
+ if (families.length > 0) {
928
+ logger.info(`Building ${families.length} famil${families.length === 1 ? "y" : "ies"}\u2026`);
929
+ for (const family of families) {
930
+ const familyResults = await buildFamily(
931
+ family,
932
+ config.settings,
933
+ buildOptions,
934
+ configDir
935
+ );
936
+ allResults.push(...familyResults);
937
+ }
938
+ }
939
+ const succeeded = allResults.filter((r) => r.success).length;
940
+ const failed = allResults.filter((r) => !r.success).length;
941
+ logger.info(`
942
+ Summary: ${succeeded} succeeded, ${failed} failed out of ${allResults.length} total`);
943
+ process.exit(failed > 0 ? 1 : 0);
944
+ });
945
+ }
946
+
947
+ // src/commands/list.ts
948
+ import chalk2 from "chalk";
949
+ function registerListCommand(program2) {
950
+ program2.command("list").description("List all registered standalone packages and families.").option("--config <path>", "Path to config file").action(async (opts) => {
951
+ const configPath = opts.config ?? findDefaultConfigPath(process.cwd());
952
+ const { config, configDir } = await loadConfig(configPath);
953
+ const standalone = config.standalone ?? [];
954
+ const families = config.families ?? [];
955
+ console.log(chalk2.bold("\n=== Standalone Packages ==="));
956
+ if (standalone.length === 0) {
957
+ console.log(chalk2.gray(" (none)"));
958
+ } else {
959
+ for (const pkg of standalone) {
960
+ const root = resolvePath(configDir, pkg.root);
961
+ let version = "?";
962
+ try {
963
+ const json = readSourcePackageJson(root, pkg.name);
964
+ version = json.version;
965
+ } catch {
966
+ version = chalk2.red("(package.json missing)");
967
+ }
968
+ const type = pkg.type ?? "typescript";
969
+ const formats = (pkg.formats ?? ["esm", "cjs"]).join(", ");
970
+ console.log(
971
+ ` ${chalk2.cyan(pkg.name)} ${chalk2.yellow(`v${version}`)}` + chalk2.gray(` [${type}] [${formats}]`)
972
+ );
973
+ console.log(chalk2.gray(` root: ${root}`));
974
+ }
975
+ }
976
+ console.log(chalk2.bold("\n=== Families ==="));
977
+ if (families.length === 0) {
978
+ console.log(chalk2.gray(" (none)"));
979
+ } else {
980
+ for (const family of families) {
981
+ console.log(chalk2.bold(`
982
+ Family: ${chalk2.magenta(family.name)}`));
983
+ for (const pkg of family.packages) {
984
+ const root = resolvePath(configDir, pkg.root);
985
+ let version = "?";
986
+ try {
987
+ const json = readSourcePackageJson(root, pkg.name);
988
+ version = json.version;
989
+ } catch {
990
+ version = chalk2.red("(package.json missing)");
991
+ }
992
+ const type = pkg.type ?? "typescript";
993
+ const formats = (pkg.formats ?? ["esm", "cjs"]).join(", ");
994
+ console.log(
995
+ ` ${chalk2.cyan(pkg.name)} ${chalk2.yellow(`v${version}`)}` + chalk2.gray(` [${type}] [${formats}]`)
996
+ );
997
+ console.log(chalk2.gray(` root: ${root}`));
998
+ }
999
+ }
1000
+ }
1001
+ console.log("");
1002
+ });
1003
+ }
1004
+
1005
+ // src/commands/validate.ts
1006
+ import chalk3 from "chalk";
1007
+ import fs5 from "fs";
1008
+ function registerValidateCommand(program2) {
1009
+ program2.command("validate").description("Validate the config file and check that all package roots exist.").option("--config <path>", "Path to config file").action(async (opts) => {
1010
+ const configPath = opts.config ?? findDefaultConfigPath(process.cwd());
1011
+ let config;
1012
+ let configDir;
1013
+ try {
1014
+ const loaded = await loadConfig(configPath);
1015
+ config = loaded.config;
1016
+ configDir = loaded.configDir;
1017
+ } catch (err) {
1018
+ logger.error(
1019
+ `Config load failed: ${err instanceof Error ? err.message : String(err)}`
1020
+ );
1021
+ process.exit(1);
1022
+ }
1023
+ const issues = [];
1024
+ const allPackages = [
1025
+ ...(config.standalone ?? []).map((p) => ({
1026
+ name: p.name,
1027
+ root: p.root,
1028
+ context: "standalone"
1029
+ })),
1030
+ ...(config.families ?? []).flatMap(
1031
+ (f) => f.packages.map((p) => ({
1032
+ name: p.name,
1033
+ root: p.root,
1034
+ context: `family:${f.name}`
1035
+ }))
1036
+ )
1037
+ ];
1038
+ for (const { name, root, context } of allPackages) {
1039
+ const abs = resolvePath(configDir, root);
1040
+ if (!fs5.existsSync(abs)) {
1041
+ issues.push({
1042
+ level: "error",
1043
+ message: `[${context}] "${name}": root does not exist \u2014 ${abs}`
1044
+ });
1045
+ continue;
1046
+ }
1047
+ const pkgJsonPath = resolvePath(abs, "package.json");
1048
+ if (!fs5.existsSync(pkgJsonPath)) {
1049
+ issues.push({
1050
+ level: "error",
1051
+ message: `[${context}] "${name}": package.json not found at ${pkgJsonPath}`
1052
+ });
1053
+ continue;
1054
+ }
1055
+ try {
1056
+ const raw = fs5.readFileSync(pkgJsonPath, "utf-8");
1057
+ const parsed = JSON.parse(raw);
1058
+ if (!parsed["version"]) {
1059
+ issues.push({
1060
+ level: "error",
1061
+ message: `[${context}] "${name}": package.json is missing "version" field`
1062
+ });
1063
+ }
1064
+ } catch (err) {
1065
+ issues.push({
1066
+ level: "error",
1067
+ message: `[${context}] "${name}": failed to parse package.json \u2014 ${err instanceof Error ? err.message : String(err)}`
1068
+ });
1069
+ }
1070
+ }
1071
+ const buildDir = resolvePath(configDir, config.settings.buildDir);
1072
+ if (!fs5.existsSync(buildDir)) {
1073
+ issues.push({
1074
+ level: "warn",
1075
+ message: `settings.buildDir does not exist yet and will be created on first build: ${buildDir}`
1076
+ });
1077
+ }
1078
+ if (issues.length === 0) {
1079
+ console.log(chalk3.green(`Config is valid. ${allPackages.length} package(s) found.`));
1080
+ process.exit(0);
1081
+ } else {
1082
+ const errors = issues.filter((i) => i.level === "error");
1083
+ const warnings = issues.filter((i) => i.level === "warn");
1084
+ for (const issue of warnings) {
1085
+ console.log(chalk3.yellow(`WARN ${issue.message}`));
1086
+ }
1087
+ for (const issue of errors) {
1088
+ console.log(chalk3.red(`ERROR ${issue.message}`));
1089
+ }
1090
+ console.log(
1091
+ `
1092
+ ${errors.length} error(s), ${warnings.length} warning(s) found.`
1093
+ );
1094
+ process.exit(errors.length > 0 ? 1 : 0);
1095
+ }
1096
+ });
1097
+ }
1098
+
1099
+ // src/cli.ts
1100
+ var __dirname2 = path5.dirname(fileURLToPath(import.meta.url));
1101
+ var cliVersion = "1.0.0";
1102
+ try {
1103
+ const pkgPath = path5.resolve(__dirname2, "../package.json");
1104
+ const raw = readFileSync(pkgPath, "utf-8");
1105
+ const pkg = JSON.parse(raw);
1106
+ cliVersion = pkg.version ?? "1.0.0";
1107
+ } catch {
1108
+ }
1109
+ var program = new Command();
1110
+ program.name("pkgist").description("Build, version, and publish TypeScript npm packages using tsdown.").version(cliVersion).option("--verbose", "Enable verbose debug logging", false).hook("preAction", (thisCommand) => {
1111
+ const opts = thisCommand.opts();
1112
+ if (opts.verbose) {
1113
+ setVerbose(true);
1114
+ }
1115
+ });
1116
+ registerBuildCommand(program);
1117
+ registerBuildFamilyCommand(program);
1118
+ registerBuildAllCommand(program);
1119
+ registerListCommand(program);
1120
+ registerValidateCommand(program);
1121
+ program.parseAsync(process.argv).catch((err) => {
1122
+ console.error(err instanceof Error ? err.message : String(err));
1123
+ process.exit(1);
1124
+ });
1125
+ //# sourceMappingURL=cli.js.map