tcdona_unilib 1.0.2 → 1.0.3
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/package.json +1 -1
- package/staticMeta/idupdate.ts +302 -262
package/package.json
CHANGED
package/staticMeta/idupdate.ts
CHANGED
|
@@ -2,60 +2,76 @@
|
|
|
2
2
|
* 自动更新所有 fileInit() 调用的路径参数为正确的相对路径
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { fileURLToPath } from
|
|
6
|
-
import { err, ok } from
|
|
7
|
-
import type { EffContext } from
|
|
8
|
-
import { Eff } from
|
|
9
|
-
import type { Result } from
|
|
10
|
-
import type { SgNode } from
|
|
11
|
-
import { fileInit } from
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
import { fileURLToPath } from "node:url"
|
|
6
|
+
import { err, ok } from "neverthrow"
|
|
7
|
+
import type { EffContext } from "./eff.js"
|
|
8
|
+
import { Eff } from "./eff.js"
|
|
9
|
+
import type { Result } from "neverthrow"
|
|
10
|
+
import type { SgNode } from "@ast-grep/napi"
|
|
11
|
+
import { fileInit } from "./enum.api.js"
|
|
12
|
+
import {
|
|
13
|
+
extractArgumentValue,
|
|
14
|
+
findFunctionCalls,
|
|
15
|
+
hasImport,
|
|
16
|
+
replaceRanges,
|
|
17
|
+
} from "./ast.js"
|
|
18
|
+
import {
|
|
19
|
+
getRelativePath,
|
|
20
|
+
parseFileAsync,
|
|
21
|
+
readFileAsync,
|
|
22
|
+
scanTypeScriptFilesAsync,
|
|
23
|
+
writeFileAsync,
|
|
24
|
+
} from "./ast.scan.js"
|
|
25
|
+
|
|
26
|
+
type NonEmptyArray<T> = readonly [T, ...T[]]
|
|
16
27
|
|
|
17
28
|
// ==================== 日志配置 ====================
|
|
18
29
|
|
|
19
|
-
const fileId = fileInit(
|
|
30
|
+
const fileId = fileInit("staticMeta/idupdate.ts")
|
|
20
31
|
|
|
21
32
|
// ==================== 类型定义 ====================
|
|
22
33
|
|
|
23
|
-
type UpdateError
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
type UpdateError =
|
|
35
|
+
| { type: "VALIDATION_FILENAME_FAILED"; message: string }
|
|
36
|
+
| { type: "FILE_ERROR"; message: string; filePath: string }
|
|
26
37
|
|
|
27
38
|
interface StaticMetaCallInfo {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
startOffset: number
|
|
40
|
+
endOffset: number
|
|
41
|
+
currentValue: string | null
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
interface InvalidCall {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
reason: "NO_ARGUMENT" | "VARIABLE_ARGUMENT" | "TEMPLATE_STRING"
|
|
46
|
+
line: number
|
|
47
|
+
text: string
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
// ==================== 初始化 ====================
|
|
40
51
|
|
|
41
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
52
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
42
53
|
|
|
43
54
|
// ==================== 验证 ====================
|
|
44
55
|
|
|
45
|
-
function validate(): {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
function validate(): {
|
|
57
|
+
isErr: () => boolean
|
|
58
|
+
isOk: () => boolean
|
|
59
|
+
value?: void
|
|
60
|
+
error?: UpdateError
|
|
61
|
+
} {
|
|
62
|
+
// 验证 staticMeta 功能
|
|
63
|
+
const currentFileAbsPath = fileURLToPath(import.meta.url)
|
|
64
|
+
const expectedRelativePath = getRelativePath(currentFileAbsPath)
|
|
65
|
+
|
|
66
|
+
const conf = fileId("validate1").conf
|
|
67
|
+
if (conf.srcFile !== expectedRelativePath) {
|
|
68
|
+
return err({
|
|
69
|
+
type: "VALIDATION_FILENAME_FAILED",
|
|
70
|
+
message: `__filename 不正确:\n 期望: ${expectedRelativePath}\n 实际: ${conf.srcFile}`,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return ok(undefined)
|
|
59
75
|
}
|
|
60
76
|
|
|
61
77
|
// ==================== 文件处理 ====================
|
|
@@ -64,120 +80,120 @@ function validate(): { isErr: () => boolean; isOk: () => boolean; value?: void;
|
|
|
64
80
|
* 找出文件中所有 fileInit 调用(有效和无效)
|
|
65
81
|
*/
|
|
66
82
|
function findFileInitCalls(
|
|
67
|
-
|
|
83
|
+
root: SgNode
|
|
68
84
|
): [StaticMetaCallInfo[], InvalidCall[]] {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
validCalls.push({
|
|
108
|
-
startOffset: range.start.index,
|
|
109
|
-
endOffset: range.end.index,
|
|
110
|
-
currentValue: value,
|
|
111
|
-
});
|
|
85
|
+
const validCalls: StaticMetaCallInfo[] = []
|
|
86
|
+
const invalidCalls: InvalidCall[] = []
|
|
87
|
+
|
|
88
|
+
const allCalls = findFunctionCalls(root, "fileInit")
|
|
89
|
+
|
|
90
|
+
for (const call of allCalls) {
|
|
91
|
+
const children = call.children()
|
|
92
|
+
if (children.length < 2) continue
|
|
93
|
+
|
|
94
|
+
const args = children[1]
|
|
95
|
+
|
|
96
|
+
if (args?.kind() !== "arguments") continue
|
|
97
|
+
|
|
98
|
+
const value = extractArgumentValue(args)
|
|
99
|
+
const range = call.range()
|
|
100
|
+
|
|
101
|
+
if (value === null) {
|
|
102
|
+
const argText = args.text()
|
|
103
|
+
let reason: InvalidCall["reason"] = "NO_ARGUMENT"
|
|
104
|
+
|
|
105
|
+
if (argText.includes("`")) {
|
|
106
|
+
reason = "TEMPLATE_STRING"
|
|
107
|
+
} else if (
|
|
108
|
+
argText.length > 2 &&
|
|
109
|
+
!argText.includes('"') &&
|
|
110
|
+
!argText.includes("'")
|
|
111
|
+
) {
|
|
112
|
+
reason = "VARIABLE_ARGUMENT"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
invalidCalls.push({
|
|
116
|
+
reason,
|
|
117
|
+
line: range.start.line,
|
|
118
|
+
text: call.text(),
|
|
119
|
+
})
|
|
120
|
+
continue
|
|
112
121
|
}
|
|
113
122
|
|
|
114
|
-
|
|
123
|
+
validCalls.push({
|
|
124
|
+
startOffset: range.start.index,
|
|
125
|
+
endOffset: range.end.index,
|
|
126
|
+
currentValue: value,
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return [validCalls, invalidCalls]
|
|
115
131
|
}
|
|
116
132
|
|
|
117
133
|
/**
|
|
118
134
|
* 更新文件中所有 fileInit 调用的路径参数
|
|
119
135
|
*/
|
|
120
136
|
async function updateStaticMeta(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
filePath: string,
|
|
138
|
+
content: string,
|
|
139
|
+
root: SgNode,
|
|
140
|
+
dryRun: boolean,
|
|
141
|
+
signal: AbortSignal
|
|
126
142
|
): Promise<{ isErr: boolean; error?: UpdateError; value?: boolean }> {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// 如果没有有效调用则跳过
|
|
151
|
-
if (validCalls.length === 0) {
|
|
152
|
-
return { isErr: false, value: false };
|
|
143
|
+
// 计算正确路径
|
|
144
|
+
const relativePath = getRelativePath(filePath)
|
|
145
|
+
|
|
146
|
+
// 检测导入 fileInit
|
|
147
|
+
if (!hasImport(root, "fileInit")) {
|
|
148
|
+
return { isErr: false, value: false }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 查找 fileInit 调用
|
|
152
|
+
const [validCalls, invalidCalls] = findFileInitCalls(root)
|
|
153
|
+
|
|
154
|
+
// 记录无效调用
|
|
155
|
+
if (invalidCalls.length > 0) {
|
|
156
|
+
fileId("invalidFileInitCalls").warn(
|
|
157
|
+
`⚠️ ${filePath} 包含无效的 fileInit 调用:`
|
|
158
|
+
)
|
|
159
|
+
for (const invalid of invalidCalls) {
|
|
160
|
+
fileId("invalidFileInitDetail").warn(
|
|
161
|
+
` L${invalid.line} [${invalid.reason}]: ${invalid.text}`
|
|
162
|
+
)
|
|
153
163
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 如果没有有效调用则跳过
|
|
167
|
+
if (validCalls.length === 0) {
|
|
168
|
+
return { isErr: false, value: false }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 替换内容
|
|
172
|
+
const updatedContent = replaceRanges(
|
|
173
|
+
content,
|
|
174
|
+
validCalls.map((call) => ({
|
|
175
|
+
startIndex: call.startOffset,
|
|
176
|
+
endIndex: call.endOffset,
|
|
177
|
+
replacement: `fileInit('${relativePath}')`,
|
|
178
|
+
}))
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
// 写入文件
|
|
182
|
+
if (!dryRun) {
|
|
183
|
+
const writeResult = await writeFileAsync(filePath, updatedContent, signal)
|
|
184
|
+
if (writeResult.isErr()) {
|
|
185
|
+
return {
|
|
186
|
+
isErr: true,
|
|
187
|
+
error: {
|
|
188
|
+
type: "FILE_ERROR",
|
|
189
|
+
message: `无法写入文件: ${writeResult.error.message}`,
|
|
190
|
+
filePath,
|
|
191
|
+
},
|
|
192
|
+
}
|
|
178
193
|
}
|
|
194
|
+
}
|
|
179
195
|
|
|
180
|
-
|
|
196
|
+
return { isErr: false, value: true }
|
|
181
197
|
}
|
|
182
198
|
|
|
183
199
|
// ==================== 更新文件执行器 ====================
|
|
@@ -186,39 +202,47 @@ async function updateStaticMeta(
|
|
|
186
202
|
* 为单个文件创建更新任务 executor
|
|
187
203
|
*/
|
|
188
204
|
function createUpdateExecutor(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
): (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
205
|
+
file: string,
|
|
206
|
+
dryRun: boolean
|
|
207
|
+
): (
|
|
208
|
+
ctx: EffContext
|
|
209
|
+
) => Promise<Result<{ updated: boolean; file: string } | null, UpdateError>> {
|
|
210
|
+
return async ({ signal: innerSignal }: EffContext) => {
|
|
211
|
+
if (innerSignal.aborted) {
|
|
212
|
+
return err({ type: "FILE_ERROR", message: "任务已取消", filePath: file })
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const readResult = await readFileAsync(file, innerSignal)
|
|
216
|
+
if (readResult.isErr()) {
|
|
217
|
+
return err({
|
|
218
|
+
type: "FILE_ERROR",
|
|
219
|
+
message: `无法读取文件: ${readResult.error.message}`,
|
|
220
|
+
filePath: file,
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const parseResult = await parseFileAsync(file, innerSignal)
|
|
225
|
+
if (parseResult.isErr()) {
|
|
226
|
+
return err({
|
|
227
|
+
type: "FILE_ERROR",
|
|
228
|
+
message: `AST 解析失败: ${parseResult.error.message}`,
|
|
229
|
+
filePath: file,
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const updateResult = await updateStaticMeta(
|
|
234
|
+
file,
|
|
235
|
+
readResult.value,
|
|
236
|
+
parseResult.value.root,
|
|
237
|
+
dryRun,
|
|
238
|
+
innerSignal
|
|
239
|
+
)
|
|
240
|
+
if (updateResult.isErr) {
|
|
241
|
+
return err(updateResult.error!)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return ok(updateResult.value ? { updated: true, file } : null)
|
|
245
|
+
}
|
|
222
246
|
}
|
|
223
247
|
|
|
224
248
|
// ==================== 结果处理与汇总 ====================
|
|
@@ -227,108 +251,124 @@ function createUpdateExecutor(
|
|
|
227
251
|
* 统计、分类并输出更新结果
|
|
228
252
|
*/
|
|
229
253
|
function collectAndPrintResults(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
254
|
+
results: Result<{ updated: boolean; file: string } | null, UpdateError>[],
|
|
255
|
+
files: string[],
|
|
256
|
+
dryRun: boolean
|
|
233
257
|
): { updatedFiles: string[]; errors: UpdateError[] } {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
258
|
+
const updatedFiles: string[] = []
|
|
259
|
+
const skippedFiles: string[] = []
|
|
260
|
+
const errors: UpdateError[] = []
|
|
261
|
+
|
|
262
|
+
for (let i = 0; i < results.length; i++) {
|
|
263
|
+
const result = results[i]
|
|
264
|
+
const file = files[i]
|
|
265
|
+
|
|
266
|
+
if (result.isErr()) {
|
|
267
|
+
errors.push(result.error)
|
|
268
|
+
} else if (result.value?.updated) {
|
|
269
|
+
updatedFiles.push(getRelativePath(result.value.file))
|
|
270
|
+
fileId("updateProgress").info(` ✓ ${getRelativePath(result.value.file)}`)
|
|
271
|
+
} else {
|
|
272
|
+
skippedFiles.push(getRelativePath(file))
|
|
250
273
|
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 打印汇总
|
|
277
|
+
fileId("summaryHeaderLine1").info(`\n${"=".repeat(60)}`)
|
|
278
|
+
fileId("summaryHeaderLine2").info("更新结果汇总")
|
|
279
|
+
fileId("summaryHeaderLine3").info(`${"=".repeat(60)}`)
|
|
280
|
+
|
|
281
|
+
if (updatedFiles.length > 0) {
|
|
282
|
+
fileId("updateSuccess").info(
|
|
283
|
+
`\n✅ ${dryRun ? "将更新" : "已更新"} ${updatedFiles.length} 个文件`
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (skippedFiles.length > 0) {
|
|
288
|
+
fileId("updateSkipped").info(
|
|
289
|
+
`⊘ 已跳过 ${skippedFiles.length} 个文件 (无 fileInit 调用)`
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (errors.length > 0) {
|
|
294
|
+
fileId("updateFailed").error(`\n❌ 更新失败 ${errors.length} 个文件:`)
|
|
295
|
+
for (const error of errors) {
|
|
296
|
+
const filePath = error.type === "FILE_ERROR" ? error.filePath : "(验证)"
|
|
297
|
+
const message = error.message
|
|
298
|
+
fileId("updateErrorPath").error(` ${filePath}`)
|
|
299
|
+
fileId("updateErrorMessage").error(` └─ ${message}`)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
251
302
|
|
|
252
|
-
|
|
253
|
-
fileId('summaryHeaderLine1').info(`\n${'='.repeat(60)}`);
|
|
254
|
-
fileId('summaryHeaderLine2').info('更新结果汇总');
|
|
255
|
-
fileId('summaryHeaderLine3').info(`${'='.repeat(60)}`);
|
|
303
|
+
fileId("summaryFooter").info(`${"=".repeat(60)}\n`)
|
|
256
304
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
`\n✅ ${dryRun ? '将更新' : '已更新'} ${updatedFiles.length} 个文件`,
|
|
260
|
-
);
|
|
261
|
-
}
|
|
305
|
+
return { updatedFiles, errors }
|
|
306
|
+
}
|
|
262
307
|
|
|
263
|
-
|
|
264
|
-
fileId('updateSkipped').info(
|
|
265
|
-
`⊘ 已跳过 ${skippedFiles.length} 个文件 (无 fileInit 调用)`,
|
|
266
|
-
);
|
|
267
|
-
}
|
|
308
|
+
// ==================== 主函数 ====================
|
|
268
309
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
310
|
+
const main = () => {
|
|
311
|
+
return Eff.root(async ({ signal }) => {
|
|
312
|
+
if (signal.aborted) return err(new Error("操作已取消"))
|
|
313
|
+
|
|
314
|
+
const args = process.argv.slice(2)
|
|
315
|
+
const dryRun = args.includes("--dry-run") || args.includes("-n")
|
|
316
|
+
|
|
317
|
+
// 验证 staticMeta 功能
|
|
318
|
+
// const validationResult = validate();
|
|
319
|
+
// if (validationResult.isErr?.()) {
|
|
320
|
+
// fileId('validateFailed').error(
|
|
321
|
+
// '验证失败:',
|
|
322
|
+
// (validationResult as any).error.message,
|
|
323
|
+
// );
|
|
324
|
+
// // 下面的这个 as any 会让类型推断中断,从而无法保证,return 后面的所有语句的推导正确性,再一次证明了 as any 要少用
|
|
325
|
+
// return err((validationResult as any).error);
|
|
326
|
+
// }
|
|
327
|
+
|
|
328
|
+
// 扫描文件
|
|
329
|
+
const files = await scanTypeScriptFilesAsync(["**/staticMeta/idupdate.ts"])
|
|
330
|
+
fileId("updateStart").info(
|
|
331
|
+
`开始更新 fileInit... (共 ${files.length} 个文件)\n`
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
if (files.length === 0) {
|
|
335
|
+
return ok(undefined)
|
|
277
336
|
}
|
|
278
337
|
|
|
279
|
-
|
|
338
|
+
// 创建并行更新任务
|
|
339
|
+
type UpdateResult = { updated: boolean; file: string } | null
|
|
340
|
+
const executors = files.map((file) =>
|
|
341
|
+
createUpdateExecutor(file, dryRun)
|
|
342
|
+
) as unknown as NonEmptyArray<
|
|
343
|
+
(ctx: EffContext) => Promise<Result<UpdateResult, UpdateError>>
|
|
344
|
+
>
|
|
280
345
|
|
|
281
|
-
|
|
282
|
-
|
|
346
|
+
// 执行并行更新
|
|
347
|
+
const allResults = await Eff.allSettled(executors, signal)
|
|
348
|
+
const results = allResults.isOk() ? allResults.value : []
|
|
283
349
|
|
|
284
|
-
//
|
|
350
|
+
// 统计结果并输出汇总
|
|
351
|
+
const { errors } = collectAndPrintResults(results, files, dryRun)
|
|
285
352
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
(validationResult as any).error.message,
|
|
299
|
-
);
|
|
300
|
-
return err((validationResult as any).error);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// 扫描文件
|
|
304
|
-
const files = await scanTypeScriptFilesAsync(['**/staticMeta/idupdate.ts']);
|
|
305
|
-
fileId('updateStart').info(`开始更新 fileInit... (共 ${files.length} 个文件)\n`);
|
|
306
|
-
|
|
307
|
-
if (files.length === 0) {
|
|
308
|
-
return ok(undefined);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// 创建并行更新任务
|
|
312
|
-
type UpdateResult = { updated: boolean; file: string } | null;
|
|
313
|
-
const executors = files.map((file) =>
|
|
314
|
-
createUpdateExecutor(file, dryRun),
|
|
315
|
-
) as unknown as NonEmptyArray<(ctx: EffContext) => Promise<Result<UpdateResult, UpdateError>>>;
|
|
316
|
-
|
|
317
|
-
// 执行并行更新
|
|
318
|
-
const allResults = await Eff.allSettled(executors, signal);
|
|
319
|
-
const results = allResults.isOk() ? allResults.value : [];
|
|
320
|
-
|
|
321
|
-
// 统计结果并输出汇总
|
|
322
|
-
const { errors } = collectAndPrintResults(results, files, dryRun);
|
|
323
|
-
|
|
324
|
-
return errors.length > 0 ? err(errors[0]) : ok(undefined);
|
|
325
|
-
});
|
|
326
|
-
};
|
|
353
|
+
if (errors.length > 0) {
|
|
354
|
+
const error = errors[0]
|
|
355
|
+
const message =
|
|
356
|
+
error.type === "FILE_ERROR"
|
|
357
|
+
? `${error.message} (${error.filePath})`
|
|
358
|
+
: error.message
|
|
359
|
+
return err(new Error(message))
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return ok(undefined)
|
|
363
|
+
})
|
|
364
|
+
}
|
|
327
365
|
|
|
328
366
|
main().match(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}
|
|
334
|
-
)
|
|
367
|
+
() => {
|
|
368
|
+
/* success */
|
|
369
|
+
},
|
|
370
|
+
(err) => {
|
|
371
|
+
fileId("scriptFailed").error(`脚本执行失败: ${err.message ?? String(err)}`)
|
|
372
|
+
process.exit(1)
|
|
373
|
+
}
|
|
374
|
+
)
|