From 1106be57a224de8bb9789a6309261e1906322c1f Mon Sep 17 00:00:00 2001 From: LyneXiao Date: Tue, 7 Jan 2025 21:37:59 +0800 Subject: [PATCH 1/7] =?UTF-8?q?BaseAppInit.startInit=E6=97=B6=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E4=BB=BB=E5=8A=A1=E5=88=97=E8=A1=A8=EF=BC=8C=E4=BB=A5?= =?UTF-8?q?=E4=BE=BF=E5=9C=A8=E5=88=9D=E5=A7=8B=E5=8C=96manager=E5=89=8D?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E8=BF=9B=E8=A1=8C=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/app/assets/base/BaseAppInit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/app/assets/base/BaseAppInit.ts b/extensions/app/assets/base/BaseAppInit.ts index 7de2755..0ae79b7 100644 --- a/extensions/app/assets/base/BaseAppInit.ts +++ b/extensions/app/assets/base/BaseAppInit.ts @@ -48,7 +48,7 @@ export default abstract class BaseAppInit extends Component { */ protected startInit() { const projectBundles = settings.querySettings(Settings.Category.ASSETS, 'projectBundles') as string[]; - Core.inst.lib.task.createAny() + return Core.inst.lib.task.createAny() // 预加载control、model、admin、manager .add([ (next, retry) => { -- Gitee From 1b0a902ef737422882d352b1e297ea015d9b9e1c Mon Sep 17 00:00:00 2001 From: LyneXiao Date: Fri, 10 Jan 2025 01:00:17 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=BA=AF=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E4=BE=9D=E8=B5=96=E5=85=B3=E7=B3=BB=EF=BC=9B=E6=89=A9?= =?UTF-8?q?=E5=B1=95Bundle=E4=BE=9D=E8=B5=96=E5=85=B3=E7=B3=BB=EF=BC=9B?= =?UTF-8?q?=E4=BC=98=E5=85=88=E5=8A=A0=E8=BD=BD=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/app/assets/base/BaseAppInit.ts | 97 ++++----- .../assets/manager/loader/LoaderManager.ts | 43 +++- extensions/app/engine/dist/builder/hooks.js | 6 +- .../engine/dist/builder/utils/dependencies.js | 192 ++++++++++++++++++ .../engine/dist/inspector/asset-directory.js | 39 ++++ .../panel/components/create-controller.js | 2 +- extensions/app/engine/package-lock.json | 9 +- extensions/app/engine/package.json | 2 +- extensions/app/engine/src/builder/hooks.ts | 6 + .../engine/src/builder/utils/dependencies.ts | 179 ++++++++++++++++ .../engine/src/inspector/asset-directory.ts | 38 +++- 11 files changed, 553 insertions(+), 60 deletions(-) create mode 100644 extensions/app/engine/dist/builder/utils/dependencies.js create mode 100644 extensions/app/engine/src/builder/utils/dependencies.ts diff --git a/extensions/app/assets/base/BaseAppInit.ts b/extensions/app/assets/base/BaseAppInit.ts index 0ae79b7..1983e6f 100644 --- a/extensions/app/assets/base/BaseAppInit.ts +++ b/extensions/app/assets/base/BaseAppInit.ts @@ -1,4 +1,4 @@ -import { Button, Component, EventTouch, Node, Settings, _decorator, assetManager, isValid, settings, warn } from 'cc'; +import { Button, Component, EventTouch, ForwardFlow, Node, Settings, _decorator, assetManager, isValid, settings, warn } from 'cc'; import { EDITOR } from 'cc/env'; import Core from '../Core'; import BaseManager from './BaseManager'; @@ -48,52 +48,57 @@ export default abstract class BaseAppInit extends Component { */ protected startInit() { const projectBundles = settings.querySettings(Settings.Category.ASSETS, 'projectBundles') as string[]; - return Core.inst.lib.task.createAny() - // 预加载control、model、admin、manager - .add([ - (next, retry) => { - // 预加载control(废弃) - if (projectBundles.indexOf(ControlBundleName) === -1) return next(); - assetManager.preloadAny({ url: ControlBundleName }, { ext: 'bundle' }, null, (err) => { - if (err) return retry(0.1); - next(); - }); - }, - (next, retry) => { - // 预加载controller - if (projectBundles.indexOf(ControllerBundleName) === -1) return next(); - assetManager.preloadAny({ url: ControllerBundleName }, { ext: 'bundle' }, null, (err) => { - if (err) return retry(0.1); - next(); - }); - }, - (next, retry) => { - // 预加载model - if (projectBundles.indexOf(ModelBundleName) === -1) return next(); - assetManager.preloadAny({ url: ModelBundleName }, { ext: 'bundle' }, null, (err) => { - if (err) return retry(0.1); - next(); - }); - }, - (next, retry) => { - // 预加载admin - if (projectBundles.indexOf(AdminBundleName) === -1) return next(); - assetManager.preloadAny({ url: AdminBundleName }, { ext: 'bundle' }, null, (err) => { - if (err) return retry(0.1); - next(); - }); - }, - (next, retry) => { - // 预加载manage - if (projectBundles.indexOf(ManagerBundleName) === -1) return next(); - assetManager.preloadAny({ url: ManagerBundleName }, { ext: 'bundle' }, null, (err) => { - if (err) return retry(0.1); - next(); - }); + const dependencies = settings.querySettings(Settings.Category.ASSETS, 'dependencies') ?? {} as any; + const appBundles = [ControlBundleName, ControllerBundleName, ModelBundleName, AdminBundleName, ManagerBundleName]; + let preloadBundles = new Set(); + let depBundles = new Set(); + // 统计所有预加载Bundle,加入被依赖的包 + for (const name of appBundles) { + if (projectBundles.indexOf(name) === -1) continue; + preloadBundles.add(name); + if (dependencies[name]) { + for (const dep of dependencies[name]) { + depBundles.add(dep); + preloadBundles.add(dep); } - ]) - // 加载control(废弃) - .add((next, retry) => { + } + } + let preloads = []; + for (const name of preloadBundles) { + preloads.push((next, retry)=>{ + assetManager.preloadAny({ url: name }, { ext: 'bundle' }, null, (err) => { + if (err) return retry(0.1); + next(); + }); + }) + } + + let task = Core.inst.lib.task.createAny(); + task.add(preloads); // 先预加载所有包 + + // 然后按序加载所有依赖包 + let deps: string[] = []; + for (const dep of depBundles) { + deps.push(dep); + } + deps.sort((a: string, b: string)=>{ + if (!dependencies[a]) return -1; + if (!dependencies[b]) return 1; + if (dependencies[b].indexOf(a) >= 0) return -1; + if (dependencies[a].indexOf(b) >= 0) return 1; + return 0; + }) + for (const dep of deps) { + task.add((next, retry)=>{ + assetManager.loadBundle(dep, (err) => { + if (err) return retry(0.1); + next(); + }); + }) + } + + return task + .add((next, retry) => { // 加载control(废弃) if (projectBundles.indexOf(ControlBundleName) === -1) return next(); assetManager.loadBundle(ControlBundleName, (err) => { if (err) return retry(0.1); diff --git a/extensions/app/assets/manager/loader/LoaderManager.ts b/extensions/app/assets/manager/loader/LoaderManager.ts index 0f45cd5..7ac9e87 100644 --- a/extensions/app/assets/manager/loader/LoaderManager.ts +++ b/extensions/app/assets/manager/loader/LoaderManager.ts @@ -1,11 +1,19 @@ -import { Asset, AssetManager, Font, ImageAsset, JsonAsset, Label, SceneAsset, Sprite, SpriteFrame, Texture2D, TextureCube, _decorator, assetManager, isValid, path, sp } from 'cc'; +import { Asset, AssetManager, Font, ImageAsset, JsonAsset, Label, SceneAsset, Settings, Sprite, SpriteFrame, Texture2D, TextureCube, _decorator, assetManager, isValid, path, settings, sp } from 'cc'; import { MINIGAME } from 'cc/env'; import BaseManager from '../../base/BaseManager'; +import Core from '../../Core'; const { ccclass } = _decorator; const REGEX = /^https?:\/\/.*/; @ccclass('LoaderManager') export default class LoaderManager extends BaseManager { + private _dependencies: {[key: string]: string[]}; + + protected init(finish?: Function): void { + const dependencies = settings.querySettings(Settings.Category.ASSETS, 'dependencies') ?? {} as any; + this._dependencies = dependencies; + super.init(finish); + } private handle(handle: string, { bundle, version, path, type, onProgress, onComplete }: { bundle?: string, version?: string, path: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (result: unknown | null) => void }) { if (!handle) { @@ -167,6 +175,29 @@ export default class LoaderManager extends BaseManager { assetManager.getBundle(bundle)?.releaseUnusedAssets(); } + private _loadOneBundle(bundle: string, onComplete?: (bundle: AssetManager.Bundle | null) => any) { + assetManager.loadBundle(bundle, (err: Error, result: AssetManager.Bundle) => { + onComplete && onComplete(err ? null : result); + }); + } + + private _loadBundleInternal(bundle: string, onComplete?: (bundle: AssetManager.Bundle | null) => any) { + if (this._dependencies[bundle]) { + let deps = []; + for (const dep of this._dependencies[bundle]) { + deps.push((next)=>{ + this._loadOneBundle(dep, next); + }) + } + let task = Core.inst.lib.task.createAny(); + task.add(deps).start(()=>{ + this._loadOneBundle(bundle, onComplete); + }) + } else { + this._loadOneBundle(bundle, onComplete); + } + } + /** * 加载一个bundle * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle @@ -187,6 +218,7 @@ export default class LoaderManager extends BaseManager { // 1. 指定的version与本地version不一致 // 2. 是远程bundle本地不存在 if (version && assetManager.downloader.bundleVers[bundle] !== version) { + // 带版本号的不处理依赖关题 TODO? // 这里需要先加载bundle触发内部的一些初始化逻辑(主要是缓存相关的初始化) assetManager.loadBundle(bundle, (err: Error, b: AssetManager.Bundle) => { if (err || !b) return onComplete?.(null); @@ -194,21 +226,18 @@ export default class LoaderManager extends BaseManager { this.reloadBundle({ bundle, version, onComplete }); }); } else { - assetManager.loadBundle(bundle, (err: Error, bundle: AssetManager.Bundle) => { - onComplete && onComplete(err ? null : bundle); - }); + this._loadBundleInternal(bundle, onComplete); } return; } if (version) { + // 带版本号的不处理依赖关题 TODO? assetManager.loadBundle(bundle, { version }, (err: Error, bundle: AssetManager.Bundle) => { onComplete && onComplete(err ? null : bundle); }); } else { - assetManager.loadBundle(bundle, (err: Error, bundle: AssetManager.Bundle) => { - onComplete && onComplete(err ? null : bundle); - }); + this._loadBundleInternal(bundle, onComplete); } } diff --git a/extensions/app/engine/dist/builder/hooks.js b/extensions/app/engine/dist/builder/hooks.js index b0adaf2..8aad1a6 100644 --- a/extensions/app/engine/dist/builder/hooks.js +++ b/extensions/app/engine/dist/builder/hooks.js @@ -3,10 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.onAfterBuild = void 0; +exports.onAfterBuild = exports.throwError = void 0; const path_1 = __importDefault(require("path")); const file_1 = require("./utils/file"); +const dependencies_1 = require("./utils/dependencies"); +exports.throwError = true; const onAfterBuild = async function (options, result) { + // 管理Bundle间的依赖关系,确保Bundle中脚本加载顺序 + dependencies_1.resaveAllBundleDependencies(result.dest, options.moveRemoteBundleScript); if (options.platform !== 'web-mobile' && options.platform !== 'web-desktop') { return; } diff --git a/extensions/app/engine/dist/builder/utils/dependencies.js b/extensions/app/engine/dist/builder/utils/dependencies.js new file mode 100644 index 0000000..d906239 --- /dev/null +++ b/extensions/app/engine/dist/builder/utils/dependencies.js @@ -0,0 +1,192 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.resaveAllBundleDependencies = void 0; +const path_1 = __importDefault(require("path")); +const fs = __importStar(require("fs")); +const settingsFileReg = /^settings\.(\w+\.)?json$/g; +const jsRegisterReg = /^\s*System.register\(\s*[\"\'](.*?)[\"\']\s*,\s*\[\s*(.*?)\]\s*,/; +const jsChunkNameReg = /^chunks:\/\/\/_virtual\/(.*)$/; +const ignoreJsDepends = ['cc', 'cc/env', './rollupPluginModLoBabelHelpers.js']; +function findSettingFile(srcPath) { + const files = fs.readdirSync(srcPath); + for (const file of files) { + if (settingsFileReg.test(file)) + return path_1.default.join(srcPath, file); + } + throw new Error("Cannot find settings.json in dir:" + srcPath); +} +function traverseDirectoryRecursive(dir, callback) { + fs.readdirSync(dir).forEach((file) => { + const filePath = path_1.default.join(dir, file); + const stats = fs.statSync(filePath); + if (stats.isDirectory()) { + callback(filePath, file); + traverseDirectoryRecursive(filePath, callback); // 递归调用 + } + }); +} +function extendDeepDenps(self, set, deps, allDeps) { + for (const dep of deps) { + if (self === dep) + return true; + set.add(dep); + if (!allDeps.has(dep)) + continue; + if (extendDeepDenps(self, set, allDeps.get(dep), allDeps)) { + return true; + } + } + return false; +} +function extendAndCheckCircleDependent(allBundels, deps) { + for (const name of allBundels) { + if (!deps.has(name)) + continue; + let set = new Set(); + if (extendDeepDenps(name, set, deps.get(name), deps)) { + return true; + } + deps.set(name, set); + } + return false; +} +function resaveAllBundleDependencies(dstPath, scriptMoved) { + const file = findSettingFile(path_1.default.join(dstPath, 'src')); + const settings = JSON.parse(fs.readFileSync(file, 'utf-8')); + const allBundles = settings.assets.projectBundles; + const remoteBundles = settings.assets.remoteBundles; + const bundleVers = settings.assets.bundleVers; + const subpackages = settings.assets.subpackages; + const md5Bundles = Object.keys(bundleVers); + let deps = new Map(); + let jsBundles = new Map(); + let bundleImports = new Map(); + for (const name of allBundles) { + const bundleVersion = md5Bundles.indexOf(name) !== -1 ? bundleVers[name] : undefined; + const isRemote = remoteBundles.indexOf(name) !== -1; + const isSubpackage = subpackages.indexOf(name) !== -1; + const jsonFile = path_1.default.join(dstPath, isRemote ? (isSubpackage ? 'subpackages' : 'remote') : 'assets', name, bundleVersion ? `config.${bundleVersion}.json` : 'config.json'); + const config = JSON.parse(fs.readFileSync(jsonFile, 'utf-8')); + deps.set(name, new Set(config.deps)); + const jsFile = path_1.default.join(dstPath, isRemote ? (isSubpackage ? 'subpackages' : (scriptMoved ? 'src/bundle-scripts' : 'remote')) : 'assets', name, isSubpackage ? 'game.js' : (bundleVersion ? `index.${bundleVersion}.js` : 'index.js')); + const lines = fs.readFileSync(jsFile, 'utf-8').split(/\r?\n/); + let allImports = new Set(); + for (const line of lines) { + const match = line.match(jsRegisterReg); + if (!match) + continue; + const chunkName = match[1]; + const imports = match[2]; + const chunkMatch = chunkName.match(jsChunkNameReg); + if (!chunkMatch) { + throw new Error(`Unknow register name with ${chunkName} in file ${jsFile}`); + } + const fileName = chunkMatch[1]; + if (fileName != name) { + jsBundles.set('./' + fileName, name); + } + for (const str of imports.split(',')) { + const dep = eval(str); + if (dep && ignoreJsDepends.indexOf(dep) === -1) { + allImports.add(dep); + } + } + } + bundleImports.set(name, allImports); + } + // 查找所有bundle的meta文件以添加扩展仍赖 + traverseDirectoryRecursive(Editor.Project.path, (dir, name) => { + var _a; + const mataPath = dir + ".meta"; + if (!fs.existsSync(mataPath)) + return; + const meta = JSON.parse(fs.readFileSync(mataPath, 'utf-8')); + const userData = meta['userData']; + if (!userData || !userData['isBundle']) + return; + const bundleName = (_a = userData['bundleName']) !== null && _a !== void 0 ? _a : name; + if (allBundles.indexOf(bundleName) === -1) + return; + const exts = userData['dep_ext']; + if (!exts) + return; + for (const n of exts.split(',')) { + const fixName = n.trim(); + if (fixName.length != 0) { + if (!deps.has(name)) { + deps.set(name, new Set()); + } + deps.get(name).add(fixName); + } + } + }); + for (const name of allBundles) { + if (!bundleImports.has(name)) + continue; + for (const js of bundleImports.get(name)) { + if (!jsBundles.has(js)) { + console.error(`Cannot find which bundle is script:${js} in!`); + continue; + } + const bundle = jsBundles.get(js); + if (bundle == name) + continue; + if (!deps.has(name)) { + deps.set(name, new Set()); + } + deps.get(name).add(bundle); + } + } + // 递归查找所有依赖并检查是否循环依赖 + const hasCircle = extendAndCheckCircleDependent(allBundles, deps); + let data = {}; + for (const kv of deps) { + const set = kv[1]; + if (set && set.size > 0) { + let list = []; + for (const d of set) { + list.push(d); + } + list.sort((a, b) => { + if (!deps.has(a)) + return -1; + if (!deps.has(b)) + return 1; + if (deps.get(b).has(a)) + return -1; + if (deps.get(a).has(b)) + return 1; + return 0; + }); + data[kv[0]] = list; + } + } + if (hasCircle) { + throw new Error('存在循环依赖,请检查依赖关系!' + JSON.stringify(data)); + } + settings.assets['dependencies'] = data; + fs.writeFileSync(file, JSON.stringify(settings)); +} +exports.resaveAllBundleDependencies = resaveAllBundleDependencies; diff --git a/extensions/app/engine/dist/inspector/asset-directory.js b/extensions/app/engine/dist/inspector/asset-directory.js index 8db8ee8..a4b3737 100644 --- a/extensions/app/engine/dist/inspector/asset-directory.js +++ b/extensions/app/engine/dist/inspector/asset-directory.js @@ -6,13 +6,25 @@ const path_1 = require("path"); exports.$ = { 'code': '#code', 'section': '#section', + 'deps': '#deps', + 'depsInput': '#depsInput', }; +// 添加bundle的自定义依赖,不知道怎么使用list引用 Assets,先用input代替 exports.template = ` + + + + + `; function update(assetList, metaList) { + var _a; this.assetList = assetList; this.metaList = metaList; if (assetList.length === 0) { @@ -37,10 +49,37 @@ function update(assetList, metaList) { else { this.$.section.hidden = false; } + // 添加bundle的自定义依赖 + this.currentMeta = null; + this.currentUrl = null; + const input = this.$.depsInput; + if (assetList.length === 1) { // 不支持multiple + let meta = metaList[0]; + if (meta.userData['isBundle']) { + this.$.deps.hidden = false; + input.value = (_a = meta.userData['dep_ext']) !== null && _a !== void 0 ? _a : ""; + this.currentUrl = assetList[0].url; + this.currentMeta = meta; + } + else { + input.value = ''; + this.$.deps.hidden = true; + } + } + else { + input.value = ''; + this.$.deps.hidden = true; + } } exports.update = update; function ready() { // TODO something + this.$.depsInput.addEventListener('confirm', () => { + if (!this.currentMeta) + return; + const input = this.$.depsInput; + this.currentMeta.userData['dep_ext'] = input.value; + }); } exports.ready = ready; function close() { diff --git a/extensions/app/engine/dist/panel/components/create-controller.js b/extensions/app/engine/dist/panel/components/create-controller.js index 100b983..af9bd49 100644 --- a/extensions/app/engine/dist/panel/components/create-controller.js +++ b/extensions/app/engine/dist/panel/components/create-controller.js @@ -16,7 +16,7 @@ function getScript(name) { ' // 定义了事件,并同时定义参数列表和返回值\r\n' + ' Refresh: (a: number) => boolean\r\n' + '}>() {\r\n' + - ' // controller中发射事件, UI中监听事件:\r\n' + + ' // Controller中发射事件, UI中监听事件:\r\n' + ' // 1、UI中需要将 「extends BaseView」 改为=> 「extends BaseView.bindController(' + name + ')」\r\n' + ' // 2、UI中使用「this.controller.on/once」监听事件, 使用「this.controller.emit」发射事件, 使用「this.controller.off/targetOff」取消监听事件\r\n' + ' // 3、在外部(无法使用this.controller的地方)可以通过「app.controller.xxx」来调用对外导出的方法, 比如下面的refresh方法\r\n' + diff --git a/extensions/app/engine/package-lock.json b/extensions/app/engine/package-lock.json index c190858..961dfd9 100644 --- a/extensions/app/engine/package-lock.json +++ b/extensions/app/engine/package-lock.json @@ -6,14 +6,15 @@ "": { "name": "engine", "devDependencies": { - "@types/node": "16.0.1", + "@types/node": "^16.0.1", "typescript": "4.3.4" } }, "node_modules/@types/node": { "version": "16.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.0.1.tgz", + "integrity": "sha512-hBOx4SUlEPKwRi6PrXuTGw1z6lz0fjsibcWCM378YxsSu/6+C30L6CR49zIBKHiwNWCYIcOLjg4OHKZaFeLAug==", + "dev": true }, "node_modules/typescript": { "version": "4.3.4", @@ -31,6 +32,8 @@ "dependencies": { "@types/node": { "version": "16.0.1", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.0.1.tgz", + "integrity": "sha512-hBOx4SUlEPKwRi6PrXuTGw1z6lz0fjsibcWCM378YxsSu/6+C30L6CR49zIBKHiwNWCYIcOLjg4OHKZaFeLAug==", "dev": true }, "typescript": { diff --git a/extensions/app/engine/package.json b/extensions/app/engine/package.json index 2f629e8..0b615ca 100755 --- a/extensions/app/engine/package.json +++ b/extensions/app/engine/package.json @@ -1,7 +1,7 @@ { "name": "engine", "devDependencies": { - "@types/node": "16.0.1", + "@types/node": "^16.0.1", "typescript": "4.3.4" }, "scripts": { diff --git a/extensions/app/engine/src/builder/hooks.ts b/extensions/app/engine/src/builder/hooks.ts index df880ab..15a34a3 100644 --- a/extensions/app/engine/src/builder/hooks.ts +++ b/extensions/app/engine/src/builder/hooks.ts @@ -1,8 +1,14 @@ import path from 'path'; import { BuildHook } from '../../@types/packages/builder/@types'; import { adaptFileMD5 } from './utils/file'; +import { resaveAllBundleDependencies } from './utils/dependencies'; + +export const throwError = true; export const onAfterBuild: BuildHook.onAfterBuild = async function (options, result) { + // 管理Bundle间的依赖关系,确保Bundle中脚本加载顺序 + resaveAllBundleDependencies(result.dest, options.moveRemoteBundleScript); + if (options.platform !== 'web-mobile' && options.platform !== 'web-desktop') { return; } diff --git a/extensions/app/engine/src/builder/utils/dependencies.ts b/extensions/app/engine/src/builder/utils/dependencies.ts new file mode 100644 index 0000000..c148637 --- /dev/null +++ b/extensions/app/engine/src/builder/utils/dependencies.ts @@ -0,0 +1,179 @@ +import path from "path"; +import * as fs from 'fs'; +import { IBundleConfig, IOutputSettings, Platform } from "../../../@types/packages/builder/@types"; + +const settingsFileReg = /^settings\.(\w+\.)?json$/g; +const jsRegisterReg = /^\s*System.register\(\s*[\"\'](.*?)[\"\']\s*,\s*\[\s*(.*?)\]\s*,/; +const jsChunkNameReg = /^chunks:\/\/\/_virtual\/(.*)$/; +const ignoreJsDepends = ['cc', 'cc/env', './rollupPluginModLoBabelHelpers.js']; + +function findSettingFile(srcPath: string): string { + const files = fs.readdirSync(srcPath); + for (const file of files) { + if (settingsFileReg.test(file)) + return path.join(srcPath, file); + } + + throw new Error("Cannot find settings.json in dir:" + srcPath); +} + +function traverseDirectoryRecursive(dir: string, callback:(dir:string, name: string)=>void):void { + fs.readdirSync(dir).forEach((file) => { + const filePath = path.join(dir, file); + const stats = fs.statSync(filePath); + if (stats.isDirectory()) { + callback(filePath, file); + traverseDirectoryRecursive(filePath, callback); // 递归调用 + } + }); +} + +function extendDeepDenps(self: string, set: Set, deps: Set, allDeps: Map>): boolean { + for (const dep of deps) { + if (self === dep) + return true; + set.add(dep); + if (!allDeps.has(dep)) continue; + if (extendDeepDenps(self, set, allDeps.get(dep), allDeps)) { + return true; + } + } + + return false; +} + +function extendAndCheckCircleDependent(allBundels: string[], deps: Map>): boolean { + for (const name of allBundels) { + if (!deps.has(name)) continue; + let set = new Set(); + if (extendDeepDenps(name, set, deps.get(name), deps)) { + return true; + } + deps.set(name, set); + } + + return false; +} + +export function resaveAllBundleDependencies(dstPath:string, scriptMoved: boolean): void { + const file = findSettingFile(path.join(dstPath, 'src')); + const settings: IOutputSettings = JSON.parse(fs.readFileSync(file, 'utf-8')); + const allBundles = settings.assets.projectBundles; + const remoteBundles = settings.assets.remoteBundles; + const bundleVers = settings.assets.bundleVers; + const subpackages = settings.assets.subpackages; + const md5Bundles = Object.keys(bundleVers); + let deps: Map> = new Map(); + let jsBundles: Map = new Map(); + let bundleImports: Map> = new Map(); + for (const name of allBundles) { + const bundleVersion = md5Bundles.indexOf(name) !== -1 ? bundleVers[name]: undefined; + const isRemote = remoteBundles.indexOf(name) !== -1; + const isSubpackage = subpackages.indexOf(name) !== -1; + const jsonFile = path.join(dstPath + , isRemote ? (isSubpackage ? 'subpackages' : 'remote') : 'assets' + , name + , bundleVersion ? `config.${bundleVersion}.json` : 'config.json' + ); + const config: IBundleConfig = JSON.parse(fs.readFileSync(jsonFile, 'utf-8')); + deps.set(name, new Set(config.deps)); + + const jsFile = path.join(dstPath + , isRemote ? (isSubpackage ? 'subpackages' : (scriptMoved ? 'src/bundle-scripts' : 'remote')) : 'assets' + , name + , isSubpackage ? 'game.js' : (bundleVersion ? `index.${bundleVersion}.js` : 'index.js') + ); + const lines = fs.readFileSync(jsFile, 'utf-8').split(/\r?\n/); + let allImports = new Set(); + for (const line of lines) { + const match = line.match(jsRegisterReg); + if (!match) continue; + const chunkName = match[1]; + const imports = match[2]; + const chunkMatch = chunkName.match(jsChunkNameReg); + if (!chunkMatch) { + throw new Error(`Unknow register name with ${chunkName} in file ${jsFile}`); + } + const fileName = chunkMatch[1]; + if (fileName != name) { + jsBundles.set('./' + fileName, name); + } + for (const str of imports.split(',')) { + const dep = eval(str); + if (dep && ignoreJsDepends.indexOf(dep) === -1) { + allImports.add(dep); + } + } + } + bundleImports.set(name, allImports); + } + + // 查找所有bundle的meta文件以添加扩展仍赖 + traverseDirectoryRecursive(Editor.Project.path, (dir: string, name: string)=>{ + const mataPath = dir + ".meta"; + if (!fs.existsSync(mataPath)) return; + const meta = JSON.parse(fs.readFileSync(mataPath, 'utf-8')); + const userData = meta['userData'] + if (!userData || !userData['isBundle']) return; + const bundleName = userData['bundleName'] ?? name; + if (allBundles.indexOf(bundleName) === -1) return; + const exts: string = userData['dep_ext']; + if (!exts) return; + for (const n of exts.split(',')) { + const fixName = n.trim(); + if (fixName.length != 0) { + if (!deps.has(name)) { + deps.set(name, new Set()); + } + deps.get(name).add(fixName); + } + } + }); + + for (const name of allBundles) { + if (!bundleImports.has(name)) continue; + for (const js of bundleImports.get(name)) { + if (!jsBundles.has(js)) { + console.error(`Cannot find which bundle is script:${js} in!`); + continue; + } + const bundle = jsBundles.get(js); + if (bundle == name) continue; + if (!deps.has(name)) { + deps.set(name, new Set()); + } + deps.get(name).add(bundle); + } + } + + // 递归查找所有依赖并检查是否循环依赖 + const hasCircle = extendAndCheckCircleDependent(allBundles, deps); + + let data: any = {}; + for (const kv of deps) { + const set = kv[1]; + if (set && set.size > 0) { + let list = []; + for (const d of set) { + list.push(d); + } + + list.sort((a: string, b: string)=>{ + if (!deps.has(a)) return -1; + if (!deps.has(b)) return 1; + if (deps.get(b).has(a)) return -1; + if (deps.get(a).has(b)) return 1; + return 0; + }) + + data[kv[0]] = list; + } + } + + if (hasCircle) { + throw new Error('存在循环依赖,请检查依赖关系!' + JSON.stringify(data)); + } + + settings.assets['dependencies'] = data; + fs.writeFileSync(file, JSON.stringify(settings)); +} \ No newline at end of file diff --git a/extensions/app/engine/src/inspector/asset-directory.ts b/extensions/app/engine/src/inspector/asset-directory.ts index a7d6d9f..a827887 100644 --- a/extensions/app/engine/src/inspector/asset-directory.ts +++ b/extensions/app/engine/src/inspector/asset-directory.ts @@ -41,15 +41,26 @@ type Selector<$> = { $: Record } & { dispatch(str: string) export const $ = { 'code': '#code', 'section': '#section', + 'deps': '#deps', + 'depsInput': '#depsInput', }; +// 添加bundle的自定义依赖,不知道怎么使用list引用 Assets,先用input代替 export const template = ` + + + + + `; -type PanelThis = Selector; +type PanelThis = Selector & { currentMeta?: Meta, currentUrl:string }; export function update(this: PanelThis, assetList: Asset[], metaList: Meta[]) { this.assetList = assetList; @@ -76,10 +87,35 @@ export function update(this: PanelThis, assetList: Asset[], metaList: Meta[]) { } else { this.$.section.hidden = false; } + + // 添加bundle的自定义依赖 + this.currentMeta = null; + this.currentUrl = null; + const input = this.$.depsInput as any; + if (assetList.length === 1) { // 不支持multiple + let meta = metaList[0]; + if (meta.userData['isBundle']) { + this.$.deps.hidden = false; + input.value = meta.userData['dep_ext'] ?? "" + this.currentUrl = assetList[0].url; + this.currentMeta = meta; + } else { + input.value = '' + this.$.deps.hidden = true; + } + } else { + input.value = '' + this.$.deps.hidden = true; + } } export function ready(this: PanelThis) { // TODO something + this.$.depsInput.addEventListener('confirm', ()=>{ + if (!this.currentMeta) return; + const input = this.$.depsInput as any; + this.currentMeta.userData['dep_ext'] = input.value; + }) } export function close(this: PanelThis,) { -- Gitee From a51bff6929d08963c538b6bfc109ba15dc9dc465 Mon Sep 17 00:00:00 2001 From: LyneXiao Date: Fri, 10 Jan 2025 09:10:47 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/manager/loader/LoaderManager.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/extensions/app/assets/manager/loader/LoaderManager.ts b/extensions/app/assets/manager/loader/LoaderManager.ts index 7ac9e87..18139df 100644 --- a/extensions/app/assets/manager/loader/LoaderManager.ts +++ b/extensions/app/assets/manager/loader/LoaderManager.ts @@ -183,16 +183,26 @@ export default class LoaderManager extends BaseManager { private _loadBundleInternal(bundle: string, onComplete?: (bundle: AssetManager.Bundle | null) => any) { if (this._dependencies[bundle]) { - let deps = []; - for (const dep of this._dependencies[bundle]) { - deps.push((next)=>{ - this._loadOneBundle(dep, next); + let deps = this._dependencies[bundle]; + let preloads = []; + for (const dep of deps) { + preloads.push((next, retry)=>{ + assetManager.preloadAny({ url: dep }, { ext: 'bundle' }, null, (err) => { + if (err) return retry(0.1); + next(); + }); }) } let task = Core.inst.lib.task.createAny(); - task.add(deps).start(()=>{ + task.add(preloads); + for (const dep of deps) { + task.add((next)=>{ + this._loadOneBundle(dep, next); + }); + } + task.start(()=>{ this._loadOneBundle(bundle, onComplete); - }) + }); } else { this._loadOneBundle(bundle, onComplete); } -- Gitee From a2f1cbf8bc62a9e4efa94ec9ba7cf6120e3ebb90 Mon Sep 17 00:00:00 2001 From: LyneXiao Date: Sun, 12 Jan 2025 00:05:26 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E9=94=99=E8=AF=AF=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=EF=BC=9B=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=8A=A8=E6=8E=92=E9=99=A4?= =?UTF-8?q?Bundle=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/app/engine/dist/builder/hooks.js | 2 +- .../engine/dist/builder/utils/dependencies.js | 46 ++++++++++++++++- .../engine/dist/inspector/asset-directory.js | 17 ++++++- extensions/app/engine/src/builder/hooks.ts | 2 +- .../engine/src/builder/utils/dependencies.ts | 50 ++++++++++++++++++- .../engine/src/inspector/asset-directory.ts | 16 +++++- 6 files changed, 123 insertions(+), 10 deletions(-) diff --git a/extensions/app/engine/dist/builder/hooks.js b/extensions/app/engine/dist/builder/hooks.js index 8aad1a6..2399eeb 100644 --- a/extensions/app/engine/dist/builder/hooks.js +++ b/extensions/app/engine/dist/builder/hooks.js @@ -10,7 +10,7 @@ const dependencies_1 = require("./utils/dependencies"); exports.throwError = true; const onAfterBuild = async function (options, result) { // 管理Bundle间的依赖关系,确保Bundle中脚本加载顺序 - dependencies_1.resaveAllBundleDependencies(result.dest, options.moveRemoteBundleScript); + dependencies_1.resaveAllBundleDependencies(result.dest, options.moveRemoteBundleScript || options.platform == 'wechatgame'); if (options.platform !== 'web-mobile' && options.platform !== 'web-desktop') { return; } diff --git a/extensions/app/engine/dist/builder/utils/dependencies.js b/extensions/app/engine/dist/builder/utils/dependencies.js index d906239..4d95cc5 100644 --- a/extensions/app/engine/dist/builder/utils/dependencies.js +++ b/extensions/app/engine/dist/builder/utils/dependencies.js @@ -29,6 +29,31 @@ const settingsFileReg = /^settings\.(\w+\.)?json$/g; const jsRegisterReg = /^\s*System.register\(\s*[\"\'](.*?)[\"\']\s*,\s*\[\s*(.*?)\]\s*,/; const jsChunkNameReg = /^chunks:\/\/\/_virtual\/(.*)$/; const ignoreJsDepends = ['cc', 'cc/env', './rollupPluginModLoBabelHelpers.js']; +function deleteDirIfExist(dir) { + if (fs.existsSync(dir)) { + fs.rmSync(dir, { recursive: true, force: true }); + console.log(`removed dir: ${dir} as it's not been depended`); + } +} +function excludeBundle(name, settings, dir) { + let projectBundles = settings.assets.projectBundles; + projectBundles.splice(projectBundles.indexOf(name), 1); + let remotes = settings.assets.remoteBundles; + if (remotes.indexOf(name) !== -1) { + remotes.splice(remotes.indexOf(name), 1); + deleteDirIfExist(path_1.default.join(dir, "remote", name)); + deleteDirIfExist(path_1.default.join(dir, "src", "bundle-scripts", name)); + } + let subpackages = settings.assets.subpackages; + if (subpackages.indexOf(name) !== -1) { + subpackages.splice(subpackages.indexOf(name), 1); + deleteDirIfExist(path_1.default.join(dir, "subpackages", name)); + } + deleteDirIfExist(path_1.default.join(dir, "assets", name)); + if (settings.assets.bundleVers[name]) { + delete settings.assets.bundleVers[name]; + } +} function findSettingFile(srcPath) { const files = fs.readdirSync(srcPath); for (const file of files) { @@ -83,14 +108,15 @@ function resaveAllBundleDependencies(dstPath, scriptMoved) { let deps = new Map(); let jsBundles = new Map(); let bundleImports = new Map(); + let autoExcludeBundles = []; for (const name of allBundles) { const bundleVersion = md5Bundles.indexOf(name) !== -1 ? bundleVers[name] : undefined; const isRemote = remoteBundles.indexOf(name) !== -1; const isSubpackage = subpackages.indexOf(name) !== -1; - const jsonFile = path_1.default.join(dstPath, isRemote ? (isSubpackage ? 'subpackages' : 'remote') : 'assets', name, bundleVersion ? `config.${bundleVersion}.json` : 'config.json'); + const jsonFile = path_1.default.join(dstPath, isSubpackage ? 'subpackages' : (isRemote ? 'remote' : 'assets'), name, bundleVersion ? `config.${bundleVersion}.json` : 'config.json'); const config = JSON.parse(fs.readFileSync(jsonFile, 'utf-8')); deps.set(name, new Set(config.deps)); - const jsFile = path_1.default.join(dstPath, isRemote ? (isSubpackage ? 'subpackages' : (scriptMoved ? 'src/bundle-scripts' : 'remote')) : 'assets', name, isSubpackage ? 'game.js' : (bundleVersion ? `index.${bundleVersion}.js` : 'index.js')); + const jsFile = path_1.default.join(dstPath, isSubpackage ? 'subpackages' : (isRemote ? (scriptMoved ? 'src/bundle-scripts' : 'remote') : 'assets'), name, isSubpackage ? 'game.js' : (bundleVersion ? `index.${bundleVersion}.js` : 'index.js')); const lines = fs.readFileSync(jsFile, 'utf-8').split(/\r?\n/); let allImports = new Set(); for (const line of lines) { @@ -129,6 +155,9 @@ function resaveAllBundleDependencies(dstPath, scriptMoved) { const bundleName = (_a = userData['bundleName']) !== null && _a !== void 0 ? _a : name; if (allBundles.indexOf(bundleName) === -1) return; + if (userData['auto_exclude']) { + autoExcludeBundles.push(bundleName); + } const exts = userData['dep_ext']; if (!exts) return; @@ -187,6 +216,19 @@ function resaveAllBundleDependencies(dstPath, scriptMoved) { throw new Error('存在循环依赖,请检查依赖关系!' + JSON.stringify(data)); } settings.assets['dependencies'] = data; + // 排除所有未被依赖且自动排除的Bundle + for (const name of autoExcludeBundles) { + let isDep = false; + for (const set of deps.values()) { + if (set.has(name)) { + isDep = true; + break; + } + } + if (!isDep) { + excludeBundle(name, settings, dstPath); + } + } fs.writeFileSync(file, JSON.stringify(settings)); } exports.resaveAllBundleDependencies = resaveAllBundleDependencies; diff --git a/extensions/app/engine/dist/inspector/asset-directory.js b/extensions/app/engine/dist/inspector/asset-directory.js index a4b3737..23d2a73 100644 --- a/extensions/app/engine/dist/inspector/asset-directory.js +++ b/extensions/app/engine/dist/inspector/asset-directory.js @@ -8,15 +8,21 @@ exports.$ = { 'section': '#section', 'deps': '#deps', 'depsInput': '#depsInput', + 'autoExclude': '#autoExclude', }; // 添加bundle的自定义依赖,不知道怎么使用list引用 Assets,先用input代替 exports.template = ` - + + + 是否自动排除 + + + 输入额外依赖的Assets Bundle @@ -24,7 +30,7 @@ exports.template = ` `; function update(assetList, metaList) { - var _a; + var _a, _b; this.assetList = assetList; this.metaList = metaList; if (assetList.length === 0) { @@ -60,6 +66,8 @@ function update(assetList, metaList) { input.value = (_a = meta.userData['dep_ext']) !== null && _a !== void 0 ? _a : ""; this.currentUrl = assetList[0].url; this.currentMeta = meta; + const checkBox = this.$.autoExclude; + checkBox.value = (_b = meta.userData['auto_exclude']) !== null && _b !== void 0 ? _b : false; } else { input.value = ''; @@ -80,6 +88,11 @@ function ready() { const input = this.$.depsInput; this.currentMeta.userData['dep_ext'] = input.value; }); + this.$.autoExclude.addEventListener('confirm', () => { + if (!this.currentMeta) + return; + this.currentMeta.userData['auto_exclude'] = !this.currentMeta.userData['auto_exclude'] ? true : undefined; + }); } exports.ready = ready; function close() { diff --git a/extensions/app/engine/src/builder/hooks.ts b/extensions/app/engine/src/builder/hooks.ts index 15a34a3..aabba7d 100644 --- a/extensions/app/engine/src/builder/hooks.ts +++ b/extensions/app/engine/src/builder/hooks.ts @@ -7,7 +7,7 @@ export const throwError = true; export const onAfterBuild: BuildHook.onAfterBuild = async function (options, result) { // 管理Bundle间的依赖关系,确保Bundle中脚本加载顺序 - resaveAllBundleDependencies(result.dest, options.moveRemoteBundleScript); + resaveAllBundleDependencies(result.dest, options.moveRemoteBundleScript || options.platform == 'wechatgame'); if (options.platform !== 'web-mobile' && options.platform !== 'web-desktop') { return; diff --git a/extensions/app/engine/src/builder/utils/dependencies.ts b/extensions/app/engine/src/builder/utils/dependencies.ts index c148637..f226af5 100644 --- a/extensions/app/engine/src/builder/utils/dependencies.ts +++ b/extensions/app/engine/src/builder/utils/dependencies.ts @@ -7,6 +7,33 @@ const jsRegisterReg = /^\s*System.register\(\s*[\"\'](.*?)[\"\']\s*,\s*\[\s*(.*? const jsChunkNameReg = /^chunks:\/\/\/_virtual\/(.*)$/; const ignoreJsDepends = ['cc', 'cc/env', './rollupPluginModLoBabelHelpers.js']; +function deleteDirIfExist(dir: string): void { + if (fs.existsSync(dir)) { + fs.rmSync(dir, { recursive: true, force: true }); + console.log(`removed dir: ${dir} as it's not been depended`) + } +} + +function excludeBundle(name: string, settings: IOutputSettings, dir: string): void { + let projectBundles = settings.assets.projectBundles; + projectBundles.splice(projectBundles.indexOf(name), 1); + let remotes = settings.assets.remoteBundles; + if (remotes.indexOf(name) !== -1) { + remotes.splice(remotes.indexOf(name), 1); + deleteDirIfExist(path.join(dir, "remote", name)); + deleteDirIfExist(path.join(dir, "src", "bundle-scripts", name)); + } + let subpackages = settings.assets.subpackages; + if (subpackages.indexOf(name) !== -1) { + subpackages.splice(subpackages.indexOf(name), 1); + deleteDirIfExist(path.join(dir, "subpackages", name)); + } + deleteDirIfExist(path.join(dir, "assets", name)); + if (settings.assets.bundleVers[name]) { + delete settings.assets.bundleVers[name]; + } +} + function findSettingFile(srcPath: string): string { const files = fs.readdirSync(srcPath); for (const file of files) { @@ -66,12 +93,13 @@ export function resaveAllBundleDependencies(dstPath:string, scriptMoved: boolean let deps: Map> = new Map(); let jsBundles: Map = new Map(); let bundleImports: Map> = new Map(); + let autoExcludeBundles: string[] = []; for (const name of allBundles) { const bundleVersion = md5Bundles.indexOf(name) !== -1 ? bundleVers[name]: undefined; const isRemote = remoteBundles.indexOf(name) !== -1; const isSubpackage = subpackages.indexOf(name) !== -1; const jsonFile = path.join(dstPath - , isRemote ? (isSubpackage ? 'subpackages' : 'remote') : 'assets' + , isSubpackage ? 'subpackages' : (isRemote ? 'remote' : 'assets') , name , bundleVersion ? `config.${bundleVersion}.json` : 'config.json' ); @@ -79,7 +107,7 @@ export function resaveAllBundleDependencies(dstPath:string, scriptMoved: boolean deps.set(name, new Set(config.deps)); const jsFile = path.join(dstPath - , isRemote ? (isSubpackage ? 'subpackages' : (scriptMoved ? 'src/bundle-scripts' : 'remote')) : 'assets' + , isSubpackage ? 'subpackages' : (isRemote ? (scriptMoved ? 'src/bundle-scripts' : 'remote') : 'assets') , name , isSubpackage ? 'game.js' : (bundleVersion ? `index.${bundleVersion}.js` : 'index.js') ); @@ -117,6 +145,9 @@ export function resaveAllBundleDependencies(dstPath:string, scriptMoved: boolean if (!userData || !userData['isBundle']) return; const bundleName = userData['bundleName'] ?? name; if (allBundles.indexOf(bundleName) === -1) return; + if (userData['auto_exclude']) { + autoExcludeBundles.push(bundleName); + } const exts: string = userData['dep_ext']; if (!exts) return; for (const n of exts.split(',')) { @@ -175,5 +206,20 @@ export function resaveAllBundleDependencies(dstPath:string, scriptMoved: boolean } settings.assets['dependencies'] = data; + + // 排除所有未被依赖且自动排除的Bundle + for (const name of autoExcludeBundles) { + let isDep = false; + for (const set of deps.values()) { + if (set.has(name)) { + isDep = true; + break; + } + } + if (!isDep) { + excludeBundle(name, settings, dstPath); + } + } + fs.writeFileSync(file, JSON.stringify(settings)); } \ No newline at end of file diff --git a/extensions/app/engine/src/inspector/asset-directory.ts b/extensions/app/engine/src/inspector/asset-directory.ts index a827887..4540afe 100644 --- a/extensions/app/engine/src/inspector/asset-directory.ts +++ b/extensions/app/engine/src/inspector/asset-directory.ts @@ -43,16 +43,22 @@ export const $ = { 'section': '#section', 'deps': '#deps', 'depsInput': '#depsInput', + 'autoExclude': '#autoExclude', }; // 添加bundle的自定义依赖,不知道怎么使用list引用 Assets,先用input代替 export const template = ` - + + + 是否自动排除 + + + 输入额外依赖的Assets Bundle @@ -96,9 +102,11 @@ export function update(this: PanelThis, assetList: Asset[], metaList: Meta[]) { let meta = metaList[0]; if (meta.userData['isBundle']) { this.$.deps.hidden = false; - input.value = meta.userData['dep_ext'] ?? "" + input.value = meta.userData['dep_ext'] ?? ""; this.currentUrl = assetList[0].url; this.currentMeta = meta; + const checkBox = this.$.autoExclude as any; + checkBox.value = meta.userData['auto_exclude'] ?? false; } else { input.value = '' this.$.deps.hidden = true; @@ -116,6 +124,10 @@ export function ready(this: PanelThis) { const input = this.$.depsInput as any; this.currentMeta.userData['dep_ext'] = input.value; }) + this.$.autoExclude.addEventListener('confirm', ()=>{ + if (!this.currentMeta) return; + this.currentMeta.userData['auto_exclude'] = !this.currentMeta.userData['auto_exclude'] ? true : undefined; + }) } export function close(this: PanelThis,) { -- Gitee From 44a706f25cbb66596124c71ba414e01f7be45626 Mon Sep 17 00:00:00 2001 From: LyneXiao Date: Thu, 16 Jan 2025 00:23:10 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=8E=92=E9=99=A4?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E4=BC=98=E5=8C=96=EF=BC=9B=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?Bundle=E5=BC=95=E7=94=A8=E8=AE=A1=E6=95=B0=EF=BC=8C=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E5=8A=A0=E8=BD=BD=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/assets/manager/loader/AssetsBundle.ts | 96 +++++++++++++++++++ .../manager/loader/AssetsBundle.ts.meta | 9 ++ .../manager/loader/AssetsBundleManager.ts | 50 ++++++++++ .../loader/AssetsBundleManager.ts.meta | 9 ++ .../assets/manager/loader/LoaderManager.ts | 73 ++++++++++---- extensions/app/assets/manager/ui/UIManager.ts | 4 +- .../engine/dist/builder/utils/dependencies.js | 45 ++++++--- .../engine/src/builder/utils/dependencies.ts | 49 +++++++--- 8 files changed, 288 insertions(+), 47 deletions(-) create mode 100644 extensions/app/assets/manager/loader/AssetsBundle.ts create mode 100644 extensions/app/assets/manager/loader/AssetsBundle.ts.meta create mode 100644 extensions/app/assets/manager/loader/AssetsBundleManager.ts create mode 100644 extensions/app/assets/manager/loader/AssetsBundleManager.ts.meta diff --git a/extensions/app/assets/manager/loader/AssetsBundle.ts b/extensions/app/assets/manager/loader/AssetsBundle.ts new file mode 100644 index 0000000..a730d5e --- /dev/null +++ b/extensions/app/assets/manager/loader/AssetsBundle.ts @@ -0,0 +1,96 @@ +import { AssetManager, assetManager } from 'cc'; + +export type ReleaseCallback = ()=>void; + +export interface IAssetsBundleManager { + getDependencies(name: string): string[]; + + getAssetsBundle(name: string): AssetsBundle|null; + + removeAssetsBundle(name: string): void; +} + +export default class AssetsBundle { + private _manager: IAssetsBundleManager; + private _bundle: AssetManager.Bundle; + private _ref: number; + private _autoRelease: boolean; + + private _onRelease: ReleaseCallback[]; + + get Bundle(): AssetManager.Bundle { + return this._bundle; + } + + constructor(manager: IAssetsBundleManager, bundle: AssetManager.Bundle) { + this._manager = manager; + this._bundle = bundle; + this._ref = 0; + this._autoRelease = true; + // console.warn(`Bundle ${this._bundle.name} has been loaded`); + } + + onRelease(callback: ReleaseCallback): void { + if (!this._onRelease) this._onRelease = []; + this._onRelease.push(callback); + } + + offRelease(callback: ReleaseCallback): void { + if (!this._onRelease) return; + const index: number = this._onRelease.indexOf(callback); + if (index === -1) return; + this._onRelease.splice(index, 1); + } + + setAutoRelease(auto: boolean): void { + this._autoRelease = auto ?? false; + if (this._autoRelease && this._ref <= 0) { + this.releaseInternal(); + } + } + + addRef(): void { + this._ref++; + // 不考虑循环引用的问题,循环引用时,构建会失败 + // 加载顺序出错时这里会出错,LoaderManager中已处理加载顺序 + const manager = this._manager; + for (const dep of manager.getDependencies(this._bundle.name)) { + const depBundle = manager.getAssetsBundle(dep); + if (depBundle) { + depBundle.addRef(); + } else { + console.error(`Bundle ${dep} has not been loaded!`) + } + } + } + + release(): void { + const manager = this._manager; + for (const dep of manager.getDependencies(this._bundle.name)) { + const depBundle = manager.getAssetsBundle(dep); + if (depBundle) { + depBundle.release(); + } + } + this._ref--; + if (!this._autoRelease) return; + if (this._ref <= 0) { + this.releaseInternal(); + } + } + + private releaseInternal(): void { + console.log('>>>>>>>>>>>>. releaseInternal >>>>>>>> ', this._bundle.name, new Error().stack); + this._manager.removeAssetsBundle(this._bundle.name); + this._bundle.releaseAll(); + assetManager.removeBundle(this._bundle); + // console.warn(`Bundle ${this._bundle.name} has already been release`); + if (this._onRelease) { + for (const callback of this._onRelease) { + callback(); + } + this._onRelease.length = 0; + } + } + +} \ No newline at end of file diff --git a/extensions/app/assets/manager/loader/AssetsBundle.ts.meta b/extensions/app/assets/manager/loader/AssetsBundle.ts.meta new file mode 100644 index 0000000..79c6d3f --- /dev/null +++ b/extensions/app/assets/manager/loader/AssetsBundle.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "8fac489e-b911-4cc2-abf0-9d8487b7bd1f", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/extensions/app/assets/manager/loader/AssetsBundleManager.ts b/extensions/app/assets/manager/loader/AssetsBundleManager.ts new file mode 100644 index 0000000..a38e88c --- /dev/null +++ b/extensions/app/assets/manager/loader/AssetsBundleManager.ts @@ -0,0 +1,50 @@ +import { assetManager, AssetManager, path } from 'cc'; +import AssetsBundle, { IAssetsBundleManager } from './AssetsBundle'; + +export default class AssetsBundleManager implements IAssetsBundleManager { + private _deps: Map; + private _bundles: Map; + + constructor(deps: {[key: string]: string[]}){ + this._deps = new Map(); + for (const key in deps) { + this._deps.set(key, deps[key].slice()); + } + this._bundles = new Map(); + } + + hasDependencies(name: string): boolean { + return this._deps.has(name); + } + + getDependencies(name: string): string[] { + return this._deps.get(name) ?? []; + } + + getAssetsBundle(name: string): AssetsBundle | null { + return this._bundles.get(path.basename(name)) ?? null; + } + + addAssetsBundle(name: string, bundle: AssetManager.Bundle): void { + if (this._bundles.has(name)) { + return console.error(`Same name with ${name} bundle is loaded!`) + } + this._bundles.set(name, new AssetsBundle(this, bundle)); + } + + removeAssetsBundle(name: string): void { + if (this._bundles.has(name)) { + this._bundles.delete(name); + } + } + + public release(name: string): void { + const bundle = this.getAssetsBundle(path.basename(name)); + if (bundle) { + bundle.release(); + } else { + const origin = assetManager.getBundle(name); + if (origin) origin.releaseAll(); + } + } +} \ No newline at end of file diff --git a/extensions/app/assets/manager/loader/AssetsBundleManager.ts.meta b/extensions/app/assets/manager/loader/AssetsBundleManager.ts.meta new file mode 100644 index 0000000..2d3fb1e --- /dev/null +++ b/extensions/app/assets/manager/loader/AssetsBundleManager.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "301bd3e5-e36c-4cef-ba19-aa723149916a", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/extensions/app/assets/manager/loader/LoaderManager.ts b/extensions/app/assets/manager/loader/LoaderManager.ts index 18139df..c7a8ab0 100644 --- a/extensions/app/assets/manager/loader/LoaderManager.ts +++ b/extensions/app/assets/manager/loader/LoaderManager.ts @@ -2,16 +2,19 @@ import { Asset, AssetManager, Font, ImageAsset, JsonAsset, Label, SceneAsset, Se import { MINIGAME } from 'cc/env'; import BaseManager from '../../base/BaseManager'; import Core from '../../Core'; +import AssetsBundleManager from './AssetsBundleManager'; +import AssetsBundle from './AssetsBundle'; const { ccclass } = _decorator; const REGEX = /^https?:\/\/.*/; @ccclass('LoaderManager') export default class LoaderManager extends BaseManager { - private _dependencies: {[key: string]: string[]}; + private _assetsBundleManager: AssetsBundleManager; + private _loadingBundles: Map> = new Map(); protected init(finish?: Function): void { const dependencies = settings.querySettings(Settings.Category.ASSETS, 'dependencies') ?? {} as any; - this._dependencies = dependencies; + this._assetsBundleManager = new AssetsBundleManager(dependencies); super.init(finish); } @@ -154,15 +157,11 @@ export default class LoaderManager extends BaseManager { * @param bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名 */ public releaseAll(bundle?: string) { - if (!bundle) bundle = 'resources'; - const _bundle = assetManager.getBundle(bundle); - if (!_bundle) return; - // 只释放自己内部的资源,依赖的资源只减少引用计数 - _bundle.getDirWithPath('/', Asset).forEach((asset) => { - _bundle.release(asset.path, asset.ctor); - }); - // cocos提供的方法会将依赖的资源也卸载(这个设计很奇怪) - // _bundle?.releaseAll(); + if (!bundle) { + assetManager.resources.releaseAll(); + } else { + this._assetsBundleManager.release(bundle); + } } /** @@ -175,15 +174,45 @@ export default class LoaderManager extends BaseManager { assetManager.getBundle(bundle)?.releaseUnusedAssets(); } - private _loadOneBundle(bundle: string, onComplete?: (bundle: AssetManager.Bundle | null) => any) { - assetManager.loadBundle(bundle, (err: Error, result: AssetManager.Bundle) => { - onComplete && onComplete(err ? null : result); + private _loadOneBundlePromise(bundle: string): Promise { + const key = path.basename(bundle); + if (this._loadingBundles.has(key)) { + return this._loadingBundles.get(key); + } + + const loaded = this._assetsBundleManager.getAssetsBundle(bundle); + if (loaded) { + return new Promise((resolve, reject)=>{ + resolve && resolve(loaded.Bundle); + }) + } + + const promise = new Promise((resolve, reject)=>{ + assetManager.loadBundle(bundle, (err: Error, result: AssetManager.Bundle) => { + this._loadingBundles.delete(key); + if (!err) this._assetsBundleManager.addAssetsBundle(path.basename(bundle), result); + resolve && resolve(err ? null : result); + }); }); + this._loadingBundles.set(key, promise); + return promise; + } + + private _loadOneBundle(bundle: string, onComplete?: (bundle: AssetManager.Bundle | null) => any) { + this._loadOneBundlePromise(bundle).then(onComplete); } private _loadBundleInternal(bundle: string, onComplete?: (bundle: AssetManager.Bundle | null) => any) { - if (this._dependencies[bundle]) { - let deps = this._dependencies[bundle]; + const loaded = this._assetsBundleManager.getAssetsBundle(path.basename(bundle)); + if (loaded) { + setTimeout(()=>{ + onComplete && onComplete(loaded.Bundle); + }, 0) + return; + } + + if (this._assetsBundleManager.hasDependencies(bundle)) { + let deps = this._assetsBundleManager.getDependencies(bundle); let preloads = []; for (const dep of deps) { preloads.push((next, retry)=>{ @@ -274,9 +303,19 @@ export default class LoaderManager extends BaseManager { return assetManager.getBundle(bundle); } + /** + * 通过名称获取已加载的AssetBundle + * @param name Bundle的名称 + * @returns + */ + public getAssetsBundle(name: string): AssetsBundle|null { + return this._assetsBundleManager.getAssetsBundle(name); + } + /** * 移除一个已经加载的bundle * @param bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名 + * @deprecated 将会移除,请改用 releaseAll */ public removeBundle(bundle?: string) { if (!bundle) bundle = 'resources'; @@ -324,7 +363,7 @@ export default class LoaderManager extends BaseManager { } this.releaseAll(path.basename(bundle)); - this.removeBundle(path.basename(bundle)); + // this.removeBundle(path.basename(bundle)); const ab = new AssetManager.Bundle(); const config = data.json as any; diff --git a/extensions/app/assets/manager/ui/UIManager.ts b/extensions/app/assets/manager/ui/UIManager.ts index 7b9d23f..0ea58f9 100644 --- a/extensions/app/assets/manager/ui/UIManager.ts +++ b/extensions/app/assets/manager/ui/UIManager.ts @@ -653,8 +653,8 @@ export default class UIManager e const naBundle = this.getNativeBundleName(name); Core.inst.manager.loader.releaseAll(resBundle); Core.inst.manager.loader.releaseAll(naBundle); - Core.inst.manager.loader.removeBundle(resBundle); - Core.inst.manager.loader.removeBundle(naBundle); + // Core.inst.manager.loader.removeBundle(resBundle); + // Core.inst.manager.loader.removeBundle(naBundle); this.log(`卸载: ${name}`); } diff --git a/extensions/app/engine/dist/builder/utils/dependencies.js b/extensions/app/engine/dist/builder/utils/dependencies.js index 4d95cc5..1f24615 100644 --- a/extensions/app/engine/dist/builder/utils/dependencies.js +++ b/extensions/app/engine/dist/builder/utils/dependencies.js @@ -142,7 +142,7 @@ function resaveAllBundleDependencies(dstPath, scriptMoved) { } bundleImports.set(name, allImports); } - // 查找所有bundle的meta文件以添加扩展仍赖 + // 查找所有bundle的meta文件以添加扩展依赖 traverseDirectoryRecursive(Editor.Project.path, (dir, name) => { var _a; const mataPath = dir + ".meta"; @@ -164,10 +164,10 @@ function resaveAllBundleDependencies(dstPath, scriptMoved) { for (const n of exts.split(',')) { const fixName = n.trim(); if (fixName.length != 0) { - if (!deps.has(name)) { - deps.set(name, new Set()); + if (!deps.has(bundleName)) { + deps.set(bundleName, new Set()); } - deps.get(name).add(fixName); + deps.get(bundleName).add(fixName); } } }); @@ -188,6 +188,32 @@ function resaveAllBundleDependencies(dstPath, scriptMoved) { deps.get(name).add(bundle); } } + // 循环检查自动排查的Bundle,将未被依赖的Bundle记录等待移除 + while (autoExcludeBundles.length > 0) { + let allDeps = new Set(); + for (const array of deps.values()) { + for (const element of array) { + allDeps.add(element); + } + } + let dirty = false; + for (let i = autoExcludeBundles.length - 1; i >= 0; i--) { + const exclude = autoExcludeBundles[i]; + if (!allDeps.has(exclude) && deps.has(exclude)) { + deps.delete(exclude); + dirty = true; + } + } + if (!dirty) { + for (let i = autoExcludeBundles.length - 1; i >= 0; i--) { + const exclude = autoExcludeBundles[i]; + if (allDeps.has(exclude)) { + autoExcludeBundles.splice(i, 1); + } + } + break; + } + } // 递归查找所有依赖并检查是否循环依赖 const hasCircle = extendAndCheckCircleDependent(allBundles, deps); let data = {}; @@ -218,16 +244,7 @@ function resaveAllBundleDependencies(dstPath, scriptMoved) { settings.assets['dependencies'] = data; // 排除所有未被依赖且自动排除的Bundle for (const name of autoExcludeBundles) { - let isDep = false; - for (const set of deps.values()) { - if (set.has(name)) { - isDep = true; - break; - } - } - if (!isDep) { - excludeBundle(name, settings, dstPath); - } + excludeBundle(name, settings, dstPath); } fs.writeFileSync(file, JSON.stringify(settings)); } diff --git a/extensions/app/engine/src/builder/utils/dependencies.ts b/extensions/app/engine/src/builder/utils/dependencies.ts index f226af5..49cce4f 100644 --- a/extensions/app/engine/src/builder/utils/dependencies.ts +++ b/extensions/app/engine/src/builder/utils/dependencies.ts @@ -136,7 +136,7 @@ export function resaveAllBundleDependencies(dstPath:string, scriptMoved: boolean bundleImports.set(name, allImports); } - // 查找所有bundle的meta文件以添加扩展仍赖 + // 查找所有bundle的meta文件以添加扩展依赖 traverseDirectoryRecursive(Editor.Project.path, (dir: string, name: string)=>{ const mataPath = dir + ".meta"; if (!fs.existsSync(mataPath)) return; @@ -153,10 +153,10 @@ export function resaveAllBundleDependencies(dstPath:string, scriptMoved: boolean for (const n of exts.split(',')) { const fixName = n.trim(); if (fixName.length != 0) { - if (!deps.has(name)) { - deps.set(name, new Set()); + if (!deps.has(bundleName)) { + deps.set(bundleName, new Set()); } - deps.get(name).add(fixName); + deps.get(bundleName).add(fixName); } } }); @@ -177,6 +177,36 @@ export function resaveAllBundleDependencies(dstPath:string, scriptMoved: boolean } } + // 循环检查自动排查的Bundle,将未被依赖的Bundle记录等待移除 + while (autoExcludeBundles.length > 0) { + let allDeps :Set = new Set(); + for (const array of deps.values()) { + for (const element of array) { + allDeps.add(element); + } + } + + let dirty = false; + for (let i = autoExcludeBundles.length - 1; i >= 0; i--) { + const exclude = autoExcludeBundles[i]; + if (!allDeps.has(exclude) && deps.has(exclude)) { + deps.delete(exclude); + dirty = true; + } + } + + if (!dirty) { + for (let i = autoExcludeBundles.length - 1; i >= 0; i--) { + const exclude = autoExcludeBundles[i]; + if (allDeps.has(exclude)) { + autoExcludeBundles.splice(i, 1); + } + } + + break; + } + } + // 递归查找所有依赖并检查是否循环依赖 const hasCircle = extendAndCheckCircleDependent(allBundles, deps); @@ -209,16 +239,7 @@ export function resaveAllBundleDependencies(dstPath:string, scriptMoved: boolean // 排除所有未被依赖且自动排除的Bundle for (const name of autoExcludeBundles) { - let isDep = false; - for (const set of deps.values()) { - if (set.has(name)) { - isDep = true; - break; - } - } - if (!isDep) { - excludeBundle(name, settings, dstPath); - } + excludeBundle(name, settings, dstPath); } fs.writeFileSync(file, JSON.stringify(settings)); -- Gitee From bcaa2a50cfba2ef63638c6b88d30768b5e977987 Mon Sep 17 00:00:00 2001 From: LyneXiao Date: Mon, 20 Jan 2025 20:22:31 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E6=96=B0=E5=A2=9EBundle=E4=B8=8D=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E4=BE=9D=E8=B5=96=E5=A4=96=E9=83=A8=E6=88=96=E8=A2=AB?= =?UTF-8?q?=E5=A4=96=E9=83=A8=E4=BE=9D=E8=B5=96=E7=9A=84=E7=89=B9=E6=80=A7?= =?UTF-8?q?=EF=BC=9B=E4=BC=98=E5=8C=96Bundle=E6=89=A9=E5=B1=95=E9=80=89?= =?UTF-8?q?=E9=A1=B9=E5=8F=98=E6=9B=B4=E5=90=8E=E8=87=AA=E5=8A=A8=E4=BF=9D?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../engine/dist/builder/utils/dependencies.js | 39 +++++++++++++++--- .../engine/dist/inspector/asset-directory.js | 34 ++++++++++++++- .../engine/src/builder/utils/dependencies.ts | 41 ++++++++++++++++--- .../engine/src/inspector/asset-directory.ts | 31 +++++++++++++- 4 files changed, 130 insertions(+), 15 deletions(-) diff --git a/extensions/app/engine/dist/builder/utils/dependencies.js b/extensions/app/engine/dist/builder/utils/dependencies.js index 1f24615..bb567ee 100644 --- a/extensions/app/engine/dist/builder/utils/dependencies.js +++ b/extensions/app/engine/dist/builder/utils/dependencies.js @@ -109,6 +109,8 @@ function resaveAllBundleDependencies(dstPath, scriptMoved) { let jsBundles = new Map(); let bundleImports = new Map(); let autoExcludeBundles = []; + let cantBeDepBundles = []; + let cantDepOthersBundles = []; for (const name of allBundles) { const bundleVersion = md5Bundles.indexOf(name) !== -1 ? bundleVers[name] : undefined; const isRemote = remoteBundles.indexOf(name) !== -1; @@ -158,6 +160,12 @@ function resaveAllBundleDependencies(dstPath, scriptMoved) { if (userData['auto_exclude']) { autoExcludeBundles.push(bundleName); } + if (userData['cannot_be_dep']) { + cantBeDepBundles.push(bundleName); + } + if (userData['cannot_dep_others']) { + cantDepOthersBundles.push(bundleName); + } const exts = userData['dep_ext']; if (!exts) return; @@ -188,14 +196,33 @@ function resaveAllBundleDependencies(dstPath, scriptMoved) { deps.get(name).add(bundle); } } - // 循环检查自动排查的Bundle,将未被依赖的Bundle记录等待移除 - while (autoExcludeBundles.length > 0) { - let allDeps = new Set(); - for (const array of deps.values()) { - for (const element of array) { - allDeps.add(element); + for (const bn of cantDepOthersBundles) { + if (deps.has(bn) && deps.get(bn).size > 0) + throw new Error(`${bn} cannot be depend other bundlers, but depend on ${[...deps.get(bn)].join(',')}`); + } + while (true) { + let allDeps = new Map(); + for (const kv of deps) { + for (const element of kv[1]) { + if (allDeps.has(element)) { + allDeps.get(element).add(kv[0]); + } + else { + allDeps.set(element, new Set([kv[0]])); + } } } + if (cantBeDepBundles.length > 0) { + for (const bn of cantBeDepBundles) { + if (allDeps.has(bn)) { + throw new Error(`${bn} cannot be depend but ${[...allDeps.get(bn)].join(',')} depend on it!`); + } + } + } + cantBeDepBundles.length = 0; + // 循环检查自动排查的Bundle,将未被依赖的Bundle记录等待移除 + if (autoExcludeBundles.length <= 0) + break; let dirty = false; for (let i = autoExcludeBundles.length - 1; i >= 0; i--) { const exclude = autoExcludeBundles[i]; diff --git a/extensions/app/engine/dist/inspector/asset-directory.js b/extensions/app/engine/dist/inspector/asset-directory.js index 23d2a73..6fef55a 100644 --- a/extensions/app/engine/dist/inspector/asset-directory.js +++ b/extensions/app/engine/dist/inspector/asset-directory.js @@ -9,6 +9,8 @@ exports.$ = { 'deps': '#deps', 'depsInput': '#depsInput', 'autoExclude': '#autoExclude', + 'cantBeDepend': '#cantBeDepend', + 'cantDependOthers': '#cantDependOthers', }; // 添加bundle的自定义依赖,不知道怎么使用list引用 Assets,先用input代替 exports.template = ` @@ -17,6 +19,14 @@ exports.template = ` 是否自动排除 + + 不能被依赖 + + + + 不能依赖外部 + +