@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.
- package/cli/bin/playable-scripts.js +6 -53
- package/cli/commands/build.js +32 -241
- package/cli/commands/builds.js +2 -13
- package/cli/commands/dev.js +1 -22
- package/cli/commands/pack.js +40 -320
- package/cli/commands/vite-dev.js +1 -7
- package/core/artifacts/emit-fonts.test.js +1 -0
- package/core/artifacts/emit.js +4 -98
- package/core/artifacts/fflate.js +1 -31
- package/core/artifacts/zip.js +1 -39
- package/core/batch-build.js +147 -1261
- package/core/constants.js +2 -20
- package/core/cos-uploader.js +23 -289
- package/core/engines/cocos/adapter-vfs-path.test.js +1 -0
- package/core/engines/cocos/clean-cache.js +5 -179
- package/core/engines/cocos/clean-cache.test.js +1 -75
- package/core/engines/cocos/cli-build.js +2 -116
- package/core/engines/cocos/cli.js +11 -38
- package/core/engines/cocos/constants.js +1 -21
- package/core/engines/cocos/creator-cli.js +1 -108
- package/core/engines/cocos/dev-server.js +4 -412
- package/core/engines/cocos/fs.js +1 -105
- package/core/engines/cocos/injects/3x/init.js +19 -438
- package/core/engines/cocos/injects/3x/main.js +2 -52
- package/core/engines/cocos/injects/libs/pako.js +1 -2
- package/core/engines/cocos/mcp-refresh.js +2 -76
- package/core/engines/cocos/merge-inline.test.js +1 -0
- package/core/engines/cocos/merge.js +9 -148
- package/core/engines/cocos/omit-playable-weight.js +10 -0
- package/core/engines/cocos/omit-playable-weight.test.js +2 -0
- package/core/engines/cocos/pack.js +1 -187
- package/core/engines/cocos/preview-cache.js +2 -48
- package/core/engines/cocos/preview-cache.test.js +1 -32
- package/core/engines/cocos/preview-concat.js +6 -189
- package/core/engines/cocos/preview-concat.test.js +1 -73
- package/core/engines/cocos/preview-json-hook.js +1 -200
- package/core/engines/cocos/preview-json-pack.js +3 -376
- package/core/engines/cocos/preview-json-pack.test.js +2 -226
- package/core/engines/cocos/preview-proxy.js +1 -132
- package/core/engines/cocos/rebundle-preview.js +2 -54
- package/core/engines/cocos/strip-social-share.js +3 -115
- package/core/engines/cocos/theme-assets.js +2 -161
- package/core/engines/cocos/theme-levels.js +1 -0
- package/core/engines/cocos/writable-cli.js +2 -248
- package/core/engines/cocos/writable-cli.test.js +1 -112
- package/core/engines/web/index.js +1 -20
- package/core/engines/web/vite.dev.js +17 -322
- package/core/engines/web/webpack.build.js +37 -522
- package/core/engines/web/webpack.common.js +11 -177
- package/core/engines/web/webpack.dev.js +6 -88
- package/core/index.js +7 -101
- package/core/injectors/apply.js +1 -94
- package/core/injectors/check-channel-matrix.js +2 -130
- package/core/injectors/spec.js +10 -217
- package/core/loaders/gltf-loader.js +28 -74
- package/core/options.js +16 -353
- package/core/plugins/AdikteevInjectorPlugin.js +4 -26
- package/core/plugins/BigoAdsInjectorPlugin.js +4 -21
- package/core/plugins/DAPIInjectorPlugin.js +2 -25
- package/core/plugins/DebuggerInjectionPlugin.js +3 -31
- package/core/plugins/ExitAPIInjectorPlugin.js +3 -56
- package/core/plugins/FflateCompressionPlugin.js +160 -307
- package/core/plugins/FflateCompressionPlugin.test.js +13 -0
- package/core/plugins/FontInjectionWebpackPlugin.js +4 -46
- package/core/plugins/LiftoffInjectorPlugin.js +4 -26
- package/core/plugins/MRAIDInjectorPlugin.js +2 -24
- package/core/plugins/MintegralInjectorPlugin.js +2 -25
- package/core/plugins/PangleInjectorPlugin.js +2 -24
- package/core/plugins/PlaycraftOrientationInjectorPlugin.js +3 -29
- package/core/plugins/SnapchatInjectorPlugin.js +4 -26
- package/core/plugins/SocialMediaInjectorPlugin.js +7 -119
- package/core/plugins/TikTokInjectorPlugin.js +2 -24
- package/core/plugins/UnityInjectorPlugin.js +8 -37
- package/core/plugins/ZipPlugin.js +20 -154
- package/core/plugins/socialMediaVitePlugin.js +2 -108
- package/core/utils/buildDefines.js +2 -30
- package/core/utils/buildTemplateString.js +5 -39
- package/core/utils/date.js +1 -16
- package/core/utils/fontRegistry.js +13 -81
- package/core/utils/fontResolver.js +37 -335
- package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +8 -42
- package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +3 -30
- package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +7 -43
- package/core/utils/getPlaycraftOrientationSuffix.js +2 -9
- package/core/utils/injectSDKPlugins.js +13 -68
- package/core/utils/logOptions.js +5 -46
- package/core/utils/mergeOptions.js +2 -21
- package/core/utils/obfuscateLevels.js +14 -0
- package/core/utils/obfuscateLevels.test.js +10 -0
- package/core/utils/parseArgvOptions.js +5 -125
- package/core/utils/resolveChannelFold2Zip.js +3 -15
- package/core/utils/validateThemeData.js +26 -227
- package/core/validators/pre-build-checker.js +25 -287
- package/core/validators/tracking-validator.js +40 -441
- package/core/validators/tracking-validator.test.js +2 -84
- package/core/vite.dev.js +1 -1
- package/core/webpack.build.js +1 -1
- package/core/webpack.common.js +1 -1
- package/core/webpack.dev.js +1 -1
- package/package.json +2 -3
- package/README.md +0 -262
|
@@ -1,162 +1,3 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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};
|