knarr 0.0.2 → 0.1.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.
- package/README.md +1 -0
- package/dist/add-4Z3V5722.mjs +3 -0
- package/dist/bell-SIN4ERLS.mjs +2 -0
- package/dist/{check-YVEJEI2G.mjs → check-7CJHAF6Y.mjs} +1 -1
- package/dist/{chokidar-LVDD2IK4.mjs → chokidar-2G63V464.mjs} +1 -1
- package/dist/chunk-3SXN6MQL.mjs +4 -0
- package/dist/chunk-7GCKCCHU.mjs +3 -0
- package/dist/chunk-7ROQJISX.mjs +15 -0
- package/dist/chunk-AA43FODB.mjs +3 -0
- package/dist/chunk-ABG6UD3X.mjs +22 -0
- package/dist/chunk-BH2WXCLS.mjs +4 -0
- package/dist/chunk-COI7GWH3.mjs +4 -0
- package/dist/chunk-E3HZCU3S.mjs +5 -0
- package/dist/chunk-FSVETLZK.mjs +4 -0
- package/dist/{chunk-3KNUBUPH.mjs → chunk-G7HFTV5Y.mjs} +1 -1
- package/dist/chunk-GAWQLCRB.mjs +3 -0
- package/dist/{chunk-MBKCCWSD.mjs → chunk-HVN6HBWZ.mjs} +1 -1
- package/dist/{chunk-SYADAYF4.mjs → chunk-ICU4V73X.mjs} +1 -1
- package/dist/{chunk-XQPVRRTN.mjs → chunk-IP5ROSRE.mjs} +1 -1
- package/dist/chunk-J6HZHQVL.mjs +3 -0
- package/dist/chunk-L7O2RSTN.mjs +3 -0
- package/dist/chunk-M3TRMEJV.mjs +3 -0
- package/dist/chunk-MN4DV2NO.mjs +3 -0
- package/dist/chunk-N3B2JO4H.mjs +13 -0
- package/dist/{chunk-7HVPEBK5.mjs → chunk-NGPAGRK4.mjs} +1 -1
- package/dist/{chunk-B3DZ5HVQ.mjs → chunk-OQDZJP55.mjs} +1 -1
- package/dist/chunk-QEH5VHRO.mjs +13 -0
- package/dist/chunk-QFHYSC3K.mjs +3 -0
- package/dist/{chunk-CTJF2EWO.mjs → chunk-QHNHLR6T.mjs} +2 -2
- package/dist/chunk-RJSBXX2Q.mjs +3 -0
- package/dist/{chunk-NBSJGM2X.mjs → chunk-SPHWZS5P.mjs} +1 -1
- package/dist/chunk-U6O35NZ7.mjs +3 -0
- package/dist/chunk-UCSYKFEZ.mjs +3 -0
- package/dist/chunk-W2MMKBWU.mjs +3 -0
- package/dist/chunk-WBTSRLB6.mjs +7 -0
- package/dist/chunk-WN3DOKIM.mjs +3 -0
- package/dist/{chunk-TEFMLGCB.mjs → chunk-XDWZZBCM.mjs} +1 -1
- package/dist/{chunk-2GDRDQA5.mjs → chunk-Z5K6DFA6.mjs} +1 -1
- package/dist/clean-EJIYYQZM.mjs +3 -0
- package/dist/cli.mjs +4 -4
- package/dist/dev-PUNHH4MS.mjs +3 -0
- package/dist/doctor-ZK4OPSD4.mjs +6 -0
- package/dist/explain-BDTONX7H.mjs +5 -0
- package/dist/fs-NU36NNEG.mjs +2 -0
- package/dist/history-MLJHRV3Q.mjs +2 -0
- package/dist/index.d.ts +79 -18
- package/dist/index.mjs +1524 -443
- package/dist/init-X4F3XKVC.mjs +16 -0
- package/dist/{list-GLSA6I67.mjs → list-4PTHMIQ4.mjs} +2 -2
- package/dist/migrate-VHDLW6I7.mjs +8 -0
- package/dist/nextjs-config-T5D6LOJE.mjs +2 -0
- package/dist/preflight-IXICTR6G.mjs +2 -0
- package/dist/{publish-TAWTHPSD.mjs → publish-UGDDG6OT.mjs} +2 -2
- package/dist/push-V575ASM4.mjs +3 -0
- package/dist/remove-EPVDMWCS.mjs +2 -0
- package/dist/reset-5M6AU4FU.mjs +3 -0
- package/dist/restore-QPISOGEK.mjs +12 -0
- package/dist/rollback-CTGK7SXG.mjs +3 -0
- package/dist/{status-FA6UEHEF.mjs → status-3ZXM3CRH.mjs} +2 -2
- package/dist/{tailwind-source-RIWWXW2Y.mjs → tailwind-source-HCTY2XDU.mjs} +2 -2
- package/dist/topo-sort-ZNGR6D7M.mjs +2 -0
- package/dist/{tracker-JJEYXX45.mjs → tracker-QAKEXYUJ.mjs} +1 -1
- package/dist/update-RSPDDAJK.mjs +12 -0
- package/dist/use-LZ2GIXYZ.mjs +3 -0
- package/dist/vite-config-XOGAKYGQ.mjs +2 -0
- package/dist/watch-orchestrator-N6HZUDS3.mjs +3 -0
- package/dist/watcher-KOLIXRUG.mjs +3 -0
- package/dist/workspace-T6PNO4L3.mjs +2 -0
- package/dist/{xxhash-wasm-DTW44IIQ.mjs → xxhash-wasm-I6Q3T2SL.mjs} +1 -1
- package/package.json +10 -4
- package/dist/add-S4U56IVA.mjs +0 -3
- package/dist/bell-YD6IWNXO.mjs +0 -2
- package/dist/chunk-2VCW5RWI.mjs +0 -3
- package/dist/chunk-37AAX47E.mjs +0 -19
- package/dist/chunk-5BZD55UB.mjs +0 -3
- package/dist/chunk-6QHABEBL.mjs +0 -14
- package/dist/chunk-7JG555TZ.mjs +0 -3
- package/dist/chunk-7SDPRKFT.mjs +0 -13
- package/dist/chunk-BS4VKVYH.mjs +0 -3
- package/dist/chunk-EE2UYGFD.mjs +0 -4
- package/dist/chunk-GO6F6AGH.mjs +0 -3
- package/dist/chunk-GQYG5FCW.mjs +0 -5
- package/dist/chunk-KOHUNKHP.mjs +0 -3
- package/dist/chunk-LXGALE74.mjs +0 -13
- package/dist/chunk-OPLSUHCD.mjs +0 -4
- package/dist/chunk-QGLOGD5G.mjs +0 -3
- package/dist/chunk-SFLWVTJC.mjs +0 -3
- package/dist/chunk-SN4TOUQW.mjs +0 -7
- package/dist/chunk-UBGMLVMB.mjs +0 -3
- package/dist/chunk-XKO24LUM.mjs +0 -3
- package/dist/chunk-ZJEEAMB3.mjs +0 -3
- package/dist/clean-ZD5GPGXQ.mjs +0 -3
- package/dist/dev-TMWMIT7W.mjs +0 -3
- package/dist/doctor-AXP7GVBM.mjs +0 -4
- package/dist/fs-2NITBGIO.mjs +0 -2
- package/dist/history-SKKGT225.mjs +0 -2
- package/dist/init-XK4B7XYG.mjs +0 -7
- package/dist/migrate-MW4BVLL2.mjs +0 -8
- package/dist/preflight-TVJFHRI2.mjs +0 -2
- package/dist/push-65ZZFZJY.mjs +0 -3
- package/dist/remove-LOBXHBMC.mjs +0 -2
- package/dist/reset-EAN5NVGM.mjs +0 -3
- package/dist/restore-YHZYT54B.mjs +0 -11
- package/dist/rollback-U3LQTWH5.mjs +0 -3
- package/dist/topo-sort-WEIVPJKN.mjs +0 -2
- package/dist/update-VOUKMOLK.mjs +0 -3
- package/dist/use-DUZYEUVU.mjs +0 -3
- package/dist/vite-config-UWCLPTOZ.mjs +0 -2
- package/dist/watch-orchestrator-I2623SMT.mjs +0 -3
- package/dist/watcher-PTPUN2HE.mjs +0 -3
- package/dist/workspace-S3TAUSS3.mjs +0 -2
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __esm = (fn, res) => function __init() {
|
|
4
|
-
|
|
3
|
+
var __esm = (fn, res, err) => function __init() {
|
|
4
|
+
if (err) throw err[0];
|
|
5
|
+
try {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
throw err = [e], e;
|
|
9
|
+
}
|
|
5
10
|
};
|
|
6
11
|
var __export = (target, all) => {
|
|
7
12
|
for (var name in all)
|
|
@@ -293,7 +298,7 @@ var init_logger = __esm({
|
|
|
293
298
|
// src/utils/hash.ts
|
|
294
299
|
import { createHash } from "crypto";
|
|
295
300
|
import { readFile as readFile2, stat } from "fs/promises";
|
|
296
|
-
import { relative as relative2 } from "path";
|
|
301
|
+
import { join as join3, relative as relative2 } from "path";
|
|
297
302
|
import { availableParallelism } from "os";
|
|
298
303
|
function getXXHash() {
|
|
299
304
|
if (!_xxhash) {
|
|
@@ -304,18 +309,28 @@ function getXXHash() {
|
|
|
304
309
|
}
|
|
305
310
|
return _xxhash;
|
|
306
311
|
}
|
|
307
|
-
async function computeContentHash(files, baseDir) {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
312
|
+
async function computeContentHash(files, baseDir, contentOverrides = /* @__PURE__ */ new Map()) {
|
|
313
|
+
const fileByRel = new Map(
|
|
314
|
+
files.map((file) => [normalizePath(relative2(baseDir, file)), file])
|
|
315
|
+
);
|
|
316
|
+
for (const rel of contentOverrides.keys()) {
|
|
317
|
+
if (!fileByRel.has(rel)) fileByRel.set(rel, join3(baseDir, rel));
|
|
318
|
+
}
|
|
319
|
+
const sorted = [...fileByRel.entries()].sort(
|
|
320
|
+
([relA], [relB]) => relA.localeCompare(relB)
|
|
321
|
+
);
|
|
322
|
+
const currentFiles = new Set(fileByRel.values());
|
|
314
323
|
let cacheHits = 0;
|
|
315
324
|
const contents = await Promise.all(
|
|
316
325
|
sorted.map(
|
|
317
|
-
(file) => limit(async () => {
|
|
318
|
-
const
|
|
326
|
+
([rel, file]) => limit(async () => {
|
|
327
|
+
const override = contentOverrides.get(rel);
|
|
328
|
+
if (override !== void 0) {
|
|
329
|
+
return {
|
|
330
|
+
rel,
|
|
331
|
+
content: Buffer.isBuffer(override) ? override : Buffer.from(override)
|
|
332
|
+
};
|
|
333
|
+
}
|
|
319
334
|
const s = await stat(file);
|
|
320
335
|
const cached = _contentCache.get(file);
|
|
321
336
|
if (cached && cached.mtimeMs === s.mtimeMs && cached.size === s.size) {
|
|
@@ -331,7 +346,7 @@ async function computeContentHash(files, baseDir) {
|
|
|
331
346
|
for (const key of _contentCache.keys()) {
|
|
332
347
|
if (!currentFiles.has(key)) _contentCache.delete(key);
|
|
333
348
|
}
|
|
334
|
-
verbose(`[hash] Computing content hash for ${
|
|
349
|
+
verbose(`[hash] Computing content hash for ${sorted.length} files (${cacheHits} cached)`);
|
|
335
350
|
const hash = createHash("sha256");
|
|
336
351
|
const lenBuf = Buffer.alloc(4);
|
|
337
352
|
for (const { rel, content } of contents) {
|
|
@@ -382,13 +397,21 @@ var init_hash = __esm({
|
|
|
382
397
|
function recordMutation(mutation) {
|
|
383
398
|
mutations.push(mutation);
|
|
384
399
|
}
|
|
400
|
+
function getMutations() {
|
|
401
|
+
return [...mutations];
|
|
402
|
+
}
|
|
403
|
+
function markDryRunJsonReportPrinted() {
|
|
404
|
+
jsonReportPrinted = true;
|
|
405
|
+
}
|
|
385
406
|
function printDryRunReport() {
|
|
386
407
|
if (mutations.length === 0) {
|
|
387
408
|
consola.info("[dry-run] No mutations would be performed");
|
|
388
409
|
return;
|
|
389
410
|
}
|
|
390
411
|
if (isJsonOutput()) {
|
|
412
|
+
if (jsonReportPrinted) return;
|
|
391
413
|
console.log(JSON.stringify({ dryRun: true, mutations }, null, 2));
|
|
414
|
+
jsonReportPrinted = true;
|
|
392
415
|
return;
|
|
393
416
|
}
|
|
394
417
|
const grouped = /* @__PURE__ */ new Map();
|
|
@@ -430,14 +453,16 @@ function printDryRunReport() {
|
|
|
430
453
|
}
|
|
431
454
|
function resetMutations() {
|
|
432
455
|
mutations.length = 0;
|
|
456
|
+
jsonReportPrinted = false;
|
|
433
457
|
}
|
|
434
|
-
var mutations;
|
|
458
|
+
var mutations, jsonReportPrinted;
|
|
435
459
|
var init_dry_run = __esm({
|
|
436
460
|
"src/utils/dry-run.ts"() {
|
|
437
461
|
"use strict";
|
|
438
462
|
init_logger();
|
|
439
463
|
init_console();
|
|
440
464
|
mutations = [];
|
|
465
|
+
jsonReportPrinted = false;
|
|
441
466
|
}
|
|
442
467
|
});
|
|
443
468
|
|
|
@@ -469,7 +494,7 @@ import {
|
|
|
469
494
|
writeFile,
|
|
470
495
|
constants
|
|
471
496
|
} from "fs/promises";
|
|
472
|
-
import { join as
|
|
497
|
+
import { join as join4, dirname, relative as relative3, parse as parsePath } from "path";
|
|
473
498
|
import { availableParallelism as availableParallelism2 } from "os";
|
|
474
499
|
function isNodeError(err) {
|
|
475
500
|
return err instanceof Error && "code" in err;
|
|
@@ -508,7 +533,39 @@ async function copyWithCoW(src, dest, options) {
|
|
|
508
533
|
}
|
|
509
534
|
async function collectFiles(dir) {
|
|
510
535
|
const entries = await readdir(dir, { recursive: true, withFileTypes: true });
|
|
511
|
-
return entries.filter((e) => e.isFile()).map((e) =>
|
|
536
|
+
return entries.filter((e) => e.isFile()).map((e) => join4(e.parentPath, e.name));
|
|
537
|
+
}
|
|
538
|
+
async function removeDestinationConflicts(destDir, relPath) {
|
|
539
|
+
const parts = relPath.split(/[\\/]/).filter(Boolean);
|
|
540
|
+
let current = destDir;
|
|
541
|
+
for (let i = 0; i < parts.length; i++) {
|
|
542
|
+
current = join4(current, parts[i]);
|
|
543
|
+
try {
|
|
544
|
+
const s = await stat2(current);
|
|
545
|
+
const isLeaf = i === parts.length - 1;
|
|
546
|
+
if (s.isDirectory()) {
|
|
547
|
+
if (isLeaf) {
|
|
548
|
+
if (isDryRun()) {
|
|
549
|
+
recordMutation({ type: "remove", path: current });
|
|
550
|
+
} else {
|
|
551
|
+
await rm(current, { recursive: true, force: true });
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (!isLeaf) {
|
|
557
|
+
if (isDryRun()) {
|
|
558
|
+
recordMutation({ type: "remove", path: current });
|
|
559
|
+
return;
|
|
560
|
+
} else {
|
|
561
|
+
await rm(current, { force: true });
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
} catch (err) {
|
|
565
|
+
if (isNodeError(err) && err.code === "ENOENT") return;
|
|
566
|
+
throw err;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
512
569
|
}
|
|
513
570
|
async function incrementalCopy(srcDir, destDir, options = {}) {
|
|
514
571
|
const srcFilesPromise = collectFiles(srcDir);
|
|
@@ -524,7 +581,7 @@ async function incrementalCopy(srcDir, destDir, options = {}) {
|
|
|
524
581
|
srcFiles.map(
|
|
525
582
|
(srcFile) => ioLimit(async () => {
|
|
526
583
|
const rel = relative3(srcDir, srcFile);
|
|
527
|
-
const destFile =
|
|
584
|
+
const destFile = join4(destDir, rel);
|
|
528
585
|
let needsCopy = true;
|
|
529
586
|
let srcTimes = null;
|
|
530
587
|
if (options.force) {
|
|
@@ -554,7 +611,7 @@ async function incrementalCopy(srcDir, destDir, options = {}) {
|
|
|
554
611
|
}
|
|
555
612
|
}
|
|
556
613
|
} catch (err) {
|
|
557
|
-
if (isNodeError(err) && err.code === "ENOENT") {
|
|
614
|
+
if (isNodeError(err) && (err.code === "ENOENT" || err.code === "ENOTDIR")) {
|
|
558
615
|
verbose(`[copy] ${rel} (new file)`);
|
|
559
616
|
} else {
|
|
560
617
|
throw err;
|
|
@@ -562,7 +619,9 @@ async function incrementalCopy(srcDir, destDir, options = {}) {
|
|
|
562
619
|
}
|
|
563
620
|
}
|
|
564
621
|
if (needsCopy) {
|
|
622
|
+
await removeDestinationConflicts(destDir, rel);
|
|
565
623
|
await copyWithCoW(srcFile, destFile);
|
|
624
|
+
if (isDryRun()) return "copied";
|
|
566
625
|
if (!srcTimes) {
|
|
567
626
|
const s = await stat2(srcFile);
|
|
568
627
|
srcTimes = { atime: s.atime, mtime: s.mtime };
|
|
@@ -589,7 +648,12 @@ async function incrementalCopy(srcDir, destDir, options = {}) {
|
|
|
589
648
|
if (isDryRun()) {
|
|
590
649
|
recordMutation({ type: "remove", path: destFile });
|
|
591
650
|
} else {
|
|
592
|
-
await rm(destFile)
|
|
651
|
+
await rm(destFile, { force: true }).catch((err) => {
|
|
652
|
+
if (isNodeError(err) && (err.code === "ENOENT" || err.code === "ENOTDIR" || err.code === "EISDIR" || err.code === "ERR_FS_EISDIR")) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
throw err;
|
|
656
|
+
});
|
|
593
657
|
}
|
|
594
658
|
})
|
|
595
659
|
)
|
|
@@ -691,17 +755,17 @@ var init_fs = __esm({
|
|
|
691
755
|
|
|
692
756
|
// src/utils/pack-list.ts
|
|
693
757
|
import { readFile as readFile3, readdir as readdir2, stat as stat3 } from "fs/promises";
|
|
694
|
-
import { join as
|
|
758
|
+
import { join as join5, relative as relative4, resolve as resolve2, sep } from "path";
|
|
695
759
|
import picomatch from "picomatch";
|
|
696
760
|
async function resolvePackFiles(packageDir, pkg) {
|
|
697
761
|
const files = [];
|
|
698
762
|
const absDir = resolve2(packageDir);
|
|
699
|
-
files.push(
|
|
763
|
+
files.push(join5(absDir, "package.json"));
|
|
700
764
|
const allFiles = await collectAllFiles(absDir, absDir);
|
|
701
765
|
const allRelPaths = allFiles.map((f) => normalizePath(relative4(absDir, f)));
|
|
702
766
|
if (pkg.files && pkg.files.length > 0) {
|
|
703
767
|
for (const pattern of pkg.files) {
|
|
704
|
-
const target =
|
|
768
|
+
const target = join5(absDir, pattern);
|
|
705
769
|
const resolved = resolve2(target);
|
|
706
770
|
if (!resolved.startsWith(absDir + sep) && resolved !== absDir) {
|
|
707
771
|
consola.warn(`files pattern "${pattern}" escapes package directory, skipping`);
|
|
@@ -754,7 +818,7 @@ async function resolvePackFiles(packageDir, pkg) {
|
|
|
754
818
|
const fileSet = new Set(files);
|
|
755
819
|
const allFileSet = new Set(allFiles);
|
|
756
820
|
for (const name of ["README.md", "README", "LICENSE", "LICENCE", "CHANGELOG.md"]) {
|
|
757
|
-
const p =
|
|
821
|
+
const p = join5(absDir, name);
|
|
758
822
|
if (fileSet.has(p)) continue;
|
|
759
823
|
if (!allFileSet.has(p)) continue;
|
|
760
824
|
files.push(p);
|
|
@@ -778,28 +842,38 @@ function shouldIgnore(relPath, matchers) {
|
|
|
778
842
|
return false;
|
|
779
843
|
}
|
|
780
844
|
async function loadNpmIgnore(dir) {
|
|
781
|
-
const
|
|
845
|
+
const npmIgnore = await readIgnoreFile(dir, ".npmignore");
|
|
846
|
+
if (npmIgnore !== null) return parseIgnoreFile(npmIgnore);
|
|
847
|
+
const gitIgnore = await readIgnoreFile(dir, ".gitignore");
|
|
848
|
+
return gitIgnore === null ? createIgnoreMatchers() : parseIgnoreFile(gitIgnore);
|
|
849
|
+
}
|
|
850
|
+
async function readIgnoreFile(dir, filename) {
|
|
782
851
|
try {
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
852
|
+
return await readFile3(join5(dir, filename), "utf-8");
|
|
853
|
+
} catch (err) {
|
|
854
|
+
if (isNodeError(err) && err.code === "ENOENT") return null;
|
|
855
|
+
throw err;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
function createIgnoreMatchers() {
|
|
859
|
+
return { literals: /* @__PURE__ */ new Set(), patterns: [], negations: [] };
|
|
860
|
+
}
|
|
861
|
+
function parseIgnoreFile(content) {
|
|
862
|
+
const matchers = { literals: /* @__PURE__ */ new Set(), patterns: [], negations: [] };
|
|
863
|
+
for (const line of content.split("\n")) {
|
|
864
|
+
const trimmed = line.trim();
|
|
865
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
866
|
+
if (trimmed.startsWith("!")) {
|
|
867
|
+
const pat = trimmed.slice(1);
|
|
868
|
+
if (hasGlobChars(pat)) {
|
|
869
|
+
matchers.negations.push(picomatch(pat, { dot: true }));
|
|
796
870
|
} else {
|
|
797
|
-
matchers.
|
|
871
|
+
matchers.negations.push(picomatch(pat, { dot: true }));
|
|
798
872
|
}
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
873
|
+
} else if (hasGlobChars(trimmed)) {
|
|
874
|
+
matchers.patterns.push(picomatch(trimmed, { dot: true }));
|
|
875
|
+
} else {
|
|
876
|
+
matchers.literals.add(trimmed.replace(/\/$/, ""));
|
|
803
877
|
}
|
|
804
878
|
}
|
|
805
879
|
return matchers;
|
|
@@ -812,7 +886,7 @@ async function collectAllFiles(dir, rootDir) {
|
|
|
812
886
|
try {
|
|
813
887
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
814
888
|
for (const entry of entries) {
|
|
815
|
-
const full =
|
|
889
|
+
const full = join5(dir, entry.name);
|
|
816
890
|
if (entry.isDirectory()) {
|
|
817
891
|
if (entry.name === ".git") continue;
|
|
818
892
|
if (dir === rootDir && entry.name === "node_modules") continue;
|
|
@@ -844,6 +918,7 @@ var init_pack_list = __esm({
|
|
|
844
918
|
".hg",
|
|
845
919
|
".DS_Store",
|
|
846
920
|
".npmrc",
|
|
921
|
+
".gitignore",
|
|
847
922
|
".knarr",
|
|
848
923
|
"test",
|
|
849
924
|
"tests",
|
|
@@ -1028,6 +1103,208 @@ var init_lockfile = __esm({
|
|
|
1028
1103
|
}
|
|
1029
1104
|
});
|
|
1030
1105
|
|
|
1106
|
+
// src/utils/pm-detect.ts
|
|
1107
|
+
import { readFile as readFile5, stat as stat5 } from "fs/promises";
|
|
1108
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
1109
|
+
function parsePackageManagerSpec(value) {
|
|
1110
|
+
if (typeof value !== "string") return null;
|
|
1111
|
+
const match = value.trim().match(/^([^@\s]+)(?:@(.+))?$/);
|
|
1112
|
+
if (!match) return null;
|
|
1113
|
+
const name = match[1];
|
|
1114
|
+
if (!VALID_PMS.has(name)) return null;
|
|
1115
|
+
return {
|
|
1116
|
+
name,
|
|
1117
|
+
version: match[2]?.trim() ?? ""
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
async function readPackageManagerSpec(dir) {
|
|
1121
|
+
try {
|
|
1122
|
+
const raw = await readFile5(join7(dir, "package.json"), "utf-8");
|
|
1123
|
+
const pkg = JSON.parse(raw);
|
|
1124
|
+
return parsePackageManagerSpec(pkg.packageManager);
|
|
1125
|
+
} catch {
|
|
1126
|
+
return null;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
async function findPackageManagerSpec(projectDir) {
|
|
1130
|
+
let dir = projectDir;
|
|
1131
|
+
for (; ; ) {
|
|
1132
|
+
const spec = await readPackageManagerSpec(dir);
|
|
1133
|
+
if (spec) return spec;
|
|
1134
|
+
const parent = dirname3(dir);
|
|
1135
|
+
if (parent === dir) return null;
|
|
1136
|
+
dir = parent;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
function yarnDefaultsToPnp(version) {
|
|
1140
|
+
const normalized = version.trim().replace(/^npm:/, "");
|
|
1141
|
+
if (normalized === "classic") return false;
|
|
1142
|
+
const major = normalized.match(/^v?(\d+)(?:[.+-]|$)/);
|
|
1143
|
+
if (major) return Number(major[1]) >= 2;
|
|
1144
|
+
return normalized.length > 0;
|
|
1145
|
+
}
|
|
1146
|
+
async function detectPackageManager(projectDir) {
|
|
1147
|
+
return (await detectPackageManagerInfo(projectDir)).packageManager;
|
|
1148
|
+
}
|
|
1149
|
+
async function detectPackageManagerInfo(projectDir) {
|
|
1150
|
+
let dir = projectDir;
|
|
1151
|
+
for (; ; ) {
|
|
1152
|
+
const fromField = await readPackageManagerSpec(dir);
|
|
1153
|
+
if (fromField) {
|
|
1154
|
+
return {
|
|
1155
|
+
packageManager: fromField.name,
|
|
1156
|
+
source: "packageManager",
|
|
1157
|
+
dir,
|
|
1158
|
+
file: join7(dir, "package.json")
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
const results = await Promise.all(
|
|
1162
|
+
LOCKFILES.map(async ([lockfile, packageManager]) => {
|
|
1163
|
+
try {
|
|
1164
|
+
await stat5(join7(dir, lockfile));
|
|
1165
|
+
return { lockfile, packageManager };
|
|
1166
|
+
} catch {
|
|
1167
|
+
return null;
|
|
1168
|
+
}
|
|
1169
|
+
})
|
|
1170
|
+
);
|
|
1171
|
+
const found = results.find((result) => result !== null);
|
|
1172
|
+
if (found) {
|
|
1173
|
+
return {
|
|
1174
|
+
packageManager: found.packageManager,
|
|
1175
|
+
source: "lockfile",
|
|
1176
|
+
dir,
|
|
1177
|
+
file: join7(dir, found.lockfile)
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
const yarnArtifact = await findExistingFile(dir, YARN_PROJECT_ARTIFACTS);
|
|
1181
|
+
if (yarnArtifact) {
|
|
1182
|
+
return {
|
|
1183
|
+
packageManager: "yarn",
|
|
1184
|
+
source: "yarnArtifact",
|
|
1185
|
+
dir,
|
|
1186
|
+
file: join7(dir, yarnArtifact)
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
const parent = dirname3(dir);
|
|
1190
|
+
if (parent === dir) {
|
|
1191
|
+
return {
|
|
1192
|
+
packageManager: "npm",
|
|
1193
|
+
source: "default",
|
|
1194
|
+
dir: projectDir
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
dir = parent;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
async function findExistingFile(dir, filenames) {
|
|
1201
|
+
for (const filename of filenames) {
|
|
1202
|
+
try {
|
|
1203
|
+
await stat5(join7(dir, filename));
|
|
1204
|
+
return filename;
|
|
1205
|
+
} catch {
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
return null;
|
|
1209
|
+
}
|
|
1210
|
+
function readYarnrcScalar(content, key) {
|
|
1211
|
+
for (const line of content.split("\n")) {
|
|
1212
|
+
const trimmed = line.trim();
|
|
1213
|
+
if (trimmed.startsWith("#") || !trimmed.includes(key)) continue;
|
|
1214
|
+
const match = trimmed.match(new RegExp(`^${key}:\\s*(.+)$`));
|
|
1215
|
+
if (!match) continue;
|
|
1216
|
+
const value = match[1].trim().replace(/\s+#.*$/, "").trim().replace(/^["']|["']$/g, "");
|
|
1217
|
+
if (value) return value;
|
|
1218
|
+
}
|
|
1219
|
+
return null;
|
|
1220
|
+
}
|
|
1221
|
+
async function findYarnrcScalar(projectDir, key) {
|
|
1222
|
+
let dir = projectDir;
|
|
1223
|
+
for (; ; ) {
|
|
1224
|
+
try {
|
|
1225
|
+
const content = await readFile5(join7(dir, ".yarnrc.yml"), "utf-8");
|
|
1226
|
+
const value = readYarnrcScalar(content, key);
|
|
1227
|
+
if (value) return value;
|
|
1228
|
+
} catch {
|
|
1229
|
+
}
|
|
1230
|
+
const parent = dirname3(dir);
|
|
1231
|
+
if (parent === dir) return null;
|
|
1232
|
+
dir = parent;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
async function detectYarnNodeLinker(projectDir) {
|
|
1236
|
+
const value = await findYarnrcScalar(projectDir, "nodeLinker");
|
|
1237
|
+
if (value === "node-modules" || value === "pnpm" || value === "pnp") {
|
|
1238
|
+
return value;
|
|
1239
|
+
}
|
|
1240
|
+
return null;
|
|
1241
|
+
}
|
|
1242
|
+
async function detectYarnPnpmStoreFolder(projectDir) {
|
|
1243
|
+
return findYarnrcScalar(projectDir, "pnpmStoreFolder");
|
|
1244
|
+
}
|
|
1245
|
+
async function hasYarnrcYml(projectDir) {
|
|
1246
|
+
let dir = projectDir;
|
|
1247
|
+
for (; ; ) {
|
|
1248
|
+
try {
|
|
1249
|
+
await stat5(join7(dir, ".yarnrc.yml"));
|
|
1250
|
+
return true;
|
|
1251
|
+
} catch {
|
|
1252
|
+
const parent = dirname3(dir);
|
|
1253
|
+
if (parent === dir) return false;
|
|
1254
|
+
dir = parent;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
async function hasYarnPnpManifest(projectDir) {
|
|
1259
|
+
let dir = projectDir;
|
|
1260
|
+
for (; ; ) {
|
|
1261
|
+
const results = await Promise.all(
|
|
1262
|
+
YARN_PNP_MANIFESTS.map(async (manifest) => {
|
|
1263
|
+
try {
|
|
1264
|
+
await stat5(join7(dir, manifest));
|
|
1265
|
+
return true;
|
|
1266
|
+
} catch {
|
|
1267
|
+
return false;
|
|
1268
|
+
}
|
|
1269
|
+
})
|
|
1270
|
+
);
|
|
1271
|
+
if (results.some(Boolean)) return true;
|
|
1272
|
+
const parent = dirname3(dir);
|
|
1273
|
+
if (parent === dir) return false;
|
|
1274
|
+
dir = parent;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
async function hasYarnPnpMarkers(projectDir) {
|
|
1278
|
+
if (await hasYarnPnpManifest(projectDir)) return true;
|
|
1279
|
+
return await detectYarnNodeLinker(projectDir) === "pnp";
|
|
1280
|
+
}
|
|
1281
|
+
async function isYarnPnpProject(projectDir) {
|
|
1282
|
+
const linker = await detectYarnNodeLinker(projectDir);
|
|
1283
|
+
if (linker === "pnp") return true;
|
|
1284
|
+
if (linker === "node-modules" || linker === "pnpm") return false;
|
|
1285
|
+
if (await hasYarnPnpManifest(projectDir)) return true;
|
|
1286
|
+
if (await hasYarnrcYml(projectDir)) return true;
|
|
1287
|
+
const spec = await findPackageManagerSpec(projectDir);
|
|
1288
|
+
return spec?.name === "yarn" && yarnDefaultsToPnp(spec.version);
|
|
1289
|
+
}
|
|
1290
|
+
var VALID_PMS, LOCKFILES, YARN_PNP_MANIFESTS, YARN_PROJECT_ARTIFACTS;
|
|
1291
|
+
var init_pm_detect = __esm({
|
|
1292
|
+
"src/utils/pm-detect.ts"() {
|
|
1293
|
+
"use strict";
|
|
1294
|
+
VALID_PMS = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun"]);
|
|
1295
|
+
LOCKFILES = [
|
|
1296
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
1297
|
+
["bun.lockb", "bun"],
|
|
1298
|
+
["bun.lock", "bun"],
|
|
1299
|
+
["yarn.lock", "yarn"],
|
|
1300
|
+
["package-lock.json", "npm"],
|
|
1301
|
+
["npm-shrinkwrap.json", "npm"]
|
|
1302
|
+
];
|
|
1303
|
+
YARN_PNP_MANIFESTS = [".pnp.cjs", ".pnp.js", ".pnp.loader.mjs"];
|
|
1304
|
+
YARN_PROJECT_ARTIFACTS = [".yarnrc.yml", ...YARN_PNP_MANIFESTS];
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1031
1308
|
// src/core/history.ts
|
|
1032
1309
|
var history_exports = {};
|
|
1033
1310
|
__export(history_exports, {
|
|
@@ -1039,13 +1316,13 @@ __export(history_exports, {
|
|
|
1039
1316
|
resolveHistoryLimit: () => resolveHistoryLimit,
|
|
1040
1317
|
restoreHistoryEntry: () => restoreHistoryEntry
|
|
1041
1318
|
});
|
|
1042
|
-
import { readdir as readdir4, readFile as
|
|
1043
|
-
import { join as
|
|
1319
|
+
import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
|
|
1320
|
+
import { join as join8 } from "path";
|
|
1044
1321
|
async function captureHistory(name, version, oldEntryDir, historyLimit) {
|
|
1045
|
-
const metaPath =
|
|
1322
|
+
const metaPath = join8(oldEntryDir, ".knarr-meta.json");
|
|
1046
1323
|
let meta;
|
|
1047
1324
|
try {
|
|
1048
|
-
meta = JSON.parse(await
|
|
1325
|
+
meta = JSON.parse(await readFile6(metaPath, "utf-8"));
|
|
1049
1326
|
} catch {
|
|
1050
1327
|
verbose(`[history] Could not read meta from ${metaPath}, skipping history capture`);
|
|
1051
1328
|
return;
|
|
@@ -1065,12 +1342,12 @@ async function captureHistory(name, version, oldEntryDir, historyLimit) {
|
|
|
1065
1342
|
const tmpHistoryEntry = entryDir + `.tmp-${process.pid}`;
|
|
1066
1343
|
try {
|
|
1067
1344
|
await ensureDir(tmpHistoryEntry);
|
|
1068
|
-
const oldPkgDir =
|
|
1345
|
+
const oldPkgDir = join8(oldEntryDir, "package");
|
|
1069
1346
|
if (await exists(oldPkgDir)) {
|
|
1070
|
-
await moveDir(oldPkgDir,
|
|
1347
|
+
await moveDir(oldPkgDir, join8(tmpHistoryEntry, "package"));
|
|
1071
1348
|
}
|
|
1072
1349
|
await atomicWriteFile(
|
|
1073
|
-
|
|
1350
|
+
join8(tmpHistoryEntry, ".knarr-meta.json"),
|
|
1074
1351
|
JSON.stringify(meta, null, 2)
|
|
1075
1352
|
);
|
|
1076
1353
|
await moveDir(tmpHistoryEntry, entryDir);
|
|
@@ -1093,16 +1370,16 @@ async function listHistory(name, version) {
|
|
|
1093
1370
|
}
|
|
1094
1371
|
const result = [];
|
|
1095
1372
|
for (const buildId of entries) {
|
|
1096
|
-
const entryDir =
|
|
1097
|
-
const metaPath =
|
|
1373
|
+
const entryDir = join8(historyDir, buildId);
|
|
1374
|
+
const metaPath = join8(entryDir, ".knarr-meta.json");
|
|
1098
1375
|
try {
|
|
1099
|
-
const meta = JSON.parse(await
|
|
1376
|
+
const meta = JSON.parse(await readFile6(metaPath, "utf-8"));
|
|
1100
1377
|
result.push({
|
|
1101
1378
|
buildId: meta.buildId ?? buildId,
|
|
1102
1379
|
contentHash: meta.contentHash,
|
|
1103
1380
|
publishedAt: meta.publishedAt,
|
|
1104
1381
|
sourcePath: meta.sourcePath,
|
|
1105
|
-
packageDir:
|
|
1382
|
+
packageDir: join8(entryDir, "package")
|
|
1106
1383
|
});
|
|
1107
1384
|
} catch {
|
|
1108
1385
|
}
|
|
@@ -1112,15 +1389,15 @@ async function listHistory(name, version) {
|
|
|
1112
1389
|
}
|
|
1113
1390
|
async function getHistoryEntry(name, version, buildId) {
|
|
1114
1391
|
const entryDir = getHistoryEntryPath(name, version, buildId);
|
|
1115
|
-
const metaPath =
|
|
1392
|
+
const metaPath = join8(entryDir, ".knarr-meta.json");
|
|
1116
1393
|
try {
|
|
1117
|
-
const meta = JSON.parse(await
|
|
1394
|
+
const meta = JSON.parse(await readFile6(metaPath, "utf-8"));
|
|
1118
1395
|
return {
|
|
1119
1396
|
buildId: meta.buildId ?? buildId,
|
|
1120
1397
|
contentHash: meta.contentHash,
|
|
1121
1398
|
publishedAt: meta.publishedAt,
|
|
1122
1399
|
sourcePath: meta.sourcePath,
|
|
1123
|
-
packageDir:
|
|
1400
|
+
packageDir: join8(entryDir, "package")
|
|
1124
1401
|
};
|
|
1125
1402
|
} catch {
|
|
1126
1403
|
return null;
|
|
@@ -1131,17 +1408,17 @@ async function restoreHistoryEntry(name, version, buildId, historyLimit) {
|
|
|
1131
1408
|
if (!entry) return null;
|
|
1132
1409
|
const storeEntryDir = getStoreEntryPath(name, version);
|
|
1133
1410
|
const historyEntryDir = getHistoryEntryPath(name, version, buildId);
|
|
1134
|
-
const historyPkg =
|
|
1135
|
-
const historyMeta =
|
|
1136
|
-
const metaContent = await
|
|
1137
|
-
const storePkg =
|
|
1138
|
-
const storeMeta =
|
|
1411
|
+
const historyPkg = join8(historyEntryDir, "package");
|
|
1412
|
+
const historyMeta = join8(historyEntryDir, ".knarr-meta.json");
|
|
1413
|
+
const metaContent = await readFile6(historyMeta, "utf-8");
|
|
1414
|
+
const storePkg = join8(storeEntryDir, "package");
|
|
1415
|
+
const storeMeta = join8(storeEntryDir, ".knarr-meta.json");
|
|
1139
1416
|
if (!await exists(historyPkg)) {
|
|
1140
1417
|
throw new Error(`History entry ${buildId} is missing its package directory`);
|
|
1141
1418
|
}
|
|
1142
|
-
const currentMetaContent = await
|
|
1419
|
+
const currentMetaContent = await readFile6(storeMeta, "utf-8").catch(() => null);
|
|
1143
1420
|
const oldEntryDir = storeEntryDir + `.restore-old-${process.pid}-${Date.now()}`;
|
|
1144
|
-
const oldPkg =
|
|
1421
|
+
const oldPkg = join8(oldEntryDir, "package");
|
|
1145
1422
|
let stagedOld = false;
|
|
1146
1423
|
let historyMovedToStore = false;
|
|
1147
1424
|
try {
|
|
@@ -1151,7 +1428,7 @@ async function restoreHistoryEntry(name, version, buildId, historyLimit) {
|
|
|
1151
1428
|
await moveDir(storePkg, oldPkg);
|
|
1152
1429
|
}
|
|
1153
1430
|
if (currentMetaContent) {
|
|
1154
|
-
await atomicWriteFile(
|
|
1431
|
+
await atomicWriteFile(join8(oldEntryDir, ".knarr-meta.json"), currentMetaContent);
|
|
1155
1432
|
}
|
|
1156
1433
|
stagedOld = true;
|
|
1157
1434
|
}
|
|
@@ -1227,30 +1504,35 @@ var workspace_exports = {};
|
|
|
1227
1504
|
__export(workspace_exports, {
|
|
1228
1505
|
buildReverseAdjacency: () => buildReverseAdjacency,
|
|
1229
1506
|
buildWorkspaceGraph: () => buildWorkspaceGraph,
|
|
1507
|
+
filterPublishableWorkspaceGraph: () => filterPublishableWorkspaceGraph,
|
|
1508
|
+
findWorkspacePackageRoot: () => findWorkspacePackageRoot,
|
|
1230
1509
|
findWorkspacePackages: () => findWorkspacePackages,
|
|
1231
1510
|
findWorkspaceRoot: () => findWorkspaceRoot,
|
|
1232
1511
|
parseCatalogs: () => parseCatalogs
|
|
1233
1512
|
});
|
|
1234
|
-
import { readFile as
|
|
1235
|
-
import { join as
|
|
1513
|
+
import { readFile as readFile7, readdir as readdir5 } from "fs/promises";
|
|
1514
|
+
import { join as join9, dirname as dirname4, resolve as resolve3, relative as relative5 } from "path";
|
|
1236
1515
|
import picomatch2 from "picomatch";
|
|
1237
1516
|
async function findWorkspaceRoot(startDir) {
|
|
1238
1517
|
let dir = startDir;
|
|
1239
1518
|
for (; ; ) {
|
|
1240
|
-
if (await exists(
|
|
1519
|
+
if (await exists(join9(dir, "pnpm-workspace.yaml"))) {
|
|
1241
1520
|
return dir;
|
|
1242
1521
|
}
|
|
1243
|
-
const parent =
|
|
1522
|
+
const parent = dirname4(dir);
|
|
1244
1523
|
if (parent === dir) return null;
|
|
1245
1524
|
dir = parent;
|
|
1246
1525
|
}
|
|
1247
1526
|
}
|
|
1527
|
+
async function findWorkspacePackageRoot(startDir) {
|
|
1528
|
+
return await findWorkspaceRoot(startDir) ?? await findPackageJsonWorkspaceRoot(startDir);
|
|
1529
|
+
}
|
|
1248
1530
|
async function parseCatalogs(workspaceRoot) {
|
|
1249
1531
|
const result = { default: {}, named: {} };
|
|
1250
|
-
const filePath =
|
|
1532
|
+
const filePath = join9(workspaceRoot, "pnpm-workspace.yaml");
|
|
1251
1533
|
let content;
|
|
1252
1534
|
try {
|
|
1253
|
-
content = await
|
|
1535
|
+
content = await readFile7(filePath, "utf-8");
|
|
1254
1536
|
} catch {
|
|
1255
1537
|
return result;
|
|
1256
1538
|
}
|
|
@@ -1319,7 +1601,7 @@ async function findWorkspacePackages(startDir) {
|
|
|
1319
1601
|
const rootDir = pnpmRoot ?? await findPackageJsonWorkspaceRoot(startDir);
|
|
1320
1602
|
if (!rootDir) return [];
|
|
1321
1603
|
try {
|
|
1322
|
-
const rootPkg = JSON.parse(await
|
|
1604
|
+
const rootPkg = JSON.parse(await readFile7(join9(rootDir, "package.json"), "utf-8"));
|
|
1323
1605
|
const workspaces = Array.isArray(rootPkg.workspaces) ? rootPkg.workspaces : rootPkg.workspaces?.packages ?? [];
|
|
1324
1606
|
if (workspaces.length === 0) return [];
|
|
1325
1607
|
const positive = workspaces.filter((p) => !p.startsWith("!"));
|
|
@@ -1330,10 +1612,10 @@ async function findWorkspacePackages(startDir) {
|
|
|
1330
1612
|
}
|
|
1331
1613
|
}
|
|
1332
1614
|
async function parsePnpmWorkspacePackages(workspaceRoot) {
|
|
1333
|
-
const filePath =
|
|
1615
|
+
const filePath = join9(workspaceRoot, "pnpm-workspace.yaml");
|
|
1334
1616
|
let content;
|
|
1335
1617
|
try {
|
|
1336
|
-
content = await
|
|
1618
|
+
content = await readFile7(filePath, "utf-8");
|
|
1337
1619
|
} catch {
|
|
1338
1620
|
return [];
|
|
1339
1621
|
}
|
|
@@ -1360,11 +1642,11 @@ async function findPackageJsonWorkspaceRoot(startDir) {
|
|
|
1360
1642
|
let dir = startDir;
|
|
1361
1643
|
for (; ; ) {
|
|
1362
1644
|
try {
|
|
1363
|
-
const pkg = JSON.parse(await
|
|
1645
|
+
const pkg = JSON.parse(await readFile7(join9(dir, "package.json"), "utf-8"));
|
|
1364
1646
|
if (pkg.workspaces) return dir;
|
|
1365
1647
|
} catch {
|
|
1366
1648
|
}
|
|
1367
|
-
const parent =
|
|
1649
|
+
const parent = dirname4(dir);
|
|
1368
1650
|
if (parent === dir) return null;
|
|
1369
1651
|
dir = parent;
|
|
1370
1652
|
}
|
|
@@ -1382,7 +1664,7 @@ async function resolveWorkspaceGlobs(rootDir, patterns, negations = []) {
|
|
|
1382
1664
|
foundGlob = true;
|
|
1383
1665
|
globParts.push(part);
|
|
1384
1666
|
} else {
|
|
1385
|
-
staticPrefix =
|
|
1667
|
+
staticPrefix = join9(staticPrefix, part);
|
|
1386
1668
|
}
|
|
1387
1669
|
}
|
|
1388
1670
|
if (globParts.length === 1 && globParts[0] === "*") {
|
|
@@ -1390,8 +1672,8 @@ async function resolveWorkspaceGlobs(rootDir, patterns, negations = []) {
|
|
|
1390
1672
|
const entries = await readdir5(staticPrefix, { withFileTypes: true });
|
|
1391
1673
|
for (const entry of entries) {
|
|
1392
1674
|
if (entry.isDirectory()) {
|
|
1393
|
-
const pkgDir =
|
|
1394
|
-
if (await exists(
|
|
1675
|
+
const pkgDir = join9(staticPrefix, entry.name);
|
|
1676
|
+
if (await exists(join9(pkgDir, "package.json"))) {
|
|
1395
1677
|
results.push(pkgDir);
|
|
1396
1678
|
}
|
|
1397
1679
|
}
|
|
@@ -1403,14 +1685,14 @@ async function resolveWorkspaceGlobs(rootDir, patterns, negations = []) {
|
|
|
1403
1685
|
const candidates = await collectDirs(rootDir, 8);
|
|
1404
1686
|
for (const candidate of candidates) {
|
|
1405
1687
|
const rel = normalizePath(relative5(rootDir, candidate));
|
|
1406
|
-
if (isMatch(rel) && await exists(
|
|
1688
|
+
if (isMatch(rel) && await exists(join9(candidate, "package.json"))) {
|
|
1407
1689
|
results.push(candidate);
|
|
1408
1690
|
}
|
|
1409
1691
|
}
|
|
1410
1692
|
}
|
|
1411
1693
|
} else {
|
|
1412
1694
|
const pkgDir = resolve3(rootDir, pattern);
|
|
1413
|
-
if (await exists(
|
|
1695
|
+
if (await exists(join9(pkgDir, "package.json"))) {
|
|
1414
1696
|
results.push(pkgDir);
|
|
1415
1697
|
}
|
|
1416
1698
|
}
|
|
@@ -1430,7 +1712,7 @@ async function collectDirs(dir, maxDepth) {
|
|
|
1430
1712
|
const entries = await readdir5(dir, { withFileTypes: true });
|
|
1431
1713
|
for (const entry of entries) {
|
|
1432
1714
|
if (!entry.isDirectory() || entry.name === "node_modules" || entry.name === ".git") continue;
|
|
1433
|
-
const full =
|
|
1715
|
+
const full = join9(dir, entry.name);
|
|
1434
1716
|
results.push(full);
|
|
1435
1717
|
results.push(...await collectDirs(full, maxDepth - 1));
|
|
1436
1718
|
}
|
|
@@ -1444,7 +1726,7 @@ async function buildWorkspaceGraph(startDir) {
|
|
|
1444
1726
|
for (const dir of dirs) {
|
|
1445
1727
|
try {
|
|
1446
1728
|
const pkg = JSON.parse(
|
|
1447
|
-
await
|
|
1729
|
+
await readFile7(join9(dir, "package.json"), "utf-8")
|
|
1448
1730
|
);
|
|
1449
1731
|
if (pkg.name && pkg.version) {
|
|
1450
1732
|
packages.push({ name: pkg.name, version: pkg.version, dir, pkg });
|
|
@@ -1469,6 +1751,19 @@ async function buildWorkspaceGraph(startDir) {
|
|
|
1469
1751
|
}
|
|
1470
1752
|
return { packages, adjacency };
|
|
1471
1753
|
}
|
|
1754
|
+
function filterPublishableWorkspaceGraph(graph) {
|
|
1755
|
+
const packages = graph.packages.filter((pkg) => !pkg.pkg.private);
|
|
1756
|
+
const names = new Set(packages.map((pkg) => pkg.name));
|
|
1757
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
1758
|
+
for (const pkg of packages) {
|
|
1759
|
+
const deps = graph.adjacency.get(pkg.name) ?? /* @__PURE__ */ new Set();
|
|
1760
|
+
adjacency.set(
|
|
1761
|
+
pkg.name,
|
|
1762
|
+
new Set([...deps].filter((dep) => names.has(dep)))
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1765
|
+
return { packages, adjacency };
|
|
1766
|
+
}
|
|
1472
1767
|
function buildReverseAdjacency(adjacency) {
|
|
1473
1768
|
const reverse = /* @__PURE__ */ new Map();
|
|
1474
1769
|
for (const name of adjacency.keys()) {
|
|
@@ -1491,10 +1786,25 @@ function parseKeyValue(line) {
|
|
|
1491
1786
|
const colonIdx = trimmed.indexOf(":");
|
|
1492
1787
|
if (colonIdx <= 0) return null;
|
|
1493
1788
|
const key = trimmed.slice(0, colonIdx).trim();
|
|
1494
|
-
const value = trimmed.slice(colonIdx + 1).trim();
|
|
1789
|
+
const value = stripYamlInlineComment(trimmed.slice(colonIdx + 1)).trim();
|
|
1495
1790
|
if (!key || !value) return null;
|
|
1791
|
+
const unquotedKey = key.replace(/^["']|["']$/g, "");
|
|
1496
1792
|
const unquoted = value.replace(/^["']|["']$/g, "");
|
|
1497
|
-
return [
|
|
1793
|
+
return [unquotedKey, unquoted];
|
|
1794
|
+
}
|
|
1795
|
+
function stripYamlInlineComment(value) {
|
|
1796
|
+
let quote = null;
|
|
1797
|
+
for (let i = 0; i < value.length; i++) {
|
|
1798
|
+
const char = value[i];
|
|
1799
|
+
if ((char === '"' || char === "'") && value[i - 1] !== "\\") {
|
|
1800
|
+
quote = quote === char ? null : quote ?? char;
|
|
1801
|
+
continue;
|
|
1802
|
+
}
|
|
1803
|
+
if (!quote && char === "#" && (i === 0 || /\s/.test(value[i - 1]))) {
|
|
1804
|
+
return value.slice(0, i);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
return value;
|
|
1498
1808
|
}
|
|
1499
1809
|
var init_workspace = __esm({
|
|
1500
1810
|
"src/utils/workspace.ts"() {
|
|
@@ -1505,16 +1815,16 @@ var init_workspace = __esm({
|
|
|
1505
1815
|
});
|
|
1506
1816
|
|
|
1507
1817
|
// src/core/publisher.ts
|
|
1508
|
-
import { readFile as
|
|
1509
|
-
import { join as
|
|
1818
|
+
import { readFile as readFile8, stat as stat6 } from "fs/promises";
|
|
1819
|
+
import { delimiter, join as join10, relative as relative6, dirname as dirname5, resolve as resolve4 } from "path";
|
|
1510
1820
|
import { spawn } from "child_process";
|
|
1511
1821
|
import { platform } from "os";
|
|
1512
1822
|
import { availableParallelism as availableParallelism3 } from "os";
|
|
1513
1823
|
async function publish(packageDir, options = {}) {
|
|
1514
|
-
const pkgPath =
|
|
1824
|
+
const pkgPath = join10(packageDir, "package.json");
|
|
1515
1825
|
let pkgContent;
|
|
1516
1826
|
try {
|
|
1517
|
-
pkgContent = await
|
|
1827
|
+
pkgContent = await readFile8(pkgPath, "utf-8");
|
|
1518
1828
|
} catch {
|
|
1519
1829
|
throw new Error(`No package.json found in ${packageDir}`);
|
|
1520
1830
|
}
|
|
@@ -1522,11 +1832,6 @@ async function publish(packageDir, options = {}) {
|
|
|
1522
1832
|
if (!pkg.name) throw new Error("package.json missing 'name' field");
|
|
1523
1833
|
if (!pkg.version) throw new Error("package.json missing 'version' field");
|
|
1524
1834
|
validatePackageIdentity(pkg.name, pkg.version);
|
|
1525
|
-
if (pkg.private && !options.allowPrivate) {
|
|
1526
|
-
throw new Error(
|
|
1527
|
-
`Package "${pkg.name}" is private. Use --private flag to publish private packages.`
|
|
1528
|
-
);
|
|
1529
|
-
}
|
|
1530
1835
|
await runLifecycleHook(packageDir, pkg, "preknarr");
|
|
1531
1836
|
if (options.runScripts !== false) {
|
|
1532
1837
|
await runLifecycleHook(packageDir, pkg, "prepack");
|
|
@@ -1535,7 +1840,7 @@ async function publish(packageDir, options = {}) {
|
|
|
1535
1840
|
if (pkg.publishConfig?.directory) {
|
|
1536
1841
|
publishDir = resolve4(packageDir, pkg.publishConfig.directory);
|
|
1537
1842
|
try {
|
|
1538
|
-
const s = await
|
|
1843
|
+
const s = await stat6(publishDir);
|
|
1539
1844
|
if (!s.isDirectory()) {
|
|
1540
1845
|
throw new Error(`publishConfig.directory "${pkg.publishConfig.directory}" is not a directory`);
|
|
1541
1846
|
}
|
|
@@ -1547,22 +1852,46 @@ async function publish(packageDir, options = {}) {
|
|
|
1547
1852
|
}
|
|
1548
1853
|
verbose(`[publish] Using publishConfig.directory: ${publishDir}`);
|
|
1549
1854
|
}
|
|
1550
|
-
const
|
|
1551
|
-
const
|
|
1855
|
+
const publishPkgPath = join10(publishDir, "package.json");
|
|
1856
|
+
const publishDirHasPackageJson = publishDir === packageDir || await exists(publishPkgPath);
|
|
1857
|
+
const filePkg = publishDirHasPackageJson ? JSON.parse(await readFile8(publishPkgPath, "utf-8")) : pkg;
|
|
1858
|
+
if (!filePkg.name) throw new Error("publish package.json missing 'name' field");
|
|
1859
|
+
if (!filePkg.version) throw new Error("publish package.json missing 'version' field");
|
|
1860
|
+
validatePackageIdentity(filePkg.name, filePkg.version);
|
|
1861
|
+
if (filePkg.private && !options.allowPrivate) {
|
|
1862
|
+
throw new Error(
|
|
1863
|
+
`Package "${filePkg.name}" is private. Use --private flag to publish private packages.`
|
|
1864
|
+
);
|
|
1865
|
+
}
|
|
1866
|
+
const packListPkg = publishDirHasPackageJson ? filePkg : { ...filePkg, files: void 0 };
|
|
1867
|
+
const files = await resolvePackFiles(publishDir, packListPkg);
|
|
1552
1868
|
if (files.length === 0) {
|
|
1553
1869
|
throw new Error("No publishable files found");
|
|
1554
1870
|
}
|
|
1555
|
-
verbose(`[publish] Resolved ${files.length} files for ${
|
|
1556
|
-
|
|
1557
|
-
await
|
|
1558
|
-
|
|
1871
|
+
verbose(`[publish] Resolved ${files.length} files for ${filePkg.name}@${filePkg.version}`);
|
|
1872
|
+
await preloadWorkspaceVersions(filePkg, packageDir);
|
|
1873
|
+
await preloadCatalogs(filePkg, packageDir);
|
|
1874
|
+
let processedPkg = rewriteProtocolVersions(filePkg);
|
|
1875
|
+
processedPkg = applyPublishConfig(processedPkg);
|
|
1876
|
+
const contentOverrides = /* @__PURE__ */ new Map();
|
|
1877
|
+
if (processedPkg !== filePkg || !publishDirHasPackageJson) {
|
|
1878
|
+
contentOverrides.set("package.json", JSON.stringify(processedPkg, null, 2));
|
|
1879
|
+
}
|
|
1880
|
+
const hashFiles = [...files];
|
|
1881
|
+
const hashFileRels = new Set(
|
|
1882
|
+
files.map((file) => relative6(publishDir, file).replace(/\\/g, "/"))
|
|
1883
|
+
);
|
|
1884
|
+
for (const rel of contentOverrides.keys()) {
|
|
1885
|
+
if (!hashFileRels.has(rel)) hashFiles.push(join10(publishDir, rel));
|
|
1886
|
+
}
|
|
1887
|
+
const contentHash = await computeContentHash(hashFiles, publishDir, contentOverrides);
|
|
1559
1888
|
if (!options.force) {
|
|
1560
|
-
const existingMeta = await readMeta(
|
|
1889
|
+
const existingMeta = await readMeta(filePkg.name, filePkg.version);
|
|
1561
1890
|
if (existingMeta && existingMeta.contentHash === contentHash) {
|
|
1562
|
-
consola.info(`${
|
|
1891
|
+
consola.info(`${filePkg.name}@${filePkg.version} already up to date (no changes since last publish)`);
|
|
1563
1892
|
return {
|
|
1564
|
-
name:
|
|
1565
|
-
version:
|
|
1893
|
+
name: filePkg.name,
|
|
1894
|
+
version: filePkg.version,
|
|
1566
1895
|
fileCount: files.length,
|
|
1567
1896
|
skipped: true,
|
|
1568
1897
|
contentHash,
|
|
@@ -1570,17 +1899,17 @@ async function publish(packageDir, options = {}) {
|
|
|
1570
1899
|
};
|
|
1571
1900
|
}
|
|
1572
1901
|
}
|
|
1573
|
-
const storeEntryDir = getStoreEntryPath(
|
|
1902
|
+
const storeEntryDir = getStoreEntryPath(filePkg.name, filePkg.version);
|
|
1574
1903
|
const result = await withFileLock(
|
|
1575
1904
|
storeEntryDir + ".lock",
|
|
1576
1905
|
async () => {
|
|
1577
1906
|
if (!options.force) {
|
|
1578
|
-
const metaUnderLock = await readMeta(
|
|
1907
|
+
const metaUnderLock = await readMeta(filePkg.name, filePkg.version);
|
|
1579
1908
|
if (metaUnderLock && metaUnderLock.contentHash === contentHash) {
|
|
1580
|
-
consola.info(`${
|
|
1909
|
+
consola.info(`${filePkg.name}@${filePkg.version} already up to date (no changes since last publish)`);
|
|
1581
1910
|
return {
|
|
1582
|
-
name:
|
|
1583
|
-
version:
|
|
1911
|
+
name: filePkg.name,
|
|
1912
|
+
version: filePkg.version,
|
|
1584
1913
|
fileCount: files.length,
|
|
1585
1914
|
skipped: true,
|
|
1586
1915
|
contentHash,
|
|
@@ -1589,33 +1918,36 @@ async function publish(packageDir, options = {}) {
|
|
|
1589
1918
|
}
|
|
1590
1919
|
}
|
|
1591
1920
|
const tmpDir = storeEntryDir + `.tmp-${process.pid}-${Date.now()}`;
|
|
1592
|
-
const tmpPackageDir =
|
|
1921
|
+
const tmpPackageDir = join10(tmpDir, "package");
|
|
1593
1922
|
const buildId = contentHash.slice(9, 17);
|
|
1594
1923
|
try {
|
|
1595
1924
|
await ensurePrivateDir(tmpPackageDir);
|
|
1596
|
-
let processedPkg = rewriteProtocolVersions(pkg);
|
|
1597
|
-
processedPkg = applyPublishConfig(processedPkg);
|
|
1598
1925
|
verbose(`[publish] Copying files to temp store...`);
|
|
1599
1926
|
const uniqueDirs = new Set(
|
|
1600
|
-
files.map((file) =>
|
|
1927
|
+
files.map((file) => dirname5(join10(tmpPackageDir, relative6(publishDir, file))))
|
|
1601
1928
|
);
|
|
1602
1929
|
await Promise.all([...uniqueDirs].map((d) => ensureDir(d)));
|
|
1603
1930
|
await Promise.all(
|
|
1604
1931
|
files.map(
|
|
1605
1932
|
(file) => copyLimit(async () => {
|
|
1606
1933
|
const rel = relative6(publishDir, file);
|
|
1607
|
-
const
|
|
1608
|
-
|
|
1609
|
-
|
|
1934
|
+
const normalizedRel = rel.replace(/\\/g, "/");
|
|
1935
|
+
const dest = join10(tmpPackageDir, rel);
|
|
1936
|
+
const override = contentOverrides.get(normalizedRel);
|
|
1937
|
+
if (override !== void 0) {
|
|
1938
|
+
await atomicWriteFile(
|
|
1939
|
+
dest,
|
|
1940
|
+
Buffer.isBuffer(override) ? override.toString("utf-8") : override
|
|
1941
|
+
);
|
|
1610
1942
|
} else {
|
|
1611
1943
|
await copyWithCoW(file, dest, { ensureParent: false });
|
|
1612
1944
|
}
|
|
1613
1945
|
})
|
|
1614
1946
|
)
|
|
1615
1947
|
);
|
|
1616
|
-
if (
|
|
1948
|
+
if (contentOverrides.has("package.json")) {
|
|
1617
1949
|
await atomicWriteFile(
|
|
1618
|
-
|
|
1950
|
+
join10(tmpPackageDir, "package.json"),
|
|
1619
1951
|
JSON.stringify(processedPkg, null, 2)
|
|
1620
1952
|
);
|
|
1621
1953
|
}
|
|
@@ -1627,7 +1959,7 @@ async function publish(packageDir, options = {}) {
|
|
|
1627
1959
|
buildId
|
|
1628
1960
|
};
|
|
1629
1961
|
await atomicWriteFile(
|
|
1630
|
-
|
|
1962
|
+
join10(tmpDir, ".knarr-meta.json"),
|
|
1631
1963
|
JSON.stringify(meta, null, 2)
|
|
1632
1964
|
);
|
|
1633
1965
|
const hadOld = await exists(storeEntryDir);
|
|
@@ -1651,7 +1983,7 @@ async function publish(packageDir, options = {}) {
|
|
|
1651
1983
|
if (hadOld && !isDryRun()) {
|
|
1652
1984
|
try {
|
|
1653
1985
|
const { captureHistory: captureHistory2 } = await Promise.resolve().then(() => (init_history(), history_exports));
|
|
1654
|
-
await captureHistory2(
|
|
1986
|
+
await captureHistory2(filePkg.name, filePkg.version, oldDir, options.historyLimit);
|
|
1655
1987
|
} catch (err) {
|
|
1656
1988
|
verbose(`[publish] History capture failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1657
1989
|
}
|
|
@@ -1663,8 +1995,8 @@ async function publish(packageDir, options = {}) {
|
|
|
1663
1995
|
throw err;
|
|
1664
1996
|
}
|
|
1665
1997
|
return {
|
|
1666
|
-
name:
|
|
1667
|
-
version:
|
|
1998
|
+
name: filePkg.name,
|
|
1999
|
+
version: filePkg.version,
|
|
1668
2000
|
fileCount: files.length,
|
|
1669
2001
|
skipped: false,
|
|
1670
2002
|
contentHash,
|
|
@@ -1679,10 +2011,46 @@ async function publish(packageDir, options = {}) {
|
|
|
1679
2011
|
}
|
|
1680
2012
|
await runLifecycleHook(packageDir, pkg, "postknarr");
|
|
1681
2013
|
consola.success(
|
|
1682
|
-
`Published ${
|
|
2014
|
+
`Published ${filePkg.name}@${filePkg.version} (${files.length} files) [${result.buildId}]`
|
|
1683
2015
|
);
|
|
1684
2016
|
return result;
|
|
1685
2017
|
}
|
|
2018
|
+
function pathEnvKey(env) {
|
|
2019
|
+
if (platform() !== "win32") return "PATH";
|
|
2020
|
+
return Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "Path";
|
|
2021
|
+
}
|
|
2022
|
+
function setEnvVar(env, key, value) {
|
|
2023
|
+
if (platform() === "win32") {
|
|
2024
|
+
const lowered = key.toLowerCase();
|
|
2025
|
+
for (const existingKey of Object.keys(env)) {
|
|
2026
|
+
if (existingKey.toLowerCase() === lowered) {
|
|
2027
|
+
delete env[existingKey];
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
env[key] = value;
|
|
2032
|
+
}
|
|
2033
|
+
function buildLifecycleEnv(packageDir, pkg, hookName, script) {
|
|
2034
|
+
const env = { ...process.env };
|
|
2035
|
+
const key = pathEnvKey(env);
|
|
2036
|
+
const existingPath = env[key] ?? "";
|
|
2037
|
+
setEnvVar(
|
|
2038
|
+
env,
|
|
2039
|
+
key,
|
|
2040
|
+
[
|
|
2041
|
+
join10(packageDir, "node_modules", ".bin"),
|
|
2042
|
+
existingPath
|
|
2043
|
+
].filter(Boolean).join(delimiter)
|
|
2044
|
+
);
|
|
2045
|
+
if (!("INIT_CWD" in env) || !env.INIT_CWD) {
|
|
2046
|
+
setEnvVar(env, "INIT_CWD", packageDir);
|
|
2047
|
+
}
|
|
2048
|
+
setEnvVar(env, "npm_lifecycle_event", hookName);
|
|
2049
|
+
setEnvVar(env, "npm_lifecycle_script", script);
|
|
2050
|
+
setEnvVar(env, "npm_package_name", pkg.name);
|
|
2051
|
+
setEnvVar(env, "npm_package_version", pkg.version);
|
|
2052
|
+
return env;
|
|
2053
|
+
}
|
|
1686
2054
|
async function runLifecycleHook(packageDir, pkg, hookName) {
|
|
1687
2055
|
const script = pkg.scripts?.[hookName];
|
|
1688
2056
|
if (!script) return;
|
|
@@ -1694,13 +2062,16 @@ async function runLifecycleHook(packageDir, pkg, hookName) {
|
|
|
1694
2062
|
});
|
|
1695
2063
|
return;
|
|
1696
2064
|
}
|
|
1697
|
-
|
|
2065
|
+
const command = await resolveLifecycleCommand(packageDir, hookName, script);
|
|
2066
|
+
verbose(`[lifecycle] Running ${hookName}: ${command}`);
|
|
1698
2067
|
return new Promise((resolve8, reject) => {
|
|
1699
2068
|
const isWin = platform() === "win32";
|
|
1700
2069
|
const shell = isWin ? "cmd" : "sh";
|
|
1701
2070
|
const shellFlag = isWin ? "/c" : "-c";
|
|
1702
|
-
const
|
|
2071
|
+
const env = buildLifecycleEnv(packageDir, pkg, hookName, script);
|
|
2072
|
+
const child = spawn(shell, [shellFlag, command], {
|
|
1703
2073
|
cwd: packageDir,
|
|
2074
|
+
env,
|
|
1704
2075
|
stdio: "inherit"
|
|
1705
2076
|
});
|
|
1706
2077
|
const timer = setTimeout(() => {
|
|
@@ -1721,6 +2092,10 @@ async function runLifecycleHook(packageDir, pkg, hookName) {
|
|
|
1721
2092
|
});
|
|
1722
2093
|
});
|
|
1723
2094
|
}
|
|
2095
|
+
async function resolveLifecycleCommand(packageDir, hookName, script) {
|
|
2096
|
+
const shouldUseYarn = await hasYarnPnpMarkers(packageDir) || (await detectPackageManagerInfo(packageDir)).packageManager === "yarn" && await isYarnPnpProject(packageDir);
|
|
2097
|
+
return shouldUseYarn ? `yarn run ${hookName}` : script;
|
|
2098
|
+
}
|
|
1724
2099
|
function applyPublishConfig(pkg) {
|
|
1725
2100
|
if (!pkg.publishConfig) return pkg;
|
|
1726
2101
|
const result = { ...pkg };
|
|
@@ -1749,13 +2124,9 @@ function rewriteProtocolVersions(pkg) {
|
|
|
1749
2124
|
const newDeps = { ...deps };
|
|
1750
2125
|
for (const [name, version] of Object.entries(deps)) {
|
|
1751
2126
|
if (version.startsWith("workspace:")) {
|
|
1752
|
-
const
|
|
1753
|
-
if (
|
|
1754
|
-
|
|
1755
|
-
newDeps[name] = versionPart === "*" ? depVersion : versionPart + depVersion;
|
|
1756
|
-
} else {
|
|
1757
|
-
newDeps[name] = versionPart;
|
|
1758
|
-
}
|
|
2127
|
+
const resolved = resolveWorkspaceSpecifier(name, version);
|
|
2128
|
+
if (!resolved) continue;
|
|
2129
|
+
newDeps[name] = resolved;
|
|
1759
2130
|
fieldChanged = true;
|
|
1760
2131
|
changed = true;
|
|
1761
2132
|
} else if (version.startsWith("catalog:")) {
|
|
@@ -1783,6 +2154,32 @@ function rewriteProtocolVersions(pkg) {
|
|
|
1783
2154
|
}
|
|
1784
2155
|
return changed ? result : pkg;
|
|
1785
2156
|
}
|
|
2157
|
+
function resolveWorkspaceSpecifier(depName, specifier) {
|
|
2158
|
+
const raw = specifier.slice("workspace:".length);
|
|
2159
|
+
const alias = parseWorkspaceAlias(raw);
|
|
2160
|
+
const targetName = alias?.name ?? depName;
|
|
2161
|
+
const versionPart = alias?.range ?? raw;
|
|
2162
|
+
if (versionPart === "*" || versionPart === "^" || versionPart === "~") {
|
|
2163
|
+
const depVersion = _cachedWorkspaceVersions?.versions.get(targetName);
|
|
2164
|
+
if (!depVersion) {
|
|
2165
|
+
consola.warn(
|
|
2166
|
+
`workspace: specifier for "${targetName}" could not be resolved \u2014 published package.json will contain "${specifier}" which may cause install failures`
|
|
2167
|
+
);
|
|
2168
|
+
return null;
|
|
2169
|
+
}
|
|
2170
|
+
const resolved = versionPart === "*" ? depVersion : versionPart + depVersion;
|
|
2171
|
+
return alias && targetName !== depName ? `npm:${targetName}@${resolved}` : resolved;
|
|
2172
|
+
}
|
|
2173
|
+
return alias && targetName !== depName ? `npm:${targetName}@${versionPart}` : versionPart;
|
|
2174
|
+
}
|
|
2175
|
+
function parseWorkspaceAlias(specifierBody) {
|
|
2176
|
+
const atIndex = specifierBody.lastIndexOf("@");
|
|
2177
|
+
if (atIndex <= 0) return null;
|
|
2178
|
+
const name = specifierBody.slice(0, atIndex);
|
|
2179
|
+
const range = specifierBody.slice(atIndex + 1);
|
|
2180
|
+
if (!name || !range) return null;
|
|
2181
|
+
return { name, range };
|
|
2182
|
+
}
|
|
1786
2183
|
function resolveCatalogVersion(specifier, depName, catalogs) {
|
|
1787
2184
|
const catalogRef = specifier.slice("catalog:".length);
|
|
1788
2185
|
if (catalogRef === "" || catalogRef === "default") {
|
|
@@ -1792,8 +2189,8 @@ function resolveCatalogVersion(specifier, depName, catalogs) {
|
|
|
1792
2189
|
}
|
|
1793
2190
|
async function getWorkspaceRoot(packageDir) {
|
|
1794
2191
|
if (_cachedWorkspaceRoot?.dir === packageDir) return _cachedWorkspaceRoot.root;
|
|
1795
|
-
const {
|
|
1796
|
-
const root = await
|
|
2192
|
+
const { findWorkspacePackageRoot: findWorkspacePackageRoot2 } = await Promise.resolve().then(() => (init_workspace(), workspace_exports));
|
|
2193
|
+
const root = await findWorkspacePackageRoot2(packageDir);
|
|
1797
2194
|
_cachedWorkspaceRoot = { dir: packageDir, root };
|
|
1798
2195
|
return root;
|
|
1799
2196
|
}
|
|
@@ -1813,7 +2210,6 @@ async function preloadWorkspaceVersions(pkg, packageDir) {
|
|
|
1813
2210
|
_cachedWorkspaceVersions = null;
|
|
1814
2211
|
return;
|
|
1815
2212
|
}
|
|
1816
|
-
if (_cachedWorkspaceVersions?.root === root) return;
|
|
1817
2213
|
const { findWorkspacePackages: findWorkspacePackages2 } = await Promise.resolve().then(() => (init_workspace(), workspace_exports));
|
|
1818
2214
|
const pkgDirs = await findWorkspacePackages2(root);
|
|
1819
2215
|
const versions = /* @__PURE__ */ new Map();
|
|
@@ -1821,7 +2217,7 @@ async function preloadWorkspaceVersions(pkg, packageDir) {
|
|
|
1821
2217
|
pkgDirs.map(async (dir) => {
|
|
1822
2218
|
try {
|
|
1823
2219
|
const depPkg = JSON.parse(
|
|
1824
|
-
await
|
|
2220
|
+
await readFile8(join10(dir, "package.json"), "utf-8")
|
|
1825
2221
|
);
|
|
1826
2222
|
if (depPkg.name && depPkg.version) {
|
|
1827
2223
|
versions.set(depPkg.name, depPkg.version);
|
|
@@ -1848,8 +2244,8 @@ async function preloadCatalogs(pkg, packageDir) {
|
|
|
1848
2244
|
_cachedCatalogs = null;
|
|
1849
2245
|
return;
|
|
1850
2246
|
}
|
|
1851
|
-
const workspaceFile =
|
|
1852
|
-
const mtimeMs = (await
|
|
2247
|
+
const workspaceFile = join10(root, "pnpm-workspace.yaml");
|
|
2248
|
+
const mtimeMs = (await stat6(workspaceFile).catch(() => null))?.mtimeMs ?? 0;
|
|
1853
2249
|
if (_cachedCatalogs?.root === root && _cachedCatalogs.mtimeMs === mtimeMs) return;
|
|
1854
2250
|
const { parseCatalogs: parseCatalogs2 } = await Promise.resolve().then(() => (init_workspace(), workspace_exports));
|
|
1855
2251
|
const catalogs = await parseCatalogs2(root);
|
|
@@ -1870,6 +2266,7 @@ var init_publisher = __esm({
|
|
|
1870
2266
|
init_logger();
|
|
1871
2267
|
init_dry_run();
|
|
1872
2268
|
init_validators();
|
|
2269
|
+
init_pm_detect();
|
|
1873
2270
|
copyLimit = pLimit(Math.max(availableParallelism3(), 8));
|
|
1874
2271
|
HOOK_TIMEOUT = parseInt(process.env.KNARR_HOOK_TIMEOUT ?? "30000", 10);
|
|
1875
2272
|
PUBLISH_CONFIG_OVERRIDES = [
|
|
@@ -1888,9 +2285,54 @@ var init_publisher = __esm({
|
|
|
1888
2285
|
});
|
|
1889
2286
|
|
|
1890
2287
|
// src/utils/bin-linker.ts
|
|
1891
|
-
import {
|
|
1892
|
-
|
|
2288
|
+
import {
|
|
2289
|
+
mkdir as mkdir3,
|
|
2290
|
+
symlink,
|
|
2291
|
+
writeFile as writeFile2,
|
|
2292
|
+
chmod,
|
|
2293
|
+
rm as rm3,
|
|
2294
|
+
lstat,
|
|
2295
|
+
readFile as readFile9,
|
|
2296
|
+
readlink
|
|
2297
|
+
} from "fs/promises";
|
|
2298
|
+
import { join as join11, relative as relative7, resolve as resolve5, sep as sep2 } from "path";
|
|
1893
2299
|
import { platform as platform2 } from "os";
|
|
2300
|
+
function safeInterpreter(value) {
|
|
2301
|
+
return value && SAFE_INTERPRETER_RE.test(value) ? value : "node";
|
|
2302
|
+
}
|
|
2303
|
+
function parseShebangInterpreter(content) {
|
|
2304
|
+
const firstLine = content.split(/\r?\n/, 1)[0]?.trim();
|
|
2305
|
+
if (!firstLine?.startsWith("#!")) return "node";
|
|
2306
|
+
const command = firstLine.slice(2).trim();
|
|
2307
|
+
const parts = command.split(/\s+/).filter(Boolean);
|
|
2308
|
+
if (parts.length === 0) return "node";
|
|
2309
|
+
const executable = parts[0].replace(/\\/g, "/").split("/").pop();
|
|
2310
|
+
if (executable === "env") {
|
|
2311
|
+
const args = parts.slice(1);
|
|
2312
|
+
const interpreter = args[0] === "-S" ? args[1] : args[0];
|
|
2313
|
+
return safeInterpreter(interpreter);
|
|
2314
|
+
}
|
|
2315
|
+
return safeInterpreter(executable);
|
|
2316
|
+
}
|
|
2317
|
+
async function readBinInterpreter(targetAbsolute) {
|
|
2318
|
+
try {
|
|
2319
|
+
return parseShebangInterpreter(await readFile9(targetAbsolute, "utf-8"));
|
|
2320
|
+
} catch {
|
|
2321
|
+
return "node";
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
function resolveBinPath(binDir, binName, suffix = "") {
|
|
2325
|
+
if (!binName || binName === "." || binName === ".." || CONTROL_CHARS_RE2.test(binName) || binName.includes("/") || binName.includes("\\") || binName.includes(":")) {
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
const path = join11(binDir, `${binName}${suffix}`);
|
|
2329
|
+
const resolvedBinDir = resolve5(binDir);
|
|
2330
|
+
const resolvedPath = resolve5(path);
|
|
2331
|
+
if (resolvedPath !== resolvedBinDir && resolvedPath.startsWith(resolvedBinDir + sep2)) {
|
|
2332
|
+
return path;
|
|
2333
|
+
}
|
|
2334
|
+
return null;
|
|
2335
|
+
}
|
|
1894
2336
|
function resolveBinEntries(pkg) {
|
|
1895
2337
|
if (!pkg.bin) return {};
|
|
1896
2338
|
if (typeof pkg.bin === "string") {
|
|
@@ -1902,28 +2344,46 @@ function resolveBinEntries(pkg) {
|
|
|
1902
2344
|
async function createBinLinks(consumerPath, packageName, pkg) {
|
|
1903
2345
|
const entries = resolveBinEntries(pkg);
|
|
1904
2346
|
if (Object.keys(entries).length === 0) return 0;
|
|
2347
|
+
const binDir = join11(consumerPath, "node_modules", ".bin");
|
|
2348
|
+
const safeEntries = Object.entries(entries).map(([binName, binPath]) => ({
|
|
2349
|
+
binName,
|
|
2350
|
+
binPath,
|
|
2351
|
+
linkPath: resolveBinPath(binDir, binName),
|
|
2352
|
+
cmdPath: resolveBinPath(binDir, binName, ".cmd"),
|
|
2353
|
+
ps1Path: resolveBinPath(binDir, binName, ".ps1")
|
|
2354
|
+
})).filter((entry) => {
|
|
2355
|
+
if (!entry.linkPath || !entry.cmdPath || !entry.ps1Path) {
|
|
2356
|
+
consola.warn(`bin name "${entry.binName}" is not safe, skipping`);
|
|
2357
|
+
return false;
|
|
2358
|
+
}
|
|
2359
|
+
return true;
|
|
2360
|
+
});
|
|
2361
|
+
const packageRoot = join11(consumerPath, "node_modules", packageName);
|
|
2362
|
+
const resolvedPackageRoot = resolve5(packageRoot);
|
|
2363
|
+
const validEntries = safeEntries.filter(({ binName, binPath }) => {
|
|
2364
|
+
const resolvedTarget = resolve5(join11(packageRoot, binPath));
|
|
2365
|
+
if (resolvedTarget.startsWith(resolvedPackageRoot + sep2) || resolvedTarget === resolvedPackageRoot) {
|
|
2366
|
+
return true;
|
|
2367
|
+
}
|
|
2368
|
+
consola.warn(`bin "${binName}" points outside package directory, skipping`);
|
|
2369
|
+
return false;
|
|
2370
|
+
});
|
|
2371
|
+
if (validEntries.length === 0) return 0;
|
|
1905
2372
|
if (isDryRun()) {
|
|
1906
|
-
for (const
|
|
1907
|
-
recordMutation({ type: "bin-link", path:
|
|
2373
|
+
for (const { linkPath } of validEntries) {
|
|
2374
|
+
recordMutation({ type: "bin-link", path: linkPath, detail: packageName });
|
|
1908
2375
|
}
|
|
1909
|
-
verbose(`[dry-run] would create ${
|
|
1910
|
-
return
|
|
2376
|
+
verbose(`[dry-run] would create ${validEntries.length} bin link(s) for ${packageName}`);
|
|
2377
|
+
return validEntries.length;
|
|
1911
2378
|
}
|
|
1912
|
-
const binDir = join9(consumerPath, "node_modules", ".bin");
|
|
1913
2379
|
await mkdir3(binDir, { recursive: true });
|
|
1914
2380
|
const isWindows = platform2() === "win32";
|
|
1915
2381
|
let count = 0;
|
|
1916
|
-
for (const
|
|
1917
|
-
const
|
|
1918
|
-
const targetAbsolute = join9(packageRoot, binPath);
|
|
1919
|
-
const resolvedTarget = resolve5(targetAbsolute);
|
|
1920
|
-
if (!resolvedTarget.startsWith(resolve5(packageRoot) + sep2) && resolvedTarget !== resolve5(packageRoot)) {
|
|
1921
|
-
consola.warn(`bin "${binName}" points outside package directory, skipping`);
|
|
1922
|
-
continue;
|
|
1923
|
-
}
|
|
2382
|
+
for (const { binName, binPath, linkPath, cmdPath, ps1Path } of validEntries) {
|
|
2383
|
+
const targetAbsolute = join11(packageRoot, binPath);
|
|
1924
2384
|
const targetRelative = normalizePath(relative7(binDir, targetAbsolute));
|
|
2385
|
+
const interpreter = await readBinInterpreter(targetAbsolute);
|
|
1925
2386
|
if (isWindows) {
|
|
1926
|
-
const cmdPath = join9(binDir, `${binName}.cmd`);
|
|
1927
2387
|
const targetWindows = targetRelative.replace(/\//g, "\\");
|
|
1928
2388
|
const cmdContent = `@ECHO off\r
|
|
1929
2389
|
GOTO start\r
|
|
@@ -1932,23 +2392,21 @@ SET dp0=%~dp0\r
|
|
|
1932
2392
|
EXIT /b\r
|
|
1933
2393
|
:start\r
|
|
1934
2394
|
CALL :find_dp0\r
|
|
1935
|
-
|
|
2395
|
+
${interpreter} "%dp0%\\${targetWindows}" %*\r
|
|
1936
2396
|
`;
|
|
1937
2397
|
await writeFile2(cmdPath, cmdContent);
|
|
1938
|
-
const ps1Path = join9(binDir, `${binName}.ps1`);
|
|
1939
2398
|
const ps1Content = `#!/usr/bin/env pwsh
|
|
1940
2399
|
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
|
1941
|
-
&
|
|
2400
|
+
& ${interpreter} "$basedir/${targetRelative}" $args
|
|
1942
2401
|
exit $LASTEXITCODE
|
|
1943
2402
|
`;
|
|
1944
2403
|
await writeFile2(ps1Path, ps1Content);
|
|
1945
|
-
const shPath = join9(binDir, binName);
|
|
1946
2404
|
const shContent = `#!/bin/sh
|
|
1947
|
-
|
|
2405
|
+
basedir=$(dirname "$0")
|
|
2406
|
+
exec ${interpreter} "$basedir/${targetRelative}" "$@"
|
|
1948
2407
|
`;
|
|
1949
|
-
await writeFile2(
|
|
2408
|
+
await writeFile2(linkPath, shContent);
|
|
1950
2409
|
} else {
|
|
1951
|
-
const linkPath = join9(binDir, binName);
|
|
1952
2410
|
try {
|
|
1953
2411
|
await rm3(linkPath, { force: true });
|
|
1954
2412
|
} catch {
|
|
@@ -1960,7 +2418,8 @@ exec node "${targetRelative}" "$@"
|
|
|
1960
2418
|
if (isNodeError(err) && (err.code === "EPERM" || err.code === "EACCES")) {
|
|
1961
2419
|
verbose(`[bin-linker] Symlink failed (${err.code}), using shell wrapper for ${binName}`);
|
|
1962
2420
|
const shContent = `#!/bin/sh
|
|
1963
|
-
|
|
2421
|
+
basedir=$(dirname "$0")
|
|
2422
|
+
exec ${interpreter} "$basedir/${targetRelative}" "$@"
|
|
1964
2423
|
`;
|
|
1965
2424
|
await writeFile2(linkPath, shContent);
|
|
1966
2425
|
await chmod(linkPath, 493);
|
|
@@ -1975,26 +2434,62 @@ exec node "${targetRelative}" "$@"
|
|
|
1975
2434
|
}
|
|
1976
2435
|
async function removeBinLinks(consumerPath, pkg) {
|
|
1977
2436
|
const entries = resolveBinEntries(pkg);
|
|
2437
|
+
const binDir = join11(consumerPath, "node_modules", ".bin");
|
|
2438
|
+
const safeEntries = Object.keys(entries).map((binName) => ({
|
|
2439
|
+
binName,
|
|
2440
|
+
linkPath: resolveBinPath(binDir, binName),
|
|
2441
|
+
cmdPath: resolveBinPath(binDir, binName, ".cmd"),
|
|
2442
|
+
ps1Path: resolveBinPath(binDir, binName, ".ps1")
|
|
2443
|
+
})).filter(
|
|
2444
|
+
(entry) => !!entry.linkPath && !!entry.cmdPath && !!entry.ps1Path
|
|
2445
|
+
);
|
|
1978
2446
|
if (isDryRun()) {
|
|
1979
|
-
for (const
|
|
1980
|
-
recordMutation({ type: "bin-unlink", path:
|
|
2447
|
+
for (const { linkPath } of safeEntries) {
|
|
2448
|
+
recordMutation({ type: "bin-unlink", path: linkPath });
|
|
1981
2449
|
}
|
|
1982
|
-
verbose(`[dry-run] would remove ${
|
|
2450
|
+
verbose(`[dry-run] would remove ${safeEntries.length} bin link(s)`);
|
|
1983
2451
|
return;
|
|
1984
2452
|
}
|
|
1985
|
-
const binDir = join9(consumerPath, "node_modules", ".bin");
|
|
1986
2453
|
const isWindows = platform2() === "win32";
|
|
1987
|
-
|
|
2454
|
+
const packageRoot = join11(consumerPath, "node_modules", pkg.name);
|
|
2455
|
+
for (const { linkPath, cmdPath, ps1Path } of safeEntries) {
|
|
1988
2456
|
try {
|
|
1989
|
-
await
|
|
2457
|
+
if (await binFileBelongsToPackage(binDir, linkPath, packageRoot)) {
|
|
2458
|
+
await rm3(linkPath, { force: true });
|
|
2459
|
+
}
|
|
1990
2460
|
if (isWindows) {
|
|
1991
|
-
await
|
|
1992
|
-
|
|
2461
|
+
if (await binFileBelongsToPackage(binDir, cmdPath, packageRoot)) {
|
|
2462
|
+
await rm3(cmdPath, { force: true });
|
|
2463
|
+
}
|
|
2464
|
+
if (await binFileBelongsToPackage(binDir, ps1Path, packageRoot)) {
|
|
2465
|
+
await rm3(ps1Path, { force: true });
|
|
2466
|
+
}
|
|
1993
2467
|
}
|
|
1994
2468
|
} catch {
|
|
1995
2469
|
}
|
|
1996
2470
|
}
|
|
1997
2471
|
}
|
|
2472
|
+
async function binFileBelongsToPackage(binDir, linkPath, packageRoot) {
|
|
2473
|
+
let fileStat;
|
|
2474
|
+
try {
|
|
2475
|
+
fileStat = await lstat(linkPath);
|
|
2476
|
+
} catch (err) {
|
|
2477
|
+
if (isNodeError(err) && err.code === "ENOENT") return false;
|
|
2478
|
+
throw err;
|
|
2479
|
+
}
|
|
2480
|
+
const resolvedPackageRoot = resolve5(packageRoot);
|
|
2481
|
+
if (fileStat.isSymbolicLink()) {
|
|
2482
|
+
const target = await readlink(linkPath);
|
|
2483
|
+
const resolvedTarget = resolve5(binDir, target);
|
|
2484
|
+
return resolvedTarget === resolvedPackageRoot || resolvedTarget.startsWith(resolvedPackageRoot + sep2);
|
|
2485
|
+
}
|
|
2486
|
+
if (!fileStat.isFile()) return false;
|
|
2487
|
+
const content = await readFile9(linkPath, "utf-8");
|
|
2488
|
+
const relPackageRoot = normalizePath(relative7(binDir, packageRoot));
|
|
2489
|
+
const relWindowsPackageRoot = relPackageRoot.replace(/\//g, "\\");
|
|
2490
|
+
return content.includes(`${relPackageRoot}/`) || content.includes(`${relPackageRoot}"`) || content.includes(`${relWindowsPackageRoot}\\`) || content.includes(`${relWindowsPackageRoot}"`);
|
|
2491
|
+
}
|
|
2492
|
+
var CONTROL_CHARS_RE2, SAFE_INTERPRETER_RE;
|
|
1998
2493
|
var init_bin_linker = __esm({
|
|
1999
2494
|
"src/utils/bin-linker.ts"() {
|
|
2000
2495
|
"use strict";
|
|
@@ -2003,106 +2498,31 @@ var init_bin_linker = __esm({
|
|
|
2003
2498
|
init_logger();
|
|
2004
2499
|
init_paths();
|
|
2005
2500
|
init_dry_run();
|
|
2501
|
+
CONTROL_CHARS_RE2 = /[\x00-\x1F\x7F]/;
|
|
2502
|
+
SAFE_INTERPRETER_RE = /^[A-Za-z0-9._+-]+$/;
|
|
2006
2503
|
}
|
|
2007
2504
|
});
|
|
2008
2505
|
|
|
2009
|
-
// src/utils/
|
|
2010
|
-
import {
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2506
|
+
// src/utils/bundler-detect.ts
|
|
2507
|
+
import { join as join12 } from "path";
|
|
2508
|
+
async function detectAllBundlers(projectDir) {
|
|
2509
|
+
const checks = BUNDLER_CONFIGS.flatMap(
|
|
2510
|
+
([type, configFiles]) => configFiles.map(async (configFile) => ({
|
|
2511
|
+
type,
|
|
2512
|
+
configFile: join12(projectDir, configFile),
|
|
2513
|
+
found: await exists(join12(projectDir, configFile))
|
|
2514
|
+
}))
|
|
2515
|
+
);
|
|
2516
|
+
const all = await Promise.all(checks);
|
|
2517
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2518
|
+
const results = [];
|
|
2519
|
+
for (const { type, configFile, found } of all) {
|
|
2520
|
+
if (found && !seen.has(type)) {
|
|
2521
|
+
seen.add(type);
|
|
2522
|
+
results.push({ type, configFile });
|
|
2523
|
+
}
|
|
2021
2524
|
}
|
|
2022
|
-
|
|
2023
|
-
async function detectPackageManager(projectDir) {
|
|
2024
|
-
let dir = projectDir;
|
|
2025
|
-
for (; ; ) {
|
|
2026
|
-
const fromField = await readPackageManagerField(dir);
|
|
2027
|
-
if (fromField) return fromField;
|
|
2028
|
-
const results = await Promise.all(
|
|
2029
|
-
LOCKFILES.map(async ([lockfile, pm]) => {
|
|
2030
|
-
try {
|
|
2031
|
-
await stat6(join10(dir, lockfile));
|
|
2032
|
-
return pm;
|
|
2033
|
-
} catch {
|
|
2034
|
-
return null;
|
|
2035
|
-
}
|
|
2036
|
-
})
|
|
2037
|
-
);
|
|
2038
|
-
const found = results.find((pm) => pm !== null);
|
|
2039
|
-
if (found) return found;
|
|
2040
|
-
const parent = dirname5(dir);
|
|
2041
|
-
if (parent === dir) return "npm";
|
|
2042
|
-
dir = parent;
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
async function detectYarnNodeLinker(projectDir) {
|
|
2046
|
-
let dir = projectDir;
|
|
2047
|
-
for (; ; ) {
|
|
2048
|
-
let content;
|
|
2049
|
-
try {
|
|
2050
|
-
content = await readFile8(join10(dir, ".yarnrc.yml"), "utf-8");
|
|
2051
|
-
} catch {
|
|
2052
|
-
const parent = dirname5(dir);
|
|
2053
|
-
if (parent === dir) return null;
|
|
2054
|
-
dir = parent;
|
|
2055
|
-
continue;
|
|
2056
|
-
}
|
|
2057
|
-
for (const line of content.split("\n")) {
|
|
2058
|
-
const trimmed = line.trim();
|
|
2059
|
-
if (trimmed.startsWith("#") || !trimmed.includes("nodeLinker")) continue;
|
|
2060
|
-
const match = trimmed.match(/^nodeLinker:\s*(.+)$/);
|
|
2061
|
-
if (match) {
|
|
2062
|
-
const value = match[1].trim().replace(/^["']|["']$/g, "");
|
|
2063
|
-
if (value === "node-modules" || value === "pnpm" || value === "pnp") {
|
|
2064
|
-
return value;
|
|
2065
|
-
}
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
return null;
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
var VALID_PMS, LOCKFILES;
|
|
2072
|
-
var init_pm_detect = __esm({
|
|
2073
|
-
"src/utils/pm-detect.ts"() {
|
|
2074
|
-
"use strict";
|
|
2075
|
-
VALID_PMS = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun"]);
|
|
2076
|
-
LOCKFILES = [
|
|
2077
|
-
["pnpm-lock.yaml", "pnpm"],
|
|
2078
|
-
["bun.lockb", "bun"],
|
|
2079
|
-
["bun.lock", "bun"],
|
|
2080
|
-
["yarn.lock", "yarn"],
|
|
2081
|
-
["package-lock.json", "npm"]
|
|
2082
|
-
];
|
|
2083
|
-
}
|
|
2084
|
-
});
|
|
2085
|
-
|
|
2086
|
-
// src/utils/bundler-detect.ts
|
|
2087
|
-
import { join as join11 } from "path";
|
|
2088
|
-
async function detectAllBundlers(projectDir) {
|
|
2089
|
-
const checks = BUNDLER_CONFIGS.flatMap(
|
|
2090
|
-
([type, configFiles]) => configFiles.map(async (configFile) => ({
|
|
2091
|
-
type,
|
|
2092
|
-
configFile: join11(projectDir, configFile),
|
|
2093
|
-
found: await exists(join11(projectDir, configFile))
|
|
2094
|
-
}))
|
|
2095
|
-
);
|
|
2096
|
-
const all = await Promise.all(checks);
|
|
2097
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2098
|
-
const results = [];
|
|
2099
|
-
for (const { type, configFile, found } of all) {
|
|
2100
|
-
if (found && !seen.has(type)) {
|
|
2101
|
-
seen.add(type);
|
|
2102
|
-
results.push({ type, configFile });
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2105
|
-
return results;
|
|
2525
|
+
return results;
|
|
2106
2526
|
}
|
|
2107
2527
|
var BUNDLER_CONFIGS;
|
|
2108
2528
|
var init_bundler_detect = __esm({
|
|
@@ -2120,27 +2540,29 @@ var init_bundler_detect = __esm({
|
|
|
2120
2540
|
});
|
|
2121
2541
|
|
|
2122
2542
|
// src/utils/bundler-cache.ts
|
|
2123
|
-
import { join as
|
|
2543
|
+
import { join as join13 } from "path";
|
|
2124
2544
|
async function invalidateBundlerCache(consumerPath) {
|
|
2125
2545
|
if (isDryRun()) {
|
|
2126
2546
|
verbose(`[dry-run] would invalidate bundler caches for ${consumerPath}`);
|
|
2127
2547
|
recordMutation({ type: "cache-invalidate", path: consumerPath });
|
|
2128
|
-
return;
|
|
2548
|
+
return 0;
|
|
2129
2549
|
}
|
|
2130
2550
|
let bundlers = bundlerCache.get(consumerPath);
|
|
2131
2551
|
if (!bundlers) {
|
|
2132
2552
|
bundlers = await detectAllBundlers(consumerPath);
|
|
2133
2553
|
bundlerCache.set(consumerPath, bundlers);
|
|
2134
2554
|
}
|
|
2555
|
+
let invalidated = 0;
|
|
2135
2556
|
for (const bundler of bundlers) {
|
|
2136
2557
|
if (!bundler.type) continue;
|
|
2137
2558
|
const dirs = CACHE_DIRS[bundler.type];
|
|
2138
2559
|
if (!dirs) continue;
|
|
2139
2560
|
for (const dir of dirs) {
|
|
2140
|
-
const cacheDir =
|
|
2561
|
+
const cacheDir = join13(consumerPath, dir);
|
|
2141
2562
|
if (await exists(cacheDir)) {
|
|
2142
2563
|
try {
|
|
2143
2564
|
await removeDir(cacheDir);
|
|
2565
|
+
invalidated++;
|
|
2144
2566
|
verbose(`[inject] Invalidated ${bundler.type} cache: ${dir}`);
|
|
2145
2567
|
} catch {
|
|
2146
2568
|
verbose(`[inject] Could not clear ${bundler.type} cache: ${dir} (locked?)`);
|
|
@@ -2148,6 +2570,7 @@ async function invalidateBundlerCache(consumerPath) {
|
|
|
2148
2570
|
}
|
|
2149
2571
|
}
|
|
2150
2572
|
}
|
|
2573
|
+
return invalidated;
|
|
2151
2574
|
}
|
|
2152
2575
|
var CACHE_DIRS, bundlerCache;
|
|
2153
2576
|
var init_bundler_cache = __esm({
|
|
@@ -2166,59 +2589,78 @@ var init_bundler_cache = __esm({
|
|
|
2166
2589
|
});
|
|
2167
2590
|
|
|
2168
2591
|
// src/core/injector.ts
|
|
2169
|
-
import { readFile as
|
|
2170
|
-
import { join as
|
|
2592
|
+
import { lstat as lstat2, readFile as readFile10, readdir as readdir6, realpath, rm as rm4, symlink as symlink2 } from "fs/promises";
|
|
2593
|
+
import { dirname as dirname6, isAbsolute as isAbsolute2, join as join14, relative as relative8, resolve as resolve6 } from "path";
|
|
2594
|
+
import { platform as platform3 } from "os";
|
|
2171
2595
|
async function inject(storeEntry, consumerPath, pm, options = {}) {
|
|
2172
|
-
const targetDir = await
|
|
2596
|
+
const targetDir = await resolveInjectionTarget(
|
|
2173
2597
|
consumerPath,
|
|
2174
2598
|
storeEntry.name,
|
|
2175
2599
|
pm,
|
|
2176
|
-
storeEntry.version
|
|
2600
|
+
storeEntry.version,
|
|
2601
|
+
{ repairMissingLink: true }
|
|
2177
2602
|
);
|
|
2178
2603
|
verbose(`[inject] ${storeEntry.name}@${storeEntry.version} \u2192 ${targetDir}`);
|
|
2179
2604
|
await ensureDir(targetDir);
|
|
2605
|
+
const previousPkg = await readPackageJson(targetDir);
|
|
2180
2606
|
const { copied, removed, skipped } = await incrementalCopy(
|
|
2181
2607
|
storeEntry.packageDir,
|
|
2182
2608
|
targetDir,
|
|
2183
2609
|
{ force: options.force }
|
|
2184
2610
|
);
|
|
2185
2611
|
verbose(`[inject] ${copied} copied, ${removed} removed, ${skipped} skipped`);
|
|
2186
|
-
|
|
2187
|
-
await invalidateBundlerCache(consumerPath);
|
|
2188
|
-
}
|
|
2612
|
+
const cacheInvalidations = copied > 0 || removed > 0 ? await invalidateBundlerCache(consumerPath) : 0;
|
|
2189
2613
|
const pkg = await readPackageJson(storeEntry.packageDir);
|
|
2614
|
+
if (previousPkg) {
|
|
2615
|
+
await removeBinLinks(consumerPath, previousPkg);
|
|
2616
|
+
}
|
|
2190
2617
|
const binLinks = pkg ? await createBinLinks(consumerPath, storeEntry.name, pkg) : 0;
|
|
2191
2618
|
if (binLinks > 0) {
|
|
2192
2619
|
verbose(`[inject] Created ${binLinks} bin link(s)`);
|
|
2193
2620
|
}
|
|
2194
|
-
return { copied, removed, skipped, binLinks };
|
|
2621
|
+
return { copied, removed, skipped, binLinks, cacheInvalidations };
|
|
2195
2622
|
}
|
|
2196
2623
|
async function backupExisting(consumerPath, packageName, pm) {
|
|
2197
|
-
const installedDir = await
|
|
2624
|
+
const installedDir = await resolveInjectionTarget(consumerPath, packageName, pm);
|
|
2198
2625
|
if (!await exists(installedDir)) return false;
|
|
2199
2626
|
const backupDir = getConsumerBackupPath(consumerPath, packageName);
|
|
2200
2627
|
await removeDir(backupDir);
|
|
2201
2628
|
await copyDir(installedDir, backupDir);
|
|
2202
2629
|
return true;
|
|
2203
2630
|
}
|
|
2204
|
-
async function restoreBackup(consumerPath, packageName, pm) {
|
|
2631
|
+
async function restoreBackup(consumerPath, packageName, pm, version) {
|
|
2205
2632
|
const backupDir = getConsumerBackupPath(consumerPath, packageName);
|
|
2206
2633
|
if (!await exists(backupDir)) return false;
|
|
2207
|
-
const targetDir = await
|
|
2634
|
+
const targetDir = await resolveInjectionTarget(consumerPath, packageName, pm, version);
|
|
2635
|
+
const currentPkg = await readPackageJson(targetDir);
|
|
2636
|
+
if (currentPkg) {
|
|
2637
|
+
await removeBinLinks(consumerPath, currentPkg);
|
|
2638
|
+
}
|
|
2208
2639
|
await removeDir(targetDir);
|
|
2209
2640
|
await copyDir(backupDir, targetDir);
|
|
2641
|
+
const restoredPkg = await readPackageJson(isDryRun() ? backupDir : targetDir);
|
|
2642
|
+
if (restoredPkg) {
|
|
2643
|
+
const binLinks = await createBinLinks(consumerPath, packageName, restoredPkg);
|
|
2644
|
+
if (binLinks > 0) {
|
|
2645
|
+
verbose(`[restore] Restored ${binLinks} original bin link(s) for ${packageName}`);
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2210
2648
|
await removeDir(backupDir);
|
|
2211
2649
|
return true;
|
|
2212
2650
|
}
|
|
2213
|
-
async function removeInjected(consumerPath, packageName, pm) {
|
|
2214
|
-
const
|
|
2651
|
+
async function removeInjected(consumerPath, packageName, pm, version) {
|
|
2652
|
+
const directPath = getNodeModulesPackagePath(consumerPath, packageName);
|
|
2653
|
+
const targetDir = await resolveInjectionTarget(consumerPath, packageName, pm, version);
|
|
2215
2654
|
const pkg = await readPackageJson(targetDir);
|
|
2216
2655
|
if (pkg) {
|
|
2217
2656
|
await removeBinLinks(consumerPath, pkg);
|
|
2218
2657
|
}
|
|
2219
2658
|
await removeDir(targetDir);
|
|
2659
|
+
if (resolve6(targetDir) !== resolve6(directPath)) {
|
|
2660
|
+
await removeDir(directPath);
|
|
2661
|
+
}
|
|
2220
2662
|
}
|
|
2221
|
-
async function checkMissingDeps(storeEntry, consumerPath) {
|
|
2663
|
+
async function checkMissingDeps(storeEntry, consumerPath, pm) {
|
|
2222
2664
|
const pkg = await readPackageJson(storeEntry.packageDir);
|
|
2223
2665
|
if (!pkg) return [];
|
|
2224
2666
|
const allDeps = {
|
|
@@ -2234,75 +2676,455 @@ async function checkMissingDeps(storeEntry, consumerPath) {
|
|
|
2234
2676
|
const results = await Promise.all(
|
|
2235
2677
|
depNames.map(async (dep) => ({
|
|
2236
2678
|
dep,
|
|
2237
|
-
installed: await
|
|
2679
|
+
installed: await isDependencyInstalledForPackage(dep, storeEntry, consumerPath, pm)
|
|
2238
2680
|
}))
|
|
2239
2681
|
);
|
|
2240
2682
|
return results.filter((r) => !r.installed).map((r) => r.dep);
|
|
2241
2683
|
}
|
|
2242
|
-
async function
|
|
2684
|
+
async function isDependencyInstalledForPackage(dependencyName, storeEntry, consumerPath, pm) {
|
|
2685
|
+
if (await exists(join14(consumerPath, "node_modules", dependencyName))) {
|
|
2686
|
+
return true;
|
|
2687
|
+
}
|
|
2688
|
+
if (!pm) return false;
|
|
2689
|
+
const targetDir = await resolveInjectionTarget(
|
|
2690
|
+
consumerPath,
|
|
2691
|
+
storeEntry.name,
|
|
2692
|
+
pm,
|
|
2693
|
+
storeEntry.version,
|
|
2694
|
+
{ warnOnFallback: false }
|
|
2695
|
+
);
|
|
2696
|
+
return dependencyVisibleFromDirectory(targetDir, dependencyName);
|
|
2697
|
+
}
|
|
2698
|
+
async function dependencyVisibleFromDirectory(startDir, dependencyName) {
|
|
2699
|
+
const realStart = await realpath(startDir).catch((err) => {
|
|
2700
|
+
if (isNodeError(err) && err.code === "ENOENT") return startDir;
|
|
2701
|
+
throw err;
|
|
2702
|
+
});
|
|
2703
|
+
let dir = realStart;
|
|
2704
|
+
for (; ; ) {
|
|
2705
|
+
if (await exists(join14(dir, "node_modules", dependencyName))) {
|
|
2706
|
+
return true;
|
|
2707
|
+
}
|
|
2708
|
+
const parent = dirname6(dir);
|
|
2709
|
+
if (parent === dir) return false;
|
|
2710
|
+
dir = parent;
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
async function resolveInjectionTarget(consumerPath, packageName, pm, version, options = {}) {
|
|
2243
2714
|
const directPath = getNodeModulesPackagePath(consumerPath, packageName);
|
|
2244
|
-
const
|
|
2245
|
-
|
|
2715
|
+
const currentPm = await detectPackageManagerInfo(consumerPath);
|
|
2716
|
+
const useTrackedPm = currentPm.source === "default";
|
|
2717
|
+
const effectiveYarn = currentPm.packageManager === "yarn" || useTrackedPm && pm === "yarn";
|
|
2718
|
+
if (await hasYarnPnpMarkers(consumerPath) || effectiveYarn && await isYarnPnpProject(consumerPath)) {
|
|
2719
|
+
throw new Error(
|
|
2720
|
+
"Yarn PnP mode is not compatible with Knarr. Set `nodeLinker: node-modules` or `nodeLinker: pnpm` in .yarnrc.yml, then run `yarn install`."
|
|
2721
|
+
);
|
|
2722
|
+
}
|
|
2723
|
+
const yarnLinker = effectiveYarn ? await detectYarnNodeLinker(consumerPath) : null;
|
|
2724
|
+
const storeKind = getEffectiveStoreKind(
|
|
2725
|
+
pm,
|
|
2726
|
+
currentPm.packageManager,
|
|
2727
|
+
currentPm.source,
|
|
2728
|
+
yarnLinker
|
|
2729
|
+
);
|
|
2730
|
+
const effectiveBun = currentPm.packageManager === "bun" || useTrackedPm && pm === "bun";
|
|
2731
|
+
if (!storeKind) {
|
|
2732
|
+
if (effectiveBun) {
|
|
2733
|
+
return resolveBunTarget(consumerPath, directPath, packageName, version);
|
|
2734
|
+
}
|
|
2246
2735
|
return directPath;
|
|
2247
2736
|
}
|
|
2737
|
+
const virtualStoreDirs = storeKind === "pnpm" ? await getPnpmVirtualStoreDirs(consumerPath) : await getYarnPnpmStoreDirs(consumerPath);
|
|
2248
2738
|
try {
|
|
2249
|
-
const realPath = await
|
|
2250
|
-
if (realPath
|
|
2251
|
-
|
|
2739
|
+
const realPath = await resolvePackageEntrySymlink(directPath);
|
|
2740
|
+
if (realPath) {
|
|
2741
|
+
const valid = storeKind === "pnpm" ? await isPnpmVirtualStorePackageRealPath(virtualStoreDirs, packageName, realPath) : await isYarnPnpmStorePackageRealPath(virtualStoreDirs, realPath);
|
|
2742
|
+
if (!valid) {
|
|
2743
|
+
throw new Error(
|
|
2744
|
+
`Refusing to inject ${packageName}: node_modules entry resolves outside a configured ${storeKindLabel(storeKind)} virtual store (${realPath})`
|
|
2745
|
+
);
|
|
2746
|
+
}
|
|
2747
|
+
await validateResolvedPackageIdentity(realPath, packageName, version, storeKind);
|
|
2748
|
+
verbose(`[inject] ${storeKindLabel(storeKind)}: resolved symlink \u2192 ${realPath}`);
|
|
2252
2749
|
return realPath;
|
|
2253
2750
|
}
|
|
2254
2751
|
} catch (err) {
|
|
2255
|
-
if (isNodeError(err) && err.code
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
const pnpmDir = join13(consumerPath, "node_modules", ".pnpm");
|
|
2260
|
-
if (await exists(pnpmDir)) {
|
|
2261
|
-
verbose(`[inject] pnpm: scanning .pnpm/ for ${packageName}`);
|
|
2262
|
-
const encodedName = packageName.replaceAll("/", "+");
|
|
2263
|
-
if (version) {
|
|
2264
|
-
const exactEntry = `${encodedName}@${version}`;
|
|
2265
|
-
const candidate = join13(pnpmDir, exactEntry, "node_modules", packageName);
|
|
2266
|
-
if (await exists(candidate)) {
|
|
2267
|
-
verbose(`[inject] pnpm: exact version match in .pnpm/ \u2192 ${candidate}`);
|
|
2268
|
-
return candidate;
|
|
2752
|
+
if (isNodeError(err) && err.code === "ENOENT") {
|
|
2753
|
+
} else {
|
|
2754
|
+
if (isNodeError(err)) {
|
|
2755
|
+
verbose(`${storeKindLabel(storeKind)} symlink resolution error: ${err instanceof Error ? err.message : String(err)}`);
|
|
2269
2756
|
}
|
|
2757
|
+
throw err;
|
|
2270
2758
|
}
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2759
|
+
}
|
|
2760
|
+
const existingStoreDirs = [];
|
|
2761
|
+
for (const storeDir of virtualStoreDirs) {
|
|
2762
|
+
if (await exists(storeDir)) {
|
|
2763
|
+
existingStoreDirs.push(storeDir);
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
if (existingStoreDirs.length > 0) {
|
|
2767
|
+
if (!await isDeclaredConsumerDependency(consumerPath, packageName)) {
|
|
2768
|
+
verbose(
|
|
2769
|
+
`[inject] ${storeKindLabel(storeKind)}: ${packageName} is not declared by the consumer, skipping virtual store scan`
|
|
2770
|
+
);
|
|
2771
|
+
return directPath;
|
|
2772
|
+
}
|
|
2773
|
+
if (storeKind === "yarn-pnpm") {
|
|
2774
|
+
const matches = [];
|
|
2775
|
+
for (const storeDir of existingStoreDirs) {
|
|
2776
|
+
verbose(`[inject] yarn-pnpm: scanning ${relative8(consumerPath, storeDir) || "."} for ${packageName}`);
|
|
2777
|
+
const candidate2 = await resolveYarnPnpmCandidate(
|
|
2778
|
+
storeDir,
|
|
2779
|
+
packageName,
|
|
2780
|
+
version
|
|
2279
2781
|
);
|
|
2280
|
-
if (
|
|
2281
|
-
|
|
2782
|
+
if (candidate2) {
|
|
2783
|
+
matches.push(candidate2);
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
const candidate = requireSingleVirtualStoreCandidate(packageName, storeKind, matches);
|
|
2787
|
+
if (candidate) {
|
|
2788
|
+
verbose(`[inject] yarn-pnpm: found in virtual store \u2192 ${candidate}`);
|
|
2789
|
+
if (options.repairMissingLink) {
|
|
2790
|
+
await repairMissingVirtualStoreLink(directPath, candidate, packageName, storeKind);
|
|
2791
|
+
}
|
|
2792
|
+
return candidate;
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
if (storeKind === "pnpm") {
|
|
2796
|
+
const encodedName = packageName.replaceAll("/", "+");
|
|
2797
|
+
for (const pnpmDir of existingStoreDirs) {
|
|
2798
|
+
verbose(`[inject] pnpm: scanning ${relative8(consumerPath, pnpmDir) || "."} for ${packageName}`);
|
|
2799
|
+
if (version) {
|
|
2800
|
+
const exactEntry = `${encodedName}@${version}`;
|
|
2801
|
+
const candidate2 = await resolvePnpmCandidate(
|
|
2802
|
+
pnpmDir,
|
|
2803
|
+
packageName,
|
|
2804
|
+
version,
|
|
2805
|
+
join14(pnpmDir, exactEntry, "node_modules", packageName)
|
|
2806
|
+
);
|
|
2807
|
+
if (candidate2) {
|
|
2808
|
+
verbose(`[inject] pnpm: exact version match in virtual store \u2192 ${candidate2}`);
|
|
2809
|
+
if (options.repairMissingLink) {
|
|
2810
|
+
await repairMissingVirtualStoreLink(directPath, candidate2, packageName, storeKind);
|
|
2811
|
+
}
|
|
2812
|
+
return candidate2;
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
const entries = await readdir6(pnpmDir);
|
|
2816
|
+
const matches = [];
|
|
2817
|
+
for (const entry of entries) {
|
|
2818
|
+
if (matchesPnpmPackageEntry(encodedName, version, entry)) {
|
|
2819
|
+
const candidate2 = await resolvePnpmCandidate(
|
|
2820
|
+
pnpmDir,
|
|
2821
|
+
packageName,
|
|
2822
|
+
version,
|
|
2823
|
+
join14(pnpmDir, entry, "node_modules", packageName)
|
|
2824
|
+
);
|
|
2825
|
+
if (candidate2) {
|
|
2826
|
+
matches.push(candidate2);
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
const candidate = requireSingleVirtualStoreCandidate(packageName, storeKind, matches);
|
|
2831
|
+
if (candidate) {
|
|
2832
|
+
verbose(`[inject] pnpm: found in virtual store \u2192 ${candidate}`);
|
|
2833
|
+
if (options.repairMissingLink) {
|
|
2834
|
+
await repairMissingVirtualStoreLink(directPath, candidate, packageName, storeKind);
|
|
2835
|
+
}
|
|
2282
2836
|
return candidate;
|
|
2283
2837
|
}
|
|
2284
2838
|
}
|
|
2285
2839
|
}
|
|
2286
2840
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2841
|
+
if (options.warnOnFallback !== false) {
|
|
2842
|
+
consola.warn(
|
|
2843
|
+
`${storeKindLabel(storeKind)}: Could not find ${packageName} in a configured virtual store, using direct node_modules path. If this causes issues, run your package manager's install command to rebuild the virtual store, then 'knarr add' again.`
|
|
2844
|
+
);
|
|
2845
|
+
}
|
|
2290
2846
|
return directPath;
|
|
2291
2847
|
}
|
|
2292
|
-
|
|
2848
|
+
function storeKindLabel(kind) {
|
|
2849
|
+
return kind === "pnpm" ? "pnpm" : "Yarn pnpm-linker";
|
|
2850
|
+
}
|
|
2851
|
+
async function resolveBunTarget(consumerPath, directPath, packageName, version) {
|
|
2293
2852
|
try {
|
|
2294
|
-
await
|
|
2295
|
-
|
|
2853
|
+
const linkStat = await lstat2(directPath);
|
|
2854
|
+
if (!linkStat.isSymbolicLink()) return directPath;
|
|
2296
2855
|
} catch (err) {
|
|
2297
|
-
if (isNodeError(err) && err.code === "ENOENT")
|
|
2298
|
-
|
|
2856
|
+
if (isNodeError(err) && err.code === "ENOENT") return directPath;
|
|
2857
|
+
throw err;
|
|
2858
|
+
}
|
|
2859
|
+
const realPath = await realpath(directPath);
|
|
2860
|
+
const bunStoreDir = join14(consumerPath, "node_modules", ".bun");
|
|
2861
|
+
const bunStoreRoot = await realpath(bunStoreDir).catch((err) => {
|
|
2862
|
+
if (isNodeError(err) && err.code === "ENOENT") return bunStoreDir;
|
|
2863
|
+
throw err;
|
|
2864
|
+
});
|
|
2865
|
+
if (!isPathInside(bunStoreRoot, realPath)) {
|
|
2866
|
+
throw new Error(
|
|
2867
|
+
`Refusing to inject ${packageName}: Bun target resolves outside the local node_modules/.bun store (${realPath})`
|
|
2868
|
+
);
|
|
2869
|
+
}
|
|
2870
|
+
await validateResolvedPackageIdentity(realPath, packageName, version, "Bun isolated");
|
|
2871
|
+
verbose(`[inject] Bun isolated: resolved symlink \u2192 ${realPath}`);
|
|
2872
|
+
return realPath;
|
|
2873
|
+
}
|
|
2874
|
+
function isPathInside(parentDir, childPath) {
|
|
2875
|
+
const rel = relative8(resolve6(parentDir), resolve6(childPath));
|
|
2876
|
+
return rel === "" || !!rel && !rel.startsWith("..") && !isAbsolute2(rel);
|
|
2877
|
+
}
|
|
2878
|
+
function getEffectiveStoreKind(storedPm, currentPm, currentSource, yarnLinker) {
|
|
2879
|
+
if (currentSource !== "default") {
|
|
2880
|
+
if (currentPm === "yarn") {
|
|
2881
|
+
return yarnLinker === "pnpm" ? "yarn-pnpm" : null;
|
|
2299
2882
|
}
|
|
2883
|
+
return currentPm === "pnpm" ? "pnpm" : null;
|
|
2884
|
+
}
|
|
2885
|
+
if (storedPm === "yarn") {
|
|
2886
|
+
return yarnLinker === "pnpm" ? "yarn-pnpm" : null;
|
|
2887
|
+
}
|
|
2888
|
+
return storedPm === "pnpm" ? "pnpm" : null;
|
|
2889
|
+
}
|
|
2890
|
+
async function resolvePackageEntrySymlink(linkPath) {
|
|
2891
|
+
const linkStat = await lstat2(linkPath);
|
|
2892
|
+
if (!linkStat.isSymbolicLink()) return null;
|
|
2893
|
+
return realpath(linkPath);
|
|
2894
|
+
}
|
|
2895
|
+
async function validateResolvedPackageIdentity(targetDir, packageName, version, storeKind) {
|
|
2896
|
+
const label = storeKind === "Bun isolated" ? storeKind : storeKindLabel(storeKind);
|
|
2897
|
+
const pkg = await readPackageJson(targetDir);
|
|
2898
|
+
if (!pkg) return;
|
|
2899
|
+
if (pkg.name && pkg.name !== packageName) {
|
|
2900
|
+
throw new Error(
|
|
2901
|
+
`Refusing to inject ${packageName}: ${label} target contains package "${pkg.name}"`
|
|
2902
|
+
);
|
|
2903
|
+
}
|
|
2904
|
+
if (version && pkg.version && pkg.version !== version) {
|
|
2905
|
+
throw new Error(
|
|
2906
|
+
`Refusing to inject ${packageName}@${version}: ${label} target contains version ${pkg.version}`
|
|
2907
|
+
);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
async function repairMissingVirtualStoreLink(directPath, targetDir, packageName, storeKind) {
|
|
2911
|
+
let removeDanglingSymlink = false;
|
|
2912
|
+
try {
|
|
2913
|
+
const directStat = await lstat2(directPath);
|
|
2914
|
+
if (!directStat.isSymbolicLink()) return;
|
|
2915
|
+
removeDanglingSymlink = true;
|
|
2916
|
+
} catch (err) {
|
|
2917
|
+
if (!isNodeError(err) || err.code !== "ENOENT") throw err;
|
|
2918
|
+
}
|
|
2919
|
+
const linkParent = dirname6(directPath);
|
|
2920
|
+
if (isDryRun()) {
|
|
2921
|
+
const targetRelative2 = relative8(linkParent, targetDir);
|
|
2922
|
+
verbose(
|
|
2923
|
+
`[inject] ${storeKindLabel(storeKind)}: would restore node_modules symlink for ${packageName} \u2192 ${targetRelative2}`
|
|
2924
|
+
);
|
|
2925
|
+
recordMutation({
|
|
2926
|
+
type: "write",
|
|
2927
|
+
path: directPath,
|
|
2928
|
+
dest: targetDir,
|
|
2929
|
+
detail: `${storeKindLabel(storeKind)} package symlink`
|
|
2930
|
+
});
|
|
2931
|
+
return;
|
|
2932
|
+
}
|
|
2933
|
+
const isWindows = platform3() === "win32";
|
|
2934
|
+
await ensureDir(linkParent);
|
|
2935
|
+
const linkParentReal = await realpath(linkParent).catch(() => linkParent);
|
|
2936
|
+
const targetRelative = relative8(linkParentReal, targetDir);
|
|
2937
|
+
verbose(
|
|
2938
|
+
`[inject] ${storeKindLabel(storeKind)}: restoring node_modules symlink for ${packageName} \u2192 ${targetRelative}`
|
|
2939
|
+
);
|
|
2940
|
+
if (removeDanglingSymlink) {
|
|
2941
|
+
await rm4(directPath, { force: true });
|
|
2942
|
+
}
|
|
2943
|
+
await symlink2(
|
|
2944
|
+
isWindows ? targetDir : targetRelative,
|
|
2945
|
+
directPath,
|
|
2946
|
+
isWindows ? "junction" : "dir"
|
|
2947
|
+
);
|
|
2948
|
+
}
|
|
2949
|
+
async function resolvePnpmCandidate(pnpmDir, packageName, version, candidatePath) {
|
|
2950
|
+
if (!isPnpmVirtualStorePackagePathFromRoot(pnpmDir, packageName, candidatePath)) {
|
|
2951
|
+
return null;
|
|
2952
|
+
}
|
|
2953
|
+
try {
|
|
2954
|
+
const [pnpmRoot, realPath] = await Promise.all([
|
|
2955
|
+
realpath(pnpmDir),
|
|
2956
|
+
realpath(candidatePath)
|
|
2957
|
+
]);
|
|
2958
|
+
if (!isPnpmVirtualStorePackagePathFromRoot(pnpmRoot, packageName, realPath)) {
|
|
2959
|
+
throw new Error(
|
|
2960
|
+
`Refusing to inject ${packageName}: virtual store candidate resolves outside a configured pnpm virtual store (${realPath})`
|
|
2961
|
+
);
|
|
2962
|
+
}
|
|
2963
|
+
await validateResolvedPackageIdentity(realPath, packageName, version, "pnpm");
|
|
2964
|
+
return realPath;
|
|
2965
|
+
} catch (err) {
|
|
2966
|
+
if (isNodeError(err) && err.code === "ENOENT") return null;
|
|
2300
2967
|
throw err;
|
|
2301
2968
|
}
|
|
2302
2969
|
}
|
|
2970
|
+
async function resolveYarnPnpmCandidate(storeDir, packageName, version) {
|
|
2971
|
+
const entries = await readdir6(storeDir);
|
|
2972
|
+
const matches = [];
|
|
2973
|
+
for (const entry of entries) {
|
|
2974
|
+
const candidatePath = join14(storeDir, entry, "package");
|
|
2975
|
+
const pkg = await readPackageJson(candidatePath);
|
|
2976
|
+
if (!pkg || pkg.name !== packageName) continue;
|
|
2977
|
+
if (version && pkg.version !== version) continue;
|
|
2978
|
+
const [storeRoot, realPath] = await Promise.all([
|
|
2979
|
+
realpath(storeDir),
|
|
2980
|
+
realpath(candidatePath)
|
|
2981
|
+
]);
|
|
2982
|
+
if (!isYarnPnpmStorePackagePathFromRoot(storeRoot, realPath)) {
|
|
2983
|
+
throw new Error(
|
|
2984
|
+
`Refusing to inject ${packageName}: virtual store candidate resolves outside a configured Yarn pnpm-linker virtual store (${realPath})`
|
|
2985
|
+
);
|
|
2986
|
+
}
|
|
2987
|
+
matches.push(realPath);
|
|
2988
|
+
}
|
|
2989
|
+
return requireSingleVirtualStoreCandidate(packageName, "yarn-pnpm", matches);
|
|
2990
|
+
}
|
|
2991
|
+
function matchesPnpmPackageEntry(encodedName, version, entry) {
|
|
2992
|
+
if (!version) return entry.startsWith(`${encodedName}@`);
|
|
2993
|
+
const exactEntry = `${encodedName}@${version}`;
|
|
2994
|
+
return entry === exactEntry || entry.startsWith(`${exactEntry}_`);
|
|
2995
|
+
}
|
|
2996
|
+
function isInside(root, target) {
|
|
2997
|
+
const rel = relative8(resolve6(root), resolve6(target));
|
|
2998
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute2(rel);
|
|
2999
|
+
}
|
|
3000
|
+
function requireSingleVirtualStoreCandidate(packageName, storeKind, matches) {
|
|
3001
|
+
if (matches.length === 0) return null;
|
|
3002
|
+
if (matches.length === 1) return matches[0];
|
|
3003
|
+
throw new Error(
|
|
3004
|
+
`Refusing to repair ${packageName}: multiple ${storeKindLabel(storeKind)} virtual-store entries match. Run your package manager's install command, then try again.`
|
|
3005
|
+
);
|
|
3006
|
+
}
|
|
3007
|
+
async function getPnpmVirtualStoreDirs(consumerPath) {
|
|
3008
|
+
const nodeModulesDir = join14(consumerPath, "node_modules");
|
|
3009
|
+
const dirs = [];
|
|
3010
|
+
const defaultDir = join14(nodeModulesDir, ".pnpm");
|
|
3011
|
+
if (await isLocalVirtualStoreDir(consumerPath, defaultDir)) {
|
|
3012
|
+
dirs.push(defaultDir);
|
|
3013
|
+
}
|
|
3014
|
+
const configured = await readConfiguredPnpmVirtualStoreDir(nodeModulesDir);
|
|
3015
|
+
if (!configured) return dirs;
|
|
3016
|
+
const configuredDir = isAbsolute2(configured) ? configured : resolve6(nodeModulesDir, configured);
|
|
3017
|
+
if (!await isLocalVirtualStoreDir(consumerPath, configuredDir)) {
|
|
3018
|
+
verbose(`[inject] pnpm: ignoring external virtualStoreDir ${configuredDir}`);
|
|
3019
|
+
return dirs;
|
|
3020
|
+
}
|
|
3021
|
+
if (!dirs.some((dir) => resolve6(dir) === resolve6(configuredDir))) {
|
|
3022
|
+
dirs.push(configuredDir);
|
|
3023
|
+
}
|
|
3024
|
+
return dirs;
|
|
3025
|
+
}
|
|
3026
|
+
async function getYarnPnpmStoreDirs(consumerPath) {
|
|
3027
|
+
const nodeModulesDir = join14(consumerPath, "node_modules");
|
|
3028
|
+
const dirs = [];
|
|
3029
|
+
const defaultDir = join14(nodeModulesDir, ".store");
|
|
3030
|
+
if (await isLocalVirtualStoreDir(consumerPath, defaultDir)) {
|
|
3031
|
+
dirs.push(defaultDir);
|
|
3032
|
+
}
|
|
3033
|
+
const configured = await detectYarnPnpmStoreFolder(consumerPath);
|
|
3034
|
+
if (!configured) return dirs;
|
|
3035
|
+
const configuredDir = isAbsolute2(configured) ? configured : resolve6(consumerPath, configured);
|
|
3036
|
+
if (!await isLocalVirtualStoreDir(consumerPath, configuredDir)) {
|
|
3037
|
+
verbose(`[inject] yarn-pnpm: ignoring external pnpmStoreFolder ${configuredDir}`);
|
|
3038
|
+
return dirs;
|
|
3039
|
+
}
|
|
3040
|
+
if (!dirs.some((dir) => resolve6(dir) === resolve6(configuredDir))) {
|
|
3041
|
+
dirs.push(configuredDir);
|
|
3042
|
+
}
|
|
3043
|
+
return dirs;
|
|
3044
|
+
}
|
|
3045
|
+
async function isLocalVirtualStoreDir(consumerPath, storeDir) {
|
|
3046
|
+
const consumerRoot = await realpath(consumerPath).catch(() => resolve6(consumerPath));
|
|
3047
|
+
const storeRoot = await realpath(storeDir).catch((err) => {
|
|
3048
|
+
if (isNodeError(err) && err.code === "ENOENT") return resolve6(storeDir);
|
|
3049
|
+
throw err;
|
|
3050
|
+
});
|
|
3051
|
+
return isInside(consumerRoot, storeRoot);
|
|
3052
|
+
}
|
|
3053
|
+
async function readConfiguredPnpmVirtualStoreDir(nodeModulesDir) {
|
|
3054
|
+
try {
|
|
3055
|
+
const content = await readFile10(join14(nodeModulesDir, ".modules.yaml"), "utf-8");
|
|
3056
|
+
return parsePnpmVirtualStoreDir(content);
|
|
3057
|
+
} catch (err) {
|
|
3058
|
+
if (isNodeError(err) && err.code === "ENOENT") return null;
|
|
3059
|
+
throw err;
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
function parsePnpmVirtualStoreDir(content) {
|
|
3063
|
+
try {
|
|
3064
|
+
const parsed = JSON.parse(content);
|
|
3065
|
+
if (typeof parsed.virtualStoreDir === "string" && parsed.virtualStoreDir.trim()) {
|
|
3066
|
+
return parsed.virtualStoreDir.trim();
|
|
3067
|
+
}
|
|
3068
|
+
} catch {
|
|
3069
|
+
}
|
|
3070
|
+
const match = content.match(/^virtualStoreDir:\s*(.+?)\s*$/m);
|
|
3071
|
+
if (!match) return null;
|
|
3072
|
+
let value = match[1].trim().replace(/\s+#.*$/, "");
|
|
3073
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
3074
|
+
value = value.slice(1, -1);
|
|
3075
|
+
}
|
|
3076
|
+
return value || null;
|
|
3077
|
+
}
|
|
3078
|
+
async function isPnpmVirtualStorePackageRealPath(pnpmDirs, packageName, targetPath) {
|
|
3079
|
+
for (const pnpmDir of pnpmDirs) {
|
|
3080
|
+
try {
|
|
3081
|
+
const pnpmRoot = await realpath(pnpmDir);
|
|
3082
|
+
if (isPnpmVirtualStorePackagePathFromRoot(pnpmRoot, packageName, targetPath)) {
|
|
3083
|
+
return true;
|
|
3084
|
+
}
|
|
3085
|
+
} catch (err) {
|
|
3086
|
+
if (isNodeError(err) && err.code === "ENOENT") continue;
|
|
3087
|
+
throw err;
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
return false;
|
|
3091
|
+
}
|
|
3092
|
+
async function isYarnPnpmStorePackageRealPath(storeDirs, targetPath) {
|
|
3093
|
+
for (const storeDir of storeDirs) {
|
|
3094
|
+
try {
|
|
3095
|
+
const storeRoot = await realpath(storeDir);
|
|
3096
|
+
if (isYarnPnpmStorePackagePathFromRoot(storeRoot, targetPath)) {
|
|
3097
|
+
return true;
|
|
3098
|
+
}
|
|
3099
|
+
} catch (err) {
|
|
3100
|
+
if (isNodeError(err) && err.code === "ENOENT") continue;
|
|
3101
|
+
throw err;
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
return false;
|
|
3105
|
+
}
|
|
3106
|
+
function isPnpmVirtualStorePackagePathFromRoot(pnpmDir, packageName, targetPath) {
|
|
3107
|
+
if (!isInside(pnpmDir, targetPath)) return false;
|
|
3108
|
+
const rel = relative8(pnpmDir, targetPath).replace(/\\/g, "/");
|
|
3109
|
+
const suffix = `node_modules/${packageName}`.replace(/\\/g, "/");
|
|
3110
|
+
return rel === suffix || rel.endsWith(`/${suffix}`);
|
|
3111
|
+
}
|
|
3112
|
+
function isYarnPnpmStorePackagePathFromRoot(storeDir, targetPath) {
|
|
3113
|
+
if (!isInside(storeDir, targetPath)) return false;
|
|
3114
|
+
const rel = relative8(storeDir, targetPath).replace(/\\/g, "/");
|
|
3115
|
+
const parts = rel.split("/");
|
|
3116
|
+
return parts.length === 2 && parts[1] === "package";
|
|
3117
|
+
}
|
|
3118
|
+
async function isDeclaredConsumerDependency(consumerPath, packageName) {
|
|
3119
|
+
const pkg = await readPackageJson(consumerPath);
|
|
3120
|
+
if (!pkg) return false;
|
|
3121
|
+
return Boolean(
|
|
3122
|
+
pkg.dependencies?.[packageName] || pkg.devDependencies?.[packageName] || pkg.optionalDependencies?.[packageName] || pkg.peerDependencies?.[packageName]
|
|
3123
|
+
);
|
|
3124
|
+
}
|
|
2303
3125
|
async function readPackageJson(dir) {
|
|
2304
3126
|
try {
|
|
2305
|
-
const content = await
|
|
3127
|
+
const content = await readFile10(join14(dir, "package.json"), "utf-8");
|
|
2306
3128
|
return JSON.parse(content);
|
|
2307
3129
|
} catch (err) {
|
|
2308
3130
|
if (isNodeError(err) && err.code !== "ENOENT") {
|
|
@@ -2319,14 +3141,15 @@ var init_injector = __esm({
|
|
|
2319
3141
|
init_fs();
|
|
2320
3142
|
init_bin_linker();
|
|
2321
3143
|
init_logger();
|
|
3144
|
+
init_dry_run();
|
|
2322
3145
|
init_pm_detect();
|
|
2323
3146
|
init_bundler_cache();
|
|
2324
3147
|
}
|
|
2325
3148
|
});
|
|
2326
3149
|
|
|
2327
3150
|
// src/core/tracker.ts
|
|
2328
|
-
import { readFile as
|
|
2329
|
-
import { dirname as
|
|
3151
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
3152
|
+
import { dirname as dirname7 } from "path";
|
|
2330
3153
|
async function readConsumerState(consumerPath) {
|
|
2331
3154
|
const { state } = await readConsumerStateSafe(consumerPath);
|
|
2332
3155
|
return state;
|
|
@@ -2334,7 +3157,7 @@ async function readConsumerState(consumerPath) {
|
|
|
2334
3157
|
async function readConsumerStateSafe(consumerPath) {
|
|
2335
3158
|
const statePath = getConsumerStatePath(consumerPath);
|
|
2336
3159
|
try {
|
|
2337
|
-
const content = await
|
|
3160
|
+
const content = await readFile11(statePath, "utf-8");
|
|
2338
3161
|
const parsed = JSON.parse(content);
|
|
2339
3162
|
if (!isConsumerState(parsed)) {
|
|
2340
3163
|
consola.warn(`Invalid consumer state in ${statePath}, using defaults`);
|
|
@@ -2387,7 +3210,7 @@ async function getLink(consumerPath, packageName) {
|
|
|
2387
3210
|
async function readConsumersRegistry() {
|
|
2388
3211
|
const regPath = getConsumersPath();
|
|
2389
3212
|
try {
|
|
2390
|
-
const content = await
|
|
3213
|
+
const content = await readFile11(regPath, "utf-8");
|
|
2391
3214
|
const parsed = JSON.parse(content);
|
|
2392
3215
|
if (!isConsumersRegistry(parsed)) {
|
|
2393
3216
|
consola.warn(`Invalid consumers registry, using empty registry`);
|
|
@@ -2403,7 +3226,7 @@ async function readConsumersRegistry() {
|
|
|
2403
3226
|
}
|
|
2404
3227
|
async function writeConsumersRegistry(registry) {
|
|
2405
3228
|
const regPath = getConsumersPath();
|
|
2406
|
-
await ensurePrivateDir(
|
|
3229
|
+
await ensurePrivateDir(dirname7(getConsumersPath()));
|
|
2407
3230
|
await atomicWriteFile(regPath, JSON.stringify(registry, null, 2));
|
|
2408
3231
|
}
|
|
2409
3232
|
async function registerConsumer(packageName, consumerPath) {
|
|
@@ -2439,22 +3262,37 @@ async function getConsumers(packageName) {
|
|
|
2439
3262
|
const registry = await readConsumersRegistry();
|
|
2440
3263
|
return registry[packageName] ?? [];
|
|
2441
3264
|
}
|
|
2442
|
-
async function cleanStaleConsumers() {
|
|
3265
|
+
async function cleanStaleConsumers(options = {}) {
|
|
2443
3266
|
const regPath = getConsumersPath();
|
|
2444
3267
|
let removedConsumers = 0;
|
|
2445
3268
|
let removedPackages = 0;
|
|
3269
|
+
let removedMissingLinks = 0;
|
|
3270
|
+
let skippedUnreliableConsumers = 0;
|
|
2446
3271
|
await withFileLock(regPath, async () => {
|
|
2447
3272
|
const registry = await readConsumersRegistry();
|
|
2448
3273
|
const updated = {};
|
|
2449
3274
|
for (const [pkgName, consumers] of Object.entries(registry)) {
|
|
2450
3275
|
const results = await Promise.all(
|
|
2451
|
-
consumers.map(async (consumerPath) =>
|
|
2452
|
-
consumerPath
|
|
2453
|
-
|
|
2454
|
-
|
|
3276
|
+
consumers.map(async (consumerPath) => {
|
|
3277
|
+
if (!await exists(consumerPath)) {
|
|
3278
|
+
return { consumerPath, valid: false, reason: "missing-dir" };
|
|
3279
|
+
}
|
|
3280
|
+
if (options.removeMissingLinks) {
|
|
3281
|
+
const { state, reliable } = await readConsumerStateSafe(consumerPath);
|
|
3282
|
+
if (!reliable) {
|
|
3283
|
+
skippedUnreliableConsumers++;
|
|
3284
|
+
return { consumerPath, valid: true, reason: "unreliable-state" };
|
|
3285
|
+
}
|
|
3286
|
+
if (!state.links[pkgName]) {
|
|
3287
|
+
return { consumerPath, valid: false, reason: "missing-link" };
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
return { consumerPath, valid: true, reason: null };
|
|
3291
|
+
})
|
|
2455
3292
|
);
|
|
2456
3293
|
const validConsumers = results.filter((r) => r.valid).map((r) => r.consumerPath);
|
|
2457
3294
|
removedConsumers += consumers.length - validConsumers.length;
|
|
3295
|
+
removedMissingLinks += results.filter((r) => r.reason === "missing-link").length;
|
|
2458
3296
|
if (validConsumers.length > 0) {
|
|
2459
3297
|
updated[pkgName] = validConsumers;
|
|
2460
3298
|
} else {
|
|
@@ -2463,7 +3301,12 @@ async function cleanStaleConsumers() {
|
|
|
2463
3301
|
}
|
|
2464
3302
|
await writeConsumersRegistry(updated);
|
|
2465
3303
|
});
|
|
2466
|
-
return {
|
|
3304
|
+
return {
|
|
3305
|
+
removedConsumers,
|
|
3306
|
+
removedPackages,
|
|
3307
|
+
removedMissingLinks,
|
|
3308
|
+
skippedUnreliableConsumers
|
|
3309
|
+
};
|
|
2467
3310
|
}
|
|
2468
3311
|
var init_tracker = __esm({
|
|
2469
3312
|
"src/core/tracker.ts"() {
|
|
@@ -2477,13 +3320,13 @@ var init_tracker = __esm({
|
|
|
2477
3320
|
});
|
|
2478
3321
|
|
|
2479
3322
|
// src/utils/build-detect.ts
|
|
2480
|
-
import { readFile as
|
|
2481
|
-
import { join as
|
|
3323
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
3324
|
+
import { join as join15 } from "path";
|
|
2482
3325
|
async function detectBuildCommand(packageDir, pm) {
|
|
2483
|
-
const runPrefix = pm === "npm"
|
|
3326
|
+
const runPrefix = pm === "npm" || pm === "bun" ? `${pm} run ` : `${pm} `;
|
|
2484
3327
|
try {
|
|
2485
3328
|
const pkg = JSON.parse(
|
|
2486
|
-
await
|
|
3329
|
+
await readFile12(join15(packageDir, "package.json"), "utf-8")
|
|
2487
3330
|
);
|
|
2488
3331
|
const scripts = pkg.scripts || {};
|
|
2489
3332
|
for (const name of ["build", "compile", "bundle", "tsc"]) {
|
|
@@ -2502,11 +3345,11 @@ var init_build_detect = __esm({
|
|
|
2502
3345
|
});
|
|
2503
3346
|
|
|
2504
3347
|
// src/utils/config.ts
|
|
2505
|
-
import { readFile as
|
|
2506
|
-
import { join as
|
|
3348
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
3349
|
+
import { join as join16 } from "path";
|
|
2507
3350
|
async function loadKnarrConfig(projectDir) {
|
|
2508
3351
|
try {
|
|
2509
|
-
const raw = await
|
|
3352
|
+
const raw = await readFile13(join16(projectDir, "package.json"), "utf-8");
|
|
2510
3353
|
const pkg = JSON.parse(raw);
|
|
2511
3354
|
const source = pkg.knarr;
|
|
2512
3355
|
if (!source || typeof source !== "object") return {};
|
|
@@ -2569,6 +3412,19 @@ var init_timer = __esm({
|
|
|
2569
3412
|
// src/utils/output.ts
|
|
2570
3413
|
function output(data) {
|
|
2571
3414
|
if (isJsonOutput()) {
|
|
3415
|
+
if (isDryRun()) {
|
|
3416
|
+
markDryRunJsonReportPrinted();
|
|
3417
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
3418
|
+
console.log(JSON.stringify({
|
|
3419
|
+
...data,
|
|
3420
|
+
dryRun: true,
|
|
3421
|
+
mutations: getMutations()
|
|
3422
|
+
}, null, 2));
|
|
3423
|
+
} else {
|
|
3424
|
+
console.log(JSON.stringify({ data, dryRun: true, mutations: getMutations() }, null, 2));
|
|
3425
|
+
}
|
|
3426
|
+
return;
|
|
3427
|
+
}
|
|
2572
3428
|
console.log(JSON.stringify(data, null, 2));
|
|
2573
3429
|
}
|
|
2574
3430
|
}
|
|
@@ -2576,6 +3432,7 @@ var init_output = __esm({
|
|
|
2576
3432
|
"src/utils/output.ts"() {
|
|
2577
3433
|
"use strict";
|
|
2578
3434
|
init_logger();
|
|
3435
|
+
init_dry_run();
|
|
2579
3436
|
}
|
|
2580
3437
|
});
|
|
2581
3438
|
|
|
@@ -2669,8 +3526,8 @@ var init_errors = __esm({
|
|
|
2669
3526
|
});
|
|
2670
3527
|
|
|
2671
3528
|
// src/core/push-engine.ts
|
|
2672
|
-
import { readFile as
|
|
2673
|
-
import { join as
|
|
3529
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
3530
|
+
import { join as join17 } from "path";
|
|
2674
3531
|
async function doPush(packageDir, options = {}) {
|
|
2675
3532
|
const timer = new Timer();
|
|
2676
3533
|
const result = await publish(packageDir, {
|
|
@@ -2679,96 +3536,240 @@ async function doPush(packageDir, options = {}) {
|
|
|
2679
3536
|
historyLimit: options.historyLimit
|
|
2680
3537
|
});
|
|
2681
3538
|
if (result.skipped) {
|
|
2682
|
-
|
|
2683
|
-
|
|
3539
|
+
const entry2 = await getStoreEntry(result.name, result.version);
|
|
3540
|
+
if (entry2) {
|
|
3541
|
+
return pushStoreEntry(entry2, {
|
|
3542
|
+
force: options.force,
|
|
3543
|
+
timer,
|
|
3544
|
+
noConsumersStatus: "available",
|
|
3545
|
+
noChange: true,
|
|
3546
|
+
skippedReason: "content unchanged"
|
|
3547
|
+
});
|
|
3548
|
+
} else {
|
|
3549
|
+
const summary = createEmptySummary(result.name, result.version, result.buildId, timer.elapsedMs());
|
|
3550
|
+
summary.noChange = true;
|
|
3551
|
+
summary.skippedReason = "content unchanged";
|
|
3552
|
+
consola.info(
|
|
3553
|
+
`No changes to push for ${result.name}@${result.version}` + (result.buildId ? ` [${result.buildId}]` : "")
|
|
3554
|
+
);
|
|
3555
|
+
output(summary);
|
|
3556
|
+
return summary;
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
if (isDryRun()) {
|
|
3560
|
+
const consumers = await getConsumers(result.name);
|
|
3561
|
+
const summary = createEmptySummary(result.name, result.version, result.buildId, timer.elapsedMs());
|
|
3562
|
+
summary.skippedReason = "dry-run";
|
|
3563
|
+
summary.consumers = consumers.length;
|
|
3564
|
+
summary.skippedConsumers = consumers.length;
|
|
3565
|
+
summary.consumerResults = consumers.map((consumerPath) => ({
|
|
3566
|
+
consumerPath,
|
|
3567
|
+
status: "skipped",
|
|
3568
|
+
copied: 0,
|
|
3569
|
+
removed: 0,
|
|
3570
|
+
skipped: 0,
|
|
3571
|
+
binLinks: 0,
|
|
3572
|
+
cacheInvalidations: 0,
|
|
3573
|
+
reason: "dry-run publish preview; store entry was not written"
|
|
3574
|
+
}));
|
|
3575
|
+
consola.info(
|
|
3576
|
+
`Dry-run: would publish ${result.name}@${result.version}` + (result.buildId ? ` [${result.buildId}]` : "") + ". Skipping consumer injection preview because the store entry was not written."
|
|
3577
|
+
);
|
|
3578
|
+
output(summary);
|
|
3579
|
+
return summary;
|
|
2684
3580
|
}
|
|
2685
3581
|
const entry = await getStoreEntry(result.name, result.version);
|
|
2686
3582
|
if (!entry) {
|
|
2687
3583
|
errorWithSuggestion(
|
|
2688
3584
|
`Failed to read store entry for ${result.name}@${result.version} after publish`
|
|
2689
3585
|
);
|
|
2690
|
-
|
|
3586
|
+
const summary = createEmptySummary(result.name, result.version, result.buildId, timer.elapsedMs());
|
|
3587
|
+
summary.failedConsumers = 1;
|
|
3588
|
+
summary.consumerResults = [
|
|
3589
|
+
{
|
|
3590
|
+
consumerPath: packageDir,
|
|
3591
|
+
status: "failed",
|
|
3592
|
+
copied: 0,
|
|
3593
|
+
removed: 0,
|
|
3594
|
+
skipped: 0,
|
|
3595
|
+
binLinks: 0,
|
|
3596
|
+
cacheInvalidations: 0,
|
|
3597
|
+
error: "store entry missing after publish"
|
|
3598
|
+
}
|
|
3599
|
+
];
|
|
3600
|
+
return summary;
|
|
2691
3601
|
}
|
|
2692
|
-
|
|
3602
|
+
return pushStoreEntry(entry, {
|
|
3603
|
+
force: options.force,
|
|
3604
|
+
timer,
|
|
3605
|
+
noConsumersStatus: "published"
|
|
3606
|
+
});
|
|
3607
|
+
}
|
|
3608
|
+
async function pushStoreEntry(entry, options = {}) {
|
|
3609
|
+
const timer = options.timer ?? new Timer();
|
|
3610
|
+
const consumers = await getConsumers(entry.name);
|
|
2693
3611
|
if (consumers.length === 0) {
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
);
|
|
3612
|
+
const status = options.noConsumersStatus ?? "available";
|
|
3613
|
+
consola.success(`${entry.name}@${entry.version} ${status} in store`);
|
|
2697
3614
|
consola.info(
|
|
2698
|
-
"No consumers registered yet. Run 'knarr add " +
|
|
3615
|
+
"No consumers registered yet. Run 'knarr add " + entry.name + "' in a consumer project to start receiving pushes."
|
|
2699
3616
|
);
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
buildId
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
return;
|
|
3617
|
+
const summary2 = createEmptySummary(
|
|
3618
|
+
entry.name,
|
|
3619
|
+
entry.version,
|
|
3620
|
+
entry.meta.buildId ?? "",
|
|
3621
|
+
timer.elapsedMs()
|
|
3622
|
+
);
|
|
3623
|
+
summary2.noChange = options.noChange ?? false;
|
|
3624
|
+
summary2.skippedReason = options.skippedReason;
|
|
3625
|
+
if (options.emitOutput !== false) output(summary2);
|
|
3626
|
+
return summary2;
|
|
2711
3627
|
}
|
|
2712
3628
|
let totalCopied = 0;
|
|
3629
|
+
let totalRemoved = 0;
|
|
2713
3630
|
let totalSkipped = 0;
|
|
2714
|
-
let
|
|
3631
|
+
let totalBinLinks = 0;
|
|
3632
|
+
let totalCacheInvalidations = 0;
|
|
3633
|
+
let updatedCount = 0;
|
|
3634
|
+
let skippedCount = 0;
|
|
2715
3635
|
let failedCount = 0;
|
|
2716
3636
|
const results = await Promise.all(
|
|
2717
3637
|
consumers.map(
|
|
2718
3638
|
(consumerPath) => consumerLimit(async () => {
|
|
2719
|
-
const link = await getLink(consumerPath,
|
|
3639
|
+
const link = await getLink(consumerPath, entry.name);
|
|
2720
3640
|
if (!link) {
|
|
2721
3641
|
verbose(
|
|
2722
|
-
`[push] No link found for ${
|
|
3642
|
+
`[push] No link found for ${entry.name} in ${consumerPath}, skipping`
|
|
2723
3643
|
);
|
|
2724
|
-
return
|
|
3644
|
+
return {
|
|
3645
|
+
consumerPath,
|
|
3646
|
+
status: "skipped",
|
|
3647
|
+
copied: 0,
|
|
3648
|
+
removed: 0,
|
|
3649
|
+
skipped: 0,
|
|
3650
|
+
binLinks: 0,
|
|
3651
|
+
cacheInvalidations: 0,
|
|
3652
|
+
reason: "not linked in consumer state"
|
|
3653
|
+
};
|
|
2725
3654
|
}
|
|
2726
3655
|
try {
|
|
3656
|
+
const currentPm = await detectPackageManagerInfo(consumerPath);
|
|
3657
|
+
const packageManager = currentPm.source === "default" ? link.packageManager : currentPm.packageManager;
|
|
2727
3658
|
const injectResult = await inject(
|
|
2728
3659
|
entry,
|
|
2729
3660
|
consumerPath,
|
|
2730
|
-
|
|
3661
|
+
packageManager,
|
|
2731
3662
|
{ force: options.force }
|
|
2732
3663
|
);
|
|
2733
|
-
await addLink(consumerPath,
|
|
3664
|
+
await addLink(consumerPath, entry.name, {
|
|
2734
3665
|
...link,
|
|
2735
3666
|
contentHash: entry.meta.contentHash,
|
|
2736
3667
|
linkedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2737
|
-
buildId: entry.meta.buildId ?? ""
|
|
3668
|
+
buildId: entry.meta.buildId ?? "",
|
|
3669
|
+
packageManager
|
|
2738
3670
|
});
|
|
2739
|
-
return
|
|
3671
|
+
return {
|
|
3672
|
+
consumerPath,
|
|
3673
|
+
status: "updated",
|
|
3674
|
+
copied: injectResult.copied,
|
|
3675
|
+
removed: injectResult.removed,
|
|
3676
|
+
skipped: injectResult.skipped,
|
|
3677
|
+
binLinks: injectResult.binLinks,
|
|
3678
|
+
cacheInvalidations: injectResult.cacheInvalidations
|
|
3679
|
+
};
|
|
2740
3680
|
} catch (err) {
|
|
3681
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
2741
3682
|
consola.warn(
|
|
2742
|
-
`Failed to push to ${consumerPath}: ${
|
|
3683
|
+
`Failed to push to ${consumerPath}: ${message2}`
|
|
2743
3684
|
);
|
|
2744
|
-
return
|
|
3685
|
+
return {
|
|
3686
|
+
consumerPath,
|
|
3687
|
+
status: "failed",
|
|
3688
|
+
copied: 0,
|
|
3689
|
+
removed: 0,
|
|
3690
|
+
skipped: 0,
|
|
3691
|
+
binLinks: 0,
|
|
3692
|
+
cacheInvalidations: 0,
|
|
3693
|
+
error: message2
|
|
3694
|
+
};
|
|
2745
3695
|
}
|
|
2746
3696
|
})
|
|
2747
3697
|
)
|
|
2748
3698
|
);
|
|
2749
3699
|
for (const r of results) {
|
|
2750
|
-
if (r) {
|
|
3700
|
+
if (r.status === "updated") {
|
|
2751
3701
|
totalCopied += r.copied;
|
|
3702
|
+
totalRemoved += r.removed;
|
|
2752
3703
|
totalSkipped += r.skipped;
|
|
2753
|
-
|
|
3704
|
+
totalBinLinks += r.binLinks;
|
|
3705
|
+
totalCacheInvalidations += r.cacheInvalidations;
|
|
3706
|
+
updatedCount++;
|
|
3707
|
+
} else if (r.status === "skipped") {
|
|
3708
|
+
skippedCount++;
|
|
2754
3709
|
} else {
|
|
2755
3710
|
failedCount++;
|
|
2756
3711
|
}
|
|
2757
3712
|
}
|
|
2758
|
-
const
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
3713
|
+
const buildId = entry.meta.buildId ?? "";
|
|
3714
|
+
const buildTag = buildId ? ` [${buildId}]` : "";
|
|
3715
|
+
const detailParts = [
|
|
3716
|
+
`${totalCopied} copied`,
|
|
3717
|
+
`${totalRemoved} removed`,
|
|
3718
|
+
`${totalSkipped} unchanged`
|
|
3719
|
+
];
|
|
3720
|
+
if (totalBinLinks > 0) detailParts.push(`${totalBinLinks} bin links`);
|
|
3721
|
+
if (totalCacheInvalidations > 0) {
|
|
3722
|
+
detailParts.push(`${totalCacheInvalidations} cache invalidations`);
|
|
3723
|
+
}
|
|
3724
|
+
if (skippedCount > 0) detailParts.push(`${skippedCount} consumer(s) skipped`);
|
|
3725
|
+
if (failedCount > 0) detailParts.push(`${failedCount} failed`);
|
|
3726
|
+
const consumerLabel = failedCount > 0 || skippedCount > 0 ? `${updatedCount}/${consumers.length} consumer(s)` : `${updatedCount} consumer(s)`;
|
|
3727
|
+
const message = `Pushed ${entry.name}@${entry.version}${buildTag} to ${consumerLabel} in ${timer.elapsed()} (${detailParts.join(", ")})`;
|
|
3728
|
+
if (failedCount > 0) {
|
|
3729
|
+
consola.warn(message);
|
|
3730
|
+
} else {
|
|
3731
|
+
consola.success(message);
|
|
3732
|
+
}
|
|
3733
|
+
const summary = {
|
|
3734
|
+
name: entry.name,
|
|
3735
|
+
version: entry.version,
|
|
3736
|
+
buildId,
|
|
3737
|
+
noChange: false,
|
|
3738
|
+
consumers: consumers.length,
|
|
3739
|
+
updatedConsumers: updatedCount,
|
|
2767
3740
|
failedConsumers: failedCount,
|
|
3741
|
+
skippedConsumers: skippedCount,
|
|
2768
3742
|
copied: totalCopied,
|
|
3743
|
+
removed: totalRemoved,
|
|
2769
3744
|
skipped: totalSkipped,
|
|
2770
|
-
|
|
2771
|
-
|
|
3745
|
+
binLinks: totalBinLinks,
|
|
3746
|
+
cacheInvalidations: totalCacheInvalidations,
|
|
3747
|
+
elapsed: timer.elapsedMs(),
|
|
3748
|
+
consumerResults: results
|
|
3749
|
+
};
|
|
3750
|
+
summary.noChange = options.noChange ?? false;
|
|
3751
|
+
summary.skippedReason = options.skippedReason;
|
|
3752
|
+
if (options.emitOutput !== false) output(summary);
|
|
3753
|
+
return summary;
|
|
3754
|
+
}
|
|
3755
|
+
function createEmptySummary(name, version, buildId, elapsed) {
|
|
3756
|
+
return {
|
|
3757
|
+
name,
|
|
3758
|
+
version,
|
|
3759
|
+
buildId,
|
|
3760
|
+
noChange: false,
|
|
3761
|
+
consumers: 0,
|
|
3762
|
+
updatedConsumers: 0,
|
|
3763
|
+
failedConsumers: 0,
|
|
3764
|
+
skippedConsumers: 0,
|
|
3765
|
+
copied: 0,
|
|
3766
|
+
removed: 0,
|
|
3767
|
+
skipped: 0,
|
|
3768
|
+
binLinks: 0,
|
|
3769
|
+
cacheInvalidations: 0,
|
|
3770
|
+
elapsed,
|
|
3771
|
+
consumerResults: []
|
|
3772
|
+
};
|
|
2772
3773
|
}
|
|
2773
3774
|
async function resolveWatchConfig(packageDir, args, config) {
|
|
2774
3775
|
let buildCmd = args.build;
|
|
@@ -2786,22 +3787,24 @@ async function resolveWatchConfig(packageDir, args, config) {
|
|
|
2786
3787
|
consola.info(`Auto-detected build command: ${detected}`);
|
|
2787
3788
|
}
|
|
2788
3789
|
}
|
|
2789
|
-
if (buildCmd) {
|
|
3790
|
+
if (buildCmd && !patterns) {
|
|
2790
3791
|
const { exists: exists3 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
2791
3792
|
const candidates = ["src", "lib", "source", "app", "pages", "components"];
|
|
2792
3793
|
const existing = (await Promise.all(
|
|
2793
3794
|
candidates.map(async (dir) => ({
|
|
2794
3795
|
dir,
|
|
2795
|
-
exists: await exists3(
|
|
3796
|
+
exists: await exists3(join17(packageDir, dir))
|
|
2796
3797
|
}))
|
|
2797
3798
|
)).filter((c) => c.exists).map((c) => c.dir);
|
|
2798
3799
|
patterns = existing.length > 0 ? existing : ["src", "lib"];
|
|
2799
3800
|
verbose(`[watch] Using source patterns with build command: ${patterns.join(", ")}`);
|
|
3801
|
+
} else if (buildCmd && patterns) {
|
|
3802
|
+
verbose(`[watch] Using configured watch patterns with build command: ${patterns.join(", ")}`);
|
|
2800
3803
|
} else {
|
|
2801
3804
|
consola.info("No build command detected \u2014 watching output directories directly");
|
|
2802
3805
|
try {
|
|
2803
3806
|
const pkg = JSON.parse(
|
|
2804
|
-
await
|
|
3807
|
+
await readFile14(join17(packageDir, "package.json"), "utf-8")
|
|
2805
3808
|
);
|
|
2806
3809
|
if (pkg.files && pkg.files.length > 0) {
|
|
2807
3810
|
patterns = pkg.files;
|
|
@@ -2920,14 +3923,16 @@ __export(watcher_exports, {
|
|
|
2920
3923
|
startWatcher: () => startWatcher
|
|
2921
3924
|
});
|
|
2922
3925
|
import { spawn as spawn2 } from "child_process";
|
|
2923
|
-
import { readdir as readdir7, stat as
|
|
2924
|
-
import { platform as
|
|
2925
|
-
import { join as
|
|
3926
|
+
import { readdir as readdir7, stat as stat7 } from "fs/promises";
|
|
3927
|
+
import { platform as platform4 } from "os";
|
|
3928
|
+
import { join as join18 } from "path";
|
|
2926
3929
|
function killActiveBuild() {
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
3930
|
+
for (const child of activeChildren) {
|
|
3931
|
+
if (!child.killed) {
|
|
3932
|
+
child.kill("SIGTERM");
|
|
3933
|
+
}
|
|
2930
3934
|
}
|
|
3935
|
+
activeChildren.clear();
|
|
2931
3936
|
}
|
|
2932
3937
|
async function walkDir(dir, snapshot) {
|
|
2933
3938
|
let entries;
|
|
@@ -2938,12 +3943,12 @@ async function walkDir(dir, snapshot) {
|
|
|
2938
3943
|
}
|
|
2939
3944
|
for (const entry of entries) {
|
|
2940
3945
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
2941
|
-
const fullPath =
|
|
3946
|
+
const fullPath = join18(dir, entry.name);
|
|
2942
3947
|
if (entry.isDirectory()) {
|
|
2943
3948
|
await walkDir(fullPath, snapshot);
|
|
2944
3949
|
} else {
|
|
2945
3950
|
try {
|
|
2946
|
-
const st = await
|
|
3951
|
+
const st = await stat7(fullPath);
|
|
2947
3952
|
snapshot.set(fullPath, st.mtimeMs);
|
|
2948
3953
|
} catch {
|
|
2949
3954
|
}
|
|
@@ -2954,7 +3959,7 @@ async function buildSnapshot(watchPaths) {
|
|
|
2954
3959
|
const snapshot = /* @__PURE__ */ new Map();
|
|
2955
3960
|
for (const p of watchPaths) {
|
|
2956
3961
|
try {
|
|
2957
|
-
const st = await
|
|
3962
|
+
const st = await stat7(p);
|
|
2958
3963
|
if (st.isDirectory()) {
|
|
2959
3964
|
await walkDir(p, snapshot);
|
|
2960
3965
|
} else {
|
|
@@ -2977,6 +3982,8 @@ async function startWatcher(watchDir, options, onChange) {
|
|
|
2977
3982
|
let closed = false;
|
|
2978
3983
|
let running = false;
|
|
2979
3984
|
let lastBuildEndTime = 0;
|
|
3985
|
+
let cycle = 0;
|
|
3986
|
+
let lastSuccessfulPush = null;
|
|
2980
3987
|
let hasPendingChanges = false;
|
|
2981
3988
|
const doBuild = async () => {
|
|
2982
3989
|
if (closed || running) return;
|
|
@@ -2992,11 +3999,15 @@ async function startWatcher(watchDir, options, onChange) {
|
|
|
2992
3999
|
}
|
|
2993
4000
|
running = true;
|
|
2994
4001
|
hasPendingChanges = false;
|
|
4002
|
+
cycle++;
|
|
2995
4003
|
try {
|
|
4004
|
+
consola.info(`Change cycle #${cycle} started`);
|
|
2996
4005
|
if (options.buildCmd) {
|
|
2997
4006
|
const success = await runBuildCommand(options.buildCmd, watchDir);
|
|
2998
4007
|
if (!success) {
|
|
2999
|
-
consola.warn(
|
|
4008
|
+
consola.warn(
|
|
4009
|
+
"Build failed (see output above), skipping push" + (lastSuccessfulPush ? `. Last successful push: ${lastSuccessfulPush.toLocaleTimeString()}` : ". No successful push yet.")
|
|
4010
|
+
);
|
|
3000
4011
|
if (options.notify) {
|
|
3001
4012
|
const { ringBell: ringBell2 } = await Promise.resolve().then(() => (init_bell(), bell_exports));
|
|
3002
4013
|
ringBell2(true);
|
|
@@ -3005,6 +4016,7 @@ async function startWatcher(watchDir, options, onChange) {
|
|
|
3005
4016
|
}
|
|
3006
4017
|
}
|
|
3007
4018
|
await onChange();
|
|
4019
|
+
lastSuccessfulPush = /* @__PURE__ */ new Date();
|
|
3008
4020
|
if (options.notify) ringBell(true);
|
|
3009
4021
|
} catch (err) {
|
|
3010
4022
|
consola.error(`Push failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -3110,7 +4122,7 @@ async function startWatcher(watchDir, options, onChange) {
|
|
|
3110
4122
|
}
|
|
3111
4123
|
function runBuildCommand(cmd, cwd) {
|
|
3112
4124
|
return new Promise((resolve8) => {
|
|
3113
|
-
const isWin =
|
|
4125
|
+
const isWin = platform4() === "win32";
|
|
3114
4126
|
const shell = isWin ? "cmd" : "sh";
|
|
3115
4127
|
const shellFlag = isWin ? "/c" : "-c";
|
|
3116
4128
|
consola.start(`Running: ${cmd}`);
|
|
@@ -3118,9 +4130,9 @@ function runBuildCommand(cmd, cwd) {
|
|
|
3118
4130
|
cwd,
|
|
3119
4131
|
stdio: "inherit"
|
|
3120
4132
|
});
|
|
3121
|
-
|
|
4133
|
+
activeChildren.add(child);
|
|
3122
4134
|
child.on("close", (code) => {
|
|
3123
|
-
|
|
4135
|
+
activeChildren.delete(child);
|
|
3124
4136
|
if (code === 0) {
|
|
3125
4137
|
consola.success("Build succeeded");
|
|
3126
4138
|
resolve8(true);
|
|
@@ -3130,35 +4142,39 @@ function runBuildCommand(cmd, cwd) {
|
|
|
3130
4142
|
}
|
|
3131
4143
|
});
|
|
3132
4144
|
child.on("error", (err) => {
|
|
3133
|
-
|
|
4145
|
+
activeChildren.delete(child);
|
|
3134
4146
|
consola.error(`Build error: ${err.message}`);
|
|
3135
4147
|
resolve8(false);
|
|
3136
4148
|
});
|
|
3137
4149
|
});
|
|
3138
4150
|
}
|
|
3139
|
-
var
|
|
4151
|
+
var activeChildren, activeWatcher, IGNORED_DIRS;
|
|
3140
4152
|
var init_watcher = __esm({
|
|
3141
4153
|
"src/core/watcher.ts"() {
|
|
3142
4154
|
"use strict";
|
|
3143
4155
|
init_console();
|
|
3144
4156
|
init_bell();
|
|
3145
|
-
|
|
4157
|
+
activeChildren = /* @__PURE__ */ new Set();
|
|
3146
4158
|
activeWatcher = null;
|
|
3147
4159
|
IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".knarr"]);
|
|
3148
4160
|
}
|
|
3149
4161
|
});
|
|
3150
4162
|
|
|
3151
4163
|
// src/core/watch-orchestrator.ts
|
|
3152
|
-
|
|
4164
|
+
function assertPushSucceeded(summary, packageName) {
|
|
4165
|
+
if (summary.failedConsumers === 0) return;
|
|
4166
|
+
throw new Error(
|
|
4167
|
+
`Push for ${packageName} failed for ${summary.failedConsumers} consumer(s)`
|
|
4168
|
+
);
|
|
4169
|
+
}
|
|
4170
|
+
var WatchOrchestrator;
|
|
3153
4171
|
var init_watch_orchestrator = __esm({
|
|
3154
4172
|
"src/core/watch-orchestrator.ts"() {
|
|
3155
4173
|
"use strict";
|
|
3156
|
-
init_concurrency();
|
|
3157
4174
|
init_console();
|
|
3158
4175
|
init_logger();
|
|
3159
4176
|
init_config();
|
|
3160
4177
|
init_push_engine();
|
|
3161
|
-
cascadeLimit = pLimit(2);
|
|
3162
4178
|
WatchOrchestrator = class {
|
|
3163
4179
|
packages = /* @__PURE__ */ new Map();
|
|
3164
4180
|
dependents = /* @__PURE__ */ new Map();
|
|
@@ -3169,14 +4185,23 @@ var init_watch_orchestrator = __esm({
|
|
|
3169
4185
|
}
|
|
3170
4186
|
async start(startDir, args, pushOptions) {
|
|
3171
4187
|
this.pushOptions = pushOptions;
|
|
3172
|
-
const {
|
|
4188
|
+
const {
|
|
4189
|
+
buildWorkspaceGraph: buildWorkspaceGraph2,
|
|
4190
|
+
buildReverseAdjacency: buildReverseAdjacency2,
|
|
4191
|
+
filterPublishableWorkspaceGraph: filterPublishableWorkspaceGraph2
|
|
4192
|
+
} = await Promise.resolve().then(() => (init_workspace(), workspace_exports));
|
|
3173
4193
|
const { topoSort: topoSort2, CycleError: CycleError2 } = await Promise.resolve().then(() => (init_topo_sort(), topo_sort_exports));
|
|
3174
4194
|
const { startWatcher: startWatcher2 } = await Promise.resolve().then(() => (init_watcher(), watcher_exports));
|
|
3175
|
-
const
|
|
4195
|
+
const discovered = await buildWorkspaceGraph2(startDir);
|
|
4196
|
+
const graph = filterPublishableWorkspaceGraph2(discovered);
|
|
3176
4197
|
if (graph.packages.length === 0) {
|
|
3177
|
-
consola.warn("No workspace packages found");
|
|
4198
|
+
consola.warn("No publishable workspace packages found");
|
|
3178
4199
|
return;
|
|
3179
4200
|
}
|
|
4201
|
+
const privateCount = discovered.packages.length - graph.packages.length;
|
|
4202
|
+
if (privateCount > 0) {
|
|
4203
|
+
consola.info(`Skipping ${privateCount} private workspace package(s)`);
|
|
4204
|
+
}
|
|
3180
4205
|
let ordered;
|
|
3181
4206
|
try {
|
|
3182
4207
|
ordered = topoSort2(graph.adjacency);
|
|
@@ -3199,14 +4224,25 @@ var init_watch_orchestrator = __esm({
|
|
|
3199
4224
|
const { buildCmd, patterns } = await resolveWatchConfig(dir, args, config);
|
|
3200
4225
|
const notify = args.notify ?? config.notify ?? false;
|
|
3201
4226
|
const wrappedOnChange = async () => {
|
|
3202
|
-
await
|
|
3203
|
-
|
|
4227
|
+
await this.runExclusive(name, async () => {
|
|
4228
|
+
const summary = await doPush(dir, pushOptions);
|
|
4229
|
+
assertPushSucceeded(summary, name);
|
|
4230
|
+
await this.onPackagePushed(name);
|
|
4231
|
+
});
|
|
3204
4232
|
};
|
|
3205
4233
|
const parseMs = (v) => {
|
|
3206
4234
|
if (!v) return void 0;
|
|
3207
4235
|
const n = parseInt(v, 10);
|
|
3208
4236
|
return Number.isFinite(n) ? n : void 0;
|
|
3209
4237
|
};
|
|
4238
|
+
const entry = {
|
|
4239
|
+
dir,
|
|
4240
|
+
buildCmd,
|
|
4241
|
+
state: "idle",
|
|
4242
|
+
watcher: { close: async () => {
|
|
4243
|
+
} }
|
|
4244
|
+
};
|
|
4245
|
+
this.packages.set(name, entry);
|
|
3210
4246
|
const watcher = await startWatcher2(
|
|
3211
4247
|
dir,
|
|
3212
4248
|
{
|
|
@@ -3218,7 +4254,7 @@ var init_watch_orchestrator = __esm({
|
|
|
3218
4254
|
},
|
|
3219
4255
|
wrappedOnChange
|
|
3220
4256
|
);
|
|
3221
|
-
|
|
4257
|
+
entry.watcher = watcher;
|
|
3222
4258
|
}
|
|
3223
4259
|
consola.info(`Watching ${this.packages.size} workspace packages`);
|
|
3224
4260
|
await new Promise((resolve8) => {
|
|
@@ -3236,12 +4272,11 @@ var init_watch_orchestrator = __esm({
|
|
|
3236
4272
|
const deps = this.dependents.get(name);
|
|
3237
4273
|
if (!deps || deps.size === 0) return;
|
|
3238
4274
|
verbose(`[cascade] ${name} pushed, triggering dependents: ${[...deps].join(", ")}`);
|
|
3239
|
-
const
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
await Promise.all(tasks);
|
|
4275
|
+
for (const depName of deps) {
|
|
4276
|
+
await this.requestRebuild(depName);
|
|
4277
|
+
}
|
|
3243
4278
|
}
|
|
3244
|
-
async
|
|
4279
|
+
async runExclusive(name, task) {
|
|
3245
4280
|
const entry = this.packages.get(name);
|
|
3246
4281
|
if (!entry) return;
|
|
3247
4282
|
if (entry.state === "queued") {
|
|
@@ -3254,30 +4289,35 @@ var init_watch_orchestrator = __esm({
|
|
|
3254
4289
|
return;
|
|
3255
4290
|
}
|
|
3256
4291
|
entry.state = "building";
|
|
3257
|
-
verbose(`[cascade] Rebuilding ${name}`);
|
|
3258
4292
|
try {
|
|
4293
|
+
await task(entry);
|
|
4294
|
+
} finally {
|
|
4295
|
+
const wasQueued = entry.state === "queued";
|
|
4296
|
+
entry.state = "idle";
|
|
4297
|
+
if (wasQueued) {
|
|
4298
|
+
await this.requestRebuild(name);
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
async requestRebuild(name) {
|
|
4303
|
+
await this.runExclusive(name, async (entry) => {
|
|
4304
|
+
verbose(`[cascade] Rebuilding ${name}`);
|
|
3259
4305
|
if (entry.buildCmd) {
|
|
3260
4306
|
const { runBuildCommand: runBuildCommand2 } = await Promise.resolve().then(() => (init_watcher(), watcher_exports));
|
|
3261
4307
|
const success = await runBuildCommand2(entry.buildCmd, entry.dir);
|
|
3262
4308
|
if (!success) {
|
|
3263
4309
|
consola.warn(`[cascade] Build failed for ${name}, skipping dependents`);
|
|
3264
|
-
entry.state = "idle";
|
|
3265
4310
|
return;
|
|
3266
4311
|
}
|
|
3267
4312
|
}
|
|
3268
|
-
await doPush(entry.dir, this.pushOptions);
|
|
4313
|
+
const summary = await doPush(entry.dir, this.pushOptions);
|
|
4314
|
+
assertPushSucceeded(summary, name);
|
|
3269
4315
|
await this.onPackagePushed(name);
|
|
3270
|
-
}
|
|
4316
|
+
}).catch((err) => {
|
|
3271
4317
|
consola.warn(
|
|
3272
4318
|
`[cascade] Push failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
|
|
3273
4319
|
);
|
|
3274
|
-
}
|
|
3275
|
-
const wasQueued = entry.state === "queued";
|
|
3276
|
-
entry.state = "idle";
|
|
3277
|
-
if (wasQueued) {
|
|
3278
|
-
await this.requestRebuild(name);
|
|
3279
|
-
}
|
|
3280
|
-
}
|
|
4320
|
+
});
|
|
3281
4321
|
}
|
|
3282
4322
|
async close() {
|
|
3283
4323
|
await Promise.all(
|
|
@@ -3305,42 +4345,82 @@ init_timer();
|
|
|
3305
4345
|
init_logger();
|
|
3306
4346
|
async function doPushAll(startDir, options = {}) {
|
|
3307
4347
|
const timer = new Timer();
|
|
3308
|
-
const
|
|
4348
|
+
const discovered = await buildWorkspaceGraph(startDir);
|
|
4349
|
+
const graph = filterPublishableWorkspaceGraph(discovered);
|
|
3309
4350
|
if (graph.packages.length === 0) {
|
|
3310
|
-
consola.warn("No workspace packages found");
|
|
4351
|
+
consola.warn("No publishable workspace packages found");
|
|
3311
4352
|
return;
|
|
3312
4353
|
}
|
|
4354
|
+
const privateCount = discovered.packages.length - graph.packages.length;
|
|
4355
|
+
if (privateCount > 0) {
|
|
4356
|
+
consola.info(`Skipping ${privateCount} private workspace package(s)`);
|
|
4357
|
+
}
|
|
3313
4358
|
let ordered;
|
|
3314
4359
|
try {
|
|
3315
4360
|
ordered = topoSort(graph.adjacency);
|
|
3316
4361
|
} catch (err) {
|
|
3317
4362
|
if (err instanceof CycleError) {
|
|
3318
4363
|
consola.error(`Cannot push: ${err.message}`);
|
|
3319
|
-
|
|
4364
|
+
throw err;
|
|
3320
4365
|
}
|
|
3321
4366
|
throw err;
|
|
3322
4367
|
}
|
|
3323
4368
|
const nameToDir = new Map(graph.packages.map((p) => [p.name, p.dir]));
|
|
3324
4369
|
consola.info(`Pushing ${ordered.length} packages in dependency order`);
|
|
3325
4370
|
verbose(`[batch-push] Order: ${ordered.join(" \u2192 ")}`);
|
|
4371
|
+
const reverseAdjacency = buildReverseAdjacency(graph.adjacency);
|
|
4372
|
+
const blocked = new Set(options.skipPackages ?? []);
|
|
4373
|
+
for (const name of [...blocked]) {
|
|
4374
|
+
markTransitiveDependentsBlocked(name, reverseAdjacency, blocked);
|
|
4375
|
+
}
|
|
3326
4376
|
let success = 0;
|
|
3327
4377
|
let failed = 0;
|
|
4378
|
+
let skipped = 0;
|
|
3328
4379
|
for (const name of ordered) {
|
|
3329
4380
|
const dir = nameToDir.get(name);
|
|
3330
4381
|
if (!dir) continue;
|
|
4382
|
+
if (blocked.has(name)) {
|
|
4383
|
+
verbose(`[batch-push] Skipping ${name}: dependency build/push failed`);
|
|
4384
|
+
skipped++;
|
|
4385
|
+
continue;
|
|
4386
|
+
}
|
|
3331
4387
|
try {
|
|
3332
|
-
await doPush(dir, options);
|
|
3333
|
-
|
|
4388
|
+
const summary = await doPush(dir, options);
|
|
4389
|
+
if (summary.failedConsumers > 0) {
|
|
4390
|
+
failed++;
|
|
4391
|
+
markTransitiveDependentsBlocked(name, reverseAdjacency, blocked);
|
|
4392
|
+
} else {
|
|
4393
|
+
success++;
|
|
4394
|
+
}
|
|
3334
4395
|
} catch (err) {
|
|
3335
4396
|
consola.warn(
|
|
3336
4397
|
`Failed to push ${name}: ${err instanceof Error ? err.message : String(err)}`
|
|
3337
4398
|
);
|
|
3338
4399
|
failed++;
|
|
4400
|
+
markTransitiveDependentsBlocked(name, reverseAdjacency, blocked);
|
|
3339
4401
|
}
|
|
3340
4402
|
}
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
4403
|
+
const details = [
|
|
4404
|
+
failed > 0 ? `${failed} failed` : null,
|
|
4405
|
+
skipped > 0 ? `${skipped} skipped` : null
|
|
4406
|
+
].filter((part) => part !== null);
|
|
4407
|
+
const summaryMessage = `Pushed ${success}/${ordered.length} packages in ${timer.elapsed()}` + (details.length > 0 ? ` (${details.join(", ")})` : "");
|
|
4408
|
+
if (failed > 0) {
|
|
4409
|
+
consola.warn(summaryMessage);
|
|
4410
|
+
throw new Error(`Failed to push ${failed} workspace package(s)`);
|
|
4411
|
+
}
|
|
4412
|
+
if (skipped > 0) {
|
|
4413
|
+
consola.warn(summaryMessage);
|
|
4414
|
+
return;
|
|
4415
|
+
}
|
|
4416
|
+
consola.success(summaryMessage);
|
|
4417
|
+
}
|
|
4418
|
+
function markTransitiveDependentsBlocked(name, reverseAdjacency, blocked) {
|
|
4419
|
+
for (const dependent of reverseAdjacency.get(name) ?? []) {
|
|
4420
|
+
if (blocked.has(dependent)) continue;
|
|
4421
|
+
blocked.add(dependent);
|
|
4422
|
+
markTransitiveDependentsBlocked(dependent, reverseAdjacency, blocked);
|
|
4423
|
+
}
|
|
3344
4424
|
}
|
|
3345
4425
|
|
|
3346
4426
|
// src/index.ts
|
|
@@ -3354,13 +4434,13 @@ init_workspace();
|
|
|
3354
4434
|
|
|
3355
4435
|
// src/utils/preflight.ts
|
|
3356
4436
|
init_logger();
|
|
3357
|
-
import { readFile as
|
|
3358
|
-
import { join as
|
|
4437
|
+
import { readFile as readFile15, stat as stat8 } from "fs/promises";
|
|
4438
|
+
import { join as join19, resolve as resolve7 } from "path";
|
|
3359
4439
|
async function runPreflightChecks(packageDir) {
|
|
3360
4440
|
const issues = [];
|
|
3361
4441
|
let pkgContent;
|
|
3362
4442
|
try {
|
|
3363
|
-
pkgContent = await
|
|
4443
|
+
pkgContent = await readFile15(join19(packageDir, "package.json"), "utf-8");
|
|
3364
4444
|
} catch {
|
|
3365
4445
|
issues.push({
|
|
3366
4446
|
code: "NO_PACKAGE_JSON",
|
|
@@ -3418,7 +4498,7 @@ async function runPreflightChecks(packageDir) {
|
|
|
3418
4498
|
}
|
|
3419
4499
|
async function fileExists(filePath) {
|
|
3420
4500
|
try {
|
|
3421
|
-
const s = await
|
|
4501
|
+
const s = await stat8(filePath);
|
|
3422
4502
|
return s.isFile();
|
|
3423
4503
|
} catch {
|
|
3424
4504
|
return false;
|
|
@@ -3506,7 +4586,7 @@ init_paths();
|
|
|
3506
4586
|
init_dry_run();
|
|
3507
4587
|
|
|
3508
4588
|
// src/utils/vite-config.ts
|
|
3509
|
-
import { readFile as
|
|
4589
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
3510
4590
|
function defineConfigUsesTernary(content) {
|
|
3511
4591
|
const callRegex = /(^|[^A-Za-z0-9_$])defineConfig\s*\(/g;
|
|
3512
4592
|
let match;
|
|
@@ -3592,6 +4672,7 @@ export {
|
|
|
3592
4672
|
cleanStaleConsumers,
|
|
3593
4673
|
clearHistory,
|
|
3594
4674
|
detectPackageManager,
|
|
4675
|
+
detectPackageManagerInfo,
|
|
3595
4676
|
doPush,
|
|
3596
4677
|
doPushAll,
|
|
3597
4678
|
findStoreEntry,
|