feng3d-cli 0.0.5 → 0.0.6

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