@icebreakers/monorepo 3.2.14 → 3.2.16
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/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.cjs +15 -1
- package/dist/index.d.cts +26 -5
- package/dist/index.d.mts +27 -6
- package/dist/index.mjs +2 -2
- package/dist/{upgrade-BGgjWFfq.mjs → upgrade-C0Gz88iI.mjs} +219 -355
- package/dist/{upgrade-C2M-cSL9.cjs → upgrade-C9HSrDIr.cjs} +363 -394
- package/package.json +5 -10
|
@@ -5,112 +5,22 @@ import { findWorkspacePackages } from "@pnpm/workspace.find-packages";
|
|
|
5
5
|
import { readWorkspaceManifest } from "@pnpm/workspace.read-manifest";
|
|
6
6
|
import path from "pathe";
|
|
7
7
|
import process from "node:process";
|
|
8
|
-
import
|
|
8
|
+
import { access, cp, mkdir, mkdtemp, open, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
9
9
|
import { createConsola } from "consola";
|
|
10
10
|
import { assetsDir, checkbox, ensureTemplateAssetsPrepared, execaCommand, getAssetTargets, isGitignoreFile, scaffoldTemplate, templatesDir, toPublishGitignorePath, toWorkspaceGitignorePath } from "@icebreakers/monorepo-templates";
|
|
11
|
+
import YAML, { isSeq, parseDocument } from "yaml";
|
|
12
|
+
import crypto from "node:crypto";
|
|
11
13
|
import { loadConfig } from "c12";
|
|
12
14
|
import os from "node:os";
|
|
13
15
|
import * as path$1 from "node:path";
|
|
14
16
|
import { fileURLToPath } from "node:url";
|
|
15
17
|
import pc from "picocolors";
|
|
16
18
|
import "@pnpm/types";
|
|
17
|
-
import YAML, { isSeq, parseDocument } from "yaml";
|
|
18
|
-
import crypto from "node:crypto";
|
|
19
19
|
import { parse, stringify } from "comment-json";
|
|
20
20
|
import PQueue from "p-queue";
|
|
21
21
|
import klaw from "klaw";
|
|
22
|
-
import { Buffer
|
|
22
|
+
import { Buffer } from "node:buffer";
|
|
23
23
|
import { coerce, gte, minVersion } from "semver";
|
|
24
|
-
//#region \0rolldown/runtime.js
|
|
25
|
-
var __create = Object.create;
|
|
26
|
-
var __defProp$1 = Object.defineProperty;
|
|
27
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
28
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
29
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
30
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
31
|
-
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
32
|
-
var __copyProps = (to, from, except, desc) => {
|
|
33
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
34
|
-
key = keys[i];
|
|
35
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp$1(to, key, {
|
|
36
|
-
get: ((k) => from[k]).bind(null, key),
|
|
37
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
return to;
|
|
41
|
-
};
|
|
42
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp$1(target, "default", {
|
|
43
|
-
value: mod,
|
|
44
|
-
enumerable: true
|
|
45
|
-
}) : target, mod));
|
|
46
|
-
//#endregion
|
|
47
|
-
//#region ../../node_modules/.pnpm/get-value@4.0.1/node_modules/get-value/dist/index.mjs
|
|
48
|
-
var __defProp = Object.defineProperty;
|
|
49
|
-
var __name = (target, value) => __defProp(target, "name", {
|
|
50
|
-
value,
|
|
51
|
-
configurable: true
|
|
52
|
-
});
|
|
53
|
-
var isObject = /* @__PURE__ */ __name((v) => v !== null && typeof v === "object", "isObject");
|
|
54
|
-
var join = /* @__PURE__ */ __name((segs, joinChar, options) => {
|
|
55
|
-
if (typeof options.join === "function") return options.join(segs);
|
|
56
|
-
return segs[0] + joinChar + segs[1];
|
|
57
|
-
}, "join");
|
|
58
|
-
var split = /* @__PURE__ */ __name((path, splitChar, options) => {
|
|
59
|
-
if (typeof options.split === "function") return options.split(path);
|
|
60
|
-
return path.split(splitChar);
|
|
61
|
-
}, "split");
|
|
62
|
-
var isValid = /* @__PURE__ */ __name((key, target = {}, options) => {
|
|
63
|
-
if (typeof options?.isValid === "function") return options.isValid(key, target);
|
|
64
|
-
return true;
|
|
65
|
-
}, "isValid");
|
|
66
|
-
var isValidObject = /* @__PURE__ */ __name((v) => {
|
|
67
|
-
return isObject(v) || typeof v === "function";
|
|
68
|
-
}, "isValidObject");
|
|
69
|
-
var index_default = /* @__PURE__ */ __name((target, path, options = {}) => {
|
|
70
|
-
if (!isObject(options)) options = { default: options };
|
|
71
|
-
if (!isValidObject(target)) return typeof options.default !== "undefined" ? options.default : target;
|
|
72
|
-
if (typeof path === "number") path = String(path);
|
|
73
|
-
const pathIsArray = Array.isArray(path);
|
|
74
|
-
const pathIsString = typeof path === "string";
|
|
75
|
-
const splitChar = options.separator || ".";
|
|
76
|
-
const joinChar = options.joinChar || (typeof splitChar === "string" ? splitChar : ".");
|
|
77
|
-
if (!pathIsString && !pathIsArray) return target;
|
|
78
|
-
if (target[path] !== void 0) return isValid(path, target, options) ? target[path] : options.default;
|
|
79
|
-
const segs = pathIsArray ? path : split(path, splitChar, options);
|
|
80
|
-
const len = segs.length;
|
|
81
|
-
let idx = 0;
|
|
82
|
-
do {
|
|
83
|
-
let prop = segs[idx];
|
|
84
|
-
if (typeof prop !== "string") prop = String(prop);
|
|
85
|
-
while (prop && prop.slice(-1) === "\\") prop = join([prop.slice(0, -1), segs[++idx] || ""], joinChar, options);
|
|
86
|
-
if (target[prop] !== void 0) {
|
|
87
|
-
if (!isValid(prop, target, options)) return options.default;
|
|
88
|
-
target = target[prop];
|
|
89
|
-
} else {
|
|
90
|
-
let hasProp = false;
|
|
91
|
-
let n = idx + 1;
|
|
92
|
-
while (n < len) {
|
|
93
|
-
prop = join([prop, segs[n++]], joinChar, options);
|
|
94
|
-
if (hasProp = target[prop] !== void 0) {
|
|
95
|
-
if (!isValid(prop, target, options)) return options.default;
|
|
96
|
-
target = target[prop];
|
|
97
|
-
idx = n - 1;
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (!hasProp) return options.default;
|
|
102
|
-
}
|
|
103
|
-
} while (++idx < len && isValidObject(target));
|
|
104
|
-
if (idx === len) return target;
|
|
105
|
-
return options.default;
|
|
106
|
-
}, "getValue");
|
|
107
|
-
/*!
|
|
108
|
-
* get-value <https://github.com/jonschlinkert/get-value>
|
|
109
|
-
*
|
|
110
|
-
* Copyright (c) 2014-present, Jon Schlinkert.
|
|
111
|
-
* Released under the MIT License.
|
|
112
|
-
*/
|
|
113
|
-
//#endregion
|
|
114
24
|
//#region src/core/git.ts
|
|
115
25
|
/**
|
|
116
26
|
* 对 simple-git 的轻量封装,集中处理配置缓存与常用查询。
|
|
@@ -145,8 +55,8 @@ var GitClient = class {
|
|
|
145
55
|
* 解析 remote.origin.url,返回 git-url-parse 的结构,便于获取仓库元信息。
|
|
146
56
|
*/
|
|
147
57
|
async getGitUrl() {
|
|
148
|
-
const x =
|
|
149
|
-
if (x) return gitUrlParse(x);
|
|
58
|
+
const x = (await this.getConfig())["remote.origin.url"];
|
|
59
|
+
if (typeof x === "string") return gitUrlParse(x);
|
|
150
60
|
}
|
|
151
61
|
/**
|
|
152
62
|
* 组合 owner/name,生成常用的仓库名表达。
|
|
@@ -161,8 +71,8 @@ var GitClient = class {
|
|
|
161
71
|
async getUser() {
|
|
162
72
|
const config = await this.getConfig();
|
|
163
73
|
return {
|
|
164
|
-
name:
|
|
165
|
-
email:
|
|
74
|
+
name: config["user.name"],
|
|
75
|
+
email: config["user.email"]
|
|
166
76
|
};
|
|
167
77
|
}
|
|
168
78
|
/**
|
|
@@ -256,6 +166,90 @@ async function getWorkspaceData(cwd, options) {
|
|
|
256
166
|
};
|
|
257
167
|
}
|
|
258
168
|
//#endregion
|
|
169
|
+
//#region src/utils/fs.ts
|
|
170
|
+
function stringifyJson(data, options) {
|
|
171
|
+
return JSON.stringify(data, void 0, options?.spaces);
|
|
172
|
+
}
|
|
173
|
+
async function ensureParentDir(targetPath) {
|
|
174
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
175
|
+
}
|
|
176
|
+
async function pathExists(targetPath) {
|
|
177
|
+
try {
|
|
178
|
+
await access(targetPath);
|
|
179
|
+
return true;
|
|
180
|
+
} catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const exists = pathExists;
|
|
185
|
+
async function ensureDir(targetPath) {
|
|
186
|
+
await mkdir(targetPath, { recursive: true });
|
|
187
|
+
}
|
|
188
|
+
async function ensureFile(targetPath) {
|
|
189
|
+
await ensureParentDir(targetPath);
|
|
190
|
+
await (await open(targetPath, "a")).close();
|
|
191
|
+
}
|
|
192
|
+
async function remove(targetPath) {
|
|
193
|
+
await rm(targetPath, {
|
|
194
|
+
recursive: true,
|
|
195
|
+
force: true,
|
|
196
|
+
maxRetries: 3,
|
|
197
|
+
retryDelay: 100
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
async function copy(sourcePath, targetPath) {
|
|
201
|
+
await cp(sourcePath, targetPath, {
|
|
202
|
+
recursive: true,
|
|
203
|
+
force: true
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function outputFile(targetPath, data, options) {
|
|
207
|
+
await ensureParentDir(targetPath);
|
|
208
|
+
await writeFile(targetPath, data, options);
|
|
209
|
+
}
|
|
210
|
+
async function readJson(targetPath) {
|
|
211
|
+
return JSON.parse(await readFile(targetPath, "utf8"));
|
|
212
|
+
}
|
|
213
|
+
const readJSON = readJson;
|
|
214
|
+
async function writeJson(targetPath, data, options) {
|
|
215
|
+
await writeFile(targetPath, stringifyJson(data, options), "utf8");
|
|
216
|
+
}
|
|
217
|
+
const writeJSON = writeJson;
|
|
218
|
+
async function outputJson(targetPath, data, options) {
|
|
219
|
+
await ensureParentDir(targetPath);
|
|
220
|
+
await writeJson(targetPath, data, options);
|
|
221
|
+
}
|
|
222
|
+
const outputJSON = outputJson;
|
|
223
|
+
/**
|
|
224
|
+
* 判断是否为可忽略的文件系统错误。
|
|
225
|
+
* - ENOENT: 文件已被删除
|
|
226
|
+
* - EBUSY: Windows 中资源被系统占用
|
|
227
|
+
*/
|
|
228
|
+
function isIgnorableFsError(error) {
|
|
229
|
+
if (!error) return false;
|
|
230
|
+
const code = error.code;
|
|
231
|
+
return code === "ENOENT" || code === "EBUSY" || code === "EEXIST";
|
|
232
|
+
}
|
|
233
|
+
const fs = {
|
|
234
|
+
copy,
|
|
235
|
+
ensureDir,
|
|
236
|
+
ensureFile,
|
|
237
|
+
exists,
|
|
238
|
+
mkdtemp,
|
|
239
|
+
outputFile,
|
|
240
|
+
outputJSON,
|
|
241
|
+
outputJson,
|
|
242
|
+
pathExists,
|
|
243
|
+
readFile,
|
|
244
|
+
readJSON,
|
|
245
|
+
readJson,
|
|
246
|
+
remove,
|
|
247
|
+
stat,
|
|
248
|
+
writeFile,
|
|
249
|
+
writeJSON,
|
|
250
|
+
writeJson
|
|
251
|
+
};
|
|
252
|
+
//#endregion
|
|
259
253
|
//#region src/core/logger.ts
|
|
260
254
|
/**
|
|
261
255
|
* 统一的日志实例,便于在命令行中输出带有前缀和颜色的消息。
|
|
@@ -332,169 +326,126 @@ async function generateAgenticTemplates(tasks, defaults = {}) {
|
|
|
332
326
|
return results;
|
|
333
327
|
}
|
|
334
328
|
//#endregion
|
|
335
|
-
//#region
|
|
336
|
-
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
* Copyright (c) 2014-present, Jon Schlinkert.
|
|
340
|
-
* Released under the MIT License.
|
|
329
|
+
//#region src/utils/github.ts
|
|
330
|
+
/**
|
|
331
|
+
* 将 Issue 模版里的 discussions 链接同步为当前仓库的 discussions 地址。
|
|
341
332
|
*/
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
333
|
+
function updateIssueTemplateConfig(source, repoName) {
|
|
334
|
+
if (!repoName) return source;
|
|
335
|
+
let doc;
|
|
336
|
+
try {
|
|
337
|
+
doc = parseDocument(source);
|
|
338
|
+
} catch {
|
|
339
|
+
return source;
|
|
340
|
+
}
|
|
341
|
+
const contactLinks = doc.get("contact_links");
|
|
342
|
+
if (!isSeq(contactLinks)) return source;
|
|
343
|
+
const nextUrl = `https://github.com/${repoName}/discussions`;
|
|
344
|
+
let changed = false;
|
|
345
|
+
contactLinks.items.forEach((_, index) => {
|
|
346
|
+
const url = doc.getIn([
|
|
347
|
+
"contact_links",
|
|
348
|
+
index,
|
|
349
|
+
"url"
|
|
350
|
+
]);
|
|
351
|
+
if (typeof url !== "string") return;
|
|
352
|
+
if (!url.includes("/discussions")) return;
|
|
353
|
+
if (url === nextUrl) return;
|
|
354
|
+
doc.setIn([
|
|
355
|
+
"contact_links",
|
|
356
|
+
index,
|
|
357
|
+
"url"
|
|
358
|
+
], nextUrl);
|
|
359
|
+
changed = true;
|
|
360
|
+
});
|
|
361
|
+
if (!changed) return source;
|
|
362
|
+
return doc.toString();
|
|
363
|
+
}
|
|
348
364
|
//#endregion
|
|
349
|
-
//#region
|
|
350
|
-
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
* Copyright (c) 2014-2017, Jon Schlinkert.
|
|
354
|
-
* Released under the MIT License.
|
|
365
|
+
//#region src/utils/hash.ts
|
|
366
|
+
/**
|
|
367
|
+
* 生成给定二进制内容的 md5 摘要,用于快速比较文件内容。
|
|
355
368
|
*/
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
/*!
|
|
364
|
-
* is-plain-object <https://github.com/jonschlinkert/is-plain-object>
|
|
365
|
-
*
|
|
366
|
-
* Copyright (c) 2014-2017, Jon Schlinkert.
|
|
367
|
-
* Released under the MIT License.
|
|
369
|
+
function getFileHash(data) {
|
|
370
|
+
const hashSum = crypto.createHash("md5");
|
|
371
|
+
hashSum.update(data);
|
|
372
|
+
return hashSum.digest("hex");
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* 对比两个文件的 md5,如果不一致则认为内容有变化。
|
|
368
376
|
*/
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
377
|
+
function isFileChanged(src, dest) {
|
|
378
|
+
try {
|
|
379
|
+
return getFileHash(src) !== getFileHash(dest);
|
|
380
|
+
} catch (err) {
|
|
381
|
+
logger.error("Error calculating file hash:", err);
|
|
382
|
+
return false;
|
|
373
383
|
}
|
|
374
|
-
|
|
375
|
-
var ctor, prot;
|
|
376
|
-
if (isObjectObject(o) === false) return false;
|
|
377
|
-
ctor = o.constructor;
|
|
378
|
-
if (typeof ctor !== "function") return false;
|
|
379
|
-
prot = ctor.prototype;
|
|
380
|
-
if (isObjectObject(prot) === false) return false;
|
|
381
|
-
if (prot.hasOwnProperty("isPrototypeOf") === false) return false;
|
|
382
|
-
return true;
|
|
383
|
-
};
|
|
384
|
-
}));
|
|
384
|
+
}
|
|
385
385
|
//#endregion
|
|
386
|
-
//#region
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const isUnsafeKey = (key) => {
|
|
401
|
-
return key === "__proto__" || key === "constructor" || key === "prototype";
|
|
402
|
-
};
|
|
403
|
-
const validateKey = (key) => {
|
|
404
|
-
if (!isPrimitive(key)) throw new TypeError("Object keys must be strings or symbols");
|
|
405
|
-
if (isUnsafeKey(key)) throw new Error(`Cannot set unsafe key: "${key}"`);
|
|
406
|
-
};
|
|
407
|
-
const toStringKey = (input) => {
|
|
408
|
-
return Array.isArray(input) ? input.flat().map(String).join(",") : input;
|
|
409
|
-
};
|
|
410
|
-
const createMemoKey = (input, options) => {
|
|
411
|
-
if (typeof input !== "string" || !options) return input;
|
|
412
|
-
let key = input + ";";
|
|
413
|
-
if (options.arrays !== void 0) key += `arrays=${options.arrays};`;
|
|
414
|
-
if (options.separator !== void 0) key += `separator=${options.separator};`;
|
|
415
|
-
if (options.split !== void 0) key += `split=${options.split};`;
|
|
416
|
-
if (options.merge !== void 0) key += `merge=${options.merge};`;
|
|
417
|
-
if (options.preservePaths !== void 0) key += `preservePaths=${options.preservePaths};`;
|
|
418
|
-
return key;
|
|
419
|
-
};
|
|
420
|
-
const memoize = (input, options, fn) => {
|
|
421
|
-
const key = toStringKey(options ? createMemoKey(input, options) : input);
|
|
422
|
-
validateKey(key);
|
|
423
|
-
const value = setValue.cache.get(key) || fn();
|
|
424
|
-
setValue.cache.set(key, value);
|
|
425
|
-
return value;
|
|
426
|
-
};
|
|
427
|
-
const splitString = (input, options = {}) => {
|
|
428
|
-
const sep = options.separator || ".";
|
|
429
|
-
const preserve = sep === "/" ? false : options.preservePaths;
|
|
430
|
-
if (typeof input === "string" && preserve !== false && /\//.test(input)) return [input];
|
|
431
|
-
const parts = [];
|
|
432
|
-
let part = "";
|
|
433
|
-
const push = (part) => {
|
|
434
|
-
let number;
|
|
435
|
-
if (part.trim() !== "" && Number.isInteger(number = Number(part))) parts.push(number);
|
|
436
|
-
else parts.push(part);
|
|
437
|
-
};
|
|
438
|
-
for (let i = 0; i < input.length; i++) {
|
|
439
|
-
const value = input[i];
|
|
440
|
-
if (value === "\\") {
|
|
441
|
-
part += input[++i];
|
|
442
|
-
continue;
|
|
443
|
-
}
|
|
444
|
-
if (value === sep) {
|
|
445
|
-
push(part);
|
|
446
|
-
part = "";
|
|
447
|
-
continue;
|
|
448
|
-
}
|
|
449
|
-
part += value;
|
|
386
|
+
//#region src/utils/object.ts
|
|
387
|
+
const arrayIndexPattern = /^\d+$/;
|
|
388
|
+
function isArrayIndex(value) {
|
|
389
|
+
return arrayIndexPattern.test(value);
|
|
390
|
+
}
|
|
391
|
+
function parsePath(input) {
|
|
392
|
+
const segments = [];
|
|
393
|
+
let current = "";
|
|
394
|
+
let escaped = false;
|
|
395
|
+
for (const char of input) {
|
|
396
|
+
if (escaped) {
|
|
397
|
+
current += char;
|
|
398
|
+
escaped = false;
|
|
399
|
+
continue;
|
|
450
400
|
}
|
|
451
|
-
if (
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
const split = (input, options) => {
|
|
455
|
-
if (options && typeof options.split === "function") return options.split(input);
|
|
456
|
-
if (typeof input === "symbol") return [input];
|
|
457
|
-
if (Array.isArray(input)) return input;
|
|
458
|
-
return memoize(input, options, () => splitString(input, options));
|
|
459
|
-
};
|
|
460
|
-
const assignProp = (obj, prop, value, options) => {
|
|
461
|
-
validateKey(prop);
|
|
462
|
-
if (value === void 0) deleteProperty(obj, prop);
|
|
463
|
-
else if (options && options.merge) {
|
|
464
|
-
const merge = options.merge === "function" ? options.merge : Object.assign;
|
|
465
|
-
if (merge && isPlainObject(obj[prop]) && isPlainObject(value)) obj[prop] = merge(obj[prop], value);
|
|
466
|
-
else obj[prop] = value;
|
|
467
|
-
} else obj[prop] = value;
|
|
468
|
-
return obj;
|
|
469
|
-
};
|
|
470
|
-
const setValue = (target, path, value, options) => {
|
|
471
|
-
if (!path || !isObject(target)) return target;
|
|
472
|
-
const keys = split(path, options);
|
|
473
|
-
let obj = target;
|
|
474
|
-
for (let i = 0; i < keys.length; i++) {
|
|
475
|
-
const key = keys[i];
|
|
476
|
-
const next = keys[i + 1];
|
|
477
|
-
validateKey(key);
|
|
478
|
-
if (next === void 0) {
|
|
479
|
-
assignProp(obj, key, value, options);
|
|
480
|
-
break;
|
|
481
|
-
}
|
|
482
|
-
if (typeof next === "number" && !Array.isArray(obj[key])) {
|
|
483
|
-
obj = obj[key] = [];
|
|
484
|
-
continue;
|
|
485
|
-
}
|
|
486
|
-
if (!isObject(obj[key])) obj[key] = {};
|
|
487
|
-
obj = obj[key];
|
|
401
|
+
if (char === "\\") {
|
|
402
|
+
escaped = true;
|
|
403
|
+
continue;
|
|
488
404
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
|
|
405
|
+
if (char === ".") {
|
|
406
|
+
segments.push(current);
|
|
407
|
+
current = "";
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
current += char;
|
|
411
|
+
}
|
|
412
|
+
segments.push(current);
|
|
413
|
+
return segments;
|
|
414
|
+
}
|
|
415
|
+
function setByPath(target, path, value) {
|
|
416
|
+
const segments = typeof path === "string" ? parsePath(path) : [...path];
|
|
417
|
+
if (!segments.length) return target;
|
|
418
|
+
let current = target;
|
|
419
|
+
for (let index = 0; index < segments.length - 1; index++) {
|
|
420
|
+
const segment = segments[index];
|
|
421
|
+
const nextSegment = segments[index + 1];
|
|
422
|
+
const key = Array.isArray(current) && isArrayIndex(segment) ? Number(segment) : segment;
|
|
423
|
+
const existing = current[key];
|
|
424
|
+
if (typeof existing !== "object" || existing === null) current[key] = isArrayIndex(nextSegment) ? [] : {};
|
|
425
|
+
current = current[key];
|
|
426
|
+
}
|
|
427
|
+
const lastSegment = segments.at(-1);
|
|
428
|
+
const lastKey = Array.isArray(current) && isArrayIndex(lastSegment) ? Number(lastSegment) : lastSegment;
|
|
429
|
+
current[lastKey] = value;
|
|
430
|
+
return target;
|
|
431
|
+
}
|
|
432
|
+
//#endregion
|
|
433
|
+
//#region src/utils/regexp.ts
|
|
434
|
+
const regexpSpecialCharacterPattern = /[|\\{}()[\]^$+*?.]/g;
|
|
435
|
+
const hyphenPattern = /-/g;
|
|
436
|
+
/**
|
|
437
|
+
* 逃逸正则表达式中所有特殊字符,避免被当做模式解析。
|
|
438
|
+
*/
|
|
439
|
+
function escapeStringRegexp(str) {
|
|
440
|
+
return str.replace(regexpSpecialCharacterPattern, "\\$&").replace(hyphenPattern, "\\x2d");
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* 判断字符串是否命中任意一个正则,用于过滤要同步的资产文件。
|
|
444
|
+
*/
|
|
445
|
+
function isMatch(str, arr) {
|
|
446
|
+
for (const reg of arr) if (reg.test(str)) return true;
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
498
449
|
//#endregion
|
|
499
450
|
//#region src/core/config.ts
|
|
500
451
|
/**
|
|
@@ -542,7 +493,7 @@ async function resolveCommandConfig(name, cwd) {
|
|
|
542
493
|
//#endregion
|
|
543
494
|
//#region package.json
|
|
544
495
|
var name = "@icebreakers/monorepo";
|
|
545
|
-
var version = "3.2.
|
|
496
|
+
var version = "3.2.16";
|
|
546
497
|
//#endregion
|
|
547
498
|
//#region src/constants.ts
|
|
548
499
|
/**
|
|
@@ -604,7 +555,6 @@ async function syncSkills(options = {}) {
|
|
|
604
555
|
}
|
|
605
556
|
//#endregion
|
|
606
557
|
//#region src/commands/clean.ts
|
|
607
|
-
var import_set_value = /* @__PURE__ */ __toESM(require_set_value(), 1);
|
|
608
558
|
function mergeCleanConfig(base, overrides) {
|
|
609
559
|
const normalizedBase = base ?? {};
|
|
610
560
|
if (!overrides) return normalizedBase;
|
|
@@ -658,7 +608,7 @@ async function cleanProjects(cwd, overrides) {
|
|
|
658
608
|
}));
|
|
659
609
|
const name = path.resolve(workspaceDir, "package.json");
|
|
660
610
|
const pkgJson = await fs.readJson(name);
|
|
661
|
-
(
|
|
611
|
+
setByPath(pkgJson, "devDependencies.@icebreakers/monorepo", cleanConfig?.pinnedVersion ?? "latest");
|
|
662
612
|
await fs.outputJson(name, pkgJson, { spaces: 2 });
|
|
663
613
|
}
|
|
664
614
|
//#endregion
|
|
@@ -765,7 +715,7 @@ async function applyGitMetadata(pkgJson, repoDir, targetDir) {
|
|
|
765
715
|
const git = new GitClient({ baseDir: repoDir });
|
|
766
716
|
const repoName = await git.getRepoName();
|
|
767
717
|
if (!repoName) return;
|
|
768
|
-
(
|
|
718
|
+
setByPath(pkgJson, ["bugs", "url"], `https://github.com/${repoName}/issues`);
|
|
769
719
|
const repository = {
|
|
770
720
|
type: "git",
|
|
771
721
|
url: `git+https://github.com/${repoName}.git`
|
|
@@ -773,9 +723,9 @@ async function applyGitMetadata(pkgJson, repoDir, targetDir) {
|
|
|
773
723
|
const directoryBase = await git.getRepoRoot() ?? repoDir;
|
|
774
724
|
const relative = path.relative(directoryBase, targetDir);
|
|
775
725
|
if (relative && relative !== ".") repository.directory = relative.split(path.sep).join("/");
|
|
776
|
-
(
|
|
726
|
+
setByPath(pkgJson, "repository", repository);
|
|
777
727
|
const gitUser = await git.getUser();
|
|
778
|
-
if (gitUser?.name && gitUser?.email) (
|
|
728
|
+
if (gitUser?.name && gitUser?.email) setByPath(pkgJson, "author", `${gitUser.name} <${gitUser.email}>`);
|
|
779
729
|
} catch {}
|
|
780
730
|
}
|
|
781
731
|
/**
|
|
@@ -808,8 +758,8 @@ async function createNewProject(options) {
|
|
|
808
758
|
});
|
|
809
759
|
if (hasPackageJson) {
|
|
810
760
|
const sourceJson = await fs.readJson(sourceJsonPath);
|
|
811
|
-
(
|
|
812
|
-
(
|
|
761
|
+
setByPath(sourceJson, "version", "0.0.0");
|
|
762
|
+
setByPath(sourceJson, "name", name?.startsWith("@") ? name : path.basename(targetName));
|
|
813
763
|
await applyGitMetadata(sourceJson, cwd, to);
|
|
814
764
|
await fs.outputJson(path.resolve(to, renameJson ? "package.mock.json" : "package.json"), sourceJson, { spaces: 2 });
|
|
815
765
|
}
|
|
@@ -847,99 +797,13 @@ async function setChangeset_default(ctx) {
|
|
|
847
797
|
if (await fs.exists(changesetConfigPath)) {
|
|
848
798
|
const changesetConfig = await fs.readJson(changesetConfigPath);
|
|
849
799
|
if (gitUrl.full_name) {
|
|
850
|
-
(
|
|
800
|
+
setByPath(changesetConfig, "changelog.1.repo", gitUrl.full_name);
|
|
851
801
|
await fs.outputJson(changesetConfigPath, changesetConfig, { spaces: 2 });
|
|
852
802
|
}
|
|
853
803
|
}
|
|
854
804
|
}
|
|
855
805
|
}
|
|
856
806
|
//#endregion
|
|
857
|
-
//#region src/utils/fs.ts
|
|
858
|
-
/**
|
|
859
|
-
* 判断是否为可忽略的文件系统错误。
|
|
860
|
-
* - ENOENT: 文件已被删除
|
|
861
|
-
* - EBUSY: Windows 中资源被系统占用
|
|
862
|
-
*/
|
|
863
|
-
function isIgnorableFsError(error) {
|
|
864
|
-
if (!error) return false;
|
|
865
|
-
const code = error.code;
|
|
866
|
-
return code === "ENOENT" || code === "EBUSY" || code === "EEXIST";
|
|
867
|
-
}
|
|
868
|
-
//#endregion
|
|
869
|
-
//#region src/utils/github.ts
|
|
870
|
-
/**
|
|
871
|
-
* 将 Issue 模版里的 discussions 链接同步为当前仓库的 discussions 地址。
|
|
872
|
-
*/
|
|
873
|
-
function updateIssueTemplateConfig(source, repoName) {
|
|
874
|
-
if (!repoName) return source;
|
|
875
|
-
let doc;
|
|
876
|
-
try {
|
|
877
|
-
doc = parseDocument(source);
|
|
878
|
-
} catch {
|
|
879
|
-
return source;
|
|
880
|
-
}
|
|
881
|
-
const contactLinks = doc.get("contact_links");
|
|
882
|
-
if (!isSeq(contactLinks)) return source;
|
|
883
|
-
const nextUrl = `https://github.com/${repoName}/discussions`;
|
|
884
|
-
let changed = false;
|
|
885
|
-
contactLinks.items.forEach((_, index) => {
|
|
886
|
-
const url = doc.getIn([
|
|
887
|
-
"contact_links",
|
|
888
|
-
index,
|
|
889
|
-
"url"
|
|
890
|
-
]);
|
|
891
|
-
if (typeof url !== "string") return;
|
|
892
|
-
if (!url.includes("/discussions")) return;
|
|
893
|
-
if (url === nextUrl) return;
|
|
894
|
-
doc.setIn([
|
|
895
|
-
"contact_links",
|
|
896
|
-
index,
|
|
897
|
-
"url"
|
|
898
|
-
], nextUrl);
|
|
899
|
-
changed = true;
|
|
900
|
-
});
|
|
901
|
-
if (!changed) return source;
|
|
902
|
-
return doc.toString();
|
|
903
|
-
}
|
|
904
|
-
//#endregion
|
|
905
|
-
//#region src/utils/hash.ts
|
|
906
|
-
/**
|
|
907
|
-
* 生成给定二进制内容的 md5 摘要,用于快速比较文件内容。
|
|
908
|
-
*/
|
|
909
|
-
function getFileHash(data) {
|
|
910
|
-
const hashSum = crypto.createHash("md5");
|
|
911
|
-
hashSum.update(data);
|
|
912
|
-
return hashSum.digest("hex");
|
|
913
|
-
}
|
|
914
|
-
/**
|
|
915
|
-
* 对比两个文件的 md5,如果不一致则认为内容有变化。
|
|
916
|
-
*/
|
|
917
|
-
function isFileChanged(src, dest) {
|
|
918
|
-
try {
|
|
919
|
-
return getFileHash(src) !== getFileHash(dest);
|
|
920
|
-
} catch (err) {
|
|
921
|
-
logger.error("Error calculating file hash:", err);
|
|
922
|
-
return false;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
//#endregion
|
|
926
|
-
//#region src/utils/regexp.ts
|
|
927
|
-
const regexpSpecialCharacterPattern = /[|\\{}()[\]^$+*?.]/g;
|
|
928
|
-
const hyphenPattern = /-/g;
|
|
929
|
-
/**
|
|
930
|
-
* 逃逸正则表达式中所有特殊字符,避免被当做模式解析。
|
|
931
|
-
*/
|
|
932
|
-
function escapeStringRegexp(str) {
|
|
933
|
-
return str.replace(regexpSpecialCharacterPattern, "\\$&").replace(hyphenPattern, "\\x2d");
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
|
-
* 判断字符串是否命中任意一个正则,用于过滤要同步的资产文件。
|
|
937
|
-
*/
|
|
938
|
-
function isMatch(str, arr) {
|
|
939
|
-
for (const reg of arr) if (reg.test(str)) return true;
|
|
940
|
-
return false;
|
|
941
|
-
}
|
|
942
|
-
//#endregion
|
|
943
807
|
//#region src/commands/init/setIssueTemplateConfig.ts
|
|
944
808
|
/**
|
|
945
809
|
* 同步 Issue 模版里的 discussions 链接到当前仓库。
|
|
@@ -965,14 +829,14 @@ async function setPkgJson_default(ctx) {
|
|
|
965
829
|
if (!await fs.pathExists(pkg.pkgJsonPath)) return;
|
|
966
830
|
const pkgJson = JSON.parse(JSON.stringify(pkg.manifest));
|
|
967
831
|
const directory = path.relative(cwd, pkg.rootDir);
|
|
968
|
-
(
|
|
832
|
+
setByPath(pkgJson, ["bugs", "url"], `https://github.com/${gitUrl.full_name}/issues`);
|
|
969
833
|
const repository = {
|
|
970
834
|
type: "git",
|
|
971
835
|
url: `git+https://github.com/${gitUrl.full_name}.git`
|
|
972
836
|
};
|
|
973
837
|
if (directory) repository.directory = directory;
|
|
974
|
-
(
|
|
975
|
-
if (gitUser?.name && gitUser?.email) (
|
|
838
|
+
setByPath(pkgJson, "repository", repository);
|
|
839
|
+
if (gitUser?.name && gitUser?.email) setByPath(pkgJson, "author", `${gitUser.name} <${gitUser.email}>`);
|
|
976
840
|
const nextContent = `${JSON.stringify(pkgJson, void 0, 2)}\n`;
|
|
977
841
|
if (await fs.readFile(pkg.pkgJsonPath, "utf8") !== nextContent) await fs.writeFile(pkg.pkgJsonPath, nextContent, "utf8");
|
|
978
842
|
}));
|
|
@@ -1078,7 +942,7 @@ function setMirror(obj, envs = chinaMirrorsEnvs) {
|
|
|
1078
942
|
"osx"
|
|
1079
943
|
];
|
|
1080
944
|
const prefix = "terminal.integrated.env";
|
|
1081
|
-
if (typeof obj === "object" && obj) for (const platform of platforms) (
|
|
945
|
+
if (typeof obj === "object" && obj) for (const platform of platforms) setByPath(obj, [prefix, platform].join(".").replaceAll(".", "\\."), envs);
|
|
1082
946
|
}
|
|
1083
947
|
//#endregion
|
|
1084
948
|
//#region src/commands/mirror/binaryMirror.ts
|
|
@@ -1230,7 +1094,7 @@ function isAgentsMarkdownEquivalent(left, right) {
|
|
|
1230
1094
|
//#endregion
|
|
1231
1095
|
//#region src/commands/upgrade/overwrite.ts
|
|
1232
1096
|
function asBuffer(data) {
|
|
1233
|
-
return typeof data === "string" ? Buffer
|
|
1097
|
+
return typeof data === "string" ? Buffer.from(data) : data;
|
|
1234
1098
|
}
|
|
1235
1099
|
async function evaluateWriteIntent(targetPath, options) {
|
|
1236
1100
|
const { skipOverwrite, source } = options;
|
|
@@ -1578,7 +1442,7 @@ async function upgradeMonorepo(opts) {
|
|
|
1578
1442
|
}
|
|
1579
1443
|
if (relPath === ".changeset/config.json" && repoName) {
|
|
1580
1444
|
const changesetJson = await fs.readJson(file.path);
|
|
1581
|
-
(
|
|
1445
|
+
setByPath(changesetJson, "changelog.1.repo", repoName);
|
|
1582
1446
|
const data = `${JSON.stringify(changesetJson, void 0, 2)}\n`;
|
|
1583
1447
|
const intent = await evaluateWriteIntent(targetPath, buildWriteIntentOptions(data));
|
|
1584
1448
|
const action = async () => {
|
|
@@ -1646,4 +1510,4 @@ async function upgradeMonorepo(opts) {
|
|
|
1646
1510
|
await flushPendingOverwrites(pendingOverwrites);
|
|
1647
1511
|
}
|
|
1648
1512
|
//#endregion
|
|
1649
|
-
export {
|
|
1513
|
+
export { getWorkspaceData as $, toPublishGitignorePath as A, ensureDir as B, resolveCommandConfig as C, getFileHash as D, setByPath as E, generateAgenticTemplate as F, outputJSON as G, exists as H, generateAgenticTemplates as I, readJSON as J, outputJson as K, loadAgenticTasks as L, updateIssueTemplateConfig as M, createTimestampFolderName as N, isFileChanged as O, defaultAgenticBaseDir as P, writeJson as Q, logger as R, loadMonorepoConfig as S, isMatch as T, isIgnorableFsError as U, ensureFile as V, outputFile as W, remove as X, readJson as Y, writeJSON as Z, rootDir as _, createContext as a, version as b, getCreateChoices as c, cleanProjects as d, getWorkspacePackages as et, getSkillTargetPaths as f, packageDir as g, assetsDir as h, init as i, toWorkspaceGitignorePath as j, isGitignoreFile as k, getTemplateMap as l, syncSkills as m, syncNpmMirror as n, createNewProject as o, skillTargets as p, pathExists as q, setVscodeBinaryMirror as r, defaultTemplate as s, upgradeMonorepo as t, GitClient as tt, templateMap as u, templatesDir as v, escapeStringRegexp as w, defineMonorepoConfig as x, name as y, copy as z };
|