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

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 (101) hide show
  1. package/cli/bin/playable-scripts.js +6 -53
  2. package/cli/commands/build.js +32 -241
  3. package/cli/commands/builds.js +2 -13
  4. package/cli/commands/dev.js +1 -22
  5. package/cli/commands/pack.js +40 -320
  6. package/cli/commands/vite-dev.js +1 -7
  7. package/core/artifacts/emit-fonts.test.js +1 -0
  8. package/core/artifacts/emit.js +4 -98
  9. package/core/artifacts/fflate.js +1 -31
  10. package/core/artifacts/zip.js +1 -39
  11. package/core/batch-build.js +147 -1261
  12. package/core/constants.js +2 -20
  13. package/core/cos-uploader.js +23 -289
  14. package/core/engines/cocos/adapter-vfs-path.test.js +1 -0
  15. package/core/engines/cocos/clean-cache.js +5 -179
  16. package/core/engines/cocos/clean-cache.test.js +1 -75
  17. package/core/engines/cocos/cli-build.js +2 -116
  18. package/core/engines/cocos/cli.js +11 -38
  19. package/core/engines/cocos/constants.js +1 -21
  20. package/core/engines/cocos/creator-cli.js +1 -108
  21. package/core/engines/cocos/dev-server.js +4 -412
  22. package/core/engines/cocos/fs.js +1 -105
  23. package/core/engines/cocos/injects/3x/init.js +19 -438
  24. package/core/engines/cocos/injects/3x/main.js +2 -52
  25. package/core/engines/cocos/injects/libs/pako.js +1 -2
  26. package/core/engines/cocos/mcp-refresh.js +2 -76
  27. package/core/engines/cocos/merge-inline.test.js +1 -0
  28. package/core/engines/cocos/merge.js +9 -148
  29. package/core/engines/cocos/omit-playable-weight.js +10 -0
  30. package/core/engines/cocos/omit-playable-weight.test.js +2 -0
  31. package/core/engines/cocos/pack.js +1 -187
  32. package/core/engines/cocos/preview-cache.js +2 -48
  33. package/core/engines/cocos/preview-cache.test.js +1 -32
  34. package/core/engines/cocos/preview-concat.js +6 -189
  35. package/core/engines/cocos/preview-concat.test.js +1 -73
  36. package/core/engines/cocos/preview-json-hook.js +1 -200
  37. package/core/engines/cocos/preview-json-pack.js +3 -376
  38. package/core/engines/cocos/preview-json-pack.test.js +2 -226
  39. package/core/engines/cocos/preview-proxy.js +1 -132
  40. package/core/engines/cocos/rebundle-preview.js +2 -54
  41. package/core/engines/cocos/strip-social-share.js +3 -115
  42. package/core/engines/cocos/theme-assets.js +2 -161
  43. package/core/engines/cocos/theme-levels.js +1 -0
  44. package/core/engines/cocos/writable-cli.js +2 -248
  45. package/core/engines/cocos/writable-cli.test.js +1 -112
  46. package/core/engines/web/index.js +1 -20
  47. package/core/engines/web/vite.dev.js +17 -322
  48. package/core/engines/web/webpack.build.js +37 -522
  49. package/core/engines/web/webpack.common.js +11 -177
  50. package/core/engines/web/webpack.dev.js +6 -88
  51. package/core/index.js +7 -101
  52. package/core/injectors/apply.js +1 -94
  53. package/core/injectors/check-channel-matrix.js +2 -130
  54. package/core/injectors/spec.js +10 -217
  55. package/core/loaders/gltf-loader.js +28 -74
  56. package/core/options.js +16 -353
  57. package/core/plugins/AdikteevInjectorPlugin.js +4 -26
  58. package/core/plugins/BigoAdsInjectorPlugin.js +4 -21
  59. package/core/plugins/DAPIInjectorPlugin.js +2 -25
  60. package/core/plugins/DebuggerInjectionPlugin.js +3 -31
  61. package/core/plugins/ExitAPIInjectorPlugin.js +3 -56
  62. package/core/plugins/FflateCompressionPlugin.js +160 -307
  63. package/core/plugins/FflateCompressionPlugin.test.js +13 -0
  64. package/core/plugins/FontInjectionWebpackPlugin.js +4 -46
  65. package/core/plugins/LiftoffInjectorPlugin.js +4 -26
  66. package/core/plugins/MRAIDInjectorPlugin.js +2 -24
  67. package/core/plugins/MintegralInjectorPlugin.js +2 -25
  68. package/core/plugins/PangleInjectorPlugin.js +2 -24
  69. package/core/plugins/PlaycraftOrientationInjectorPlugin.js +3 -29
  70. package/core/plugins/SnapchatInjectorPlugin.js +4 -26
  71. package/core/plugins/SocialMediaInjectorPlugin.js +7 -119
  72. package/core/plugins/TikTokInjectorPlugin.js +2 -24
  73. package/core/plugins/UnityInjectorPlugin.js +8 -37
  74. package/core/plugins/ZipPlugin.js +20 -154
  75. package/core/plugins/socialMediaVitePlugin.js +2 -108
  76. package/core/utils/buildDefines.js +2 -30
  77. package/core/utils/buildTemplateString.js +5 -39
  78. package/core/utils/date.js +1 -16
  79. package/core/utils/fontRegistry.js +13 -81
  80. package/core/utils/fontResolver.js +37 -335
  81. package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +8 -42
  82. package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +3 -30
  83. package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +7 -43
  84. package/core/utils/getPlaycraftOrientationSuffix.js +2 -9
  85. package/core/utils/injectSDKPlugins.js +13 -68
  86. package/core/utils/logOptions.js +5 -46
  87. package/core/utils/mergeOptions.js +2 -21
  88. package/core/utils/obfuscateLevels.js +14 -0
  89. package/core/utils/obfuscateLevels.test.js +10 -0
  90. package/core/utils/parseArgvOptions.js +5 -125
  91. package/core/utils/resolveChannelFold2Zip.js +3 -15
  92. package/core/utils/validateThemeData.js +26 -227
  93. package/core/validators/pre-build-checker.js +25 -287
  94. package/core/validators/tracking-validator.js +40 -441
  95. package/core/validators/tracking-validator.test.js +2 -84
  96. package/core/vite.dev.js +1 -1
  97. package/core/webpack.build.js +1 -1
  98. package/core/webpack.common.js +1 -1
  99. package/core/webpack.dev.js +1 -1
  100. package/package.json +2 -3
  101. package/README.md +0 -262
@@ -1,162 +1,3 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const JSON5 = require('json5');
6
- const { getBase64FromFile, getAllFilesFromDir, sniffAudioMime } = require('./fs');
7
- const { THEME_ASSET_EXT } = require('./constants');
8
- const { validateThemeData } = require('../../utils/validateThemeData');
9
-
10
- function detectCurrentTheme(projectRoot, themeConfig) {
11
- const entryFile = (themeConfig && themeConfig.entryFile) || 'src/theme/index.ts';
12
- const entryPath = path.join(projectRoot, entryFile);
13
- if (!fs.existsSync(entryPath)) return null;
14
- const content = fs.readFileSync(entryPath, 'utf8');
15
- const match = content.match(/from\s+['"]\.\/([^'"]+)['"]/);
16
- return match ? match[1] : null;
17
- }
18
-
19
- function isInlinableThemeAsset(filePath) {
20
- if (THEME_ASSET_EXT.test(filePath)) return true;
21
- try {
22
- return Boolean(sniffAudioMime(fs.readFileSync(filePath)));
23
- } catch {
24
- return false;
25
- }
26
- }
27
-
28
- function collectDirAssets(assetsDir, keyPrefix) {
29
- const map = {};
30
- if (!fs.existsSync(assetsDir)) return map;
31
- const root = assetsDir.replace(/\\/g, '/');
32
- for (const filePath of getAllFilesFromDir(assetsDir)) {
33
- if (!isInlinableThemeAsset(filePath)) continue;
34
- const rel = filePath.replace(/\\/g, '/').slice(root.length).replace(/^\//, '');
35
- map[`${keyPrefix}${rel}`] = getBase64FromFile(filePath);
36
- }
37
- return map;
38
- }
39
-
40
- function collectReferencedFiles(projectRoot, sourceDir, themeName, mergedData, assetRecord) {
41
- if (!mergedData || typeof mergedData !== 'object') return;
42
- const themeRoot = path.join(projectRoot, sourceDir, themeName);
43
- for (const value of Object.values(mergedData)) {
44
- if (typeof value === 'string') {
45
- if (assetRecord[value]) continue;
46
- const rel = value.replace(/^\.\//, '');
47
- const candidates = [
48
- path.join(themeRoot, rel),
49
- path.join(themeRoot, 'assets', path.basename(rel)),
50
- path.resolve(projectRoot, rel),
51
- ];
52
- const abs = candidates.find((p) => fs.existsSync(p) && fs.statSync(p).isFile() && isInlinableThemeAsset(p));
53
- if (abs) assetRecord[value] = getBase64FromFile(abs);
54
- } else if (value && typeof value === 'object' && !Array.isArray(value)) {
55
- collectReferencedFiles(projectRoot, sourceDir, themeName, value, assetRecord);
56
- }
57
- }
58
- }
59
-
60
- function loadThemeI18n(projectRoot, sourceDir, themeName) {
61
- const p = path.join(projectRoot, sourceDir, themeName, 'theme.i18n.runtime.json5');
62
- if (!fs.existsSync(p)) return {};
63
- try {
64
- return JSON5.parse(fs.readFileSync(p, 'utf8')) || {};
65
- } catch {
66
- return {};
67
- }
68
- }
69
-
70
- /**
1
+ "use strict";const fs=require("fs");const path=require("path");const JSON5=require("json5");const{getBase64FromFile,getAllFilesFromDir,sniffAudioMime}=require("./fs");const{THEME_ASSET_EXT}=require("./constants");const{validateThemeData}=require("../../utils/validateThemeData");const{loadThemeLevels,writeGeneratedLevels}=require("./theme-levels.js");function detectCurrentTheme(projectRoot,themeConfig){const entryFile=themeConfig&&themeConfig.entryFile||"src/theme/index.ts";const entryPath=path.join(projectRoot,entryFile);if(!fs.existsSync(entryPath))return null;const content=fs.readFileSync(entryPath,"utf8");const match=content.match(/from\s+['"]\.\/([^'"]+)['"]/);return match?match[1]:null}function isInlinableThemeAsset(filePath){if(THEME_ASSET_EXT.test(filePath))return true;try{return Boolean(sniffAudioMime(fs.readFileSync(filePath)))}catch{return false}}function collectDirAssets(assetsDir,keyPrefix){const map={};if(!fs.existsSync(assetsDir))return map;const root=assetsDir.replace(/\\/g,"/");for(const filePath of getAllFilesFromDir(assetsDir)){if(!isInlinableThemeAsset(filePath))continue;const rel=filePath.replace(/\\/g,"/").slice(root.length).replace(/^\//,"");map[`${keyPrefix}${rel}`]=getBase64FromFile(filePath)}return map}function collectReferencedFiles(projectRoot,sourceDir,themeName,mergedData,assetRecord){if(!mergedData||typeof mergedData!=="object")return;const themeRoot=path.join(projectRoot,sourceDir,themeName);for(const value of Object.values(mergedData)){if(typeof value==="string"){if(assetRecord[value])continue;const rel=value.replace(/^\.\//,"");const candidates=[path.join(themeRoot,rel),path.join(themeRoot,"assets",path.basename(rel)),path.resolve(projectRoot,rel)];const abs=candidates.find(p=>fs.existsSync(p)&&fs.statSync(p).isFile()&&isInlinableThemeAsset(p));if(abs)assetRecord[value]=getBase64FromFile(abs)}else if(value&&typeof value==="object"&&!Array.isArray(value)){collectReferencedFiles(projectRoot,sourceDir,themeName,value,assetRecord)}}}function loadThemeI18n(projectRoot,sourceDir,themeName){const p=path.join(projectRoot,sourceDir,themeName,"theme.i18n.runtime.json5");if(!fs.existsSync(p))return{};try{return JSON5.parse(fs.readFileSync(p,"utf8"))||{}}catch{return{}}}/**
71
2
  * Read src/theme json5 + theme/assets the same way Phaser webpack inlines them.
72
- */
73
- function loadThemePayload(projectRoot, themeName, themeConfig = {}) {
74
- const sourceDir = themeConfig.sourceDir || 'src/theme';
75
- const { mergedData, errors } = validateThemeData(themeName, projectRoot, themeConfig);
76
- if (errors) {
77
- throw new Error(`Theme "${themeName}" validation failed:\n${errors.join('\n')}`);
78
- }
79
-
80
- const assetsDir = path.join(projectRoot, sourceDir, themeName, 'assets');
81
- const assetRecordByPath = collectDirAssets(assetsDir, './assets/');
82
- collectReferencedFiles(projectRoot, sourceDir, themeName, mergedData, assetRecordByPath);
83
-
84
- return {
85
- themeName,
86
- mergedData: mergedData || {},
87
- i18nOverride: loadThemeI18n(projectRoot, sourceDir, themeName),
88
- assetRecordByPath,
89
- };
90
- }
91
-
92
- function copyThemeAssetsToResources(projectRoot, themeName) {
93
- const srcDir = path.join(projectRoot, 'src/theme', themeName, 'assets');
94
- const destDir = path.join(projectRoot, 'assets/resources/textures');
95
- if (!fs.existsSync(srcDir) || !fs.existsSync(destDir)) return false;
96
- let changed = false;
97
- for (const name of fs.readdirSync(srcDir)) {
98
- if (!/\.(png|webp|jpe?g)$/i.test(name)) continue;
99
- const dest = path.join(destDir, name);
100
- if (!fs.existsSync(dest)) continue;
101
- const from = path.join(srcDir, name);
102
- const next = fs.readFileSync(from);
103
- const prev = fs.readFileSync(dest);
104
- if (Buffer.compare(next, prev) === 0) continue;
105
- fs.writeFileSync(dest, next);
106
- changed = true;
107
- }
108
- return changed;
109
- }
110
-
111
- function themeAssetStamp(dir) {
112
- if (!fs.existsSync(dir)) return '';
113
- try {
114
- return fs
115
- .readdirSync(dir)
116
- .filter((name) => THEME_ASSET_EXT.test(name))
117
- .map((name) => {
118
- const st = fs.statSync(path.join(dir, name));
119
- return `${name}:${st.size}:${Math.round(st.mtimeMs)}`;
120
- })
121
- .join('|');
122
- } catch {
123
- return '';
124
- }
125
- }
126
-
127
- function writeGeneratedThemeModule(filePath, payload) {
128
- const header =
129
- '/* eslint-disable */\n' +
130
- '// Generated from src/theme by playable-scripts. Do not edit.\n';
131
- const body =
132
- `export const THEME_DATA = ${JSON.stringify(payload.mergedData, null, 2)} as Record<string, unknown>;\n` +
133
- `export const THEME_I18N_OVERRIDE = ${JSON.stringify(payload.i18nOverride, null, 2)} as Record<string, Record<string, string>>;\n` +
134
- `export const THEME_ASSETS = ${JSON.stringify(payload.assetRecordByPath)} as Record<string, string>;\n`;
135
- const next = header + body;
136
- if (fs.existsSync(filePath) && fs.readFileSync(filePath, 'utf8') === next) {
137
- return false;
138
- }
139
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
140
- fs.writeFileSync(filePath, next);
141
- return true;
142
- }
143
-
144
- function syncGeneratedTheme(projectRoot, options = {}) {
145
- const themeConfig = options.themeConfig || { sourceDir: 'src/theme', entryFile: 'src/theme/index.ts' };
146
- const themeName = options.themeName || detectCurrentTheme(projectRoot, themeConfig);
147
- if (!themeName) return null;
148
- const payload = loadThemePayload(projectRoot, themeName, themeConfig);
149
- const rel = options.generatedRel || 'assets/scripts/platform/theme.generated.ts';
150
- const copied = copyThemeAssetsToResources(projectRoot, themeName);
151
- const changed = writeGeneratedThemeModule(path.join(projectRoot, rel), payload);
152
- return { themeName, payload, generatedRel: rel, changed: changed || copied };
153
- }
154
-
155
- module.exports = {
156
- detectCurrentTheme,
157
- loadThemePayload,
158
- writeGeneratedThemeModule,
159
- syncGeneratedTheme,
160
- collectDirAssets,
161
- themeAssetStamp,
162
- };
3
+ */function loadThemePayload(projectRoot,themeName,themeConfig={}){const sourceDir=themeConfig.sourceDir||"src/theme";const{mergedData,errors}=validateThemeData(themeName,projectRoot,themeConfig);if(errors){throw new Error(`Theme "${themeName}" validation failed:\n${errors.join("\n")}`)}const assetsDir=path.join(projectRoot,sourceDir,themeName,"assets");const assetRecordByPath=collectDirAssets(assetsDir,"./assets/");collectReferencedFiles(projectRoot,sourceDir,themeName,mergedData,assetRecordByPath);return{themeName,mergedData:mergedData||{},i18nOverride:loadThemeI18n(projectRoot,sourceDir,themeName),assetRecordByPath,levels:loadThemeLevels(projectRoot,themeName,themeConfig)}}function copyThemeAssetsToResources(projectRoot,themeName){const srcDir=path.join(projectRoot,"src/theme",themeName,"assets");const destDir=path.join(projectRoot,"assets/resources/textures");if(!fs.existsSync(srcDir)||!fs.existsSync(destDir))return false;let changed=false;for(const name of fs.readdirSync(srcDir)){if(!/\.(png|webp|jpe?g)$/i.test(name))continue;const dest=path.join(destDir,name);if(!fs.existsSync(dest))continue;const from=path.join(srcDir,name);const next=fs.readFileSync(from);const prev=fs.readFileSync(dest);if(Buffer.compare(next,prev)===0)continue;fs.writeFileSync(dest,next);changed=true}return changed}function themeAssetStamp(dir){if(!fs.existsSync(dir))return"";try{return fs.readdirSync(dir).filter(name=>THEME_ASSET_EXT.test(name)).map(name=>{const st=fs.statSync(path.join(dir,name));return`${name}:${st.size}:${Math.round(st.mtimeMs)}`}).join("|")}catch{return""}}function writeGeneratedThemeModule(filePath,payload){const header="/* eslint-disable */\n"+"// Generated from src/theme by playable-scripts. Do not edit.\n";const body=`export const THEME_DATA = ${JSON.stringify(payload.mergedData,null,2)} as Record<string, unknown>;\n`+`export const THEME_I18N_OVERRIDE = ${JSON.stringify(payload.i18nOverride,null,2)} as Record<string, Record<string, string>>;\n`+`export const THEME_ASSETS = ${JSON.stringify(payload.assetRecordByPath)} as Record<string, string>;\n`;const next=header+body;if(fs.existsSync(filePath)&&fs.readFileSync(filePath,"utf8")===next){return false}fs.mkdirSync(path.dirname(filePath),{recursive:true});fs.writeFileSync(filePath,next);return true}function syncGeneratedTheme(projectRoot,options={}){const themeConfig=options.themeConfig||{sourceDir:"src/theme",entryFile:"src/theme/index.ts"};const themeName=options.themeName||detectCurrentTheme(projectRoot,themeConfig);if(!themeName)return null;const payload=loadThemePayload(projectRoot,themeName,themeConfig);const rel=options.generatedRel||"assets/scripts/platform/theme.generated.ts";const copied=copyThemeAssetsToResources(projectRoot,themeName);const changed=writeGeneratedThemeModule(path.join(projectRoot,rel),payload);const levelsRel=options.levelsGeneratedRel||"assets/scripts/data/levels.generated.ts";const levelsChanged=writeGeneratedLevels(path.join(projectRoot,levelsRel),payload.levels||[]);return{themeName,payload,generatedRel:rel,changed:changed||copied||levelsChanged}}module.exports={detectCurrentTheme,loadThemePayload,writeGeneratedThemeModule,syncGeneratedTheme,collectDirAssets,themeAssetStamp};
@@ -0,0 +1 @@
1
+ "use strict";const fs=require("fs");const path=require("path");function loadThemeLevels(projectRoot,themeName,themeConfig={}){const sourceDir=themeConfig.sourceDir||"src/theme";const dir=path.join(projectRoot,sourceDir,themeName,"levels");if(!fs.existsSync(dir))return[];let ids=[];const indexPath=path.join(dir,"index.json");if(fs.existsSync(indexPath)){const index=JSON.parse(fs.readFileSync(indexPath,"utf8"));ids=Array.isArray(index.levels)?index.levels.map(String):[]}else{ids=fs.readdirSync(dir).filter(name=>name.endsWith(".json")&&name!=="index.json"&&name!=="palette.json").map(name=>name.replace(/\.json$/i,"")).sort()}return ids.map(id=>{const filePath=path.join(dir,`${id}.json`);if(!fs.existsSync(filePath)){throw new Error(`[levels] missing ${path.relative(projectRoot,filePath)}`)}const raw=JSON.parse(fs.readFileSync(filePath,"utf8"));if(!raw||typeof raw!=="object"||Array.isArray(raw)){throw new Error(`[levels] ${id}.json must be an object`)}return{...raw,id:raw.id||id}})}function writeGeneratedLevels(filePath,levels){const header="/* eslint-disable */\n"+"// Generated from src/theme/<theme>/levels. Do not edit.\n";const body=`export const THEME_LEVELS = ${JSON.stringify(levels,null,2)} as Record<string, unknown>[];\n`;const next=header+body;if(fs.existsSync(filePath)&&fs.readFileSync(filePath,"utf8")===next){return false}fs.mkdirSync(path.dirname(filePath),{recursive:true});fs.writeFileSync(filePath,next);return true}function detectThemeName(projectRoot,themeConfig={}){const entryFile=themeConfig.entryFile||"src/theme/index.ts";const entryPath=path.join(projectRoot,entryFile);if(!fs.existsSync(entryPath))return null;const match=fs.readFileSync(entryPath,"utf8").match(/from\s+['"]\.\/([^'"]+)['"]/);return match?match[1]:null}function syncGeneratedLevels(projectRoot,options={}){const themeConfig=options.themeConfig||{sourceDir:"src/theme",entryFile:"src/theme/index.ts"};const themeName=options.themeName||detectThemeName(projectRoot,themeConfig);if(!themeName)return null;const levels=loadThemeLevels(projectRoot,themeName,themeConfig);const rel=options.generatedRel||"assets/scripts/data/levels.generated.ts";const changed=writeGeneratedLevels(path.join(projectRoot,rel),levels);return{themeName,levels,generatedRel:rel,changed}}module.exports={loadThemeLevels,writeGeneratedLevels,syncGeneratedLevels};
@@ -1,250 +1,4 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const { spawnSync } = require('child_process');
6
-
7
- function exists(p) {
8
- try {
9
- return Boolean(p) && fs.existsSync(p);
10
- } catch {
11
- return false;
12
- }
13
- }
14
-
15
- function isWritableDir(dir) {
16
- try {
17
- fs.mkdirSync(dir, { recursive: true });
18
- const probe = path.join(dir, `.write-probe-${process.pid}`);
19
- fs.writeFileSync(probe, 'ok');
20
- fs.unlinkSync(probe);
21
- return true;
22
- } catch {
23
- return false;
24
- }
25
- }
26
-
27
- function which(bin) {
28
- if (!bin) return '';
29
- if (path.isAbsolute(bin) && exists(bin)) return bin;
30
- const result = spawnSync(process.platform === 'win32' ? 'where' : 'which', [bin], {
31
- encoding: 'utf8',
32
- });
33
- const line = (result.stdout || '').split(/\r?\n/).map((s) => s.trim()).find(Boolean);
34
- if (!line) return '';
35
- try {
36
- return fs.realpathSync(line);
37
- } catch {
38
- return line;
39
- }
40
- }
41
-
42
- function climbToCliRoot(start) {
43
- let dir = start;
44
- try {
45
- if (exists(start) && fs.statSync(start).isFile()) dir = path.dirname(start);
46
- } catch {
47
- return '';
48
- }
49
- for (let i = 0; i < 6; i++) {
50
- if (exists(path.join(dir, 'packages', 'engine', 'package.json'))
51
- || exists(path.join(dir, 'packages', 'engine', 'cc.config.json'))
52
- || exists(path.join(dir, 'packages', 'engine'))) {
53
- return dir;
54
- }
55
- const parent = path.dirname(dir);
56
- if (parent === dir) break;
57
- dir = parent;
58
- }
59
- return '';
60
- }
61
-
62
- function resolveCocosCliRoot(env = process.env) {
63
- if (env.COCOS_CLI_ROOT && exists(env.COCOS_CLI_ROOT)) {
64
- return path.resolve(env.COCOS_CLI_ROOT);
65
- }
66
- const bin = which(env.COCOS_CLI || 'cocos');
67
- if (bin) {
68
- const root = climbToCliRoot(bin);
69
- if (root) return root;
70
- }
71
- const fallbacks = [
72
- '/opt/cocos-cli',
73
- '/usr/local/cocos-cli',
74
- path.join(env.HOME || '', 'cocos-cli'),
75
- ];
76
- return fallbacks.find((p) => p && exists(path.join(p, 'packages', 'engine'))) || '';
77
- }
78
-
79
- function copyTree(src, dest) {
80
- if (exists(dest)) return;
81
- fs.mkdirSync(path.dirname(dest), { recursive: true });
82
- const al = spawnSync('cp', ['-al', src, dest], { encoding: 'utf8' });
83
- if (al.status === 0 && exists(dest)) return;
84
- const a = spawnSync('cp', ['-a', src, dest], { encoding: 'utf8' });
85
- if (a.status === 0 && exists(dest)) return;
86
- if (typeof fs.cpSync === 'function') {
87
- fs.cpSync(src, dest, { recursive: true });
88
- return;
89
- }
90
- throw new Error(`[cocos] failed to copy ${src} → ${dest}: ${(al.stderr || a.stderr || '').trim()}`);
91
- }
92
-
93
- function symlinkOrSkip(from, to) {
94
- if (exists(to)) return;
95
- fs.symlinkSync(from, to);
96
- }
97
-
98
- function overlayEngine(srcEngine, destEngine) {
99
- fs.mkdirSync(destEngine, { recursive: true });
100
- for (const name of fs.readdirSync(srcEngine)) {
101
- if (name === 'bin' || name === 'editor') continue;
102
- symlinkOrSkip(path.join(srcEngine, name), path.join(destEngine, name));
103
- }
104
- const srcBin = path.join(srcEngine, 'bin');
105
- const srcEditor = path.join(srcEngine, 'editor');
106
- if (exists(srcBin)) copyTree(srcBin, path.join(destEngine, 'bin'));
107
- else fs.mkdirSync(path.join(destEngine, 'bin'), { recursive: true });
108
- if (exists(srcEditor)) copyTree(srcEditor, path.join(destEngine, 'editor'));
109
- else fs.mkdirSync(path.join(destEngine, 'editor', 'library'), { recursive: true });
110
- fs.mkdirSync(path.join(destEngine, 'bin', 'temp'), { recursive: true });
111
- fs.mkdirSync(path.join(destEngine, 'editor', 'library'), { recursive: true });
112
- }
113
-
114
- function shimLinuxFbxConverter(cliRoot) {
115
- if (process.platform !== 'linux') return false;
116
- const binDir = path.join(cliRoot, 'node_modules', '@cocos', 'fbx-gltf-conv', 'bin');
117
- const linux = path.join(binDir, 'linux', 'FBX-glTF-conv');
118
- const darwin = path.join(binDir, 'darwin', 'FBX-glTF-conv');
119
- if (!exists(linux) || !exists(darwin)) return false;
120
- try {
121
- fs.unlinkSync(darwin);
122
- fs.copyFileSync(linux, darwin);
123
- fs.chmodSync(darwin, 0o755);
124
- return true;
125
- } catch {
126
- return false;
127
- }
128
- }
129
-
130
- function writeBinWrapper(destRoot) {
131
- const candidates = [
132
- path.join(destRoot, 'src', 'cli.js'),
133
- path.join(destRoot, 'dist', 'cli.js'),
134
- path.join(destRoot, 'cli.js'),
135
- ];
136
- const cliJs = candidates.find(exists);
137
- if (!cliJs) {
138
- throw new Error(`[cocos] overlay missing cli entry under ${destRoot}`);
139
- }
140
- const binDir = path.join(destRoot, 'bin');
141
- fs.mkdirSync(binDir, { recursive: true });
142
- const bin = path.join(binDir, 'cocos');
143
- fs.writeFileSync(bin, `#!/usr/bin/env node\nrequire(${JSON.stringify(cliJs)});\n`);
144
- fs.chmodSync(bin, 0o755);
145
- return bin;
146
- }
147
-
148
- function overlayNodeModules(srcNm, destNm) {
149
- fs.mkdirSync(destNm, { recursive: true });
150
- for (const name of fs.readdirSync(srcNm)) {
151
- if (name === '@cocos') continue;
152
- symlinkOrSkip(path.join(srcNm, name), path.join(destNm, name));
153
- }
154
- const srcCocos = path.join(srcNm, '@cocos');
155
- if (!exists(srcCocos)) return;
156
- const destCocos = path.join(destNm, '@cocos');
157
- fs.mkdirSync(destCocos, { recursive: true });
158
- for (const name of fs.readdirSync(srcCocos)) {
159
- if (name === 'fbx-gltf-conv') {
160
- copyTree(path.join(srcCocos, name), path.join(destCocos, name));
161
- continue;
162
- }
163
- symlinkOrSkip(path.join(srcCocos, name), path.join(destCocos, name));
164
- }
165
- }
166
-
167
- function overlayCocosCli(srcRoot, destRoot) {
168
- fs.mkdirSync(destRoot, { recursive: true });
169
- const skip = new Set(['src', 'dist', 'packages', 'bin', 'node_modules']);
170
- for (const name of fs.readdirSync(srcRoot)) {
171
- if (skip.has(name)) continue;
172
- symlinkOrSkip(path.join(srcRoot, name), path.join(destRoot, name));
173
- }
174
-
175
- if (exists(path.join(srcRoot, 'src'))) copyTree(path.join(srcRoot, 'src'), path.join(destRoot, 'src'));
176
- if (exists(path.join(srcRoot, 'dist'))) copyTree(path.join(srcRoot, 'dist'), path.join(destRoot, 'dist'));
177
- if (exists(path.join(srcRoot, 'node_modules'))) {
178
- overlayNodeModules(path.join(srcRoot, 'node_modules'), path.join(destRoot, 'node_modules'));
179
- }
180
-
181
- const srcPkgs = path.join(srcRoot, 'packages');
182
- const destPkgs = path.join(destRoot, 'packages');
183
- fs.mkdirSync(destPkgs, { recursive: true });
184
- if (exists(srcPkgs)) {
185
- for (const name of fs.readdirSync(srcPkgs)) {
186
- if (name === 'engine') continue;
187
- symlinkOrSkip(path.join(srcPkgs, name), path.join(destPkgs, name));
188
- }
189
- overlayEngine(path.join(srcPkgs, 'engine'), path.join(destPkgs, 'engine'));
190
- }
191
-
192
- const bin = writeBinWrapper(destRoot);
193
- shimLinuxFbxConverter(destRoot);
194
- return bin;
195
- }
196
-
197
- function engineNeedsOverlay(cliRoot) {
198
- const engine = path.join(cliRoot, 'packages', 'engine');
199
- if (!exists(engine)) return false;
200
- return !isWritableDir(path.join(engine, 'bin', 'temp'))
201
- || !isWritableDir(path.join(engine, 'editor', 'library'));
202
- }
203
-
204
- /**
1
+ "use strict";const fs=require("fs");const path=require("path");const{spawnSync}=require("child_process");function exists(p){try{return Boolean(p)&&fs.existsSync(p)}catch{return false}}function isWritableDir(dir){try{fs.mkdirSync(dir,{recursive:true});const probe=path.join(dir,`.write-probe-${process.pid}`);fs.writeFileSync(probe,"ok");fs.unlinkSync(probe);return true}catch{return false}}function which(bin){if(!bin)return"";if(path.isAbsolute(bin)&&exists(bin))return bin;const result=spawnSync(process.platform==="win32"?"where":"which",[bin],{encoding:"utf8"});const line=(result.stdout||"").split(/\r?\n/).map(s=>s.trim()).find(Boolean);if(!line)return"";try{return fs.realpathSync(line)}catch{return line}}function climbToCliRoot(start){let dir=start;try{if(exists(start)&&fs.statSync(start).isFile())dir=path.dirname(start)}catch{return""}for(let i=0;i<6;i++){if(exists(path.join(dir,"packages","engine","package.json"))||exists(path.join(dir,"packages","engine","cc.config.json"))||exists(path.join(dir,"packages","engine"))){return dir}const parent=path.dirname(dir);if(parent===dir)break;dir=parent}return""}function resolveCocosCliRoot(env=process.env){if(env.COCOS_CLI_ROOT&&exists(env.COCOS_CLI_ROOT)){return path.resolve(env.COCOS_CLI_ROOT)}const bin=which(env.COCOS_CLI||"cocos");if(bin){const root=climbToCliRoot(bin);if(root)return root}const fallbacks=["/opt/cocos-cli","/usr/local/cocos-cli",path.join(env.HOME||"","cocos-cli")];return fallbacks.find(p=>p&&exists(path.join(p,"packages","engine")))||""}function copyTree(src,dest){if(exists(dest))return;fs.mkdirSync(path.dirname(dest),{recursive:true});const al=spawnSync("cp",["-al",src,dest],{encoding:"utf8"});if(al.status===0&&exists(dest))return;const a=spawnSync("cp",["-a",src,dest],{encoding:"utf8"});if(a.status===0&&exists(dest))return;if(typeof fs.cpSync==="function"){fs.cpSync(src,dest,{recursive:true});return}throw new Error(`[cocos] failed to copy ${src} → ${dest}: ${(al.stderr||a.stderr||"").trim()}`)}function symlinkOrSkip(from,to){if(exists(to))return;fs.symlinkSync(from,to)}function overlayEngine(srcEngine,destEngine){fs.mkdirSync(destEngine,{recursive:true});for(const name of fs.readdirSync(srcEngine)){if(name==="bin"||name==="editor")continue;symlinkOrSkip(path.join(srcEngine,name),path.join(destEngine,name))}const srcBin=path.join(srcEngine,"bin");const srcEditor=path.join(srcEngine,"editor");if(exists(srcBin))copyTree(srcBin,path.join(destEngine,"bin"));else fs.mkdirSync(path.join(destEngine,"bin"),{recursive:true});if(exists(srcEditor))copyTree(srcEditor,path.join(destEngine,"editor"));else fs.mkdirSync(path.join(destEngine,"editor","library"),{recursive:true});fs.mkdirSync(path.join(destEngine,"bin","temp"),{recursive:true});fs.mkdirSync(path.join(destEngine,"editor","library"),{recursive:true})}function shimLinuxFbxConverter(cliRoot){if(process.platform!=="linux")return false;const binDir=path.join(cliRoot,"node_modules","@cocos","fbx-gltf-conv","bin");const linux=path.join(binDir,"linux","FBX-glTF-conv");const darwin=path.join(binDir,"darwin","FBX-glTF-conv");if(!exists(linux)||!exists(darwin))return false;try{fs.unlinkSync(darwin);fs.copyFileSync(linux,darwin);fs.chmodSync(darwin,493);return true}catch{return false}}function writeBinWrapper(destRoot){const candidates=[path.join(destRoot,"src","cli.js"),path.join(destRoot,"dist","cli.js"),path.join(destRoot,"cli.js")];const cliJs=candidates.find(exists);if(!cliJs){throw new Error(`[cocos] overlay missing cli entry under ${destRoot}`)}const binDir=path.join(destRoot,"bin");fs.mkdirSync(binDir,{recursive:true});const bin=path.join(binDir,"cocos");fs.writeFileSync(bin,`#!/usr/bin/env node\nrequire(${JSON.stringify(cliJs)});\n`);fs.chmodSync(bin,493);return bin}function overlayNodeModules(srcNm,destNm){fs.mkdirSync(destNm,{recursive:true});for(const name of fs.readdirSync(srcNm)){if(name==="@cocos")continue;symlinkOrSkip(path.join(srcNm,name),path.join(destNm,name))}const srcCocos=path.join(srcNm,"@cocos");if(!exists(srcCocos))return;const destCocos=path.join(destNm,"@cocos");fs.mkdirSync(destCocos,{recursive:true});for(const name of fs.readdirSync(srcCocos)){if(name==="fbx-gltf-conv"){copyTree(path.join(srcCocos,name),path.join(destCocos,name));continue}symlinkOrSkip(path.join(srcCocos,name),path.join(destCocos,name))}}function overlayCocosCli(srcRoot,destRoot){fs.mkdirSync(destRoot,{recursive:true});const skip=new Set(["src","dist","packages","bin","node_modules"]);for(const name of fs.readdirSync(srcRoot)){if(skip.has(name))continue;symlinkOrSkip(path.join(srcRoot,name),path.join(destRoot,name))}if(exists(path.join(srcRoot,"src")))copyTree(path.join(srcRoot,"src"),path.join(destRoot,"src"));if(exists(path.join(srcRoot,"dist")))copyTree(path.join(srcRoot,"dist"),path.join(destRoot,"dist"));if(exists(path.join(srcRoot,"node_modules"))){overlayNodeModules(path.join(srcRoot,"node_modules"),path.join(destRoot,"node_modules"))}const srcPkgs=path.join(srcRoot,"packages");const destPkgs=path.join(destRoot,"packages");fs.mkdirSync(destPkgs,{recursive:true});if(exists(srcPkgs)){for(const name of fs.readdirSync(srcPkgs)){if(name==="engine")continue;symlinkOrSkip(path.join(srcPkgs,name),path.join(destPkgs,name))}overlayEngine(path.join(srcPkgs,"engine"),path.join(destPkgs,"engine"))}const bin=writeBinWrapper(destRoot);shimLinuxFbxConverter(destRoot);return bin}function engineNeedsOverlay(cliRoot){const engine=path.join(cliRoot,"packages","engine");if(!exists(engine))return false;return!isWritableDir(path.join(engine,"bin","temp"))||!isWritableDir(path.join(engine,"editor","library"))}/**
205
2
  * Linux 沙箱里 /opt/cocos-cli/packages/engine 只读时,把 CLI 映到项目 temp,
206
3
  * 让 GlobalPaths.enginePath(join(__dirname, '../packages/engine'))落在可写目录。
207
- */
208
- function prepareWritableCocosCli(projectRoot, env = process.env) {
209
- const srcRoot = resolveCocosCliRoot(env);
210
- if (!srcRoot) {
211
- return { overlaid: false, cliRoot: '', env: {} };
212
- }
213
- if (!engineNeedsOverlay(srcRoot)) {
214
- return { overlaid: false, cliRoot: srcRoot, env: { COCOS_ENGINE: path.join(srcRoot, 'packages', 'engine') } };
215
- }
216
-
217
- const destRoot = path.join(projectRoot, 'temp', 'cocos-cli');
218
- const destEngine = path.join(destRoot, 'packages', 'engine');
219
- const destBin = path.join(destRoot, 'bin', 'cocos');
220
- if (!(exists(destBin) && exists(destEngine) && isWritableDir(path.join(destEngine, 'bin', 'temp')))) {
221
- if (exists(destRoot)) fs.rmSync(destRoot, { recursive: true, force: true });
222
- overlayCocosCli(srcRoot, destRoot);
223
- } else {
224
- shimLinuxFbxConverter(destRoot);
225
- }
226
-
227
- console.log(`[cocos] engine not writable (${srcRoot}); overlay ${path.relative(projectRoot, destRoot)}`);
228
- return {
229
- overlaid: true,
230
- cliRoot: destRoot,
231
- env: {
232
- COCOS_CLI: destBin,
233
- COCOS_CLI_ROOT: destRoot,
234
- COCOS_ENGINE: destEngine,
235
- },
236
- };
237
- }
238
-
239
- module.exports = {
240
- exists,
241
- isWritableDir,
242
- which,
243
- climbToCliRoot,
244
- resolveCocosCliRoot,
245
- overlayCocosCli,
246
- overlayEngine,
247
- shimLinuxFbxConverter,
248
- engineNeedsOverlay,
249
- prepareWritableCocosCli,
250
- };
4
+ */function prepareWritableCocosCli(projectRoot,env=process.env){const srcRoot=resolveCocosCliRoot(env);if(!srcRoot){return{overlaid:false,cliRoot:"",env:{}}}if(!engineNeedsOverlay(srcRoot)){return{overlaid:false,cliRoot:srcRoot,env:{COCOS_ENGINE:path.join(srcRoot,"packages","engine")}}}const destRoot=path.join(projectRoot,"temp","cocos-cli");const destEngine=path.join(destRoot,"packages","engine");const destBin=path.join(destRoot,"bin","cocos");if(!(exists(destBin)&&exists(destEngine)&&isWritableDir(path.join(destEngine,"bin","temp")))){if(exists(destRoot))fs.rmSync(destRoot,{recursive:true,force:true});overlayCocosCli(srcRoot,destRoot)}else{shimLinuxFbxConverter(destRoot)}console.log(`[cocos] engine not writable (${srcRoot}); overlay ${path.relative(projectRoot,destRoot)}`);return{overlaid:true,cliRoot:destRoot,env:{COCOS_CLI:destBin,COCOS_CLI_ROOT:destRoot,COCOS_ENGINE:destEngine}}}module.exports={exists,isWritableDir,which,climbToCliRoot,resolveCocosCliRoot,overlayCocosCli,overlayEngine,shimLinuxFbxConverter,engineNeedsOverlay,prepareWritableCocosCli};
@@ -1,112 +1 @@
1
- 'use strict';
2
-
3
- const { describe, it, before, after, beforeEach } = require('node:test');
4
- const assert = require('node:assert/strict');
5
- const fs = require('fs');
6
- const os = require('os');
7
- const path = require('path');
8
- const {
9
- overlayCocosCli,
10
- engineNeedsOverlay,
11
- prepareWritableCocosCli,
12
- shimLinuxFbxConverter,
13
- } = require('./writable-cli.js');
14
-
15
- function writeFile(file, body) {
16
- fs.mkdirSync(path.dirname(file), { recursive: true });
17
- fs.writeFileSync(file, body);
18
- }
19
-
20
- function makeCliTree(root) {
21
- writeFile(path.join(root, 'src', 'cli.js'), 'console.log("cli");\n');
22
- writeFile(
23
- path.join(root, 'src', 'global.js'),
24
- 'const { join } = require("path");\nmodule.exports = { GlobalPaths: { enginePath: join(__dirname, "..", "packages", "engine") } };\n',
25
- );
26
- writeFile(path.join(root, 'packages', 'engine', 'cc.config.json'), '{}\n');
27
- writeFile(path.join(root, 'packages', 'engine', 'bin', 'keep.txt'), 'cache\n');
28
- writeFile(path.join(root, 'packages', 'engine', 'editor', 'library', '.internal'), 'meta\n');
29
- writeFile(path.join(root, 'packages', 'other', 'pkg.json'), '{}\n');
30
- writeFile(path.join(root, 'node_modules', '@cocos', 'fbx-gltf-conv', 'bin', 'linux', 'FBX-glTF-conv'), 'linux-bin');
31
- writeFile(path.join(root, 'node_modules', '@cocos', 'fbx-gltf-conv', 'bin', 'darwin', 'FBX-glTF-conv'), 'darwin-bin');
32
- writeFile(path.join(root, 'bin', 'cocos'), '#!/usr/bin/env node\nrequire("../src/cli.js");\n');
33
- fs.chmodSync(path.join(root, 'bin', 'cocos'), 0o755);
34
- }
35
-
36
- describe('writable cocos-cli overlay', () => {
37
- let root;
38
-
39
- beforeEach(() => {
40
- if (root && fs.existsSync(root)) fs.rmSync(root, { recursive: true, force: true });
41
- root = fs.mkdtempSync(path.join(os.tmpdir(), 'cocos-cli-overlay-'));
42
- });
43
-
44
- after(() => {
45
- if (root && fs.existsSync(root)) fs.rmSync(root, { recursive: true, force: true });
46
- });
47
-
48
- it('reports overlay when engine bin/temp is not writable', () => {
49
- const cli = path.join(root, 'opt-cli');
50
- makeCliTree(cli);
51
- fs.chmodSync(path.join(cli, 'packages', 'engine', 'bin'), 0o555);
52
- assert.equal(engineNeedsOverlay(cli), true);
53
- fs.chmodSync(path.join(cli, 'packages', 'engine', 'bin'), 0o755);
54
- });
55
-
56
- it('maps GlobalPaths.enginePath onto a writable overlay', () => {
57
- const cli = path.join(root, 'opt-cli');
58
- makeCliTree(cli);
59
- const dest = path.join(root, 'overlay');
60
- const bin = overlayCocosCli(cli, dest);
61
- assert.equal(fs.existsSync(bin), true);
62
-
63
- const enginePath = path.join(dest, 'packages', 'engine');
64
- fs.mkdirSync(path.join(enginePath, 'bin', 'temp'), { recursive: true });
65
- fs.writeFileSync(path.join(enginePath, 'bin', 'temp', 'ok.txt'), '1');
66
- assert.equal(fs.readFileSync(path.join(enginePath, 'bin', 'keep.txt'), 'utf8'), 'cache\n');
67
-
68
- const globalJs = path.join(dest, 'src', 'global.js');
69
- delete require.cache[require.resolve(globalJs)];
70
- const resolved = require(globalJs).GlobalPaths.enginePath;
71
- assert.equal(resolved, enginePath);
72
-
73
- const srcDarwin = path.join(cli, 'node_modules', '@cocos', 'fbx-gltf-conv', 'bin', 'darwin', 'FBX-glTF-conv');
74
- const destDarwin = path.join(dest, 'node_modules', '@cocos', 'fbx-gltf-conv', 'bin', 'darwin', 'FBX-glTF-conv');
75
- assert.equal(fs.readFileSync(srcDarwin, 'utf8'), 'darwin-bin');
76
- if (process.platform === 'linux') {
77
- assert.equal(fs.readFileSync(destDarwin, 'utf8'), 'linux-bin');
78
- }
79
- });
80
-
81
- it('replaces darwin FBX converter with the linux binary', () => {
82
- const cli = path.join(root, 'opt-cli');
83
- makeCliTree(cli);
84
- if (process.platform !== 'linux') {
85
- const original = process.platform;
86
- Object.defineProperty(process, 'platform', { value: 'linux' });
87
- try {
88
- assert.equal(shimLinuxFbxConverter(cli), true);
89
- } finally {
90
- Object.defineProperty(process, 'platform', { value: original });
91
- }
92
- } else {
93
- assert.equal(shimLinuxFbxConverter(cli), true);
94
- }
95
- const darwin = path.join(cli, 'node_modules', '@cocos', 'fbx-gltf-conv', 'bin', 'darwin', 'FBX-glTF-conv');
96
- assert.equal(fs.readFileSync(darwin, 'utf8'), 'linux-bin');
97
- });
98
-
99
- it('prepareWritableCocosCli overlays a locked engine into project temp', () => {
100
- const cli = path.join(root, 'opt-cli');
101
- makeCliTree(cli);
102
- fs.chmodSync(path.join(cli, 'packages', 'engine', 'bin'), 0o555);
103
- const project = path.join(root, 'game');
104
- fs.mkdirSync(project);
105
- const result = prepareWritableCocosCli(project, { COCOS_CLI_ROOT: cli });
106
- assert.equal(result.overlaid, true);
107
- assert.equal(fs.existsSync(result.env.COCOS_CLI), true);
108
- fs.mkdirSync(path.join(result.env.COCOS_ENGINE, 'bin', 'temp'), { recursive: true });
109
- fs.writeFileSync(path.join(result.env.COCOS_ENGINE, 'bin', 'temp', 'ok.txt'), '1');
110
- fs.chmodSync(path.join(cli, 'packages', 'engine', 'bin'), 0o755);
111
- });
112
- });
1
+ "use strict";const{describe,it,before,after,beforeEach}=require("node:test");const assert=require("node:assert/strict");const fs=require("fs");const os=require("os");const path=require("path");const{overlayCocosCli,engineNeedsOverlay,prepareWritableCocosCli,shimLinuxFbxConverter}=require("./writable-cli.js");function writeFile(file,body){fs.mkdirSync(path.dirname(file),{recursive:true});fs.writeFileSync(file,body)}function makeCliTree(root){writeFile(path.join(root,"src","cli.js"),"console.log(\"cli\");\n");writeFile(path.join(root,"src","global.js"),"const { join } = require(\"path\");\nmodule.exports = { GlobalPaths: { enginePath: join(__dirname, \"..\", \"packages\", \"engine\") } };\n");writeFile(path.join(root,"packages","engine","cc.config.json"),"{}\n");writeFile(path.join(root,"packages","engine","bin","keep.txt"),"cache\n");writeFile(path.join(root,"packages","engine","editor","library",".internal"),"meta\n");writeFile(path.join(root,"packages","other","pkg.json"),"{}\n");writeFile(path.join(root,"node_modules","@cocos","fbx-gltf-conv","bin","linux","FBX-glTF-conv"),"linux-bin");writeFile(path.join(root,"node_modules","@cocos","fbx-gltf-conv","bin","darwin","FBX-glTF-conv"),"darwin-bin");writeFile(path.join(root,"bin","cocos"),"#!/usr/bin/env node\nrequire(\"../src/cli.js\");\n");fs.chmodSync(path.join(root,"bin","cocos"),493)}describe("writable cocos-cli overlay",()=>{let root;beforeEach(()=>{if(root&&fs.existsSync(root))fs.rmSync(root,{recursive:true,force:true});root=fs.mkdtempSync(path.join(os.tmpdir(),"cocos-cli-overlay-"))});after(()=>{if(root&&fs.existsSync(root))fs.rmSync(root,{recursive:true,force:true})});it("reports overlay when engine bin/temp is not writable",()=>{const cli=path.join(root,"opt-cli");makeCliTree(cli);fs.chmodSync(path.join(cli,"packages","engine","bin"),365);assert.equal(engineNeedsOverlay(cli),true);fs.chmodSync(path.join(cli,"packages","engine","bin"),493)});it("maps GlobalPaths.enginePath onto a writable overlay",()=>{const cli=path.join(root,"opt-cli");makeCliTree(cli);const dest=path.join(root,"overlay");const bin=overlayCocosCli(cli,dest);assert.equal(fs.existsSync(bin),true);const enginePath=path.join(dest,"packages","engine");fs.mkdirSync(path.join(enginePath,"bin","temp"),{recursive:true});fs.writeFileSync(path.join(enginePath,"bin","temp","ok.txt"),"1");assert.equal(fs.readFileSync(path.join(enginePath,"bin","keep.txt"),"utf8"),"cache\n");const globalJs=path.join(dest,"src","global.js");delete require.cache[require.resolve(globalJs)];const resolved=require(globalJs).GlobalPaths.enginePath;assert.equal(resolved,enginePath);const srcDarwin=path.join(cli,"node_modules","@cocos","fbx-gltf-conv","bin","darwin","FBX-glTF-conv");const destDarwin=path.join(dest,"node_modules","@cocos","fbx-gltf-conv","bin","darwin","FBX-glTF-conv");assert.equal(fs.readFileSync(srcDarwin,"utf8"),"darwin-bin");if(process.platform==="linux"){assert.equal(fs.readFileSync(destDarwin,"utf8"),"linux-bin")}});it("replaces darwin FBX converter with the linux binary",()=>{const cli=path.join(root,"opt-cli");makeCliTree(cli);if(process.platform!=="linux"){const original=process.platform;Object.defineProperty(process,"platform",{value:"linux"});try{assert.equal(shimLinuxFbxConverter(cli),true)}finally{Object.defineProperty(process,"platform",{value:original})}}else{assert.equal(shimLinuxFbxConverter(cli),true)}const darwin=path.join(cli,"node_modules","@cocos","fbx-gltf-conv","bin","darwin","FBX-glTF-conv");assert.equal(fs.readFileSync(darwin,"utf8"),"linux-bin")});it("prepareWritableCocosCli overlays a locked engine into project temp",()=>{const cli=path.join(root,"opt-cli");makeCliTree(cli);fs.chmodSync(path.join(cli,"packages","engine","bin"),365);const project=path.join(root,"game");fs.mkdirSync(project);const result=prepareWritableCocosCli(project,{COCOS_CLI_ROOT:cli});assert.equal(result.overlaid,true);assert.equal(fs.existsSync(result.env.COCOS_CLI),true);fs.mkdirSync(path.join(result.env.COCOS_ENGINE,"bin","temp"),{recursive:true});fs.writeFileSync(path.join(result.env.COCOS_ENGINE,"bin","temp","ok.txt"),"1");fs.chmodSync(path.join(cli,"packages","engine","bin"),493)})});
@@ -1,20 +1 @@
1
- 'use strict';
2
-
3
- const { allowedExtensions, webpackCommonConfig } = require('./webpack.common.js');
4
- const { runBuild, makeWebpackBuildConfig } = require('./webpack.build.js');
5
- const { runDev, makeWebpackDevConfig } = require('./webpack.dev.js');
6
- const { runViteDev, makeViteDevConfig, json5Plugin, assetInlinePlugin, fontFamilyPlugin } = require('./vite.dev.js');
7
-
8
- module.exports = {
9
- allowedExtensions,
10
- webpackCommonConfig,
11
- runBuild,
12
- makeWebpackBuildConfig,
13
- runDev,
14
- makeWebpackDevConfig,
15
- runViteDev,
16
- makeViteDevConfig,
17
- json5Plugin,
18
- assetInlinePlugin,
19
- fontFamilyPlugin,
20
- };
1
+ "use strict";const{allowedExtensions,webpackCommonConfig}=require("./webpack.common.js");const{runBuild,makeWebpackBuildConfig}=require("./webpack.build.js");const{runDev,makeWebpackDevConfig}=require("./webpack.dev.js");const{runViteDev,makeViteDevConfig,json5Plugin,assetInlinePlugin,fontFamilyPlugin}=require("./vite.dev.js");module.exports={allowedExtensions,webpackCommonConfig,runBuild,makeWebpackBuildConfig,runDev,makeWebpackDevConfig,runViteDev,makeViteDevConfig,json5Plugin,assetInlinePlugin,fontFamilyPlugin};