dsh-code-server-app 0.1.43 → 0.2.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.en.md +113 -106
- package/README.md +125 -92
- package/cordis.patch.yml +19 -15
- package/lib/client.js +1 -3
- package/lib/index.js +247 -144
- package/lib/launcher.mjs +313 -0
- package/lib/serve-dsh.mjs +162 -0
- package/lib/vendor.js +94 -33
- package/package.json +11 -7
- package/scripts/vendor-repacks.mjs +72 -99
- package/scripts/vendor-vscode-server.mjs +278 -0
- package/vendor/VENDOR.json +5 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-code-server-app",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "VS Code (from a code-server release) inside DSH Web and Desktop: right-sidebar tab with a floating-ball fallback, driven by the plugin's own launcher over the in-process VS Code server (lib/launcher.mjs). Two serving modes: loopback port (default) or same-origin mount on DSH's own webServer (/code-server, protected by ctx.connection.requestRejection). No code-server Node layer, no argon2, no C++ toolchain.",
|
|
5
5
|
"homepage": "https://github.com/jinsiyu/dsh-code-server-app",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -40,11 +40,14 @@
|
|
|
40
40
|
"files": [
|
|
41
41
|
"lib/index.js",
|
|
42
42
|
"lib/client.js",
|
|
43
|
+
"lib/launcher.mjs",
|
|
44
|
+
"lib/serve-dsh.mjs",
|
|
43
45
|
"lib/vendor.js",
|
|
44
46
|
"lib/native.js",
|
|
45
47
|
"assets/favicon.ico",
|
|
46
48
|
"assets/favicon.svg",
|
|
47
49
|
"assets/extensions/dshcs-open-file",
|
|
50
|
+
"scripts/vendor-vscode-server.mjs",
|
|
48
51
|
"scripts/vendor-code-server.mjs",
|
|
49
52
|
"scripts/vendor-repacks.mjs",
|
|
50
53
|
"vendor/VENDOR.json",
|
|
@@ -59,7 +62,7 @@
|
|
|
59
62
|
},
|
|
60
63
|
"license": "MIT",
|
|
61
64
|
"dependencies": {
|
|
62
|
-
"@jinsiyu/dshcs-
|
|
65
|
+
"@jinsiyu/dshcs-vscode-server": "4.136.2",
|
|
63
66
|
"@microsoft/1ds-core-js": "3.2.13",
|
|
64
67
|
"@microsoft/1ds-post-js": "3.2.13",
|
|
65
68
|
"@vscode/iconv-lite-umd": "0.7.1",
|
|
@@ -97,13 +100,14 @@
|
|
|
97
100
|
"zod": "4.4.3"
|
|
98
101
|
},
|
|
99
102
|
"optionalDependencies": {
|
|
100
|
-
"@jinsiyu/dsh-code-server-runtime-win32-arm64": "^0.
|
|
101
|
-
"@jinsiyu/dsh-code-server-runtime-win32-x64": "^0.
|
|
103
|
+
"@jinsiyu/dsh-code-server-runtime-win32-arm64": "^0.2.0",
|
|
104
|
+
"@jinsiyu/dsh-code-server-runtime-win32-x64": "^0.2.0"
|
|
102
105
|
},
|
|
103
106
|
"scripts": {
|
|
107
|
+
"vendor:vscode": "node scripts/vendor-vscode-server.mjs",
|
|
108
|
+
"vendor:latest": "node scripts/vendor-vscode-server.mjs --force",
|
|
109
|
+
"vendor:check": "node scripts/vendor-vscode-server.mjs --check",
|
|
104
110
|
"vendor:code-server": "node scripts/vendor-code-server.mjs",
|
|
105
|
-
"vendor:latest": "node scripts/vendor-code-server.mjs --force",
|
|
106
|
-
"vendor:check": "node scripts/vendor-code-server.mjs --check",
|
|
107
111
|
"repack:build": "node scripts/vendor-repacks.mjs",
|
|
108
112
|
"publish:repacks": "node scripts/publish-repacks.mjs",
|
|
109
113
|
"publish:plugin": "node scripts/publish-plugin.mjs",
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
// scripts/vendor-repacks.mjs —
|
|
1
|
+
// scripts/vendor-repacks.mjs — 打包期把「VS Code 树」与「pnpm 拒绝安装」的原生包全部打成可安装的预编译包。
|
|
2
2
|
//
|
|
3
3
|
// 为什么:pnpm 认为「含安装脚本」或「含 binding.gyp / .hooks」的包需要构建,必须由宿主
|
|
4
|
-
// pnpm-workspace.yaml 的 allowBuilds 批准,否则 dsh plugin add 直接 exit 1。
|
|
5
|
-
//
|
|
4
|
+
// pnpm-workspace.yaml 的 allowBuilds 批准,否则 dsh plugin add 直接 exit 1。VS Code 内部依赖里
|
|
5
|
+
// 的一批原生包都属于这一类;树本身则因为 npm 打包永远排除**包根** node_modules 而必须重打包。
|
|
6
6
|
//
|
|
7
7
|
// 产物(全部由本脚本统一生成,再由 scripts/publish-repacks.mjs 统一发布):
|
|
8
|
-
// ① @<scope>/dshcs-
|
|
9
|
-
// (
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// ③ @<scope>/dshcs-<名字>[-<platform>-<arch>]@<版本> VS Code 内部依赖里需要构建的包
|
|
8
|
+
// ① @<scope>/dshcs-vscode-server@<code-server 版本> 平台无关:vendor/vscode 的树
|
|
9
|
+
// (lib/vscode + out/browser + src/browser);**不含** code-server 的 out/node 与其 136 个依赖
|
|
10
|
+
// —— 那层由插件自带的 lib/launcher.mjs 取代(见 docs/analysis-code-server-as-dsh-plugin.md);
|
|
11
|
+
// ② @<scope>/dshcs-<名字>[-<platform>-<arch>]@<版本> VS Code 内部依赖里需要构建的包
|
|
13
12
|
// (node-pty / @vscode/sqlite3 / kerberos / koffi / ssh2 / …),
|
|
14
13
|
// 依赖链上命中它们的包也一并重打包(含 optionalDependencies,如 @vscode/proxy-agent → windows-ca-certs);
|
|
15
|
-
//
|
|
16
|
-
// dependencies 用 npm: 别名把
|
|
17
|
-
// 这样
|
|
14
|
+
// ③ @<scope>/dsh-code-server-runtime-<platform>-<arch>@<插件版本> 平台聚合包:
|
|
15
|
+
// dependencies 用 npm: 别名把 ② 装回**原始名字**(node-pty / @vscode/sqlite3 / …),
|
|
16
|
+
// 这样 VS Code 的 require/import 不用改,插件只需按 os/cpu 声明 ①(常规依赖)+ ③(可选依赖)。
|
|
17
|
+
// (argon2 已随 code-server 服务层一起移除:auth=none 不再需要口令哈希。)
|
|
18
18
|
//
|
|
19
19
|
// 结果:pnpm install 不再遇到任何带构建信号的包 → 无需 allowBuilds、无需「安装环境」步骤。
|
|
20
20
|
//
|
|
@@ -50,7 +50,7 @@ const FROM = argValue('--from');
|
|
|
50
50
|
const SCOPE = argValue('--scope') ?? '@jinsiyu';
|
|
51
51
|
const DO_PACK = process.argv.includes('--pack');
|
|
52
52
|
// --reuse:复用 repack/build 里已有的原生包(不重新 analyze/交叉编译),只重建
|
|
53
|
-
//
|
|
53
|
+
// VS Code 树包、平台聚合包与插件 package.json。
|
|
54
54
|
// 适用于「原生包没变、只调整打包结构」的场景(本机已无可分析的完整源树时也用它)。
|
|
55
55
|
const REUSE = process.argv.includes('--reuse');
|
|
56
56
|
const TARGETS = argValues('--target');
|
|
@@ -277,12 +277,13 @@ function prepareCrossTree(specs, target) {
|
|
|
277
277
|
* 1) npm install code-server@<内置版本> --ignore-scripts(只解包);
|
|
278
278
|
* 2) 在 lib/vscode 与 lib/vscode/extensions 里 npm install --ignore-scripts(解包内部依赖,约 1GB);
|
|
279
279
|
* 3) 缺 Spectre 库时先降级 gyp,再 npm rebuild(编译本机原生包)。
|
|
280
|
+
* 注意:只有 lib/vscode 的依赖会被 analyze 用到 —— code-server 自己的 136 个依赖已随服务层移除。
|
|
280
281
|
* @returns {string} 源树根 */
|
|
281
282
|
function prepareSourceTree() {
|
|
282
|
-
const vendorVersion = readJson(join(pkgRoot, 'vendor', '
|
|
283
|
+
const vendorVersion = readJson(join(pkgRoot, 'vendor', 'vscode', 'package.json'))?.version
|
|
283
284
|
?? readJson(join(pkgRoot, 'vendor', 'code-server', 'package.json'))?.version;
|
|
284
285
|
if (typeof vendorVersion !== 'string' || vendorVersion === '') {
|
|
285
|
-
throw new Error('缺少 vendor/
|
|
286
|
+
throw new Error('缺少 vendor/vscode;先运行 `node scripts/vendor-vscode-server.mjs`');
|
|
286
287
|
}
|
|
287
288
|
const tmp = join(pkgRoot, '.vendor-tmp', `repack-src-${process.pid}`);
|
|
288
289
|
rmSync(tmp, { recursive: true, force: true });
|
|
@@ -325,86 +326,48 @@ function peMachine(file) {
|
|
|
325
326
|
} catch { return null; }
|
|
326
327
|
}
|
|
327
328
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
function buildCodeServerPackage() {
|
|
337
|
-
const vendorTree = join(pkgRoot, 'vendor', 'code-server');
|
|
338
|
-
if (!existsSync(join(vendorTree, 'out', 'node', 'entry.js'))) {
|
|
339
|
-
throw new Error('缺少 vendor/code-server;先运行 `node scripts/vendor-code-server.mjs`');
|
|
329
|
+
/** ① VS Code 树(平台无关):vendor/vscode → repack/build/vscode/vscode/。
|
|
330
|
+
* 排除 VS Code 的「内部依赖目录」(lib/vscode/node_modules、lib/vscode/extensions/node_modules)
|
|
331
|
+
* —— 它们由包管理器安装 + 平台聚合包按架构提供,树里只留 lib/vscode 本体与静态资源。
|
|
332
|
+
* 不含 code-server 的 out/node 与服务层依赖(已由 lib/launcher.mjs 取代)。 */
|
|
333
|
+
function buildVscodeServerPackage() {
|
|
334
|
+
const vendorTree = join(pkgRoot, 'vendor', 'vscode');
|
|
335
|
+
if (!existsSync(join(vendorTree, 'lib', 'vscode', 'out', 'server-main.js'))) {
|
|
336
|
+
throw new Error('缺少 vendor/vscode;先运行 `node scripts/vendor-vscode-server.mjs`');
|
|
340
337
|
}
|
|
341
338
|
const version = readJson(join(vendorTree, 'package.json'))?.version;
|
|
342
|
-
if (typeof version !== 'string') throw new Error('
|
|
343
|
-
const dir = join(OUT, 'build', '
|
|
339
|
+
if (typeof version !== 'string') throw new Error('读不到树版本(vendor/vscode/package.json)');
|
|
340
|
+
const dir = join(OUT, 'build', 'vscode');
|
|
344
341
|
rmSync(dir, { recursive: true, force: true });
|
|
345
342
|
mkdirSync(dir, { recursive: true });
|
|
346
|
-
|
|
347
|
-
|
|
343
|
+
const inner = [
|
|
344
|
+
join('lib', 'vscode', 'node_modules'),
|
|
345
|
+
join('lib', 'vscode', 'extensions', 'node_modules'),
|
|
346
|
+
].map((p) => p.replaceAll('\\', '/'));
|
|
347
|
+
console.log(`[repack] VS Code 树(平台无关)→ ${relative(pkgRoot, join(dir, 'vscode'))}`);
|
|
348
|
+
cpSync(vendorTree, join(dir, 'vscode'), {
|
|
348
349
|
recursive: true,
|
|
349
350
|
dereference: false,
|
|
350
351
|
maxRetries: 6,
|
|
351
352
|
retryDelay: 250,
|
|
353
|
+
filter: (source) => {
|
|
354
|
+
const rel = source.slice(vendorTree.length + 1).replaceAll('\\', '/');
|
|
355
|
+
return !inner.some((p) => rel === p || rel.startsWith(`${p}/`));
|
|
356
|
+
},
|
|
352
357
|
});
|
|
353
|
-
|
|
354
|
-
rmSync(join(dir, 'code-server', 'node_modules', 'argon2'), { recursive: true, force: true });
|
|
358
|
+
const name = `${SCOPE}/dshcs-vscode-server`;
|
|
355
359
|
writeFileSync(join(dir, 'package.json'), JSON.stringify({
|
|
356
|
-
name
|
|
360
|
+
name,
|
|
357
361
|
version,
|
|
358
|
-
description: `code-server ${version} (
|
|
359
|
-
+
|
|
360
|
-
files: ['
|
|
362
|
+
description: `The VS Code server tree from code-server ${version} (lib/vscode + browser assets), repacked so pnpm `
|
|
363
|
+
+ 'installs it without any build script. The code-server Node layer is replaced by the plugin launcher.',
|
|
364
|
+
files: ['vscode'],
|
|
361
365
|
license: 'MIT',
|
|
362
366
|
repository: readJson(join(pkgRoot, 'package.json'))?.repository ?? undefined,
|
|
363
367
|
}, null, 2) + '\n', 'utf8');
|
|
364
|
-
const name = `${SCOPE}/dshcs-code-server`;
|
|
365
368
|
return { dir, name, version, file: tgzName(name, version) };
|
|
366
369
|
}
|
|
367
370
|
|
|
368
|
-
/** ② argon2 平台二进制包(每个目标一个):@<scope>/dshcs-argon2-<target>@<argon2 版本>。
|
|
369
|
-
* 与其它原生包同一套规则:删 scripts/files/binding.gyp,保留 build/Release 里的 .node;
|
|
370
|
-
* prebuilds/ 一律删掉(统一走 build/Release,避免 node-gyp-build 在 win32-x64 选到上游 .glibc 命名文件)。 */
|
|
371
|
-
function buildArgon2Package(target) {
|
|
372
|
-
const [platform, arch] = target.split('-');
|
|
373
|
-
const src = join(pkgRoot, 'vendor', 'code-server', 'node_modules', 'argon2');
|
|
374
|
-
if (!existsSync(join(src, 'package.json'))) throw new Error('缺少 vendor/code-server/node_modules/argon2');
|
|
375
|
-
const version = readJson(join(src, 'package.json')).version;
|
|
376
|
-
const dir = join(OUT, 'build', `argon2-${target}`);
|
|
377
|
-
rmSync(dir, { recursive: true, force: true });
|
|
378
|
-
mkdirSync(dirname(dir), { recursive: true });
|
|
379
|
-
cpSync(src, dir, { recursive: true, maxRetries: 6, retryDelay: 250 }); // dest 不存在 → 内容平铺到 dir
|
|
380
|
-
rmSync(join(dir, 'build'), { recursive: true, force: true }); // 重新编译,避免带走 host 架构产物
|
|
381
|
-
|
|
382
|
-
console.log(`[repack] argon2 ${target}: node-gyp rebuild --arch=${arch} …`);
|
|
383
|
-
const res = spawnSync(process.execPath, [nodeGypCli(), 'rebuild', `--arch=${arch}`], { cwd: dir, stdio: 'inherit' });
|
|
384
|
-
if ((res.status ?? 1) !== 0) throw new Error(`argon2 ${target} 编译失败 (${res.status})`);
|
|
385
|
-
// 编译完成后才剥掉构建信号:删 prebuilds(统一走 build/Release)、binding.gyp、中间产物
|
|
386
|
-
rmSync(join(dir, 'prebuilds'), { recursive: true, force: true });
|
|
387
|
-
rmSync(join(dir, 'binding.gyp'), { force: true });
|
|
388
|
-
rmSync(join(dir, '.npmignore'), { force: true });
|
|
389
|
-
rmSync(join(dir, 'build', 'gyp'), { recursive: true, force: true }); // 中间产物,别进包
|
|
390
|
-
const bin = join(dir, 'build', 'Release', 'argon2.node');
|
|
391
|
-
if (!existsSync(bin)) throw new Error(`argon2 ${target} 缺 build/Release/argon2.node`);
|
|
392
|
-
const want = arch === 'x64' ? '0x8664' : arch === 'arm64' ? '0xaa64' : null;
|
|
393
|
-
const machine = peMachine(bin);
|
|
394
|
-
if (want !== null && machine !== want) throw new Error(`argon2 ${target} PE machine=${machine},期望 ${want}`);
|
|
395
|
-
|
|
396
|
-
const manifest = readJson(join(dir, 'package.json'));
|
|
397
|
-
delete manifest.scripts;
|
|
398
|
-
delete manifest.files;
|
|
399
|
-
manifest.name = `${SCOPE}/dshcs-argon2-${target}`;
|
|
400
|
-
manifest.version = version;
|
|
401
|
-
manifest.os = [platform];
|
|
402
|
-
manifest.cpu = [arch];
|
|
403
|
-
writeFileSync(join(dir, 'package.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf8');
|
|
404
|
-
console.log(` ✓ argon2 ${target} → ${machine} (${manifest.name}@${version})`);
|
|
405
|
-
return { dir, name: manifest.name, version, file: tgzName(manifest.name, version) };
|
|
406
|
-
}
|
|
407
|
-
|
|
408
371
|
/** 从已安装的 profile 的 node_modules 还原 host 平台映射(别名目录名 = 原始包名,清单名 = 重打包名)。
|
|
409
372
|
* 聚合包清单丢失时的兜底(例如上次构建中途失败)。 */
|
|
410
373
|
function hostMapFromProfile() {
|
|
@@ -425,7 +388,9 @@ function hostMapFromProfile() {
|
|
|
425
388
|
if (ent.name.startsWith('@')) { visit(full, prefix + ent.name + '/'); continue; }
|
|
426
389
|
const m = readJson(join(full, 'package.json'));
|
|
427
390
|
if (m === null || typeof m.name !== 'string') continue;
|
|
428
|
-
|
|
391
|
+
// argon2 已随 code-server 服务层移除:旧 profile 的别名表里可能仍有它
|
|
392
|
+
if (ent.name === 'argon2' || /dshcs-argon2/.test(m.name)) continue;
|
|
393
|
+
if (!/^@[^/]+\/dshcs-/.test(m.name) || /dshcs-(code-server|vscode-server)/.test(m.name)) continue;
|
|
429
394
|
map.set(`${prefix}${ent.name}`, { pkgName: m.name, version: m.version });
|
|
430
395
|
}
|
|
431
396
|
};
|
|
@@ -449,7 +414,8 @@ function reuseExisting(targets, hostKey) {
|
|
|
449
414
|
if (agg === null) continue;
|
|
450
415
|
const map = new Map();
|
|
451
416
|
for (const [orig, spec] of Object.entries(agg.dependencies ?? {})) {
|
|
452
|
-
|
|
417
|
+
// 旧的聚合包清单里可能仍带 argon2(已随 code-server 服务层移除)
|
|
418
|
+
if (orig === 'argon2') continue;
|
|
453
419
|
const hit = /^npm:(.+)@([^@]+)$/.exec(spec);
|
|
454
420
|
if (hit === null) throw new Error(`无法解析聚合包依赖 ${orig}: ${spec}`);
|
|
455
421
|
map.set(orig, { pkgName: hit[1], version: hit[2] });
|
|
@@ -495,10 +461,10 @@ function reuseExisting(targets, hostKey) {
|
|
|
495
461
|
for (const [orig, info] of hostSet) {
|
|
496
462
|
repack.set(orig, { pkg: { version: info.version }, platformSpecific: info.pkgName.endsWith(`-${knownTarget}`) });
|
|
497
463
|
}
|
|
498
|
-
// 已有原生包目录 → plan
|
|
464
|
+
// 已有原生包目录 → plan 项;顺带清掉将被重建的树目录(旧 code-server / argon2 目录一并清理)
|
|
499
465
|
const reusedItems = [];
|
|
500
466
|
for (const ent of readdirSync(buildRoot)) {
|
|
501
|
-
if (/^code-server/.test(ent) || /^argon2-/.test(ent)) {
|
|
467
|
+
if (ent === 'vscode' || /^code-server/.test(ent) || /^argon2-/.test(ent)) {
|
|
502
468
|
rmSync(join(buildRoot, ent), { recursive: true, force: true });
|
|
503
469
|
continue;
|
|
504
470
|
}
|
|
@@ -573,6 +539,21 @@ function main() {
|
|
|
573
539
|
const [platform, arch] = pkg.pkgName.match(/-(win32|darwin|linux)-(arm64|x64)$/).slice(1);
|
|
574
540
|
m.os = [platform];
|
|
575
541
|
m.cpu = [arch];
|
|
542
|
+
if (platform === 'win32') {
|
|
543
|
+
// 交叉编译产物静态校验:.node 的 PE machine 必须是目标架构(0x8664=x64 / 0xaa64=arm64)
|
|
544
|
+
const want = arch === 'x64' ? '0x8664' : '0xaa64';
|
|
545
|
+
const files = nodeFilesOf(dir);
|
|
546
|
+
const wrong = files
|
|
547
|
+
.map((file) => [file, peMachine(file)])
|
|
548
|
+
.filter(([, machine]) => machine !== null && machine !== want);
|
|
549
|
+
if (files.length > 0 && wrong.length === files.length) {
|
|
550
|
+
throw new Error(`${pkg.pkgName}: 所有 .node 架构都不是 ${want}`
|
|
551
|
+
+ `(${wrong.map(([file, machine]) => `${relative(dir, file)}=${machine}`).join(', ')})`);
|
|
552
|
+
}
|
|
553
|
+
if (wrong.length > 0) {
|
|
554
|
+
console.warn(` ⚠ ${pkg.pkgName}: ${wrong.length} 个 .node 架构不符(期望 ${want})`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
576
557
|
}
|
|
577
558
|
for (const fld of ['dependencies', 'optionalDependencies']) {
|
|
578
559
|
if (!m[fld]) continue;
|
|
@@ -588,15 +569,9 @@ function main() {
|
|
|
588
569
|
const plan = [];
|
|
589
570
|
// 0) 已打包的原生包(--reuse 模式)
|
|
590
571
|
for (const item of reusedItems) plan.push(item);
|
|
591
|
-
// 1)
|
|
592
|
-
const
|
|
593
|
-
plan.push({ dir:
|
|
594
|
-
const argon2ByTarget = new Map();
|
|
595
|
-
for (const target of targets) {
|
|
596
|
-
const pkg = buildArgon2Package(target);
|
|
597
|
-
argon2ByTarget.set(target, pkg);
|
|
598
|
-
plan.push({ dir: pkg.dir, file: pkg.file });
|
|
599
|
-
}
|
|
572
|
+
// 1) VS Code 树(平台无关,1 个包)
|
|
573
|
+
const vscodePkg = buildVscodeServerPackage();
|
|
574
|
+
plan.push({ dir: vscodePkg.dir, file: vscodePkg.file });
|
|
600
575
|
// 2) 全平台重打包包(从 host 树;--reuse 时用已打包目录)
|
|
601
576
|
if (!REUSE) {
|
|
602
577
|
for (const [name, r] of repack) {
|
|
@@ -624,7 +599,7 @@ function main() {
|
|
|
624
599
|
}
|
|
625
600
|
if (crossTree !== null) rmSync(crossTree, { recursive: true, force: true });
|
|
626
601
|
}
|
|
627
|
-
// 3) 聚合包(每平台一个):dependencies 用 npm: 别名把重打包包装回原名
|
|
602
|
+
// 3) 聚合包(每平台一个):dependencies 用 npm: 别名把重打包包装回原名
|
|
628
603
|
for (const target of targets) {
|
|
629
604
|
const map = byTarget.get(target);
|
|
630
605
|
const aggName = `${SCOPE}/dsh-code-server-runtime-${target}`;
|
|
@@ -632,13 +607,11 @@ function main() {
|
|
|
632
607
|
mkdirSync(aggDir, { recursive: true });
|
|
633
608
|
const deps = {};
|
|
634
609
|
for (const [name] of repack) deps[name] = `npm:${map.get(name).pkgName}@${map.get(name).version}`;
|
|
635
|
-
// argon2:code-server 本体包不带原生二进制,由这里按平台装回原名 "argon2"
|
|
636
|
-
const argon2 = argon2ByTarget.get(target);
|
|
637
|
-
if (argon2 !== undefined) deps.argon2 = `npm:${argon2.name}@${argon2.version}`;
|
|
638
610
|
writeFileSync(join(aggDir, 'package.json'), JSON.stringify({
|
|
639
611
|
name: aggName,
|
|
640
612
|
version: pluginVersion,
|
|
641
|
-
description: `Prebuilt native modules for
|
|
613
|
+
description: `Prebuilt native modules for the VS Code server tree on ${target} `
|
|
614
|
+
+ '(node-pty, @vscode/sqlite3, kerberos, @vscode/spdlog, …), '
|
|
642
615
|
+ 'so that installing the VS Code inner dependencies needs no build approval or C++ toolchain.',
|
|
643
616
|
os: [target.split('-')[0]],
|
|
644
617
|
cpu: [target.split('-')[1]],
|
|
@@ -652,19 +625,19 @@ function main() {
|
|
|
652
625
|
writeFileSync(join(OUT, 'pack-plan.json'), JSON.stringify(plan, null, 2) + '\n', 'utf8');
|
|
653
626
|
console.log(`[repack] 生成 ${plan.length} 个待打包目录 → repack/ (计划:repack/pack-plan.json)`);
|
|
654
627
|
|
|
655
|
-
// 4) 改写插件 package.json 的依赖:纯 JS 直装集 +
|
|
656
|
-
// 平台专属的(
|
|
628
|
+
// 4) 改写插件 package.json 的依赖:纯 JS 直装集 + VS Code 树包(平台无关);
|
|
629
|
+
// 平台专属的(原生包)通过聚合包按 os/cpu 自动选。
|
|
657
630
|
const pkgFile = join(pkgRoot, 'package.json');
|
|
658
631
|
const pluginPkg = readJson(pkgFile);
|
|
659
632
|
pluginPkg.dependencies = Object.fromEntries([
|
|
660
|
-
[
|
|
633
|
+
[vscodePkg.name, vscodePkg.version],
|
|
661
634
|
...declare.map((d) => [d.name, d.version]),
|
|
662
635
|
].sort(([a], [b]) => a.localeCompare(b)));
|
|
663
636
|
pluginPkg.optionalDependencies = Object.fromEntries(targets
|
|
664
637
|
.map((t) => [`${SCOPE}/dsh-code-server-runtime-${t}`, `^${pluginVersion}`]));
|
|
665
638
|
writeFileSync(pkgFile, JSON.stringify(pluginPkg, null, 2) + '\n', 'utf8');
|
|
666
639
|
console.log(`[repack] 已写入 package.json:dependencies ${pluginPkg.dependencies ? Object.keys(pluginPkg.dependencies).length : 0} 个`
|
|
667
|
-
+ `(
|
|
640
|
+
+ `(VS Code 树 + 纯 JS),optionalDependencies ${targets.length} 个平台聚合包`);
|
|
668
641
|
|
|
669
642
|
// 5) 可选:直接打包
|
|
670
643
|
if (DO_PACK) {
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// scripts/vendor-vscode-server.mjs — 打包期准备「VS Code 树」(取代旧的 vendor-code-server.mjs 全量树)。
|
|
2
|
+
//
|
|
3
|
+
// 模型(重构后):
|
|
4
|
+
// code-server 的 Node 服务层(out/node/**, 136 个依赖)不再随包发布 —— 它由插件自带的
|
|
5
|
+
// lib/launcher.mjs 取代(见 docs/analysis-code-server-as-dsh-plugin.md)。本脚本只保留:
|
|
6
|
+
// lib/vscode/** 已由 code-server 打过补丁的 VS Code server + 内置扩展(≈197MB)
|
|
7
|
+
// out/browser/** serviceWorker.js
|
|
8
|
+
// src/browser/** favicon / PWA 图标 / robots.txt
|
|
9
|
+
// LICENSE, ThirdPartyNotices.txt
|
|
10
|
+
// 并生成树根 package.json(版本 = 上游 code-server 版本,便于版本比对)。
|
|
11
|
+
//
|
|
12
|
+
// 产物:
|
|
13
|
+
// vendor/vscode/ 本地开发树(--dev-links 时把 VS Code 内部依赖用 junction 补上,可直接跑)
|
|
14
|
+
// vendor/VENDOR.json 版本与来源元数据
|
|
15
|
+
//
|
|
16
|
+
// 用法:
|
|
17
|
+
// node scripts/vendor-vscode-server.mjs [--check] [--force]
|
|
18
|
+
// node scripts/vendor-vscode-server.mjs --from <code-server 树> # 从现有树复制(最快)
|
|
19
|
+
// node scripts/vendor-vscode-server.mjs --version 4.136.2 # 从 registry 安装
|
|
20
|
+
// node scripts/vendor-vscode-server.mjs --dev-links # 额外补 lib/vscode/node_modules
|
|
21
|
+
import { spawnSync } from 'node:child_process';
|
|
22
|
+
import {
|
|
23
|
+
cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync,
|
|
24
|
+
rmSync, statSync, symlinkSync, writeFileSync,
|
|
25
|
+
} from 'node:fs';
|
|
26
|
+
import { dirname, join, resolve } from 'node:path';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
import { homedir } from 'node:os';
|
|
29
|
+
|
|
30
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
31
|
+
const pkgRoot = resolve(here, '..');
|
|
32
|
+
const vendorDir = join(pkgRoot, 'vendor');
|
|
33
|
+
const vendorTree = join(vendorDir, 'vscode');
|
|
34
|
+
const legacyTree = join(vendorDir, 'code-server'); // 旧全量树(回退用,不再生成)
|
|
35
|
+
const workRoot = join(pkgRoot, '.vendor-tmp');
|
|
36
|
+
const npmCache = join(pkgRoot, '.npm-cache');
|
|
37
|
+
|
|
38
|
+
function argValues(name) {
|
|
39
|
+
const out = [];
|
|
40
|
+
for (let i = 0; i < process.argv.length - 1; i += 1) {
|
|
41
|
+
if (process.argv[i] !== name) continue;
|
|
42
|
+
for (const part of String(process.argv[i + 1]).split(',')) {
|
|
43
|
+
const v = part.trim();
|
|
44
|
+
if (v !== '') out.push(v);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
function argValue(name) {
|
|
50
|
+
const all = argValues(name);
|
|
51
|
+
return all.length > 0 ? all[all.length - 1] : null;
|
|
52
|
+
}
|
|
53
|
+
const FORCE = process.argv.includes('--force');
|
|
54
|
+
const DEV_LINKS = process.argv.includes('--dev-links');
|
|
55
|
+
const FROM = argValue('--from');
|
|
56
|
+
const PINNED = argValue('--version') || process.env.DSHCS_CODE_SERVER_VERSION || null;
|
|
57
|
+
|
|
58
|
+
function run(cmd, args, cwd, extraEnv) {
|
|
59
|
+
const useShell = process.platform === 'win32' && /\.(cmd|bat)$/i.test(cmd);
|
|
60
|
+
console.log(`[vendor] $ ${cmd} ${args.join(' ')}${cwd ? ` (cwd=${cwd})` : ''}`);
|
|
61
|
+
const res = spawnSync(cmd, args, {
|
|
62
|
+
cwd, stdio: 'inherit', shell: useShell,
|
|
63
|
+
env: extraEnv ? { ...process.env, ...extraEnv } : process.env,
|
|
64
|
+
});
|
|
65
|
+
if (res.status !== 0) throw new Error(`${cmd} ${args.join(' ')} failed (${res.status})`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function npm(args, cwd) {
|
|
69
|
+
const env = { npm_config_cache: npmCache, npm_config_update_notifier: 'false' };
|
|
70
|
+
const cli = join(dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
|
|
71
|
+
if (existsSync(cli)) run(process.execPath, [cli, ...args], cwd, env);
|
|
72
|
+
else run(process.platform === 'win32' ? 'npm.cmd' : 'npm', args, cwd, env);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const readJson = (file) => { try { return JSON.parse(readFileSync(file, 'utf8')); } catch { return null; } };
|
|
76
|
+
|
|
77
|
+
async function latestVersion() {
|
|
78
|
+
const res = await fetch('https://registry.npmjs.org/code-server/latest');
|
|
79
|
+
if (!res.ok) throw new Error(`registry responded ${res.status}`);
|
|
80
|
+
const json = await res.json();
|
|
81
|
+
if (typeof json.version !== 'string') throw new Error('registry payload has no version');
|
|
82
|
+
return json.version;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function sizeMB(dir) {
|
|
86
|
+
let total = 0;
|
|
87
|
+
const stack = [dir];
|
|
88
|
+
while (stack.length > 0) {
|
|
89
|
+
const cur = stack.pop();
|
|
90
|
+
let entries;
|
|
91
|
+
try { entries = readdirSync(cur, { withFileTypes: true }); } catch { continue; }
|
|
92
|
+
for (const ent of entries) {
|
|
93
|
+
const full = join(cur, ent.name);
|
|
94
|
+
if (ent.isDirectory()) stack.push(full);
|
|
95
|
+
else if (ent.isFile()) { try { total += statSync(full).size; } catch { /* ignore */ } }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return Math.round((total / 1024 / 1024) * 10) / 10;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** 默认源树:已安装 profile 里最新的 dshcs-code-server 树 → 旧 vendor 树。 */
|
|
102
|
+
function defaultSourceTree() {
|
|
103
|
+
const dshHome = process.env.DSH_HOME || join(process.env.USERPROFILE ?? homedir(), '.dsh');
|
|
104
|
+
const profiles = join(dshHome, 'profiles');
|
|
105
|
+
const cands = [];
|
|
106
|
+
try {
|
|
107
|
+
for (const name of readdirSync(profiles)) {
|
|
108
|
+
cands.push(join(profiles, name, 'node_modules', '@jinsiyu', 'dshcs-code-server', 'code-server'));
|
|
109
|
+
}
|
|
110
|
+
} catch { /* ignore */ }
|
|
111
|
+
cands.push(legacyTree);
|
|
112
|
+
for (const c of cands) if (existsSync(join(c, 'lib', 'vscode', 'out', 'server-main.js'))) return c;
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** 复制策略:只留服务 IDE 必需的部分。
|
|
117
|
+
* keep: lib/vscode/**、out/browser/**、src/browser/**(除 pages)、LICENSE、ThirdPartyNotices.txt、README.md
|
|
118
|
+
* 排除: node_modules/**、out/node/**、out/common/**、src/browser/pages/**、lib/vscode/(extensions/)node_modules/** */
|
|
119
|
+
function copySlim(src, dest) {
|
|
120
|
+
rmSync(dest, { recursive: true, force: true });
|
|
121
|
+
mkdirSync(dest, { recursive: true });
|
|
122
|
+
const dropDirs = [
|
|
123
|
+
'node_modules',
|
|
124
|
+
join('out', 'node'),
|
|
125
|
+
join('out', 'common'),
|
|
126
|
+
join('src', 'browser', 'pages'),
|
|
127
|
+
join('lib', 'vscode', 'node_modules'),
|
|
128
|
+
join('lib', 'vscode', 'extensions', 'node_modules'),
|
|
129
|
+
].map((p) => p.replaceAll('\\', '/'));
|
|
130
|
+
const keepFiles = new Set(['LICENSE', 'ThirdPartyNotices.txt', 'README.md']);
|
|
131
|
+
const keepRootDirs = new Set(['lib', 'out', 'src']);
|
|
132
|
+
cpSync(src, dest, {
|
|
133
|
+
recursive: true,
|
|
134
|
+
dereference: false,
|
|
135
|
+
maxRetries: 6,
|
|
136
|
+
retryDelay: 250,
|
|
137
|
+
filter: (source) => {
|
|
138
|
+
const rel = source.slice(src.length + 1).replaceAll('\\', '/');
|
|
139
|
+
if (rel === '') return true;
|
|
140
|
+
if (dropDirs.some((d) => rel === d || rel.startsWith(`${d}/`))) return false;
|
|
141
|
+
const top = rel.split('/')[0];
|
|
142
|
+
if (rel.includes('/')) return keepRootDirs.has(top);
|
|
143
|
+
return keepRootDirs.has(top) || keepFiles.has(top);
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** 把源树的 VS Code 内部依赖 junction 复刻到目标树(开发期直接可跑用)。
|
|
149
|
+
* 必须用 type:'junction'(Windows 下 symlink 需要权限,cpSync 复制链接会 EPERM)。 */
|
|
150
|
+
function linkInnerDeps(srcTree, destTree) {
|
|
151
|
+
const pairs = [
|
|
152
|
+
[join('lib', 'vscode', 'node_modules'), join('lib', 'vscode', 'package.json')],
|
|
153
|
+
[join('lib', 'vscode', 'extensions', 'node_modules'), join('lib', 'vscode', 'extensions', 'package.json')],
|
|
154
|
+
];
|
|
155
|
+
let linked = 0;
|
|
156
|
+
const failed = [];
|
|
157
|
+
for (const [relNm] of pairs) {
|
|
158
|
+
const srcNm = join(srcTree, relNm);
|
|
159
|
+
const dstNm = join(destTree, relNm);
|
|
160
|
+
if (!existsSync(srcNm)) continue;
|
|
161
|
+
mkdirSync(dstNm, { recursive: true });
|
|
162
|
+
const entries = [];
|
|
163
|
+
for (const ent of readdirSync(srcNm, { withFileTypes: true })) {
|
|
164
|
+
if (ent.name.startsWith('.')) continue;
|
|
165
|
+
if (ent.isDirectory() && ent.name.startsWith('@')) {
|
|
166
|
+
for (const sub of readdirSync(join(srcNm, ent.name), { withFileTypes: true })) {
|
|
167
|
+
if (sub.name.startsWith('.')) continue;
|
|
168
|
+
entries.push(`${ent.name}/${sub.name}`);
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
entries.push(ent.name);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const name of entries) {
|
|
175
|
+
const from = join(srcNm, name);
|
|
176
|
+
const to = join(dstNm, name);
|
|
177
|
+
let target = from;
|
|
178
|
+
try { if (lstatSync(from).isSymbolicLink()) target = readlinkSync(from); } catch { /* use as-is */ }
|
|
179
|
+
try {
|
|
180
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
181
|
+
rmSync(to, { recursive: true, force: true });
|
|
182
|
+
symlinkSync(target, to, 'junction');
|
|
183
|
+
linked += 1;
|
|
184
|
+
} catch (error) {
|
|
185
|
+
failed.push(`${name}: ${error.code ?? error.message}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return { linked, failed };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function main() {
|
|
193
|
+
const existing = readJson(join(vendorTree, 'package.json'))?.version ?? null;
|
|
194
|
+
|
|
195
|
+
if (process.argv.includes('--check')) {
|
|
196
|
+
const latest = await latestVersion();
|
|
197
|
+
console.log(`[vendor] 内置 VS Code 树: ${existing ?? '(无)'} | 上游 code-server latest: ${latest}`);
|
|
198
|
+
console.log(existing === latest ? '[vendor] 已是最新' : '[vendor] 有新版本:pnpm run vendor:latest → repack:build → publish:repacks → 版本 +1 → pnpm pack');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const skip = existing !== null && !FORCE && FROM === null && PINNED === null;
|
|
203
|
+
let codeServerVersion = existing;
|
|
204
|
+
let sourceKind = 'registry';
|
|
205
|
+
let sourceTree = null;
|
|
206
|
+
|
|
207
|
+
if (skip) {
|
|
208
|
+
console.log(`[vendor] vendor/vscode 已存在(code-server@${existing});跳过(用 --force 重建)`);
|
|
209
|
+
} else if (FROM !== null || (sourceTree = defaultSourceTree()) !== null) {
|
|
210
|
+
const src = FROM ?? sourceTree;
|
|
211
|
+
codeServerVersion = readJson(join(src, 'package.json'))?.version ?? null;
|
|
212
|
+
if (codeServerVersion === null) throw new Error(`--from ${src} 不是一棵 code-server 树`);
|
|
213
|
+
console.log(`[vendor] 源树 ${src}(code-server@${codeServerVersion})→ vendor/vscode/`);
|
|
214
|
+
copySlim(src, vendorTree);
|
|
215
|
+
sourceKind = FROM !== null ? `snapshot:${src}` : `local:${src}`;
|
|
216
|
+
} else {
|
|
217
|
+
codeServerVersion = PINNED ?? await latestVersion();
|
|
218
|
+
console.log(`[vendor] 从 registry 准备 code-server@${codeServerVersion} …`);
|
|
219
|
+
const tmp = join(workRoot, `source-${process.pid}`);
|
|
220
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
221
|
+
mkdirSync(tmp, { recursive: true });
|
|
222
|
+
try {
|
|
223
|
+
npm(['install', `code-server@${codeServerVersion}`, '--ignore-scripts', '--omit=dev',
|
|
224
|
+
'--no-audit', '--no-fund', '--no-save', '--prefix', tmp], tmp);
|
|
225
|
+
const tree = join(tmp, 'node_modules', 'code-server');
|
|
226
|
+
if (!existsSync(join(tree, 'lib', 'vscode', 'out', 'server-main.js'))) throw new Error('code-server 安装不完整');
|
|
227
|
+
copySlim(tree, vendorTree);
|
|
228
|
+
} finally {
|
|
229
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const vscodePkg = readJson(join(vendorTree, 'lib', 'vscode', 'package.json')) ?? {};
|
|
234
|
+
const product = readJson(join(vendorTree, 'lib', 'vscode', 'product.json')) ?? {};
|
|
235
|
+
const productPath = `${product.quality ?? 'oss'}-${product.commit ?? 'dev'}`;
|
|
236
|
+
const layout = { name: 'dshcs-vscode-tree', private: true, version: codeServerVersion };
|
|
237
|
+
layout.vscodeVersion = vscodePkg.version ?? null;
|
|
238
|
+
layout.productPath = productPath;
|
|
239
|
+
writeFileSync(join(vendorTree, 'package.json'), JSON.stringify(layout, null, 2) + '\n', 'utf8');
|
|
240
|
+
writeFileSync(join(vendorTree, 'VENDOR-TREE.json'), JSON.stringify({
|
|
241
|
+
codeServerVersion,
|
|
242
|
+
vscodeVersion: vscodePkg.version ?? null,
|
|
243
|
+
productPath,
|
|
244
|
+
preparedAt: new Date().toISOString(),
|
|
245
|
+
source: sourceKind,
|
|
246
|
+
}, null, 2) + '\n', 'utf8');
|
|
247
|
+
|
|
248
|
+
let links = { linked: 0, failed: [] };
|
|
249
|
+
if (DEV_LINKS) {
|
|
250
|
+
const src = FROM ?? defaultSourceTree();
|
|
251
|
+
if (src === null) console.warn('[vendor] --dev-links: 找不到可复刻 junction 的源树,跳过');
|
|
252
|
+
else {
|
|
253
|
+
links = linkInnerDeps(src, vendorTree);
|
|
254
|
+
console.log(`[vendor] dev: 补 VS Code 内部依赖 junction ${links.linked} 个${links.failed.length > 0 ? `(失败 ${links.failed.length}: ${links.failed.slice(0, 5).join(', ')})` : ''}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const meta = {
|
|
259
|
+
codeServerVersion,
|
|
260
|
+
vscodeVersion: vscodePkg.version ?? null,
|
|
261
|
+
productPath,
|
|
262
|
+
layout: 'vscode-only',
|
|
263
|
+
preparedAt: new Date().toISOString(),
|
|
264
|
+
source: sourceKind,
|
|
265
|
+
node: process.version,
|
|
266
|
+
platform: process.platform,
|
|
267
|
+
arch: process.arch,
|
|
268
|
+
sizeMB: sizeMB(vendorTree),
|
|
269
|
+
};
|
|
270
|
+
writeFileSync(join(vendorDir, 'VENDOR.json'), JSON.stringify(meta, null, 2) + '\n', 'utf8');
|
|
271
|
+
console.log(`[vendor] 完成:vendor/vscode (code-server@${codeServerVersion}, VS Code ${meta.vscodeVersion}, ${meta.sizeMB} MB, productPath=${productPath})`);
|
|
272
|
+
if (!DEV_LINKS) {
|
|
273
|
+
console.log('[vendor] 提示:本树不含 VS Code 内部依赖;运行时由 lib/native.js 的 ensureRuntimeLayout() 补 junction'
|
|
274
|
+
+ '(开发期可直接 `--dev-links` 复刻)。');
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
await main();
|
package/vendor/VENDOR.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"codeServerVersion": "4.136.2",
|
|
3
|
-
"
|
|
3
|
+
"vscodeVersion": "1.136.1",
|
|
4
|
+
"productPath": "stable-8d5f383f301ca20681f5b6606b8207d9dc87bdd8",
|
|
5
|
+
"layout": "vscode-only",
|
|
6
|
+
"preparedAt": "2026-09-10T08:30:15.204Z",
|
|
4
7
|
"source": "registry",
|
|
5
8
|
"node": "v24.13.1",
|
|
6
9
|
"platform": "win32",
|
|
7
10
|
"arch": "arm64",
|
|
8
|
-
"sizeMB":
|
|
11
|
+
"sizeMB": 197.2
|
|
9
12
|
}
|