feng3d-cli 0.1.1 → 0.1.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/.claude-skill/README.md +6 -0
- package/.claude-skill/SKILL.md +1 -1
- package/.claude-skill/skill.json +1 -1
- package/README.md +4 -1
- package/bin/cli.js +5 -1
- package/dist/__vite-browser-external-2Ng8QIWW.js +5 -0
- package/dist/__vite-browser-external-2Ng8QIWW.js.map +1 -0
- package/dist/index.js +435 -163
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +440 -163
- package/dist/index.umd.cjs.map +1 -1
- package/lib/commands/update.d.ts +3 -6
- package/lib/commands/update.d.ts.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/types.d.ts +75 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/utils/merge.d.ts +52 -0
- package/lib/utils/merge.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -93,9 +93,254 @@ function getSrcIndexTemplate(options) {
|
|
|
93
93
|
function getVitestConfigTemplate() {
|
|
94
94
|
return fs.readFileSync(path.join(TEMPLATES_DIR, "vitest.config.ts"), "utf-8");
|
|
95
95
|
}
|
|
96
|
-
async function
|
|
96
|
+
async function createProject(name, options) {
|
|
97
|
+
const projectDir = path.join(options.directory, name);
|
|
98
|
+
if (await fs.pathExists(projectDir)) {
|
|
99
|
+
throw new Error(`目录 ${projectDir} 已存在`);
|
|
100
|
+
}
|
|
101
|
+
await fs.ensureDir(projectDir);
|
|
102
|
+
await fs.ensureDir(path.join(projectDir, "src"));
|
|
103
|
+
console.log(chalk.gray(` 创建目录: ${projectDir}`));
|
|
104
|
+
const packageJson = createPackageJson(name, options);
|
|
105
|
+
await fs.writeJson(path.join(projectDir, "package.json"), packageJson, { spaces: 4 });
|
|
106
|
+
console.log(chalk.gray(" 创建: package.json"));
|
|
107
|
+
await fs.writeJson(path.join(projectDir, "tsconfig.json"), getTsconfigTemplate(), { spaces: 4 });
|
|
108
|
+
console.log(chalk.gray(" 创建: tsconfig.json"));
|
|
109
|
+
await fs.writeFile(path.join(projectDir, ".gitignore"), getGitignoreTemplate());
|
|
110
|
+
console.log(chalk.gray(" 创建: .gitignore"));
|
|
111
|
+
await fs.writeFile(path.join(projectDir, ".cursorrules"), getCursorrrulesTemplate());
|
|
112
|
+
console.log(chalk.gray(" 创建: .cursorrules"));
|
|
113
|
+
await fs.writeFile(path.join(projectDir, "eslint.config.js"), getEslintConfigTemplate());
|
|
114
|
+
console.log(chalk.gray(" 创建: eslint.config.js"));
|
|
115
|
+
const typedocConfig = getTypedocConfig({ repoName: name });
|
|
116
|
+
await fs.writeJson(path.join(projectDir, "typedoc.json"), typedocConfig, { spaces: 4 });
|
|
117
|
+
console.log(chalk.gray(" 创建: typedoc.json"));
|
|
118
|
+
await fs.writeFile(path.join(projectDir, "src/index.ts"), getSrcIndexTemplate({ name: `@feng3d/${name}` }));
|
|
119
|
+
console.log(chalk.gray(" 创建: src/index.ts"));
|
|
120
|
+
await fs.writeFile(path.join(projectDir, "README.md"), `# @feng3d/${name}
|
|
121
|
+
`);
|
|
122
|
+
console.log(chalk.gray(" 创建: README.md"));
|
|
123
|
+
if (options.examples !== false) {
|
|
124
|
+
await fs.ensureDir(path.join(projectDir, "examples"));
|
|
125
|
+
console.log(chalk.gray(" 创建: examples/"));
|
|
126
|
+
}
|
|
127
|
+
if (options.vitest !== false) {
|
|
128
|
+
await fs.ensureDir(path.join(projectDir, "test"));
|
|
129
|
+
console.log(chalk.gray(" 创建: test/"));
|
|
130
|
+
}
|
|
131
|
+
await fs.ensureDir(path.join(projectDir, ".github/workflows"));
|
|
132
|
+
await fs.writeFile(path.join(projectDir, ".github/workflows/publish.yml"), getPublishWorkflowTemplate());
|
|
133
|
+
console.log(chalk.gray(" 创建: .github/workflows/publish.yml"));
|
|
134
|
+
await fs.ensureDir(path.join(projectDir, "scripts"));
|
|
135
|
+
await fs.writeFile(path.join(projectDir, "scripts/prepublish.js"), getPrepublishScriptTemplate());
|
|
136
|
+
await fs.writeFile(path.join(projectDir, "scripts/postpublish.js"), getPostpublishScriptTemplate());
|
|
137
|
+
console.log(chalk.gray(" 创建: scripts/prepublish.js"));
|
|
138
|
+
console.log(chalk.gray(" 创建: scripts/postpublish.js"));
|
|
139
|
+
if (options.examples !== false) {
|
|
140
|
+
await fs.writeFile(path.join(projectDir, "scripts/postdocs.js"), getPostdocsScriptTemplate());
|
|
141
|
+
console.log(chalk.gray(" 创建: scripts/postdocs.js"));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function createPackageJson(name, options) {
|
|
145
|
+
const scripts = {
|
|
146
|
+
clean: "rimraf lib dist public",
|
|
147
|
+
build: "vite build && tsc",
|
|
148
|
+
types: "tsc",
|
|
149
|
+
watch: "tsc -w",
|
|
150
|
+
lint: "eslint . --ext .js,.ts --max-warnings 0",
|
|
151
|
+
lintfix: "npm run lint -- --fix",
|
|
152
|
+
docs: "typedoc",
|
|
153
|
+
release: "npm run clean && npm run lint && npm test && npm run build && npm run docs && npm publish",
|
|
154
|
+
prepublishOnly: "node scripts/prepublish.js",
|
|
155
|
+
postpublish: "node scripts/postpublish.js"
|
|
156
|
+
};
|
|
157
|
+
if (options.examples !== false) {
|
|
158
|
+
scripts["examples:dev"] = "cd examples && npm run dev";
|
|
159
|
+
scripts.postdocs = "node scripts/postdocs.js && cd examples && vite build --outDir ../public";
|
|
160
|
+
}
|
|
161
|
+
if (options.vitest !== false) {
|
|
162
|
+
scripts.test = "vitest run";
|
|
163
|
+
scripts["test:watch"] = "vitest";
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
name: `@feng3d/${name}`,
|
|
167
|
+
version: "0.0.1",
|
|
168
|
+
description: "",
|
|
169
|
+
homepage: `https://feng3d.com/${name}/`,
|
|
170
|
+
author: "feng",
|
|
171
|
+
type: "module",
|
|
172
|
+
main: "./src/index.ts",
|
|
173
|
+
types: "./src/index.ts",
|
|
174
|
+
module: "./src/index.ts",
|
|
175
|
+
exports: {
|
|
176
|
+
".": {
|
|
177
|
+
types: "./src/index.ts",
|
|
178
|
+
import: "./src/index.ts",
|
|
179
|
+
require: "./src/index.ts"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
scripts,
|
|
183
|
+
repository: {
|
|
184
|
+
type: "git",
|
|
185
|
+
url: `https://github.com/feng3d-labs/${name}.git`
|
|
186
|
+
},
|
|
187
|
+
publishConfig: {
|
|
188
|
+
access: "public"
|
|
189
|
+
},
|
|
190
|
+
files: ["src", "dist", "lib"],
|
|
191
|
+
devDependencies: getDevDependencies({
|
|
192
|
+
includeVitest: options.vitest !== false,
|
|
193
|
+
includeTypedoc: true
|
|
194
|
+
})
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function deepMerge(target, source) {
|
|
198
|
+
const result = { ...target };
|
|
199
|
+
for (const key in source) {
|
|
200
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
201
|
+
const sourceValue = source[key];
|
|
202
|
+
const targetValue = result[key];
|
|
203
|
+
if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue)) {
|
|
204
|
+
result[key] = deepMerge(targetValue, sourceValue);
|
|
205
|
+
} else if (!(key in result)) {
|
|
206
|
+
result[key] = sourceValue;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
async function mergeJsonConfig(existingPath, standardContent) {
|
|
213
|
+
const existingContent = await fs.readFile(existingPath, "utf-8");
|
|
214
|
+
const existingJson = JSON.parse(existingContent);
|
|
215
|
+
const standardJson = JSON.parse(standardContent);
|
|
216
|
+
const indent = detectIndent$1(existingContent);
|
|
217
|
+
const hasTrailingNewline = existingContent.endsWith("\n");
|
|
218
|
+
const merged = deepMerge(existingJson, standardJson);
|
|
219
|
+
let result = JSON.stringify(merged, null, indent);
|
|
220
|
+
if (hasTrailingNewline) {
|
|
221
|
+
result += "\n";
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
function detectIndent$1(content) {
|
|
226
|
+
const match = content.match(/^[ \t]+/m);
|
|
227
|
+
return match ? match[0] : " ";
|
|
228
|
+
}
|
|
229
|
+
async function determineFileAction(filePath, mergeStrategy) {
|
|
230
|
+
const exists = await fs.pathExists(filePath);
|
|
231
|
+
if (!exists) {
|
|
232
|
+
return "overwrite";
|
|
233
|
+
}
|
|
234
|
+
switch (mergeStrategy) {
|
|
235
|
+
case "overwrite":
|
|
236
|
+
return "overwrite";
|
|
237
|
+
case "skip-existing":
|
|
238
|
+
return "skip";
|
|
239
|
+
case "merge":
|
|
240
|
+
default: {
|
|
241
|
+
const ext = path.extname(filePath);
|
|
242
|
+
if (ext === ".json") {
|
|
243
|
+
return "merge";
|
|
244
|
+
}
|
|
245
|
+
return "overwrite";
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async function askFileAction(filePath) {
|
|
250
|
+
const readline = await import("./__vite-browser-external-2Ng8QIWW.js");
|
|
251
|
+
const rl = readline.createInterface({
|
|
252
|
+
input: process.stdin,
|
|
253
|
+
output: process.stdout
|
|
254
|
+
});
|
|
255
|
+
return new Promise((resolve) => {
|
|
256
|
+
console.log(chalk.yellow(`
|
|
257
|
+
文件已存在: ${filePath}`));
|
|
258
|
+
console.log("请选择处理方式:");
|
|
259
|
+
console.log(" 1. 覆盖 (overwrite) - 完全使用标准配置");
|
|
260
|
+
console.log(" 2. 合并 (merge) - 保留用户配置,添加缺失项");
|
|
261
|
+
console.log(" 3. 跳过 (skip) - 不修改该文件");
|
|
262
|
+
rl.question("请输入选项 (1/2/3,默认 2): ", (answer) => {
|
|
263
|
+
rl.close();
|
|
264
|
+
switch (answer.trim()) {
|
|
265
|
+
case "1":
|
|
266
|
+
resolve("overwrite");
|
|
267
|
+
break;
|
|
268
|
+
case "3":
|
|
269
|
+
resolve("skip");
|
|
270
|
+
break;
|
|
271
|
+
case "2":
|
|
272
|
+
case "":
|
|
273
|
+
default:
|
|
274
|
+
resolve("merge");
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
async function writeFileContent(filePath, content, dryRun = false) {
|
|
281
|
+
if (dryRun) {
|
|
282
|
+
console.log(chalk.gray(` [预览] 将写入: ${filePath}`));
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
286
|
+
await fs.writeFile(filePath, content);
|
|
287
|
+
}
|
|
288
|
+
async function handleFileUpdate(filePath, standardContent, action, dryRun = false) {
|
|
289
|
+
const exists = await fs.pathExists(filePath);
|
|
290
|
+
const relativePath = path.relative(process.cwd(), filePath);
|
|
291
|
+
switch (action) {
|
|
292
|
+
case "skip":
|
|
293
|
+
console.log(chalk.yellow(` 跳过: ${relativePath}`));
|
|
294
|
+
return false;
|
|
295
|
+
case "merge":
|
|
296
|
+
if (!exists) {
|
|
297
|
+
await writeFileContent(filePath, standardContent, dryRun);
|
|
298
|
+
console.log(chalk.gray(` 创建: ${relativePath}`));
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
const ext = path.extname(filePath);
|
|
303
|
+
if (ext === ".json") {
|
|
304
|
+
const merged = await mergeJsonConfig(filePath, standardContent);
|
|
305
|
+
await writeFileContent(filePath, merged, dryRun);
|
|
306
|
+
console.log(chalk.blue(` 合并: ${relativePath}`));
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
await writeFileContent(filePath, standardContent, dryRun);
|
|
310
|
+
console.log(chalk.gray(` 更新: ${relativePath}`));
|
|
311
|
+
return true;
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.log(chalk.red(` 合并失败,跳过: ${relativePath}`));
|
|
314
|
+
console.log(chalk.red(` 错误: ${error}`));
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
case "overwrite":
|
|
318
|
+
default:
|
|
319
|
+
await writeFileContent(filePath, standardContent, dryRun);
|
|
320
|
+
if (exists) {
|
|
321
|
+
console.log(chalk.gray(` 更新: ${relativePath}`));
|
|
322
|
+
} else {
|
|
323
|
+
console.log(chalk.gray(` 创建: ${relativePath}`));
|
|
324
|
+
}
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function updateProject(options = {}) {
|
|
329
|
+
const directory = options.directory || ".";
|
|
97
330
|
const projectDir = path.resolve(directory);
|
|
98
331
|
const packageJsonPath = path.join(projectDir, "package.json");
|
|
332
|
+
let mergeStrategy = options.mergeStrategy || "merge";
|
|
333
|
+
if (options.force) {
|
|
334
|
+
mergeStrategy = "overwrite";
|
|
335
|
+
}
|
|
336
|
+
const interactive = options.interactive || false;
|
|
337
|
+
const dryRun = options.dryRun || false;
|
|
338
|
+
if (dryRun) {
|
|
339
|
+
console.log(chalk.yellow(" [预览模式] 不会实际修改文件\n"));
|
|
340
|
+
}
|
|
341
|
+
if (interactive) {
|
|
342
|
+
console.log(chalk.cyan(" [交互模式] 将逐个询问文件处理方式\n"));
|
|
343
|
+
}
|
|
99
344
|
if (!await fs.pathExists(packageJsonPath)) {
|
|
100
345
|
await fs.ensureDir(projectDir);
|
|
101
346
|
const dirName = path.basename(projectDir);
|
|
@@ -114,71 +359,181 @@ async function updateProject(directory = ".") {
|
|
|
114
359
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
115
360
|
const name = packageJson.name || path.basename(projectDir);
|
|
116
361
|
const repoName = name.replace(/^@[^/]+\//, "");
|
|
362
|
+
const isMonorepoRoot = Boolean(packageJson.workspaces);
|
|
117
363
|
const isFeng3dCli = name === "feng3d-cli";
|
|
118
|
-
await
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
await fs.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
await
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
364
|
+
await updateSingleFile(
|
|
365
|
+
path.join(projectDir, ".gitignore"),
|
|
366
|
+
getGitignoreTemplate(),
|
|
367
|
+
"overwrite",
|
|
368
|
+
// .gitignore 总是覆盖
|
|
369
|
+
false,
|
|
370
|
+
dryRun
|
|
371
|
+
);
|
|
372
|
+
await updateSingleFile(
|
|
373
|
+
path.join(projectDir, ".cursorrules"),
|
|
374
|
+
getCursorrrulesTemplate(),
|
|
375
|
+
mergeStrategy,
|
|
376
|
+
interactive,
|
|
377
|
+
dryRun
|
|
378
|
+
);
|
|
379
|
+
await updateSingleFile(
|
|
380
|
+
path.join(projectDir, "eslint.config.js"),
|
|
381
|
+
getEslintConfigTemplate(),
|
|
382
|
+
mergeStrategy,
|
|
383
|
+
interactive,
|
|
384
|
+
dryRun
|
|
385
|
+
);
|
|
386
|
+
if (!dryRun) {
|
|
387
|
+
await fs.ensureDir(path.join(projectDir, ".github/workflows"));
|
|
388
|
+
}
|
|
389
|
+
await updateSingleFile(
|
|
390
|
+
path.join(projectDir, ".github/workflows/publish.yml"),
|
|
391
|
+
getPublishWorkflowTemplate(),
|
|
392
|
+
mergeStrategy,
|
|
393
|
+
interactive,
|
|
394
|
+
dryRun
|
|
395
|
+
);
|
|
396
|
+
await updateSingleFile(
|
|
397
|
+
path.join(projectDir, ".github/workflows/pages.yml"),
|
|
398
|
+
getPagesWorkflowTemplate(),
|
|
399
|
+
mergeStrategy,
|
|
400
|
+
interactive,
|
|
401
|
+
dryRun
|
|
402
|
+
);
|
|
403
|
+
await updateSingleFile(
|
|
404
|
+
path.join(projectDir, ".github/workflows/pull-request.yml"),
|
|
405
|
+
getPullRequestWorkflowTemplate(),
|
|
406
|
+
mergeStrategy,
|
|
407
|
+
interactive,
|
|
408
|
+
dryRun
|
|
409
|
+
);
|
|
410
|
+
await updateSingleFile(
|
|
411
|
+
path.join(projectDir, ".github/workflows/upload-oss.yml"),
|
|
412
|
+
getUploadOssWorkflowTemplate(),
|
|
413
|
+
mergeStrategy,
|
|
414
|
+
interactive,
|
|
415
|
+
dryRun
|
|
416
|
+
);
|
|
417
|
+
if (!isMonorepoRoot) {
|
|
418
|
+
await updateSingleFile(
|
|
419
|
+
path.join(projectDir, "typedoc.json"),
|
|
420
|
+
getTypedocConfigTemplate({ repoName }),
|
|
421
|
+
mergeStrategy,
|
|
422
|
+
interactive,
|
|
423
|
+
dryRun
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
if (!isMonorepoRoot) {
|
|
427
|
+
const testDir = path.join(projectDir, "test");
|
|
428
|
+
if (!dryRun) {
|
|
429
|
+
await fs.ensureDir(testDir);
|
|
430
|
+
}
|
|
431
|
+
const testFiles = dryRun ? [] : await fs.readdir(testDir);
|
|
432
|
+
if (testFiles.length === 0) {
|
|
433
|
+
await updateSingleFile(
|
|
434
|
+
path.join(testDir, "_.test.ts"),
|
|
435
|
+
getTestIndexTemplate(),
|
|
436
|
+
"overwrite",
|
|
437
|
+
// 新文件,直接创建
|
|
438
|
+
false,
|
|
439
|
+
dryRun
|
|
440
|
+
);
|
|
441
|
+
}
|
|
158
442
|
}
|
|
443
|
+
if (!dryRun) {
|
|
444
|
+
await updateDependencies(projectDir, isMonorepoRoot);
|
|
445
|
+
console.log(chalk.gray(" 更新: package.json devDependencies"));
|
|
446
|
+
}
|
|
447
|
+
if (!dryRun) {
|
|
448
|
+
await fs.ensureDir(path.join(projectDir, ".husky"));
|
|
449
|
+
}
|
|
450
|
+
await updateSingleFile(
|
|
451
|
+
path.join(projectDir, ".husky/pre-commit"),
|
|
452
|
+
getHuskyPreCommitTemplate(),
|
|
453
|
+
mergeStrategy,
|
|
454
|
+
interactive,
|
|
455
|
+
dryRun
|
|
456
|
+
);
|
|
457
|
+
if (!dryRun) {
|
|
458
|
+
await updateHuskyConfig(projectDir);
|
|
459
|
+
}
|
|
460
|
+
await updateSingleFile(
|
|
461
|
+
path.join(projectDir, "LICENSE"),
|
|
462
|
+
getLicenseTemplate(),
|
|
463
|
+
"overwrite",
|
|
464
|
+
// LICENSE 总是覆盖
|
|
465
|
+
false,
|
|
466
|
+
dryRun
|
|
467
|
+
);
|
|
468
|
+
if (!dryRun) {
|
|
469
|
+
await fs.ensureDir(path.join(projectDir, ".vscode"));
|
|
470
|
+
}
|
|
471
|
+
await updateSingleFile(
|
|
472
|
+
path.join(projectDir, ".vscode/settings.json"),
|
|
473
|
+
getVscodeSettingsTemplate(),
|
|
474
|
+
"overwrite",
|
|
475
|
+
// VS Code 设置文件总是覆盖
|
|
476
|
+
false,
|
|
477
|
+
dryRun
|
|
478
|
+
);
|
|
159
479
|
if (!isFeng3dCli) {
|
|
160
|
-
await
|
|
161
|
-
|
|
480
|
+
await updateSingleFile(
|
|
481
|
+
path.join(projectDir, "tsconfig.json"),
|
|
482
|
+
getTsconfigTemplateString(),
|
|
483
|
+
"overwrite",
|
|
484
|
+
// tsconfig.json 总是覆盖
|
|
485
|
+
false,
|
|
486
|
+
dryRun
|
|
487
|
+
);
|
|
162
488
|
}
|
|
163
489
|
if (!isFeng3dCli) {
|
|
164
|
-
await
|
|
165
|
-
|
|
490
|
+
await updateSingleFile(
|
|
491
|
+
path.join(projectDir, "vite.config.js"),
|
|
492
|
+
getViteConfigTemplate(),
|
|
493
|
+
mergeStrategy,
|
|
494
|
+
interactive,
|
|
495
|
+
dryRun
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
if (!isFeng3dCli && !isMonorepoRoot) {
|
|
499
|
+
await updateSingleFile(
|
|
500
|
+
path.join(projectDir, "vitest.config.ts"),
|
|
501
|
+
getVitestConfigTemplate(),
|
|
502
|
+
mergeStrategy,
|
|
503
|
+
interactive,
|
|
504
|
+
dryRun
|
|
505
|
+
);
|
|
166
506
|
}
|
|
167
507
|
const scriptsDir = path.join(projectDir, "scripts");
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
await
|
|
172
|
-
|
|
508
|
+
if (!dryRun) {
|
|
509
|
+
await fs.ensureDir(scriptsDir);
|
|
510
|
+
}
|
|
511
|
+
await updateSingleFile(
|
|
512
|
+
path.join(scriptsDir, "prepublish.js"),
|
|
513
|
+
getPrepublishScriptTemplate(),
|
|
514
|
+
mergeStrategy,
|
|
515
|
+
interactive,
|
|
516
|
+
dryRun
|
|
517
|
+
);
|
|
518
|
+
await updateSingleFile(
|
|
519
|
+
path.join(scriptsDir, "postpublish.js"),
|
|
520
|
+
getPostpublishScriptTemplate(),
|
|
521
|
+
mergeStrategy,
|
|
522
|
+
interactive,
|
|
523
|
+
dryRun
|
|
524
|
+
);
|
|
173
525
|
const examplesDir = path.join(projectDir, "examples");
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
526
|
+
const hasExamples = dryRun ? false : await fs.pathExists(examplesDir);
|
|
527
|
+
if (hasExamples) {
|
|
528
|
+
await updateSingleFile(
|
|
529
|
+
path.join(scriptsDir, "postdocs.js"),
|
|
530
|
+
getPostdocsScriptTemplate(),
|
|
531
|
+
mergeStrategy,
|
|
532
|
+
interactive,
|
|
533
|
+
dryRun
|
|
534
|
+
);
|
|
177
535
|
}
|
|
178
536
|
}
|
|
179
|
-
async function createEslintConfigFile(projectDir) {
|
|
180
|
-
await fs.writeFile(path.join(projectDir, "eslint.config.js"), getEslintConfigTemplate());
|
|
181
|
-
}
|
|
182
537
|
function detectIndent(content) {
|
|
183
538
|
const match = content.match(/^[ \t]+/m);
|
|
184
539
|
return match ? match[0] : " ";
|
|
@@ -243,15 +598,15 @@ function reorderPackageJson(packageJson) {
|
|
|
243
598
|
}
|
|
244
599
|
return ordered;
|
|
245
600
|
}
|
|
246
|
-
async function updateDependencies(projectDir) {
|
|
601
|
+
async function updateDependencies(projectDir, isMonorepoRoot = false) {
|
|
247
602
|
const packageJsonPath = path.join(projectDir, "package.json");
|
|
248
603
|
const originalContent = await fs.readFile(packageJsonPath, "utf-8");
|
|
249
604
|
const indent = detectIndent(originalContent);
|
|
250
605
|
const hasTrailingNewline = originalContent.endsWith("\n");
|
|
251
606
|
const packageJson = JSON.parse(originalContent);
|
|
252
607
|
const standardDeps = getDevDependencies({
|
|
253
|
-
includeVitest:
|
|
254
|
-
includeTypedoc:
|
|
608
|
+
includeVitest: !isMonorepoRoot,
|
|
609
|
+
includeTypedoc: !isMonorepoRoot
|
|
255
610
|
});
|
|
256
611
|
let updated = false;
|
|
257
612
|
if (!packageJson.devDependencies) {
|
|
@@ -274,15 +629,19 @@ async function updateDependencies(projectDir) {
|
|
|
274
629
|
const standardScripts = {
|
|
275
630
|
clean: "rimraf lib dist public",
|
|
276
631
|
build: "vite build && tsc",
|
|
277
|
-
watch: 'concurrently "vite build --watch" "tsc -w" "vitest"',
|
|
278
|
-
test: "vitest run",
|
|
279
632
|
lint: "eslint . --ext .js,.ts --max-warnings 0",
|
|
280
633
|
lintfix: "npm run lint -- --fix",
|
|
281
|
-
docs: "typedoc",
|
|
282
634
|
prepublishOnly: "node scripts/prepublish.js",
|
|
283
|
-
release: "npm run clean && npm run lint && npm test && npm run build && npm run docs && npm publish",
|
|
284
635
|
postpublish: "node scripts/postpublish.js"
|
|
285
636
|
};
|
|
637
|
+
if (!isMonorepoRoot) {
|
|
638
|
+
standardScripts.watch = 'concurrently "vite build --watch" "tsc -w" "vitest"';
|
|
639
|
+
standardScripts.test = "vitest run";
|
|
640
|
+
standardScripts.docs = "typedoc";
|
|
641
|
+
standardScripts.release = "npm run clean && npm run lint && npm test && npm run build && npm run docs && npm publish";
|
|
642
|
+
} else {
|
|
643
|
+
standardScripts.release = "npm run clean && npm run lint && npm run build && npm publish";
|
|
644
|
+
}
|
|
286
645
|
const examplesDir = path.join(projectDir, "examples");
|
|
287
646
|
if (await fs.pathExists(examplesDir)) {
|
|
288
647
|
standardScripts["examples:dev"] = "cd examples && npm run dev";
|
|
@@ -360,7 +719,7 @@ async function updateHuskyConfig(projectDir) {
|
|
|
360
719
|
console.log(chalk.gray(` 更新: devDependencies.husky = "${VERSIONS.husky}"`));
|
|
361
720
|
}
|
|
362
721
|
if (packageJson.devDependencies["lint-staged"] !== VERSIONS["lint-staged"]) {
|
|
363
|
-
packageJson
|
|
722
|
+
packageJson["lint-staged"] = VERSIONS["lint-staged"];
|
|
364
723
|
updated = true;
|
|
365
724
|
console.log(chalk.gray(` 更新: devDependencies.lint-staged = "${VERSIONS["lint-staged"]}"`));
|
|
366
725
|
}
|
|
@@ -389,106 +748,19 @@ async function updateHuskyConfig(projectDir) {
|
|
|
389
748
|
await fs.writeFile(packageJsonPath, newContent);
|
|
390
749
|
}
|
|
391
750
|
}
|
|
392
|
-
async function
|
|
393
|
-
|
|
394
|
-
if (
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
await fs.writeJson(path.join(projectDir, "tsconfig.json"), getTsconfigTemplate(), { spaces: 4 });
|
|
404
|
-
console.log(chalk.gray(" 创建: tsconfig.json"));
|
|
405
|
-
await fs.writeFile(path.join(projectDir, ".gitignore"), getGitignoreTemplate());
|
|
406
|
-
console.log(chalk.gray(" 创建: .gitignore"));
|
|
407
|
-
await fs.writeFile(path.join(projectDir, ".cursorrules"), getCursorrrulesTemplate());
|
|
408
|
-
console.log(chalk.gray(" 创建: .cursorrules"));
|
|
409
|
-
await createEslintConfigFile(projectDir);
|
|
410
|
-
console.log(chalk.gray(" 创建: eslint.config.js"));
|
|
411
|
-
const typedocConfig = getTypedocConfig({ repoName: name });
|
|
412
|
-
await fs.writeJson(path.join(projectDir, "typedoc.json"), typedocConfig, { spaces: 4 });
|
|
413
|
-
console.log(chalk.gray(" 创建: typedoc.json"));
|
|
414
|
-
await fs.writeFile(path.join(projectDir, "src/index.ts"), getSrcIndexTemplate({ name: `@feng3d/${name}` }));
|
|
415
|
-
console.log(chalk.gray(" 创建: src/index.ts"));
|
|
416
|
-
await fs.writeFile(path.join(projectDir, "README.md"), `# @feng3d/${name}
|
|
417
|
-
`);
|
|
418
|
-
console.log(chalk.gray(" 创建: README.md"));
|
|
419
|
-
if (options.examples !== false) {
|
|
420
|
-
await fs.ensureDir(path.join(projectDir, "examples"));
|
|
421
|
-
console.log(chalk.gray(" 创建: examples/"));
|
|
422
|
-
}
|
|
423
|
-
if (options.vitest !== false) {
|
|
424
|
-
await fs.ensureDir(path.join(projectDir, "test"));
|
|
425
|
-
console.log(chalk.gray(" 创建: test/"));
|
|
426
|
-
}
|
|
427
|
-
await fs.ensureDir(path.join(projectDir, ".github/workflows"));
|
|
428
|
-
await fs.writeFile(path.join(projectDir, ".github/workflows/publish.yml"), getPublishWorkflowTemplate());
|
|
429
|
-
console.log(chalk.gray(" 创建: .github/workflows/publish.yml"));
|
|
430
|
-
await fs.ensureDir(path.join(projectDir, "scripts"));
|
|
431
|
-
await fs.writeFile(path.join(projectDir, "scripts/prepublish.js"), getPrepublishScriptTemplate());
|
|
432
|
-
await fs.writeFile(path.join(projectDir, "scripts/postpublish.js"), getPostpublishScriptTemplate());
|
|
433
|
-
console.log(chalk.gray(" 创建: scripts/prepublish.js"));
|
|
434
|
-
console.log(chalk.gray(" 创建: scripts/postpublish.js"));
|
|
435
|
-
if (options.examples !== false) {
|
|
436
|
-
await fs.writeFile(path.join(projectDir, "scripts/postdocs.js"), getPostdocsScriptTemplate());
|
|
437
|
-
console.log(chalk.gray(" 创建: scripts/postdocs.js"));
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
function createPackageJson(name, options) {
|
|
441
|
-
const scripts = {
|
|
442
|
-
clean: "rimraf lib dist public",
|
|
443
|
-
build: "vite build && tsc",
|
|
444
|
-
types: "tsc",
|
|
445
|
-
watch: "tsc -w",
|
|
446
|
-
lint: "eslint . --ext .js,.ts --max-warnings 0",
|
|
447
|
-
lintfix: "npm run lint -- --fix",
|
|
448
|
-
docs: "typedoc",
|
|
449
|
-
release: "npm run clean && npm run lint && npm test && npm run build && npm run docs && npm publish",
|
|
450
|
-
prepublishOnly: "node scripts/prepublish.js",
|
|
451
|
-
postpublish: "node scripts/postpublish.js"
|
|
452
|
-
};
|
|
453
|
-
if (options.examples !== false) {
|
|
454
|
-
scripts["examples:dev"] = "cd examples && npm run dev";
|
|
455
|
-
scripts.postdocs = "node scripts/postdocs.js && cd examples && vite build --outDir ../public";
|
|
456
|
-
}
|
|
457
|
-
if (options.vitest !== false) {
|
|
458
|
-
scripts.test = "vitest run";
|
|
459
|
-
scripts["test:watch"] = "vitest";
|
|
751
|
+
async function updateSingleFile(filePath, content, mergeStrategy, interactive, dryRun) {
|
|
752
|
+
let action;
|
|
753
|
+
if (interactive) {
|
|
754
|
+
const exists = await fs.pathExists(filePath);
|
|
755
|
+
if (exists) {
|
|
756
|
+
action = await askFileAction(path.relative(process.cwd(), filePath));
|
|
757
|
+
} else {
|
|
758
|
+
action = "overwrite";
|
|
759
|
+
}
|
|
760
|
+
} else {
|
|
761
|
+
action = await determineFileAction(filePath, mergeStrategy);
|
|
460
762
|
}
|
|
461
|
-
|
|
462
|
-
name: `@feng3d/${name}`,
|
|
463
|
-
version: "0.0.1",
|
|
464
|
-
description: "",
|
|
465
|
-
homepage: `https://feng3d.com/${name}/`,
|
|
466
|
-
author: "feng",
|
|
467
|
-
type: "module",
|
|
468
|
-
main: "./src/index.ts",
|
|
469
|
-
types: "./src/index.ts",
|
|
470
|
-
module: "./src/index.ts",
|
|
471
|
-
exports: {
|
|
472
|
-
".": {
|
|
473
|
-
types: "./src/index.ts",
|
|
474
|
-
import: "./src/index.ts",
|
|
475
|
-
require: "./src/index.ts"
|
|
476
|
-
}
|
|
477
|
-
},
|
|
478
|
-
scripts,
|
|
479
|
-
repository: {
|
|
480
|
-
type: "git",
|
|
481
|
-
url: `https://github.com/feng3d-labs/${name}.git`
|
|
482
|
-
},
|
|
483
|
-
publishConfig: {
|
|
484
|
-
access: "public"
|
|
485
|
-
},
|
|
486
|
-
files: ["src", "dist", "lib"],
|
|
487
|
-
devDependencies: getDevDependencies({
|
|
488
|
-
includeVitest: options.vitest !== false,
|
|
489
|
-
includeTypedoc: true
|
|
490
|
-
})
|
|
491
|
-
};
|
|
763
|
+
await handleFileUpdate(filePath, content, action, dryRun);
|
|
492
764
|
}
|
|
493
765
|
const __filename$1 = fileURLToPath(import.meta.url);
|
|
494
766
|
const __dirname$1 = path.dirname(__filename$1);
|