import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const buildRoot = path.join(projectRoot, 'build', 'wechatgame'); const ccDepsWithoutPhysics = [ 'plugin:cocos/2d.js', 'plugin:cocos/sorting.js', 'plugin:cocos/3d.js', './affine-transform.js', './animation.js', 'plugin:cocos/audio.js', 'plugin:cocos/base.js', './custom-pipeline.js', 'plugin:cocos/dragon-bones.js', 'plugin:cocos/gfx-webgl.js', 'plugin:cocos/graphics.js', 'plugin:cocos/intersection-2d.js', 'plugin:cocos/light-probe.js', 'plugin:cocos/mask.js', 'plugin:cocos/particle.js', 'plugin:cocos/particle-2d.js', 'plugin:cocos/primitive.js', 'plugin:cocos/profiler.js', 'plugin:cocos/rich-text.js', './skeletal-animation.js', 'plugin:cocos/spine.js', 'plugin:cocos/terrain.js', 'plugin:cocos/tiled-map.js', 'plugin:cocos/tween.js', 'plugin:cocos/ui.js', 'plugin:cocos/ui-skew.js', 'plugin:cocos/video.js', 'plugin:cocos/webview.js', ]; function readJson(file) { return JSON.parse(fs.readFileSync(file, 'utf8')); } function writeJson(file, data) { fs.writeFileSync(file, JSON.stringify(data)); } function safeBuildPath(relPath) { const target = path.resolve(buildRoot, relPath); if (target !== buildRoot && !target.startsWith(buildRoot + path.sep)) { throw new Error(`Refusing to touch path outside build root: ${target}`); } return target; } function removeBuildPath(relPath) { fs.rmSync(safeBuildPath(relPath), { recursive: true, force: true }); } function rewriteCcWithoutPhysics() { const ccPath = safeBuildPath(path.join('cocos-js', 'cc.js')); if (!fs.existsSync(ccPath)) return; const body = `System.register(${JSON.stringify(ccDepsWithoutPhysics)}, function (_export, _context) { "use strict"; function exportAll(module) { var exportObj = {}; for (var key in module) { if (key !== "default" && key !== "__esModule") exportObj[key] = module[key]; } _export(exportObj); } return { setters: [${ccDepsWithoutPhysics.map(() => 'exportAll').join(', ')}], execute: function () {} }; }); `; fs.writeFileSync(ccPath, body); } function removeFirstScreen() { const gamePath = safeBuildPath('game.js'); if (!fs.existsSync(gamePath)) return; let source = fs.readFileSync(gamePath, 'utf8'); source = source.replace("const firstScreen = require('./first-screen');\r\n\r\n", ''); source = source.replace("const firstScreen = require('./first-screen');\n\n", ''); source = source.replace(/firstScreen\.start\('default', 'default', 'false'\)\.then\(\(\) => \{\s*return System\.import\('\.\/application\.js'\);\s*\}\)\.then\(\(module\) => \{\s*return firstScreen\.setProgress\(0\.2\)\.then\(\(\) => Promise\.resolve\(module\)\);\s*\}\)/s, "System.import('./application.js')"); source = source.replace(/\.then\(\(application\) => \{\s*return firstScreen\.setProgress\(0\.4\)\.then\(\(\) => Promise\.resolve\(application\)\);\s*\}\)/s, ''); source = source.replace(/return firstScreen\.setProgress\(0\.6\)\.then\(\(\) => Promise\.resolve\(module\)\);/g, 'return module;'); source = source.replace(/return firstScreen\.end\(\)\.then\(\(\) => application\.start\(\)\);/g, 'return application.start();'); fs.writeFileSync(gamePath, source); removeBuildPath('first-screen.js'); removeBuildPath('logo.png'); removeBuildPath('slogan.png'); } function patchFullscreenCanvas() { const gamePath = safeBuildPath('game.js'); if (!fs.existsSync(gamePath)) return; let source = fs.readFileSync(gamePath, 'utf8'); source = source.replace(/\/\/ Adapt for IOS, swap if opposite[\s\S]*?const importMap = require\("src\/import-map\.js"\)\.default;/, 'const importMap = require("src/import-map.js").default;'); source = source.replace(/\nfunction __steelGetLandscapeFrame \(\) \{[\s\S]*?\nwx\.onWindowResize && wx\.onWindowResize\(__steelApplyFullscreenCanvas\);\n\n?/, '\n'); const shim = ` function __steelGetLandscapeFrame () { const info = (wx.getWindowInfo && wx.getWindowInfo()) || wx.getSystemInfoSync(); const add = function (list, width, height) { if (!width || !height || width <= 0 || height <= 0) return; list.push({ width: Math.max(width, height), height: Math.min(width, height) }); }; const candidates = []; add(candidates, info.windowWidth, info.windowHeight); add(candidates, info.screenWidth, info.screenHeight); add(candidates, canvas.width, canvas.height); add(candidates, canvas.clientWidth, canvas.clientHeight); const frame = candidates.reduce(function (best, item) { return item.width / Math.max(1, item.height) > best.width / Math.max(1, best.height) ? item : best; }, { width: 1624, height: 750 }); const dpr = info.pixelRatio || info.devicePixelRatio || window.devicePixelRatio || 1; return { width: frame.width, height: frame.height, dpr }; } function __steelSetWindowValue (key, value) { try { const descriptor = Object.getOwnPropertyDescriptor(window, key); if (!descriptor || descriptor.configurable) { Object.defineProperty(window, key, { value, configurable: true, writable: true }); } else if (descriptor.writable) { window[key] = value; } } catch (error) {} } function __steelApplyFullscreenCanvas () { if (!canvas) return; const frame = __steelGetLandscapeFrame(); const pixelWidth = Math.max(1, Math.round(frame.width * frame.dpr)); const pixelHeight = Math.max(1, Math.round(frame.height * frame.dpr)); __steelSetWindowValue('innerWidth', frame.width); __steelSetWindowValue('innerHeight', frame.height); __steelSetWindowValue('screen', { width: frame.width, height: frame.height, availWidth: frame.width, availHeight: frame.height, availLeft: 0, availTop: 0 }); canvas.width = pixelWidth; canvas.height = pixelHeight; canvas.style = canvas.style || {}; canvas.style.top = '0px'; canvas.style.left = '0px'; canvas.style.width = frame.width + 'px'; canvas.style.height = frame.height + 'px'; canvas.getBoundingClientRect = function () { return { top: 0, left: 0, width: frame.width, height: frame.height }; }; console.log('[SteelAssaultGame] wx fullscreen canvas ' + frame.width + 'x' + frame.height + ' dpr ' + frame.dpr + ' backing ' + pixelWidth + 'x' + pixelHeight); } __steelApplyFullscreenCanvas(); wx.onWindowResize && wx.onWindowResize(__steelApplyFullscreenCanvas); `; source = source.replace(/require\('\.\/web-adapter'\);\r?\n/, `require('./web-adapter');\n${shim}\n`); fs.writeFileSync(gamePath, source); } function disablePhysicsSettingOnly() { const settingsPath = safeBuildPath(path.join('src', 'settings.json')); if (!fs.existsSync(settingsPath)) return; const settings = readJson(settingsPath); settings.physics = { physicsEngine: '' }; if (settings.splashScreen) { settings.splashScreen.totalTime = 0; if (settings.splashScreen.logo) settings.splashScreen.logo.image = ''; } writeJson(settingsPath, settings); } function removeDefaultPhysicsMaterialAsset() { const settingsPath = safeBuildPath(path.join('src', 'settings.json')); const internalConfigPath = safeBuildPath(path.join('assets', 'internal', 'config.json')); const internalPackPath = safeBuildPath(path.join('assets', 'internal', 'import', '07', '07d3aae9f.json')); if (!fs.existsSync(settingsPath) || !fs.existsSync(internalConfigPath) || !fs.existsSync(internalPackPath)) return; const settings = readJson(settingsPath); if (settings.engine?.builtinAssets) { settings.engine.builtinAssets = settings.engine.builtinAssets.filter((uuid) => uuid !== 'd1346436-ac96-4271-b863-1f4fdead95b0'); writeJson(settingsPath, settings); } const internalConfig = readJson(internalConfigPath); const internalPack = readJson(internalPackPath); const packIds = internalConfig.packs?.['07d3aae9f'] ?? []; const physicsIndex = packIds.indexOf(22); if (physicsIndex >= 0 && Array.isArray(internalPack[5])) { internalPack[5].splice(physicsIndex, 1); } const physicsTypeIndex = Array.isArray(internalPack[3]) ? internalPack[3].findIndex((entry) => entry?.[0] === 'cc.PhysicsMaterial') : -1; if (physicsTypeIndex >= 0) { internalPack[3].splice(physicsTypeIndex, 1); if (Array.isArray(internalPack[4])) { internalPack[4] = internalPack[4].filter((entry) => entry?.[0] !== physicsTypeIndex); } } if (internalConfig.packs?.['07d3aae9f']) { internalConfig.packs['07d3aae9f'] = packIds.filter((id) => id !== 22); } delete internalConfig.paths?.['22']; if (internalConfig.types) { internalConfig.types = internalConfig.types.filter((type) => type !== 'cc.PhysicsMaterial'); } writeJson(internalConfigPath, internalConfig); writeJson(internalPackPath, internalPack); } function removePhysicsEngineFiles() { [ 'cocos-js/physics-framework.js', 'cocos-js/physics-ammo.js', 'cocos-js/physics-2d-framework.js', 'cocos-js/physics-2d-framework-CVNkMl3f.js', 'cocos-js/physics-2d-box2d.js', 'cocos-js/index-D5MM0hSc.js', 'cocos-js/collision-matrix-C-tmFF1b.js', 'cocos-js/array-collision-matrix-DU5_JQW3.js', 'cocos-js/tuple-dictionary-By7Ih6PO.js', 'cocos-js/bullet.release.asm-CtSjh1p_.js', 'cocos-js/bullet.release.wasm-CV1y-N0i.js', 'cocos-js/bullet.release.wasm-mT4aJDLq.js', 'cocos-js/assets/bullet.release.wasm-BIzkn7bF.wasm', ].forEach(removeBuildPath); } function removeUnusedDefaultScene() { const mainConfigPath = safeBuildPath(path.join('assets', 'main', 'config.json')); if (fs.existsSync(mainConfigPath)) { const mainConfig = readJson(mainConfigPath); for (const id of ['3', '4', '19']) delete mainConfig.paths?.[id]; if (mainConfig.scenes) delete mainConfig.scenes['db://assets/scenes/scene.scene']; delete mainConfig.packs?.['0699aaa4c']; delete mainConfig.packs?.['06add430f']; mainConfig.redirect = []; mainConfig.types = ['cc.SceneAsset']; writeJson(mainConfigPath, mainConfig); } [ 'assets/main/import/e6/e612b3cf-fca7-4207-8dcc-5a0b5d305242.json', 'assets/main/import/06/0699aaa4c.json', 'assets/main/import/06/06add430f.json', 'assets/main/import/fd/fd8ec536-a354-4a17-9c74-4f3883c378c8.json', 'assets/main/native/6f', 'assets/main/native/d0', ].forEach(removeBuildPath); } function totalBuildBytes() { let total = 0; const walk = (dir) => { for (const name of fs.readdirSync(dir)) { const file = path.join(dir, name); const stat = fs.statSync(file); if (stat.isDirectory()) walk(file); else total += stat.size; } }; walk(buildRoot); return total; } if (!fs.existsSync(buildRoot)) { throw new Error(`Wechat build folder not found: ${buildRoot}`); } removeFirstScreen(); patchFullscreenCanvas(); rewriteCcWithoutPhysics(); disablePhysicsSettingOnly(); removeDefaultPhysicsMaterialAsset(); removePhysicsEngineFiles(); removeUnusedDefaultScene(); const total = totalBuildBytes(); console.log(`wechat build size: ${total} bytes (${(total / 1024 / 1024).toFixed(2)} MB)`);