版本声明
本文中使用的5.2.33引擎版本
大体思路
- 由于每次更新图集都要手动把资源文件拖一次,为了简化这个部分完成了MyEmitResConfigFilePlugin插件,原来的其实也是可以用的,但是图集的 png 文件也会在里面,而我们手动拖过去是没有的。
原来的
手动托的 - 这个可以使用 egret build 命令运行,但是我每次只更新图集,不想要等那么长时间的 build,于是想办法扩展了命令 egret build res,执行上面的插件
具体实现
重写EmitResConfigFilePlugin
在刚准备重写的时候是有点懵的,因为他原始的结构非常简单,
只实现了plugins.Command 和一个构造,有点无从下手,于是写下了一个这样的类。
export class MyEmitResConfigFilePlugin extends EmitResConfigFilePlugin {
async onFile(file: plugins.File): Promise<plugins.File | null> {
super.onFile(file);
}
async onFinish(pluginContext?: plugins.CommandContext): Promise<void> {
console.log(this);
super.onFinish(pluginContext);
}
}
其中两个方法是 plugins.Command 接口中声明的。
/**
* 构建管线命令
*/
interface Command {
/**
* 项目中的每个文件都会执行此函数,返回 file 表示保留此文件,返回 null 表示将此文件从构建管线中删除,即不会发布
*/
onFile?(file: File): Promise<File | null>
/**
* 项目中所有文件均执行完后,最终会执行此函数。
* 这个函数主要被用于创建新文件
*/
onFinish?(pluginContext?: CommandContext): Promise<void>
[options: string]: any;
}
运行后的打印:发现里面是有资源的一些结构信息的
TestEmitResConfigFilePlugun {
options:
{ output: 'resource/default.res.json',
typeSelector: [Function: typeSelector],
nameSelector: [Function: nameSelector],
groupSelector: [Function: groupSelector] },
config:
{ alias: {},
groups: {},
resources:
{ default_thm_json: [Object],
description_json: [Object],
bg_jpg: [Object],
by_bg_mp3: [Object],
egret_icon_png: [Object],
.....}},
fileSystem:
FileSystem {
root:
{ default_thm_json: [Object],
description_json: [Object],
bg_jpg: [Object],
lobby_json: [Object],
......},
rootPath: 'resource' },
remoteConfig: { alias: {}, groups: {}, resources: {} },
remoteFileSystem: FileSystem { root: {}, rootPath: 'resource' },
remoteRoot: '' }
里面还是有很多东西没有声明出来的,
那我们就直接给他声明出来好了,方便后面的操作,
于是api.d.ts 中的 EmitResConfigFilePlugin 变成了这样:
/**
* 生成 res.json 文件或者 res.js 文件
*/
export class EmitResConfigFilePlugin implements plugins.Command {
protected config: { alias: {}, groups: {}, resources: { [key: string]: {url: string, type: string, name: string}} };
protected fileSystem: { root: {}, rootPath: string };
protected remoteConfig: { alias: {}, groups: {}, resources: { [key: string]: {url: string, type: string, name: string}} };
protected remoteFileSystem: { root: {}, rootPath: string };
protected remoteRoot: string;
constructor(options: EmitResConfigFilePluginOptions)
onFile(file: plugins.File);
onFinish(pluginContext?: plugins.CommandContext);
}
然后我们就可以进行自己想要的剔除图集png的操作了,
这里是在 onFinish() 方法中实现的。
通过上面的 console.log(this),我们知道了资源信息在
this.config.resources 和 this.fileSystem.root 中
那我们把这两个中的剔除就好了。
这里把 this.remoteConfig.resources 和 this.remoteFileSystem.root 也进行了检查
上代码:
async onFinish(pluginContext?: plugins.CommandContext): Promise<void> {
// 需要移除的
let needRemoveArr = [];
// console.log(this.config)
let setSubkeys = (resources, root) => {
for (let key in resources) {
let item = resources[key];
if (item.type == "sheet") {
// 读取json文件
let frames = (readFileSyncJson(root + item.url) as { file: string, frames: {} }).frames;
if (frames) {
needRemoveArr.push(item);
let keys: string[] = [];
// key为图片名称
for (let key in frames) {
keys.push(key);
}
// 写入二级key
item.subkeys = keys.join(",");
}
// 防止之前的类型转换出错
else if (item.type == "sheet") {
item.type = "json";
}
}
}
}
// 移除png
let reomveSheetPngFile = (resources, root) => {
for (let k in needRemoveArr) {
let name: string = needRemoveArr[k].name;
let pngName = name.replace(/_json/, '_png');
delete resources[pngName];
delete root[pngName];
}
}
let outputDir = pluginContext ? pluginContext.outputDir : "";
setSubkeys(this.config.resources, outputDir + "/" + this.fileSystem.rootPath + "/");
reomveSheetPngFile(this.config.resources, this.fileSystem.root);
needRemoveArr.length = 0;
setSubkeys(this.remoteConfig.resources, this.remoteRoot + "/" + this.remoteFileSystem.rootPath + "/");
reomveSheetPngFile(this.remoteConfig.resources, this.remoteFileSystem.root);
super.onFinish(pluginContext);
}
其中用到了读取文件 readFileSyncJson() 方法,这是自己封装的,
自己新建一个类,这里的名为:fileUtil.ts
import * as fs from 'fs';
import * as path from 'path';
export function readFileSyncJson(filePath: string) {
let text = readFileSyncText(filePath);
return text ? JSON.parse(text) : undefined;
}
最后把这个类在 config.ts 中替换掉原来的 EmitResConfigFilePlugin,
并把 typeSelector 方法按照自己的需要修改返回类型就完成了。
typeSelector: (path) => {
const ext = path.substr(path.lastIndexOf(".") + 1);
const typeMap = {
"jpg": "image",
"png": "image",
"webp": "image",
"json": "json",
"fnt": "font",
"pvr": "pvr",
"mp3": "sound",
"zip": "zip",
"sheet": "sheet",
"exml": "text",
"txt": "text",
"dbbin": "bin",
}
let type = typeMap[ext];
if (type == "json") {
if (type.indexOf('_tex') == -1) {
type = "sheet";
} else if (path.indexOf("movieclip") >= 0) {
type = "movieclip";
}
}
return type;
}
其中 onFile 和 onFinish 两个方法中都要用 super 调用父类的实现。同时我们可以在onFile() 中去对是否写入进行筛选。
async onFile(file: plugins.File) {
if (file.path.indexOf('resource\\asset') == -1) {
return null;
}
super.onFile(file);
return file;
}
扩展命令
经过上面的操作就可以在 egret build 命令中执行了,但是如果项目大的话每次
build 需要的时间也是很久的,在官方论坛中看到 egret 的命令是用 nood.js 实现的,
于是就想试着打印一下,看看能不能获取到执行的命令,最后找到了两地方都可以拿到
分别是:global['egret'].args,global.process.argv,可以打印出来自己看下
于是就根据后面拿到的去做自己想要的处理,这里使用的
global['egret'].args.commands[1] 获取指令
buildConfig: (params) => {
const { target, command, projectName, version } = params;
if (command == 'build') {
const com = global['egret'].args.commands[1];
const outputDir = '.';
if (com == 'res') {
return {
outputDir: '',
commands: [
new TestEmitResConfigFilePlugun({
output: "resource/default.res.json",
typeSelector: config.typeSelector,
nameSelector: p => path.basename(p).replace(/\./gi, "_"),
groupSelector: p => ''
}),
]
}
}
return {
outputDir,
commands: [
new ExmlPlugin('debug'), // 非 EUI 项目关闭此设置
new IncrementCompilePlugin(),
new TestEmitResConfigFilePlugun({
output: "resource/default.res.json",
typeSelector: config.typeSelector,
nameSelector: p => path.basename(p).replace(/\./gi, "_"),
groupSelector: p => ''
}),
]
}
}
大家可以根据这个扩展自己的订制命令了~