@playcraft/build 0.0.44 → 0.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-builder.d.ts +2 -0
- package/dist/base-builder.js +44 -0
- package/dist/engines/playable-scripts-adapter.d.ts +6 -1
- package/dist/engines/playable-scripts-adapter.js +92 -14
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/platforms/bigo.js +3 -5
- package/dist/platforms/inmobi.js +3 -5
- package/dist/platforms/liftoff.js +3 -5
- package/dist/platforms/mintegral.js +4 -6
- package/dist/platforms/snapchat.js +1 -1
- package/dist/platforms/tiktok.js +3 -5
- package/dist/types.d.ts +13 -0
- package/dist/validation/html-validator.d.ts +2 -0
- package/dist/validation/html-validator.js +140 -0
- package/dist/validation/index.d.ts +8 -0
- package/dist/validation/index.js +8 -0
- package/dist/validation/package-validator.d.ts +2 -0
- package/dist/validation/package-validator.js +198 -0
- package/dist/validation/profiles.d.ts +5 -0
- package/dist/validation/profiles.js +222 -0
- package/dist/validation/reporter.d.ts +4 -0
- package/dist/validation/reporter.js +30 -0
- package/dist/validation/source-scanner.d.ts +4 -0
- package/dist/validation/source-scanner.js +271 -0
- package/dist/validation/types.d.ts +112 -0
- package/dist/validation/validation-runner.d.ts +5 -0
- package/dist/validation/validation-runner.js +96 -0
- package/dist/validation/zip-reader.d.ts +17 -0
- package/dist/validation/zip-reader.js +84 -0
- package/dist/vite/platform-configs.js +7 -7
- package/dist/vite-builder.d.ts +1 -0
- package/dist/vite-builder.js +43 -0
- package/package.json +2 -2
- package/dist/analyzers/__tests__/optimization-analyzer.test.js +0 -169
- package/dist/templates/__loading__.js +0 -100
- package/dist/templates/__modules__.js +0 -47
- package/dist/templates/__settings__.template.js +0 -20
- package/dist/templates/__start__.js +0 -332
- package/dist/templates/index.html +0 -18
- package/dist/templates/logo.png +0 -0
- package/dist/templates/manifest.json +0 -1
- package/dist/templates/patches/cannon.min.js +0 -28
- package/dist/templates/patches/lz4.js +0 -10
- package/dist/templates/patches/one-page-http-get.js +0 -20
- package/dist/templates/patches/one-page-inline-game-scripts.js +0 -52
- package/dist/templates/patches/one-page-mraid-resize-canvas.js +0 -46
- package/dist/templates/patches/p2.min.js +0 -27
- package/dist/templates/patches/playcraft-no-xhr.js +0 -76
- package/dist/templates/playcanvas-stable.min.js +0 -16363
- package/dist/templates/styles.css +0 -43
- /package/dist/{analyzers/__tests__/optimization-analyzer.test.d.ts → validation/types.js} +0 -0
package/dist/vite-builder.js
CHANGED
|
@@ -4,6 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import { ViteConfigBuilder } from './vite/config-builder.js';
|
|
5
5
|
import { GenericViteConfigBuilder } from './vite/config-builder-generic.js';
|
|
6
6
|
import { PLATFORM_CONFIGS } from './vite/platform-configs.js';
|
|
7
|
+
import { formatValidationSummary, hasBlockingValidationErrors, runPlayableValidation, saveValidationReport, } from './validation/index.js';
|
|
7
8
|
/**
|
|
8
9
|
* Vite 构建器 - 使用 Vite 构建 Playable Ads
|
|
9
10
|
*
|
|
@@ -90,6 +91,7 @@ export class ViteBuilder {
|
|
|
90
91
|
// 6. 验证输出大小
|
|
91
92
|
const outputPath = this.getOutputPath();
|
|
92
93
|
await this.validateSize(outputPath);
|
|
94
|
+
await this.runComplianceValidation(outputPath, metadata);
|
|
93
95
|
// 7. 生成报告
|
|
94
96
|
this.generateReport(outputPath);
|
|
95
97
|
return outputPath;
|
|
@@ -350,6 +352,47 @@ export class ViteBuilder {
|
|
|
350
352
|
/**
|
|
351
353
|
* 格式化字节数
|
|
352
354
|
*/
|
|
355
|
+
async runComplianceValidation(outputPath, metadata) {
|
|
356
|
+
const validationOptions = this.options.validation;
|
|
357
|
+
if (validationOptions?.enabled === false) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (this.engine !== 'phaser') {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const isZip = outputPath.toLowerCase().endsWith('.zip');
|
|
364
|
+
const stages = validationOptions?.stages
|
|
365
|
+
?? (isZip ? ['delivery-package'] : ['html-artifact']);
|
|
366
|
+
const report = await runPlayableValidation({
|
|
367
|
+
projectRoot: this.baseBuildDir,
|
|
368
|
+
channel: this.options.platform,
|
|
369
|
+
engine: this.engine,
|
|
370
|
+
buildTool: metadata?.buildTool,
|
|
371
|
+
buildChain: metadata?.buildTool === 'playable-scripts'
|
|
372
|
+
? 'devkit'
|
|
373
|
+
: metadata?.buildTool === 'playcanvas-native'
|
|
374
|
+
? 'playcanvas-native'
|
|
375
|
+
: 'generic',
|
|
376
|
+
htmlPath: isZip ? undefined : outputPath,
|
|
377
|
+
packagePath: isZip ? outputPath : undefined,
|
|
378
|
+
outputDir: this.options.outputDir,
|
|
379
|
+
sourceGlobs: validationOptions?.sourceGlobs,
|
|
380
|
+
metadata: metadata ? { ...metadata } : undefined,
|
|
381
|
+
}, stages);
|
|
382
|
+
const summary = formatValidationSummary(report);
|
|
383
|
+
if (report.summary.status === 'pass') {
|
|
384
|
+
console.log(summary);
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
console.warn(summary);
|
|
388
|
+
}
|
|
389
|
+
if (validationOptions?.reportPath) {
|
|
390
|
+
await saveValidationReport(report, validationOptions.reportPath);
|
|
391
|
+
}
|
|
392
|
+
if (validationOptions?.failOnError && hasBlockingValidationErrors(report)) {
|
|
393
|
+
throw new Error(`Playable validation failed for ${this.options.platform}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
353
396
|
formatBytes(bytes) {
|
|
354
397
|
if (bytes === 0)
|
|
355
398
|
return '0 Bytes';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcraft/build",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.46",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"scripts": {
|
|
33
33
|
"dev": "tsc -w",
|
|
34
34
|
"build": "tsc",
|
|
35
|
-
"release": "node scripts/release.
|
|
35
|
+
"release": "node ../../scripts/release-npm.mjs --packages=build --skip-install --skip-tests --skip-git-push"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@playcraft/ads-tracking": "^1.0.0",
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { OptimizationAnalyzer } from '../optimization-analyzer.js';
|
|
3
|
-
describe('OptimizationAnalyzer', () => {
|
|
4
|
-
it('should detect large uncompressed files', () => {
|
|
5
|
-
const state = {
|
|
6
|
-
version: '1.0.0',
|
|
7
|
-
buildTime: Date.now(),
|
|
8
|
-
assets: {
|
|
9
|
-
'large-file': {
|
|
10
|
-
id: 'large-file',
|
|
11
|
-
originalName: 'large-file.js',
|
|
12
|
-
originalPath: '/path/to/large-file.js',
|
|
13
|
-
originalSize: 200 * 1024, // 200KB
|
|
14
|
-
finalSize: 195 * 1024, // 只压缩了 5KB
|
|
15
|
-
totalCompressionRatio: 0.025, // 2.5% 压缩率
|
|
16
|
-
type: 'script',
|
|
17
|
-
processingHistory: [
|
|
18
|
-
{
|
|
19
|
-
stage: 'base-build',
|
|
20
|
-
name: 'large-file.js',
|
|
21
|
-
size: 195 * 1024,
|
|
22
|
-
optimizations: [],
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
stages: [],
|
|
28
|
-
};
|
|
29
|
-
const analyzer = new OptimizationAnalyzer(state);
|
|
30
|
-
const result = analyzer.analyze();
|
|
31
|
-
expect(result.totalSuggestions).toBeGreaterThan(0);
|
|
32
|
-
expect(result.suggestions.some(s => s.type === 'large-uncompressed')).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
it('should detect PNG images that can be converted to WebP', () => {
|
|
35
|
-
const state = {
|
|
36
|
-
version: '1.0.0',
|
|
37
|
-
buildTime: Date.now(),
|
|
38
|
-
assets: {
|
|
39
|
-
'image': {
|
|
40
|
-
id: 'image',
|
|
41
|
-
originalName: 'image.png',
|
|
42
|
-
originalPath: '/path/to/image.png',
|
|
43
|
-
originalSize: 100 * 1024, // 100KB
|
|
44
|
-
finalSize: 100 * 1024,
|
|
45
|
-
totalCompressionRatio: 0,
|
|
46
|
-
type: 'texture',
|
|
47
|
-
processingHistory: [
|
|
48
|
-
{
|
|
49
|
-
stage: 'base-build',
|
|
50
|
-
name: 'image.png',
|
|
51
|
-
size: 100 * 1024,
|
|
52
|
-
optimizations: [],
|
|
53
|
-
},
|
|
54
|
-
],
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
stages: [],
|
|
58
|
-
};
|
|
59
|
-
const analyzer = new OptimizationAnalyzer(state);
|
|
60
|
-
const result = analyzer.analyze();
|
|
61
|
-
expect(result.suggestions.some(s => s.type === 'format-conversion')).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
it('should detect missing minification', () => {
|
|
64
|
-
const state = {
|
|
65
|
-
version: '1.0.0',
|
|
66
|
-
buildTime: Date.now(),
|
|
67
|
-
assets: {
|
|
68
|
-
'script': {
|
|
69
|
-
id: 'script',
|
|
70
|
-
originalName: 'script.js',
|
|
71
|
-
originalPath: '/path/to/script.js',
|
|
72
|
-
originalSize: 50 * 1024, // 50KB
|
|
73
|
-
finalSize: 50 * 1024,
|
|
74
|
-
totalCompressionRatio: 0,
|
|
75
|
-
type: 'script',
|
|
76
|
-
processingHistory: [
|
|
77
|
-
{
|
|
78
|
-
stage: 'base-build',
|
|
79
|
-
name: 'script.js',
|
|
80
|
-
size: 50 * 1024,
|
|
81
|
-
optimizations: [], // 没有 minify
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
stages: [],
|
|
87
|
-
};
|
|
88
|
-
const analyzer = new OptimizationAnalyzer(state);
|
|
89
|
-
const result = analyzer.analyze();
|
|
90
|
-
expect(result.suggestions.some(s => s.type === 'enable-minify')).toBe(true);
|
|
91
|
-
});
|
|
92
|
-
it('should group suggestions by severity', () => {
|
|
93
|
-
const state = {
|
|
94
|
-
version: '1.0.0',
|
|
95
|
-
buildTime: Date.now(),
|
|
96
|
-
assets: {
|
|
97
|
-
'large-uncompressed': {
|
|
98
|
-
id: 'large-uncompressed',
|
|
99
|
-
originalName: 'large.js',
|
|
100
|
-
originalPath: '/path/to/large.js',
|
|
101
|
-
originalSize: 200 * 1024,
|
|
102
|
-
finalSize: 195 * 1024,
|
|
103
|
-
totalCompressionRatio: 0.025,
|
|
104
|
-
type: 'script',
|
|
105
|
-
processingHistory: [
|
|
106
|
-
{
|
|
107
|
-
stage: 'base-build',
|
|
108
|
-
name: 'large.js',
|
|
109
|
-
size: 195 * 1024,
|
|
110
|
-
optimizations: [],
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
},
|
|
114
|
-
'png-image': {
|
|
115
|
-
id: 'png-image',
|
|
116
|
-
originalName: 'image.png',
|
|
117
|
-
originalPath: '/path/to/image.png',
|
|
118
|
-
originalSize: 100 * 1024,
|
|
119
|
-
finalSize: 100 * 1024,
|
|
120
|
-
totalCompressionRatio: 0,
|
|
121
|
-
type: 'texture',
|
|
122
|
-
processingHistory: [
|
|
123
|
-
{
|
|
124
|
-
stage: 'base-build',
|
|
125
|
-
name: 'image.png',
|
|
126
|
-
size: 100 * 1024,
|
|
127
|
-
optimizations: [],
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
stages: [],
|
|
133
|
-
};
|
|
134
|
-
const analyzer = new OptimizationAnalyzer(state);
|
|
135
|
-
const result = analyzer.analyze();
|
|
136
|
-
expect(result.bySeverity.high.length).toBeGreaterThan(0);
|
|
137
|
-
expect(result.bySeverity.medium.length).toBeGreaterThan(0);
|
|
138
|
-
});
|
|
139
|
-
it('should calculate total estimated savings', () => {
|
|
140
|
-
const state = {
|
|
141
|
-
version: '1.0.0',
|
|
142
|
-
buildTime: Date.now(),
|
|
143
|
-
assets: {
|
|
144
|
-
'large-file': {
|
|
145
|
-
id: 'large-file',
|
|
146
|
-
originalName: 'large-file.js',
|
|
147
|
-
originalPath: '/path/to/large-file.js',
|
|
148
|
-
originalSize: 200 * 1024,
|
|
149
|
-
finalSize: 195 * 1024,
|
|
150
|
-
totalCompressionRatio: 0.025,
|
|
151
|
-
type: 'script',
|
|
152
|
-
processingHistory: [
|
|
153
|
-
{
|
|
154
|
-
stage: 'base-build',
|
|
155
|
-
name: 'large-file.js',
|
|
156
|
-
size: 195 * 1024,
|
|
157
|
-
optimizations: [],
|
|
158
|
-
},
|
|
159
|
-
],
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
stages: [],
|
|
163
|
-
};
|
|
164
|
-
const analyzer = new OptimizationAnalyzer(state);
|
|
165
|
-
const result = analyzer.analyze();
|
|
166
|
-
expect(result.totalEstimatedSavings).toBeGreaterThan(0);
|
|
167
|
-
expect(result.estimatedOptimizationRate).toBeGreaterThan(0);
|
|
168
|
-
});
|
|
169
|
-
});
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
pc.script.createLoadingScreen((app) => {
|
|
2
|
-
const createCss = () => {
|
|
3
|
-
const css = `
|
|
4
|
-
body {
|
|
5
|
-
background-color: #283538;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
#application-splash-wrapper {
|
|
9
|
-
position: absolute;
|
|
10
|
-
top: 0;
|
|
11
|
-
left: 0;
|
|
12
|
-
height: 100%;
|
|
13
|
-
width: 100%;
|
|
14
|
-
background-color: #283538;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
#application-splash {
|
|
18
|
-
position: absolute;
|
|
19
|
-
top: calc(50% - 28px);
|
|
20
|
-
width: 264px;
|
|
21
|
-
left: calc(50% - 132px);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
#application-splash img {
|
|
25
|
-
width: 100%;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
#progress-bar-container {
|
|
29
|
-
margin: 20px auto 0 auto;
|
|
30
|
-
height: 2px;
|
|
31
|
-
width: 100%;
|
|
32
|
-
background-color: #1d292c;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
#progress-bar {
|
|
36
|
-
width: 0%;
|
|
37
|
-
height: 100%;
|
|
38
|
-
background-color: #f60;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@media (max-width: 480px) {
|
|
42
|
-
#application-splash {
|
|
43
|
-
width: 170px;
|
|
44
|
-
left: calc(50% - 85px);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
`;
|
|
48
|
-
|
|
49
|
-
const style = document.createElement('style');
|
|
50
|
-
style.textContent = css;
|
|
51
|
-
document.head.appendChild(style);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const showSplash = () => {
|
|
55
|
-
const wrapper = document.createElement('div');
|
|
56
|
-
wrapper.id = 'application-splash-wrapper';
|
|
57
|
-
document.body.appendChild(wrapper);
|
|
58
|
-
|
|
59
|
-
const splash = document.createElement('div');
|
|
60
|
-
splash.id = 'application-splash';
|
|
61
|
-
wrapper.appendChild(splash);
|
|
62
|
-
splash.style.display = 'none';
|
|
63
|
-
|
|
64
|
-
const logo = document.createElement('img');
|
|
65
|
-
logo.src = `${ASSET_PREFIX}logo.png`;
|
|
66
|
-
splash.appendChild(logo);
|
|
67
|
-
logo.onload = () => {
|
|
68
|
-
splash.style.display = 'block';
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const container = document.createElement('div');
|
|
72
|
-
container.id = 'progress-bar-container';
|
|
73
|
-
splash.appendChild(container);
|
|
74
|
-
|
|
75
|
-
const bar = document.createElement('div');
|
|
76
|
-
bar.id = 'progress-bar';
|
|
77
|
-
container.appendChild(bar);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const setProgress = (value) => {
|
|
81
|
-
const bar = document.getElementById('progress-bar');
|
|
82
|
-
if (bar) {
|
|
83
|
-
value = Math.min(1, Math.max(0, value));
|
|
84
|
-
bar.style.width = `${value * 100}%`;
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const hideSplash = () => {
|
|
89
|
-
document.getElementById('application-splash-wrapper').remove();
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
createCss();
|
|
93
|
-
showSplash();
|
|
94
|
-
|
|
95
|
-
app.on('preload:end', () => {
|
|
96
|
-
app.off('preload:progress');
|
|
97
|
-
});
|
|
98
|
-
app.on('preload:progress', setProgress);
|
|
99
|
-
app.on('start', hideSplash);
|
|
100
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
var loadModules = function (modules, urlPrefix, doneCallback) { // eslint-disable-line no-unused-vars
|
|
2
|
-
|
|
3
|
-
if (typeof modules === "undefined" || modules.length === 0) {
|
|
4
|
-
// caller may depend on callback behaviour being async
|
|
5
|
-
setTimeout(doneCallback);
|
|
6
|
-
} else {
|
|
7
|
-
let remaining = modules.length;
|
|
8
|
-
const moduleLoaded = () => {
|
|
9
|
-
if (--remaining === 0) {
|
|
10
|
-
doneCallback();
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
modules.forEach(function (m) {
|
|
15
|
-
pc.WasmModule.setConfig(m.moduleName, {
|
|
16
|
-
glueUrl: urlPrefix + m.glueUrl,
|
|
17
|
-
wasmUrl: urlPrefix + m.wasmUrl,
|
|
18
|
-
fallbackUrl: urlPrefix + m.fallbackUrl
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
if (!m.hasOwnProperty('preload') || m.preload) {
|
|
22
|
-
if (m.moduleName === 'BASIS') {
|
|
23
|
-
// preload basis transcoder
|
|
24
|
-
pc.basisInitialize();
|
|
25
|
-
moduleLoaded();
|
|
26
|
-
} else if (m.moduleName === 'DracoDecoderModule') {
|
|
27
|
-
// preload draco decoder
|
|
28
|
-
if (pc.dracoInitialize) {
|
|
29
|
-
// 1.63 onwards
|
|
30
|
-
pc.dracoInitialize();
|
|
31
|
-
moduleLoaded();
|
|
32
|
-
} else {
|
|
33
|
-
// 1.62 and earlier
|
|
34
|
-
pc.WasmModule.getInstance(m.moduleName, () => { moduleLoaded(); });
|
|
35
|
-
}
|
|
36
|
-
} else {
|
|
37
|
-
// load remaining modules in global scope
|
|
38
|
-
pc.WasmModule.getInstance(m.moduleName, () => { moduleLoaded(); });
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
moduleLoaded();
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
window.loadModules = loadModules;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
window.ASSET_PREFIX = "";
|
|
2
|
-
window.SCRIPT_PREFIX = "";
|
|
3
|
-
window.SCENE_PATH = "{{SCENE_PATH}}";
|
|
4
|
-
window.CONTEXT_OPTIONS = {
|
|
5
|
-
'antialias': {{ANTIALIAS}},
|
|
6
|
-
'alpha': false,
|
|
7
|
-
'preserveDrawingBuffer': {{PRESERVE_DRAWING_BUFFER}},
|
|
8
|
-
'deviceTypes': [`webgl2`, `webgl1`],
|
|
9
|
-
'powerPreference': "{{POWER_PREFERENCE}}"
|
|
10
|
-
};
|
|
11
|
-
window.SCRIPTS = {{SCRIPTS}};
|
|
12
|
-
window.CONFIG_FILENAME = "config.json";
|
|
13
|
-
window.INPUT_SETTINGS = {
|
|
14
|
-
useKeyboard: {{USE_KEYBOARD}},
|
|
15
|
-
useMouse: {{USE_MOUSE}},
|
|
16
|
-
useGamepads: {{USE_GAMEPAD}},
|
|
17
|
-
useTouch: {{USE_TOUCH}}
|
|
18
|
-
};
|
|
19
|
-
pc.script.legacy = {{USE_LEGACY_SCRIPTS}};
|
|
20
|
-
window.PRELOAD_MODULES = {{PRELOAD_MODULES}};
|