宿主生成的 profile 层 cordis.patch.yml 可能整体只有一个空 flow 序列 [],
那本身就是一个完整 YAML 文档。旧实现无条件在文件尾追加 `- insert:`,
结果是第二个文档,宿主启动解析报
"end of the stream or a document separator is expected" 直接崩掉
(macOS DSH Desktop 0.2.1-rc.4)。
- 新增 classifyPatchBody() 判断数据部分形态:empty / empty-flow /
block-seq / flow-seq / unknown,只看最后一个文档的根节点
- 空 flow 序列([] 或 [ ],可带 --- 前缀)→ 把整行替换成 insert 块,
绝不追加成第二个文档
- 空文件 / 只有注释和空行 → 写 insert 块,注释保留
- 正常块序列 → 沿用旧的追加行为,前导空行规则不变
- 非空 flow 序列(宿主/用户内容)以及认不准的形态 → 不动,
返回 { ok: false, wrote: false },宁可留着也别写坏
- postinstall 脚本区分「跳过」和「已有」,跳过时打印说明
- profileHasPurgeBodyInsert() 检测逻辑未动
- 新增 9 条用例(形态分类 / 空文件 / 只有注释 / 注释+[] / ---+[] /
块序列追加 / 非空 flow 不写 / 已有本体行零写入 / 替换后幂等),
版本 1.1.83
Co-Authored-By: Claude <noreply@anthropic.com>
90 lines
2.8 KiB
JavaScript
90 lines
2.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* 装进 DSH profile 时补两笔(#154):
|
|
* 1. dsh.profile.bundles
|
|
* 2. 用户层 cordis.patch.yml 的本体 insert
|
|
* pnpm add / dsh plugin add 都不会自己写 insert;市场会写,已有则跳过。
|
|
* 在插件仓库里跑 pnpm install 则整段跳过。
|
|
*/
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import {
|
|
PURGE_NAME as NAME,
|
|
ensureProfileBodyInsert,
|
|
ensureProfileBundles,
|
|
ensurePnpmAllowBuild,
|
|
profileHasPurgeBodyInsert,
|
|
} from "../lib/profile-insert.js";
|
|
|
|
export { profileHasPurgeBodyInsert };
|
|
|
|
function readJson(fp) {
|
|
try {
|
|
return JSON.parse(fs.readFileSync(fp, "utf8"));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function isProfilePkg(pkg) {
|
|
if (!pkg || typeof pkg !== "object") return false;
|
|
if (typeof pkg.name === "string" && pkg.name.startsWith("dsh-profile-")) return true;
|
|
return Array.isArray(pkg.dsh?.profile?.bundles);
|
|
}
|
|
|
|
function findProfileDir() {
|
|
const seeds = [process.env.INIT_CWD, process.env.npm_config_local_prefix, process.cwd()].filter(Boolean);
|
|
for (const seed of seeds) {
|
|
let dir = path.resolve(seed);
|
|
for (let i = 0; i < 8; i += 1) {
|
|
if (isProfilePkg(readJson(path.join(dir, "package.json")))) return dir;
|
|
const parent = path.dirname(dir);
|
|
if (parent === dir) break;
|
|
dir = parent;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function ensureProfileInsert(profileDir) {
|
|
const result = ensureProfileBodyInsert(profileDir);
|
|
if (result.wrote) {
|
|
process.stdout.write(`dsh-purge: wrote body insert (${result.path})\n`);
|
|
} else if (result.ok === false) {
|
|
// 宿主 / 用户自己的 cordis.patch.yml 内容,宁可不动也别写坏(#191)
|
|
process.stdout.write(`dsh-purge: skipped body insert, unrecognized patch body (${result.path})\n`);
|
|
} else {
|
|
process.stdout.write(`dsh-purge: insert already in ${result.path}\n`);
|
|
}
|
|
return result.wrote;
|
|
}
|
|
|
|
function main() {
|
|
const profileDir = findProfileDir();
|
|
if (!profileDir) return;
|
|
|
|
const fp = path.join(profileDir, "package.json");
|
|
const pkg = readJson(fp);
|
|
if (!pkg) {
|
|
process.stderr.write(`dsh-purge: cannot read ${fp}\n`);
|
|
process.exit(1);
|
|
}
|
|
const bundles = ensureProfileBundles(profileDir);
|
|
process.stdout.write(bundles.wrote
|
|
? `dsh-purge: appended to bundles (${profileDir})\n`
|
|
: `dsh-purge: already in bundles (${profileDir})\n`);
|
|
|
|
try {
|
|
ensureProfileInsert(profileDir);
|
|
const pnpm = ensurePnpmAllowBuild(profileDir);
|
|
if (pnpm.wrote) process.stdout.write(`dsh-purge: allowed pnpm build for ${NAME}\n`);
|
|
} catch (e) {
|
|
process.stderr.write(`dsh-purge: insert failed: ${e && e.message ? e.message : e}\n`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
const invoked = process.argv[1] ? path.resolve(process.argv[1]) : "";
|
|
if (invoked === fileURLToPath(import.meta.url)) main();
|