@playcraft/build 0.0.8 → 0.0.10

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.
Files changed (73) hide show
  1. package/README.md +122 -6
  2. package/dist/analyzers/__tests__/optimization-analyzer.test.d.ts +1 -0
  3. package/dist/analyzers/__tests__/optimization-analyzer.test.js +169 -0
  4. package/dist/analyzers/build-analyzer.d.ts +98 -0
  5. package/dist/analyzers/build-analyzer.js +1160 -0
  6. package/dist/analyzers/enhanced-report-template.d.ts +13 -0
  7. package/dist/analyzers/enhanced-report-template.js +957 -0
  8. package/dist/analyzers/index.d.ts +6 -0
  9. package/dist/analyzers/index.js +9 -0
  10. package/dist/analyzers/optimization-analyzer.d.ts +88 -0
  11. package/dist/analyzers/optimization-analyzer.js +278 -0
  12. package/dist/analyzers/playable-analyzer.d.ts +91 -0
  13. package/dist/analyzers/playable-analyzer.js +977 -0
  14. package/dist/analyzers/report-template.d.ts +50 -0
  15. package/dist/analyzers/report-template.js +591 -0
  16. package/dist/analyzers/scene-asset-collector.js +8 -0
  17. package/dist/base-builder.d.ts +9 -0
  18. package/dist/base-builder.js +156 -2
  19. package/dist/build-state-manager.d.ts +110 -0
  20. package/dist/build-state-manager.js +169 -0
  21. package/dist/generators/config-generator.d.ts +2 -0
  22. package/dist/generators/config-generator.js +179 -10
  23. package/dist/index.d.ts +8 -0
  24. package/dist/index.js +6 -0
  25. package/dist/loaders/playcanvas-loader.d.ts +7 -0
  26. package/dist/loaders/playcanvas-loader.js +17 -0
  27. package/dist/platforms/adikteev.js +4 -2
  28. package/dist/platforms/applovin.js +9 -3
  29. package/dist/platforms/inmobi.js +4 -2
  30. package/dist/platforms/ironsource.js +4 -1
  31. package/dist/platforms/liftoff.js +8 -3
  32. package/dist/platforms/snapchat.js +8 -2
  33. package/dist/platforms/unity.js +8 -2
  34. package/dist/playable-builder.js +3 -1
  35. package/dist/state/build-state-manager.d.ts +174 -0
  36. package/dist/state/build-state-manager.js +235 -0
  37. package/dist/state/index.d.ts +4 -0
  38. package/dist/state/index.js +2 -0
  39. package/dist/state/state-to-report-converter.d.ts +141 -0
  40. package/dist/state/state-to-report-converter.js +177 -0
  41. package/dist/types.d.ts +1 -0
  42. package/dist/utils.d.ts +4 -0
  43. package/dist/utils.js +11 -0
  44. package/dist/vite/config-builder.js +11 -1
  45. package/dist/vite/platform-configs.d.ts +1 -0
  46. package/dist/vite/platform-configs.js +1 -0
  47. package/dist/vite/plugin-build-state.d.ts +13 -0
  48. package/dist/vite/plugin-build-state.js +147 -0
  49. package/dist/vite/plugin-esm-html-generator.js +11 -2
  50. package/dist/vite/plugin-platform.js +3 -1
  51. package/dist/vite/plugin-playcanvas.d.ts +1 -0
  52. package/dist/vite/plugin-playcanvas.js +160 -20
  53. package/dist/vite/plugin-source-builder.js +1 -0
  54. package/dist/vite/plugin-template-minifier.d.ts +20 -0
  55. package/dist/vite/plugin-template-minifier.js +392 -0
  56. package/package.json +12 -12
  57. package/templates/patches/one-page-mraid-resize-canvas.js +18 -4
  58. package/dist/templates/__loading__.js +0 -100
  59. package/dist/templates/__modules__.js +0 -47
  60. package/dist/templates/__settings__.template.js +0 -20
  61. package/dist/templates/__start__.js +0 -332
  62. package/dist/templates/index.html +0 -18
  63. package/dist/templates/logo.png +0 -0
  64. package/dist/templates/manifest.json +0 -1
  65. package/dist/templates/patches/cannon.min.js +0 -28
  66. package/dist/templates/patches/lz4.js +0 -10
  67. package/dist/templates/patches/one-page-http-get.js +0 -20
  68. package/dist/templates/patches/one-page-inline-game-scripts.js +0 -52
  69. package/dist/templates/patches/one-page-mraid-resize-canvas.js +0 -46
  70. package/dist/templates/patches/p2.min.js +0 -27
  71. package/dist/templates/patches/playcraft-no-xhr.js +0 -76
  72. package/dist/templates/playcanvas-stable.min.js +0 -16363
  73. package/dist/templates/styles.css +0 -43
@@ -5,6 +5,8 @@ import { build as viteBuild } from 'vite';
5
5
  import { visualizer } from 'rollup-plugin-visualizer';
6
6
  import { viteSourceBuilderPlugin } from './vite/plugin-source-builder.js';
7
7
  import { loadPlayCanvasProject } from './loaders/playcanvas-loader.js';
8
+ import { BuildAnalyzer } from './analyzers/build-analyzer.js';
9
+ import { BuildStateManager } from './state/index.js';
8
10
  const __filename = fileURLToPath(import.meta.url);
9
11
  const __dirname = path.dirname(__filename);
10
12
  /**
@@ -20,12 +22,19 @@ export class BaseBuilder {
20
22
  constructor(projectDir, options) {
21
23
  this.projectDir = projectDir;
22
24
  this.options = options;
25
+ this.stateManager = new BuildStateManager(projectDir, options.outputDir);
23
26
  console.log(`[BaseBuilder] 初始化: projectDir=${projectDir}, outputDir=${options.outputDir}`);
24
27
  }
25
28
  /**
26
29
  * 执行基础构建
27
30
  */
28
31
  async build() {
32
+ // 开始构建阶段
33
+ this.stateManager.startStage('base-build', {
34
+ selectedScenes: this.options.selectedScenes,
35
+ analyze: this.options.analyze,
36
+ clean: this.options.clean,
37
+ });
29
38
  // 1. 检测项目类型
30
39
  const projectType = await this.detectProjectType();
31
40
  let result;
@@ -41,6 +50,16 @@ export class BaseBuilder {
41
50
  }
42
51
  // 保存构建元数据
43
52
  await this.saveBuildMetadata(result.metadata);
53
+ // 记录构建产物
54
+ await this.recordBuildOutput(result);
55
+ // 结束构建阶段
56
+ this.stateManager.endStage();
57
+ // 生成分析报告(如果启用)
58
+ if (this.options.analyze) {
59
+ // 只在分析模式下保存状态文件(分析报告需要用到)
60
+ await this.stateManager.saveState();
61
+ await this.generateAnalysisReport();
62
+ }
44
63
  return result;
45
64
  }
46
65
  /**
@@ -413,7 +432,8 @@ export class BaseBuilder {
413
432
  build: {
414
433
  outDir: this.options.outputDir,
415
434
  emptyOutDir: shouldEmptyOutDir,
416
- write: false, // 不写入文件,仅用于触发插件
435
+ // 如果启用了分析报告,需要 write: true 以便 visualizer 插件能够生成报告
436
+ write: this.options.analyze ? true : false,
417
437
  rollupOptions: {
418
438
  input: {
419
439
  // 使用虚拟入口,仅用于触发插件
@@ -446,6 +466,19 @@ export class BaseBuilder {
446
466
  buildMode: buildMode,
447
467
  importMap: importMap,
448
468
  }),
469
+ // 打包分析报告(如果启用)
470
+ ...(this.options.analyze ? [
471
+ visualizer({
472
+ filename: this.options.analyzeReportPath
473
+ ? path.join(this.options.outputDir, this.options.analyzeReportPath)
474
+ : path.join(this.options.outputDir, 'base-bundle-report.html'),
475
+ template: 'treemap',
476
+ gzipSize: true,
477
+ brotliSize: true,
478
+ open: false,
479
+ sourcemap: false,
480
+ }),
481
+ ] : []),
449
482
  ],
450
483
  } : {
451
484
  // 传统模式:打包用户脚本
@@ -500,11 +533,16 @@ export class BaseBuilder {
500
533
  gzipSize: true,
501
534
  brotliSize: true,
502
535
  open: false,
503
- sourcemap: false,
504
536
  }),
505
537
  ] : []),
506
538
  ],
507
539
  };
540
+ // DEBUG: 检查 analyze 配置
541
+ if (this.options.analyze) {
542
+ console.log('[BaseBuilder] analyze 已启用,报告路径:', this.options.analyzeReportPath
543
+ ? path.join(this.options.outputDir, this.options.analyzeReportPath)
544
+ : path.join(this.options.outputDir, 'base-bundle-report.html'));
545
+ }
508
546
  // 2. 执行 Vite 构建
509
547
  await viteBuild(viteConfig);
510
548
  // 3. 扫描生成的文件
@@ -608,4 +646,120 @@ export class BaseBuilder {
608
646
  }
609
647
  }
610
648
  }
649
+ /**
650
+ * 记录构建产物信息到状态管理器
651
+ */
652
+ async recordBuildOutput(result) {
653
+ console.log('[BaseBuilder] 记录构建产物信息...');
654
+ try {
655
+ // 记录 HTML 文件
656
+ if (result.files.html) {
657
+ const stat = await fs.stat(result.files.html);
658
+ this.stateManager.recordAsset('index.html', 'index.html', result.files.html, stat.size, 'html');
659
+ this.stateManager.updateAsset('index.html', 'base-build', 'index.html', 'index.html', stat.size);
660
+ }
661
+ // 记录引擎文件
662
+ if (result.files.engine) {
663
+ const stat = await fs.stat(result.files.engine);
664
+ const engineName = path.basename(result.files.engine);
665
+ this.stateManager.recordAsset('playcanvas-engine', engineName, result.files.engine, stat.size, 'script');
666
+ this.stateManager.updateAsset('playcanvas-engine', 'base-build', engineName, engineName, stat.size);
667
+ }
668
+ // 记录 config.json
669
+ if (result.files.config) {
670
+ const stat = await fs.stat(result.files.config);
671
+ this.stateManager.recordAsset('config.json', 'config.json', result.files.config, stat.size, 'json');
672
+ this.stateManager.updateAsset('config.json', 'base-build', 'config.json', 'config.json', stat.size);
673
+ }
674
+ // 记录其他脚本文件
675
+ const scriptFiles = [
676
+ { path: result.files.start, id: '__start__.js' },
677
+ { path: result.files.settings, id: '__settings__.js' },
678
+ { path: result.files.modules, id: '__modules__.js' },
679
+ ];
680
+ for (const { path: filePath, id } of scriptFiles) {
681
+ if (filePath) {
682
+ try {
683
+ const stat = await fs.stat(filePath);
684
+ const fileName = path.basename(filePath);
685
+ this.stateManager.recordAsset(id, fileName, filePath, stat.size, 'script');
686
+ this.stateManager.updateAsset(id, 'base-build', fileName, fileName, stat.size);
687
+ }
688
+ catch (error) {
689
+ // 文件可能不存在
690
+ }
691
+ }
692
+ }
693
+ // 记录场景文件
694
+ for (const scenePath of result.files.scenes) {
695
+ try {
696
+ const stat = await fs.stat(scenePath);
697
+ const sceneName = path.basename(scenePath);
698
+ const sceneId = `scene-${sceneName}`;
699
+ this.stateManager.recordAsset(sceneId, sceneName, scenePath, stat.size, 'json');
700
+ this.stateManager.updateAsset(sceneId, 'base-build', sceneName, path.relative(this.options.outputDir, scenePath), stat.size);
701
+ }
702
+ catch (error) {
703
+ // 忽略错误
704
+ }
705
+ }
706
+ // 记录资产文件
707
+ for (const assetPath of result.files.assets) {
708
+ try {
709
+ const stat = await fs.stat(assetPath);
710
+ const assetName = path.basename(assetPath);
711
+ const assetId = `asset-${assetName}`;
712
+ // 根据文件扩展名判断类型
713
+ const ext = path.extname(assetName).toLowerCase();
714
+ let assetType = 'other';
715
+ if (['.png', '.jpg', '.jpeg', '.webp', '.gif'].includes(ext)) {
716
+ assetType = 'texture';
717
+ }
718
+ else if (['.mp3', '.wav', '.ogg'].includes(ext)) {
719
+ assetType = 'audio';
720
+ }
721
+ else if (['.glb', '.gltf', '.obj', '.fbx'].includes(ext)) {
722
+ assetType = 'model';
723
+ }
724
+ this.stateManager.recordAsset(assetId, assetName, assetPath, stat.size, assetType);
725
+ this.stateManager.updateAsset(assetId, 'base-build', assetName, path.relative(this.options.outputDir, assetPath), stat.size);
726
+ }
727
+ catch (error) {
728
+ // 忽略错误
729
+ }
730
+ }
731
+ console.log('[BaseBuilder] 构建产物信息已记录');
732
+ }
733
+ catch (error) {
734
+ console.error('[BaseBuilder] 记录构建产物信息失败:', error);
735
+ }
736
+ }
737
+ /**
738
+ * 生成构建分析报告
739
+ */
740
+ async generateAnalysisReport() {
741
+ console.log('\n[BaseBuilder] 生成构建分析报告...');
742
+ try {
743
+ const analyzer = new BuildAnalyzer(this.options.outputDir, this.options.outputDir);
744
+ const report = await analyzer.analyze();
745
+ const reportPath = await analyzer.generateHTMLReport(report);
746
+ // 删除 rollup-plugin-visualizer 生成的报告文件
747
+ const oldReportPath = path.join(this.options.outputDir, 'base-bundle-report.html');
748
+ try {
749
+ await fs.unlink(oldReportPath);
750
+ console.log('[BaseBuilder] 已删除旧的分析报告: base-bundle-report.html');
751
+ }
752
+ catch (error) {
753
+ // 文件可能不存在,忽略错误
754
+ }
755
+ console.log(`\n📊 构建分析报告:`);
756
+ console.log(` 文件总数: ${report.totalFiles}`);
757
+ console.log(` 当前总大小: ${report.totalSizeFormatted}`);
758
+ console.log(` 预估单 HTML 大小: ${report.estimatedHtmlSizeFormatted}`);
759
+ console.log(` 报告路径: ${reportPath}\n`);
760
+ }
761
+ catch (error) {
762
+ console.error('[BaseBuilder] 生成分析报告失败:', error);
763
+ }
764
+ }
611
765
  }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * 资源类型
3
+ */
4
+ export type AssetType = 'script' | 'texture' | 'audio' | 'json' | 'css' | 'html' | 'other';
5
+ /**
6
+ * 资源处理记录
7
+ */
8
+ export interface AssetProcessingRecord {
9
+ originalName: string;
10
+ originalPath: string;
11
+ originalSize: number;
12
+ type: AssetType;
13
+ processedName?: string;
14
+ processedSize?: number;
15
+ processedPath?: string;
16
+ compressionRate?: number;
17
+ minified?: boolean;
18
+ inlined?: boolean;
19
+ finalLocation?: 'inline' | 'external';
20
+ finalReference?: string;
21
+ }
22
+ /**
23
+ * 构建状态
24
+ */
25
+ export interface BuildState {
26
+ version: string;
27
+ buildTime: string;
28
+ platform?: string;
29
+ buildMode?: string;
30
+ assets: {
31
+ [assetId: string]: AssetProcessingRecord;
32
+ };
33
+ output: {
34
+ totalSize: number;
35
+ htmlSize?: number;
36
+ files: {
37
+ [filename: string]: {
38
+ size: number;
39
+ type: string;
40
+ };
41
+ };
42
+ };
43
+ }
44
+ /**
45
+ * 构建状态管理器
46
+ *
47
+ * 职责:
48
+ * 1. 记录打包过程中每个资源的处理信息
49
+ * 2. 追踪资源的转换过程(原始大小 -> 处理后大小)
50
+ * 3. 生成 state.json 文件供分析使用
51
+ */
52
+ export declare class BuildStateManager {
53
+ private state;
54
+ private stateFilePath;
55
+ constructor(outputDir: string, platform?: string, buildMode?: string);
56
+ /**
57
+ * 记录资源的原始信息
58
+ */
59
+ recordAsset(assetId: string, originalName: string, originalPath: string, originalSize: number, type: AssetType): void;
60
+ /**
61
+ * 更新资源的处理信息
62
+ */
63
+ updateAssetProcessing(assetId: string, processedName: string, processedSize: number, processedPath: string, options?: {
64
+ minified?: boolean;
65
+ inlined?: boolean;
66
+ }): void;
67
+ /**
68
+ * 更新资源的最终位置
69
+ */
70
+ updateAssetFinalLocation(assetId: string, finalLocation: 'inline' | 'external', finalReference?: string): void;
71
+ /**
72
+ * 记录输出文件
73
+ */
74
+ recordOutputFile(filename: string, size: number, type: string): void;
75
+ /**
76
+ * 获取状态
77
+ */
78
+ getState(): BuildState;
79
+ /**
80
+ * 保存状态到文件
81
+ */
82
+ save(): Promise<void>;
83
+ /**
84
+ * 从文件加载状态
85
+ */
86
+ static load(outputDir: string): Promise<BuildStateManager | null>;
87
+ /**
88
+ * 生成分析报告数据
89
+ */
90
+ generateAnalysisData(): {
91
+ assets: Array<{
92
+ name: string;
93
+ originalSize: number;
94
+ processedSize: number;
95
+ compressionRate: number;
96
+ type: AssetType;
97
+ finalLocation: string;
98
+ }>;
99
+ summary: {
100
+ totalOriginalSize: number;
101
+ totalProcessedSize: number;
102
+ totalCompressionRate: number;
103
+ assetCount: number;
104
+ };
105
+ };
106
+ /**
107
+ * 打印摘要
108
+ */
109
+ printSummary(): void;
110
+ }
@@ -0,0 +1,169 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { formatBytes } from './utils.js';
4
+ /**
5
+ * 构建状态管理器
6
+ *
7
+ * 职责:
8
+ * 1. 记录打包过程中每个资源的处理信息
9
+ * 2. 追踪资源的转换过程(原始大小 -> 处理后大小)
10
+ * 3. 生成 state.json 文件供分析使用
11
+ */
12
+ export class BuildStateManager {
13
+ constructor(outputDir, platform, buildMode) {
14
+ this.stateFilePath = path.join(outputDir, 'build-state.json');
15
+ this.state = {
16
+ version: '1.0.0',
17
+ buildTime: new Date().toISOString(),
18
+ platform,
19
+ buildMode,
20
+ assets: {},
21
+ output: {
22
+ totalSize: 0,
23
+ files: {},
24
+ },
25
+ };
26
+ }
27
+ /**
28
+ * 记录资源的原始信息
29
+ */
30
+ recordAsset(assetId, originalName, originalPath, originalSize, type) {
31
+ this.state.assets[assetId] = {
32
+ originalName,
33
+ originalPath,
34
+ originalSize,
35
+ type,
36
+ };
37
+ }
38
+ /**
39
+ * 更新资源的处理信息
40
+ */
41
+ updateAssetProcessing(assetId, processedName, processedSize, processedPath, options) {
42
+ const asset = this.state.assets[assetId];
43
+ if (!asset) {
44
+ console.warn(`[BuildState] 资源不存在: ${assetId}`);
45
+ return;
46
+ }
47
+ asset.processedName = processedName;
48
+ asset.processedSize = processedSize;
49
+ asset.processedPath = processedPath;
50
+ asset.minified = options?.minified;
51
+ asset.inlined = options?.inlined;
52
+ // 计算压缩率
53
+ if (asset.originalSize > 0) {
54
+ asset.compressionRate = ((asset.originalSize - processedSize) / asset.originalSize) * 100;
55
+ }
56
+ }
57
+ /**
58
+ * 更新资源的最终位置
59
+ */
60
+ updateAssetFinalLocation(assetId, finalLocation, finalReference) {
61
+ const asset = this.state.assets[assetId];
62
+ if (!asset) {
63
+ console.warn(`[BuildState] 资源不存在: ${assetId}`);
64
+ return;
65
+ }
66
+ asset.finalLocation = finalLocation;
67
+ asset.finalReference = finalReference;
68
+ }
69
+ /**
70
+ * 记录输出文件
71
+ */
72
+ recordOutputFile(filename, size, type) {
73
+ this.state.output.files[filename] = { size, type };
74
+ this.state.output.totalSize += size;
75
+ if (filename.endsWith('.html')) {
76
+ this.state.output.htmlSize = size;
77
+ }
78
+ }
79
+ /**
80
+ * 获取状态
81
+ */
82
+ getState() {
83
+ return this.state;
84
+ }
85
+ /**
86
+ * 保存状态到文件
87
+ */
88
+ async save() {
89
+ await fs.writeFile(this.stateFilePath, JSON.stringify(this.state, null, 2), 'utf-8');
90
+ console.log(`[BuildState] 状态已保存: ${this.stateFilePath}`);
91
+ }
92
+ /**
93
+ * 从文件加载状态
94
+ */
95
+ static async load(outputDir) {
96
+ const stateFilePath = path.join(outputDir, 'build-state.json');
97
+ try {
98
+ const content = await fs.readFile(stateFilePath, 'utf-8');
99
+ const state = JSON.parse(content);
100
+ const manager = new BuildStateManager(outputDir);
101
+ manager.state = state;
102
+ return manager;
103
+ }
104
+ catch (error) {
105
+ console.warn(`[BuildState] 无法加载状态文件: ${stateFilePath}`);
106
+ return null;
107
+ }
108
+ }
109
+ /**
110
+ * 生成分析报告数据
111
+ */
112
+ generateAnalysisData() {
113
+ const assets = Object.entries(this.state.assets).map(([id, asset]) => ({
114
+ name: asset.processedName || asset.originalName,
115
+ originalSize: asset.originalSize,
116
+ processedSize: asset.processedSize || asset.originalSize,
117
+ compressionRate: asset.compressionRate || 0,
118
+ type: asset.type,
119
+ finalLocation: asset.finalLocation || 'unknown',
120
+ }));
121
+ const totalOriginalSize = assets.reduce((sum, a) => sum + a.originalSize, 0);
122
+ const totalProcessedSize = assets.reduce((sum, a) => sum + a.processedSize, 0);
123
+ const totalCompressionRate = totalOriginalSize > 0
124
+ ? ((totalOriginalSize - totalProcessedSize) / totalOriginalSize) * 100
125
+ : 0;
126
+ return {
127
+ assets,
128
+ summary: {
129
+ totalOriginalSize,
130
+ totalProcessedSize,
131
+ totalCompressionRate,
132
+ assetCount: assets.length,
133
+ },
134
+ };
135
+ }
136
+ /**
137
+ * 打印摘要
138
+ */
139
+ printSummary() {
140
+ const { assets, summary } = this.generateAnalysisData();
141
+ console.log('\n📊 构建状态摘要');
142
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
143
+ console.log(`资源总数: ${summary.assetCount}`);
144
+ console.log(`原始大小: ${formatBytes(summary.totalOriginalSize)}`);
145
+ console.log(`处理后大小: ${formatBytes(summary.totalProcessedSize)}`);
146
+ console.log(`压缩率: ${summary.totalCompressionRate.toFixed(2)}%`);
147
+ console.log(`最终产物大小: ${formatBytes(this.state.output.totalSize)}`);
148
+ if (this.state.output.htmlSize) {
149
+ console.log(`HTML 大小: ${formatBytes(this.state.output.htmlSize)}`);
150
+ }
151
+ // 按类型分组
152
+ const byType = new Map();
153
+ for (const asset of assets) {
154
+ const existing = byType.get(asset.type) || { count: 0, originalSize: 0, processedSize: 0 };
155
+ existing.count++;
156
+ existing.originalSize += asset.originalSize;
157
+ existing.processedSize += asset.processedSize;
158
+ byType.set(asset.type, existing);
159
+ }
160
+ console.log('\n按类型统计:');
161
+ for (const [type, stats] of byType.entries()) {
162
+ const rate = stats.originalSize > 0
163
+ ? ((stats.originalSize - stats.processedSize) / stats.originalSize) * 100
164
+ : 0;
165
+ console.log(` ${type}: ${stats.count} 个, ${formatBytes(stats.originalSize)} → ${formatBytes(stats.processedSize)} (${rate.toFixed(1)}%)`);
166
+ }
167
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
168
+ }
169
+ }
@@ -3,6 +3,8 @@ import { PlayCraftProject } from '../loaders/playcraft-loader.js';
3
3
  export type ProjectConfig = PlayCanvasProject | PlayCraftProject;
4
4
  export interface GenerateConfigOptions {
5
5
  selectedScenes?: string[];
6
+ projectDir?: string;
7
+ stripMetadata?: boolean;
6
8
  }
7
9
  /**
8
10
  * 生成 PlayCanvas 构建产物格式的 config.json