@playcraft/build 0.0.2

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 (74) hide show
  1. package/README.md +96 -0
  2. package/dist/base-builder.d.ts +66 -0
  3. package/dist/base-builder.js +415 -0
  4. package/dist/converter.d.ts +35 -0
  5. package/dist/converter.js +148 -0
  6. package/dist/generators/config-generator.d.ts +7 -0
  7. package/dist/generators/config-generator.js +122 -0
  8. package/dist/generators/settings-generator.d.ts +14 -0
  9. package/dist/generators/settings-generator.js +100 -0
  10. package/dist/index.d.ts +14 -0
  11. package/dist/index.js +14 -0
  12. package/dist/loaders/playcanvas-loader.d.ts +10 -0
  13. package/dist/loaders/playcanvas-loader.js +18 -0
  14. package/dist/loaders/playcraft-loader.d.ts +10 -0
  15. package/dist/loaders/playcraft-loader.js +51 -0
  16. package/dist/platforms/applovin.d.ts +10 -0
  17. package/dist/platforms/applovin.js +67 -0
  18. package/dist/platforms/base.d.ts +29 -0
  19. package/dist/platforms/base.js +11 -0
  20. package/dist/platforms/bigo.d.ts +15 -0
  21. package/dist/platforms/bigo.js +77 -0
  22. package/dist/platforms/facebook.d.ts +9 -0
  23. package/dist/platforms/facebook.js +37 -0
  24. package/dist/platforms/google.d.ts +10 -0
  25. package/dist/platforms/google.js +53 -0
  26. package/dist/platforms/index.d.ts +14 -0
  27. package/dist/platforms/index.js +47 -0
  28. package/dist/platforms/ironsource.d.ts +10 -0
  29. package/dist/platforms/ironsource.js +71 -0
  30. package/dist/platforms/liftoff.d.ts +10 -0
  31. package/dist/platforms/liftoff.js +56 -0
  32. package/dist/platforms/moloco.d.ts +10 -0
  33. package/dist/platforms/moloco.js +53 -0
  34. package/dist/platforms/snapchat.d.ts +10 -0
  35. package/dist/platforms/snapchat.js +59 -0
  36. package/dist/platforms/tiktok.d.ts +15 -0
  37. package/dist/platforms/tiktok.js +65 -0
  38. package/dist/platforms/unity.d.ts +10 -0
  39. package/dist/platforms/unity.js +69 -0
  40. package/dist/playable-builder.d.ts +97 -0
  41. package/dist/playable-builder.js +590 -0
  42. package/dist/types.d.ts +90 -0
  43. package/dist/types.js +1 -0
  44. package/dist/vite/config-builder.d.ts +15 -0
  45. package/dist/vite/config-builder.js +212 -0
  46. package/dist/vite/platform-configs.d.ts +38 -0
  47. package/dist/vite/platform-configs.js +257 -0
  48. package/dist/vite/plugin-model-compression.d.ts +11 -0
  49. package/dist/vite/plugin-model-compression.js +63 -0
  50. package/dist/vite/plugin-platform.d.ts +17 -0
  51. package/dist/vite/plugin-platform.js +241 -0
  52. package/dist/vite/plugin-playcanvas.d.ts +18 -0
  53. package/dist/vite/plugin-playcanvas.js +711 -0
  54. package/dist/vite/plugin-source-builder.d.ts +15 -0
  55. package/dist/vite/plugin-source-builder.js +344 -0
  56. package/dist/vite-builder.d.ts +51 -0
  57. package/dist/vite-builder.js +122 -0
  58. package/package.json +51 -0
  59. package/templates/__loading__.js +100 -0
  60. package/templates/__modules__.js +47 -0
  61. package/templates/__settings__.template.js +20 -0
  62. package/templates/__start__.js +332 -0
  63. package/templates/index.html +18 -0
  64. package/templates/logo.png +0 -0
  65. package/templates/manifest.json +1 -0
  66. package/templates/patches/cannon.min.js +28 -0
  67. package/templates/patches/lz4.js +10 -0
  68. package/templates/patches/one-page-http-get.js +20 -0
  69. package/templates/patches/one-page-inline-game-scripts.js +20 -0
  70. package/templates/patches/one-page-mraid-resize-canvas.js +46 -0
  71. package/templates/patches/p2.min.js +27 -0
  72. package/templates/patches/playcraft-no-xhr.js +52 -0
  73. package/templates/playcanvas-stable.min.js +16363 -0
  74. package/templates/styles.css +43 -0
@@ -0,0 +1,15 @@
1
+ import { type UserConfig } from 'vite';
2
+ import type { Platform, BuildOptions } from '../types.js';
3
+ export declare class ViteConfigBuilder {
4
+ private baseBuildDir;
5
+ private platform;
6
+ private options;
7
+ constructor(baseBuildDir: string, platform: Platform, options: BuildOptions);
8
+ create(): UserConfig;
9
+ private getPlatformConfig;
10
+ private shouldMinifyCSS;
11
+ private shouldMinifyJS;
12
+ private createPlugins;
13
+ private getPlayableOptions;
14
+ private resolveExternFiles;
15
+ }
@@ -0,0 +1,212 @@
1
+ import { createLogger, defineConfig } from 'vite';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { viteSingleFile } from 'vite-plugin-singlefile';
5
+ import viteImagemin from '@vheemstra/vite-plugin-imagemin';
6
+ import imageminMozjpeg from 'imagemin-mozjpeg';
7
+ import imageminPngquant from 'imagemin-pngquant';
8
+ import imageminWebp from 'imagemin-webp';
9
+ import { visualizer } from 'rollup-plugin-visualizer';
10
+ import { PLATFORM_CONFIGS } from './platform-configs.js';
11
+ import { vitePlayCanvasPlugin } from './plugin-playcanvas.js';
12
+ import { vitePlatformPlugin } from './plugin-platform.js';
13
+ import { viteModelCompressionPlugin } from './plugin-model-compression.js';
14
+ import { createPlatformAdapter } from '../platforms/index.js';
15
+ export class ViteConfigBuilder {
16
+ constructor(baseBuildDir, platform, options) {
17
+ this.baseBuildDir = baseBuildDir;
18
+ this.platform = platform;
19
+ this.options = options;
20
+ }
21
+ create() {
22
+ const platformConfig = this.getPlatformConfig();
23
+ const outputDir = this.options.outputDir || './dist';
24
+ const playableOptions = this.getPlayableOptions(platformConfig);
25
+ const rootDir = fs.realpathSync(this.baseBuildDir);
26
+ const logger = createLogger();
27
+ const customLogger = {
28
+ ...logger,
29
+ warn(msg, options) {
30
+ if (typeof msg === 'string' && msg.includes('can\'t be bundled without type="module"')) {
31
+ return;
32
+ }
33
+ logger.warn(msg, options);
34
+ },
35
+ };
36
+ return defineConfig({
37
+ root: rootDir,
38
+ base: './',
39
+ customLogger,
40
+ build: {
41
+ outDir: outputDir,
42
+ emptyOutDir: true,
43
+ // CSS 压缩
44
+ cssMinify: this.shouldMinifyCSS(platformConfig) ? 'lightningcss' : false,
45
+ // JS 压缩
46
+ minify: this.shouldMinifyJS(platformConfig) ? 'terser' : false,
47
+ terserOptions: {
48
+ compress: {
49
+ drop_console: true,
50
+ drop_debugger: true,
51
+ },
52
+ },
53
+ // 资源内联阈值(所有资源都内联)
54
+ assetsInlineLimit: Infinity,
55
+ // 不生成 sourcemap(Playable Ads 不需要)
56
+ sourcemap: platformConfig.includeSourcemap,
57
+ rollupOptions: {
58
+ input: {
59
+ main: path.join(rootDir, 'index.html'),
60
+ },
61
+ onwarn(warning, warn) {
62
+ const message = typeof warning === 'string' ? warning : warning.message;
63
+ if (message && message.includes('can\'t be bundled without type="module"')) {
64
+ return;
65
+ }
66
+ warn(warning);
67
+ },
68
+ },
69
+ },
70
+ plugins: this.createPlugins(platformConfig, outputDir, playableOptions),
71
+ });
72
+ }
73
+ getPlatformConfig() {
74
+ const baseConfig = PLATFORM_CONFIGS[this.platform];
75
+ const resolvedOutputFormat = this.options.format ?? baseConfig.outputFormat;
76
+ // 根据选项覆盖配置
77
+ return {
78
+ ...baseConfig,
79
+ outputFormat: resolvedOutputFormat,
80
+ minifyCSS: this.options.cssMinify ?? baseConfig.minifyCSS,
81
+ minifyJS: this.options.jsMinify ?? baseConfig.minifyJS,
82
+ compressImages: this.options.compressImages ?? baseConfig.compressImages,
83
+ compressModels: this.options.compressModels ?? baseConfig.compressModels,
84
+ modelCompression: {
85
+ method: this.options.modelCompression ?? baseConfig.modelCompression?.method ?? 'draco',
86
+ quality: baseConfig.modelCompression?.quality ?? 0.8,
87
+ },
88
+ };
89
+ }
90
+ shouldMinifyCSS(config) {
91
+ return config.minifyCSS;
92
+ }
93
+ shouldMinifyJS(config) {
94
+ return config.minifyJS;
95
+ }
96
+ createPlugins(platformConfig, outputDir, playableOptions) {
97
+ const plugins = [];
98
+ // 1. PlayCanvas 资源转换插件(最先执行)
99
+ plugins.push(vitePlayCanvasPlugin({
100
+ baseBuildDir: this.baseBuildDir,
101
+ inlineScripts: true,
102
+ convertDataUrls: platformConfig.outputFormat === 'html',
103
+ outputFormat: playableOptions.outputFormat,
104
+ patchXhrOut: playableOptions.patchXhrOut,
105
+ inlineGameScripts: playableOptions.inlineGameScripts,
106
+ compressEngine: playableOptions.compressEngine,
107
+ configJsonInline: playableOptions.configJsonInline,
108
+ mraidSupport: playableOptions.mraidSupport,
109
+ ammoReplacement: this.options.ammoReplacement,
110
+ }));
111
+ // 2. 平台特定插件
112
+ const platformAdapter = createPlatformAdapter(this.options);
113
+ plugins.push(vitePlatformPlugin({
114
+ platform: this.platform,
115
+ adapter: platformAdapter,
116
+ outputDir: outputDir,
117
+ baseBuildDir: this.baseBuildDir,
118
+ outputFormat: platformConfig.outputFormat,
119
+ externFiles: playableOptions.externFiles,
120
+ mraidSupport: playableOptions.mraidSupport,
121
+ }));
122
+ // 3. 图片压缩插件
123
+ if (platformConfig.compressImages) {
124
+ const imageQuality = platformConfig.imageQuality || {
125
+ jpg: 75,
126
+ png: [0.7, 0.8],
127
+ webp: 75,
128
+ };
129
+ const jpegQuality = this.options.imageQuality ?? imageQuality.jpg ?? 75;
130
+ const pngQuality = Array.isArray(imageQuality.png) && imageQuality.png.length >= 2
131
+ ? [imageQuality.png[0], imageQuality.png[1]]
132
+ : [0.7, 0.8];
133
+ const webpQuality = imageQuality.webp ?? 75;
134
+ const imageminPlugin = viteImagemin({
135
+ plugins: {
136
+ jpg: [imageminMozjpeg({ quality: jpegQuality })],
137
+ jpeg: [imageminMozjpeg({ quality: jpegQuality })],
138
+ png: [imageminPngquant({ quality: pngQuality })],
139
+ },
140
+ makeWebp: this.options.convertToWebP !== false ? {
141
+ plugins: {
142
+ jpg: [imageminWebp({ quality: webpQuality })],
143
+ jpeg: [imageminWebp({ quality: webpQuality })],
144
+ png: [imageminWebp({ quality: webpQuality })],
145
+ },
146
+ } : undefined,
147
+ });
148
+ if (imageminPlugin) {
149
+ plugins.push(imageminPlugin);
150
+ }
151
+ }
152
+ // 4. 模型压缩插件
153
+ if (platformConfig.compressModels) {
154
+ plugins.push(viteModelCompressionPlugin({
155
+ quality: platformConfig.modelCompression?.quality ?? 0.8,
156
+ method: platformConfig.modelCompression?.method ?? 'draco',
157
+ enabled: true,
158
+ }));
159
+ }
160
+ // 5. 单文件输出插件(仅HTML格式)
161
+ if (platformConfig.outputFormat === 'html') {
162
+ plugins.push(viteSingleFile({
163
+ removeViteModuleLoader: true,
164
+ }));
165
+ }
166
+ // 6. 打包分析报告
167
+ if (this.options.analyze) {
168
+ const reportPath = this.options.analyzeReportPath
169
+ ? this.options.analyzeReportPath
170
+ : path.join(outputDir, 'bundle-report.html');
171
+ plugins.push(visualizer({
172
+ filename: reportPath,
173
+ template: 'treemap',
174
+ gzipSize: true,
175
+ brotliSize: true,
176
+ open: false,
177
+ }));
178
+ }
179
+ return plugins.filter(Boolean);
180
+ }
181
+ getPlayableOptions(config) {
182
+ const playable = config.playable ?? {};
183
+ const externFiles = this.resolveExternFiles(playable.externFiles);
184
+ return {
185
+ outputFormat: config.outputFormat,
186
+ patchXhrOut: this.options.patchXhrOut ?? playable.patchXhrOut ?? false,
187
+ inlineGameScripts: this.options.inlineGameScripts ?? playable.inlineGameScripts ?? false,
188
+ compressEngine: this.options.compressEngine ?? playable.compressEngine ?? false,
189
+ configJsonInline: playable.configJsonInline ?? (config.outputFormat === 'html'),
190
+ externFiles,
191
+ mraidSupport: this.options.mraidSupport ?? playable.mraidSupport ?? false,
192
+ snapchatCta: this.options.snapchatCta ?? playable.snapchatCta ?? false,
193
+ };
194
+ }
195
+ resolveExternFiles(defaultValue) {
196
+ const optionValue = this.options.externFiles;
197
+ const raw = optionValue ?? defaultValue;
198
+ if (!raw) {
199
+ return undefined;
200
+ }
201
+ if (typeof raw === 'object') {
202
+ return raw;
203
+ }
204
+ const folderName = this.options.snapchat?.folderName;
205
+ const externalUrlPrefix = this.options.snapchat?.externalUrlPrefix;
206
+ return {
207
+ enabled: true,
208
+ folderName,
209
+ externalUrlPrefix,
210
+ };
211
+ }
212
+ }
@@ -0,0 +1,38 @@
1
+ import type { Platform, ExternFilesConfig } from '../types.js';
2
+ /**
3
+ * 平台 Vite 配置接口
4
+ */
5
+ export interface PlatformViteConfig {
6
+ sizeLimit: number;
7
+ outputFormat: 'html' | 'zip';
8
+ minifyCSS: boolean;
9
+ minifyJS: boolean;
10
+ compressImages: boolean;
11
+ compressModels: boolean;
12
+ injectScripts?: string[];
13
+ htmlTransforms?: Array<(html: string) => string>;
14
+ outputFileName: string;
15
+ includeSourcemap: boolean;
16
+ imageQuality?: {
17
+ jpg?: number;
18
+ png?: [number, number];
19
+ webp?: number;
20
+ };
21
+ modelCompression?: {
22
+ method?: 'draco' | 'meshopt';
23
+ quality?: number;
24
+ };
25
+ playable?: {
26
+ patchXhrOut?: boolean;
27
+ inlineGameScripts?: boolean;
28
+ compressEngine?: boolean;
29
+ configJsonInline?: boolean;
30
+ externFiles?: boolean | ExternFilesConfig;
31
+ mraidSupport?: boolean;
32
+ snapchatCta?: boolean;
33
+ };
34
+ }
35
+ /**
36
+ * 平台配置映射
37
+ */
38
+ export declare const PLATFORM_CONFIGS: Record<Platform, PlatformViteConfig>;
@@ -0,0 +1,257 @@
1
+ /**
2
+ * 平台配置映射
3
+ */
4
+ export const PLATFORM_CONFIGS = {
5
+ facebook: {
6
+ sizeLimit: 5 * 1024 * 1024, // 5MB(HTML/ZIP 总包体)
7
+ outputFormat: 'html',
8
+ minifyCSS: true,
9
+ minifyJS: true,
10
+ compressImages: true,
11
+ compressModels: true,
12
+ injectScripts: ['fbPlayableAd'],
13
+ outputFileName: 'index.html',
14
+ includeSourcemap: false,
15
+ imageQuality: {
16
+ jpg: 75,
17
+ png: [0.7, 0.8],
18
+ webp: 75,
19
+ },
20
+ modelCompression: {
21
+ method: 'draco',
22
+ quality: 0.8,
23
+ },
24
+ playable: {
25
+ patchXhrOut: true,
26
+ inlineGameScripts: true,
27
+ compressEngine: true,
28
+ configJsonInline: true,
29
+ },
30
+ },
31
+ snapchat: {
32
+ sizeLimit: 5 * 1024 * 1024, // 5MB
33
+ outputFormat: 'zip',
34
+ minifyCSS: true,
35
+ minifyJS: true,
36
+ compressImages: true,
37
+ compressModels: true,
38
+ injectScripts: ['mraid', 'snapchatCta'],
39
+ outputFileName: 'ad.html',
40
+ includeSourcemap: false,
41
+ imageQuality: {
42
+ jpg: 80,
43
+ png: [0.75, 0.85],
44
+ webp: 80,
45
+ },
46
+ modelCompression: {
47
+ method: 'draco',
48
+ quality: 0.85,
49
+ },
50
+ playable: {
51
+ inlineGameScripts: true,
52
+ externFiles: true,
53
+ mraidSupport: true,
54
+ snapchatCta: true,
55
+ },
56
+ },
57
+ ironsource: {
58
+ sizeLimit: 5 * 1024 * 1024, // 5MB(修正)
59
+ outputFormat: 'html',
60
+ minifyCSS: true,
61
+ minifyJS: true,
62
+ compressImages: true,
63
+ compressModels: true,
64
+ injectScripts: ['mraid', 'dapi'],
65
+ outputFileName: 'index.html',
66
+ includeSourcemap: false,
67
+ imageQuality: {
68
+ jpg: 75,
69
+ png: [0.7, 0.8],
70
+ webp: 75,
71
+ },
72
+ modelCompression: {
73
+ method: 'draco',
74
+ quality: 0.8,
75
+ },
76
+ playable: {
77
+ patchXhrOut: true,
78
+ inlineGameScripts: true,
79
+ configJsonInline: true,
80
+ mraidSupport: true,
81
+ },
82
+ },
83
+ applovin: {
84
+ sizeLimit: 5 * 1024 * 1024, // 5MB(修正)
85
+ outputFormat: 'html',
86
+ minifyCSS: true,
87
+ minifyJS: true,
88
+ compressImages: true,
89
+ compressModels: true,
90
+ injectScripts: ['mraid'],
91
+ outputFileName: 'index.html',
92
+ includeSourcemap: false,
93
+ imageQuality: {
94
+ jpg: 75,
95
+ png: [0.7, 0.8],
96
+ webp: 75,
97
+ },
98
+ modelCompression: {
99
+ method: 'draco',
100
+ quality: 0.8,
101
+ },
102
+ playable: {
103
+ patchXhrOut: true,
104
+ inlineGameScripts: true,
105
+ configJsonInline: true,
106
+ mraidSupport: true,
107
+ },
108
+ },
109
+ google: {
110
+ sizeLimit: 5 * 1024 * 1024, // 5MB(修正)
111
+ outputFormat: 'zip', // ZIP格式(修正)
112
+ minifyCSS: true,
113
+ minifyJS: true,
114
+ compressImages: true,
115
+ compressModels: true,
116
+ injectScripts: ['exitApi'],
117
+ outputFileName: 'index.html',
118
+ includeSourcemap: false,
119
+ imageQuality: {
120
+ jpg: 75,
121
+ png: [0.7, 0.8],
122
+ webp: 75,
123
+ },
124
+ modelCompression: {
125
+ method: 'draco',
126
+ quality: 0.8,
127
+ },
128
+ playable: {
129
+ inlineGameScripts: true,
130
+ externFiles: true,
131
+ },
132
+ },
133
+ tiktok: {
134
+ sizeLimit: 5 * 1024 * 1024, // 5MB
135
+ outputFormat: 'zip',
136
+ minifyCSS: true,
137
+ minifyJS: true,
138
+ compressImages: true,
139
+ compressModels: true,
140
+ injectScripts: ['pangleSdk'],
141
+ outputFileName: 'index.html',
142
+ includeSourcemap: false,
143
+ imageQuality: {
144
+ jpg: 80,
145
+ png: [0.75, 0.85],
146
+ webp: 80,
147
+ },
148
+ modelCompression: {
149
+ method: 'draco',
150
+ quality: 0.85,
151
+ },
152
+ playable: {
153
+ inlineGameScripts: true,
154
+ externFiles: true,
155
+ },
156
+ },
157
+ unity: {
158
+ sizeLimit: 5 * 1024 * 1024, // 5MB
159
+ outputFormat: 'html',
160
+ minifyCSS: true,
161
+ minifyJS: true,
162
+ compressImages: true,
163
+ compressModels: true,
164
+ injectScripts: ['mraid3'],
165
+ outputFileName: 'index.html',
166
+ includeSourcemap: false,
167
+ imageQuality: {
168
+ jpg: 75,
169
+ png: [0.7, 0.8],
170
+ webp: 75,
171
+ },
172
+ modelCompression: {
173
+ method: 'draco',
174
+ quality: 0.8,
175
+ },
176
+ playable: {
177
+ patchXhrOut: true,
178
+ inlineGameScripts: true,
179
+ configJsonInline: true,
180
+ mraidSupport: true,
181
+ },
182
+ },
183
+ liftoff: {
184
+ sizeLimit: 5 * 1024 * 1024, // 5MB
185
+ outputFormat: 'zip',
186
+ minifyCSS: true,
187
+ minifyJS: true,
188
+ compressImages: true,
189
+ compressModels: true,
190
+ injectScripts: ['mraid'],
191
+ outputFileName: 'index.html',
192
+ includeSourcemap: false,
193
+ imageQuality: {
194
+ jpg: 75,
195
+ png: [0.7, 0.8],
196
+ webp: 75,
197
+ },
198
+ modelCompression: {
199
+ method: 'draco',
200
+ quality: 0.8,
201
+ },
202
+ playable: {
203
+ inlineGameScripts: true,
204
+ externFiles: true,
205
+ mraidSupport: true,
206
+ },
207
+ },
208
+ moloco: {
209
+ sizeLimit: 5 * 1024 * 1024, // 5MB
210
+ outputFormat: 'html',
211
+ minifyCSS: true,
212
+ minifyJS: true,
213
+ compressImages: true,
214
+ compressModels: true,
215
+ injectScripts: ['fbPlayableAd'],
216
+ outputFileName: 'index.html',
217
+ includeSourcemap: false,
218
+ imageQuality: {
219
+ jpg: 75,
220
+ png: [0.7, 0.8],
221
+ webp: 75,
222
+ },
223
+ modelCompression: {
224
+ method: 'draco',
225
+ quality: 0.8,
226
+ },
227
+ playable: {
228
+ patchXhrOut: true,
229
+ inlineGameScripts: true,
230
+ configJsonInline: true,
231
+ },
232
+ },
233
+ bigo: {
234
+ sizeLimit: 5 * 1024 * 1024, // 5MB
235
+ outputFormat: 'zip',
236
+ minifyCSS: true,
237
+ minifyJS: true,
238
+ compressImages: true,
239
+ compressModels: true,
240
+ injectScripts: ['bigoSdk'],
241
+ outputFileName: 'index.html',
242
+ includeSourcemap: false,
243
+ imageQuality: {
244
+ jpg: 80,
245
+ png: [0.75, 0.85],
246
+ webp: 80,
247
+ },
248
+ modelCompression: {
249
+ method: 'draco',
250
+ quality: 0.85,
251
+ },
252
+ playable: {
253
+ inlineGameScripts: true,
254
+ externFiles: true,
255
+ },
256
+ },
257
+ };
@@ -0,0 +1,11 @@
1
+ import type { Plugin } from 'vite';
2
+ export interface ModelCompressionPluginOptions {
3
+ quality?: number;
4
+ method?: 'draco' | 'meshopt';
5
+ enabled?: boolean;
6
+ }
7
+ /**
8
+ * 3D 模型压缩 Vite 插件
9
+ * 使用 gltf-transform 压缩 GLB/GLTF 模型
10
+ */
11
+ export declare function viteModelCompressionPlugin(options?: ModelCompressionPluginOptions): Plugin;
@@ -0,0 +1,63 @@
1
+ import { NodeIO } from '@gltf-transform/core';
2
+ import { ALL_EXTENSIONS } from '@gltf-transform/extensions';
3
+ import { draco, textureCompress } from '@gltf-transform/functions';
4
+ import draco3d from 'draco3dgltf';
5
+ /**
6
+ * 3D 模型压缩 Vite 插件
7
+ * 使用 gltf-transform 压缩 GLB/GLTF 模型
8
+ */
9
+ export function viteModelCompressionPlugin(options = {}) {
10
+ const { quality = 0.8, method = 'draco', enabled = true } = options;
11
+ if (!enabled) {
12
+ return {
13
+ name: 'vite-plugin-model-compression',
14
+ // 返回空插件
15
+ };
16
+ }
17
+ return {
18
+ name: 'vite-plugin-model-compression',
19
+ async transform(code, id) {
20
+ // 只处理 GLB/GLTF 文件
21
+ if (!id.match(/\.(glb|gltf)$/i)) {
22
+ return null;
23
+ }
24
+ try {
25
+ // 初始化 gltf-transform
26
+ const io = new NodeIO()
27
+ .registerExtensions(ALL_EXTENSIONS)
28
+ .registerDependencies({
29
+ 'draco3d.decoder': await draco3d.createDecoderModule(),
30
+ 'draco3d.encoder': await draco3d.createEncoderModule(),
31
+ });
32
+ // 读取模型
33
+ const document = await io.read(id);
34
+ // 应用压缩
35
+ if (method === 'draco') {
36
+ await document.transform(draco({
37
+ quantizePosition: 14,
38
+ quantizeNormal: 10,
39
+ quantizeTexcoord: 12,
40
+ quantizeColor: 8,
41
+ }));
42
+ }
43
+ // 纹理压缩
44
+ await document.transform(textureCompress({
45
+ targetFormat: 'webp',
46
+ quality: quality,
47
+ }));
48
+ // 转换为 Base64 data URL
49
+ const glb = await io.writeBinary(document);
50
+ const base64 = Buffer.from(glb).toString('base64');
51
+ return {
52
+ code: `export default "data:model/gltf-binary;base64,${base64}"`,
53
+ map: null,
54
+ };
55
+ }
56
+ catch (error) {
57
+ console.warn(`警告: 无法压缩模型文件 ${id}: ${error instanceof Error ? error.message : String(error)}`);
58
+ // 如果压缩失败,返回原始内容
59
+ return null;
60
+ }
61
+ },
62
+ };
63
+ }
@@ -0,0 +1,17 @@
1
+ import type { Plugin } from 'vite';
2
+ import type { PlatformAdapter } from '../platforms/base.js';
3
+ import type { Platform } from '../types.js';
4
+ export interface PlatformPluginOptions {
5
+ platform: Platform;
6
+ adapter: PlatformAdapter;
7
+ outputDir: string;
8
+ baseBuildDir: string;
9
+ outputFormat: 'html' | 'zip';
10
+ externFiles?: import('../types.js').ExternFilesConfig;
11
+ mraidSupport?: boolean;
12
+ }
13
+ /**
14
+ * 平台 Vite 插件
15
+ * 注入平台特定代码和处理输出格式
16
+ */
17
+ export declare function vitePlatformPlugin(options: PlatformPluginOptions): Plugin;