@playcraft/devkit 1.0.31-beta.1 → 1.0.31-beta.11

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 (94) hide show
  1. package/README.md +262 -0
  2. package/cli/bin/playable-scripts.js +53 -6
  3. package/cli/commands/build.js +241 -32
  4. package/cli/commands/builds.js +13 -2
  5. package/cli/commands/dev.js +22 -1
  6. package/cli/commands/pack.js +323 -41
  7. package/cli/commands/vite-dev.js +7 -1
  8. package/core/artifacts/emit.js +98 -1
  9. package/core/artifacts/fflate.js +31 -1
  10. package/core/artifacts/zip.js +39 -1
  11. package/core/batch-build.js +1261 -149
  12. package/core/constants.js +20 -2
  13. package/core/cos-uploader.js +289 -23
  14. package/core/engines/cocos/clean-cache.js +188 -0
  15. package/core/engines/cocos/clean-cache.test.js +75 -0
  16. package/core/engines/cocos/cli-build.js +116 -2
  17. package/core/engines/cocos/cli.js +38 -1
  18. package/core/engines/cocos/constants.js +21 -1
  19. package/core/engines/cocos/creator-cli.js +108 -1
  20. package/core/engines/cocos/dev-server.js +412 -2
  21. package/core/engines/cocos/fs.js +105 -1
  22. package/core/engines/cocos/injects/3x/init.js +438 -19
  23. package/core/engines/cocos/injects/3x/main.js +52 -2
  24. package/core/engines/cocos/injects/libs/pako.js +2 -1
  25. package/core/engines/cocos/mcp-refresh.js +76 -2
  26. package/core/engines/cocos/merge.js +147 -2
  27. package/core/engines/cocos/pack.js +187 -1
  28. package/core/engines/cocos/preview-cache.js +51 -0
  29. package/core/engines/cocos/preview-cache.test.js +32 -0
  30. package/core/engines/cocos/preview-concat.js +192 -0
  31. package/core/engines/cocos/preview-concat.test.js +73 -0
  32. package/core/engines/cocos/preview-json-hook.js +200 -0
  33. package/core/engines/cocos/preview-json-pack.js +379 -0
  34. package/core/engines/cocos/preview-json-pack.test.js +226 -0
  35. package/core/engines/cocos/preview-proxy.js +132 -0
  36. package/core/engines/cocos/rebundle-preview.js +56 -0
  37. package/core/engines/cocos/strip-social-share.js +115 -3
  38. package/core/engines/cocos/theme-assets.js +161 -2
  39. package/core/engines/cocos/writable-cli.js +250 -0
  40. package/core/engines/cocos/writable-cli.test.js +112 -0
  41. package/core/engines/web/index.js +20 -1
  42. package/core/engines/web/vite.dev.js +322 -17
  43. package/core/engines/web/webpack.build.js +522 -65
  44. package/core/engines/web/webpack.common.js +177 -11
  45. package/core/engines/web/webpack.dev.js +88 -6
  46. package/core/index.d.ts +3 -0
  47. package/core/index.js +101 -7
  48. package/core/injectors/apply.js +94 -1
  49. package/core/injectors/check-channel-matrix.js +130 -2
  50. package/core/injectors/spec.js +217 -9
  51. package/core/loaders/gltf-loader.js +74 -28
  52. package/core/options.js +353 -16
  53. package/core/plugins/AdikteevInjectorPlugin.js +26 -4
  54. package/core/plugins/BigoAdsInjectorPlugin.js +21 -4
  55. package/core/plugins/DAPIInjectorPlugin.js +25 -2
  56. package/core/plugins/DebuggerInjectionPlugin.js +31 -3
  57. package/core/plugins/ExitAPIInjectorPlugin.js +56 -3
  58. package/core/plugins/FflateCompressionPlugin.js +231 -40
  59. package/core/plugins/FontInjectionWebpackPlugin.js +46 -4
  60. package/core/plugins/LiftoffInjectorPlugin.js +26 -4
  61. package/core/plugins/MRAIDInjectorPlugin.js +24 -2
  62. package/core/plugins/MintegralInjectorPlugin.js +25 -2
  63. package/core/plugins/PangleInjectorPlugin.js +24 -2
  64. package/core/plugins/PlaycraftOrientationInjectorPlugin.js +29 -3
  65. package/core/plugins/SnapchatInjectorPlugin.js +26 -4
  66. package/core/plugins/SocialMediaInjectorPlugin.js +119 -7
  67. package/core/plugins/TikTokInjectorPlugin.js +24 -2
  68. package/core/plugins/UnityInjectorPlugin.js +37 -8
  69. package/core/plugins/ZipPlugin.js +154 -20
  70. package/core/plugins/socialMediaVitePlugin.js +108 -2
  71. package/core/utils/buildDefines.js +30 -2
  72. package/core/utils/buildTemplateString.js +39 -5
  73. package/core/utils/date.js +16 -1
  74. package/core/utils/fontRegistry.js +81 -13
  75. package/core/utils/fontResolver.js +335 -37
  76. package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +42 -7
  77. package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +30 -3
  78. package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +43 -7
  79. package/core/utils/getPlaycraftOrientationSuffix.js +9 -2
  80. package/core/utils/injectSDKPlugins.js +68 -13
  81. package/core/utils/logOptions.js +46 -5
  82. package/core/utils/mergeOptions.js +21 -2
  83. package/core/utils/parseArgvOptions.js +125 -5
  84. package/core/utils/resolveChannelFold2Zip.js +15 -3
  85. package/core/utils/validateThemeData.js +227 -26
  86. package/core/validators/index.d.ts +4 -0
  87. package/core/validators/pre-build-checker.js +287 -25
  88. package/core/validators/tracking-validator.js +444 -43
  89. package/core/validators/tracking-validator.test.js +92 -0
  90. package/core/vite.dev.js +1 -1
  91. package/core/webpack.build.js +1 -1
  92. package/core/webpack.common.js +1 -1
  93. package/core/webpack.dev.js +1 -1
  94. package/package.json +3 -2
@@ -1,4 +1,118 @@
1
- "use strict";const fs=require("fs");const path=require("path");const{spawnCocosSync,needsXvfb}=require("./cli.js");const{resolveCreatorOutputDir}=require("./creator-cli.js");const{stripAdsdkCjsImport}=require("./strip-social-share.js");function shouldSkipEngineBuild(buildOptions={}){const cocos=buildOptions.cocos||{};return Boolean(buildOptions.skipEngineBuild||cocos.skipEngineBuild||process.env.PLAYABLE_SKIP_ENGINE_BUILD==="1")}function resolveWebMobileDest(projectRoot,buildOptions={}){const cocos=buildOptions.cocos||{};const fromConfig=resolveCreatorOutputDir(projectRoot,cocos.buildConfig||"build-configs/web-mobile.json");const explicit=cocos.webMobileDir?path.resolve(projectRoot,cocos.webMobileDir):null;if(shouldSkipEngineBuild(buildOptions)){return explicit||fromConfig||path.resolve(projectRoot,"dist/web-mobile")}return fromConfig||explicit||path.resolve(projectRoot,"dist/web-mobile")}function logStrip(result){if(result.skipped&&result.reason==="missing"){console.warn("[cocos] adsdk CJS missing, skip social-share strip");return}if(result.skipped&&result.reason==="already"){console.log("[cocos] social-share import() already stripped");return}if(result.skipped){console.warn("[cocos] social-share import() not found in CJS");return}console.log("[cocos] stripped social-share import() from adsdk CJS")}/**
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { spawnCocosSync, needsXvfb } = require('./cli.js');
6
+ const { resolveCreatorOutputDir } = require('./creator-cli.js');
7
+ const { stripAdsdkCjsImport } = require('./strip-social-share.js');
8
+ const { prepareWritableCocosCli } = require('./writable-cli.js');
9
+ const { cleanCocosProjectCaches, ensureWritableCocosWorkdirs, shouldCleanCocosCache } = require('./clean-cache.js');
10
+
11
+ function shouldSkipEngineBuild(buildOptions = {}) {
12
+ const cocos = buildOptions.cocos || {};
13
+ return Boolean(
14
+ buildOptions.skipEngineBuild ||
15
+ cocos.skipEngineBuild ||
16
+ process.env.PLAYABLE_SKIP_ENGINE_BUILD === '1'
17
+ );
18
+ }
19
+
20
+ function resolveWebMobileDest(projectRoot, buildOptions = {}) {
21
+ const cocos = buildOptions.cocos || {};
22
+ const fromConfig = resolveCreatorOutputDir(projectRoot, cocos.buildConfig || 'build-configs/web-mobile.json');
23
+ const explicit = cocos.webMobileDir ? path.resolve(projectRoot, cocos.webMobileDir) : null;
24
+ if (shouldSkipEngineBuild(buildOptions)) {
25
+ return explicit || fromConfig || path.resolve(projectRoot, 'dist/web-mobile');
26
+ }
27
+ return fromConfig || explicit || path.resolve(projectRoot, 'dist/web-mobile');
28
+ }
29
+
30
+ function logStrip(result) {
31
+ if (result.skipped && result.reason === 'missing') {
32
+ console.warn('[cocos] adsdk CJS missing, skip social-share strip');
33
+ return;
34
+ }
35
+ if (result.skipped && result.reason === 'already') {
36
+ console.log('[cocos] social-share import() already stripped');
37
+ return;
38
+ }
39
+ if (result.skipped) {
40
+ console.warn('[cocos] social-share import() not found in CJS');
41
+ return;
42
+ }
43
+ console.log('[cocos] stripped social-share import() from adsdk CJS');
44
+ }
45
+
46
+ /**
2
47
  * 用官方 cocos CLI 打 web-mobile(不再走 Creator 编辑器)。
3
48
  * Linux 上自动套 xvfb-run -a(shader 编译需要虚拟显示)。
4
- */function runCocosEngineBuild(projectRoot,buildOptions={}){logStrip(stripAdsdkCjsImport(projectRoot));const dest=resolveWebMobileDest(projectRoot,buildOptions);if(shouldSkipEngineBuild(buildOptions)){if(!fs.existsSync(dest)){throw new Error(`[cocos] skipEngineBuild but missing ${path.relative(projectRoot,dest)}`)}console.log(`[cocos] skip engine build, using ${path.relative(projectRoot,dest)}`);return dest}const cocos=buildOptions.cocos||{};const configRel=cocos.buildConfig||"build-configs/web-mobile.json";const configPath=path.resolve(projectRoot,configRel);if(!fs.existsSync(configPath)){throw new Error(`[cocos] missing build config: ${configRel}`)}const args=["--no-interactive","build","--project",projectRoot,"--platform","web-mobile","--build-config",configPath];if(needsXvfb()){console.log("[cocos] linux, wrapping xvfb-run -a")}console.log(`[cocos] build web-mobile ← ${configRel}`);const result=spawnCocosSync(args,{cwd:projectRoot,stdio:"inherit",env:process.env});if(result.error){throw result.error}if(result.status!==0){throw new Error(`[cocos] build failed, exit ${result.status}`)}console.log(`[cocos] web-mobile ready at ${path.relative(projectRoot,dest)}`);return dest}module.exports={runCocosEngineBuild,resolveWebMobileDest,shouldSkipEngineBuild};
49
+ */
50
+ function runCocosEngineBuild(projectRoot, buildOptions = {}) {
51
+ logStrip(stripAdsdkCjsImport(projectRoot));
52
+
53
+ const dest = resolveWebMobileDest(projectRoot, buildOptions);
54
+ if (shouldSkipEngineBuild(buildOptions)) {
55
+ if (!fs.existsSync(dest)) {
56
+ throw new Error(`[cocos] skipEngineBuild but missing ${path.relative(projectRoot, dest)}`);
57
+ }
58
+ console.log(`[cocos] skip engine build, using ${path.relative(projectRoot, dest)}`);
59
+ return dest;
60
+ }
61
+
62
+ if (shouldCleanCocosCache(buildOptions)) {
63
+ const removed = cleanCocosProjectCaches(projectRoot);
64
+ console.log(`[cocos] cleaned project caches: ${removed.join(', ') || '(none)'}`);
65
+ }
66
+ ensureWritableCocosWorkdirs(projectRoot);
67
+
68
+ const cocos = buildOptions.cocos || {};
69
+ const configRel = cocos.buildConfig || 'build-configs/web-mobile.json';
70
+ const configPath = path.resolve(projectRoot, configRel);
71
+ if (!fs.existsSync(configPath)) {
72
+ throw new Error(`[cocos] missing build config: ${configRel}`);
73
+ }
74
+
75
+ const args = [
76
+ '--no-interactive',
77
+ 'build',
78
+ '--project',
79
+ projectRoot,
80
+ '--platform',
81
+ 'web-mobile',
82
+ '--build-config',
83
+ configPath,
84
+ ];
85
+
86
+ const prepared = prepareWritableCocosCli(projectRoot);
87
+ const env = { ...process.env, ...prepared.env };
88
+
89
+ if (needsXvfb()) {
90
+ console.log('[cocos] linux, wrapping xvfb-run -a');
91
+ }
92
+ console.log(`[cocos] build web-mobile ← ${configRel}`);
93
+
94
+ const result = spawnCocosSync(args, {
95
+ cwd: projectRoot,
96
+ stdio: 'inherit',
97
+ env,
98
+ });
99
+
100
+ if (result.error) {
101
+ throw result.error;
102
+ }
103
+ if (result.status !== 0) {
104
+ throw new Error(`[cocos] build failed, exit ${result.status}`);
105
+ }
106
+
107
+ console.log(`[cocos] web-mobile ready at ${path.relative(projectRoot, dest)}`);
108
+ return dest;
109
+ }
110
+
111
+ module.exports = {
112
+ runCocosEngineBuild,
113
+ resolveWebMobileDest,
114
+ shouldSkipEngineBuild,
115
+ cleanCocosProjectCaches,
116
+ ensureWritableCocosWorkdirs,
117
+ shouldCleanCocosCache,
118
+ };
@@ -1 +1,38 @@
1
- "use strict";const{spawn,spawnSync}=require("child_process");function resolveCocosBin(){return process.env.COCOS_CLI||"cocos"}function needsXvfb(){return process.platform==="linux"}function cocosCommand(args){const bin=resolveCocosBin();if(needsXvfb()){return{cmd:"xvfb-run",args:["-a",bin,...args]}}return{cmd:bin,args}}function spawnCocos(args,options){const{cmd,args:fullArgs}=cocosCommand(args);return spawn(cmd,fullArgs,options)}function spawnCocosSync(args,options){const{cmd,args:fullArgs}=cocosCommand(args);return spawnSync(cmd,fullArgs,options)}module.exports={resolveCocosBin,needsXvfb,spawnCocos,spawnCocosSync};
1
+ 'use strict';
2
+
3
+ const { spawn, spawnSync } = require('child_process');
4
+
5
+ function resolveCocosBin(env = process.env) {
6
+ return env.COCOS_CLI || 'cocos';
7
+ }
8
+
9
+ function needsXvfb() {
10
+ return process.platform === 'linux';
11
+ }
12
+
13
+ function cocosCommand(args, env = process.env) {
14
+ const bin = resolveCocosBin(env);
15
+ if (needsXvfb()) {
16
+ return { cmd: 'xvfb-run', args: ['-a', bin, ...args] };
17
+ }
18
+ return { cmd: bin, args };
19
+ }
20
+
21
+ function spawnCocos(args, options = {}) {
22
+ const env = options.env || process.env;
23
+ const { cmd, args: fullArgs } = cocosCommand(args, env);
24
+ return spawn(cmd, fullArgs, options);
25
+ }
26
+
27
+ function spawnCocosSync(args, options = {}) {
28
+ const env = options.env || process.env;
29
+ const { cmd, args: fullArgs } = cocosCommand(args, env);
30
+ return spawnSync(cmd, fullArgs, options);
31
+ }
32
+
33
+ module.exports = {
34
+ resolveCocosBin,
35
+ needsXvfb,
36
+ spawnCocos,
37
+ spawnCocosSync,
38
+ };
@@ -1 +1,21 @@
1
- "use strict";const ADAPTER_FETCH="adapterFetch";const TO_STRING_EXTNAME=[".txt",".xml",".vsh",".fsh",".atlas",".tmx",".tsx",".json",".ExportJson",".plist",".fnt",".js",".zip"];const TO_SKIP_EXTNAME=[".ico",".html",".css"];const TRANSPARENT_GIF="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";const FILE_MAX_SIZE=2*1024*1024*0.8;const THEME_ASSET_EXT=/\.(png|webp|jpe?g|gif|svg|mp3|wav|m4a|ogg|mp4)$/i;module.exports={ADAPTER_FETCH,TO_STRING_EXTNAME,TO_SKIP_EXTNAME,TRANSPARENT_GIF,FILE_MAX_SIZE,THEME_ASSET_EXT};
1
+ 'use strict';
2
+
3
+ const ADAPTER_FETCH = 'adapterFetch';
4
+ const TO_STRING_EXTNAME = [
5
+ '.txt', '.xml', '.vsh', '.fsh', '.atlas', '.tmx', '.tsx', '.json',
6
+ '.ExportJson', '.plist', '.fnt', '.js', '.zip'
7
+ ];
8
+ const TO_SKIP_EXTNAME = ['.ico', '.html', '.css'];
9
+ const TRANSPARENT_GIF =
10
+ 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
11
+ const FILE_MAX_SIZE = 2 * 1024 * 1024 * 0.8;
12
+ const THEME_ASSET_EXT = /\.(png|webp|jpe?g|gif|svg|mp3|mpeg|wav|m4a|aac|ogg|mp4)$/i;
13
+
14
+ module.exports = {
15
+ ADAPTER_FETCH,
16
+ TO_STRING_EXTNAME,
17
+ TO_SKIP_EXTNAME,
18
+ TRANSPARENT_GIF,
19
+ FILE_MAX_SIZE,
20
+ THEME_ASSET_EXT,
21
+ };
@@ -1 +1,108 @@
1
- "use strict";const fs=require("fs");const path=require("path");const{spawnSync}=require("child_process");function readCreatorVersion(root){try{const pkg=require(path.join(root,"package.json"));return pkg.creator&&pkg.creator.version||"3.8.6"}catch{return"3.8.6"}}function resolveCreator(root,creatorBin){if(creatorBin)return creatorBin;if(process.env.COCOS_CREATOR)return process.env.COCOS_CREATOR;const version=readCreatorVersion(root);const darwin=[`/Applications/Cocos/Creator/${version}/CocosCreator.app/Contents/MacOS/CocosCreator`,`/Applications/CocosCreator/Creator/${version}/CocosCreator.app/Contents/MacOS/CocosCreator`];const win32=[`C:\\ProgramData\\cocos\\editors\\Creator\\${version}\\CocosCreator.exe`,`C:\\Cocos\\Creator\\${version}\\CocosCreator.exe`,`C:\\CocosCreator\\Creator\\${version}\\CocosCreator.exe`];const linux=[`/opt/Cocos/Creator/${version}/CocosCreator`,`${process.env.HOME||""}/Cocos/Creator/${version}/CocosCreator`];const candidates=process.platform==="darwin"?darwin:process.platform==="win32"?win32:linux;return candidates.find(p=>p&&fs.existsSync(p))}function creatorEnv(){const env={...process.env};delete env.ELECTRON_RUN_AS_NODE;return env}function resolveCreatorOutputDir(root,buildConfigRel){const configPath=path.resolve(root,buildConfigRel||"build-configs/web-mobile.json");if(!fs.existsSync(configPath))return null;try{const cfg=JSON.parse(fs.readFileSync(configPath,"utf8"));const outputName=cfg.outputName||"web-mobile";const buildPath=cfg.buildPath||"project://build";if(buildPath.startsWith("project://")){const rel=buildPath.slice("project://".length).replace(/^\/+/,"");return path.resolve(root,rel,outputName)}if(path.isAbsolute(buildPath))return path.join(buildPath,outputName);return path.resolve(root,buildPath,outputName)}catch{return null}}function runCreatorBuild(root,buildOptions){const cocos=buildOptions.cocos||{};if(cocos.skipEngineBuild){console.log("[cocos] skipEngineBuild: using existing web-mobile");return}const configPath=path.resolve(root,cocos.buildConfig||"build-configs/web-mobile.json");if(!fs.existsSync(configPath)){throw new Error(`[cocos] missing Creator build config: ${configPath}`)}const bin=resolveCreator(root,cocos.creatorBin);if(!bin){throw new Error("[cocos] Cocos Creator not found. Set COCOS_CREATOR or cocos.creatorBin.")}if(process.platform==="linux"){console.warn("[cocos] Creator fallback on linux; prefer official cocos CLI (xvfb-run -a).")}const buildArg=`configPath=${configPath.replace(/\\/g,"/")};stage=build`;console.log(`[cocos] ${bin}`);console.log(`[cocos] --build ${buildArg}`);const result=spawnSync(bin,["--project",root,"--nologin","--build",buildArg],{cwd:root,stdio:"inherit",env:creatorEnv()});if(result.error)throw result.error;const code=result.status;if(code===36||code===0){console.log("[cocos] web-mobile ready");return}throw new Error(`[cocos] Creator build failed, exit ${code}`)}module.exports={readCreatorVersion,resolveCreator,resolveCreatorOutputDir,creatorEnv,runCreatorBuild};
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { spawnSync } = require('child_process');
6
+
7
+ function readCreatorVersion(root) {
8
+ try {
9
+ const pkg = require(path.join(root, 'package.json'));
10
+ return (pkg.creator && pkg.creator.version) || '3.8.6';
11
+ } catch {
12
+ return '3.8.6';
13
+ }
14
+ }
15
+
16
+ function resolveCreator(root, creatorBin) {
17
+ if (creatorBin) return creatorBin;
18
+ if (process.env.COCOS_CREATOR) return process.env.COCOS_CREATOR;
19
+
20
+ const version = readCreatorVersion(root);
21
+ const darwin = [
22
+ `/Applications/Cocos/Creator/${version}/CocosCreator.app/Contents/MacOS/CocosCreator`,
23
+ `/Applications/CocosCreator/Creator/${version}/CocosCreator.app/Contents/MacOS/CocosCreator`,
24
+ ];
25
+ const win32 = [
26
+ `C:\\ProgramData\\cocos\\editors\\Creator\\${version}\\CocosCreator.exe`,
27
+ `C:\\Cocos\\Creator\\${version}\\CocosCreator.exe`,
28
+ `C:\\CocosCreator\\Creator\\${version}\\CocosCreator.exe`,
29
+ ];
30
+ const linux = [
31
+ `/opt/Cocos/Creator/${version}/CocosCreator`,
32
+ `${process.env.HOME || ''}/Cocos/Creator/${version}/CocosCreator`,
33
+ ];
34
+ const candidates = process.platform === 'darwin' ? darwin : process.platform === 'win32' ? win32 : linux;
35
+ return candidates.find((p) => p && fs.existsSync(p));
36
+ }
37
+
38
+ function creatorEnv() {
39
+ const env = { ...process.env };
40
+ delete env.ELECTRON_RUN_AS_NODE;
41
+ return env;
42
+ }
43
+
44
+ function resolveCreatorOutputDir(root, buildConfigRel) {
45
+ const configPath = path.resolve(root, buildConfigRel || 'build-configs/web-mobile.json');
46
+ if (!fs.existsSync(configPath)) return null;
47
+ try {
48
+ const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
49
+ const outputName = cfg.outputName || 'web-mobile';
50
+ const buildPath = cfg.buildPath || 'project://build';
51
+ if (buildPath.startsWith('project://')) {
52
+ const rel = buildPath.slice('project://'.length).replace(/^\/+/, '');
53
+ return path.resolve(root, rel, outputName);
54
+ }
55
+ if (path.isAbsolute(buildPath)) return path.join(buildPath, outputName);
56
+ return path.resolve(root, buildPath, outputName);
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ function runCreatorBuild(root, buildOptions) {
63
+ const cocos = buildOptions.cocos || {};
64
+ if (cocos.skipEngineBuild) {
65
+ console.log('[cocos] skipEngineBuild: using existing web-mobile');
66
+ return;
67
+ }
68
+
69
+ const configPath = path.resolve(root, cocos.buildConfig || 'build-configs/web-mobile.json');
70
+ if (!fs.existsSync(configPath)) {
71
+ throw new Error(`[cocos] missing Creator build config: ${configPath}`);
72
+ }
73
+
74
+ const bin = resolveCreator(root, cocos.creatorBin);
75
+ if (!bin) {
76
+ throw new Error('[cocos] Cocos Creator not found. Set COCOS_CREATOR or cocos.creatorBin.');
77
+ }
78
+
79
+ if (process.platform === 'linux') {
80
+ console.warn('[cocos] Creator fallback on linux; prefer official cocos CLI (xvfb-run -a).');
81
+ }
82
+
83
+ const buildArg = `configPath=${configPath.replace(/\\/g, '/')};stage=build`;
84
+ console.log(`[cocos] ${bin}`);
85
+ console.log(`[cocos] --build ${buildArg}`);
86
+
87
+ const result = spawnSync(bin, ['--project', root, '--nologin', '--build', buildArg], {
88
+ cwd: root,
89
+ stdio: 'inherit',
90
+ env: creatorEnv(),
91
+ });
92
+
93
+ if (result.error) throw result.error;
94
+ const code = result.status;
95
+ if (code === 36 || code === 0) {
96
+ console.log('[cocos] web-mobile ready');
97
+ return;
98
+ }
99
+ throw new Error(`[cocos] Creator build failed, exit ${code}`);
100
+ }
101
+
102
+ module.exports = {
103
+ readCreatorVersion,
104
+ resolveCreator,
105
+ resolveCreatorOutputDir,
106
+ creatorEnv,
107
+ runCreatorBuild,
108
+ };