feng3d-cli 0.0.5 → 0.0.7

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.
@@ -1,58 +1,27 @@
1
1
  (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("fs-extra"), require("path"), require("url"), require("ali-oss"), require("fs"), require("chalk")) : typeof define === "function" && define.amd ? define(["exports", "fs-extra", "path", "url", "ali-oss", "fs", "chalk"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.feng3dCli = {}, global.fs, global.path, global.url, global.OSS, global.fs$1, global.chalk));
3
- })(this, (function(exports2, fs, path, url, OSS, fs$1, chalk) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("fs-extra"), require("path"), require("url"), require("chalk")) : typeof define === "function" && define.amd ? define(["exports", "fs-extra", "path", "url", "chalk"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.feng3dCli = {}, global.fs, global.path, global.url, global.chalk));
3
+ })(this, (function(exports2, fs, path, url, chalk) {
4
4
  "use strict";
5
5
  var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
6
- const VERSIONS = {
7
- // TypeScript 相关
8
- typescript: "5.8.3",
9
- tslib: "^2.8.1",
10
- // ESLint 相关
11
- eslint: "9.26.0",
12
- "@eslint/js": "^9.0.0",
13
- "@typescript-eslint/eslint-plugin": "8.32.1",
14
- "@typescript-eslint/parser": "8.32.1",
15
- "typescript-eslint": "^8.32.1",
16
- globals: "^14.0.0",
17
- // 测试相关
18
- vitest: "^3.1.3",
19
- "@vitest/coverage-v8": "^3.2.4",
20
- "happy-dom": "^20.0.11",
21
- // 构建工具
22
- vite: "^6.3.5",
23
- rimraf: "6.0.1",
24
- "cross-env": "7.0.3",
25
- // 文档
26
- typedoc: "^0.28.4",
27
- // Git hooks
28
- husky: "^9.1.7",
29
- "lint-staged": "^15.2.10"
30
- };
6
+ const __dirname$2 = path.dirname(url.fileURLToPath(typeof document === "undefined" && typeof location === "undefined" ? require("url").pathToFileURL(__filename).href : typeof document === "undefined" ? location.href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.umd.cjs", document.baseURI).href));
7
+ const TEMPLATES_DIR$1 = path.resolve(__dirname$2, "../templates");
8
+ function loadVersionsFromTemplate() {
9
+ const packageJsonPath = path.join(TEMPLATES_DIR$1, "package.json");
10
+ const packageJson = fs.readJsonSync(packageJsonPath);
11
+ return packageJson.devDependencies || {};
12
+ }
13
+ const VERSIONS = loadVersionsFromTemplate();
31
14
  function getDevDependencies(options = {}) {
32
- const deps = {
33
- "@eslint/js": VERSIONS["@eslint/js"],
34
- "@typescript-eslint/eslint-plugin": VERSIONS["@typescript-eslint/eslint-plugin"],
35
- "@typescript-eslint/parser": VERSIONS["@typescript-eslint/parser"],
36
- "cross-env": VERSIONS["cross-env"],
37
- eslint: VERSIONS.eslint,
38
- globals: VERSIONS.globals,
39
- rimraf: VERSIONS.rimraf,
40
- tslib: VERSIONS.tslib,
41
- typescript: VERSIONS.typescript,
42
- "typescript-eslint": VERSIONS["typescript-eslint"],
43
- vite: VERSIONS.vite
44
- };
45
- if (options.includeVitest !== false) {
46
- deps.vitest = VERSIONS.vitest;
15
+ const deps = { ...VERSIONS };
16
+ if (options.includeVitest === false) {
17
+ delete deps.vitest;
47
18
  }
48
- if (options.includeCoverage) {
49
- deps["@vitest/coverage-v8"] = VERSIONS["@vitest/coverage-v8"];
19
+ if (options.includeTypedoc === false) {
20
+ delete deps.typedoc;
50
21
  }
51
- if (options.includeTypedoc !== false) {
52
- deps.typedoc = VERSIONS.typedoc;
22
+ if (options.includeCoverage && !deps["@vitest/coverage-v8"]) {
23
+ deps["@vitest/coverage-v8"] = "^3.2.4";
53
24
  }
54
- deps.husky = VERSIONS.husky;
55
- deps["lint-staged"] = VERSIONS["lint-staged"];
56
25
  return deps;
57
26
  }
58
27
  const __dirname$1 = path.dirname(url.fileURLToPath(typeof document === "undefined" && typeof location === "undefined" ? require("url").pathToFileURL(__filename).href : typeof document === "undefined" ? location.href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.umd.cjs", document.baseURI).href));
@@ -94,6 +63,9 @@
94
63
  function getPullRequestWorkflowTemplate() {
95
64
  return fs.readFileSync(path.join(TEMPLATES_DIR, ".github/workflows/pull-request.yml"), "utf-8");
96
65
  }
66
+ function getUploadOssWorkflowTemplate() {
67
+ return fs.readFileSync(path.join(TEMPLATES_DIR, ".github/workflows/upload-oss.yml"), "utf-8");
68
+ }
97
69
  function getHuskyPreCommitTemplate() {
98
70
  return fs.readFileSync(path.join(TEMPLATES_DIR, ".husky/pre-commit"), "utf-8");
99
71
  }
@@ -111,396 +83,95 @@
111
83
  function getPostpublishScriptTemplate() {
112
84
  return fs.readFileSync(path.join(TEMPLATES_DIR, "scripts/postpublish.js"), "utf-8");
113
85
  }
114
- const SCHEMA_PATHS = [
115
- "./schemas/feng3d.schema.json",
116
- "./node_modules/feng3d-cli/schemas/feng3d.schema.json"
117
- ];
118
- function detectSchemaPath(projectDir) {
119
- for (const schemaPath of SCHEMA_PATHS) {
120
- const fullPath = path.join(projectDir, schemaPath);
121
- if (fs.existsSync(fullPath)) {
122
- return schemaPath;
123
- }
124
- }
125
- return SCHEMA_PATHS[1];
126
- }
127
- function getFeng3dConfigTemplate(options) {
128
- const templateContent = fs.readFileSync(path.join(TEMPLATES_DIR, "feng3d.json"), "utf-8");
129
- const content = templateContent.replace(/\{\{name\}\}/g, options.name);
130
- const config = JSON.parse(content);
131
- if (options.schemaPath) {
132
- config.$schema = options.schemaPath;
133
- }
134
- return config;
135
- }
136
- const DEFAULT_UPDATE_CONFIG = {
137
- config: true,
138
- eslint: true,
139
- gitignore: true,
140
- cursorrules: true,
141
- publish: true,
142
- pages: true,
143
- pullRequest: true,
144
- typedoc: true,
145
- test: true,
146
- deps: true,
147
- husky: true,
148
- license: true,
149
- vscode: true,
150
- tsconfig: true,
151
- vite: true
152
- };
153
- const DEFAULT_CONFIG = {
154
- eslint: {
155
- enabled: true,
156
- ignores: [],
157
- rules: {}
158
- },
159
- vitest: {
160
- enabled: true,
161
- testTimeout: 0
162
- },
163
- typedoc: {
164
- enabled: true,
165
- outDir: "public"
166
- },
167
- oss: {
168
- localDir: "./public",
169
- ossDir: ""
170
- },
171
- templates: {
172
- examples: true,
173
- test: true
174
- },
175
- update: DEFAULT_UPDATE_CONFIG
176
- };
177
- const configPath = "C:/Users/Administrator/oss_config.json";
178
- async function ossUploadDir(localDirPath, ossDirPath) {
179
- const config = readConfig(configPath);
180
- if (!config) {
181
- console.error("无法读取配置文件");
182
- return;
183
- }
184
- const client = initializeOSS(config);
185
- const { files, failedFiles } = collectFiles(localDirPath, ossDirPath);
186
- console.log(`总文件数: ${files.length}`);
187
- const { successCount, failureCount } = await uploadFiles(files, client, failedFiles);
188
- console.log(`上传完成: 成功 ${successCount} 个, 失败 ${failureCount} 个`);
189
- if (failureCount > 0) {
190
- console.log("上传失败的文件列表:");
191
- failedFiles.forEach((file) => console.log(file));
192
- }
193
- if (successCount > 0) {
194
- const baseUrl = config.baseUrl || `https://${config.bucket}.${config.region}.aliyuncs.com`;
195
- const accessUrl = `${baseUrl}/${ossDirPath}/`;
196
- console.log(`
197
- 📎 访问路径: ${accessUrl}`);
198
- }
199
- }
200
- function readConfig(filePath) {
201
- try {
202
- const fileContent = fs$1.readFileSync(filePath, "utf8");
203
- const config = JSON.parse(fileContent);
204
- return config;
205
- } catch (error) {
206
- console.error("读取或解析配置文件时出错:", error);
207
- return null;
208
- }
209
- }
210
- function initializeOSS(config) {
211
- return new OSS({
212
- region: config.region,
213
- accessKeyId: config.accessKeyId,
214
- accessKeySecret: config.accessKeySecret,
215
- bucket: config.bucket
216
- });
86
+ function getPostdocsScriptTemplate() {
87
+ return fs.readFileSync(path.join(TEMPLATES_DIR, "scripts/postdocs.js"), "utf-8");
217
88
  }
218
- function collectFiles(dirPath, ossDirPath) {
219
- const files = [];
220
- const failedFiles = [];
221
- function traverseDirectory(currentDirPath, currentOssPath) {
222
- const items = fs$1.readdirSync(currentDirPath);
223
- for (const item of items) {
224
- const localFilePath = path.join(currentDirPath, item);
225
- const ossFilePath = `${currentOssPath}/${item}`;
226
- if (fs$1.statSync(localFilePath).isDirectory()) {
227
- traverseDirectory(localFilePath, ossFilePath);
228
- } else {
229
- files.push({ localFilePath, ossFilePath });
230
- }
231
- }
232
- }
233
- traverseDirectory(dirPath, ossDirPath);
234
- return { files, failedFiles };
235
- }
236
- function renderProgressBar(current, total, barLength = 30) {
237
- const percent = current / total;
238
- const filled = Math.round(barLength * percent);
239
- const empty = barLength - filled;
240
- const bar = "█".repeat(filled) + "░".repeat(empty);
241
- const percentText = (percent * 100).toFixed(0).padStart(3, " ");
242
- return `[${bar}] ${percentText}% (${current}/${total})`;
243
- }
244
- async function uploadFiles(files, client, failedFiles) {
245
- let successCount = 0;
246
- let failureCount = 0;
247
- let uploadedCount = 0;
248
- const total = files.length;
249
- process.stdout.write(`上传进度: ${renderProgressBar(0, total)}`);
250
- for (const { localFilePath, ossFilePath } of files) {
251
- try {
252
- await client.put(ossFilePath, localFilePath);
253
- successCount++;
254
- } catch (e) {
255
- process.stdout.write("\n");
256
- console.error(`文件上传失败: ${localFilePath}`, e);
257
- failedFiles.push(localFilePath);
258
- failureCount++;
259
- }
260
- uploadedCount++;
261
- process.stdout.write(`\r上传进度: ${renderProgressBar(uploadedCount, total)}`);
262
- }
263
- process.stdout.write("\n");
264
- return { successCount, failureCount, uploadedCount };
265
- }
266
- const AUTO_GENERATED_FILES = [
267
- { path: ".cursorrules", getTemplate: () => getCursorrrulesTemplate() },
268
- { path: "eslint.config.js", getTemplate: () => getEslintConfigTemplate() },
269
- { path: "typedoc.json", getTemplate: (ctx) => getTypedocConfigTemplate({ repoName: ctx.repoName }) },
270
- { path: "test/_.test.ts", getTemplate: (ctx) => getTestIndexTemplate({ name: ctx.name }) },
271
- { path: ".husky/pre-commit", getTemplate: () => getHuskyPreCommitTemplate() },
272
- { path: ".vscode/settings.json", getTemplate: () => getVscodeSettingsTemplate() },
273
- { path: "tsconfig.json", getTemplate: () => getTsconfigTemplateString() },
274
- { path: "vite.config.js", getTemplate: () => getViteConfigTemplate() },
275
- { path: "scripts/prepublish.js", getTemplate: () => getPrepublishScriptTemplate() },
276
- { path: "scripts/postpublish.js", getTemplate: () => getPostpublishScriptTemplate() }
277
- ];
278
- async function isFileInGitignore(projectDir, filePath) {
279
- const gitignorePath = path.join(projectDir, ".gitignore");
280
- if (!await fs.pathExists(gitignorePath)) {
281
- return false;
282
- }
283
- const gitignoreContent = await fs.readFile(gitignorePath, "utf-8");
284
- const escapedPath = filePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
285
- const regex = new RegExp(`^${escapedPath}$`, "m");
286
- return regex.test(gitignoreContent);
287
- }
288
- async function updateFeng3dConfig(projectDir) {
289
- const configPath2 = path.join(projectDir, "feng3d.json");
290
- const schemaPath = detectSchemaPath(projectDir);
291
- const packageJsonPath = path.join(projectDir, "package.json");
292
- const packageJson = await fs.readJson(packageJsonPath);
293
- const name = packageJson.name || path.basename(projectDir);
294
- const isInGitignore = await isFileInGitignore(projectDir, "feng3d.json");
295
- if (!await fs.pathExists(configPath2) || isInGitignore) {
296
- const configTemplate = getFeng3dConfigTemplate({ name, schemaPath });
297
- await fs.writeJson(configPath2, configTemplate, { spaces: 4 });
298
- console.log(chalk.gray(isInGitignore ? " 覆盖: feng3d.json(在忽略列表中)" : " 创建: feng3d.json"));
299
- } else {
300
- try {
301
- const configData = await fs.readJson(configPath2);
302
- let updated = false;
303
- if (configData.$schema !== schemaPath) {
304
- configData.$schema = schemaPath;
305
- updated = true;
306
- }
307
- if (configData.name !== name) {
308
- configData.name = name;
309
- updated = true;
310
- }
311
- if (updated) {
312
- await fs.writeJson(configPath2, configData, { spaces: 4 });
313
- console.log(chalk.gray(" 更新: feng3d.json"));
314
- } else {
315
- console.log(chalk.gray(" 跳过: feng3d.json(无需更新)"));
316
- }
317
- } catch (error) {
318
- const configTemplate = getFeng3dConfigTemplate({ name, schemaPath });
319
- await fs.writeJson(configPath2, configTemplate, { spaces: 4 });
320
- console.log(chalk.gray(" 重建: feng3d.json"));
321
- }
322
- }
89
+ function getSrcIndexTemplate(options) {
90
+ const template = fs.readFileSync(path.join(TEMPLATES_DIR, "src/index.ts"), "utf-8");
91
+ return template.replace(/\{\{name\}\}/g, options.name);
323
92
  }
324
- async function loadProjectConfig(projectDir) {
325
- const configPath2 = path.join(projectDir, "feng3d.json");
326
- if (!await fs.pathExists(configPath2)) {
327
- return DEFAULT_CONFIG;
328
- }
329
- try {
330
- const configData = await fs.readJson(configPath2);
331
- return { ...DEFAULT_CONFIG, ...configData };
332
- } catch (error) {
333
- console.log(chalk.yellow(` 警告: 无法加载 feng3d.json,使用默认配置 (${error})`));
334
- return DEFAULT_CONFIG;
335
- }
336
- }
337
- function hasAnyUpdateOption(options) {
338
- return !!(options.config || options.eslint || options.gitignore || options.cursorrules || options.publish || options.pages || options.pullRequest || options.typedoc || options.test || options.deps || options.husky || options.license || options.vscode || options.tsconfig);
339
- }
340
- function mergeUpdateOptions(cliOptions, configUpdate) {
341
- if (cliOptions.all) {
342
- return DEFAULT_UPDATE_CONFIG;
343
- }
344
- if (hasAnyUpdateOption(cliOptions)) {
345
- return {
346
- config: cliOptions.config || false,
347
- eslint: cliOptions.eslint || false,
348
- gitignore: cliOptions.gitignore || false,
349
- cursorrules: cliOptions.cursorrules || false,
350
- publish: cliOptions.publish || false,
351
- pages: cliOptions.pages || false,
352
- pullRequest: cliOptions.pullRequest || false,
353
- typedoc: cliOptions.typedoc || false,
354
- test: cliOptions.test || false,
355
- deps: cliOptions.deps || false,
356
- husky: cliOptions.husky || false,
357
- license: cliOptions.license || false,
358
- vscode: cliOptions.vscode || false,
359
- tsconfig: cliOptions.tsconfig || false
360
- };
361
- }
362
- return { ...DEFAULT_UPDATE_CONFIG, ...configUpdate };
93
+ function getVitestConfigTemplate() {
94
+ return fs.readFileSync(path.join(TEMPLATES_DIR, "vitest.config.ts"), "utf-8");
363
95
  }
364
- async function updateProject(options) {
365
- const projectDir = path.resolve(options.directory);
96
+ async function updateProject(directory = ".") {
97
+ const projectDir = path.resolve(directory);
366
98
  const packageJsonPath = path.join(projectDir, "package.json");
367
99
  if (!await fs.pathExists(packageJsonPath)) {
368
- throw new Error(`${projectDir} 不是有效的项目目录(未找到 package.json)`);
369
- }
370
- const config = await loadProjectConfig(projectDir);
371
- const updateConfig = mergeUpdateOptions(options, config.update || {});
372
- if (updateConfig.config) {
373
- await updateFeng3dConfig(projectDir);
100
+ await fs.ensureDir(projectDir);
101
+ const dirName = path.basename(projectDir);
102
+ const initialPackageJson = {
103
+ name: `@feng3d/${dirName}`,
104
+ version: "0.0.1",
105
+ description: ""
106
+ };
107
+ await fs.writeJson(packageJsonPath, initialPackageJson, { spaces: 4 });
108
+ console.log(chalk.gray(" 创建: package.json"));
109
+ const srcDir = path.join(projectDir, "src");
110
+ await fs.ensureDir(srcDir);
111
+ await fs.writeFile(path.join(srcDir, "index.ts"), getSrcIndexTemplate({ name: `@feng3d/${dirName}` }));
112
+ console.log(chalk.gray(" 创建: src/index.ts"));
374
113
  }
375
114
  const packageJson = await fs.readJson(packageJsonPath);
376
115
  const name = packageJson.name || path.basename(projectDir);
377
116
  const repoName = name.replace(/^@[^/]+\//, "");
378
- const templateContext = { name, repoName };
379
- if (updateConfig.gitignore) {
380
- const gitignorePath = path.join(projectDir, ".gitignore");
381
- if (!await fs.pathExists(gitignorePath)) {
382
- await fs.writeFile(gitignorePath, getGitignoreTemplate());
383
- console.log(chalk.gray(" 创建: .gitignore"));
384
- } else {
385
- console.log(chalk.gray(" 跳过: .gitignore(已存在)"));
386
- }
387
- }
388
- if (updateConfig.cursorrules) {
389
- await fs.writeFile(path.join(projectDir, ".cursorrules"), getCursorrrulesTemplate());
390
- console.log(chalk.gray(" 更新: .cursorrules"));
391
- }
392
- if (updateConfig.eslint) {
393
- if (config.eslint?.enabled !== false) {
394
- await createEslintConfigFile(projectDir);
395
- console.log(chalk.gray(" 更新: eslint.config.js"));
396
- } else {
397
- console.log(chalk.gray(" 跳过: eslint.config.js(配置中已禁用)"));
398
- }
399
- }
400
- if (updateConfig.publish) {
401
- await fs.ensureDir(path.join(projectDir, ".github/workflows"));
402
- await fs.writeFile(path.join(projectDir, ".github/workflows/publish.yml"), getPublishWorkflowTemplate());
403
- console.log(chalk.gray(" 更新: .github/workflows/publish.yml"));
404
- }
405
- if (updateConfig.pages) {
406
- await fs.ensureDir(path.join(projectDir, ".github/workflows"));
407
- await fs.writeFile(path.join(projectDir, ".github/workflows/pages.yml"), getPagesWorkflowTemplate());
408
- console.log(chalk.gray(" 更新: .github/workflows/pages.yml"));
409
- }
410
- if (updateConfig.pullRequest) {
411
- await fs.ensureDir(path.join(projectDir, ".github/workflows"));
412
- await fs.writeFile(path.join(projectDir, ".github/workflows/pull-request.yml"), getPullRequestWorkflowTemplate());
413
- console.log(chalk.gray(" 更新: .github/workflows/pull-request.yml"));
414
- }
415
- if (updateConfig.typedoc) {
416
- if (config.typedoc?.enabled !== false) {
417
- const typedocContent = getTypedocConfigTemplate({ repoName });
418
- await fs.writeFile(path.join(projectDir, "typedoc.json"), typedocContent);
419
- console.log(chalk.gray(" 更新: typedoc.json"));
420
- } else {
421
- console.log(chalk.gray(" 跳过: typedoc.json(配置中已禁用)"));
422
- }
423
- }
424
- if (updateConfig.test) {
425
- if (config.vitest?.enabled !== false) {
426
- const testDir = path.join(projectDir, "test");
427
- const testFilePath = path.join(testDir, "_.test.ts");
428
- let hasOtherFiles = false;
429
- if (await fs.pathExists(testDir)) {
430
- const files = await fs.readdir(testDir);
431
- hasOtherFiles = files.some((file) => file !== "_.test.ts");
432
- }
433
- if (!hasOtherFiles) {
434
- await fs.ensureDir(testDir);
435
- const testContent = getTestIndexTemplate();
436
- await fs.writeFile(testFilePath, testContent);
437
- console.log(chalk.gray(" 更新: test/_.test.ts"));
438
- } else {
439
- console.log(chalk.gray(" 跳过: test/_.test.ts(测试目录已有其他文件)"));
440
- }
441
- } else {
442
- console.log(chalk.gray(" 跳过: test/_.test.ts(vitest 配置中已禁用)"));
443
- }
444
- }
445
- if (updateConfig.deps) {
446
- await updateDependencies(projectDir, config);
447
- console.log(chalk.gray(" 更新: package.json devDependencies"));
448
- }
449
- if (updateConfig.husky) {
450
- await fs.ensureDir(path.join(projectDir, ".husky"));
451
- await fs.writeFile(path.join(projectDir, ".husky/pre-commit"), getHuskyPreCommitTemplate());
452
- console.log(chalk.gray(" 更新: .husky/pre-commit"));
453
- await updateHuskyConfig(projectDir);
454
- }
455
- if (updateConfig.license) {
456
- const licensePath = path.join(projectDir, "LICENSE");
457
- if (!await fs.pathExists(licensePath)) {
458
- await fs.writeFile(licensePath, getLicenseTemplate());
459
- console.log(chalk.gray(" 创建: LICENSE"));
460
- } else {
461
- console.log(chalk.gray(" 跳过: LICENSE(已存在)"));
462
- }
463
- }
464
- if (updateConfig.vscode) {
465
- await fs.ensureDir(path.join(projectDir, ".vscode"));
466
- await fs.writeFile(path.join(projectDir, ".vscode/settings.json"), getVscodeSettingsTemplate());
467
- console.log(chalk.gray(" 更新: .vscode/settings.json"));
468
- }
469
117
  const isFeng3dCli = name === "feng3d-cli";
470
- if (updateConfig.tsconfig && !isFeng3dCli) {
471
- const tsconfigPath = path.join(projectDir, "tsconfig.json");
472
- const isIgnored = await isFileInGitignore(projectDir, "tsconfig.json");
473
- if (isIgnored || !await fs.pathExists(tsconfigPath)) {
474
- await fs.writeFile(tsconfigPath, getTsconfigTemplateString());
475
- console.log(chalk.gray(isIgnored ? " 覆盖: tsconfig.json(在忽略列表中)" : " 创建: tsconfig.json"));
476
- } else {
477
- console.log(chalk.gray(" 跳过: tsconfig.json(已存在且不在忽略列表中)"));
478
- }
479
- }
480
- if (updateConfig.vite && !isFeng3dCli) {
481
- const viteConfigPath = path.join(projectDir, "vite.config.js");
482
- const isIgnored = await isFileInGitignore(projectDir, "vite.config.js");
483
- if (isIgnored || !await fs.pathExists(viteConfigPath)) {
484
- await fs.writeFile(viteConfigPath, getViteConfigTemplate());
485
- console.log(chalk.gray(isIgnored ? " 覆盖: vite.config.js(在忽略列表中)" : " 创建: vite.config.js"));
486
- } else {
487
- console.log(chalk.gray(" 跳过: vite.config.js(已存在且不在忽略列表中)"));
488
- }
118
+ await fs.writeFile(path.join(projectDir, ".gitignore"), getGitignoreTemplate());
119
+ console.log(chalk.gray(" 更新: .gitignore"));
120
+ await fs.writeFile(path.join(projectDir, ".cursorrules"), getCursorrrulesTemplate());
121
+ console.log(chalk.gray(" 更新: .cursorrules"));
122
+ await createEslintConfigFile(projectDir);
123
+ console.log(chalk.gray(" 更新: eslint.config.js"));
124
+ await fs.ensureDir(path.join(projectDir, ".github/workflows"));
125
+ await fs.writeFile(path.join(projectDir, ".github/workflows/publish.yml"), getPublishWorkflowTemplate());
126
+ console.log(chalk.gray(" 更新: .github/workflows/publish.yml"));
127
+ await fs.writeFile(path.join(projectDir, ".github/workflows/pages.yml"), getPagesWorkflowTemplate());
128
+ console.log(chalk.gray(" 更新: .github/workflows/pages.yml"));
129
+ await fs.writeFile(path.join(projectDir, ".github/workflows/pull-request.yml"), getPullRequestWorkflowTemplate());
130
+ console.log(chalk.gray(" 更新: .github/workflows/pull-request.yml"));
131
+ await fs.writeFile(path.join(projectDir, ".github/workflows/upload-oss.yml"), getUploadOssWorkflowTemplate());
132
+ console.log(chalk.gray(" 更新: .github/workflows/upload-oss.yml"));
133
+ const typedocContent = getTypedocConfigTemplate({ repoName });
134
+ await fs.writeFile(path.join(projectDir, "typedoc.json"), typedocContent);
135
+ console.log(chalk.gray(" 更新: typedoc.json"));
136
+ const testDir = path.join(projectDir, "test");
137
+ await fs.ensureDir(testDir);
138
+ const testContent = getTestIndexTemplate();
139
+ await fs.writeFile(path.join(testDir, "_.test.ts"), testContent);
140
+ console.log(chalk.gray(" 更新: test/_.test.ts"));
141
+ await updateDependencies(projectDir);
142
+ console.log(chalk.gray(" 更新: package.json devDependencies"));
143
+ await fs.ensureDir(path.join(projectDir, ".husky"));
144
+ await fs.writeFile(path.join(projectDir, ".husky/pre-commit"), getHuskyPreCommitTemplate());
145
+ console.log(chalk.gray(" 更新: .husky/pre-commit"));
146
+ await updateHuskyConfig(projectDir);
147
+ await fs.writeFile(path.join(projectDir, "LICENSE"), getLicenseTemplate());
148
+ console.log(chalk.gray(" 更新: LICENSE"));
149
+ await fs.ensureDir(path.join(projectDir, ".vscode"));
150
+ await fs.writeFile(path.join(projectDir, ".vscode/settings.json"), getVscodeSettingsTemplate());
151
+ console.log(chalk.gray(" 更新: .vscode/settings.json"));
152
+ if (!isFeng3dCli) {
153
+ await fs.writeFile(path.join(projectDir, "tsconfig.json"), getTsconfigTemplateString());
154
+ console.log(chalk.gray(" 更新: tsconfig.json"));
155
+ }
156
+ if (!isFeng3dCli) {
157
+ await fs.writeFile(path.join(projectDir, "vite.config.js"), getViteConfigTemplate());
158
+ console.log(chalk.gray(" 更新: vite.config.js"));
159
+ }
160
+ if (!isFeng3dCli) {
161
+ await fs.writeFile(path.join(projectDir, "vitest.config.ts"), getVitestConfigTemplate());
162
+ console.log(chalk.gray(" 更新: vitest.config.ts"));
489
163
  }
490
164
  const scriptsDir = path.join(projectDir, "scripts");
491
- const prepublishPath = path.join(scriptsDir, "prepublish.js");
492
- const postpublishPath = path.join(scriptsDir, "postpublish.js");
493
- if (!await fs.pathExists(prepublishPath)) {
494
- await fs.ensureDir(scriptsDir);
495
- await fs.writeFile(prepublishPath, getPrepublishScriptTemplate());
496
- console.log(chalk.gray(" 创建: scripts/prepublish.js"));
497
- }
498
- if (!await fs.pathExists(postpublishPath)) {
499
- await fs.ensureDir(scriptsDir);
500
- await fs.writeFile(postpublishPath, getPostpublishScriptTemplate());
501
- console.log(chalk.gray(" 创建: scripts/postpublish.js"));
165
+ await fs.ensureDir(scriptsDir);
166
+ await fs.writeFile(path.join(scriptsDir, "prepublish.js"), getPrepublishScriptTemplate());
167
+ console.log(chalk.gray(" 更新: scripts/prepublish.js"));
168
+ await fs.writeFile(path.join(scriptsDir, "postpublish.js"), getPostpublishScriptTemplate());
169
+ console.log(chalk.gray(" 更新: scripts/postpublish.js"));
170
+ const examplesDir = path.join(projectDir, "examples");
171
+ if (await fs.pathExists(examplesDir)) {
172
+ await fs.writeFile(path.join(scriptsDir, "postdocs.js"), getPostdocsScriptTemplate());
173
+ console.log(chalk.gray(" 更新: scripts/postdocs.js"));
502
174
  }
503
- await syncGitignoreForModifiedFiles(projectDir, templateContext, name);
504
175
  }
505
176
  async function createEslintConfigFile(projectDir) {
506
177
  await fs.writeFile(path.join(projectDir, "eslint.config.js"), getEslintConfigTemplate());
@@ -509,15 +180,74 @@
509
180
  const match = content.match(/^[ \t]+/m);
510
181
  return match ? match[0] : " ";
511
182
  }
512
- async function updateDependencies(projectDir, config) {
183
+ const PACKAGE_JSON_FIELD_ORDER = [
184
+ "name",
185
+ "version",
186
+ "description",
187
+ "homepage",
188
+ "author",
189
+ "license",
190
+ "type",
191
+ "main",
192
+ "types",
193
+ "module",
194
+ "exports",
195
+ "bin",
196
+ "scripts",
197
+ "repository",
198
+ "publishConfig",
199
+ "files",
200
+ "devDependencies",
201
+ "dependencies",
202
+ "peerDependencies",
203
+ "lint-staged"
204
+ ];
205
+ const SCRIPTS_ORDER = [
206
+ "examples:dev",
207
+ "test_web",
208
+ "postdocs",
209
+ "clean",
210
+ "build",
211
+ "watch",
212
+ "test",
213
+ "lint",
214
+ "lintfix",
215
+ "docs",
216
+ "prepublishOnly",
217
+ "release",
218
+ "postpublish",
219
+ "prepare"
220
+ ];
221
+ function reorderObject(obj, order) {
222
+ const ordered = {};
223
+ for (const key of order) {
224
+ if (key in obj) {
225
+ ordered[key] = obj[key];
226
+ }
227
+ }
228
+ for (const key of Object.keys(obj)) {
229
+ if (!(key in ordered)) {
230
+ ordered[key] = obj[key];
231
+ }
232
+ }
233
+ return ordered;
234
+ }
235
+ function reorderPackageJson(packageJson) {
236
+ const ordered = reorderObject(packageJson, PACKAGE_JSON_FIELD_ORDER);
237
+ if (ordered.scripts && typeof ordered.scripts === "object") {
238
+ ordered.scripts = reorderObject(ordered.scripts, SCRIPTS_ORDER);
239
+ }
240
+ return ordered;
241
+ }
242
+ async function updateDependencies(projectDir) {
513
243
  const packageJsonPath = path.join(projectDir, "package.json");
514
244
  const originalContent = await fs.readFile(packageJsonPath, "utf-8");
515
245
  const indent = detectIndent(originalContent);
516
246
  const hasTrailingNewline = originalContent.endsWith("\n");
517
247
  const packageJson = JSON.parse(originalContent);
518
248
  const standardDeps = getDevDependencies({
519
- includeVitest: config.vitest?.enabled !== false,
520
- includeTypedoc: config.typedoc?.enabled !== false
249
+ includeVitest: true,
250
+ includeTypedoc: true
521
251
  });
522
252
  let updated = false;
523
253
  if (!packageJson.devDependencies) {
@@ -540,23 +270,59 @@
540
270
  const standardScripts = {
541
271
  clean: "rimraf lib dist public",
542
272
  build: "vite build && tsc",
273
+ watch: 'concurrently "vite build --watch" "tsc -w" "vitest"',
274
+ test: "vitest run",
543
275
  lint: "eslint . --ext .js,.ts --max-warnings 0",
544
276
  lintfix: "npm run lint -- --fix",
545
277
  docs: "typedoc",
546
- upload_oss: "npm run docs && npx feng3d-cli oss_upload_dir",
547
- update: "npx feng3d-cli update && npm install",
548
278
  prepublishOnly: "node scripts/prepublish.js",
279
+ release: "npm run clean && npm run lint && npm test && npm run build && npm run docs && npm publish",
549
280
  postpublish: "node scripts/postpublish.js"
550
281
  };
282
+ const examplesDir = path.join(projectDir, "examples");
283
+ if (await fs.pathExists(examplesDir)) {
284
+ standardScripts["examples:dev"] = "cd examples && npm run dev";
285
+ standardScripts.postdocs = "node scripts/postdocs.js && cd examples && vite build --outDir ../public";
286
+ }
551
287
  for (const [key, value] of Object.entries(standardScripts)) {
552
- if (!(key in packageJson.scripts)) {
288
+ if (packageJson.scripts[key] !== value) {
553
289
  packageJson.scripts[key] = value;
554
290
  updated = true;
555
- console.log(chalk.gray(` 添加: scripts.${key}`));
291
+ console.log(chalk.gray(` 更新: scripts.${key}`));
292
+ }
293
+ }
294
+ if (packageJson.type !== "module") {
295
+ packageJson.type = "module";
296
+ updated = true;
297
+ console.log(chalk.gray(' 更新: type = "module"'));
298
+ }
299
+ const entryPoints = {
300
+ main: "./src/index.ts",
301
+ types: "./src/index.ts",
302
+ module: "./src/index.ts"
303
+ };
304
+ for (const [key, value] of Object.entries(entryPoints)) {
305
+ if (packageJson[key] !== value) {
306
+ packageJson[key] = value;
307
+ updated = true;
308
+ console.log(chalk.gray(` 更新: ${key} = "${value}"`));
556
309
  }
557
310
  }
311
+ const standardExports = {
312
+ ".": {
313
+ types: "./src/index.ts",
314
+ import: "./src/index.ts",
315
+ require: "./src/index.ts"
316
+ }
317
+ };
318
+ if (JSON.stringify(packageJson.exports) !== JSON.stringify(standardExports)) {
319
+ packageJson.exports = standardExports;
320
+ updated = true;
321
+ console.log(chalk.gray(" 更新: exports"));
322
+ }
558
323
  if (updated) {
559
- let newContent = JSON.stringify(packageJson, null, indent);
324
+ const orderedPackageJson = reorderPackageJson(packageJson);
325
+ let newContent = JSON.stringify(orderedPackageJson, null, indent);
560
326
  if (hasTrailingNewline) {
561
327
  newContent += "\n";
562
328
  }
@@ -573,15 +339,15 @@
573
339
  if (!packageJson.devDependencies) {
574
340
  packageJson.devDependencies = {};
575
341
  }
576
- if (!packageJson.devDependencies.husky) {
342
+ if (packageJson.devDependencies.husky !== VERSIONS.husky) {
577
343
  packageJson.devDependencies.husky = VERSIONS.husky;
578
344
  updated = true;
579
- console.log(chalk.gray(` 添加: devDependencies.husky = "${VERSIONS.husky}"`));
345
+ console.log(chalk.gray(` 更新: devDependencies.husky = "${VERSIONS.husky}"`));
580
346
  }
581
- if (!packageJson.devDependencies["lint-staged"]) {
347
+ if (packageJson.devDependencies["lint-staged"] !== VERSIONS["lint-staged"]) {
582
348
  packageJson.devDependencies["lint-staged"] = VERSIONS["lint-staged"];
583
349
  updated = true;
584
- console.log(chalk.gray(` 添加: devDependencies.lint-staged = "${VERSIONS["lint-staged"]}"`));
350
+ console.log(chalk.gray(` 更新: devDependencies.lint-staged = "${VERSIONS["lint-staged"]}"`));
585
351
  }
586
352
  if (!packageJson.scripts) {
587
353
  packageJson.scripts = {};
@@ -589,77 +355,25 @@
589
355
  if (packageJson.scripts.prepare !== "husky") {
590
356
  packageJson.scripts.prepare = "husky";
591
357
  updated = true;
592
- console.log(chalk.gray(' 添加: scripts.prepare = "husky"'));
358
+ console.log(chalk.gray(' 更新: scripts.prepare = "husky"'));
593
359
  }
594
- if (!packageJson["lint-staged"]) {
595
- packageJson["lint-staged"] = {
596
- "*.{js,ts}": ["eslint --fix --max-warnings 0"]
597
- };
360
+ const standardLintStaged = {
361
+ "*.{js,ts}": ["eslint --fix --max-warnings 0"]
362
+ };
363
+ if (JSON.stringify(packageJson["lint-staged"]) !== JSON.stringify(standardLintStaged)) {
364
+ packageJson["lint-staged"] = standardLintStaged;
598
365
  updated = true;
599
- console.log(chalk.gray(" 添加: lint-staged 配置"));
366
+ console.log(chalk.gray(" 更新: lint-staged 配置"));
600
367
  }
601
368
  if (updated) {
602
- let newContent = JSON.stringify(packageJson, null, indent);
369
+ const orderedPackageJson = reorderPackageJson(packageJson);
370
+ let newContent = JSON.stringify(orderedPackageJson, null, indent);
603
371
  if (hasTrailingNewline) {
604
372
  newContent += "\n";
605
373
  }
606
374
  await fs.writeFile(packageJsonPath, newContent);
607
375
  }
608
376
  }
609
- const AUTO_GENERATED_COMMENT = `# 以下文件可由 feng3d-cli 自动生成,无需提交
610
- # 运行 \`feng3d-cli update\` 可重新生成`;
611
- async function syncGitignoreForModifiedFiles(projectDir, _ctx, projectName) {
612
- const gitignorePath = path.join(projectDir, ".gitignore");
613
- if (!await fs.pathExists(gitignorePath)) {
614
- return;
615
- }
616
- let gitignoreContent = await fs.readFile(gitignorePath, "utf-8");
617
- let modified = false;
618
- const isFeng3dCli = projectName === "feng3d-cli";
619
- const skipFiles = isFeng3dCli ? ["tsconfig.json", "vite.config.js"] : [];
620
- const filesToAdd = [];
621
- const feng3dConfigPath = "feng3d.json";
622
- const escapedFeng3dPath = feng3dConfigPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
623
- const feng3dRegex = new RegExp(`^${escapedFeng3dPath}$`, "m");
624
- if (!feng3dRegex.test(gitignoreContent)) {
625
- filesToAdd.push(feng3dConfigPath);
626
- }
627
- for (const file of AUTO_GENERATED_FILES) {
628
- if (skipFiles.includes(file.path)) {
629
- continue;
630
- }
631
- const escapedPath = file.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
632
- const regex = new RegExp(`^${escapedPath}$`, "m");
633
- if (!regex.test(gitignoreContent)) {
634
- const filePath = path.join(projectDir, file.path);
635
- if (await fs.pathExists(filePath)) {
636
- const fileContent = await fs.readFile(filePath, "utf-8");
637
- const templateContent = file.getTemplate(_ctx);
638
- if (fileContent.trim() === templateContent.trim()) {
639
- filesToAdd.push(file.path);
640
- }
641
- }
642
- }
643
- }
644
- if (filesToAdd.length > 0) {
645
- const hasComment = gitignoreContent.includes("# 以下文件可由 feng3d-cli 自动生成");
646
- if (!hasComment) {
647
- gitignoreContent = gitignoreContent.trim() + "\n\n" + AUTO_GENERATED_COMMENT + "\n" + filesToAdd.join("\n") + "\n";
648
- } else {
649
- for (const filePath of filesToAdd) {
650
- gitignoreContent = gitignoreContent.trim() + "\n" + filePath + "\n";
651
- }
652
- }
653
- modified = true;
654
- for (const filePath of filesToAdd) {
655
- console.log(chalk.gray(` 添加到 .gitignore: ${filePath}`));
656
- }
657
- }
658
- if (modified) {
659
- gitignoreContent = gitignoreContent.replace(/\n\n+/g, "\n\n").trim() + "\n";
660
- await fs.writeFile(gitignorePath, gitignoreContent);
661
- }
662
- }
663
377
  async function createProject(name, options) {
664
378
  const projectDir = path.join(options.directory, name);
665
379
  if (await fs.pathExists(projectDir)) {
@@ -682,12 +396,7 @@
682
396
  const typedocConfig = getTypedocConfig({ repoName: name });
683
397
  await fs.writeJson(path.join(projectDir, "typedoc.json"), typedocConfig, { spaces: 4 });
684
398
  console.log(chalk.gray(" 创建: typedoc.json"));
685
- await fs.writeFile(path.join(projectDir, "src/index.ts"), `/**
686
- * @feng3d/${name}
687
- */
688
-
689
- export {};
690
- `);
399
+ await fs.writeFile(path.join(projectDir, "src/index.ts"), getSrcIndexTemplate({ name: `@feng3d/${name}` }));
691
400
  console.log(chalk.gray(" 创建: src/index.ts"));
692
401
  await fs.writeFile(path.join(projectDir, "README.md"), `# @feng3d/${name}
693
402
  `);
@@ -708,12 +417,13 @@ export {};
708
417
  await fs.writeFile(path.join(projectDir, "scripts/postpublish.js"), getPostpublishScriptTemplate());
709
418
  console.log(chalk.gray(" 创建: scripts/prepublish.js"));
710
419
  console.log(chalk.gray(" 创建: scripts/postpublish.js"));
711
- await fs.writeJson(path.join(projectDir, "feng3d.json"), getFeng3dConfigTemplate({ name }), { spaces: 4 });
712
- console.log(chalk.gray(" 创建: feng3d.json"));
420
+ if (options.examples !== false) {
421
+ await fs.writeFile(path.join(projectDir, "scripts/postdocs.js"), getPostdocsScriptTemplate());
422
+ console.log(chalk.gray(" 创建: scripts/postdocs.js"));
423
+ }
713
424
  }
714
425
  function createPackageJson(name, options) {
715
426
  const scripts = {
716
- dev: "cd examples && npm run dev",
717
427
  clean: "rimraf lib dist public",
718
428
  build: "vite build && tsc",
719
429
  types: "tsc",
@@ -725,6 +435,10 @@ export {};
725
435
  prepublishOnly: "node scripts/prepublish.js",
726
436
  postpublish: "node scripts/postpublish.js"
727
437
  };
438
+ if (options.examples !== false) {
439
+ scripts["examples:dev"] = "cd examples && npm run dev";
440
+ scripts.postdocs = "node scripts/postdocs.js && cd examples && vite build --outDir ../public";
441
+ }
728
442
  if (options.vitest !== false) {
729
443
  scripts.test = "vitest run";
730
444
  scripts["test:watch"] = "vitest";
@@ -761,31 +475,30 @@ export {};
761
475
  })
762
476
  };
763
477
  }
764
- exports2.DEFAULT_CONFIG = DEFAULT_CONFIG;
765
- exports2.DEFAULT_UPDATE_CONFIG = DEFAULT_UPDATE_CONFIG;
766
478
  exports2.VERSIONS = VERSIONS;
767
479
  exports2.createProject = createProject;
768
- exports2.detectSchemaPath = detectSchemaPath;
769
480
  exports2.getCursorrrulesTemplate = getCursorrrulesTemplate;
770
481
  exports2.getDevDependencies = getDevDependencies;
771
482
  exports2.getEslintConfigTemplate = getEslintConfigTemplate;
772
- exports2.getFeng3dConfigTemplate = getFeng3dConfigTemplate;
773
483
  exports2.getGitignoreTemplate = getGitignoreTemplate;
774
484
  exports2.getHuskyPreCommitTemplate = getHuskyPreCommitTemplate;
775
485
  exports2.getLicenseTemplate = getLicenseTemplate;
776
486
  exports2.getPagesWorkflowTemplate = getPagesWorkflowTemplate;
487
+ exports2.getPostdocsScriptTemplate = getPostdocsScriptTemplate;
777
488
  exports2.getPostpublishScriptTemplate = getPostpublishScriptTemplate;
778
489
  exports2.getPrepublishScriptTemplate = getPrepublishScriptTemplate;
779
490
  exports2.getPublishWorkflowTemplate = getPublishWorkflowTemplate;
780
491
  exports2.getPullRequestWorkflowTemplate = getPullRequestWorkflowTemplate;
492
+ exports2.getSrcIndexTemplate = getSrcIndexTemplate;
781
493
  exports2.getTestIndexTemplate = getTestIndexTemplate;
782
494
  exports2.getTsconfigTemplate = getTsconfigTemplate;
783
495
  exports2.getTsconfigTemplateString = getTsconfigTemplateString;
784
496
  exports2.getTypedocConfig = getTypedocConfig;
785
497
  exports2.getTypedocConfigTemplate = getTypedocConfigTemplate;
498
+ exports2.getUploadOssWorkflowTemplate = getUploadOssWorkflowTemplate;
786
499
  exports2.getViteConfigTemplate = getViteConfigTemplate;
500
+ exports2.getVitestConfigTemplate = getVitestConfigTemplate;
787
501
  exports2.getVscodeSettingsTemplate = getVscodeSettingsTemplate;
788
- exports2.ossUploadDir = ossUploadDir;
789
502
  exports2.updateProject = updateProject;
790
503
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
791
504
  }));