@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.
- package/README.md +96 -0
- package/dist/base-builder.d.ts +66 -0
- package/dist/base-builder.js +415 -0
- package/dist/converter.d.ts +35 -0
- package/dist/converter.js +148 -0
- package/dist/generators/config-generator.d.ts +7 -0
- package/dist/generators/config-generator.js +122 -0
- package/dist/generators/settings-generator.d.ts +14 -0
- package/dist/generators/settings-generator.js +100 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +14 -0
- package/dist/loaders/playcanvas-loader.d.ts +10 -0
- package/dist/loaders/playcanvas-loader.js +18 -0
- package/dist/loaders/playcraft-loader.d.ts +10 -0
- package/dist/loaders/playcraft-loader.js +51 -0
- package/dist/platforms/applovin.d.ts +10 -0
- package/dist/platforms/applovin.js +67 -0
- package/dist/platforms/base.d.ts +29 -0
- package/dist/platforms/base.js +11 -0
- package/dist/platforms/bigo.d.ts +15 -0
- package/dist/platforms/bigo.js +77 -0
- package/dist/platforms/facebook.d.ts +9 -0
- package/dist/platforms/facebook.js +37 -0
- package/dist/platforms/google.d.ts +10 -0
- package/dist/platforms/google.js +53 -0
- package/dist/platforms/index.d.ts +14 -0
- package/dist/platforms/index.js +47 -0
- package/dist/platforms/ironsource.d.ts +10 -0
- package/dist/platforms/ironsource.js +71 -0
- package/dist/platforms/liftoff.d.ts +10 -0
- package/dist/platforms/liftoff.js +56 -0
- package/dist/platforms/moloco.d.ts +10 -0
- package/dist/platforms/moloco.js +53 -0
- package/dist/platforms/snapchat.d.ts +10 -0
- package/dist/platforms/snapchat.js +59 -0
- package/dist/platforms/tiktok.d.ts +15 -0
- package/dist/platforms/tiktok.js +65 -0
- package/dist/platforms/unity.d.ts +10 -0
- package/dist/platforms/unity.js +69 -0
- package/dist/playable-builder.d.ts +97 -0
- package/dist/playable-builder.js +590 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.js +1 -0
- package/dist/vite/config-builder.d.ts +15 -0
- package/dist/vite/config-builder.js +212 -0
- package/dist/vite/platform-configs.d.ts +38 -0
- package/dist/vite/platform-configs.js +257 -0
- package/dist/vite/plugin-model-compression.d.ts +11 -0
- package/dist/vite/plugin-model-compression.js +63 -0
- package/dist/vite/plugin-platform.d.ts +17 -0
- package/dist/vite/plugin-platform.js +241 -0
- package/dist/vite/plugin-playcanvas.d.ts +18 -0
- package/dist/vite/plugin-playcanvas.js +711 -0
- package/dist/vite/plugin-source-builder.d.ts +15 -0
- package/dist/vite/plugin-source-builder.js +344 -0
- package/dist/vite-builder.d.ts +51 -0
- package/dist/vite-builder.js +122 -0
- package/package.json +51 -0
- package/templates/__loading__.js +100 -0
- package/templates/__modules__.js +47 -0
- package/templates/__settings__.template.js +20 -0
- package/templates/__start__.js +332 -0
- package/templates/index.html +18 -0
- package/templates/logo.png +0 -0
- package/templates/manifest.json +1 -0
- package/templates/patches/cannon.min.js +28 -0
- package/templates/patches/lz4.js +10 -0
- package/templates/patches/one-page-http-get.js +20 -0
- package/templates/patches/one-page-inline-game-scripts.js +20 -0
- package/templates/patches/one-page-mraid-resize-canvas.js +46 -0
- package/templates/patches/p2.min.js +27 -0
- package/templates/patches/playcraft-no-xhr.js +52 -0
- package/templates/playcanvas-stable.min.js +16363 -0
- package/templates/styles.css +43 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import archiver from 'archiver';
|
|
6
|
+
import { createWriteStream } from 'fs';
|
|
7
|
+
import { PLATFORM_CONFIGS } from './platform-configs.js';
|
|
8
|
+
/**
|
|
9
|
+
* 平台 Vite 插件
|
|
10
|
+
* 注入平台特定代码和处理输出格式
|
|
11
|
+
*/
|
|
12
|
+
export function vitePlatformPlugin(options) {
|
|
13
|
+
return {
|
|
14
|
+
name: 'vite-plugin-platform',
|
|
15
|
+
enforce: 'post',
|
|
16
|
+
transformIndexHtml: {
|
|
17
|
+
order: 'post',
|
|
18
|
+
async handler(html) {
|
|
19
|
+
// 使用平台适配器修改 HTML
|
|
20
|
+
let output = options.adapter.modifyHTML(html, []);
|
|
21
|
+
if (options.mraidSupport) {
|
|
22
|
+
output = await injectMraidResizePatch(output);
|
|
23
|
+
}
|
|
24
|
+
return output;
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
async closeBundle() {
|
|
28
|
+
// 如果需要 ZIP 格式,在构建完成后打包
|
|
29
|
+
const platformConfig = PLATFORM_CONFIGS[options.platform];
|
|
30
|
+
// 仅多文件输出需要复制基础资源
|
|
31
|
+
if (options.outputFormat === 'zip') {
|
|
32
|
+
await copyBaseBuildAssets(options.baseBuildDir, options.outputDir);
|
|
33
|
+
if (options.mraidSupport) {
|
|
34
|
+
await applyMraidSupport(options.outputDir);
|
|
35
|
+
}
|
|
36
|
+
if (options.externFiles?.enabled) {
|
|
37
|
+
await applyExternFiles(options.outputDir, options.externFiles);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (options.outputFormat === 'html') {
|
|
41
|
+
await keepSingleHtml(options.outputDir, platformConfig.outputFileName);
|
|
42
|
+
}
|
|
43
|
+
if (platformConfig.outputFormat === 'zip') {
|
|
44
|
+
// 使用 Promise 处理异步操作
|
|
45
|
+
createZipOutput(options.outputDir, platformConfig.outputFileName).catch((err) => {
|
|
46
|
+
console.error('创建 ZIP 文件失败:', err);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 创建 ZIP 输出
|
|
54
|
+
*/
|
|
55
|
+
function createZipOutput(outDir, htmlFileName) {
|
|
56
|
+
return new Promise(async (resolve, reject) => {
|
|
57
|
+
const zipPath = path.join(outDir, 'playable.zip');
|
|
58
|
+
const output = createWriteStream(zipPath);
|
|
59
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
60
|
+
output.on('close', () => {
|
|
61
|
+
console.log(`✅ ZIP 文件已创建: ${zipPath} (${archive.pointer()} bytes)`);
|
|
62
|
+
resolve();
|
|
63
|
+
});
|
|
64
|
+
archive.on('error', (err) => {
|
|
65
|
+
reject(err);
|
|
66
|
+
});
|
|
67
|
+
archive.pipe(output);
|
|
68
|
+
// 添加输出目录全部内容(排除 zip 本身)
|
|
69
|
+
archive.glob('**/*', {
|
|
70
|
+
cwd: outDir,
|
|
71
|
+
ignore: ['playable.zip'],
|
|
72
|
+
});
|
|
73
|
+
archive.finalize();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async function keepSingleHtml(outDir, htmlFileName) {
|
|
77
|
+
let entries = [];
|
|
78
|
+
try {
|
|
79
|
+
entries = await fs.readdir(outDir, { withFileTypes: true });
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
await Promise.all(entries.map(async (entry) => {
|
|
85
|
+
if (entry.name === htmlFileName) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const targetPath = path.join(outDir, entry.name);
|
|
89
|
+
await fs.rm(targetPath, { recursive: true, force: true });
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
async function applyExternFiles(outDir, externFiles) {
|
|
93
|
+
const folderName = externFiles.folderName?.trim() || `playcraft-assets-${crypto.randomUUID()}`;
|
|
94
|
+
const externalPrefix = externFiles.externalUrlPrefix?.trim().replace(/\/+$/g, '');
|
|
95
|
+
const assetPrefix = externalPrefix ? `${externalPrefix}/${folderName}/` : `${folderName}/`;
|
|
96
|
+
const folderPath = path.join(outDir, folderName);
|
|
97
|
+
await fs.mkdir(folderPath, { recursive: true });
|
|
98
|
+
const entries = await fs.readdir(outDir, { withFileTypes: true });
|
|
99
|
+
for (const entry of entries) {
|
|
100
|
+
if (entry.name === 'index.html' || entry.name === folderName) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const srcPath = path.join(outDir, entry.name);
|
|
104
|
+
const destPath = path.join(folderPath, entry.name);
|
|
105
|
+
await fs.rename(srcPath, destPath);
|
|
106
|
+
}
|
|
107
|
+
await rewriteIndexHtml(outDir, assetPrefix);
|
|
108
|
+
await rewriteSettings(folderPath, assetPrefix);
|
|
109
|
+
await rewriteConfig(folderPath, assetPrefix);
|
|
110
|
+
}
|
|
111
|
+
async function rewriteIndexHtml(outDir, assetPrefix) {
|
|
112
|
+
const indexPath = path.join(outDir, 'index.html');
|
|
113
|
+
let html = await fs.readFile(indexPath, 'utf-8');
|
|
114
|
+
html = html.replace(/(<script[^>]*src=["'])([^"']+)(["'][^>]*><\/script>)/gi, (match, start, src, end) => {
|
|
115
|
+
if (src.startsWith('http') || src.startsWith('data:')) {
|
|
116
|
+
return match;
|
|
117
|
+
}
|
|
118
|
+
return `${start}${assetPrefix}${src}${end}`;
|
|
119
|
+
});
|
|
120
|
+
html = html.replace(/(<link[^>]*href=["'])([^"']+)(["'][^>]*>)/gi, (match, start, href, end) => {
|
|
121
|
+
if (href.startsWith('http') || href.startsWith('data:')) {
|
|
122
|
+
return match;
|
|
123
|
+
}
|
|
124
|
+
return `${start}${assetPrefix}${href}${end}`;
|
|
125
|
+
});
|
|
126
|
+
await fs.writeFile(indexPath, html);
|
|
127
|
+
}
|
|
128
|
+
async function rewriteSettings(folderPath, assetPrefix) {
|
|
129
|
+
const settingsPath = path.join(folderPath, '__settings__.js');
|
|
130
|
+
try {
|
|
131
|
+
let code = await fs.readFile(settingsPath, 'utf-8');
|
|
132
|
+
code = code.replace(/window\.ASSET_PREFIX\s*=\s*"[^"]*";/, `window.ASSET_PREFIX = "${assetPrefix}";`);
|
|
133
|
+
code = code.replace(/window\.SCRIPT_PREFIX\s*=\s*"[^"]*";/, `window.SCRIPT_PREFIX = "${assetPrefix}";`);
|
|
134
|
+
code = code.replace(/window\.CONFIG_FILENAME\s*=\s*"([^"]+)";/, (match, value) => {
|
|
135
|
+
if (value.startsWith('data:') || value.startsWith('http')) {
|
|
136
|
+
return match;
|
|
137
|
+
}
|
|
138
|
+
return `window.CONFIG_FILENAME = "${assetPrefix}${value}";`;
|
|
139
|
+
});
|
|
140
|
+
code = code.replace(/window\.SCENE_PATH\s*=\s*"([^"]*)";/, (match, value) => {
|
|
141
|
+
if (!value || value.startsWith('data:') || value.startsWith('http')) {
|
|
142
|
+
return match;
|
|
143
|
+
}
|
|
144
|
+
return `window.SCENE_PATH = "${assetPrefix}${value}";`;
|
|
145
|
+
});
|
|
146
|
+
await fs.writeFile(settingsPath, code);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
// 忽略缺失
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function rewriteConfig(folderPath, assetPrefix) {
|
|
153
|
+
const configPath = path.join(folderPath, 'config.json');
|
|
154
|
+
try {
|
|
155
|
+
const raw = await fs.readFile(configPath, 'utf-8');
|
|
156
|
+
const json = JSON.parse(raw);
|
|
157
|
+
if (Array.isArray(json.scenes)) {
|
|
158
|
+
json.scenes = json.scenes.map((scene) => {
|
|
159
|
+
if (!scene?.url || scene.url.startsWith('http') || scene.url.startsWith('data:')) {
|
|
160
|
+
return scene;
|
|
161
|
+
}
|
|
162
|
+
return { ...scene, url: `${assetPrefix}${scene.url}` };
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
await fs.writeFile(configPath, JSON.stringify(json));
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
// 忽略缺失
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function applyMraidSupport(outDir) {
|
|
172
|
+
await patchConfigFillMode(outDir);
|
|
173
|
+
await patchStylesForMraid(outDir);
|
|
174
|
+
}
|
|
175
|
+
async function patchConfigFillMode(outDir) {
|
|
176
|
+
const configPath = path.join(outDir, 'config.json');
|
|
177
|
+
try {
|
|
178
|
+
const raw = await fs.readFile(configPath, 'utf-8');
|
|
179
|
+
const json = JSON.parse(raw);
|
|
180
|
+
json.application_properties = {
|
|
181
|
+
...(json.application_properties || {}),
|
|
182
|
+
fillMode: 'NONE',
|
|
183
|
+
};
|
|
184
|
+
await fs.writeFile(configPath, JSON.stringify(json));
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
// 忽略缺失
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function patchStylesForMraid(outDir) {
|
|
191
|
+
const cssPath = path.join(outDir, 'styles.css');
|
|
192
|
+
try {
|
|
193
|
+
let css = await fs.readFile(cssPath, 'utf-8');
|
|
194
|
+
if (!css.includes('fill-mode-NONE')) {
|
|
195
|
+
css += '\n#application-canvas.fill-mode-NONE { margin: 0; width: 100%; height: 100%; }\n';
|
|
196
|
+
await fs.writeFile(cssPath, css);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
// 忽略缺失
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function injectMraidResizePatch(html) {
|
|
204
|
+
const patchCode = await readPatchFile('one-page-mraid-resize-canvas.js');
|
|
205
|
+
return html.replace('</head>', `<script>${patchCode}</script>\n</head>`);
|
|
206
|
+
}
|
|
207
|
+
async function readPatchFile(name) {
|
|
208
|
+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
209
|
+
const patchPath = path.resolve(currentDir, '../../templates/patches', name);
|
|
210
|
+
return await fs.readFile(patchPath, 'utf-8');
|
|
211
|
+
}
|
|
212
|
+
async function copyBaseBuildAssets(baseBuildDir, outDir) {
|
|
213
|
+
const entries = await fs.readdir(baseBuildDir, { withFileTypes: true });
|
|
214
|
+
for (const entry of entries) {
|
|
215
|
+
if (entry.name === 'index.html') {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const srcPath = path.join(baseBuildDir, entry.name);
|
|
219
|
+
const destPath = path.join(outDir, entry.name);
|
|
220
|
+
if (entry.isDirectory()) {
|
|
221
|
+
await copyDirectory(srcPath, destPath);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
await fs.copyFile(srcPath, destPath);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function copyDirectory(src, dest) {
|
|
229
|
+
await fs.mkdir(dest, { recursive: true });
|
|
230
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
231
|
+
for (const entry of entries) {
|
|
232
|
+
const srcPath = path.join(src, entry.name);
|
|
233
|
+
const destPath = path.join(dest, entry.name);
|
|
234
|
+
if (entry.isDirectory()) {
|
|
235
|
+
await copyDirectory(srcPath, destPath);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
await fs.copyFile(srcPath, destPath);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
export interface PlayCanvasPluginOptions {
|
|
3
|
+
baseBuildDir: string;
|
|
4
|
+
inlineScripts: boolean;
|
|
5
|
+
convertDataUrls: boolean;
|
|
6
|
+
outputFormat: 'html' | 'zip';
|
|
7
|
+
patchXhrOut: boolean;
|
|
8
|
+
inlineGameScripts: boolean;
|
|
9
|
+
compressEngine: boolean;
|
|
10
|
+
configJsonInline: boolean;
|
|
11
|
+
mraidSupport: boolean;
|
|
12
|
+
ammoReplacement?: 'p2' | 'cannon';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* PlayCanvas Vite 插件
|
|
16
|
+
* 处理 PlayCanvas 特定的资源转换和内联
|
|
17
|
+
*/
|
|
18
|
+
export declare function vitePlayCanvasPlugin(options: PlayCanvasPluginOptions): Plugin;
|