

























@@ -16,6 +16,80 @@ function readJson(file) {
1616return JSON.parse(fs.readFileSync(file, "utf8"));
1717}
181819+// Runs inside the bare Docker E2E image, before package dependencies are installed.
20+// Keep this to the small pnpm-workspace.yaml surface the fixture mutates.
21+function findTopLevelBlock(lines, key) {
22+const start = lines.findIndex((line) => new RegExp(`^${key}:\\s*(?:#.*)?$`).test(line));
23+if (start === -1) {
24+return null;
25+}
26+let end = start + 1;
27+while (end < lines.length && !/^[A-Za-z0-9_-]+:\s*/.test(lines[end])) {
28+end += 1;
29+}
30+return { start, end };
31+}
32+33+function parseYamlScalar(raw) {
34+const trimmed = raw.trim();
35+const withoutComment = trimmed.replace(/\s+#.*$/, "");
36+if (withoutComment.startsWith('"') && withoutComment.endsWith('"')) {
37+return withoutComment.slice(1, -1);
38+}
39+if (withoutComment.startsWith("'") && withoutComment.endsWith("'")) {
40+return withoutComment.slice(1, -1);
41+}
42+return withoutComment;
43+}
44+45+function readWorkspacePatchedDependencies(file) {
46+const lines = fs.readFileSync(file, "utf8").split("\n");
47+const block = findTopLevelBlock(lines, "patchedDependencies");
48+if (!block) {
49+return { patches: undefined };
50+}
51+52+const patches = {};
53+for (const line of lines.slice(block.start + 1, block.end)) {
54+const match = line.match(/^\s+(.+?):\s+(.+?)\s*$/);
55+if (!match) {
56+continue;
57+}
58+patches[parseYamlScalar(match[1])] = parseYamlScalar(match[2]);
59+}
60+return { patches };
61+}
62+63+function writeWorkspacePnpmConfig(file, keptPatches) {
64+const original = fs.readFileSync(file, "utf8");
65+const hadTrailingNewline = original.endsWith("\n");
66+const lines = original.replace(/\n$/, "").split("\n");
67+const patchBlock = findTopLevelBlock(lines, "patchedDependencies");
68+69+if (patchBlock) {
70+const nextLines = [];
71+nextLines.push(...lines.slice(0, patchBlock.start));
72+if (Object.keys(keptPatches).length > 0) {
73+nextLines.push("patchedDependencies:");
74+for (const [dependency, patchFile] of Object.entries(keptPatches)) {
75+nextLines.push(` ${JSON.stringify(dependency)}: ${JSON.stringify(patchFile)}`);
76+}
77+}
78+nextLines.push(...lines.slice(patchBlock.end));
79+lines.length = 0;
80+lines.push(...nextLines);
81+}
82+83+const allowUnusedIndex = lines.findIndex((line) => /^allowUnusedPatches:\s*/.test(line));
84+if (allowUnusedIndex === -1) {
85+lines.push("allowUnusedPatches: true");
86+} else {
87+lines[allowUnusedIndex] = "allowUnusedPatches: true";
88+}
89+90+fs.writeFileSync(file, `${lines.join("\n")}${hadTrailingNewline ? "\n" : ""}`);
91+}
92+1993function writeControlUi(root) {
2094const file = path.join(root, "dist", "control-ui", "index.html");
2195fs.mkdirSync(path.dirname(file), { recursive: true });
@@ -25,31 +99,41 @@ function writeControlUi(root) {
2599function prepareGitFixture(root) {
26100const packageJsonPath = path.join(root, "package.json");
27101const packageJson = readJson(packageJsonPath);
28-packageJson.pnpm = { ...packageJson.pnpm, allowUnusedPatches: true };
29-const patches = packageJson.pnpm.patchedDependencies;
102+const pnpmWorkspacePath = path.join(root, "pnpm-workspace.yaml");
103+const workspaceConfig = fs.existsSync(pnpmWorkspacePath)
104+ ? readWorkspacePatchedDependencies(pnpmWorkspacePath)
105+ : undefined;
106+const pnpmConfig = workspaceConfig ? {} : { ...packageJson.pnpm };
107+const patches = workspaceConfig?.patches ?? pnpmConfig.patchedDependencies;
108+const keptPatches = {};
30109if (patches && typeof patches === "object" && !Array.isArray(patches)) {
31-const kept = {};
32110const missing = [];
33111for (const [dependency, patchFile] of Object.entries(patches)) {
34112const exists =
35113typeof patchFile === "string" &&
36114fs.existsSync(path.resolve(path.dirname(packageJsonPath), patchFile));
37115if (exists) {
38-kept[dependency] = patchFile;
116+keptPatches[dependency] = patchFile;
39117} else {
40118missing.push(`${dependency} -> ${String(patchFile)}`);
41119}
42120}
43121if (missing.length > 0 && !legacyPackageAcceptanceCompat(packageJson.version)) {
44122throw new Error(
45-`package ${packageJson.version} has missing pnpm.patchedDependencies in package fixture: ${missing.join(", ")}`,
123+`package ${packageJson.version} has missing pnpm patchedDependencies in package fixture: ${missing.join(", ")}`,
46124);
47125}
48-if (Object.keys(kept).length > 0) {
49-packageJson.pnpm.patchedDependencies = kept;
126+}
127+if (workspaceConfig) {
128+writeWorkspacePnpmConfig(pnpmWorkspacePath, keptPatches);
129+} else {
130+pnpmConfig.allowUnusedPatches = true;
131+if (Object.keys(keptPatches).length > 0) {
132+pnpmConfig.patchedDependencies = keptPatches;
50133} else {
51-delete packageJson.pnpm.patchedDependencies;
134+delete pnpmConfig.patchedDependencies;
52135}
136+packageJson.pnpm = pnpmConfig;
53137}
54138const fixtureUiBuildSource = `const fs=require("node:fs");fs.mkdirSync("dist/control-ui",{recursive:true});fs.writeFileSync("dist/control-ui/index.html",${JSON.stringify(controlUiHtml)})`;
55139packageJson.scripts = {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。