dsh-plugin-pack 1.0.19 → 1.0.20
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/lib/lockfile.js +71 -31
- package/package.json +1 -1
package/lib/lockfile.js
CHANGED
|
@@ -22,25 +22,69 @@ import { spawnSync } from 'node:child_process';
|
|
|
22
22
|
import fs from 'node:fs';
|
|
23
23
|
import path from 'node:path';
|
|
24
24
|
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* 找 pnpm —— 优先用 dsh 自带的那个。
|
|
27
|
+
*
|
|
28
|
+
* 为什么不能随便用 PATH 上的:dsh 内置 pnpm 11(store/v11),
|
|
29
|
+
* 用户命令行可能是 pnpm 10(store/v10)。store 目录不同、锁文件格式也可能不同,
|
|
30
|
+
* 用错版本去同步等于又把 dsh 的安装/卸载搞坏。
|
|
31
|
+
*
|
|
32
|
+
* 探测顺序:DSH_PNPM 环境变量 → dsh 运行时里的 pnpm.cjs → PATH 上的 pnpm。
|
|
33
|
+
*
|
|
34
|
+
* @returns {{ command: string, args: string[], version: string|null, bundled: boolean } | null}
|
|
35
|
+
*/
|
|
26
36
|
function findPnpm() {
|
|
27
|
-
const candidates =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
const candidates = [];
|
|
38
|
+
|
|
39
|
+
if (process.env.DSH_PNPM && fs.existsSync(process.env.DSH_PNPM)) {
|
|
40
|
+
candidates.push({ command: process.execPath, args: [process.env.DSH_PNPM], bundled: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// dsh 的 node 通常在 <runtime>/node.exe,pnpm 在 <runtime>/dependencies/pnpm/bin/pnpm.cjs
|
|
44
|
+
const exeDir = path.dirname(process.execPath);
|
|
45
|
+
for (const rel of [
|
|
46
|
+
['dependencies', 'pnpm', 'bin', 'pnpm.cjs'],
|
|
47
|
+
['..', 'dependencies', 'pnpm', 'bin', 'pnpm.cjs'],
|
|
48
|
+
['runtime', 'primary-runtime', 'dependencies', 'pnpm', 'bin', 'pnpm.cjs'],
|
|
49
|
+
]) {
|
|
50
|
+
const candidate = path.join(exeDir, ...rel);
|
|
51
|
+
if (fs.existsSync(candidate)) {
|
|
52
|
+
candidates.push({ command: process.execPath, args: [candidate], bundled: true });
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 兜底:PATH 上的 pnpm
|
|
58
|
+
const names = process.platform === 'win32' ? ['pnpm.cmd', 'pnpm.exe', 'pnpm'] : ['pnpm'];
|
|
59
|
+
for (const name of names) candidates.push({ command: name, args: [], bundled: false });
|
|
31
60
|
|
|
32
|
-
for (const
|
|
33
|
-
const result = spawnSync(
|
|
61
|
+
for (const candidate of candidates) {
|
|
62
|
+
const result = spawnSync(candidate.command, [...candidate.args, '--version'], {
|
|
34
63
|
encoding: 'utf8',
|
|
35
|
-
|
|
36
|
-
|
|
64
|
+
// 自带 pnpm 是 node 直接跑 .cjs,不需要 shell;PATH 上的需要
|
|
65
|
+
shell: process.platform === 'win32' && !candidate.bundled,
|
|
66
|
+
timeout: 20000,
|
|
37
67
|
windowsHide: true,
|
|
38
68
|
});
|
|
39
|
-
if (!result.error && result.status === 0)
|
|
69
|
+
if (!result.error && result.status === 0) {
|
|
70
|
+
const version = String(result.stdout ?? '').trim().split('\n')[0] || null;
|
|
71
|
+
return { ...candidate, version };
|
|
72
|
+
}
|
|
40
73
|
}
|
|
41
74
|
return null;
|
|
42
75
|
}
|
|
43
76
|
|
|
77
|
+
/** 用找到的 pnpm 跑一条命令。 */
|
|
78
|
+
function runPnpm(pnpm, args, cwd, timeoutMs) {
|
|
79
|
+
return spawnSync(pnpm.command, [...pnpm.args, ...args], {
|
|
80
|
+
cwd,
|
|
81
|
+
encoding: 'utf8',
|
|
82
|
+
shell: process.platform === 'win32' && !pnpm.bundled,
|
|
83
|
+
timeout: timeoutMs,
|
|
84
|
+
windowsHide: true,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
44
88
|
/**
|
|
45
89
|
* 让 `pnpm-lock.yaml` 与 `package.json` 对齐。
|
|
46
90
|
*
|
|
@@ -68,23 +112,21 @@ export function syncLockfile(profileDir, options = {}) {
|
|
|
68
112
|
const pnpm = findPnpm();
|
|
69
113
|
if (!pnpm) return { ok: false, skipped: true, reason: '没找到 pnpm' };
|
|
70
114
|
|
|
71
|
-
const result =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
env: { ...process.env, CI: '' },
|
|
78
|
-
});
|
|
115
|
+
const result = runPnpm(
|
|
116
|
+
pnpm,
|
|
117
|
+
['install', '--lockfile-only', '--config.confirmModulesPurge=false'],
|
|
118
|
+
profile,
|
|
119
|
+
options.timeoutMs ?? 180000,
|
|
120
|
+
);
|
|
79
121
|
|
|
80
122
|
if (result.error) {
|
|
81
|
-
return { ok: false, error: String(result.error.message ?? result.error) };
|
|
123
|
+
return { ok: false, error: String(result.error.message ?? result.error), pnpm: pnpm.version };
|
|
82
124
|
}
|
|
83
125
|
if (result.status !== 0) {
|
|
84
126
|
const detail = `${result.stdout ?? ''}\n${result.stderr ?? ''}`.trim();
|
|
85
|
-
return { ok: false, error: detail.slice(0, 400) || `pnpm 退出码 ${result.status}
|
|
127
|
+
return { ok: false, error: detail.slice(0, 400) || `pnpm 退出码 ${result.status}`, pnpm: pnpm.version };
|
|
86
128
|
}
|
|
87
|
-
return { ok: true };
|
|
129
|
+
return { ok: true, pnpm: pnpm.version };
|
|
88
130
|
} catch (error) {
|
|
89
131
|
return { ok: false, error: String(error?.message ?? error) };
|
|
90
132
|
}
|
|
@@ -108,19 +150,17 @@ export function checkLockfile(profileDir) {
|
|
|
108
150
|
const pnpm = findPnpm();
|
|
109
151
|
if (!pnpm) return { ok: false, skipped: true, reason: '没找到 pnpm' };
|
|
110
152
|
|
|
111
|
-
const result =
|
|
112
|
-
cwd: profile,
|
|
113
|
-
encoding: 'utf8',
|
|
114
|
-
shell: process.platform === 'win32',
|
|
115
|
-
timeout: 120000,
|
|
116
|
-
windowsHide: true,
|
|
117
|
-
});
|
|
153
|
+
const result = runPnpm(pnpm, ['install', '--frozen-lockfile', '--lockfile-only'], profile, 180000);
|
|
118
154
|
// 注意:这里不能用 --reporter=silent —— 它会把 ERR_PNPM_OUTDATED_LOCKFILE
|
|
119
155
|
// 这段错误文本一起吞掉,导致分不清"锁文件不一致"和"其它失败"。
|
|
120
156
|
const detail = `${result.stdout ?? ''}\n${result.stderr ?? ''}`;
|
|
121
|
-
if (/ERR_PNPM_OUTDATED_LOCKFILE/.test(detail)) return { ok: true, outdated: true };
|
|
122
|
-
if (result.status === 0) return { ok: true, outdated: false };
|
|
123
|
-
|
|
157
|
+
if (/ERR_PNPM_OUTDATED_LOCKFILE/.test(detail)) return { ok: true, outdated: true, pnpm: pnpm.version };
|
|
158
|
+
if (result.status === 0) return { ok: true, outdated: false, pnpm: pnpm.version };
|
|
159
|
+
// 其它错误里也可能带 store 不一致
|
|
160
|
+
if (/ERR_PNPM_UNEXPECTED_STORE/.test(detail)) {
|
|
161
|
+
return { ok: false, storeMismatch: true, pnpm: pnpm.version, error: detail.trim().slice(0, 400) };
|
|
162
|
+
}
|
|
163
|
+
return { ok: false, error: detail.trim().slice(0, 400) || `pnpm 退出码 ${result.status}`, pnpm: pnpm.version };
|
|
124
164
|
} catch (error) {
|
|
125
165
|
return { ok: false, error: String(error?.message ?? error) };
|
|
126
166
|
}
|
package/package.json
CHANGED