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.
@@ -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,380 +83,105 @@
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
- });
217
- }
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})`;
86
+ function getPostdocsScriptTemplate() {
87
+ return fs.readFileSync(path.join(TEMPLATES_DIR, "scripts/postdocs.js"), "utf-8");
243
88
  }
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 };
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);
265
92
  }
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
- }
323
- }
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 };
363
- }
364
- async function updateProject(options) {
365
- const projectDir = path.resolve(options.directory);
93
+ async function updateProject(directory = ".") {
94
+ const projectDir = path.resolve(directory);
366
95
  const packageJsonPath = path.join(projectDir, "package.json");
367
96
  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);
97
+ await fs.ensureDir(projectDir);
98
+ const dirName = path.basename(projectDir);
99
+ const initialPackageJson = {
100
+ name: `@feng3d/${dirName}`,
101
+ version: "0.0.1",
102
+ description: ""
103
+ };
104
+ await fs.writeJson(packageJsonPath, initialPackageJson, { spaces: 4 });
105
+ console.log(chalk.gray(" 创建: package.json"));
106
+ const srcDir = path.join(projectDir, "src");
107
+ await fs.ensureDir(srcDir);
108
+ await fs.writeFile(path.join(srcDir, "index.ts"), getSrcIndexTemplate({ name: `@feng3d/${dirName}` }));
109
+ console.log(chalk.gray(" 创建: src/index.ts"));
374
110
  }
375
111
  const packageJson = await fs.readJson(packageJsonPath);
376
112
  const name = packageJson.name || path.basename(projectDir);
377
113
  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
- }
114
+ const isFeng3dCli = name === "feng3d-cli";
115
+ const gitignorePath = path.join(projectDir, ".gitignore");
116
+ if (!await fs.pathExists(gitignorePath)) {
117
+ await fs.writeFile(gitignorePath, getGitignoreTemplate());
118
+ console.log(chalk.gray(" 创建: .gitignore"));
119
+ } else {
120
+ console.log(chalk.gray(" 跳过: .gitignore(已存在)"));
463
121
  }
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"));
122
+ await fs.writeFile(path.join(projectDir, ".cursorrules"), getCursorrrulesTemplate());
123
+ console.log(chalk.gray(" 更新: .cursorrules"));
124
+ await createEslintConfigFile(projectDir);
125
+ console.log(chalk.gray(" 更新: eslint.config.js"));
126
+ await fs.ensureDir(path.join(projectDir, ".github/workflows"));
127
+ await fs.writeFile(path.join(projectDir, ".github/workflows/publish.yml"), getPublishWorkflowTemplate());
128
+ console.log(chalk.gray(" 更新: .github/workflows/publish.yml"));
129
+ await fs.writeFile(path.join(projectDir, ".github/workflows/pages.yml"), getPagesWorkflowTemplate());
130
+ console.log(chalk.gray(" 更新: .github/workflows/pages.yml"));
131
+ await fs.writeFile(path.join(projectDir, ".github/workflows/pull-request.yml"), getPullRequestWorkflowTemplate());
132
+ console.log(chalk.gray(" 更新: .github/workflows/pull-request.yml"));
133
+ await fs.writeFile(path.join(projectDir, ".github/workflows/upload-oss.yml"), getUploadOssWorkflowTemplate());
134
+ console.log(chalk.gray(" 更新: .github/workflows/upload-oss.yml"));
135
+ const typedocContent = getTypedocConfigTemplate({ repoName });
136
+ await fs.writeFile(path.join(projectDir, "typedoc.json"), typedocContent);
137
+ console.log(chalk.gray(" 更新: typedoc.json"));
138
+ const testDir = path.join(projectDir, "test");
139
+ const testFilePath = path.join(testDir, "_.test.ts");
140
+ let hasOtherFiles = false;
141
+ if (await fs.pathExists(testDir)) {
142
+ const files = await fs.readdir(testDir);
143
+ hasOtherFiles = files.some((file) => file !== "_.test.ts");
144
+ }
145
+ if (!hasOtherFiles) {
146
+ await fs.ensureDir(testDir);
147
+ const testContent = getTestIndexTemplate();
148
+ await fs.writeFile(testFilePath, testContent);
149
+ console.log(chalk.gray(" 更新: test/_.test.ts"));
150
+ } else {
151
+ console.log(chalk.gray(" 跳过: test/_.test.ts(测试目录已有其他文件)"));
152
+ }
153
+ await updateDependencies(projectDir);
154
+ console.log(chalk.gray(" 更新: package.json devDependencies"));
155
+ await fs.ensureDir(path.join(projectDir, ".husky"));
156
+ await fs.writeFile(path.join(projectDir, ".husky/pre-commit"), getHuskyPreCommitTemplate());
157
+ console.log(chalk.gray(" 更新: .husky/pre-commit"));
158
+ await updateHuskyConfig(projectDir);
159
+ const licensePath = path.join(projectDir, "LICENSE");
160
+ if (!await fs.pathExists(licensePath)) {
161
+ await fs.writeFile(licensePath, getLicenseTemplate());
162
+ console.log(chalk.gray(" 创建: LICENSE"));
163
+ } else {
164
+ console.log(chalk.gray(" 跳过: LICENSE(已存在)"));
468
165
  }
469
- const isFeng3dCli = name === "feng3d-cli";
470
- if (updateConfig.tsconfig && !isFeng3dCli) {
166
+ await fs.ensureDir(path.join(projectDir, ".vscode"));
167
+ await fs.writeFile(path.join(projectDir, ".vscode/settings.json"), getVscodeSettingsTemplate());
168
+ console.log(chalk.gray(" 更新: .vscode/settings.json"));
169
+ if (!isFeng3dCli) {
471
170
  const tsconfigPath = path.join(projectDir, "tsconfig.json");
472
- const isIgnored = await isFileInGitignore(projectDir, "tsconfig.json");
473
- if (isIgnored || !await fs.pathExists(tsconfigPath)) {
171
+ if (!await fs.pathExists(tsconfigPath)) {
474
172
  await fs.writeFile(tsconfigPath, getTsconfigTemplateString());
475
- console.log(chalk.gray(isIgnored ? " 覆盖: tsconfig.json(在忽略列表中)" : " 创建: tsconfig.json"));
173
+ console.log(chalk.gray(" 创建: tsconfig.json"));
476
174
  } else {
477
- console.log(chalk.gray(" 跳过: tsconfig.json(已存在且不在忽略列表中)"));
175
+ console.log(chalk.gray(" 跳过: tsconfig.json(已存在)"));
478
176
  }
479
177
  }
480
- if (updateConfig.vite && !isFeng3dCli) {
178
+ if (!isFeng3dCli) {
481
179
  const viteConfigPath = path.join(projectDir, "vite.config.js");
482
- const isIgnored = await isFileInGitignore(projectDir, "vite.config.js");
483
- if (isIgnored || !await fs.pathExists(viteConfigPath)) {
180
+ if (!await fs.pathExists(viteConfigPath)) {
484
181
  await fs.writeFile(viteConfigPath, getViteConfigTemplate());
485
- console.log(chalk.gray(isIgnored ? " 覆盖: vite.config.js(在忽略列表中)" : " 创建: vite.config.js"));
182
+ console.log(chalk.gray(" 创建: vite.config.js"));
486
183
  } else {
487
- console.log(chalk.gray(" 跳过: vite.config.js(已存在且不在忽略列表中)"));
184
+ console.log(chalk.gray(" 跳过: vite.config.js(已存在)"));
488
185
  }
489
186
  }
490
187
  const scriptsDir = path.join(projectDir, "scripts");
@@ -500,7 +197,15 @@
500
197
  await fs.writeFile(postpublishPath, getPostpublishScriptTemplate());
501
198
  console.log(chalk.gray(" 创建: scripts/postpublish.js"));
502
199
  }
503
- await syncGitignoreForModifiedFiles(projectDir, templateContext, name);
200
+ const examplesDir = path.join(projectDir, "examples");
201
+ const postdocsPath = path.join(scriptsDir, "postdocs.js");
202
+ if (await fs.pathExists(examplesDir)) {
203
+ if (!await fs.pathExists(postdocsPath)) {
204
+ await fs.ensureDir(scriptsDir);
205
+ await fs.writeFile(postdocsPath, getPostdocsScriptTemplate());
206
+ console.log(chalk.gray(" 创建: scripts/postdocs.js"));
207
+ }
208
+ }
504
209
  }
505
210
  async function createEslintConfigFile(projectDir) {
506
211
  await fs.writeFile(path.join(projectDir, "eslint.config.js"), getEslintConfigTemplate());
@@ -509,15 +214,74 @@
509
214
  const match = content.match(/^[ \t]+/m);
510
215
  return match ? match[0] : " ";
511
216
  }
512
- async function updateDependencies(projectDir, config) {
217
+ const PACKAGE_JSON_FIELD_ORDER = [
218
+ "name",
219
+ "version",
220
+ "description",
221
+ "homepage",
222
+ "author",
223
+ "license",
224
+ "type",
225
+ "main",
226
+ "types",
227
+ "module",
228
+ "exports",
229
+ "bin",
230
+ "scripts",
231
+ "repository",
232
+ "publishConfig",
233
+ "files",
234
+ "devDependencies",
235
+ "dependencies",
236
+ "peerDependencies",
237
+ "lint-staged"
238
+ ];
239
+ const SCRIPTS_ORDER = [
240
+ "examples:dev",
241
+ "test_web",
242
+ "postdocs",
243
+ "clean",
244
+ "build",
245
+ "watch",
246
+ "test",
247
+ "lint",
248
+ "lintfix",
249
+ "docs",
250
+ "prepublishOnly",
251
+ "release",
252
+ "postpublish",
253
+ "prepare"
254
+ ];
255
+ function reorderObject(obj, order) {
256
+ const ordered = {};
257
+ for (const key of order) {
258
+ if (key in obj) {
259
+ ordered[key] = obj[key];
260
+ }
261
+ }
262
+ for (const key of Object.keys(obj)) {
263
+ if (!(key in ordered)) {
264
+ ordered[key] = obj[key];
265
+ }
266
+ }
267
+ return ordered;
268
+ }
269
+ function reorderPackageJson(packageJson) {
270
+ const ordered = reorderObject(packageJson, PACKAGE_JSON_FIELD_ORDER);
271
+ if (ordered.scripts && typeof ordered.scripts === "object") {
272
+ ordered.scripts = reorderObject(ordered.scripts, SCRIPTS_ORDER);
273
+ }
274
+ return ordered;
275
+ }
276
+ async function updateDependencies(projectDir) {
513
277
  const packageJsonPath = path.join(projectDir, "package.json");
514
278
  const originalContent = await fs.readFile(packageJsonPath, "utf-8");
515
279
  const indent = detectIndent(originalContent);
516
280
  const hasTrailingNewline = originalContent.endsWith("\n");
517
281
  const packageJson = JSON.parse(originalContent);
518
282
  const standardDeps = getDevDependencies({
519
- includeVitest: config.vitest?.enabled !== false,
520
- includeTypedoc: config.typedoc?.enabled !== false
283
+ includeVitest: true,
284
+ includeTypedoc: true
521
285
  });
522
286
  let updated = false;
523
287
  if (!packageJson.devDependencies) {
@@ -540,14 +304,20 @@
540
304
  const standardScripts = {
541
305
  clean: "rimraf lib dist public",
542
306
  build: "vite build && tsc",
307
+ watch: 'concurrently "vite build --watch" "tsc -w" "vitest"',
308
+ test: "vitest run",
543
309
  lint: "eslint . --ext .js,.ts --max-warnings 0",
544
310
  lintfix: "npm run lint -- --fix",
545
311
  docs: "typedoc",
546
- upload_oss: "npm run docs && npx feng3d-cli oss_upload_dir",
547
- update: "npx feng3d-cli update && npm install",
548
312
  prepublishOnly: "node scripts/prepublish.js",
313
+ release: "npm run clean && npm run lint && npm test && npm run build && npm run docs && npm publish",
549
314
  postpublish: "node scripts/postpublish.js"
550
315
  };
316
+ const examplesDir = path.join(projectDir, "examples");
317
+ if (await fs.pathExists(examplesDir)) {
318
+ standardScripts["examples:dev"] = "cd examples && npm run dev";
319
+ standardScripts.postdocs = "node scripts/postdocs.js && cd examples && vite build --outDir ../public";
320
+ }
551
321
  for (const [key, value] of Object.entries(standardScripts)) {
552
322
  if (!(key in packageJson.scripts)) {
553
323
  packageJson.scripts[key] = value;
@@ -555,8 +325,37 @@
555
325
  console.log(chalk.gray(` 添加: scripts.${key}`));
556
326
  }
557
327
  }
328
+ if (!packageJson.type) {
329
+ packageJson.type = "module";
330
+ updated = true;
331
+ console.log(chalk.gray(' 添加: type = "module"'));
332
+ }
333
+ const entryPoints = {
334
+ main: "./src/index.ts",
335
+ types: "./src/index.ts",
336
+ module: "./src/index.ts"
337
+ };
338
+ for (const [key, value] of Object.entries(entryPoints)) {
339
+ if (!(key in packageJson)) {
340
+ packageJson[key] = value;
341
+ updated = true;
342
+ console.log(chalk.gray(` 添加: ${key} = "${value}"`));
343
+ }
344
+ }
345
+ if (!packageJson.exports) {
346
+ packageJson.exports = {
347
+ ".": {
348
+ types: "./src/index.ts",
349
+ import: "./src/index.ts",
350
+ require: "./src/index.ts"
351
+ }
352
+ };
353
+ updated = true;
354
+ console.log(chalk.gray(" 添加: exports"));
355
+ }
558
356
  if (updated) {
559
- let newContent = JSON.stringify(packageJson, null, indent);
357
+ const orderedPackageJson = reorderPackageJson(packageJson);
358
+ let newContent = JSON.stringify(orderedPackageJson, null, indent);
560
359
  if (hasTrailingNewline) {
561
360
  newContent += "\n";
562
361
  }
@@ -599,67 +398,14 @@
599
398
  console.log(chalk.gray(" 添加: lint-staged 配置"));
600
399
  }
601
400
  if (updated) {
602
- let newContent = JSON.stringify(packageJson, null, indent);
401
+ const orderedPackageJson = reorderPackageJson(packageJson);
402
+ let newContent = JSON.stringify(orderedPackageJson, null, indent);
603
403
  if (hasTrailingNewline) {
604
404
  newContent += "\n";
605
405
  }
606
406
  await fs.writeFile(packageJsonPath, newContent);
607
407
  }
608
408
  }
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
409
  async function createProject(name, options) {
664
410
  const projectDir = path.join(options.directory, name);
665
411
  if (await fs.pathExists(projectDir)) {
@@ -682,12 +428,7 @@
682
428
  const typedocConfig = getTypedocConfig({ repoName: name });
683
429
  await fs.writeJson(path.join(projectDir, "typedoc.json"), typedocConfig, { spaces: 4 });
684
430
  console.log(chalk.gray(" 创建: typedoc.json"));
685
- await fs.writeFile(path.join(projectDir, "src/index.ts"), `/**
686
- * @feng3d/${name}
687
- */
688
-
689
- export {};
690
- `);
431
+ await fs.writeFile(path.join(projectDir, "src/index.ts"), getSrcIndexTemplate({ name: `@feng3d/${name}` }));
691
432
  console.log(chalk.gray(" 创建: src/index.ts"));
692
433
  await fs.writeFile(path.join(projectDir, "README.md"), `# @feng3d/${name}
693
434
  `);
@@ -708,12 +449,13 @@ export {};
708
449
  await fs.writeFile(path.join(projectDir, "scripts/postpublish.js"), getPostpublishScriptTemplate());
709
450
  console.log(chalk.gray(" 创建: scripts/prepublish.js"));
710
451
  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"));
452
+ if (options.examples !== false) {
453
+ await fs.writeFile(path.join(projectDir, "scripts/postdocs.js"), getPostdocsScriptTemplate());
454
+ console.log(chalk.gray(" 创建: scripts/postdocs.js"));
455
+ }
713
456
  }
714
457
  function createPackageJson(name, options) {
715
458
  const scripts = {
716
- dev: "cd examples && npm run dev",
717
459
  clean: "rimraf lib dist public",
718
460
  build: "vite build && tsc",
719
461
  types: "tsc",
@@ -725,6 +467,10 @@ export {};
725
467
  prepublishOnly: "node scripts/prepublish.js",
726
468
  postpublish: "node scripts/postpublish.js"
727
469
  };
470
+ if (options.examples !== false) {
471
+ scripts["examples:dev"] = "cd examples && npm run dev";
472
+ scripts.postdocs = "node scripts/postdocs.js && cd examples && vite build --outDir ../public";
473
+ }
728
474
  if (options.vitest !== false) {
729
475
  scripts.test = "vitest run";
730
476
  scripts["test:watch"] = "vitest";
@@ -761,31 +507,29 @@ export {};
761
507
  })
762
508
  };
763
509
  }
764
- exports2.DEFAULT_CONFIG = DEFAULT_CONFIG;
765
- exports2.DEFAULT_UPDATE_CONFIG = DEFAULT_UPDATE_CONFIG;
766
510
  exports2.VERSIONS = VERSIONS;
767
511
  exports2.createProject = createProject;
768
- exports2.detectSchemaPath = detectSchemaPath;
769
512
  exports2.getCursorrrulesTemplate = getCursorrrulesTemplate;
770
513
  exports2.getDevDependencies = getDevDependencies;
771
514
  exports2.getEslintConfigTemplate = getEslintConfigTemplate;
772
- exports2.getFeng3dConfigTemplate = getFeng3dConfigTemplate;
773
515
  exports2.getGitignoreTemplate = getGitignoreTemplate;
774
516
  exports2.getHuskyPreCommitTemplate = getHuskyPreCommitTemplate;
775
517
  exports2.getLicenseTemplate = getLicenseTemplate;
776
518
  exports2.getPagesWorkflowTemplate = getPagesWorkflowTemplate;
519
+ exports2.getPostdocsScriptTemplate = getPostdocsScriptTemplate;
777
520
  exports2.getPostpublishScriptTemplate = getPostpublishScriptTemplate;
778
521
  exports2.getPrepublishScriptTemplate = getPrepublishScriptTemplate;
779
522
  exports2.getPublishWorkflowTemplate = getPublishWorkflowTemplate;
780
523
  exports2.getPullRequestWorkflowTemplate = getPullRequestWorkflowTemplate;
524
+ exports2.getSrcIndexTemplate = getSrcIndexTemplate;
781
525
  exports2.getTestIndexTemplate = getTestIndexTemplate;
782
526
  exports2.getTsconfigTemplate = getTsconfigTemplate;
783
527
  exports2.getTsconfigTemplateString = getTsconfigTemplateString;
784
528
  exports2.getTypedocConfig = getTypedocConfig;
785
529
  exports2.getTypedocConfigTemplate = getTypedocConfigTemplate;
530
+ exports2.getUploadOssWorkflowTemplate = getUploadOssWorkflowTemplate;
786
531
  exports2.getViteConfigTemplate = getViteConfigTemplate;
787
532
  exports2.getVscodeSettingsTemplate = getVscodeSettingsTemplate;
788
- exports2.ossUploadDir = ossUploadDir;
789
533
  exports2.updateProject = updateProject;
790
534
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
791
535
  }));