feat: add builder configuration file

feat: add cocos-service configuration file
feat: add device configuration file
feat: add engine configuration file
feat: add information configuration file
feat: add program configuration file
feat: add project configuration file
feat: add TypeScript configuration file
This commit is contained in:
ZhouXiao
2025-12-22 11:42:51 +08:00
parent 66cfa73345
commit 487c68994d
202 changed files with 57615 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 McvCar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,2 @@
# quickCompressImage
这是cocos creator 快速压缩图片插件

View File

@@ -0,0 +1,9 @@
module.exports = {
/**
* menu
*/
'name': 'x',
'compressPicture': 'Compress Picture',
'menu': 'Compress Picture',
'setting': 'Setting',
};

View File

@@ -0,0 +1,9 @@
module.exports = {
/**
* 菜单
*/
'name': 'x',
'compressPicture': '压缩图片',
'menu': '快闪·压缩图片',
'setting': '设置',
};

View File

@@ -0,0 +1,50 @@
{
"name": "mozjpeg",
"version": "8.0.0",
"description": "mozjpeg wrapper that makes it seamlessly available as a local dependency",
"license": "MIT",
"repository": "imagemin/mozjpeg-bin",
"type": "module",
"exports": "./index.js",
"bin": "cli.js",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"scripts": {
"postinstall": "node lib/install.js",
"test": "xo && ava --timeout=120s",
"build-linux": "docker build --tag imagemin/mozjpeg docker && docker run --rm --volume $(pwd)/vendor/linux:/src/out imagemin/mozjpeg cp cjpeg /src/out"
},
"files": [
"index.js",
"cli.js",
"lib",
"vendor/source"
],
"keywords": [
"imagemin",
"jpeg",
"jpg",
"img",
"image",
"compress",
"minify",
"mozjpeg",
"optimize"
],
"dependencies": {
"bin-build": "^3.0.0",
"bin-wrapper": "^4.0.0"
},
"devDependencies": {
"ava": "^3.8.0",
"bin-check": "^4.1.0",
"compare-size": "^3.0.0",
"execa": "^5.1.1",
"tempy": "^2.0.0",
"xo": "^0.45.0"
},
"ava": {
"serial": true
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
/*!
* fill-range <https://github.com/jonschlinkert/fill-range>
*
* Copyright (c) 2014-present, Jon Schlinkert.
* Licensed under the MIT License.
*/
/*!
* is-extglob <https://github.com/jonschlinkert/is-extglob>
*
* Copyright (c) 2014-2016, Jon Schlinkert.
* Licensed under the MIT License.
*/
/*!
* is-glob <https://github.com/jonschlinkert/is-glob>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
/*!
* is-number <https://github.com/jonschlinkert/is-number>
*
* Copyright (c) 2014-present, Jon Schlinkert.
* Released under the MIT License.
*/
/*!
* to-regex-range <https://github.com/micromatch/to-regex-range>
*
* Copyright (c) 2015-present, Jon Schlinkert.
* Released under the MIT License.
*/
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! queue-microtask. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! run-parallel. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

View File

@@ -0,0 +1,77 @@
let Path = require('path')
let Fs = require('fs');
let imageminApi = require('../lib/imagemin.min')
let packageCfg = require('../package.json')
module.exports = {
async getZipRate(){
let rate = parseInt(await Editor.Profile.getConfig(packageCfg.name,'zipRate')) || 30;
if(rate<0){
rate = 1;
}else if(rate>100){
rate = 100
}
return rate;
},
/**
* 调用压缩工具api
* @param {Array<{file:string,uuid:string}>} fileInfo
*/
async compressPicture(arrList,imageFileList){
let rate = await this.getZipRate();
let pngRate = rate*0.01;
console.log("压缩值:",rate+"%");
imageminApi.imagemin(arrList, {
plugins: [
imageminApi.imageminMozjpeg({ quality: rate }), //压缩质量0,1
imageminApi.imageminPngquant({
quality: [pngRate, Math.min(pngRate+0.25,1)] //压缩质量0,1
})
]
}).then((arrRes) => {
console.log("压缩成功,详情:\n")
for (let i = 0; i < arrRes.length; i++) {
const res = arrRes[i];
this.onCompressedSucceed(imageFileList,res)
}
}).catch(err => {
console.log("压缩失败:",err)
});
},
/**
* 压缩成功
* @param {Array<{file:string,uuid:string,size:number}>} imageFileList
* @param {Object<{data:Buffer,sourcePath:string}>} res
*/
onCompressedSucceed(imageFileList,res){
let desc = ""
for (let i = 0; i < imageFileList.length; i++) {
const fileInfo = imageFileList[i];
if(fileInfo.file.replace(/\\/g,'/') == res.sourcePath){
const fileName = Path.basename(fileInfo.file);
const newSize = res.data.byteLength;
const rate = (fileInfo.size-newSize)/fileInfo.size;
const oldMb = (fileInfo.size / 1024).toFixed(1) + " KB";
const newMb = (newSize / 1024).toFixed(1) + " KB";
if(newSize < fileInfo.size){
Fs.writeFileSync(res.sourcePath, res.data)
desc += `${fileName}、压缩率:${parseInt(rate*100)}%、压缩前后大小:${newMb} / ${oldMb}`
}else{
desc += `${fileName}、无法继续压缩`
}
break;
}
}
console.log(desc);
},
}

View File

@@ -0,0 +1,89 @@
const fs = require("fs");
const path = require("path");
const https = require("https");
const URL = require("url").URL;
const EventEmitter = require("events");
const err = (msg) => new EventEmitter().emit("error", msg);
/**
* TinyPng 远程压缩 HTTPS 请求的配置生成方法
*/
function getAjaxOptions() {
return {
method: "POST",
hostname: "tinypng.com",
path: "/web/shrink",
headers: {
rejectUnauthorized: false,
"X-Forwarded-For": Array(4)
.fill(1)
.map(() => parseInt(Math.random() * 254 + 1))
.join("."),
"Postman-Token": Date.now(),
"Cache-Control": "no-cache",
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent":
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
}
};
}
/**
* TinyPng 远程压缩 HTTPS 请求
* @param {string} img 待处理的文件
* @success {
* "input": { "size": 887, "type": "image/png" },
* "output": { "size": 785, "type": "image/png", "width": 81, "height": 81, "ratio": 0.885, "url": "https://tinypng.com/web/output/7aztz90nq5p9545zch8gjzqg5ubdatd6" }
* }
* @error {"error": "Bad request", "message" : "Request is invalid"}
*/
function fileUpload(imgPath) {
let req = https.request(getAjaxOptions(), (res) => {
res.on("data", (buf) => {
let obj = JSON.parse(buf.toString());
if (obj.error)
console.log(`压缩失败!\n 当前文件:${imgPath} \n ${obj.message}`);
else fileUpdate(imgPath, obj);
});
});
req.write(fs.readFileSync(imgPath), "binary");
req.on("error", (e) =>
console.log(`请求错误! 当前文件:${path.basename(imgPath)} \n`, e)
);
req.end();
}
// 该方法被循环调用,请求图片数据
function fileUpdate(entryImgPath, obj) {
let options = new URL(obj.output.url);
let req = https.request(options, (res) => {
let body = "";
res.setEncoding("binary");
res.on("data", (data) => (body += data));
res.on("end", () => {
fs.writeFile(entryImgPath, body, "binary", (err) => {
if (err) return console.error(err);
let log = `压缩成功:`;
log += `${path.basename(entryImgPath)}、压缩率:${ ((1 - obj.output.ratio) * 100).toFixed(2) }%、压缩前后大小:${(obj.output.size / 1024).toFixed(2)} / ${(obj.input.size / 1024).toFixed(2)}`
console.log(log);
});
});
});
req.on("error", (e) => console.warn('压缩失败:',path.basename(entryImgPath) ,e));
req.end();
}
module.exports = {
/**
* 调用压缩工具api
* @param {Array<{file:string,uuid:string}>} fileInfo
*/
async compressPicture(arrList,imageFileList){
for (let i = 0; i < arrList.length; i++) {
const imgPath = arrList[i];
fileUpload(imgPath);
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
{
"name": "quick-compress-image",
"package_version": 2,
"version": "1.0.1",
"description": "Compress a picture",
"author": "嘉年华",
"main": "./src/browser.js",
"contributions": {
"menu": [{
"path": "i18n:menu.extension/i18n:quick-compress-image.menu",
"label": "i18n:quick-compress-image.setting",
"message": "setting"
}],
"messages": {
"setting": {
"methods": ["setting"]
}
},
"assets": {
"menu": {
"methods": "./src/assets-menu.js",
"createMenu": "onCreateMenu",
"assetMenu": "onAssetMenu"
}
},
"profile": {
"editor": {
"zipRate": {
"default": 70,
"label": "压缩值(0-100%)"
},
"zipMode":{
"default": 0,
"label": "压缩模式"
}
}
},
"preferences": {
"label" : "图片压缩",
"properties": {
"zipRate": {
"ui": "ui-num-input"
},
"zipMode": {
"ui": "ui-select",
"items": [
{
"value": 0,
"label": "Imagemin"
},
{
"value": 1,
"label": "Tinypng"
}
]
}
}
}
},
"panels": {
"default": {
"title": "快闪·压缩图片",
"type": "dockable",
"main": "src/setting-panel",
"size": {
"width": 280,
"height": 260
}
}
},
"dependencies": {
},
"devDependencies": {
}
}

View File

@@ -0,0 +1,113 @@
let Path = require('path')
let Fs = require('fs');
let imageminApi = require('../lib/imageminApi')
let tinypngApi = require('../lib/tinypngApi')
let statistical = require('./tools/statistical')
let packageCfg = require('../package.json');
const tools = require('./tools/tools');
let AssetsMenu = {
onAssetMenu(assetInfo) {
if(assetInfo.importer != 'image'){
return [];
}
return [
{
label: 'i18n:quick-compress-image.compressPicture',
enabled: true,
click() {
if(confirm('确定压缩图片?')){
AssetsMenu.onStartCompressPicture(assetInfo)
}
},
},
];
},
onCreateMenu(assetInfo) {
statistical.countStartupTimes();
return [];
},
isImageFile(filePath){
let extname = Path.extname(filePath).toLocaleLowerCase()
if(extname == '.png' || extname == '.jpg' || extname == '.jpeg'){
return true;
}
return false;
},
getFileSize(fsPath){
try {
return Fs.statSync(fsPath).size
} catch (error) {
return -1
}
},
/**
* 返回选中的图片
* @param {*} assetInfo
* @returns {Promise<Array<{file:string,uuid:string,size:number}>>}
*/
async getImageFileList(assetInfo){
let fileUuidList = Editor.Selection.getSelected('asset');
let imageFileList = [];
if(fileUuidList.includes(assetInfo.uuid)){
for (let i = 0; i < fileUuidList.length; i++) {
const uuid = fileUuidList[i];
const fsPath = await Editor.Message.request("asset-db",'query-path',uuid);
if(fsPath && this.isImageFile(fsPath)){
imageFileList.push({
file: fsPath,
uuid: uuid,
size: this.getFileSize(fsPath)
})
}
}
}else{
if(assetInfo.file && this.isImageFile(assetInfo.file)){
imageFileList.push({
file: assetInfo.file,
uuid: assetInfo.uuid,
})
}
}
return imageFileList;
},
async getMode(){
let mode = tools.isX64() ? await Editor.Profile.getConfig(packageCfg.name,'zipMode') || 0 : 1;
return mode;
},
async onStartCompressPicture(assetInfo){
let imageFileList = await this.getImageFileList(assetInfo);
if(imageFileList.length == 0){
return console.log("不支持该图片格式的压缩")
}
let arrList = [];
for (let i = 0; i < imageFileList.length; i++) {
const fileInfo = imageFileList[i];
arrList.push(fileInfo.file);
}
let mode = await this.getMode();
if(mode == 0){
console.log("---------------------压缩模式:Imagemin---------------------------");
imageminApi.compressPicture(arrList,imageFileList);
}else{
console.log("---------------------压缩模式:TinyPng---------------------------");
tinypngApi.compressPicture(arrList,imageFileList);
}
},
}
module.exports = AssetsMenu;

View File

@@ -0,0 +1,15 @@
"use strict";
exports.methods={
'setting'(){
Editor.Panel.open('quick-compress-image')
}
}
exports.load=function(){
}
exports.unload=function(){
};

View File

@@ -0,0 +1,58 @@
let fs = require('fs')
let path = require('path')
let packageCfg = require('../package.json')
const tools = require('./tools/tools')
Editor.Panel.define = Editor.Panel.define || ((args)=>{return args})
function saveConfig(doms){
Editor.Profile.setConfig(packageCfg.name,'zipRate',doms.zipRateSlider.value);
Editor.Profile.setConfig(packageCfg.name,'zipMode',doms.zipModeTab.value);
}
async function getMode(){
let mode = tools.isX64() ? await Editor.Profile.getConfig(packageCfg.name,'zipMode') || 0 : 1;
return mode;
}
module.exports = Editor.Panel.define({
template: fs.readFileSync(path.join(__dirname, '../template/setting.html'), 'utf-8'),
style: fs.readFileSync(path.join(__dirname, '../template/setting.css'), 'utf-8'),
$: {
zipRateSlider: '#zipRateSlider',
saveBtn: '#saveBtn',
zipModeTab: '#zipModeTab',
},
async ready() {
this.$.zipRateSlider.value = parseInt(await Editor.Profile.getConfig(packageCfg.name,'zipRate')) || 30;
this.$.zipModeTab.value = await getMode();
this.$.zipModeTab.value == 1 ? this.$.zipRateSlider.disabled = true : this.$.zipRateSlider.removeAttribute('disabled');
this.$.zipModeTab.addEventListener('click',()=>{
let mode = this.$.zipModeTab.value;
if(!tools.isX64() && mode == 0){
this.$.zipModeTab.value = 1;
this.$.zipRateSlider.disabled = true
alert('CPU不支持该模式');
}else{
this.$.zipModeTab.value == 1 ? this.$.zipRateSlider.disabled = true : this.$.zipRateSlider.removeAttribute('disabled');
saveConfig(this.$);
}
},0)
this.$.saveBtn.addEventListener('click',()=>{
saveConfig(this.$);
},0)
},
listeners: {
},
methods: {
},
beforeClose() { },
close() {
saveConfig(this.$);
},
});

View File

@@ -0,0 +1,20 @@
const tools = require("./tools")
let isStatistical = false;
module.exports = {
// 用户使用次数统计
countStartupTimes(){
if(!isStatistical && Editor.User && Editor.User.getData){
Editor.User.getData().then((post_data)=>{
if(!post_data) {
return
}
post_data.version = Editor.App ? Editor.App.version : "?";
tools.httpPost('120.77.174.207','/compressImageLogincount',8081,post_data);
});
isStatistical = true;
}
},
}

View File

@@ -0,0 +1,560 @@
let fs = require("fs");
let path = require("path");
let packageCfg = require("../../package.json")
var http = require('http');
var querystring = require('querystring');
var os = require('os');
const inputType = { "text": 1, "password": 1, "number": 1, "date": 1, "color": 1, "range": 1, "month": 1, "week": 1, "time": 1, "email": 1, "search": 1, "url": 1, "textarea": 1 }
let checkFsPath = new RegExp("\\.\\./", "g");
let readFileQueue = []
let readFileMaxCount = 50
let readFileCount = 0
let translateMap = {}
module.exports = {
isX64(){
return os.arch() == 'x64'
},
// 不是输入状态是时
inputTypeChk(e) {
if (e.path[0]) {
let type = e.path[0].type;
if (inputType[type]) {
return true
}
}
},
getLanguage(){
return window.navigator && window.navigator.language && window.navigator.language.split('-')[0];
},
// 更新i18翻译文本解决creator不重启不会刷新修改问题
initI18t(){
let locale = this.getLanguage() || 'zh';
let filePath = Editor2D.url('packages://simple-code/i18n/'+locale+'.js');
if(!this.isFileExit(filePath)){
return
}
let mapList = require(filePath);
let converList = {}
for (const key in mapList) {
const converText = mapList[key];
converList[packageCfg.name+'.'+key] = converText
}
translateMap = converList;
// Editor.i18n.extend(converList)
},
translate(key){
let name = packageCfg.name+'.'+key
return translateMap[name] || Editor2D.T(name) || name
},
translateZhAndEn(zeText,enText){
return this.getLanguage() == 'zh' ? zeText : enText;
},
// 翻译中英文
T(zeText,enText){
return this.translateZhAndEn(zeText,enText);
},
isEmptyObject(obj){
if(obj){
for (const key in obj) {
return false
}
}
return true;
},
// 拷贝本对象方法到目标对象
// newObj 子类
// baseObj 父类
// mergeFuncs = ["init"]; 新旧类的同名函数合并一起
extendTo(newObj, baseObj, mergeFuncs = []) {
if (!baseObj || !newObj) return;
for (let k in baseObj) {
let v = baseObj[k]
if (newObj[k] == null) {
newObj[k] = v
}
// 函数继承使用 "this._super()" 调用父类
else if (typeof v == "function" && typeof newObj[k] == "function" && !newObj[k]._isExend) {
let newFunc = newObj[k];
newObj[k] = function () {
this._super = v;
let ret = newFunc.apply(this, arguments);// 执行函数并传入传参
delete this._super;
return ret;
};
newObj[k]._isExend = true
}
}
},
copyToClipboard(str) {
var input = str;
const el = document.createElement('textarea');
el.value = input;
el.setAttribute('readonly', '');
el.style.contain = 'strict';
el.style.position = 'absolute';
el.style.left = '-9999px';
el.style.fontSize = '12pt'; // Prevent zooming on iOS
const selection = getSelection();
var originalRange = false;
if (selection.rangeCount > 0) {
originalRange = selection.getRangeAt(0);
}
document.body.appendChild(el);
el.select();
el.selectionStart = 0;
el.selectionEnd = input.length;
var success = false;
try {
success = document.execCommand('copy');
} catch (err) { }
document.body.removeChild(el);
if (originalRange) {
selection.removeAllRanges();
selection.addRange(originalRange);
}
return success;
},
parseJson(text){
try {
return JSON.parse(text)
} catch (error) {
return undefined;
}
},
objectCount(obj){
let len = 0
for (const key in obj) {
len++
}
return len;
},
// 获得import路径
getImportStringPaths(codeText) {
var regEx = /(require\(|import |reference path=)(.{0,}['"])(.+)['"]/g;
var match = regEx.exec(codeText);
var imports = []
while (match) {
let start = match.index + match[1].length + match[2].length;
imports.push({
path: match[3],
start: start,
length: match[3].length,
})
match = regEx.exec(codeText);
}
return imports
},
//将相对路径转为绝对路径
relativePathTofsPath(absolutePath, relativePath) {
var uplayCount = 0; // 相对路径中返回上层的次数。
var m = relativePath.match(checkFsPath);
if (m) uplayCount = m.length;
var lastIndex = absolutePath.length - 1;
var subString = absolutePath
for (var i = 0; i <= uplayCount; i++) {
lastIndex = subString.lastIndexOf("/", lastIndex);
subString = subString.substr(0, lastIndex)
}
return this.normPath( subString + "/" + relativePath.substr(relativePath.lastIndexOf('./') + 2));
},
//将绝对路径转为相对路径
fsPathToRelativePath(currPath, importPath) {
let s_i = currPath.lastIndexOf('/')
if (s_i != -1) currPath = currPath.substr(0, s_i);
let relativePath = path.relative(currPath, importPath);
if (relativePath[0] != '.') {
relativePath = './' + relativePath;
}
return this.normPath(relativePath);
},
//转换相对路径
converRelative(relativePath, oldFilePath, newFilePath) {
let s_i = oldFilePath.lastIndexOf('/')
if (s_i != -1) oldFilePath = oldFilePath.substr(0, s_i);
s_i = newFilePath.lastIndexOf('/')
if (s_i != -1) newFilePath = newFilePath.substr(0, s_i);
let rve_to_abs = this.normPath(path.resolve(oldFilePath, relativePath));
relativePath = this.normPath(path.relative(newFilePath, rve_to_abs));
if (relativePath[0] != '.') {
relativePath = './' + relativePath;
}
return relativePath;
},
normPath(filePath) {
return filePath.replace(/\\/g, '/');
},
// 异步读取文件
readFileAsyn(filePath,callback)
{
let args = {filePath,callback};
readFileQueue.push(args)
// 最多同时读取50个文件
readFileCount++;
if(readFileCount >= readFileMaxCount){
// console.log("readFileAsyn:读取超出最大文件数量",readFileCount);
return;
}
this._handleReadFileQueue()
},
// 处理 readFileAsyn 队列
_handleReadFileQueue(){
// 1 == Math.min(50,1)
let len = Math.min( readFileMaxCount , readFileQueue.length );
for (let i = 0; i < len; i++) {
// 处理队列
let args = readFileQueue.splice(0,1)[0];
fs.readFile(args.filePath,(err,data)=>
{
readFileCount--
if(readFileCount > 500){
setTimeout(this._handleReadFileQueue.bind(this),100)
}else{
this._handleReadFileQueue()
}
try {
args.callback(err,data);
} catch (error) {
console.log(error)
}
})
}
},
copyFile(sourcePath, toPath) {
fs.writeFileSync(toPath, fs.readFileSync(sourcePath))
},
// copyFile(sourcePath,toPath){
// fs.readFile(sourcePath,function(err,data){
// if(err) throw new Error('复制失败:'+sourcePath+" TO "+data);
// fs.writeFile(toPath,data,function(err){
// if(err) throw new Error('复制写入失败'+sourcePath+" TO "+data);
// })
// })
// },
moveDir(sourcePath, toPath) {
if (!fs.existsSync(sourcePath)) {
console.log("不存在目录:", sourcePath);
return;
}
if (sourcePath[sourcePath.length - 1] != path.sep) {
sourcePath += path.sep;// 加猴嘴
}
if (toPath[toPath.length - 1] != path.sep) {
toPath += path.sep;// 加猴嘴
}
let list = this.getDirAllFiles(sourcePath, []);
list.forEach((fileName, i) => {
let toFilePath = fileName.replace(sourcePath, toPath);
console.log("执行:", fileName, toFilePath);
let dirName = path.dirname(toFilePath);
this.createDir(dirName);
// 移动文件
fs.renameSync(fileName, toFilePath);
})
},
createDir(dirPath) {
if (fs.existsSync(dirPath)) return;
let paths = dirPath.split(path.sep);//分割路径
let path_ = "";
// c:\
let n = 0
let max = paths.length
if (paths[0].indexOf(":") != -1) {
path_ = paths[0];
n++;
}
if (paths[max - 1].indexOf(".") != -1) {
max--;
}
for (n; n < max; n++) {
path_ += path.sep + paths[n];
if (!fs.existsSync(path_)) {
fs.mkdirSync(path_);
}
}
},
// 获得文件夹列表
getDirList(dirPath, result) {
let files = fs.readdirSync(dirPath);
files.forEach((val, index) => {
let fPath = path.join(dirPath, val);
if (fs.existsSync(fPath) && fs.statSync(fPath).isDirectory()) {
result.push(fPath);
}
});
return result;
},
// 获得文件列表
getFileList(dirPath, result = []) {
let files = fs.readdirSync(dirPath);
files.forEach((val, index) => {
let fPath = path.join(dirPath, val);
if (fs.existsSync(fPath) && fs.statSync(fPath).isFile()) {
result.push(fPath);
}
});
return result;
},
isDirectory(fPath) {
return fs.existsSync(fPath) && fs.statSync(fPath).isDirectory()
},
getDirAllFiles(dirPath, result = []) {
let files = fs.readdirSync(dirPath);
files.forEach((val, index) => {
let fPath = path.join(dirPath, val);
if (fs.existsSync(fPath) && fs.statSync(fPath).isDirectory()) {
this.getDirAllFiles(fPath, result);
} else if (fs.statSync(fPath).isFile()) {
result.push(fPath);
}
});
return result;
},
getFileString(fileList, options) {
let curIndex = 0;
let totalIndex = fileList.length;
let str = {};
for (let key in fileList) {
let filePath = fileList[key];
let b = this._isFileExit(filePath);
if (b) {
fs.readFile(filePath, 'utf-8', function (err, data) {
if (!err) {
self._collectString(data, str);
} else {
console.log("error: " + filePath);
}
self._onCollectStep(filePath, ++curIndex, totalIndex, str, options);
})
} else {
self._onCollectStep(filePath, ++curIndex, totalIndex, str, options);
}
}
},
_onCollectStep(filePath, cur, total, str, data) {
if (data && data.stepCb) {
data.stepCb(filePath, cur, total);
}
if (cur >= total) {
self._onCollectOver(str, data);
}
},
_onCollectOver(collectObjArr, data) {
let strArr = [];
let str = "";
for (let k in collectObjArr) {
str += k;
strArr.push(k);
}
// console.log("一共有" + strArr.length + "个字符, " + strArr);
console.log("一共有" + strArr.length + "个字符");
if (data && data.compCb) {
data.compCb(str);
}
},
mkDir(path) {
try {
fs.mkdirSync(path);
} catch (e) {
if (e.code !== 'EEXIST') throw e;
}
},
isFileExit(file) {
try {
fs.accessSync(file, fs.F_OK);
} catch (e) {
return false;
}
return true;
},
async isFileExitAsync(file) {
return new Promise((resolev)=>{
fs.access(file,(err)=>{
resolev(err == null);
});
});
},
_collectString(data, collectObject) {
for (let i in data) {
let char = data.charAt(i);
if (collectObject[char]) {
collectObject[char]++;
} else {
collectObject[char] = 1;
}
}
},
emptyDir(rootFile) {
//删除所有的文件(将所有文件夹置空)
let emptyDir = function (fileUrl) {
let files = fs.readdirSync(fileUrl);//读取该文件夹
for (let k in files) {
let filePath = path.join(fileUrl, files[k]);
let stats = fs.statSync(filePath);
if (stats.isDirectory()) {
emptyDir(filePath);
} else {
fs.unlinkSync(filePath);
console.log("删除文件:" + filePath);
}
}
};
//删除所有的空文件夹
let rmEmptyDir = function (fileUrl) {
let files = fs.readdirSync(fileUrl);
if (files.length > 0) {
for (let k in files) {
let rmDir = path.join(fileUrl, files[k]);
rmEmptyDir(rmDir);
}
if (fileUrl !== rootFile) {// 不删除根目录
fs.rmdirSync(fileUrl);
console.log('删除空文件夹' + fileUrl);
}
} else {
if (fileUrl !== rootFile) {// 不删除根目录
fs.rmdirSync(fileUrl);
console.log('删除空文件夹' + fileUrl);
}
}
};
emptyDir(rootFile);
rmEmptyDir(rootFile);
},
/*
is_fileType($('#uploadfile').val(), 'doc,pdf,txt,wps,odf,md,png,gif,jpg')
* */
is_fileType(filename, types) {
types = types.split(',');
let pattern = '\.(';
for (let i = 0; i < types.length; i++) {
if (0 !== i) {
pattern += '|';
}
pattern += types[i].trim();
}
pattern += ')$';
return new RegExp(pattern, 'i').test(filename);
},
getFileName(filePath) {
let s_i = filePath.lastIndexOf('/');
if (s_i == -1) s_i = filePath.lastIndexOf('\\');
let name = filePath
if (s_i != -1) name = name.substr(s_i + 1)
s_i = name.lastIndexOf('.');
if (s_i != -1) {
name = name.substr(0, s_i)
}
return name;
},
getFileExtname(filePath) {
let s_i = filePath.lastIndexOf('.');
let extname = ""
if (s_i != -1) {
extname = filePath.substr(s_i).toLowerCase()
}
return extname;
},
getUrlInfo(url) {
let s_i = url.lastIndexOf('/');
if (s_i == -1) s_i = url.lastIndexOf('\\');
let name = ""
if (s_i != -1) name = url.substr(s_i + 1)
s_i = name.lastIndexOf('.');
let extname = ""
if (s_i != -1) {
extname = name.substr(s_i).toLowerCase()
}
return { name, extname, url }
},
httpPost(ip,path,port,args,callback){
var options = {
hostname: ip,
port: port,
path: path,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
};
var req = http.request(options, function (res) {
// console.log('STATUS: ' + res.statusCode);
// console.log('HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
if(callback) callback(chunk);
});
});
req.on('error', function (e) {
if(callback) callback();
});
// write data to request body
var content = querystring.stringify(args);
req.write(content);
req.end();
},
}

View File

@@ -0,0 +1,136 @@
const fetch = require('./node-fetch');
/**
* 更新器
*/
const Updater = {
/**
* 远端地址
* @type {string}
*/
remoteUrl: 'https://gitee.com/mcv/simple-code-describe',
/**
* 获取远端的 package.json
* @returns {Promise<object>}
*/
async getRemotePackageJson() {
const packageJsonUrl = `${this.remoteUrl}/raw/master/package3.json`;
// 发起网络请求
const response = await fetch(packageJsonUrl, {
method: 'GET',
cache: 'no-cache',
mode: 'no-cors',
});
// 请求结果
if (response.status !== 200) {
return null;
}
// 读取 json
const json = response.json();
return json;
},
async getRemoteDesc() {
const descUrl = `${this.remoteUrl}/raw/master/README3.md`;
// 发起网络请求
const response = await fetch(descUrl, {
method: 'GET',
cache: 'no-cache',
mode: 'no-cors',
});
// 请求结果
if (response.status !== 200) {
return '';
}
const text = response.text();
return text;
},
/**
* 获取远端版本号
* @returns {Promise<string>}
*/
async getRemoteVersion() {
const package = await this.getRemotePackageJson();
if (!package) {
return null;
}
const version = package.version || null;
return version;
},
/**
* 获取本地版本号
* @returns {string}
*/
getLocalVersion() {
return require('../package.json').version;
},
/**
* 拆分版本号
* @param {string} version 版本号文本
* @returns {number[]}
* @example
* splitVersionString('1.2.0'); // [1, 2, 0]
*/
splitVersionString(version) {
return (
version.replace(/-/g, '.')
.split('.')
.map(v => (parseInt(v) || 0))
);
},
/**
* 对比版本号
* @param {string} a 版本 a
* @param {string} b 版本 b
* @returns {-1 | 0 | 1}
* @example
* compareVersion('1.0.0', '1.0.1'); // -1
* compareVersion('1.1.0', '1.1.0'); // 0
* compareVersion('1.2.1', '1.2.0'); // 1
* compareVersion('1.2.0.1', '1.2.0'); // 1
*/
compareVersion(a, b) {
const acs = this.splitVersionString(a),
bcs = this.splitVersionString(b);
const count = Math.max(acs.length, bcs.length);
for (let i = 0; i < count; i++) {
const ac = acs[i],
bc = bcs[i];
// 前者缺少分量或前者小于后者
if (ac == null || ac < bc) {
return -1;
}
// 后者缺少分量或前者大于后者
if (bc == null || ac > bc) {
return 1;
}
}
return 0;
},
/**
* 检查远端是否有新版本
* @returns {Promise<boolean>}
*/
async check() {
// 远端版本号
const remoteVersion = await this.getRemoteVersion();
if (!remoteVersion) {
return '';
}
// 本地版本号
const localVersion = this.getLocalVersion();
// 对比版本号
const result = this.compareVersion(localVersion, remoteVersion);
const remoteDesc = await this.getRemoteDesc() || '';
return (result < 0) ? remoteDesc : '';
},
};
module.exports = Updater;

View File

@@ -0,0 +1,214 @@
/**
* 1.监听文件变动
*/
const fs = require("fs");
const path = require("path");
const tools = require("./tools");
/**
* 事件回调
*
* @callback WatchEventCallback
* @param {string} eventName - 事件名 create | delete | change | init
* @param {Array<string>} files - 改变的文件
*/
/**
* 监听事件
*/
class WatchFile {
/**
* @param {WatchFile} watchObj
* @param {string} pathName
* @param {WatchEventCallback} eventCallback
*/
constructor(watchObj,pathName,eventCallback){
this.pathName = pathName;
this.eventCallback = eventCallback;
this._watchObj = watchObj;
/** @type Object<string,fs.Stats> */
this._files = {}
this._isChecking = false;
this._isInit = false;
this._isExistPath = false;
this.check();
}
stop(){
this._watchObj.removeWatch(this.eventCallback);
}
/**
* 更新文件状态
*/
async check(){
if(this._isChecking){
return;
}
let pathStat = fs.existsSync(this.pathName) ? fs.statSync(this.pathName) : undefined;
if(!pathStat){
// 监听的目录被删除
if(this._isExistPath) setTimeout(this._removeAllFile.bind(this),1);
this._isExistPath = false;
return ;
}
this._isExistPath = true;
this._isChecking = true;
let newFiles = {}
if(pathStat && pathStat.isDirectory())
{
newFiles = await getDirAllFiles(this.pathName,newFiles)
this._update(newFiles)
}else if(pathStat.isFile())
{
newFiles[this.pathName] = pathStat;
// 延迟执行防止循环调用
setTimeout(()=>this._update(newFiles),1)
}
}
/**
* 更新状态
* @param {Object<string,fs.Stats>} newFiles
*/
_update(newFiles){
let deleteFiles = [];
let changeFiles = [];
let createFiles = [];
for (const filePath in this._files) {
const oldFileStats = this._files[filePath];
const newFileStats = newFiles[filePath]
if(newFileStats == null){
// 文件被删
deleteFiles.push(filePath);
}else if(newFileStats.mtimeMs != oldFileStats.mtimeMs){
// 文件被修改
changeFiles.push(filePath);
}
}
for (const filePath in newFiles) {
/** @type fs.Stats */
const oldFileStats = this._files[filePath];
if(oldFileStats == null){
// 新创文件
createFiles.push(filePath);
}
}
let isInit = this._isInit;
this._files = newFiles;
this._isChecking = false;
this._isInit = true;
// 调用事件
if(this.eventCallback){
if(!isInit){
this.eventCallback('init',createFiles);
}else{
if(deleteFiles.length) this.eventCallback('delete',deleteFiles);
if(changeFiles.length) this.eventCallback('change',changeFiles);
if(createFiles.length) this.eventCallback('create',createFiles);
}
}
}
_removeAllFile(){
let deleteFiles = [];
for (const filePath in this._files) {
deleteFiles.push(filePath);
}
this._files = {};
if(deleteFiles.length) this.eventCallback('delete',deleteFiles);
}
}
/**
* 监听文件类
*/
class WatchMgr{
constructor(){
/** @type [WatchFile] */
this.eventListens = []
this.watchFileBuffs = {}
}
/**
*
* @param {string} pathName
* @param {WatchEventCallback} eventCallback
* @returns {WatchFile}
*/
addWatchPath(pathName,eventCallback) {
let watchFile = new WatchFile(this,pathName,eventCallback);
this.eventListens.push(watchFile);
return watchFile;
}
checkAll() {
for (let i = 0; i < this.eventListens.length; i++) {
const watchFile = this.eventListens[i];
watchFile.check();
}
}
check(pathName) {
for (let i = 0; i < this.eventListens.length; i++) {
const watchFile = this.eventListens[i];
if(watchFile.pathName == pathName){
watchFile.check();
break
}
}
}
removeWatch(pathName) {
for (let i = 0; i < this.eventListens.length; i++) {
const watchFile = this.eventListens[i];
if(watchFile.pathName == pathName){
this.eventListens.splice(i,1);
break
}
}
}
}
async function getDirAllFiles(dirPath ,result = {}) {
return new Promise((resolve, reject )=>
{
fs.readdir(dirPath,(err,files)=>
{
if(err) return reject(err);
let len = files.length;
if(len == 0){
resolve(result); // 没有文件
return;
}
let cur_ind = 0;
files.forEach((val) => {
let fPath = path.join(dirPath, val);
fs.stat(fPath,async (err,stat)=>
{
if(err) return reject(err);
if (stat.isDirectory()) {
result = await getDirAllFiles(fPath,result);
} else if (stat.isFile()) {
result[fPath] = stat ;
}
cur_ind ++;
if(cur_ind == len) {
resolve(result)
}
})
});
});
})
}
exports.WatchMgr = WatchMgr;
exports.WatchFile = WatchFile;

View File

@@ -0,0 +1,21 @@
#box {
display: flex;
flex-wrap: nowrap;
flex-direction: column;
align-content: flex-start;
align-items: stretch;
justify-content: space-between;
height: -webkit-fill-available;
margin-left: 22px;
margin-right: 22px;
margin-top: 22px;
margin-bottom: 50px;
}
.layoutItem {
flex: 0.5;
display: flex;
flex-direction: column;
flex-grow: 0;
}

View File

@@ -0,0 +1,15 @@
<div id="box">
<div class="layoutItem">
压缩模式 :<br/>
<ui-tab id = 'zipModeTab' value="0">
<ui-button>Imagemin(离线)</ui-button>
<ui-button>TinyPng(在线)</ui-button>
</ui-tab>
</div>
<br/>
<div class="layoutItem">
压缩值 (越小压缩率越大):
<ui-slider id = 'zipRateSlider' step="1" value="70" min="1" max="100"></ui-slider>
</div>
<ui-button class="layoutItem" id = 'saveBtn' class="green">保存</ui-button>
</div>

File diff suppressed because it is too large Load Diff

1191
extensions/we_sane/@types/editor.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

16092
extensions/we_sane/@types/electron.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
declare namespace Editor {
namespace Interface {
// ---- Package ---- start
interface PackageInfo {
debug: boolean;
enable: boolean;
info: PackageJson;
invalid: boolean;
name: string;
path: string;
version: string;
}
interface PackageJson {
name: string;
version: string;
title?: string;
author?: string;
debug?: boolean;
description?: string;
main?: string;
editor?: string;
panel?: any;
contributions?: { [key: string]: any };
}
// ---- Package ---- end
// ---- UI ---- start
interface PanelInfo {
template?: string;
style?: string;
listeners?: { [key: string]: () => {} };
methods?: { [key: string]: Function };
$?: { [key: string]: string };
ready?(): void;
update?(...args: any[]): void;
beforeClose?(): void;
close?(): void;
}
namespace UIKit {
interface UIPanelInfo extends PanelInfo {
// 向上触发事件
dispath(eventName: string, ...arg: any): void;
}
interface EditorElementBase extends HTMLElement {
value: any;
dispath: (name: string, event: any) => void;
}
}
// ---- UI ---- end
}
}

2
extensions/we_sane/@types/index.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
/// <reference path="./editor.d.ts"/>
/// <reference path="./message.d.ts"/>

27
extensions/we_sane/@types/message.d.ts vendored Normal file
View File

@@ -0,0 +1,27 @@
import * as AssetDB from './packages/asset-db/@types/message';
import * as Scene from './packages/scene/@types/message';
import * as Engine from './packages/engine/@types/message';
import * as Builder from './packages/builder/@types/public/message';
import * as Programming from './packages/programming/@types/message';
// import * as Extension from './packages/extension/@types/message';
declare global {
interface EditorMessageContent {
params: any[],
result: any;
}
interface EditorMessageMap {
[x: string]: EditorMessageContent;
}
interface EditorMessageMaps {
[x: string]: EditorMessageMap;
'asset-db': AssetDB.message;
'scene': Scene.message;
'engine': Engine.message;
'builder': Builder.message;
'programming': Programming.message,
// 'extension': Extension.message;
}
}

View File

@@ -0,0 +1,179 @@
import { AssetInfo, QueryAssetsOption, AssetOperationOption, AssetDBOptions, IAssetMeta } from './public';
export interface message extends EditorMessageMap {
'query-ready': {
params: [],
result: boolean,
},
'create-asset': {
params: [
string,
string | Buffer | null,
] | [
string,
string | Buffer | null,
AssetOperationOption,
],
result: AssetInfo | null,
},
'import-asset': {
params: [
string,
string,
] | [
string,
string,
AssetOperationOption,
],
result: AssetInfo | null,
},
'copy-asset': {
params: [
string,
string,
] | [
string,
string,
AssetOperationOption,
],
result: AssetInfo | null,
},
'move-asset': {
params: [
string,
string,
] | [
string,
string,
AssetOperationOption,
],
result: AssetInfo | null,
},
'delete-asset': {
params: [
string,
],
result: AssetInfo | null,
},
'open-asset': {
params: [
string,
],
result: void,
},
'save-asset': {
params: [
string,
string | Buffer,
],
result: AssetInfo | null,
},
'save-asset-meta': {
params: [
string,
string,
],
result: AssetInfo | null,
},
'reimport-asset': {
params: [
string,
],
result: boolean,
},
'refresh-asset': {
params: [
string
],
result: boolean,
},
'query-asset-info': {
params: [
string,
],
result: AssetInfo | null,
},
'query-asset-meta': {
params: [
string,
],
result: IAssetMeta | null,
},
'query-path': {
params: [
string,
],
result: string | null,
},
'query-url': {
params: [
string
],
result: string | null,
},
'query-uuid': {
params: [
string
],
result: string | null,
},
'query-assets': {
params: [] | [
QueryAssetsOption,
],
result: AssetInfo[],
},
'generate-available-url': {
params: [
string,
],
result: string,
},
// private
'query-asset-mtime': {
params: [
string
],
result: string | null,
},
'refresh': {
params: [],
result: void,
},
'open-devtools': {
params: [],
result: void,
},
'query-db-info': {
params: [
string,
],
result: AssetDBOptions,
},
'create-asset-dialog': {
params: [
string,
] | [
string,
string,
],
result: string | null,
},
'init-asset': {
params: [
string,
string,
],
result: AssetInfo | null,
},
'query-all-importer': {
params: [],
result: string[],
},
'query-all-asset-types': {
params: [],
result: string[],
},
}

View File

@@ -0,0 +1,115 @@
// Basic information about the resource
// 资源的基础信息
export interface AssetInfo {
// Asset name
// 资源名字
name: string;
// Asset display name
// 资源用于显示的名字
displayName: string;
// URL
source: string;
// loader 加载的层级地址
path: string;
// loader 加载地址会去掉扩展名,这个参数不去掉
url: string;
// 绝对路径
file: string;
// 资源的唯一 ID
uuid: string;
// 使用的导入器名字
importer: string;
// 类型
type: string;
// 是否是文件夹
isDirectory: boolean;
// 导入资源的 map
library: { [key: string]: string };
// 子资源 map
subAssets: { [key: string]: AssetInfo };
// 是否显示
visible: boolean;
// 是否只读
readonly: boolean;
// 虚拟资源可以实例化成实体的话,会带上这个扩展名
instantiation?: string;
// 跳转指向资源
redirect?: IRedirectInfo;
// 继承类型
extends?: string[];
// 是否导入完成
imported: boolean;
// 是否导入失败
invalid: boolean;
}
export interface IRedirectInfo {
// 跳转资源的类型
type: string;
// 跳转资源的 uuid
uuid: string;
}
export interface QueryAssetsOption {
type?: string;
pattern?: string;
ccType?: string;
extname?: string;
importer?: string;
isBundle?: boolean;
}
export interface AssetOperationOption {
// 是否强制覆盖已经存在的文件,默认 false
overwrite?: boolean;
// 是否自动重命名冲突文件,默认 false
rename?: boolean;
}
export interface AssetDBOptions {
name: string;
target: string;
library: string;
temp: string;
/**
* 0: 忽略错误
* 1: 仅仅打印错误
* 2: 打印错误、警告
* 3: 打印错误、警告、日志
* 4: 打印错误、警告、日志、调试信息
*/
level: number;
ignoreFiles: string[];
readonly: boolean;
}
export interface ContributionInfo {
mount?: {
path: string;
readonly?: boolean;
};
}
export interface ExecuteAssetDBScriptMethodOptions {
name: string;
method: string;
args: any[];
}
export interface IAssetMeta {
ver: string;
importer: string;
imported: boolean;
uuid: string;
files: string[];
subMetas: {
[index: string]: IAssetMeta;
};
userData: {
[index: string]: any;
};
displayName: string;
id: string;
name: string;
}

View File

@@ -0,0 +1,2 @@
export * from './public';

View File

@@ -0,0 +1,117 @@
import { AssetInfo } from "../../../asset-db/@types/public";
import { UUID } from "../public";
import { IInternalBuildOptions } from "./options";
export interface IBuildStatiscInfo {
packageName: string;
gameName: string;
platform: string;
scenesNum: number;
assetsNum: number;
scriptNum: number;
includeModules: string[];
orientation: string;
remoteServerAddress: string;
appid: string;
size: number;
time: number;
err: string;
}
// ********************************* asset-manager *********************************
export class BuilderAssetCache {
// 场景资源的 assets 信息缓存
public readonly sceneUuids: Array<string>;
// 脚本资源的 assets 信息缓存
public readonly scriptUuids: Array<string>;
// 除场景、脚本资源外的资源 assets uuid 缓存
public readonly assetUuids: Array<string>;
init: () => Promise<void>;
addAsset: (asset: IAssetInfo) => void;
addInstance: (instance: any) => void;
clearAsset: (uuid: string) => void;
getMeta: (uuid: string) => Promise<any>;
getAssetInfo: (uuid: string) => IAssetInfo;
getDependUuids: (uuid: string) => Promise<readonly string[]>;
getDependUuidsDeep: (uuid: string) => Promise<readonly string[]>;
/**
* 获取序列化文件
*/
getLibraryJSON: (uuid: string) => Promise<any>;
getSerializedJSON: (uuid: string, options: IInternalBuildOptions) => Promise<any>;
forEach: (type: string, handle: Function) => Promise<void>;
getInstance: (uuid: string) => Promise<any>;
__addStaticsInfo: (info: any) => void;
}
export interface IAssetInfo extends AssetInfo {
meta?: any;
temp?: string; // 资源的构建缓存目录
fatherInfo?: any;
// fatherUuid?: string | undefined;
userData?: any;
subAssets: Record<string, IAssetInfo>;
dirty?: boolean;
// 内置资源没有 mtime
mtime?: number;
}
export type IUrl = string; // 需要的是符合 url 标准的字符串,例如 asset/script/text.ts
export type IAssetInfoMap = Record<UUID, IAssetInfo>;
export type IUuidDependMap = Record<UUID, UUID[]>;
export type IJsonGroupMap = Record<UUID, IJSONGroupItem>;
export type IAssetGroupMap = Record<UUID, IAssetGroupItem>;
// TODO meta 的类型定义
export type IMetaMap = Record<UUID, any>;
export type IJsonMap = Record<UUID, any>;
export type IInstanceMap = Record<UUID, any>;
export type ICompressOptions = Record<string, number>;
export interface IAssetGroupItem {
// 分组名字
// name: string;
// 分组的根 url
baseUrls: string[];
// 脚本编译后的实际地址
scriptDest: string;
// 脚本 uuid 列表
scriptUuids: UUID[];
// raw 资源 uuid 列表
assetUuids: UUID[];
}
export interface IJSONGroupItem {
// 分组名字
name?: string;
// 分组名字
type: string;
// json 资源 uuid 列表
uuids: UUID[];
}
export interface IAssetGroupOptions {
// 脚本打包后的输出路径
scriptUrl: string;
baseUrl: string;
}
export type IGroupType = 'json' | 'script' | 'asset';
export interface PacInfo {
meta: any;
asset: IAssetInfo;
spriteFrames: any[];
relativePath: string;
relativeDir: string;
}
export type IUpdateType = 'asset-change' | 'asset-add' | 'asset-delete';
export interface IUpdateInfo {
type: IUpdateType;
uuid: string;
}

View File

@@ -0,0 +1,152 @@
// ********************************* plugin ****************************************
import { BundleCompressionType, IBuildPluginConfig, IBuildTaskOption, IDisplayOptions, ISettings, IVerificationRuleMap } from '../public';
import { BuilderAssetCache } from './asset-manager';
import { InternalBuildResult } from './build-result';
import { IInternalBuildOptions } from './options';
import { ITextureCompressPlatform, ITextureCompressType } from '../public/texture-compress';
export interface IBuildWorkerPluginInfo {
assetHandlers?: string;
// 注册到各个平台的钩子函数
hooks?: Record<string, string>;
pkgName: string;
internal: boolean; // 是否为内置插件
priority: number; // 优先级
}
export type IPluginHookName =
| 'onBeforeBuild'
| 'onAfterInit'
| 'onBeforeInit'
| 'onAfterInit'
| 'onBeforeBuildAssets'
| 'onAfterBuildAssets'
| 'onBeforeCompressSettings'
| 'onAfterCompressSettings'
| 'onAfterBuild';
// | 'onBeforeCompile'
// | 'compile'
// | 'onAfterCompile'
// | 'run';
export type IPluginHook = Record<IPluginHookName, IInternalBaseHooks>;
export interface IInternalHook {
throwError?: boolean; // 插件注入的钩子函数,在执行失败时是否直接退出构建流程
title?: string; // 插件任务整体 title支持 i18n 写法
// ------------------ 钩子函数 --------------------------
onBeforeBuild?: IInternalBaseHooks;
onBeforeInit?: IInternalBaseHooks;
onAfterInit?: IInternalBaseHooks;
onBeforeBuildAssets?: IInternalBaseHooks;
onAfterBuildAssets?: IInternalBaseHooks;
onBeforeCompressSettings?: IInternalBaseHooks;
onAfterCompressSettings?: IInternalBaseHooks;
onAfterBuild?: IInternalBaseHooks;
// ------------------ 其他操作函数 ---------------------
// 内置插件才有可能触发这个函数
run?: (dest: string, options: IBuildTaskOption) => Promise<boolean>;
// 内置插件才有可能触发这个函数
compile?: (dest: string, options: IBuildTaskOption) => boolean;
}
export type IInternalBaseHooks = (options: IInternalBuildOptions, result: InternalBuildResult, cache: BuilderAssetCache, ...args: any[]) => void;
export interface IBuildTask {
handle: (options: IInternalBuildOptions, result: InternalBuildResult, cache: BuilderAssetCache, settings?: ISettings) => {};
title: string;
name: string;
}
export interface IBuildHooksInfo {
pkgNameOrder: string[];
infos: Record<string, { path: string; internal: boolean }>;
}
export interface IBuildAssetHandlerInfo {
pkgNameOrder: string[];
handles: {[pkgName: string]: Function};
}
export interface IInternalBuildPluginConfig extends IBuildPluginConfig {
platformName?: string; // 平台名,可以指定为 i18n 写法, 只有官方构建插件的该字段有效
hooks?: string; // 钩子函数的存储路径
panel?: string; // 存储导出 vue 组件、button 配置的脚本路径
textureCompressConfig?: {
// 仅对内部插件开放
platformType: ITextureCompressPlatform; // 注册的纹理压缩平台类型
support: {
rgba: ITextureCompressType[];
rgb: ITextureCompressType[];
}; // 该平台支持的纹理压缩格式,按照推荐优先级排列
};
assetBundleConfig?: {
// asset bundle 的配置
supportedCompressionTypes: BundleCompressionType[];
};
priority?: number;
wrapWithFold?: boolean; // 是否将选项显示在折叠框内(默认 true
options?: IDisplayOptions; // 需要注入的平台参数配置
verifyRuleMap?: IVerificationRuleMap; // 注入的需要更改原有参数校验规则的函数
commonOptions?: Record<string, boolean>;
// TODO 之前为 ios-app-clip HACK 出来的接口,之后需要重新设计
realInFileExplorer?: (options: IInternalBuildOptions | any) => void; // 根据构建配置计算输出地址(界面中的在文件夹中显示)
debugConfig?: IDebugConfig;
internal?: boolean;
}
export interface BuildCheckResult {
error: string;
newValue: any;
}
export type IBuildVerificationFunc = (value: any, options: IBuildTaskOption) => boolean | Promise<boolean>;
export interface IButtonConfigItem {
label: string; // 按钮名称
click?: (event: Event, options: IBuildTaskOption) => void; // 点击事件响应函数
hookHandle?: string; // 点击后执行的方法,与 click 二选一
tooltip?: string; // 鼠标上移到按钮上的文本提示
// 只有指定 hookHandle 配置的按钮,才可以影响进度条,构建将会自动为每个按钮创建一个构建内置任务,并提供对应的进度 log 更新等等。
// attributes?: any; // 想要添加在按钮上的一些属性值class, style, title)
}
export interface IDebugConfig {
options?: IDisplayOptions; // 显示在构建平台编译运行调试工具上的配置选项
custom?: string; // 显示在构建平台编译运行调试工具上的配置 vue 组件
}
// ui-panel 注册数据
export interface PanelInfo {
$?: { [name: string]: string | HTMLElement | null };
template?: string; // TODO 暂时设置为可选
style?: string;
methods?: { [name: string]: Function };
ready?: Function;
close?: Function;
update?: (options: IBuildTaskOption, path: string, value: any) => void | Promise<void>;
}
export interface IPanelThis {
$: Record<string, HTMLElement>;
dispatch: (name: string, ...args: any[]) => void;
}
export interface IPanelInfo extends PanelInfo {
component?: any; // 注入面板的 vue 组件,可与与 options 共存options 会优先显示
buttonConfig?: IButtonConfig; // 要注入的构建选项脚本
}
export interface IButtonConfig {
configs?: Record<string, IButtonConfigItem>;
custom?: any;
}
export interface ICompInfo {
custom?: any;
options?: IDisplayOptions;
panelInfo?: PanelInfo;
displayName?: string;
wrapWithFold: boolean;
// ..... 初始化时未存在的字段 .....
panel?: any; // 实例化后的 panel 对象
pkgName?: string; // 插件名称
}

View File

@@ -0,0 +1,141 @@
import { BundleCompressionType, IAssetPathInfo, IBuildPaths, IBuildTaskOption, IBundleConfig, IJsonPathInfo, ISettings, UUID } from "../public";
import { IAssetInfo } from "./asset-manager";
import { ImportMapWithImports } from "./import-map";
export class InternalBuildResult {
settings: ISettings;
readonly bundles: IBundle[];
readonly bundleMap: Record<string, IBundle>;
// 构建实际使用到的插件脚本 uuid 列表
plugins: UUID[];
// 脚本资源包分组(子包/分包)
scriptPackages: string[];
// MD5 后缀 map
pluginVers: Record<UUID, string>;
// 纹理压缩任务
imageTaskMap: Record<UUID, IImageTask>;
compressImageResult: ICompressImageResult;
importMap: ImportMapWithImports;
// 传入构建的 options
rawOptions: IBuildTaskOption;
// 输出路径集合
paths: IBuildPaths;
// 允许自定义编译选项
compileOptions?: any;
addBundle: (bundle: IBundle) => void;
addPlugin: (plugin: IAssetInfo) => void;
}
export interface IImageTask {
src: string;
// TODO 这个名称已和意义有冲突需要整理调整
dest: string[];
presetId: string;
hasAlpha: boolean;
mtime?: any;
// 生成阶段将会重新获取 bundle 的输出地址
bundleNames: string[];
}
export interface IVersionMap {
import: Record<UUID, string>;
native: Record<UUID, string>;
}
export interface IMD5Map {
'raw-assets': Record<UUID, string>;
import: Record<UUID, string>;
plugin?: Record<UUID, string>;
}
export interface IAtlasResult {
assetsToImage: Record<string, string>;
imageToAtlas: Record<string, string>;
atlasToImages: Record<string, string[]>;
}
export class IBundle {
readonly scenes: UUID[]; // 该 bundle 中的所有场景,包含重定向的
readonly assets: UUID[]; // 该 bundle 中的所有资源,包含重定向的
readonly assetsWithoutRedirect: UUID[]; // 该 bundle 中的未重定向的资源
readonly scripts: UUID[]; // 该 bundle 中的所有脚本
readonly rootAssets: UUID[]; // 该 bundle 中的根资源,即直接放在 bundle 目录下的资源,包含重定向的资源
readonly isSubpackage: boolean; // 该 bundle 是否是子包
root: string; // bundle 的根目录, 开发者勾选的目录,如果是 main 包,这个字段为 ''
dest: string; // bundle 的输出目录
importBase: string;
nativeBase: string;
scriptDest: string; // 脚本的输出目录
name: string; // bundle 的名称
priority: number; // bundle 的优先级
compressionType: BundleCompressionType; // bundle 的压缩类型
assetVer: IVersionMap; // bundle 内的资源版本
version: string; // bundle 本身的版本信息
readonly isRemote: boolean; // bundle 是否是远程包
redirect: Record<UUID, string>; // bundle 中的重定向资源
deps: Set<string>; // bundle 的依赖 bundle
groups: IGroup[]; // 该 bundle 中的资源分组
cache: any;
configOutPutName: string;
config: IBundleConfig; // 该 bundle 的资源清单
readonly isZip: boolean; // 该 bundle 是否是 zip 模式
zipVer: string; // Zip 压缩模式,压缩包的版本
atlasRes: IAtlasResult;
_rootAssets: Set<UUID>; // 该 bundle 直接包含的资源
_scenes: Set<UUID>;
_scripts: Set<UUID>;
_assets: Set<UUID>;
addScene(scene: IAssetInfo): void;
addScript(script: IAssetInfo): void;
addRootAsset(asset: IAssetInfo): void;
addAsset(asset: IAssetInfo): void;
removeAsset(asset: UUID): void;
addRedirectWithUuid(asset: UUID, redirect: string): void;
addRedirect(asset: IAssetInfo, redirect: string): void;
addAssetWithUuid(asset: UUID): void;
getRedirect(uuid: UUID): string | undefined;
addGroup(type: IJSONGroupType, uuids: UUID[]): void;
addToGroup(type: IJSONGroupType, uuid: UUID): void;
removeFromGroups(uuid: UUID): void;
getJsonPath(uuid: string): string;
getAssetPathInfo(uuid: string): IAssetPathInfo | null;
containsAsset(uuid: string): boolean;
getRawAssetPaths(uuid: string): string[];
getJsonPathInfo(uuid: string): IJsonPathInfo | null;
_resolveImportPath: (name: string) => string;
_resolveNativePath: (libraryPath: string, extName: string) => string;
}
export type ICompressImageResult = Record<UUID, {
formats: string[],
files: string[],
}>;
export interface IGroup {
// 分组名字
name?: string;
// 分组类型
type: IJSONGroupType;
// 该组中的资源 uuid 列表
uuids: UUID[];
}
export type IJSONGroupType = 'NORMAL' | 'TEXTURE' | 'IMAGE';
export interface IDefaultGroup {
assetUuids: UUID[];
scriptUuids: UUID[];
jsonUuids: UUID[];
}
export interface IBundleOptions {
root: string, // bundle 的根目录, 开发者勾选的目录,如果是 main 包,这个字段为''
dest: string, // bundle 的输出目录
scriptDest: string, // 脚本的输出目录
name: string, // bundle 的名称
priority: number, // bundle 的优先级
compressionType: BundleCompressionType, // bundle 的压缩类型
isRemote: boolean // bundle 是否是远程包
// isEncrypted: boolean // bundle 中的代码是否加密,原生平台使用
}

View File

@@ -0,0 +1,18 @@
import { IBuildPanel, IInternalBuild } from ".";
// 定义 builder 进程内的全局变量
declare global {
// 构建进程可用
// @ts-ignore
const Build: IInternalBuild;
const __manager: {
taskManager: any;
currentCompileTask: any;
currentBuildTask: any;
__taskId: string;
};
// 渲染进程可用
const BuildPanel: IBuildPanel;
}

View File

@@ -0,0 +1,7 @@
export interface ImportMap {
imports?: Record<string, string>;
scopes?: Record<string, Record<string, string>>;
}
export type ImportMapWithImports = ImportMap & { imports: NonNullable<ImportMap['imports']> };

View File

@@ -0,0 +1,89 @@
import { IBuild, IBuildUtils, ITaskState } from '../public';
import { InternalBuildResult } from './build-result';
export * from './asset-manager';
export * from './import-map';
export * from './options';
export * from './build-result';
export * from './build-plugin';
export * from '../public';
export type Physics = 'cannon' | 'ammo' | 'builtin';
export type Url = string; // 需要的是符合 url 标准的字符串
export type AssetInfoArr = Array<string | number>; // 固定两位或三位数组 [url,ccType,isSubAsset]
export type IProcessingFunc = (process: number, message: string, state?: ITaskState) => void;
export interface IBuildManager {
taskManager: any;
currentCompileTask: any;
currentBuildTask: any;
__taskId: string;
}
export interface IQuickSpawnOption {
cwd?: string;
env?: any;
downGradeWaring?: boolean; // 将会转为 log 打印,默认为 false
downGradeLog?: boolean; // 将会转为 debug 打印,默认为 true
downGradeError?: boolean; // 将会转为警告,默认为 false
ignoreLog?: boolean; // 忽略 log 信息
}
export interface IInternalBuildUtils extends IBuildUtils {
/**
* 获取构建出的所有模块或者模块包文件。
*/
getModuleFiles(result: InternalBuildResult): Promise<string[]>;
/**
* 快速开启子进程
* @param command
* @param cmdParams
* @param options
*/
quickSpawn(command: string, cmdParams: string[], options?: IQuickSpawnOption): Promise<number | boolean>;
/**
* 将某个 hash 值添加到某个路径上
* @param targetPath
* @param hash
* @returns
*/
patchMd5ToPath(targetPath: string, hash: string): string;
}
export interface IInternalBuild extends IBuild {
Utils: IInternalBuildUtils;
}
export interface IBuildProcessInfo {
state: ITaskState; // 任务状态
progress: number; // 任务进度
message: string; // 最后一次更新的进度消息
id: string; // 任务唯一标识符
options: any; // 构建参数
}
export interface fileMap {
src: string;
dest: string;
}
export interface IScriptInfo {
file: string;
uuid: string;
}
type ICheckRule = 'pathExist' | 'valid' | 'required' | 'normalName' | 'noChinese' | 'array' | 'string' | 'number' | 'http';
export interface IBuildPanel {
// 内部使用的 Vue
Vue: any;
// 内置 vue 组件
vueComps: {
buildProp: any;
templateComp: any;
},
validator: {
has: (ruleName: string) => boolean;
check: (ruleName: ICheckRule, val: any) => boolean;
},
}

View File

@@ -0,0 +1,145 @@
import { IBuildTimeConstantValue } from "@cocos/build-engine/dist/build-time-constants";
import { IBuildDesignResolution, IBuildTaskOption } from "../public";
export interface ScriptAssetuserData {
isPlugin?: boolean;
isNative?: boolean;
loadPluginInNative?: boolean;
loadPluginInWeb?: boolean;
}
export interface IBuildScriptParam {
/**
* 若存在,表示将 import map 转换为指定的模块格式。
*/
importMapFormat?: 'commonjs' | 'esm';
polyfills?: IPolyFills;
/**
* 擦除模块结构。当选择后会获得更快的脚本导入速度,但无法再使用模块特性,如 `import.meta`、`import()` 等。
* @experimental
*/
experimentalEraseModules?: boolean;
outputName: string; // 输出文件夹名称(带后缀)
targets?: ITransformTarget;
system?: {
preset?: 'web' | 'commonjs-like',
},
flags: Record<string, IBuildTimeConstantValue>,
}
export interface IPolyFills {
/**
* True if async functions polyfills(i.e. regeneratorRuntime) needs to be included.
* You need to turn on this field if you want to use async functions in language.
*/
asyncFunctions?: boolean;
/**
* If true, [core-js](https://github.com/zloirock/core-js) polyfills are included.
* The default options of [core-js-builder](https://github.com/zloirock/core-js/tree/master/packages/core-js-builder)
* will be used to build the core-js.
*/
coreJs?: boolean;
targets?: string;
}
/**
* 模块保留选项。
* - 'erase' 擦除模块信息。生成的代码中将不会保留模块信息。
* - 'preserve' 保留原始模块信息。生成的文件将和原始模块文件结构一致。
* - 'facade' 保留原始模块信息,将所有模块转化为一个 SystemJS 模块,但这些模块都打包在一个单独的 IIFE bundle 模块中。
* 当这个 bundle 模块执行时,所有模块都会被注册。
* 当你希望代码中仍旧使用模块化的特性如动态导入、import.meta.url但又不希望模块零散在多个文件时可以使用这个选项。
*/
export type ModulePreservation = 'erase' | 'preserve' | 'facade';
export type INewConsoleType = 'log' | 'warn' | 'error' | 'debug';
export interface IInternalBuildOptions extends IBuildTaskOption {
dest: string;
// 编译 application.js 参数配置
appTemplateData: appTemplateData;
// 编译引擎参数配置
buildEngineParam: IBuildEngineParam;
// 编译脚本配置选项
buildScriptParam: IBuildScriptParam;
// 序列化打包资源时的特殊处理
assetSerializeOptions: {
'cc.EffectAsset': {
glsl1: boolean;
glsl3: boolean;
glsl4: boolean;
};
// 是否输出 ccon 格式
exportCCON?: boolean;
allowCCONExtension?: boolean;
};
updateOnly: boolean;
nextTasks?: string[];
generateCompileConfig?: boolean;
recompileConfig?: IRecompileConfig;
logDest?: string; // log 输出地址
// 项目设置,重复定义为必选参数
includeModules: string[];
renderPipeline: string;
designResolution: IBuildDesignResolution;
physicsConfig: any;
flags: Record<string, boolean>;
}
export interface appTemplateData {
debugMode: boolean;
renderMode: boolean; // !!options.renderMode,
// ImportMapSupported: boolean;
// NonconformingCommonJs: boolean;
showFPS: boolean;
importMapFile?: string;
resolution: {
policy: number;
width: number;
height: number;
};
// hasPhysicsAmmo: boolean;
md5Cache: boolean;
server: string; // 服务器地址
cocosTemplate?: string; // 注入的子模板路径
}
export interface IBuildEngineParam {
entry?: string; // 引擎入口文件
debug: boolean;
sourceMaps: boolean;
platform: string;
includeModules: string[];
engineVersion: string;
md5Map: string[];
engineName: string;
useCache: boolean;
split?: boolean;
targets?: ITransformTarget;
skip?: boolean;
ammoJsWasm?: boolean | 'fallback';
assetURLFormat?:
| 'relative-from-out'
| 'relative-from-chunk'
| 'runtime-resolved';
baseUrl?: string;
}
export type ITransformTarget = string | string[] | Record<string, string>;
export interface IRecompileConfig {
enable: boolean;
generateAssets: boolean;
generateScripts: boolean;
generateEngine: boolean; // 是否生成引擎
generateEngineByCache: boolean; // 是否使用缓存引擎
}

View File

@@ -0,0 +1,76 @@
import { ITextureCompressType, IPVRQuality, IASTCQuality, IETCQuality } from './texture-compress';
import { IBuildTaskOption } from './options';
import { IBuildResult } from './build-result';
export interface IBuildPluginConfig {
hooks?: string; // relate url about IHook
options?: IDisplayOptions; // config of options
verifyRuleMap?: IVerificationRuleMap;
}
export type IVerificationFunc = (val: any, ...arg: any[]) => boolean | Promise<boolean>;
export type IVerificationRuleMap = Record<
string,
{
func?: IVerificationFunc;
message?: string;
}
>;
export type IDisplayOptions = Record<string, IConfigItem>;
export type ArrayItem = {
label: string;
value: string;
};
export interface IConfigItem {
// 配置显示的名字,如果需要翻译,则传入 i18n:${key}
label?: string;
// 设置的简单说明
description?: string;
// 默认值
default?: any;
// 配置的类型
type?: 'array' | 'object';
itemConfigs?: IConfigItem[] | Record<string, IConfigItem>;
verifyRules?: string[];
attributes?: any;
render?: {
ui: string;
attributes?: any;
items?: ArrayItem[];
};
}
export interface IBuildPlugin {
configs?: BuildPlugin.Configs;
assetHandlers?: BuildPlugin.AssetHandlers;
load?: BuildPlugin.load;
unload?: BuildPlugin.Unload;
}
export type IBaseHooks = (options: IBuildTaskOption, result: IBuildResult) => Promise<void> | void;
export namespace BuildPlugin {
export type Configs = Record<string, IBuildPluginConfig>;
export type AssetHandlers = string;
export type load = () => Promise<void> | void;
export type Unload = () => Promise<void> | void;
}
export namespace BuildHook {
export type throwError = boolean; // 插件注入的钩子函数,在执行失败时是否直接退出构建流程
export type title = string; // 插件任务整体 title支持 i18n 写法
export type onBeforeBuild = IBaseHooks;
export type onBeforeCompressSettings = IBaseHooks;
export type onAfterCompressSettings = IBaseHooks;
export type onAfterBuild = IBaseHooks;
export type load = () => Promise<void> | void;
export type unload = () => Promise<void> | void;
}
export namespace AssetHandlers {
export type compressTextures = (
tasks: { src: string; dest: string; quality: number | IPVRQuality | IASTCQuality | IETCQuality; format: ITextureCompressType }[],
) => Promise<void>;
}

View File

@@ -0,0 +1,189 @@
/**
* settings.js 里定义的数据
*/
import { ISplashSetting, ICustomJointTextureLayout, UUID } from "./options";
// ****************************** settings ************************************************
// debug: true
// designResolution: {width: "960", height: "640", policy: 4}
// jsList: ["assets/resources/b.js", "assets/resources/a.js"]
// launchScene: "db://assets/New Scene-001.scene"
// platform: "web-desktop"
// rawAssets: {
// assets: {
// "0e95a9f8-d4e7-4849-875a-7a11dd692b34": ["mesh/env/gltf/textures/birch_yellow_mat_baseColor.png", "cc.ImageAsset"]
// }
// internal: {
// "1baf0fc9-befa-459c-8bdd-af1a450a0319": ["effects/builtin-standard.effect", "cc.EffectAsset"]
// }
// }
// scenes: [{url: "db://assets/New Scene-001.scene", uuid: "69dc4a42-cc6c-49fb-9a57-7de0c212f83d"},…]
// startScene: "current_scene"
export interface ISettings {
CocosEngine: string;
debug: boolean;
designResolution: ISettingsDesignResolution;
jsList: string[];
launchScene: string;
moduleIds: string[];
platform: string;
renderPipeline: string;
physics?: IPhysicsConfig;
exactFitScreen: boolean;
bundleVers: Record<string, string>;
subpackages: string[];
remoteBundles: string[];
server: string;
hasResourcesBundle: boolean;
hasStartSceneBundle: boolean;
scriptPackages?: string[];
splashScreen?: ISplashSetting;
customJointTextureLayouts?: ICustomJointTextureLayout[];
importMaps?: Array<{
url: string;
map: any;
}>;
macros?: Record<string, any>;
collisionMatrix?: any;
groupList?: any;
// preview
engineModules: string[];
customLayers: {name: string, bit: number}[];
}
// 物理配置
export interface IVec3Like {
x: number;
y: number;
z: number;
}
export interface ICollisionMatrix {
[x: string]: number;
}
export interface IPhysicsMaterial {
friction: number; // 0.5
rollingFriction: number; // 0.1
spinningFriction: number; // 0.1
restitution: number; // 0.1
}
export interface IPhysicsConfig {
gravity: IVec3Like; // 0-10 0
allowSleep: boolean; // true
sleepThreshold: number; // 0.1,最小 0
autoSimulation: boolean; // true
fixedTimeStep: number; // 1 / 60 ,最小 0
maxSubSteps: number; // 1最小 0
defaultMaterial: IPhysicsMaterial;
useNodeChains: boolean; // true
collisionMatrix: ICollisionMatrix;
physicsEngine: string;
}
export interface IPackageInfo {
name: string;
path: string;
uuids: UUID[];
}
export interface ISettingsDesignResolution {
width: number;
height: number;
policy: number;
}
interface IAssetPathBase {
bundleName?: string;
redirect?: string; // 重定向的 bundle 包名
}
export interface IRawAssetPathInfo extends IAssetPathBase {
raw: string[];
}
export declare interface IAssetPathInfo extends IAssetPathBase {
raw?: string[];
json?: string;
groupIndex?: number;
}
export interface IJsonPathInfo extends IAssetPathBase {
json?: string;
groupIndex?: number;
}
export interface IBuildPaths {
dir: string; // 构建资源输出地址( assets 所在的目录,并不一定与构建目录对应)
settings: string; // settings.json 输出地址
systemJs?: string; // system.js 生成地址
engineDir?: string; // 引擎生成地址
polyfillsJs?: string; // polyfill.js 生成地址
assets: string; // assets 目录
subpackages: string; // subpackages 目录
remote: string; // remote 目录
bundleScripts: string // bundle 的脚本,某些平台无法下载脚本,则将远程包中的脚本移到本地
applicationJS: string; // application.js 的生成地址
compileConfig?: string; // cocos.compile.config.json
importMap: string; // import-map 文件地址
}
export declare class IBuildResult {
dest: string; // options 指定的构建目录
paths: IBuildPaths; // 构建后资源相关地址集合
settings?: ISettings;
/**
* 指定的 uuid 资源是否包含在构建资源中
*/
containsAsset: (uuid: string) => boolean;
/**
* 获取指定 uuid 原始资源的存放路径(不包括序列化 json
* 自动图集的小图 uuid 和自动图集的 uuid 都将会查询到合图大图的生成路径
* 实际返回多个路径的情况:查询 uuid 为自动图集资源,且对应图集生成多张大图,纹理压缩会有多个图片格式路径
*/
getRawAssetPaths: (uuid: string) => IRawAssetPathInfo[];
/**
* 获取指定 uuid 资源的序列化 json 路径
*/
getJsonPathInfo: (uuid: string) => IJsonPathInfo[];
/**
* 获取指定 uuid 资源的路径相关信息
* @return {raw?: string[]; json?: string; groupIndex?: number;}
* @return.raw: 该资源源文件的实际存储位置
* @return.json: 该资源序列化 json 的实际存储位置,不存在为空
* @return.groupIndex: 若该资源的序列化 json 在某个 json 分组内,这里标识在分组内的 index不存在为空
*/
getAssetPathInfo: (uuid: string) => IAssetPathInfo[];
}
export interface IBundleConfig {
importBase: string; // bundle 中 import 目录的名称,通常是 'import'
nativeBase: string; // native 中 native 目录的名称,通常是 'native'
name: string; // bundle 的名称,可以通过 bundle 名称加载 bundle
deps: string[]; // 该 bundle 依赖的其他 bundle 名称
uuids: UUID[]; // 该 bundle 中的所有资源的 uuid
paths: Record<string, any[]>; // 该 bundle 中可以通过路径加载的资源,参考以前 settings 中 rawAssets 的定义
scenes: Record<string, UUID|number>; // 该 bundle 中所有场景,场景名为 key, uuid 为 value
packs: Record<UUID, UUID[]>; // 该 bundle 中所有合并的 json, 参考以前 settings 中 packedAssets 的定义
versions: { import: Array<string|number>, native: Array<string|number> }; // 该 bundle 中所有资源的版本号,参考以前 settings 中 md5AssetsMap 的定义
redirect: Array<string|number>; // 该 bundle 中重定向到其他 bundle 的资源
debug: boolean; // 是否是 debug 模式debug 模式会对 config.json 的数据进行压缩,所以运行时得解压
types?: string[]; // paths 中的类型数组,参考以前 settings 中 assetTypes 的定义
encrypted?: boolean; // 原生上使用,标记该 bundle 中的脚本是否加密
isZip?: boolean; // 是否是 zip 模式
zipVersion?: string;
extensionMap: Record<string, UUID[]>
}

View File

@@ -0,0 +1,7 @@
import { IBuild } from ".";
// 定义 builder 进程内的全局变量
declare global {
// @ts-ignore
const Build: IBuild;
}

View File

@@ -0,0 +1,100 @@
import { ITransformOptions } from './options';
export * from './build-result';
export * from './build-plugin';
export * from './texture-compress';
export * from './options';
interface IAppendRes {
hash: string;
paths: string[];
}
interface ICreateBundleOptions {
excludes?: string[];
debug?: boolean;
sourceMap?: boolean;
}
export interface IBuildUtils {
/**
* 压缩 uuid
* 'fc991dd7-0033-4b80-9d41-c8a86a702e59' -> 'fc9913XADNLgJ1ByKhqcC5Z'
*/
compressUuid: (uuid: string, min: boolean) => string;
/**
* 解压缩 uuid
* 'fc9913XADNLgJ1ByKhqcC5Z' -> 'fc991dd7-0033-4b80-9d41-c8a86a702e59'
*/
decompressUuid: (uuid: string) => string;
/**
* 翻译带有 i18n 开头的名称i18n:test)(待定)
* 'i18n:test' -> '测试'
*/
transI18nName: (name: string) => string;
/**
* 移除 db 前缀
* 'db://assets/test.jpg' -> 'assets/test.jpg'
*/
removeDbHeader: (url: string) => string;
/**
* 将 db 开头的 url 转为项目里的实际 url
* 'db://assets/test.jpg' -> 'c:/project/assets/test.jpg'
*/
dbUrlToRawPath: (url: string) => string;
/**
* 从路径里获取存在的 uuid
* 'E:\test3d\library\oc\0c0c1f5742-89b0-4a1e-b5eb-914d84f48c1c.json' -> '0c0c1f5742-89b0-4a1e-b5eb-914d84f48c1c'
*/
getUuidFromPath: (path: string) => string;
/**
* 检查是否全局安装了 nodejs
*/
isInstallNodeJs: () => Promise<boolean>;
/**
* 逐文件拷贝
*/
copyDirSync: (src: string, dest: string) => void;
/**
* 获取相对路径接口
* 返回 / 拼接的相对路径
*/
relativeUrl: (from: string, to: string) => string;
transformCode: (code: string, options: ITransformOptions) => Promise<string>;
/**
* 给指定路径添加 md5
*/
appendMd5ToPaths: (paths: string[]) => Promise<IAppendRes | null>;
calcMd5: (data: Buffer | string) => string;
copyPaths: (paths: { src: string; dest: string }[]) => Promise<void[]>;
createBundle: (src: string, dest: string, options?: ICreateBundleOptions) => Promise<unknown>;
}
export interface IBuild {
Utils: IBuildUtils;
LIBRARY_NAME: string;
IMPORT_HEADER: string;
NATIVE_HEADER: string;
ASSETS_HEADER: string;
SUBPACKAGES_HEADER: string;
REMOTE_HEADER: string;
BUNDLE_SCRIPTS_HEADER: string;
SCRIPT_NAME: string;
CONFIG_NAME: string;
BUNDLE_ZIP_NAME: string;
projectTempDir: string;
globalTempDir: string;
buildTemplateDir: string; // 构建模板地址 build-templates
}

View File

@@ -0,0 +1,40 @@
import { IBundleConfig, ISettings } from "./build-result";
import { ITaskItemJSON } from "./options";
export interface message extends EditorMessageMap {
'open-devtools': {
params: [],
result: void,
},
open: {
params: [],
result: void,
},
'generate-preview-setting': {
params: any[],
result: Promise<{
settings: ISettings;
script2library: Record<string, string>;
bundleConfigs: IBundleConfig[];
}>,
},
'query-tasks-info': {
params: [],
result: {
queue: Record<string, ITaskItemJSON>,
free: Promise<boolean>,
},
},
'query-task': {
params: string[],
result: Promise<ITaskItemJSON>,
},
/**
* 预览合图
* @param {object} pacUuid
*/
'preview-pac': {
params: string[],
result: Promise<ITaskItemJSON>,
},
}

View File

@@ -0,0 +1,132 @@
/**
* 构建所需的完整参数
*/
export interface IBuildTaskOption {
// 构建后的游戏文件夹生成的路径
buildPath: string;
debug: boolean;
inlineSpriteFrames: boolean;
md5Cache: boolean;
// bundle 设置
mainBundleCompressionType: BundleCompressionType;
mainBundleIsRemote: boolean;
moveRemoteBundleScript: boolean;
mergeJson: boolean;
name: string;
packAutoAtlas: boolean;
platform: Platform;
scenes: IBuildSceneItem[];
compressTexture: boolean;
sourceMaps: boolean;
startScene: string;
outputName: string;
experimentalEraseModules: boolean;
/**
* 是否是预览进程发送的构建请求。
* @default false
*/
preview?: boolean;
// 项目设置
includeModules?: string[];
renderPipeline?: string;
designResolution?: IBuildDesignResolution;
physicsConfig?: any;
flags?: Record<string, boolean>;
// 是否使用自定义插屏选项
replaceSplashScreen?: boolean;
splashScreen: ISplashSetting;
packages?: Record<string, any>;
id?: string; // 手动配置构建任务 id
// recompileConfig?: IRecompileConfig;
customLayers: {name: string, value: number}[];
}
export type UUID = string;
export interface ISplashSetting {
base64src: string;
displayRatio: number;
totalTime: number;
effect: string;
clearColor: { x: number; y: number; z: number; w: number };
displayWatermark: boolean;
}
export interface ICustomJointTextureLayout {
textureLength: number;
contents: IChunkContent[];
}
export interface IChunkContent {
skeleton: null | string;
clips: string[];
}
/**
* 构建使用的设计分辨率数据
*/
export interface IBuildDesignResolution {
height: number;
width: number;
fitWidth?: boolean;
fitHeight?: boolean;
}
/**
* 构建使用的场景的数据
*/
export interface IBuildSceneItem {
url: string;
uuid: string;
}
// **************************** options *******************************************
export type Platform =
| 'web-desktop'
| 'web-mobile'
| 'wechatgame'
| 'oppo-mini-game'
| 'vivo-mini-game'
| 'huawei-quick-game'
| 'alipay-mini-game'
| 'mac'
| 'ios'
// | 'ios-app-clip'
| 'android'
| 'ohos'
| 'windows'
| 'xiaomi-quick-game'
| 'baidu-mini-game'
| 'bytedance-mini-game'
| 'cocos-play'
| 'huawei-agc'
| 'link-sure'
| 'qtt'
| 'cocos-runtime'
;
export type BundleCompressionType = 'none' | 'merge_dep' | 'merge_all_json' | 'subpackage' | 'zip';
export type IModules = 'esm' | 'commonjs' | 'systemjs';
export interface ITransformOptions {
importMapFormat: IModules;
}
export type ITaskState = 'waiting' | 'success' | 'failure' | 'cancel' | 'processing';
export interface ITaskItemJSON {
id: string;
progress: number;
state: ITaskState;
message: string;
options: IBuildTaskOption;
time: string;
dirty: boolean;
rawOptions: IBuildTaskOption;
}

View File

@@ -0,0 +1,43 @@
export type ITextureCompressType =
| 'jpg'
| 'png'
| 'webp'
| 'pvrtc_4bits_rgb'
| 'pvrtc_4bits_rgba'
| 'pvrtc_4bits_rgb_a'
| 'pvrtc_2bits_rgb'
| 'pvrtc_2bits_rgba'
| 'pvrtc_2bits_rgb_a'
| 'etc1_rgb'
| 'etc1_rgb_a'
| 'etc2_rgb'
| 'etc2_rgba'
| 'astc_4x4'
| 'astc_5x5'
| 'astc_6x6'
| 'astc_8x8'
| 'astc_10x5'
| 'astc_10x10'
| 'astc_12x12';
export type ITextureCompressPlatform = 'miniGame' | 'web' | 'ios' | 'android';
export type IQualityType = 'etc' | 'pvr' | 'number' | 'astc';
export interface ITextureFormatInfo {
displayName: string;
qualityType: IQualityType;
alpha?: boolean;
}
export interface ISupportFormat {
rgb: ITextureCompressType[];
rgba: ITextureCompressType[];
}
export interface IConfigGroupsInfo {
defaultSupport?: ISupportFormat,
support: ISupportFormat,
displayName: string;
icon: string;
}
export type IConfigGroups = Record<ITextureCompressPlatform, IConfigGroupsInfo>;
export type IPVRQuality = 'fastest' | 'fast' | 'normal' | 'high' | 'best';
export type IETCQuality = 'slow' | 'fast';
export type IASTCQuality = 'veryfast' | 'fast' | 'medium' | 'thorough' | 'exhaustive';

View File

@@ -0,0 +1,16 @@
export interface IMessageItem {
rows: number;
translateY: number;
show: boolean;
title: string;
content: string[];
count: number;
fold: boolean;
type: string;
message: any;
texture: string;
date?: number;
time?: number;
process?: string;
stack: string[];
}

View File

@@ -0,0 +1,16 @@
export interface message extends EditorMessageMap {
'query-info': {
params: [] | [
string,
],
result: {
type: string;
version: string;
path: string;
nativeVersion: string; // 原生引擎类型 'custom' 'builtin'
nativePath: string;
editor: string;
renderPipeline: string;
},
},
}

View File

@@ -0,0 +1 @@
export * from './protect/';

View File

@@ -0,0 +1,33 @@
import { Platform } from "../../../builder/@types/public";
export type IPreviewType = 'game-view' | 'simulator' | 'browser';
export type ISupportDataType = 'settings' | 'renderData';
export interface IHookConfig {
methods: string;
hook: string;
}
export interface IGenerateSettingsOptions {
type: IPreviewType;
startScene?: string;
platform?: Platform;
}
export interface IPreviewPluginConfig {
methods?: string;
hooks?: Record<string, string>;
}
// 界面渲染配置
export interface IRenderData {
title: string; // 预览页面 title
enableDebugger: boolean; // 是否开启 vConsole
config: { // 预览页面菜单栏配置
device: string; // 设备名称
// https://github.com/cocos-creator/engine/blob/3d/cocos/core/platform/debug.ts
debugMode: string; // cc.DebugMode 枚举名称
showFps: boolean;
fps: number;
}
}

View File

@@ -0,0 +1,19 @@
export interface message extends EditorMessageMap {
'query-shared-settings': {
params: [],
result: {
useDefineForClassFields: boolean;
allowDeclareFields: boolean;
loose: boolean;
guessCommonJsExports: boolean;
exportsConditions: string[];
importMap?: {
json: {
imports?: Record<string, string>;
scopes?: Record<string, Record<string, string>>;
};
url: string;
};
}
};
}

View File

@@ -0,0 +1,68 @@
import {
SetPropertyOptions,
} from './public';
export interface message extends EditorMessageMap {
'update-create-node-template': {
params: [],
result: any,
},
'open': {
params: [],
result: any,
},
'open-devtools': {
params: [],
result: any,
},
'graphical-tools': {
params: [
boolean,
],
result: void,
},
'open-scene': {
params: [
string,
],
result: boolean,
},
'save-scene': {
params: [] | [
boolean,
],
result: boolean,
},
'save-as-scene': {
params: [
boolean,
],
result: boolean,
},
'close-scene': {
params: [],
result: boolean,
},
'set-property': {
params: [
SetPropertyOptions,
],
result: boolean,
},
'query-node-tree': {
params: [] | [
string,
],
result: any,
},
'execute-scene-script': {
params: [] | [
{
name: string;
method: string;
args: any[];
}
],
result: any,
},
}

View File

@@ -0,0 +1,407 @@
// ---- 一些 engine 基础数据 ---- start
interface Vec2 {
x: number;
y: number;
}
export interface Vec3 {
x: number;
y: number;
z: number;
}
interface Vec4 {
x: number;
y: number;
z: number;
w: number;
}
interface Quat {
x: number;
y: number;
z: number;
w: number;
}
interface Color3 {
r: number;
g: number;
b: number;
}
interface Color4 {
r: number;
g: number;
b: number;
a: number;
}
interface Mat3 {
m00: number;
m01: number;
m02: number;
m03: number;
m04: number;
m05: number;
m06: number;
m07: number;
m08: number;
}
interface Mat4 {
m00: number;
m01: number;
m02: number;
m03: number;
m04: number;
m05: number;
m06: number;
m07: number;
m08: number;
m09: number;
m10: number;
m11: number;
m12: number;
m13: number;
m14: number;
m15: number;
}
// ---- 一些 engine 基础数据 ---- end
// ---- 操作消息的参数定义 --- strart
// set-property 消息的 options 定义
export interface SetPropertyOptions {
uuid: string; // 修改属性的对象的 uuid
path: string; // 属性挂载对象的搜索路径
// key: string; // 属性的 key
dump: IProperty; // 属性 dump 出来的数据
}
// move-array-element 消息的 options 定义
export interface MoveArrayOptions {
uuid: string;
path: string;
target: number;
offset: number;
}
// remove-array-element 消息的 options 定义
export interface RemoveArrayOptions {
uuid: string;
path: string;
index: number;
}
export interface PasteNodeOptions {
target: string; // 目标节点
uuids: string | string[]; // 被复制的节点 uuids
keepWorldTransform?: boolean; // 是否保持新节点的世界坐标不变
}
export interface CutNodeOptions {
parent: string; // 父节点
uuids: string | string[]; // 被移入的节点 uuids
keepWorldTransform?: boolean; // 是否保持新节点的世界坐标不变
}
// create-node 消息的 options 定义
export interface CreateNodeOptions {
parent?: string;
components?: string[];
name?: string;
dump?: INode | IScene; // node 初始化应用的数据
keepWorldTransform?: boolean; // 是否保持新节点的世界坐标不变
type?: string; // 资源类型
assetUuid?: string; // asset uuid , type value 格式保持兼容拖动的数据格式,有资源 id则从资源内创建对应的节点
canvasRequired?: boolean; // 是否需要有 Canvas
unlinkPrefab?: boolean; // 创建后取消 prefab 状态
position?: Vec3; // 指定生成的位置
}
export interface ResetNodeOptions {
uuid: string | string[];
}
export interface RemoveNodeOptions {
uuid: string | string[];
keepWorldTransform?: boolean;
}
export interface CreateComponentOptions {
uuid: string;
component: string;
}
export interface ResetComponentOptions {
uuid: string;
}
export interface RemoveComponentOptions {
uuid: string;
component: string;
}
export interface ExecuteComponentMethodOptions {
uuid: string;
name: string;
args: any[];
}
export interface IAnimOperation {
funcName: string;
args: any[];
}
export interface ExecuteSceneScriptMethodOptions {
name: string;
method: string;
args: any[];
}
export type IPropertyValueType = IProperty | IProperty[] | null | undefined | number | boolean | string | Vec3 | Vec2;
export interface IPropertyGroupOptions {
id: string // 默认 'default'
name: string,
displayOrder: number, // 默认 Infinity, 排在最后面
style: string // 默认为 'tab'
}
export interface IProperty {
value: { [key: string]: IPropertyValueType } | IPropertyValueType;
default?: any; // 默认值
// 多选节点之后,这里存储多个数据,用于自行判断多选后的显示效果,无需更新该数据
values?: ({ [key: string]: IPropertyValueType } | IPropertyValueType)[];
cid?: string;
type?: string;
readonly?: boolean;
visible?: boolean;
name?: string;
elementTypeData?: IProperty; // 数组里的数据的默认值 dump
path?: string; // 数据的搜索路径,这个是由使用方填充的
isArray?: boolean;
invalid?: boolean;
extends?: string[]; // 继承链
displayName?: string; // 显示到界面上的名字
displayOrder?: number; // 显示排序
group?: IPropertyGroupOptions; // tab
tooltip?: string; // 提示文本
editor?: any; // 组件上定义的编辑器数据
animatable?: boolean; // 是否可以在动画中编辑
// Enum
enumList?: any[]; // enum 类型的 list 选项数组
bitmaskList?: any[];
// Number
min?: number; // 数值类型的最小值
max?: number; // 数值类型的最大值
step?: number; // 数值类型的步进值
slide?: boolean; // 数组是否显示为滑块
unit?: string; // 显示的单位
radian?: boolean; // 标识是否为角度
// Label
multiline?: boolean; // 字符串是否允许换行
// nullable?: boolean; 属性是否允许为空
}
export interface IRemovedComponentInfo {
name: string;
fileID: string;
}
export interface INode {
active: IProperty;
locked: IProperty;
name: IProperty;
position: IProperty;
/**
* 此为 dump 数据,非 node.rotation
* 实际指向 node.eulerAngles
* rotation 为了给用户更友好的文案
*/
rotation: IProperty;
scale: IProperty;
layer: IProperty;
uuid: IProperty;
children: any[];
parent: any;
__comps__: IProperty[];
__type__: string;
__prefab__?: any;
_prefabInstance?: any;
removedComponents?: IRemovedComponentInfo[];
mountedRoot?: string;
}
export interface IComponent extends IProperty {
value: {
enabled: IPropertyValueType;
uuid: IPropertyValueType;
name: IPropertyValueType;
} & Record<string, IPropertyValueType>;
mountedRoot?: string;
}
export interface IScene {
name: IProperty;
active: IProperty;
locked: IProperty;
_globals: any;
isScene: boolean;
autoReleaseAssets: IProperty;
uuid: IProperty;
children: any[];
parent: any;
__type__: string;
targetOverrides?: any;
}
export interface ITargetOverrideInfo {
source: string;
sourceInfo?: string[];
propertyPath: string[];
target: string;
targetInfo?: string[];
}
// ---- 操作消息的参数定义 --- end
// ---- 场景插件返回的 info 信息 ---- start
interface ScenePluginNodeInfo {
uuid: string;
components: ScenePluginComponentInfo[];
}
// 场景插件传回的场景信息
export interface ScenePluginInfo {
// 选中节点列表
nodes: ScenePluginNodeInfo[];
// gizmo 的一些信息
gizmo: {
is2D: boolean;
};
// 当前编辑模式数组
modes: string[];
}
// 场景插件传回的组件信息
export interface ScenePluginComponentInfo {
uuid: string;
enabled: boolean;
type: string;
}
export interface QueryClassesOptions {
extends?: string | string[];
excludeSelf?: boolean;
}
// ---- 场景插件返回的 info 信息 ---- end
// ---- 动画数据 ---- start
export interface IKeyDumpData {
frame: number;
dump: any; // value的dump数据
inTangent?: number;
inTangentWeight?: number;
outTangent?: number;
outTangentWeight?: number;
interpMode?: number;
broken?: boolean;
tangentWeightMode?: number;
imgUrl?: string;
easingMethod?: number;
}
export interface IDumpType {
value: string;
extends?: string[];
}
export interface IPropCurveDumpData {
nodePath: string;
keyframes: IKeyDumpData[];
displayName: string;
key: string;
type?: IDumpType;
preExtrap: number;
postExtrap: number;
isCurveSupport: boolean; // 是否支持贝塞尔曲线编辑
}
export interface IAnimCopyKeySrcInfo {
curvesDump: IPropCurveDumpData[];
}
export interface IAnimCopyNodeSrcInfo {
curvesDump: IPropCurveDumpData[];
}
export interface IAnimCopyNodeDstInfo {
nodePath: string;
}
interface IEventDump {
frame: number;
func: string;
params: string[];
}
export interface IAnimCopyEventSrcInfo {
eventsDump: IEventDump[];
}
export interface IAnimCopyPropSrcInfo {
curvesDump: IPropCurveDumpData[];
}
export interface IAnimCopyPropDstInfo {
nodePath: string;
propKeys?: string[];
}
export interface IAnimCopyKeyDstInfo {
nodePath: string;
propKeys?: string[];
startFrame: number;
}
export interface IAnimCopyEventDstInfo {
startFrame: number;
}
// ---- 动画数据 ---- end
// ---- Contributions ---- start
export interface ContributionDropItem {
type: string;
message: string;
}
// ---- Contributions ---- end
export interface UnitTestInfo {
name: string;
}

View File

@@ -0,0 +1,27 @@
// 消息定义
interface MessageInterface {
params: any[],
result: any;
}
// host
export interface HostInfo {
host: string;
ip: string;
port: number;
}
// 消息定义
export interface main {
scene: {
[x: string]: MessageInterface;
'query-port': {
params: [],
result: number,
};
'scan-lan': {
params: [],
result: HostInfo[],
};
}
}

View File

@@ -0,0 +1,19 @@
export interface ShortcutItem {
when: string;
message: string;
shortcut: string;
pkgName: string;
rawShortcut?: string;
key: string;
missing?: boolean;
}
export type IShortcutItemMap = Record<string, ShortcutItem>;
export interface IShortcutEditInfo {
key: string;
shortcut: string;
searches: ShortcutItem[];
conflict: boolean;
when: string;
}

View File

@@ -0,0 +1,16 @@
# 项目简介
一份空白的扩展。
## 开发环境
Node.js
## 安装
```bash
# 安装依赖模块
npm install
# 构建
npm run build
```

View File

@@ -0,0 +1,16 @@
# Project Title
An blank extension.
## Development Environment
Node.js
## Install
```bash
# Install dependent modules
npm install
# build
npm run build
```

105
extensions/we_sane/dist/buildH5.js vendored Normal file
View File

@@ -0,0 +1,105 @@
var Fs = require("fs");
var fsextra = require("fs-extra");
var zlib = require('zlib');
var archiver = require('archiver');
const path = require('path');
module.exports = {
load() {
this.webUrl = Editor.Project.path + "/build/web-mobile"; //打h5包的路径
this.replaceUrl = Editor.Project.path + "/build/replace_H5"; //替换文件路径
this.indexUrl = this.webUrl + "/index.html"; //index 位置
//需要 复制 的文件
this.replaceUrls = [
"/weSane.js",
"/res",
]
/** index 需要写入的 数据 row行数 */
this.writeIndexData = [
{ rowNum: 35, writeType: 'ADD', str: ' <meta data-react-helmet="true" property="og:title" content="Game Name"> ' },
{ rowNum: 36, writeType: 'ADD', str: ' <meta data-react-helmet="true" property="og:description" content="Game Description">' },
{ rowNum: 37, writeType: 'ADD', str: ' <meta data-react-helmet="true" property="og:image" content="res/share.jpg"> ' },
{ rowNum: 38, writeType: 'ADD', str: ' <meta http-equiv="cache-control" content="no-cache,no-store,must-revalidate" /> ' },
{ rowNum: 39, writeType: 'ADD', str: ' <meta http-equiv="Pragma" content="no-cache" /> ' },
{ rowNum: 40, writeType: 'ADD', str: ' <meta http-equiv="expires" content="-1" /> ' },
{ rowNum: 49, writeType: 'ADD', str: '<div id="splash"><div class="progress-bar stripes"><span style="width: 0%"></span></div></div>' },
{ rowNum: 51, writeType: 'ADD', str: '<div id="loadingImg" style="top:24%;align:center;width:100%;position:absolute;z-index:10;"> <img style="border-radius: 15px;" src="res/logo.png" width="90" height="90" /> </div>' },
{ rowNum: 52, writeType: 'ADD', str: '<div id="webText" type="text" style="width:100%;text-align:center;position:absolute;top:38%;z-index:99;font-size:16px;color:#1e2222" > </div> ' },
{ rowNum: 53, writeType: 'ADD', str: '<div id="loadingText" type="text" style="width:100%;text-align:center;position:absolute;top:56%;z-index:99;font-size:17px;color:#e86c00" > </div> ' },
{ rowNum: 54, writeType: 'ADD', str: '<div style="align:center;display: none"><img src="res/share.jpg" width="10%" /></div> ' },
{ rowNum: 55, writeType: 'ADD', str: ' <script src="weSane.js" charset="utf-8"> </script> ' },
// { rowNum: 56, writeType: 'ADD', str: ' <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> ' },
// { rowNum: 57, writeType: 'ADD', str: ' <script src="https://www.wesane.com/Public/js/Anti-addiction.js"></script> ' },
]
},
/** 写入 index 文件 */
writeIndex() {
let indexStr = Fs.readFileSync(this.indexUrl, 'utf8');//读取文件
let strArr = indexStr.split("\n"); //按回车分割成数组
this.addStrArrData(this.writeIndexData, strArr);
let weSaneJs = Fs.readFileSync(this.replaceUrl + "/weSane.js", 'utf8').split("\n");
strArr[5] = " <title>" + weSaneJs[4].split('"')[1] + '</title>'; //修改标题
let writeStr = ""; //写入inex的字符串
for (let i = 0; i < strArr.length; i++) {
if (i < strArr.length - 1) {
writeStr += (strArr[i] + "\n");
}
}
Fs.writeFileSync(this.indexUrl, writeStr, 'utf8');
console.log("index 写入成功");
},
/** 复制文件 打包zip 的名字 */
copyFloder(zipName) {
for (let i = 0; i < this.replaceUrls.length; i++) {
fsextra.copySync(this.replaceUrl + this.replaceUrls[i], this.webUrl + this.replaceUrls[i], 0);
}
console.log("复制文件完成");
// this.gzip(Editor.Project.path + "/build/web-mobile/weSane.js" );
let outurl = Editor.Project.path + '/build/' + zipName + '.zip'; //zip 输出目录
this.deleteFile(outurl);
this.zipFolder(this.webUrl, outurl, function (err, msg) {
console.log(err, msg);
});
},
/** 删除文件 */
deleteFile(url) {
if (Fs.existsSync(url)) {
Fs.unlinkSync(url);
}
},
/** 打包Zip */
zipFolder(sourceFolder, destZip, cb, subdir) {
var output = Fs.createWriteStream(destZip);
var archive = archiver('zip', {
zlib: { level: 9 }
});
archive.pipe(output);
archive.directory(sourceFolder, subdir ? sourceFolder.substr(path.dirname(sourceFolder).length + 1) : false);
archive.finalize();
console.log("打包zip 完成");
},
/** 添加 需要写入的配置里面的字符串 */
addStrArrData(dataArr, strArr) {
for (let i = 0; i < dataArr.length; i++) {
if (dataArr[i].writeType == 'ADD') {
if (strArr[dataArr[i].rowNum - 1] == dataArr[i].str) { continue; }
strArr.splice(dataArr[i].rowNum - 1, 0, dataArr[i].str);
} else if (dataArr[i].writeType == 'REVISE') {
strArr[dataArr[i].rowNum - 1] = dataArr[i].str;
} else if (dataArr[i].writeType == 'DELETE') {
strArr[dataArr[i].rowNum - 1] = '';
}
}
}
};

44
extensions/we_sane/dist/main.js vendored Normal file
View File

@@ -0,0 +1,44 @@
var Fs = require("fs");
const buildH5 = require("./buildH5");
exports.methods = {
/** 打微伞包 */
weSaneZip(){
buildH5.writeIndex(); //写入Index
buildH5.copyFloder("web-mobile_WeSane"); //复制替换文件 并打包zip
},
/** 打通用包 */
commonH5Zip(){
buildH5.writeIndex(); //写入Index
buildH5.copyFloder("web-mobile_Other"); //复制替换文件 并打包zip
}
};
/**
* 启动的时候执行的初始化方法
*/
exports.load = function() {
buildH5.load(); //初始化 打包H5
console.warn("we_sane is not compiled yet.");
};
/**
* 插件被关闭的时候执行的卸载方法
*/
exports.unload = function() {};
/** buildH5Json 配置信息
* indexUrl index.html 的工程路径
* replaceUrls 替换文件的 配置路径
* webUrls 对应替换文件 需要copy 到 web里面的路径
* writeIndexData 写入index.html 文件的配置 rowNum:行数 str:需要添加的代码
*/

View File

@@ -0,0 +1 @@
"use strict";module.exports={description:"A blank extension"};

View File

@@ -0,0 +1 @@
"use strict";module.exports={description:"一份空白的扩展"};

View File

@@ -0,0 +1,39 @@
{
"package_version": 2,
"version": "1.0.0",
"name": "we_sane",
"description": "i18n:we_sane.description",
"main": "./dist/main.js",
"devDependencies": {
"@types/node": "^16.0.1",
"typescript": "^4.3.4"
},
"author": "Chao",
"editor": ">=3.4.0",
"scripts": {
"build": "tsc -b",
"watch": "tsc -w"
},
"contributions": {
"menu": [
{
"path": "weSane/上线H5",
"label": "打包-微伞zip",
"message": "build-wesane"
},
{
"path": "weSane/上线H5",
"label": "打包-通用zip",
"message": "build-h5"
}
],
"messages": {
"build-wesane": {
"methods": ["weSaneZip"]
},
"build-h5": {
"methods": ["commonH5Zip"]
}
}
}
}

View File

@@ -0,0 +1,18 @@
/**
* @en Registration method for the main process of Extension
* @zh 为扩展的主进程的注册方法
*/
export const methods: { [key: string]: (...any: any) => any } = { };
/**
* @en Hooks triggered after extension loading is complete
* @zh 扩展加载完成后触发的钩子
*/
export const load = function() { };
/**
* @en Hooks triggered after extension uninstallation is complete
* @zh 扩展卸载完成后触发的钩子
*/
export const unload = function() { };

View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2017",
"module": "CommonJS",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
}
}

View File

@@ -0,0 +1,181 @@
# CC3.WebEasyObfus
![Static Badge](https://img.shields.io/badge/Version-1.0.4-blue) ![Static Badge](https://img.shields.io/badge/CocosCreator-3.8.x-green) ![Static Badge](https://img.shields.io/badge/Tested_On-web-yellow)
*EN | [中文](/README-CN.md)
<p align="center"><img src="./logo.jpg" width="256"></p>
勾勾、选选就完事WebEasyObfus 混淆項目代码就这样简单。
> *「走過路過,別錯過,點顆星星,支持我!」*
> *(注:对插件的意见反馈请至 [Cocos 中文论坛](https://forum.cocos.org/t/topic/164539)。)*
## 安装方法
1. 下载项目成 ZIP 文件。
2. 解压后将内容复制到 `${your_project_path}/extensions/web-easy-obfus`
3. 打开终端
* 输入 `cd ${your_project_path}/extensions/web-zip-bundle`
* 安装扩展依赖包 `npm install`
* 构建扩展 `npm run build`
4. 到 Editor 菜单 Extension -> Extension Manager -> Installed 启用 `web-easy-obfus`
<p align="center"><img src="doc/img/extension_manager.png" width="450"></p>
>(*注:安装方法也可参考官方文档 [【扩展 安装与分享】](https://docs.cocos.com/creator/3.8/manual/zh/editor/extension/install.html) 。*)
## 如何使用
1. 到 Build Setting 新增 New Build Task 并选择平台 WebMobile/WebDesktop。到 Panel 中下拉找到 web-easy-obfus 选项。
* Enable启用或关闭功能。
* Include All Bundle: 包含所有Bundle
* 启用后,所有 Bundle 内的 JS 档案将进行混淆,否则只有 `assets/main/bundle.js` 下的JS档案将进行混淆。
* Select Obfus Level混淆分为五个等级等级越高混淆程度越彻底但文件体积会增大执行性能可能略受影响。
* 基础 (Minimal),對 javascript-obfuscator 設定分別為:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Disables converting code into a more complex control flow structure.
"controlFlowFlattening": false,
// Disables adding redundant dead code blocks.
"deadCodeInjection": false,
// Keeps global variable names unchanged.
"renameGlobals": false,
// Avoids extracting strings into a separate array for simplicity.
"stringArray": false
}
```
* 标准 (Standard),對 javascript-obfuscator 設定分別為:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to 75% of the code.
"controlFlowFlatteningThreshold": 0.75,
// Avoids injecting unnecessary dead code.
"deadCodeInjection": false,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Applies string array obfuscation to 75% of strings.
"stringArrayThreshold": 0.75
}
```
* 增强 (Enhanced),對 javascript-obfuscator 設定分別為:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to 90% of the code.
"controlFlowFlatteningThreshold": 0.9,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in 40% of places.
"deadCodeInjectionThreshold": 0.4,
// Keeps global variable names unchanged for compatibility.
"renameGlobals": false,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings in the array using Base64.
"stringArrayEncoding": ["base64"],
// Applies string array obfuscation to 90% of strings.
"stringArrayThreshold": 0.9,
// Obfuscates object keys for added security.
"transformObjectKeys": true
}
```
* 安全 (Secure),對 javascript-obfuscator 設定分別為:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to all code.
"controlFlowFlatteningThreshold": 1,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in 50% of places.
"deadCodeInjectionThreshold": 0.5,
// Renames global variables for better obfuscation.
"renameGlobals": true,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings in the array using RC4 encryption.
"stringArrayEncoding": ["rc4"],
// Applies string array obfuscation to all strings.
"stringArrayThreshold": 1,
// Obfuscates object keys for added security.
"transformObjectKeys": true
}
```
* 极致 (Ultimate),對 javascript-obfuscator 設定分別為:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to all code.
"controlFlowFlatteningThreshold": 1,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in all possible places.
"deadCodeInjectionThreshold": 1,
// Renames global variables for better obfuscation.
"renameGlobals": true,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings using both Base64 and RC4 encryption.
"stringArrayEncoding": ["base64", "rc4"],
// Applies string array obfuscation to all strings.
"stringArrayThreshold": 1,
// Obfuscates object keys for added security.
"transformObjectKeys": true,
// Converts characters to Unicode escape sequences for obfuscation.
"unicodeEscapeSequence": true,
// Replaces console output calls with empty functions to hide debugging messages.
"disableConsoleOutput": true
}
```
## CI/CD
本插件天然支持 CI/CD只需從 Build Panel 导出当前平台的构建选项配置为 json 文件,用于[命令行构建](https://docs.cocos.com/creator/3.8/manual/zh/editor/publish/publish-in-command-line.html)。
<p align="center"><img src="./doc/img/build_panel_export.png" width="400"></p>
## 参考文献
* [javascript-obfuscator](https://github.com/javascript-obfuscator/javascript-obfuscator)
* [Cocos Creator 官方的 UI 範例 GitHubCocos UI Example](https://github.com/cocos/cocos-example-ui)

View File

@@ -0,0 +1,173 @@
# CC3.WebEasyObfus
![Static Badge](https://img.shields.io/badge/Version-1.0.4-blue) ![Static Badge](https://img.shields.io/badge/CocosCreator-3.8.x-green) ![Static Badge](https://img.shields.io/badge/Tested_On-web-yellow)
Just check and select, and you're done, Obfuscator code with WebEasyObfus is simple.
> *"Dont just pass by, give it a try—hit a star and show your support!"*
> *NoteIf you have any feedback on the plugin, please go to [Cocos 中文论坛](https://forum.cocos.org/t/topic/163849)*
## Installation
1. Download ZIP package from github.
2. Decompress the file and copy the contents to `${your_project_path}/extensions/web-easy-obfus`.
3. Open the terminal
* `cd ${your_project_path}/extensions/web-zip-bundle`
* Enter `npm install`, install dependency packages.
* Enter `npm run build`, build the extension.
4. Go to the Editor menu `Extension -> Extension Manager -> Installed` to active the extension.
>*NoteYou can also check out the official docs for installation instructions. [【扩展 安装与分享】](https://docs.cocos.com/creator/3.8/manual/zh/editor/extension/install.html).*
## How to Use
1. Go to the Build Setting, add a Ner Build Task, and select the platform as WebModule or WebDesktop. In the Panel, scroll down to find the web-easy=obfus option.
* EnableTurn on/off the extension.
* Include All Bundle
* Check this: all bundle JS files will be obfuscated; otherwhile, only `assets/main/bundle.js` will be obfuscated.
* Select Obfus LevelThere are five levels of obfuscation. The higher the level, the more thorough the obfuscation, but the file size wil increase, and execution performance may be affected.
* MinimalThe settings for javascript-obfuscator are as folloes:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Disables converting code into a more complex control flow structure.
"controlFlowFlattening": false,
// Disables adding redundant dead code blocks.
"deadCodeInjection": false,
// Keeps global variable names unchanged.
"renameGlobals": false,
// Avoids extracting strings into a separate array for simplicity.
"stringArray": false
}
```
* StandardThe settings for javascript-obfuscator are as folloes:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to 75% of the code.
"controlFlowFlatteningThreshold": 0.75,
// Avoids injecting unnecessary dead code.
"deadCodeInjection": false,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Applies string array obfuscation to 75% of strings.
"stringArrayThreshold": 0.75
}
```
* EnhancedThe settings for javascript-obfuscator are as folloes:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to 90% of the code.
"controlFlowFlatteningThreshold": 0.9,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in 40% of places.
"deadCodeInjectionThreshold": 0.4,
// Keeps global variable names unchanged for compatibility.
"renameGlobals": false,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings in the array using Base64.
"stringArrayEncoding": ["base64"],
// Applies string array obfuscation to 90% of strings.
"stringArrayThreshold": 0.9,
// Obfuscates object keys for added security.
"transformObjectKeys": true
}
```
* SecureThe settings for javascript-obfuscator are as folloes:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to all code.
"controlFlowFlatteningThreshold": 1,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in 50% of places.
"deadCodeInjectionThreshold": 0.5,
// Renames global variables for better obfuscation.
"renameGlobals": true,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings in the array using RC4 encryption.
"stringArrayEncoding": ["rc4"],
// Applies string array obfuscation to all strings.
"stringArrayThreshold": 1,
// Obfuscates object keys for added security.
"transformObjectKeys": true
}
```
* UltimateThe settings for javascript-obfuscator are as folloes:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to all code.
"controlFlowFlatteningThreshold": 1,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in all possible places.
"deadCodeInjectionThreshold": 1,
// Renames global variables for better obfuscation.
"renameGlobals": true,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings using both Base64 and RC4 encryption.
"stringArrayEncoding": ["base64", "rc4"],
// Applies string array obfuscation to all strings.
"stringArrayThreshold": 1,
// Obfuscates object keys for added security.
"transformObjectKeys": true,
// Converts characters to Unicode escape sequences for obfuscation.
"unicodeEscapeSequence": true,
// Replaces console output calls with empty functions to hide debugging messages.
"disableConsoleOutput": true
}
```
## CI/CD
This exrension natively supports CI/CD. Export the current platform's build config as a JSON file from the Build Panel for [命令行构建](https://docs.cocos.com/creator/3.8/manual/zh/editor/publish/publish-in-command-line.html)。
## References
* [javascript-obfuscator](https://github.com/javascript-obfuscator/javascript-obfuscator)
* [Cocos Creator 官方的 UI 範例 GitHubCocos UI Example](https://github.com/cocos/cocos-example-ui)

View File

@@ -0,0 +1,183 @@
# CC3.WebEasyObfus
![Static Badge](https://img.shields.io/badge/Version-1.0.4-blue) ![Static Badge](https://img.shields.io/badge/CocosCreator-3.8.x-green) ![Static Badge](https://img.shields.io/badge/Tested_On-web-yellow)
*EN | [中文](/README-CN.md)
<p align="center"><img src="./logo.jpg" width="256"></p>
Just check and select, and you're done, Obfuscator code with WebEasyObfus is simple.
> *"Dont just pass by, give it a try—hit a star and show your support!"*
> *NoteIf you have any feedback on the plugin, please go to [Cocos 中文论坛](https://forum.cocos.org/t/topic/164539)*
## Installation
1. Download ZIP package from github.
2. Decompress the file and copy the contents to `${your_project_path}/extensions/web-easy-obfus`.
3. Open the terminal
* `cd ${your_project_path}/extensions/web-zip-bundle`
* Enter `npm install`, install dependency packages.
* Enter `npm run build`, build the extension.
4. Go to the Editor menu `Extension -> Extension Manager -> Installed` to active the extension.
<p align="center"><img src="doc/img/extension_manager.png" width="450"></p>
>*NoteYou can also check out the official docs for installation instructions. [【扩展 安装与分享】](https://docs.cocos.com/creator/3.8/manual/zh/editor/extension/install.html).*
## How to Use
1. Go to the Build Setting, add a Ner Build Task, and select the platform as WebModule or WebDesktop. In the Panel, scroll down to find the web-easy=obfus option.
* EnableTurn on/off the extension.
* Include All Bundle
* Check this: all bundle JS files will be obfuscated; otherwhile, only `assets/main/bundle.js` will be obfuscated.
* Select Obfus LevelThere are five levels of obfuscation. The higher the level, the more thorough the obfuscation, but the file size wil increase, and execution performance may be affected.
* MinimalThe settings for javascript-obfuscator are as folloes:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Disables converting code into a more complex control flow structure.
"controlFlowFlattening": false,
// Disables adding redundant dead code blocks.
"deadCodeInjection": false,
// Keeps global variable names unchanged.
"renameGlobals": false,
// Avoids extracting strings into a separate array for simplicity.
"stringArray": false
}
```
* StandardThe settings for javascript-obfuscator are as folloes:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to 75% of the code.
"controlFlowFlatteningThreshold": 0.75,
// Avoids injecting unnecessary dead code.
"deadCodeInjection": false,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Applies string array obfuscation to 75% of strings.
"stringArrayThreshold": 0.75
}
```
* EnhancedThe settings for javascript-obfuscator are as folloes:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to 90% of the code.
"controlFlowFlatteningThreshold": 0.9,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in 40% of places.
"deadCodeInjectionThreshold": 0.4,
// Keeps global variable names unchanged for compatibility.
"renameGlobals": false,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings in the array using Base64.
"stringArrayEncoding": ["base64"],
// Applies string array obfuscation to 90% of strings.
"stringArrayThreshold": 0.9,
// Obfuscates object keys for added security.
"transformObjectKeys": true
}
```
* SecureThe settings for javascript-obfuscator are as folloes:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to all code.
"controlFlowFlatteningThreshold": 1,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in 50% of places.
"deadCodeInjectionThreshold": 0.5,
// Renames global variables for better obfuscation.
"renameGlobals": true,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings in the array using RC4 encryption.
"stringArrayEncoding": ["rc4"],
// Applies string array obfuscation to all strings.
"stringArrayThreshold": 1,
// Obfuscates object keys for added security.
"transformObjectKeys": true
}
```
* UltimateThe settings for javascript-obfuscator are as folloes:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to all code.
"controlFlowFlatteningThreshold": 1,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in all possible places.
"deadCodeInjectionThreshold": 1,
// Renames global variables for better obfuscation.
"renameGlobals": true,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings using both Base64 and RC4 encryption.
"stringArrayEncoding": ["base64", "rc4"],
// Applies string array obfuscation to all strings.
"stringArrayThreshold": 1,
// Obfuscates object keys for added security.
"transformObjectKeys": true,
// Converts characters to Unicode escape sequences for obfuscation.
"unicodeEscapeSequence": true,
// Replaces console output calls with empty functions to hide debugging messages.
"disableConsoleOutput": true
}
```
## CI/CD
This exrension natively supports CI/CD. Export the current platform's build config as a JSON file from the Build Panel for [命令行构建](https://docs.cocos.com/creator/3.8/manual/zh/editor/publish/publish-in-command-line.html)。
<p align="center"><img src="./doc/img/build_panel_export.png" width="400"></p>
## References
* [javascript-obfuscator](https://github.com/javascript-obfuscator/javascript-obfuscator)
* [Cocos Creator 官方的 UI 範例 GitHubCocos UI Example](https://github.com/cocos/cocos-example-ui)

View File

@@ -0,0 +1,175 @@
# CC3.WebEasyObfus
![Static Badge](https://img.shields.io/badge/Version-1.0.4-blue) ![Static Badge](https://img.shields.io/badge/CocosCreator-3.8.x-green) ![Static Badge](https://img.shields.io/badge/Tested_On-web-yellow)
虽然 CC 在建置时已经做了些混淆,但为了不让有心人一键带走项目,进一步的保护就成了钢需。与此同时,开发团队的 "老板" 会预期这是简单勾勾、选选就完成事...,这就是 WebEasyObfus 要做的事。
> *「走過路過,別錯過,點顆星星,支持我!」*
> *(注:对插件的意见反馈请至 [Cocos 中文论坛](https://forum.cocos.org/t/topic/163849)。)*
## 安装方法
1. 下载项目成 ZIP 文件。
2. 解压后将内容复制到 `${your_project_path}/extensions/web-easy-obfus`
3. 打开终端
* 输入 `cd ${your_project_path}/extensions/web-zip-bundle`
* 安装扩展依赖包 `npm install`
* 构建扩展 `npm run build`
4. 到 Editor 菜单 Extension -> Extension Manager -> Installed 启用 `web-easy-obfus`
>(*注:安装方法也可参考官方文档 [【扩展 安装与分享】](https://docs.cocos.com/creator/3.8/manual/zh/editor/extension/install.html) 。*)
## 如何使用
1. 到 Build Setting 新增 New Build Task 并选择平台 WebMobile/WebDesktop。到 Panel 中下拉找到 web-easy-obfus 选项。
* Enable启用或关闭功能。
* Include All Bundle: 包含所有Bundle
* 启用后,所有 Bundle 内的 JS 档案将进行混淆,否则只有 `assets/main/bundle.js` 下的JS档案将进行混淆。
* Select Obfus Level混淆分为五个等级等级越高混淆程度越彻底但文件体积会增大执行性能可能略受影响。
* 基础 (Minimal),對 javascript-obfuscator 設定分別為:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Disables converting code into a more complex control flow structure.
"controlFlowFlattening": false,
// Disables adding redundant dead code blocks.
"deadCodeInjection": false,
// Keeps global variable names unchanged.
"renameGlobals": false,
// Avoids extracting strings into a separate array for simplicity.
"stringArray": false
}
```
* 标准 (Standard),對 javascript-obfuscator 設定分別為:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to 75% of the code.
"controlFlowFlatteningThreshold": 0.75,
// Avoids injecting unnecessary dead code.
"deadCodeInjection": false,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Applies string array obfuscation to 75% of strings.
"stringArrayThreshold": 0.75
}
```
* 增强 (Enhanced),對 javascript-obfuscator 設定分別為:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to 90% of the code.
"controlFlowFlatteningThreshold": 0.9,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in 40% of places.
"deadCodeInjectionThreshold": 0.4,
// Keeps global variable names unchanged for compatibility.
"renameGlobals": false,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings in the array using Base64.
"stringArrayEncoding": ["base64"],
// Applies string array obfuscation to 90% of strings.
"stringArrayThreshold": 0.9,
// Obfuscates object keys for added security.
"transformObjectKeys": true
}
```
* 安全 (Secure),對 javascript-obfuscator 設定分別為:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to all code.
"controlFlowFlatteningThreshold": 1,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in 50% of places.
"deadCodeInjectionThreshold": 0.5,
// Renames global variables for better obfuscation.
"renameGlobals": true,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings in the array using RC4 encryption.
"stringArrayEncoding": ["rc4"],
// Applies string array obfuscation to all strings.
"stringArrayThreshold": 1,
// Obfuscates object keys for added security.
"transformObjectKeys": true
}
```
* 极致 (Ultimate),對 javascript-obfuscator 設定分別為:
```javascript
{
// Minifies the output code to reduce file size.
"compact": true,
// Enables control flow flattening for added complexity.
"controlFlowFlattening": true,
// Applies control flow flattening to all code.
"controlFlowFlatteningThreshold": 1,
// Adds dead code to make reverse engineering harder.
"deadCodeInjection": true,
// Inserts dead code in all possible places.
"deadCodeInjectionThreshold": 1,
// Renames global variables for better obfuscation.
"renameGlobals": true,
// Moves strings into a separate array for obfuscation.
"stringArray": true,
// Encodes strings using both Base64 and RC4 encryption.
"stringArrayEncoding": ["base64", "rc4"],
// Applies string array obfuscation to all strings.
"stringArrayThreshold": 1,
// Obfuscates object keys for added security.
"transformObjectKeys": true,
// Converts characters to Unicode escape sequences for obfuscation.
"unicodeEscapeSequence": true,
// Replaces console output calls with empty functions to hide debugging messages.
"disableConsoleOutput": true
}
```
## CI/CD
本插件天然支持 CI/CD只需從 Build Panel 导出当前平台的构建选项配置为 json 文件,用于[命令行构建](https://docs.cocos.com/creator/3.8/manual/zh/editor/publish/publish-in-command-line.html)。
## 参考文献
* [javascript-obfuscator](https://github.com/javascript-obfuscator/javascript-obfuscator)
* [Cocos Creator 官方的 UI 範例 GitHubCocos UI Example](https://github.com/cocos/cocos-example-ui)

View File

@@ -0,0 +1,52 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.compressTextures = void 0;
const fs_extra_1 = require("fs-extra");
const compressTextures = (tasks) => __awaiter(void 0, void 0, void 0, function* () {
console.debug(`Execute compress task ${tasks}`);
for (let i = 0; i < tasks.length; i++) {
const task = tasks[i];
if (task.format !== 'jpg') {
continue;
}
// task.dest should change suffix before compress
task.dest = task.dest.replace('.png', '.jpg');
yield pngToJPG(task.src, task.dest, task.quality);
// The compress task have done needs to be removed from the original tasks
tasks.splice(i, 1);
i--;
}
});
exports.compressTextures = compressTextures;
function pngToJPG(src, dest, quality) {
return __awaiter(this, void 0, void 0, function* () {
const img = yield getImage(src);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const imageData = canvas.toDataURL('image/jpeg', quality / 100);
yield (0, fs_extra_1.outputFile)(dest, imageData);
console.debug('pngToJPG', dest);
});
}
function getImage(path) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function () {
resolve(img);
};
img.onerror = function (err) {
reject(err);
};
img.src = path.replace('#', '%23');
});
}

View File

@@ -0,0 +1,68 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.assetHandlers = exports.configs = exports.unload = exports.load = void 0;
const global_1 = require("./global");
const load = function () {
console.debug(`${global_1.PACKAGE_NAME} load`);
};
exports.load = load;
const unload = function () {
console.debug(`${global_1.PACKAGE_NAME} unload`);
};
exports.unload = unload;
const webEasyObfusConfig = {
hooks: './hooks',
options: {
enable: {
label: `i18n:${global_1.PACKAGE_NAME}.options.enable`,
description: `i18n:${global_1.PACKAGE_NAME}.options.enable`,
default: true,
render: {
ui: 'ui-checkbox',
}
},
includeAllBundle: {
label: `i18n:${global_1.PACKAGE_NAME}.options.includeAllBundle`,
description: `i18n:${global_1.PACKAGE_NAME}.options.includeAllBundle`,
default: true,
render: {
ui: 'ui-checkbox',
}
},
selectObfusLevel: {
label: `i18n:${global_1.PACKAGE_NAME}.options.selectObfusLevel`,
description: `i18n:${global_1.PACKAGE_NAME}.options.selectObfusLevelDescription`,
default: 'option2',
render: {
ui: 'ui-select',
items: [
{
label: `i18n:${global_1.PACKAGE_NAME}.options.selectObfusLevelOptions.option0`,
value: 'option0',
},
{
label: `i18n:${global_1.PACKAGE_NAME}.options.selectObfusLevelOptions.option1`,
value: 'option1',
},
{
label: `i18n:${global_1.PACKAGE_NAME}.options.selectObfusLevelOptions.option2`,
value: 'option2',
},
{
label: `i18n:${global_1.PACKAGE_NAME}.options.selectObfusLevelOptions.option3`,
value: 'option3',
},
{
label: `i18n:${global_1.PACKAGE_NAME}.options.selectObfusLevelOptions.option4`,
value: 'option4',
},
],
},
},
},
};
exports.configs = {
'web-mobile': webEasyObfusConfig,
'web-desktop': webEasyObfusConfig
};
exports.assetHandlers = './asset-handlers';

View File

@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PACKAGE_NAME = void 0;
exports.PACKAGE_NAME = 'web-easy-obfus';

190
extensions/web-easy-obfus/dist/hooks.js vendored Normal file
View File

@@ -0,0 +1,190 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.onAfterMake = exports.onBeforeMake = exports.onError = exports.unload = exports.onAfterBuild = exports.onAfterCompressSettings = exports.onBeforeCompressSettings = exports.onBeforeBuild = exports.load = exports.throwError = void 0;
const path_1 = __importDefault(require("path"));
const global_1 = require("./global");
const fs = __importStar(require("fs-extra"));
const javascript_obfuscator_1 = __importDefault(require("javascript-obfuscator"));
exports.throwError = true;
const load = function () {
return __awaiter(this, void 0, void 0, function* () {
});
};
exports.load = load;
const onBeforeBuild = function (options, result) {
return __awaiter(this, void 0, void 0, function* () {
});
};
exports.onBeforeBuild = onBeforeBuild;
const onBeforeCompressSettings = function (options, result) {
return __awaiter(this, void 0, void 0, function* () {
});
};
exports.onBeforeCompressSettings = onBeforeCompressSettings;
const onAfterCompressSettings = function (options, result) {
return __awaiter(this, void 0, void 0, function* () {
});
};
exports.onAfterCompressSettings = onAfterCompressSettings;
const onAfterBuild = function (options, result) {
return __awaiter(this, void 0, void 0, function* () {
const pkgOptions = options.packages[global_1.PACKAGE_NAME];
if (pkgOptions.enable) {
let obfuscationOptions = {};
if (pkgOptions.selectObfusLevel === 'option1') {
obfuscationOptions = {
"compact": true,
"controlFlowFlattening": true,
"controlFlowFlatteningThreshold": 0.75,
"deadCodeInjection": false,
"stringArray": true,
"stringArrayThreshold": 0.75 // Applies string array obfuscation to 75% of strings.
};
}
else if (pkgOptions.selectObfusLevel === 'option2') {
obfuscationOptions = {
"compact": true,
"controlFlowFlattening": true,
"controlFlowFlatteningThreshold": 0.9,
"deadCodeInjection": true,
"deadCodeInjectionThreshold": 0.4,
"renameGlobals": false,
"stringArray": true,
"stringArrayEncoding": ["base64"],
"stringArrayThreshold": 0.9,
"transformObjectKeys": true // Obfuscates object keys for added security.
};
}
else if (pkgOptions.selectObfusLevel === 'option3') {
obfuscationOptions = {
"compact": true,
"controlFlowFlattening": true,
"controlFlowFlatteningThreshold": 1,
"deadCodeInjection": true,
"deadCodeInjectionThreshold": 0.5,
"renameGlobals": true,
"stringArray": true,
"stringArrayEncoding": ["rc4"],
"stringArrayThreshold": 1,
"transformObjectKeys": true // Obfuscates object keys for added security.
};
}
else if (pkgOptions.selectObfusLevel === 'option4') {
obfuscationOptions = {
"compact": true,
"controlFlowFlattening": true,
"controlFlowFlatteningThreshold": 1,
"deadCodeInjection": true,
"deadCodeInjectionThreshold": 1,
"renameGlobals": true,
"stringArray": true,
"stringArrayEncoding": ["base64", "rc4"],
"stringArrayThreshold": 1,
"transformObjectKeys": true,
"unicodeEscapeSequence": true,
"disableConsoleOutput": true // Replaces console output calls with empty functions to hide debugging messages.
};
}
else {
obfuscationOptions = {
"compact": true,
"controlFlowFlattening": false,
"deadCodeInjection": false,
"renameGlobals": false,
"stringArray": false // Avoids extracting strings into a separate array for simplicity.
};
}
const BUILD_DEST_DIR = result.dest;
if (pkgOptions.includeAllBundle) {
findAndObfusJSFile(path_1.default.join(BUILD_DEST_DIR, 'assets'), obfuscationOptions);
}
else {
findAndObfusJSFile(path_1.default.join(BUILD_DEST_DIR, 'assets', 'main'), obfuscationOptions);
}
}
});
};
exports.onAfterBuild = onAfterBuild;
const unload = function () {
return __awaiter(this, void 0, void 0, function* () {
});
};
exports.unload = unload;
const onError = function (options, result) {
return __awaiter(this, void 0, void 0, function* () {
});
};
exports.onError = onError;
const onBeforeMake = function (root, options) {
return __awaiter(this, void 0, void 0, function* () {
});
};
exports.onBeforeMake = onBeforeMake;
const onAfterMake = function (root, options) {
return __awaiter(this, void 0, void 0, function* () {
});
};
exports.onAfterMake = onAfterMake;
function findAndObfusJSFile(dir, options) {
const items = fs.readdirSync(dir);
for (const item of items) {
const fullPath = path_1.default.join(dir, item);
if (fs.lstatSync(fullPath).isDirectory()) {
findAndObfusJSFile(fullPath, options);
}
else if (item.endsWith('.js')) {
console.log('Found js-file:', fullPath);
fs.readFile(fullPath, 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
const obfuscatedData = javascript_obfuscator_1.default.obfuscate(data, options).getObfuscatedCode();
fs.writeFile(fullPath, obfuscatedData, (err) => {
if (err) {
console.error(err);
return;
}
console.log('Obfuscated:', fullPath);
});
});
return true;
}
}
return false;
}

78
extensions/web-easy-obfus/dist/panel.js vendored Normal file
View File

@@ -0,0 +1,78 @@
'use strict';
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.close = exports.ready = exports.update = exports.$ = exports.template = exports.style = void 0;
const global_1 = require("./global");
let panel;
exports.style = ``;
exports.template = `
<div class="build-plugin">
<ui-prop>
<ui-label slot="label" value="Hide Link"></ui-label>
<ui-checkbox slot="content"></ui-checkbox>
</ui-prop>
<ui-prop id="link">
<ui-label slot="label" value="Docs"></ui-label>
<ui-link slot="content" value=${Editor.Utils.Url.getDocUrl('editor/publish/custom-build-plugin.html')}></ui-link>
</ui-prop>
</div>
`;
exports.$ = {
root: '.build-plugin',
hideLink: 'ui-checkbox',
link: '#link',
};
/**
* all change of options dispatched will enter here
* @param options
* @param key
* @returns
*/
function update(options, key) {
return __awaiter(this, void 0, void 0, function* () {
if (key) {
return;
}
// when import build options, key will bey ''
init();
});
}
exports.update = update;
function ready(options) {
// @ts-ignore
panel = this;
panel.options = options;
init();
}
exports.ready = ready;
function close() {
panel.$.hideLink.removeEventListener('change', onHideLinkChange);
}
exports.close = close;
function init() {
panel.$.hideLink.value = panel.options.hideLink;
updateLink();
panel.$.hideLink.addEventListener('change', onHideLinkChange);
}
function onHideLinkChange(event) {
panel.options.hideLink = event.target.value;
// Note: dispatch the change to build panel
panel.dispatch('update', `packages.${global_1.PACKAGE_NAME}.hideLink`, panel.options.hideLink);
updateLink();
}
function updateLink() {
if (panel.options.hideLink) {
panel.$.link.style.display = 'none';
}
else {
panel.$.link.style.display = 'block';
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 KiB

View File

@@ -0,0 +1,17 @@
module.exports = {
title: "Web Easy Obfus",
description: "Easily obfuscate the project codebase!",
options: {
enable: "Enable",
includeAllBundle: "Include All Bundle",
selectObfusLevel: "Select Obfus Level",
selectObfusLevelDescription: "",
selectObfusLevelOptions: {
option0: "Minimal",
option1: "Standard",
option2: "Enhanced",
option3: "Secure",
option4: "Ultimate",
}
},
};

View File

@@ -0,0 +1,17 @@
module.exports = {
title: "Web Easy Obfus",
description: "混淆項目代码就这样简单!",
options: {
enable: "启用",
includeAllBundle: "包含所有Bundle",
selectObfusLevel: "选择混淆等级",
selectObfusLevelDescription: "",
selectObfusLevelOptions: {
option0: "基础",
option1: "标准",
option2: "增强",
option3: "安全",
option4: "极致",
}
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@@ -0,0 +1,28 @@
{
"$schema": "./@types/schema/package/index.json",
"package_version": 2,
"name": "web-easy-obfus",
"version": "1.0.4",
"author": "ericlin09@gmail.com",
"editor": ">=3.7.1",
"type": "module",
"scripts": {
"preinstall": "node ./scripts/preinstall.js",
"build": "tsc"
},
"title": "i18n:web-easy-obfus.title",
"description": "i18n:web-easy-obfus.description",
"contributions": {
"builder": "./dist/builder.js"
},
"dependencies": {
"fs-extra": "^10.0.0",
"typescript": "^4.3.4"
},
"devDependencies": {
"@cocos/creator-types": "^3.8.3",
"@types/fs-extra": "^5.0.4",
"@types/node": "^18.17.1",
"javascript-obfuscator": "^4.1.1"
}
}