@playcraft/devkit 1.0.27 → 1.0.29

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 (55) hide show
  1. package/cli/bin/playable-scripts.js +6 -44
  2. package/cli/commands/build.js +41 -170
  3. package/cli/commands/builds.js +2 -13
  4. package/cli/commands/dev.js +1 -7
  5. package/cli/commands/pack.js +39 -308
  6. package/cli/commands/vite-dev.js +1 -7
  7. package/core/batch-build.js +185 -1121
  8. package/core/constants.js +2 -16
  9. package/core/cos-uploader.js +24 -286
  10. package/core/index.d.ts +12 -5
  11. package/core/index.js +7 -76
  12. package/core/loaders/gltf-loader.js +28 -74
  13. package/core/options.js +15 -310
  14. package/core/plugins/AdikteevInjectorPlugin.js +4 -26
  15. package/core/plugins/BigoAdsInjectorPlugin.js +4 -21
  16. package/core/plugins/DAPIInjectorPlugin.js +2 -25
  17. package/core/plugins/DebuggerInjectionPlugin.js +3 -31
  18. package/core/plugins/ExitAPIInjectorPlugin.js +4 -58
  19. package/core/plugins/FflateCompressionPlugin.js +40 -231
  20. package/core/plugins/FontInjectionWebpackPlugin.js +4 -46
  21. package/core/plugins/LiftoffInjectorPlugin.js +4 -26
  22. package/core/plugins/MRAIDInjectorPlugin.js +2 -24
  23. package/core/plugins/MintegralInjectorPlugin.js +2 -25
  24. package/core/plugins/PangleInjectorPlugin.js +2 -24
  25. package/core/plugins/PlaycraftOrientationInjectorPlugin.js +7 -0
  26. package/core/plugins/SnapchatInjectorPlugin.js +4 -26
  27. package/core/plugins/SocialMediaInjectorPlugin.js +7 -119
  28. package/core/plugins/TikTokInjectorPlugin.js +2 -24
  29. package/core/plugins/UnityInjectorPlugin.js +8 -37
  30. package/core/plugins/ZipPlugin.js +20 -138
  31. package/core/plugins/socialMediaVitePlugin.js +2 -108
  32. package/core/utils/buildDefines.js +3 -29
  33. package/core/utils/buildTemplateString.js +5 -40
  34. package/core/utils/date.js +1 -16
  35. package/core/utils/fontRegistry.js +13 -81
  36. package/core/utils/fontResolver.js +37 -335
  37. package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +7 -42
  38. package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +3 -30
  39. package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +7 -43
  40. package/core/utils/getPlaycraftOrientationSuffix.js +6 -0
  41. package/core/utils/injectSDKPlugins.js +14 -69
  42. package/core/utils/logOptions.js +5 -37
  43. package/core/utils/mergeOptions.js +2 -19
  44. package/core/utils/parseArgvOptions.js +5 -111
  45. package/core/utils/resolveChannelFold2Zip.js +5 -17
  46. package/core/utils/validateThemeData.js +26 -227
  47. package/core/validators/pre-build-checker.js +26 -286
  48. package/core/validators/tracking-validator.js +40 -358
  49. package/core/vite.dev.js +17 -322
  50. package/core/webpack.build.js +65 -489
  51. package/core/webpack.common.js +11 -177
  52. package/core/webpack.dev.js +6 -82
  53. package/defines.d.ts +2 -1
  54. package/package.json +3 -4
  55. package/README.md +0 -262
@@ -1,17 +1,4 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const JSON5 = require('json5');
6
- const Ajv = require('ajv');
7
-
8
- const SCHEMA_BACKUP_SUFFIX = '.backup';
9
- const SCHEMA_BACKUP_COMMENT =
10
- '// [自动生成的备份] 此文件由打包流程创建。\n' +
11
- '// 如果打包中断,可以移除 ".backup" 后缀,\n' +
12
- '// 并用此文件替换原始的 "theme.schema.json5" 来恢复。\n';
13
-
14
- /**
1
+ "use strict";const fs=require("fs");const path=require("path");const JSON5=require("json5");const Ajv=require("ajv");const SCHEMA_BACKUP_SUFFIX=".backup";const SCHEMA_BACKUP_COMMENT="// [\u81EA\u52A8\u751F\u6210\u7684\u5907\u4EFD] \u6B64\u6587\u4EF6\u7531\u6253\u5305\u6D41\u7A0B\u521B\u5EFA\u3002\n"+"// \u5982\u679C\u6253\u5305\u4E2D\u65AD\uFF0C\u53EF\u4EE5\u79FB\u9664 \".backup\" \u540E\u7F00\uFF0C\n"+"// \u5E76\u7528\u6B64\u6587\u4EF6\u66FF\u6362\u539F\u59CB\u7684 \"theme.schema.json5\" \u6765\u6062\u590D\u3002\n";/**
15
2
  * 递归提取 JSON Schema 定义中的 `default` 值。
16
3
  *
17
4
  * - 对于 "object" 类型:递归遍历 `properties` 并构建嵌套对象。
@@ -19,42 +6,15 @@ const SCHEMA_BACKUP_COMMENT =
19
6
  *
20
7
  * @param {object} schema - JSON Schema(或子 schema)对象
21
8
  * @returns {*} 提取的默认值树,如果没有则返回 undefined
22
- */
23
- function extractDefaultsFromSchema(schema) {
24
- if (!schema || typeof schema !== 'object') return undefined;
25
-
26
- if (schema.type === 'object' && schema.properties) {
27
- const result = {};
28
- for (const [key, propSchema] of Object.entries(schema.properties)) {
29
- const val = extractDefaultsFromSchema(propSchema);
30
- if (val !== undefined) {
31
- result[key] = val;
32
- }
33
- }
34
- return Object.keys(result).length > 0 ? result : undefined;
35
- }
36
-
37
- // 基本类型或数组类型,带有默认值
38
- if ('default' in schema) {
39
- return schema.default;
40
- }
41
-
42
- return undefined;
43
- }
44
-
45
- /**
9
+ */function extractDefaultsFromSchema(schema){if(!schema||typeof schema!=="object")return undefined;if(schema.type==="object"&&schema.properties){const result={};for(const[key,propSchema]of Object.entries(schema.properties)){const val=extractDefaultsFromSchema(propSchema);if(val!==undefined){result[key]=val}}return Object.keys(result).length>0?result:undefined}// 基本类型或数组类型,带有默认值
10
+ if("default"in schema){return schema.default}return undefined}/**
46
11
  * Check whether the parsed content of theme.schema.json5 is a real JSON Schema
47
12
  * (has "type" and "properties" fields) or has already been overwritten with
48
13
  * plain merged data (e.g. due to a previous interrupted build).
49
14
  *
50
15
  * @param {object} obj - Parsed content of theme.schema.json5
51
16
  * @returns {boolean} true if it looks like a valid JSON Schema definition
52
- */
53
- function isJsonSchema(obj) {
54
- return obj && typeof obj === 'object' && obj.type === 'object' && typeof obj.properties === 'object';
55
- }
56
-
57
- /**
17
+ */function isJsonSchema(obj){return obj&&typeof obj==="object"&&obj.type==="object"&&typeof obj.properties==="object"}/**
58
18
  * 根据 schema 校验指定主题的主题数据。
59
19
  *
60
20
  * 项目中预期的文件结构:
@@ -75,151 +35,40 @@ function isJsonSchema(obj) {
75
35
  * mergedData – schema 默认值与主题数据合并后的完整数据
76
36
  * isPlainData – 如果 schema 文件已经是纯数据(非 schema 格式)则为 true
77
37
  * errors – 校验通过为 null,否则为错误信息数组
78
- */
79
- function validateThemeData(themeName, projectRoot, themeConfig) {
80
- const sourceDir = (themeConfig && themeConfig.sourceDir) || 'src/theme';
81
- const themeBaseDir = path.join(projectRoot, sourceDir);
82
-
83
- // --- 解析文件路径 ---
84
- const schemaPath = path.join(themeBaseDir, 'theme.schema.json5');
85
- const dataPath = path.join(themeBaseDir, themeName, 'theme.data.json5');
86
-
87
- // --- 读取并解析文件 ---
88
- if (!fs.existsSync(schemaPath)) {
89
- return {
90
- defaultData: null,
91
- mergedData: null,
92
- isPlainData: false,
93
- errors: [`未找到 Schema 文件: ${schemaPath}`],
94
- };
95
- }
96
-
97
- const schemaContent = JSON5.parse(fs.readFileSync(schemaPath, 'utf8'));
98
-
99
- // Safety check: if the file is already plain data (not a JSON Schema),
100
- // it was likely left over from a previous interrupted build.
101
- // Use it directly without validation or overwriting.
102
- if (!isJsonSchema(schemaContent)) {
103
- return {
104
- defaultData: schemaContent,
105
- mergedData: schemaContent,
106
- isPlainData: true,
107
- errors: null,
108
- };
109
- }
110
-
111
- // 从 schema 提取默认值
112
- const defaultData = extractDefaultsFromSchema(schemaContent) || {};
113
-
114
- let themeData = {};
115
- if (fs.existsSync(dataPath)) {
116
- themeData = JSON5.parse(fs.readFileSync(dataPath, 'utf8'));
117
- }
118
-
119
- // 深度合并(主题数据覆盖 schema 默认值),然后校验
120
- const merged = deepMerge(defaultData, themeData);
121
-
122
- // --- 根据 schema 校验合并后的数据 ---
123
- const ajv = new Ajv({ allErrors: true, strict: false });
124
- let validate;
125
- try {
126
- validate = ajv.compile(schemaContent);
127
- } catch (schemaErr) {
128
- // schema 文件被之前的构建中断污染(mergedData 覆盖了 schema)→ 按纯数据恢复
129
- console.warn(`[validateThemeData] schema 编译失败(可能被覆盖),降级为纯数据: ${schemaErr.message}`);
130
- return { defaultData: merged, mergedData: merged, isPlainData: true, errors: null };
131
- }
132
- const valid = validate(merged);
133
-
134
- if (!valid) {
135
- const errors = validate.errors.map(err => {
136
- const field = err.instancePath || '(root)';
137
- return `${field} ${err.message}`;
138
- });
139
- return { defaultData, mergedData: merged, isPlainData: false, errors };
140
- }
141
-
142
- return { defaultData, mergedData: merged, isPlainData: false, errors: null };
143
- }
144
-
145
- /**
38
+ */function validateThemeData(themeName,projectRoot,themeConfig){const sourceDir=themeConfig&&themeConfig.sourceDir||"src/theme";const themeBaseDir=path.join(projectRoot,sourceDir);// --- 解析文件路径 ---
39
+ const schemaPath=path.join(themeBaseDir,"theme.schema.json5");const dataPath=path.join(themeBaseDir,themeName,"theme.data.json5");// --- 读取并解析文件 ---
40
+ if(!fs.existsSync(schemaPath)){return{defaultData:null,mergedData:null,isPlainData:false,errors:[`未找到 Schema 文件: ${schemaPath}`]}}const schemaContent=JSON5.parse(fs.readFileSync(schemaPath,"utf8"));// Safety check: if the file is already plain data (not a JSON Schema),
41
+ // it was likely left over from a previous interrupted build.
42
+ // Use it directly without validation or overwriting.
43
+ if(!isJsonSchema(schemaContent)){return{defaultData:schemaContent,mergedData:schemaContent,isPlainData:true,errors:null}}// 从 schema 提取默认值
44
+ const defaultData=extractDefaultsFromSchema(schemaContent)||{};let themeData={};if(fs.existsSync(dataPath)){themeData=JSON5.parse(fs.readFileSync(dataPath,"utf8"))}// 深度合并(主题数据覆盖 schema 默认值),然后校验
45
+ const merged=deepMerge(defaultData,themeData);// --- 根据 schema 校验合并后的数据 ---
46
+ const ajv=new Ajv({allErrors:true,strict:false});let validate;try{validate=ajv.compile(schemaContent)}catch(schemaErr){// schema 文件被之前的构建中断污染(mergedData 覆盖了 schema)→ 按纯数据恢复
47
+ console.warn(`[validateThemeData] schema 编译失败(可能被覆盖),降级为纯数据: ${schemaErr.message}`);return{defaultData:merged,mergedData:merged,isPlainData:true,errors:null}}const valid=validate(merged);if(!valid){const errors=validate.errors.map(err=>{const field=err.instancePath||"(root)";return`${field} ${err.message}`});return{defaultData,mergedData:merged,isPlainData:false,errors}}return{defaultData,mergedData:merged,isPlainData:false,errors:null}}/**
146
48
  * 深度合并两个普通对象。`override` 中的值优先。
147
49
  * 数组会被整体替换(不会拼接)。
148
50
  *
149
51
  * @param {object} base
150
52
  * @param {object} override
151
53
  * @returns {object}
152
- */
153
- function deepMerge(base, override) {
154
- const result = { ...base };
155
-
156
- for (const key of Object.keys(override)) {
157
- const baseVal = base[key];
158
- const overVal = override[key];
159
-
160
- if (
161
- overVal !== null &&
162
- typeof overVal === 'object' &&
163
- !Array.isArray(overVal) &&
164
- baseVal !== null &&
165
- typeof baseVal === 'object' &&
166
- !Array.isArray(baseVal)
167
- ) {
168
- result[key] = deepMerge(baseVal, overVal);
169
- } else {
170
- result[key] = overVal;
171
- }
172
- }
173
-
174
- return result;
175
- }
176
-
177
- /**
54
+ */function deepMerge(base,override){const result={...base};for(const key of Object.keys(override)){const baseVal=base[key];const overVal=override[key];if(overVal!==null&&typeof overVal==="object"&&!Array.isArray(overVal)&&baseVal!==null&&typeof baseVal==="object"&&!Array.isArray(baseVal)){result[key]=deepMerge(baseVal,overVal)}else{result[key]=overVal}}return result}/**
178
55
  * 备份原始的 theme.schema.json5 文件。
179
56
  * 备份文件包含注释头,说明如何手动恢复。
180
57
  *
181
58
  * @param {string} schemaPath - theme.schema.json5 的绝对路径
182
59
  * @returns {string} 备份文件路径
183
- */
184
- function backupSchema(schemaPath) {
185
- const backupPath = schemaPath + SCHEMA_BACKUP_SUFFIX;
186
- const originalContent = fs.readFileSync(schemaPath, 'utf8');
187
- fs.writeFileSync(backupPath, SCHEMA_BACKUP_COMMENT + originalContent, 'utf8');
188
- return backupPath;
189
- }
190
-
191
- /**
60
+ */function backupSchema(schemaPath){const backupPath=schemaPath+SCHEMA_BACKUP_SUFFIX;const originalContent=fs.readFileSync(schemaPath,"utf8");fs.writeFileSync(backupPath,SCHEMA_BACKUP_COMMENT+originalContent,"utf8");return backupPath}/**
192
61
  * 从备份文件恢复 theme.schema.json5 并删除备份。
193
62
  *
194
63
  * @param {string} schemaPath - theme.schema.json5 的绝对路径
195
- */
196
- function restoreSchema(schemaPath) {
197
- const backupPath = schemaPath + SCHEMA_BACKUP_SUFFIX;
198
- if (!fs.existsSync(backupPath)) return;
199
-
200
- const backupContent = fs.readFileSync(backupPath, 'utf8');
201
- // 去除自动生成的注释头(以 "//" 开头的行)
202
- const lines = backupContent.split('\n');
203
- const contentStart = lines.findIndex(line => !line.startsWith('//'));
204
- const originalContent = lines.slice(contentStart >= 0 ? contentStart : 0).join('\n');
205
-
206
- fs.writeFileSync(schemaPath, originalContent, 'utf8');
207
- fs.unlinkSync(backupPath);
208
- }
209
-
210
- /**
64
+ */function restoreSchema(schemaPath){const backupPath=schemaPath+SCHEMA_BACKUP_SUFFIX;if(!fs.existsSync(backupPath))return;const backupContent=fs.readFileSync(backupPath,"utf8");// 去除自动生成的注释头(以 "//" 开头的行)
65
+ const lines=backupContent.split("\n");const contentStart=lines.findIndex(line=>!line.startsWith("//"));const originalContent=lines.slice(contentStart>=0?contentStart:0).join("\n");fs.writeFileSync(schemaPath,originalContent,"utf8");fs.unlinkSync(backupPath)}/**
211
66
  * 用合并后的数据(schema 默认值 + 主题覆盖值)覆写 theme.schema.json5,
212
67
  * 以便打包工具在构建时可以直接使用。
213
68
  *
214
69
  * @param {string} schemaPath - theme.schema.json5 的绝对路径
215
70
  * @param {object} mergedData - 要写入的合并后的主题数据
216
- */
217
- function overwriteSchemaWithMerged(schemaPath, mergedData) {
218
- const content = JSON.stringify(mergedData, null, 2);
219
- fs.writeFileSync(schemaPath, content, 'utf8');
220
- }
221
-
222
- /**
71
+ */function overwriteSchemaWithMerged(schemaPath,mergedData){const content=JSON.stringify(mergedData,null,2);fs.writeFileSync(schemaPath,content,"utf8")}/**
223
72
  * 执行主题校验并输出标准日志。
224
73
  * 单次构建(build.js)和批量构建(batch-build.js)共用此函数。
225
74
  *
@@ -240,60 +89,10 @@ function overwriteSchemaWithMerged(schemaPath, mergedData) {
240
89
  * valid – schema 文件不存在(跳过)或校验通过时为 true
241
90
  * errors – 校验通过为 null,否则为错误信息数组
242
91
  * schemaPath – theme.schema.json5 的绝对路径(用于 restoreSchema 调用),或 null
243
- */
244
- function runThemeValidation(themeName, projectRoot, themeConfig, options) {
245
- const sourceDir = (themeConfig && themeConfig.sourceDir) || 'src/theme';
246
- const themeBaseDir = path.join(projectRoot, sourceDir);
247
- const schemaPath = path.join(themeBaseDir, 'theme.schema.json5');
248
-
249
- // 没有 schema 文件 → 完全跳过校验
250
- if (!fs.existsSync(schemaPath)) {
251
- return { valid: true, errors: null, schemaPath: null };
252
- }
253
-
254
- const logFn = (options && options.log) || function defaultLog(msg, level) {
255
- const colors = { info: '\x1b[36m', success: '\x1b[32m', error: '\x1b[31m', warn: '\x1b[33m' };
256
- const reset = '\x1b[0m';
257
- const color = colors[level] || '';
258
- console.log(`${color}${msg}${reset}`);
259
- };
260
-
261
- logFn(`校验主题数据: ${themeName}`, 'info');
262
-
263
- const { defaultData, mergedData, isPlainData, errors } = validateThemeData(themeName, projectRoot, themeConfig);
264
-
265
- // Safety: schema file is already plain data (previous build was interrupted)
266
- // Use it directly, no need to backup/overwrite
267
- if (isPlainData) {
268
- logFn(`⚠️ theme.schema.json5 已经是纯数据格式(可能上次构建中断未恢复),直接使用`, 'warn');
269
- // schemaPath is returned as null so that restoreSchema won't be called
270
- // (there's nothing to restore — the file is already data, not a schema)
271
- return { valid: true, errors: null, schemaPath: null };
272
- }
273
-
274
- if (errors) {
275
- logFn(`主题 "${themeName}" 数据校验失败:`, 'error');
276
- errors.forEach(e => logFn(` - ${e}`, 'error'));
277
- return { valid: false, errors, schemaPath };
278
- }
279
-
280
- logFn(`主题 "${themeName}" 数据校验通过`, 'success');
281
-
282
- // 备份原始 schema,然后用合并后的数据覆写以供构建使用
283
- backupSchema(schemaPath);
284
- overwriteSchemaWithMerged(schemaPath, mergedData);
285
- logFn(`已备份 schema 并写入合并后的主题数据`, 'info');
286
-
287
- return { valid: true, errors: null, schemaPath };
288
- }
289
-
290
- module.exports = {
291
- validateThemeData,
292
- deepMerge,
293
- extractDefaultsFromSchema,
294
- isJsonSchema,
295
- runThemeValidation,
296
- backupSchema,
297
- restoreSchema,
298
- overwriteSchemaWithMerged,
299
- };
92
+ */function runThemeValidation(themeName,projectRoot,themeConfig,options){const sourceDir=themeConfig&&themeConfig.sourceDir||"src/theme";const themeBaseDir=path.join(projectRoot,sourceDir);const schemaPath=path.join(themeBaseDir,"theme.schema.json5");// 没有 schema 文件 → 完全跳过校验
93
+ if(!fs.existsSync(schemaPath)){return{valid:true,errors:null,schemaPath:null}}const logFn=options&&options.log||function defaultLog(msg,level){const colors={info:"\x1B[36m",success:"\x1B[32m",error:"\x1B[31m",warn:"\x1B[33m"};const reset="\x1B[0m";const color=colors[level]||"";console.log(`${color}${msg}${reset}`)};logFn(`校验主题数据: ${themeName}`,"info");const{defaultData,mergedData,isPlainData,errors}=validateThemeData(themeName,projectRoot,themeConfig);// Safety: schema file is already plain data (previous build was interrupted)
94
+ // Use it directly, no need to backup/overwrite
95
+ if(isPlainData){logFn(`⚠️ theme.schema.json5 已经是纯数据格式(可能上次构建中断未恢复),直接使用`,"warn");// schemaPath is returned as null so that restoreSchema won't be called
96
+ // (there's nothing to restore — the file is already data, not a schema)
97
+ return{valid:true,errors:null,schemaPath:null}}if(errors){logFn(`主题 "${themeName}" 数据校验失败:`,"error");errors.forEach(e=>logFn(` - ${e}`,"error"));return{valid:false,errors,schemaPath}}logFn(`主题 "${themeName}" 数据校验通过`,"success");// 备份原始 schema,然后用合并后的数据覆写以供构建使用
98
+ backupSchema(schemaPath);overwriteSchemaWithMerged(schemaPath,mergedData);logFn(`已备份 schema 并写入合并后的主题数据`,"info");return{valid:true,errors:null,schemaPath}}module.exports={validateThemeData,deepMerge,extractDefaultsFromSchema,isJsonSchema,runThemeValidation,backupSchema,restoreSchema,overwriteSchemaWithMerged};