@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/index.js ADDED
@@ -0,0 +1,824 @@
1
+ // src/config/define-config.ts
2
+ function defineConfig(config) {
3
+ return config;
4
+ }
5
+
6
+ // src/config/load-config.ts
7
+ import path2 from "path";
8
+ import { pathToFileURL } from "url";
9
+ import fs from "fs";
10
+
11
+ // src/utils/paths.ts
12
+ import path from "path";
13
+ function toForwardSlash(p) {
14
+ return p.replace(/\\/g, "/");
15
+ }
16
+ function joinPath(...segments) {
17
+ return toForwardSlash(path.join(...segments));
18
+ }
19
+ function resolvePath(...segments) {
20
+ return toForwardSlash(path.resolve(...segments));
21
+ }
22
+ function scopelessName(packageName) {
23
+ const slash = packageName.indexOf("/");
24
+ return slash === -1 ? packageName : packageName.slice(slash + 1);
25
+ }
26
+ function buildOutputPath(buildDir, packageName, version) {
27
+ return joinPath(buildDir, scopelessName(packageName), version);
28
+ }
29
+ function sourceSnapshotPath(sourcesDir, packageName) {
30
+ return joinPath(sourcesDir, scopelessName(packageName));
31
+ }
32
+
33
+ // src/utils/errors.ts
34
+ var BundlerError = class extends Error {
35
+ constructor(step, packageName, message, cause) {
36
+ super(message);
37
+ this.step = step;
38
+ this.packageName = packageName;
39
+ this.cause = cause;
40
+ this.name = "BundlerError";
41
+ if (cause instanceof Error && cause.stack) {
42
+ this.stack = `${this.stack}
43
+ Caused by: ${cause.stack}`;
44
+ }
45
+ }
46
+ step;
47
+ packageName;
48
+ cause;
49
+ };
50
+ function wrapError(step, packageName, error) {
51
+ if (error instanceof BundlerError) return error;
52
+ const msg = error instanceof Error ? error.message : typeof error === "string" ? error : JSON.stringify(error);
53
+ return new BundlerError(step, packageName, `[${step}] ${packageName}: ${msg}`, error);
54
+ }
55
+
56
+ // src/config/load-config.ts
57
+ async function loadConfig(configFilePath) {
58
+ const absolute = resolvePath(configFilePath);
59
+ if (!fs.existsSync(absolute)) {
60
+ throw new Error(`Config file not found: ${absolute}`);
61
+ }
62
+ let mod;
63
+ try {
64
+ const fileUrl = pathToFileURL(absolute).href;
65
+ mod = await import(fileUrl);
66
+ } catch (err) {
67
+ throw wrapError("load-config", absolute, err);
68
+ }
69
+ const config = mod.default;
70
+ if (!config || typeof config !== "object") {
71
+ throw new Error(
72
+ `Config file ${absolute} must export a default object from defineConfig(). Got: ${typeof config}`
73
+ );
74
+ }
75
+ validateConfig(config, absolute);
76
+ return {
77
+ config,
78
+ configPath: absolute,
79
+ configDir: resolvePath(path2.dirname(absolute))
80
+ };
81
+ }
82
+ function validateConfig(config, filePath) {
83
+ if (!config.settings) {
84
+ throw new Error(`Config "${filePath}" must have a "settings" object.`);
85
+ }
86
+ if (!config.settings.buildDir) {
87
+ throw new Error(`Config "${filePath}" settings.buildDir is required.`);
88
+ }
89
+ const names = /* @__PURE__ */ new Set();
90
+ for (const pkg of config.standalone ?? []) {
91
+ if (!pkg.name) throw new Error(`Standalone package is missing "name" in ${filePath}`);
92
+ if (!pkg.root) throw new Error(`Standalone package "${pkg.name}" is missing "root"`);
93
+ if (names.has(pkg.name)) throw new Error(`Duplicate package name: "${pkg.name}"`);
94
+ names.add(pkg.name);
95
+ }
96
+ for (const family of config.families ?? []) {
97
+ if (!family.name) throw new Error(`A family is missing "name" in ${filePath}`);
98
+ for (const pkg of family.packages) {
99
+ if (!pkg.name) throw new Error(`Package inside family "${family.name}" is missing "name"`);
100
+ if (!pkg.root) throw new Error(`Package "${pkg.name}" in family "${family.name}" is missing "root"`);
101
+ if (names.has(pkg.name)) throw new Error(`Duplicate package name: "${pkg.name}"`);
102
+ names.add(pkg.name);
103
+ }
104
+ }
105
+ }
106
+ function findDefaultConfigPath(cwd) {
107
+ const candidates = ["builder.ts", "builder.js", "mongez.ts", "mongez.js"];
108
+ for (const candidate of candidates) {
109
+ const full = path2.join(cwd, candidate);
110
+ if (fs.existsSync(full)) return full;
111
+ }
112
+ throw new Error(
113
+ `No config file found in ${cwd}. Tried: ${candidates.join(", ")}. Use --config <path> to specify a custom location.`
114
+ );
115
+ }
116
+
117
+ // src/files/file-manager.ts
118
+ import fs2 from "fs";
119
+ import path3 from "path";
120
+ function ensureDir(dir) {
121
+ try {
122
+ fs2.mkdirSync(dir, { recursive: true });
123
+ } catch (err) {
124
+ throw wrapError("ensure-dir", dir, err);
125
+ }
126
+ }
127
+ function copyFile(src, dest) {
128
+ ensureDir(path3.dirname(dest));
129
+ try {
130
+ fs2.copyFileSync(src, dest);
131
+ } catch (err) {
132
+ throw wrapError("copy-file", src, err);
133
+ }
134
+ }
135
+ var COPY_EXCLUDES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".turbo", ".cache"]);
136
+ function copyDir(src, dest) {
137
+ ensureDir(dest);
138
+ const entries = fs2.readdirSync(src, { withFileTypes: true });
139
+ for (const entry of entries) {
140
+ if (COPY_EXCLUDES.has(entry.name)) continue;
141
+ const srcPath = joinPath(src, entry.name);
142
+ const destPath = joinPath(dest, entry.name);
143
+ if (entry.isDirectory()) {
144
+ copyDir(srcPath, destPath);
145
+ } else {
146
+ copyFile(srcPath, destPath);
147
+ }
148
+ }
149
+ }
150
+ function readFile(filePath, packageName = filePath) {
151
+ try {
152
+ return fs2.readFileSync(filePath, "utf-8");
153
+ } catch (err) {
154
+ throw wrapError("read-file", packageName, err);
155
+ }
156
+ }
157
+ function writeFile(filePath, content, packageName = filePath) {
158
+ ensureDir(path3.dirname(filePath));
159
+ try {
160
+ fs2.writeFileSync(filePath, content, "utf-8");
161
+ } catch (err) {
162
+ throw wrapError("write-file", packageName, err);
163
+ }
164
+ }
165
+ function moveFile(src, dest, packageName = src) {
166
+ ensureDir(path3.dirname(dest));
167
+ try {
168
+ fs2.renameSync(src, dest);
169
+ } catch (err) {
170
+ try {
171
+ fs2.copyFileSync(src, dest);
172
+ fs2.unlinkSync(src);
173
+ } catch (fallbackErr) {
174
+ throw wrapError("move-file", packageName, fallbackErr);
175
+ }
176
+ }
177
+ }
178
+ function pathExists(p) {
179
+ return fs2.existsSync(p);
180
+ }
181
+
182
+ // src/files/package-json.ts
183
+ var KEPT_FIELDS = [
184
+ "name",
185
+ "description",
186
+ "keywords",
187
+ "author",
188
+ "license",
189
+ "repository",
190
+ "homepage",
191
+ "bugs",
192
+ "dependencies",
193
+ "peerDependencies",
194
+ "sideEffects",
195
+ "bin",
196
+ "engines"
197
+ ];
198
+ function readSourcePackageJson(packageRoot, packageName) {
199
+ const pkgPath = joinPath(packageRoot, "package.json");
200
+ let raw;
201
+ try {
202
+ raw = readFile(pkgPath, packageName);
203
+ } catch {
204
+ throw wrapError(
205
+ "read-package-json",
206
+ packageName,
207
+ new Error(`package.json not found at ${pkgPath}`)
208
+ );
209
+ }
210
+ try {
211
+ const parsed = JSON.parse(raw);
212
+ if (!parsed.version) {
213
+ throw new Error(`package.json at ${pkgPath} is missing "version" field`);
214
+ }
215
+ return parsed;
216
+ } catch (err) {
217
+ throw wrapError("parse-package-json", packageName, err);
218
+ }
219
+ }
220
+ function writeSourceVersion(packageRoot, packageName, newVersion) {
221
+ const pkgPath = joinPath(packageRoot, "package.json");
222
+ const raw = readFile(pkgPath, packageName);
223
+ const parsed = JSON.parse(raw);
224
+ parsed.version = newVersion;
225
+ writeFile(pkgPath, JSON.stringify(parsed, null, 2) + "\n", packageName);
226
+ }
227
+ function writeBuildPackageJson(pkg, sourceJson, buildPath, newVersion) {
228
+ const formats = pkg.formats ?? ["esm", "cjs"];
229
+ const hasEsm = formats.includes("esm");
230
+ const hasCjs = formats.includes("cjs");
231
+ const esmOnly = hasEsm && !hasCjs;
232
+ const preserve = pkg.preserveModules !== false;
233
+ const esmExt = preserve ? ".mjs" : ".js";
234
+ const cjsExt = preserve ? ".cjs" : ".js";
235
+ const esmDts = preserve ? ".d.mts" : ".d.ts";
236
+ const cjsDts = preserve ? ".d.cts" : ".d.ts";
237
+ const rawEntries = pkg.entries ?? ["index.ts"];
238
+ const entryList = Array.isArray(rawEntries) ? rawEntries : [rawEntries];
239
+ function entryPaths(entry) {
240
+ const basePath = entry.replace(/\.tsx?$/, "");
241
+ const parts = basePath.split("/");
242
+ const last = parts[parts.length - 1];
243
+ let exportPath;
244
+ if (last === "index") {
245
+ const dir = parts.slice(0, -1).join("/");
246
+ exportPath = dir ? `./${dir}` : ".";
247
+ } else {
248
+ exportPath = `./${basePath}`;
249
+ }
250
+ return { basePath, exportPath };
251
+ }
252
+ const { basePath: primaryBase } = entryPaths(entryList[0]);
253
+ const exportsMap = {};
254
+ for (const entry of entryList) {
255
+ const { basePath, exportPath } = entryPaths(entry);
256
+ const conditions = {};
257
+ if (hasEsm) {
258
+ conditions["import"] = {
259
+ types: `./esm/${basePath}${esmDts}`,
260
+ default: `./esm/${basePath}${esmExt}`
261
+ };
262
+ }
263
+ if (hasCjs) {
264
+ const cjsTypes = preserve && hasEsm ? `./esm/${basePath}${esmDts}` : `./cjs/${basePath}${cjsDts}`;
265
+ conditions["require"] = {
266
+ types: cjsTypes,
267
+ default: `./cjs/${basePath}${cjsExt}`
268
+ };
269
+ }
270
+ exportsMap[exportPath] = conditions;
271
+ }
272
+ const output = {};
273
+ for (const field of KEPT_FIELDS) {
274
+ if (field in sourceJson && sourceJson[field] !== void 0) {
275
+ output[field] = sourceJson[field];
276
+ }
277
+ }
278
+ output["name"] = pkg.name;
279
+ output["version"] = newVersion;
280
+ if (esmOnly) {
281
+ output["type"] = "module";
282
+ }
283
+ const mainType = pkg.mainType ?? "cjs";
284
+ if (mainType === "esm" || esmOnly) {
285
+ output["main"] = `./esm/${primaryBase}${esmExt}`;
286
+ } else {
287
+ output["main"] = `./cjs/${primaryBase}${cjsExt}`;
288
+ }
289
+ if (hasEsm) {
290
+ output["module"] = `./esm/${primaryBase}${esmExt}`;
291
+ }
292
+ output["types"] = `./esm/${primaryBase}${esmDts}`;
293
+ output["exports"] = exportsMap;
294
+ const pkgPath = joinPath(buildPath, "package.json");
295
+ writeFile(pkgPath, JSON.stringify(output, null, 2) + "\n", pkg.name);
296
+ }
297
+
298
+ // src/files/clone-files.ts
299
+ import fs3 from "fs";
300
+
301
+ // src/utils/logger.ts
302
+ import chalk from "chalk";
303
+ var _verbose = false;
304
+ function log(level, message) {
305
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace("Z", "");
306
+ switch (level) {
307
+ case "info":
308
+ console.log(chalk.cyan(`[${ts}] INFO ${message}`));
309
+ break;
310
+ case "success":
311
+ console.log(chalk.green(`[${ts}] OK ${message}`));
312
+ break;
313
+ case "warn":
314
+ console.warn(chalk.yellow(`[${ts}] WARN ${message}`));
315
+ break;
316
+ case "error":
317
+ console.error(chalk.red(`[${ts}] ERROR ${message}`));
318
+ break;
319
+ case "step":
320
+ console.log(chalk.blueBright(`[${ts}] STEP ${message}`));
321
+ break;
322
+ case "debug":
323
+ if (_verbose) {
324
+ console.log(chalk.gray(`[${ts}] DEBUG ${message}`));
325
+ }
326
+ break;
327
+ }
328
+ }
329
+ var logger = {
330
+ info: (msg) => log("info", msg),
331
+ success: (msg) => log("success", msg),
332
+ warn: (msg) => log("warn", msg),
333
+ error: (msg) => log("error", msg),
334
+ step: (msg) => log("step", msg),
335
+ debug: (msg) => log("debug", msg)
336
+ };
337
+
338
+ // src/files/clone-files.ts
339
+ function cloneFiles(packageRoot, buildDir, cloneList, packageName, dryRun) {
340
+ for (const entry of cloneList) {
341
+ const [srcRel, destRel] = typeof entry === "string" ? [entry, entry] : entry;
342
+ const src = joinPath(packageRoot, srcRel);
343
+ const dest = joinPath(buildDir, destRel);
344
+ if (!pathExists(src)) {
345
+ logger.warn(`[clone-files] ${packageName}: source file not found, skipping \u2014 ${src}`);
346
+ continue;
347
+ }
348
+ if (dryRun) {
349
+ logger.info(`[dry-run] clone ${src} \u2192 ${dest}`);
350
+ continue;
351
+ }
352
+ const stat = fs3.statSync(src);
353
+ if (stat.isDirectory()) {
354
+ copyDir(src, dest);
355
+ } else {
356
+ copyFile(src, dest);
357
+ }
358
+ logger.debug(`Cloned ${src} \u2192 ${dest}`);
359
+ }
360
+ }
361
+
362
+ // src/compile/tsdown-compiler.ts
363
+ import path4 from "path";
364
+ import fs4 from "fs";
365
+ import { build } from "tsdown";
366
+ function collectExternals(sourceJson) {
367
+ const deps = Object.keys(sourceJson["dependencies"] ?? {});
368
+ const peers = Object.keys(sourceJson["peerDependencies"] ?? {});
369
+ return [.../* @__PURE__ */ new Set([...deps, ...peers])];
370
+ }
371
+ function resolveEntries(pkg, packageRoot) {
372
+ const srcDir = pkg.srcDir ?? "src";
373
+ const rawEntries = pkg.entries ?? ["index.ts"];
374
+ const entryList = Array.isArray(rawEntries) ? rawEntries : [rawEntries];
375
+ return entryList.map((e) => toForwardSlash(resolvePath(packageRoot, srcDir, e)));
376
+ }
377
+ function findTsconfig(packageRoot) {
378
+ const local = joinPath(packageRoot, "tsconfig.json");
379
+ return pathExists(local) ? local : void 0;
380
+ }
381
+ async function compilePackage(pkg, packageRoot, buildPath, sourceJson, dryRun) {
382
+ if (dryRun) {
383
+ logger.info(`[dry-run] compile ${pkg.name} \u2192 ${buildPath}`);
384
+ return;
385
+ }
386
+ const entries = resolveEntries(pkg, packageRoot);
387
+ const formats = pkg.formats ?? ["esm", "cjs"];
388
+ const externals = collectExternals(sourceJson);
389
+ const tsconfig = findTsconfig(packageRoot);
390
+ const shouldPreserveModules = pkg.preserveModules !== false;
391
+ const srcDir = toForwardSlash(resolvePath(packageRoot, pkg.srcDir ?? "src"));
392
+ logger.step(`Compiling ${pkg.name} (${formats.join(", ")}) \u2192 ${buildPath}`);
393
+ logger.debug(` entries: ${entries.join(", ")}`);
394
+ logger.debug(` externals: ${externals.slice(0, 8).join(", ")}${externals.length > 8 ? "\u2026" : ""}`);
395
+ for (const subDir of ["esm", "cjs"]) {
396
+ const dir = resolvePath(buildPath, subDir);
397
+ if (fs4.existsSync(dir)) {
398
+ fs4.rmSync(dir, { recursive: true, force: true });
399
+ }
400
+ }
401
+ const rolldownInputOptions = pkg.type === "react" ? {
402
+ transform: {
403
+ jsx: "react-jsx"
404
+ // automatic runtime — works with React 17+
405
+ }
406
+ } : void 0;
407
+ const sharedOptions = {
408
+ entry: entries,
409
+ deps: { neverBundle: externals },
410
+ sourcemap: pkg.sourcemap !== false,
411
+ clean: true,
412
+ minify: pkg.minify ?? false,
413
+ tsconfig: tsconfig ?? true,
414
+ ...rolldownInputOptions ? { inputOptions: rolldownInputOptions } : {}
415
+ };
416
+ try {
417
+ if (shouldPreserveModules && formats.includes("esm") && formats.includes("cjs")) {
418
+ const tmpEsm = toForwardSlash(resolvePath(buildPath, "_tmp_esm"));
419
+ const tmpCjs = toForwardSlash(resolvePath(buildPath, "_tmp_cjs"));
420
+ ensureDir(tmpEsm);
421
+ ensureDir(tmpCjs);
422
+ await Promise.all([
423
+ build({
424
+ ...sharedOptions,
425
+ format: ["esm"],
426
+ dts: pkg.dts !== false,
427
+ outDir: tmpEsm,
428
+ outputOptions: {
429
+ preserveModules: true,
430
+ preserveModulesRoot: srcDir
431
+ }
432
+ }),
433
+ build({
434
+ ...sharedOptions,
435
+ format: ["cjs"],
436
+ // CJS skips DTS — types are served from ESM declarations (see package-json.ts).
437
+ dts: false,
438
+ outDir: tmpCjs
439
+ })
440
+ ]);
441
+ reorganiseOutput(tmpEsm, buildPath, ["esm"], pkg, true);
442
+ reorganiseOutput(tmpCjs, buildPath, ["cjs"], pkg, false, true);
443
+ rmTmp(tmpEsm);
444
+ rmTmp(tmpCjs);
445
+ } else {
446
+ const tmpDir = toForwardSlash(resolvePath(buildPath, "_tmp"));
447
+ ensureDir(tmpDir);
448
+ await build({
449
+ ...sharedOptions,
450
+ format: formats,
451
+ dts: pkg.dts !== false,
452
+ outDir: tmpDir,
453
+ ...shouldPreserveModules && formats.includes("esm") ? { outputOptions: { preserveModules: true, preserveModulesRoot: srcDir } } : {}
454
+ });
455
+ reorganiseOutput(tmpDir, buildPath, formats, pkg, shouldPreserveModules);
456
+ rmTmp(tmpDir);
457
+ }
458
+ } catch (err) {
459
+ throw wrapError("tsdown-build", pkg.name, err);
460
+ }
461
+ logger.success(`Compiled ${pkg.name}`);
462
+ }
463
+ function rmTmp(dir) {
464
+ try {
465
+ fs4.rmSync(dir, { recursive: true, force: true });
466
+ } catch {
467
+ }
468
+ }
469
+ function reorganiseOutput(tmpDir, buildPath, formats, pkg, preserveModules, keepNativeExtension = false) {
470
+ const esmDir = joinPath(buildPath, "esm");
471
+ const cjsDir = joinPath(buildPath, "cjs");
472
+ if (formats.includes("esm")) ensureDir(esmDir);
473
+ if (formats.includes("cjs")) ensureDir(cjsDir);
474
+ const mainType = pkg.mainType ?? (formats.includes("cjs") ? "cjs" : "esm");
475
+ processDirectory(tmpDir, { esmDir, cjsDir, formats, mainType, pkg, tmpDir, preserveModules, keepNativeExtension });
476
+ }
477
+ function processDirectory(dir, ctx) {
478
+ if (!fs4.existsSync(dir)) return;
479
+ const entries = fs4.readdirSync(dir, { withFileTypes: true });
480
+ for (const entry of entries) {
481
+ const fullPath = joinPath(dir, entry.name);
482
+ if (entry.isDirectory()) {
483
+ processDirectory(fullPath, ctx);
484
+ } else {
485
+ processFile(fullPath, entry.name, ctx);
486
+ }
487
+ }
488
+ }
489
+ function processFile(filePath, name, ctx) {
490
+ const { esmDir, cjsDir, formats, mainType, pkg, tmpDir, preserveModules } = ctx;
491
+ const relDir = toForwardSlash(path4.relative(tmpDir, path4.dirname(filePath)));
492
+ if (preserveModules) {
493
+ if (isEsmFile(name)) {
494
+ if (formats.includes("esm")) {
495
+ moveFile(filePath, joinPath(esmDir, relDir, name), pkg.name);
496
+ }
497
+ } else if (isCjsFile(name)) {
498
+ if (formats.includes("cjs")) {
499
+ moveFile(filePath, joinPath(cjsDir, relDir, name), pkg.name);
500
+ }
501
+ } else if (isDtsFile(name)) {
502
+ const targetDir = formats.includes("esm") ? esmDir : cjsDir;
503
+ moveFile(filePath, joinPath(targetDir, relDir, name), pkg.name);
504
+ }
505
+ return;
506
+ }
507
+ const { keepNativeExtension } = ctx;
508
+ if (isEsmFile(name)) {
509
+ if (formats.includes("esm")) {
510
+ const destName = keepNativeExtension ? name : normaliseEsmName(name);
511
+ moveFile(filePath, joinPath(esmDir, relDir, destName), pkg.name);
512
+ }
513
+ } else if (isCjsFile(name)) {
514
+ if (formats.includes("cjs")) {
515
+ const destName = keepNativeExtension ? name : normaliseCjsName(name);
516
+ moveFile(filePath, joinPath(cjsDir, relDir, destName), pkg.name);
517
+ }
518
+ } else if (isDtsFile(name)) {
519
+ const targetDir = formats.includes("esm") ? esmDir : cjsDir;
520
+ moveFile(filePath, joinPath(targetDir, relDir, name), pkg.name);
521
+ } else if (isJsFile(name)) {
522
+ const targetDir = mainType === "esm" ? esmDir : cjsDir;
523
+ if (formats.includes(mainType)) {
524
+ const destName = targetDir === cjsDir ? name.replace(/\.js\.map$/, ".cjs.map").replace(/\.js$/, ".cjs") : name;
525
+ moveFile(filePath, joinPath(targetDir, relDir, destName), pkg.name);
526
+ }
527
+ }
528
+ }
529
+ function isEsmFile(name) {
530
+ return name.endsWith(".mjs") || name.endsWith(".mjs.map") || name.endsWith(".d.mts") || name.endsWith(".d.mts.map");
531
+ }
532
+ function isCjsFile(name) {
533
+ return name.endsWith(".cjs") || name.endsWith(".cjs.map") || name.endsWith(".d.cts") || name.endsWith(".d.cts.map");
534
+ }
535
+ function isDtsFile(name) {
536
+ return name.endsWith(".d.ts") || name.endsWith(".d.ts.map");
537
+ }
538
+ function isJsFile(name) {
539
+ return (name.endsWith(".js") || name.endsWith(".js.map")) && !name.endsWith(".d.ts") && !name.endsWith(".d.ts.map");
540
+ }
541
+ function normaliseEsmName(name) {
542
+ return name.replace(/\.mjs\.map$/, ".js.map").replace(/\.mjs$/, ".js").replace(/\.d\.mts\.map$/, ".d.ts.map").replace(/\.d\.mts$/, ".d.ts");
543
+ }
544
+ function normaliseCjsName(name) {
545
+ return name.replace(/\.cjs\.map$/, ".js.map").replace(/\.cjs$/, ".js").replace(/\.d\.cts\.map$/, ".d.ts.map").replace(/\.d\.cts$/, ".d.ts");
546
+ }
547
+
548
+ // src/version/increment.ts
549
+ import semver from "semver";
550
+ function resolveVersion(currentVersion, strategy = "auto", packageName) {
551
+ const bumpType = strategy === "auto" || strategy === "patch" ? "patch" : strategy === "minor" ? "minor" : strategy === "major" ? "major" : null;
552
+ if (bumpType) {
553
+ const bumped = semver.inc(currentVersion, bumpType);
554
+ if (!bumped) {
555
+ throw wrapError(
556
+ "version-increment",
557
+ packageName,
558
+ new Error(
559
+ `Cannot ${bumpType}-bump invalid semver version "${currentVersion}" for package "${packageName}"`
560
+ )
561
+ );
562
+ }
563
+ return bumped;
564
+ }
565
+ const cleaned = semver.valid(semver.coerce(strategy) ?? strategy);
566
+ if (!cleaned) {
567
+ throw wrapError(
568
+ "version-increment",
569
+ packageName,
570
+ new Error(
571
+ `Explicit version "${strategy}" is not a valid semver string for package "${packageName}"`
572
+ )
573
+ );
574
+ }
575
+ return cleaned;
576
+ }
577
+
578
+ // src/git/operations.ts
579
+ import simpleGit from "simple-git";
580
+ async function isGitRepo(dir) {
581
+ try {
582
+ const git = simpleGit(dir);
583
+ const result = await git.checkIsRepo();
584
+ return result;
585
+ } catch {
586
+ return false;
587
+ }
588
+ }
589
+ async function gitCommitTagPush(packageRoot, packageName, version, commitMessage, branch, dryRun) {
590
+ const isRepo = await isGitRepo(packageRoot);
591
+ if (!isRepo) {
592
+ logger.warn(`[git] ${packageName}: directory is not a git repo \u2014 skipping git operations`);
593
+ return;
594
+ }
595
+ const tag = `v${version}`;
596
+ logger.step(`[git] ${packageName}: commit "${commitMessage}", tag ${tag}, push to ${branch}`);
597
+ if (dryRun) {
598
+ logger.info(`[dry-run] git add + commit "${commitMessage}" + tag ${tag} + push`);
599
+ return;
600
+ }
601
+ const git = simpleGit(packageRoot);
602
+ try {
603
+ await git.add("-A");
604
+ await git.commit(commitMessage);
605
+ await git.push("origin", branch);
606
+ await git.addTag(tag);
607
+ await git.pushTags("origin");
608
+ logger.success(`[git] ${packageName}: pushed ${branch}, tagged ${tag}`);
609
+ } catch (err) {
610
+ throw wrapError("git-operations", packageName, err);
611
+ }
612
+ }
613
+ async function currentBranch(dir) {
614
+ try {
615
+ const git = simpleGit(dir);
616
+ const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
617
+ return branch.trim() || "main";
618
+ } catch {
619
+ return "main";
620
+ }
621
+ }
622
+
623
+ // src/utils/exec.ts
624
+ import { execa } from "execa";
625
+ async function exec(step, packageName, command, args, cwd) {
626
+ try {
627
+ const result = await execa(command, args, {
628
+ cwd,
629
+ all: true,
630
+ reject: false
631
+ });
632
+ if (result.exitCode !== 0) {
633
+ const combined = result.all ?? result.stderr ?? "";
634
+ throw new Error(
635
+ `Command "${command} ${args.join(" ")}" exited with code ${result.exitCode}.
636
+ ${combined}`
637
+ );
638
+ }
639
+ return {
640
+ stdout: result.stdout ?? "",
641
+ stderr: result.stderr ?? "",
642
+ exitCode: result.exitCode
643
+ };
644
+ } catch (err) {
645
+ throw wrapError(step, packageName, err);
646
+ }
647
+ }
648
+
649
+ // src/publish/npm-publisher.ts
650
+ async function publishPackage(pkg, buildPath, dryRun) {
651
+ if (pkg.publish === false) {
652
+ logger.info(`[publish] ${pkg.name}: publish=false, skipping`);
653
+ return;
654
+ }
655
+ const access = pkg.access ?? "public";
656
+ if (dryRun) {
657
+ logger.info(`[dry-run] npm publish --access ${access} from ${buildPath}`);
658
+ return;
659
+ }
660
+ logger.step(`Publishing ${pkg.name}@... to npm (access: ${access})`);
661
+ await exec(
662
+ "npm-publish",
663
+ pkg.name,
664
+ "npm",
665
+ ["publish", "--access", access],
666
+ buildPath
667
+ );
668
+ logger.success(`Published ${pkg.name}`);
669
+ }
670
+
671
+ // src/build/package-builder.ts
672
+ async function buildPackage(pkg, versionStrategy = "auto", settings, options, configDir, overrideCommit, forcedVersion) {
673
+ const step = "package-builder";
674
+ try {
675
+ const packageRoot = resolvePath(configDir, pkg.root);
676
+ const sourceJson = readSourcePackageJson(packageRoot, pkg.name);
677
+ const currentVersion = sourceJson.version;
678
+ const newVersion = forcedVersion ?? resolveVersion(currentVersion, versionStrategy, pkg.name);
679
+ logger.info(`Building ${pkg.name}: ${currentVersion} \u2192 ${newVersion}`);
680
+ const absoluteBuildDir = resolvePath(configDir, settings.buildDir);
681
+ const buildPath = buildOutputPath(absoluteBuildDir, pkg.name, newVersion);
682
+ if (!options.dryRun) {
683
+ ensureDir(buildPath);
684
+ }
685
+ if (settings.sourcesDir) {
686
+ const absoluteSourcesDir = resolvePath(configDir, settings.sourcesDir);
687
+ const snapshotPath = sourceSnapshotPath(absoluteSourcesDir, pkg.name);
688
+ if (options.dryRun) {
689
+ logger.info(`[dry-run] snapshot ${packageRoot} \u2192 ${snapshotPath}`);
690
+ } else {
691
+ ensureDir(snapshotPath);
692
+ copyDir(packageRoot, snapshotPath);
693
+ logger.debug(`Snapshot: ${packageRoot} \u2192 ${snapshotPath}`);
694
+ }
695
+ }
696
+ await compilePackage(pkg, packageRoot, buildPath, sourceJson, options.dryRun);
697
+ if (pkg.clone && pkg.clone.length > 0) {
698
+ cloneFiles(packageRoot, buildPath, pkg.clone, pkg.name, options.dryRun);
699
+ }
700
+ if (!options.dryRun) {
701
+ writeBuildPackageJson(pkg, sourceJson, buildPath, newVersion);
702
+ logger.debug(`Wrote build package.json for ${pkg.name}@${newVersion}`);
703
+ } else {
704
+ logger.info(`[dry-run] write build package.json for ${pkg.name}@${newVersion}`);
705
+ }
706
+ if (!options.dryRun) {
707
+ writeSourceVersion(packageRoot, pkg.name, newVersion);
708
+ logger.debug(`Updated source version ${pkg.name} \u2192 ${newVersion}`);
709
+ } else {
710
+ logger.info(`[dry-run] update source package.json version \u2192 ${newVersion}`);
711
+ }
712
+ const commitMessage = overrideCommit ?? pkg.commit;
713
+ if (commitMessage && !options.noGit) {
714
+ const branch = pkg.branch ?? await currentBranch(packageRoot);
715
+ await gitCommitTagPush(
716
+ packageRoot,
717
+ pkg.name,
718
+ newVersion,
719
+ commitMessage,
720
+ branch,
721
+ options.dryRun
722
+ );
723
+ } else if (!commitMessage) {
724
+ logger.debug(`[git] ${pkg.name}: no commit message set \u2014 skipping git`);
725
+ }
726
+ if (!options.noPublish) {
727
+ await publishPackage(pkg, buildPath, options.dryRun);
728
+ } else {
729
+ logger.info(`[publish] ${pkg.name}: --no-publish, skipping`);
730
+ }
731
+ return { packageName: pkg.name, version: newVersion, buildPath, success: true };
732
+ } catch (err) {
733
+ const wrapped = wrapError(step, pkg.name, err);
734
+ logger.error(`Failed to build ${pkg.name}: ${wrapped.message}`);
735
+ return {
736
+ packageName: pkg.name,
737
+ version: "",
738
+ buildPath: "",
739
+ success: false,
740
+ error: wrapped
741
+ };
742
+ }
743
+ }
744
+
745
+ // src/build/family-builder.ts
746
+ import semver2 from "semver";
747
+
748
+ // src/build/parallel-builder.ts
749
+ import pLimit from "p-limit";
750
+ async function runParallel(tasks, concurrency) {
751
+ if (tasks.length === 0) return [];
752
+ const limit = pLimit(Math.max(1, concurrency));
753
+ logger.info(`Running ${tasks.length} task(s) with concurrency=${concurrency}`);
754
+ const results = await Promise.all(
755
+ tasks.map((task) => limit(task))
756
+ );
757
+ const succeeded = results.filter((r) => r.success).length;
758
+ const failed = results.filter((r) => !r.success).length;
759
+ if (failed > 0) {
760
+ logger.warn(`${succeeded} succeeded, ${failed} failed`);
761
+ for (const r of results.filter((r2) => !r2.success)) {
762
+ logger.error(` FAILED: ${r.packageName} \u2014 ${r.error?.message ?? "unknown error"}`);
763
+ }
764
+ } else {
765
+ logger.success(`All ${succeeded} package(s) built successfully`);
766
+ }
767
+ return results;
768
+ }
769
+
770
+ // src/build/family-builder.ts
771
+ async function buildFamily(family, settings, options, configDir) {
772
+ if (family.packages.length === 0) {
773
+ logger.warn(`Family "${family.name}" has no packages \u2014 nothing to build`);
774
+ return [];
775
+ }
776
+ let newVersion;
777
+ try {
778
+ const strategy = family.version ?? "auto";
779
+ const isBumpKeyword = strategy === "auto" || strategy === "patch" || strategy === "minor" || strategy === "major";
780
+ if (!isBumpKeyword && semver2.valid(semver2.coerce(strategy) ?? strategy)) {
781
+ newVersion = strategy;
782
+ } else {
783
+ let highestVersion = "0.0.0";
784
+ for (const pkg of family.packages) {
785
+ const root = resolvePath(configDir, pkg.root);
786
+ const sourceJson = readSourcePackageJson(root, pkg.name);
787
+ const v = sourceJson.version ?? "0.0.0";
788
+ if (semver2.gt(v, highestVersion)) {
789
+ highestVersion = v;
790
+ }
791
+ }
792
+ newVersion = resolveVersion(highestVersion, strategy, family.name);
793
+ }
794
+ } catch (err) {
795
+ throw wrapError("family-version", family.name, err);
796
+ }
797
+ logger.info(`Family "${family.name}": shared version \u2192 ${newVersion} (${family.packages.length} packages)`);
798
+ const concurrency = options.concurrency ?? settings.concurrency ?? 4;
799
+ const familyCommit = family.commit;
800
+ const tasks = family.packages.map(
801
+ (pkg) => () => buildPackage(
802
+ pkg,
803
+ "auto",
804
+ // not used — forcedVersion overrides
805
+ settings,
806
+ options,
807
+ configDir,
808
+ familyCommit ?? pkg.commit,
809
+ newVersion
810
+ // all packages use the same version
811
+ )
812
+ );
813
+ return runParallel(tasks, concurrency);
814
+ }
815
+ export {
816
+ buildFamily,
817
+ buildPackage,
818
+ defineConfig,
819
+ findDefaultConfigPath,
820
+ loadConfig,
821
+ resolveVersion,
822
+ runParallel
823
+ };
824
+ //# sourceMappingURL=index.js.map