mirror of
https://gitee.com/abc126655/finder-refferenct3
synced 2024-12-25 03:08:24 +00:00
473 lines
18 KiB
JavaScript
473 lines
18 KiB
JavaScript
|
"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());
|
|||
|
});
|
|||
|
};
|
|||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|||
|
};
|
|||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|||
|
exports.methods = void 0;
|
|||
|
exports.load = load;
|
|||
|
exports.unload = unload;
|
|||
|
const ObjectUtil_1 = __importDefault(require("./util/ObjectUtil"));
|
|||
|
const FileUtil_1 = __importDefault(require("./util/FileUtil"));
|
|||
|
const package_json_1 = __importDefault(require("../package.json"));
|
|||
|
const Fs = require('fs');
|
|||
|
const Path = require('path');
|
|||
|
const EXTENSION_NAME = '🔎';
|
|||
|
/** 扩展名对应文件类型 */
|
|||
|
const typeMap = {
|
|||
|
'.fire': 'scene',
|
|||
|
'.prefab': 'prefab',
|
|||
|
'.anim': 'animation',
|
|||
|
'.mtl': 'material',
|
|||
|
'.fnt.meta': 'font',
|
|||
|
};
|
|||
|
/** 类型对应图标 */
|
|||
|
const iconMap = {
|
|||
|
'scene': '📺',
|
|||
|
'prefab': '🧸',
|
|||
|
'node': '💾',
|
|||
|
'component': '💿',
|
|||
|
'property': '🎲',
|
|||
|
'asset': '📦',
|
|||
|
};
|
|||
|
const translate = (key) => `H.${key}`;
|
|||
|
/**
|
|||
|
* @en Registration method for the main process of Extension
|
|||
|
* @zh 为扩展的主进程的注册方法
|
|||
|
*/
|
|||
|
exports.methods = {
|
|||
|
openDebug() {
|
|||
|
console.log('openDebug');
|
|||
|
Editor.Panel.open(package_json_1.default.name);
|
|||
|
},
|
|||
|
/**
|
|||
|
* @en A method that can be triggered by message
|
|||
|
* @zh 通过 message 触发的方法
|
|||
|
*/
|
|||
|
findCurrentSelection() {
|
|||
|
return __awaiter(this, void 0, void 0, function* () {
|
|||
|
const uuids = Editor.Selection.getSelected('asset');
|
|||
|
if (uuids.length === 0) {
|
|||
|
console.log(`[${EXTENSION_NAME}]`, '先选择资源');
|
|||
|
return;
|
|||
|
}
|
|||
|
// 根据 uuid 查找
|
|||
|
for (let i = 0; i < uuids.length; i++) {
|
|||
|
yield this.module.methods.findViaUuid(uuids[i]);
|
|||
|
}
|
|||
|
});
|
|||
|
},
|
|||
|
/**
|
|||
|
* 使用 uuid 进行查找
|
|||
|
* @param {string} uuid
|
|||
|
*/
|
|||
|
findViaUuid(uuid) {
|
|||
|
return __awaiter(this, void 0, void 0, function* () {
|
|||
|
// 是否为有效 uuid
|
|||
|
if (!Editor.Utils.UUID.isUUID(uuid)) {
|
|||
|
console.log(`[${EXTENSION_NAME}]`, 'invalidUuid' + uuid);
|
|||
|
return;
|
|||
|
}
|
|||
|
// 获取资源信息
|
|||
|
const assetInfo = yield Editor.Message.request('asset-db', 'query-asset-info', uuid);
|
|||
|
if (assetInfo) {
|
|||
|
const url = assetInfo.url.replace('db://', '');
|
|||
|
// 暂不查找文件夹
|
|||
|
if (assetInfo.type === 'folder') {
|
|||
|
console.log(`[${EXTENSION_NAME}]`, 'notSupportFolder' + url);
|
|||
|
return;
|
|||
|
}
|
|||
|
// 处理文件路径 & 打印头部日志
|
|||
|
const urlItems = url.split('/');
|
|||
|
if (!urlItems[urlItems.length - 1].includes('.')) {
|
|||
|
urlItems.splice(urlItems.length - 1);
|
|||
|
}
|
|||
|
console.log(`[${EXTENSION_NAME}]`, 'findAssetRefs' + urlItems.join('/'));
|
|||
|
// 记录子资源 uuid
|
|||
|
const subUuids = assetInfo ? [] : null;
|
|||
|
// 资源类型检查
|
|||
|
if (assetInfo.type === "cc.ImageAsset") {
|
|||
|
// 纹理子资源
|
|||
|
uuid = uuid + '@f9941';
|
|||
|
}
|
|||
|
else if (assetInfo.type === 'cc.Script') {
|
|||
|
// 脚本
|
|||
|
uuid = Editor.Utils.UUID.compressUUID(uuid, true);
|
|||
|
}
|
|||
|
// 查找
|
|||
|
const results = uuid ? yield this.findReferences(uuid) : [];
|
|||
|
// Done
|
|||
|
this.printResult(results);
|
|||
|
}
|
|||
|
else {
|
|||
|
// 不存在的资源,直接查找 uuid
|
|||
|
console.log(`[${EXTENSION_NAME}]`, 'findAssetRefs' + uuid);
|
|||
|
const result = yield this.findReferences(uuid);
|
|||
|
// Done
|
|||
|
this.printResult(result);
|
|||
|
}
|
|||
|
});
|
|||
|
},
|
|||
|
/**
|
|||
|
* 获取对象中是否包含指定值以及相应属性名
|
|||
|
* @param {object} object 对象
|
|||
|
* @param {any} value 值
|
|||
|
* @returns {{ contains: boolean, property: string }}
|
|||
|
*/
|
|||
|
getContainsInfo(object, value) {
|
|||
|
const result = {
|
|||
|
contains: false,
|
|||
|
property: null
|
|||
|
};
|
|||
|
// 搜索
|
|||
|
const search = (target, parentKey) => {
|
|||
|
if (ObjectUtil_1.default.isObject(target)) {
|
|||
|
for (const key in target) {
|
|||
|
if (target[key] === value) {
|
|||
|
result.contains = true;
|
|||
|
result.property = parentKey;
|
|||
|
return;
|
|||
|
}
|
|||
|
search(target[key], key);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (Array.isArray(target)) {
|
|||
|
for (let i = 0, l = target.length; i < l; i++) {
|
|||
|
search(target[i], parentKey);
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
search(object, null);
|
|||
|
// Done
|
|||
|
return result;
|
|||
|
},
|
|||
|
/**
|
|||
|
* 查找节点中的引用
|
|||
|
* @param {object} node 目标节点
|
|||
|
* @param {string} uuid 查找的 uuid
|
|||
|
* @param {object} nodeTree 节点所在的节点树
|
|||
|
* @param {object[]} container 结果容器
|
|||
|
*/
|
|||
|
findRefsInNode(node, uuid, nodeTree, container) {
|
|||
|
return __awaiter(this, void 0, void 0, function* () {
|
|||
|
// 检查节点上的组件是否有引用
|
|||
|
const components = node.components;
|
|||
|
if (components && components.length > 0) {
|
|||
|
for (let i = 0, l = components.length; i < l; i++) {
|
|||
|
const info = this.getContainsInfo(components[i], uuid);
|
|||
|
if (!info.contains)
|
|||
|
continue;
|
|||
|
// 资源类型
|
|||
|
let type = components[i]['__type__'];
|
|||
|
// 是否为脚本资源
|
|||
|
if (Editor.Utils.UUID.isUUID(type)) {
|
|||
|
const scriptUuid = Editor.Utils.UUID.decompressUUID(type), assetInfo = yield Editor.Message.request('asset-db', 'query-asset-info', scriptUuid);
|
|||
|
type = Path.basename(assetInfo.url);
|
|||
|
}
|
|||
|
// 处理属性名称
|
|||
|
let property = info.property;
|
|||
|
if (property) {
|
|||
|
// Label 组件需要特殊处理
|
|||
|
if (type === 'cc.Label' && property === '_N$file') {
|
|||
|
property = 'font';
|
|||
|
}
|
|||
|
else {
|
|||
|
// 去除属性名的前缀
|
|||
|
if (property.startsWith('_N$')) {
|
|||
|
property = property.replace('_N$', '');
|
|||
|
}
|
|||
|
else if (property[0] === '_') {
|
|||
|
property = property.substring(1);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
// 保存结果
|
|||
|
container.push({
|
|||
|
node: node.path,
|
|||
|
component: type,
|
|||
|
property: property
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
// 检查预制体是否有引用
|
|||
|
const prefab = node.prefab;
|
|||
|
if (prefab) {
|
|||
|
// 排除预制体自己
|
|||
|
if (uuid !== nodeTree['__uuid__']) {
|
|||
|
const contains = ObjectUtil_1.default.containsValue(prefab, uuid);
|
|||
|
if (contains) {
|
|||
|
container.push({
|
|||
|
node: node.path
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
// 遍历子节点
|
|||
|
const children = node.children;
|
|||
|
if (children && children.length > 0) {
|
|||
|
for (let i = 0, l = children.length; i < l; i++) {
|
|||
|
yield this.findRefsInNode(children[i], uuid, nodeTree, container);
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
},
|
|||
|
/**
|
|||
|
* 查找引用
|
|||
|
* @param {string} uuid
|
|||
|
* @returns {object[]}
|
|||
|
*/
|
|||
|
findReferences(uuid) {
|
|||
|
return __awaiter(this, void 0, void 0, function* () {
|
|||
|
const results = [];
|
|||
|
/**
|
|||
|
* 文件处理函数
|
|||
|
* @param {string} filePath 文件路径
|
|||
|
* @param {Fs.Stats} stats
|
|||
|
*/
|
|||
|
const searchHandler = (filePath, stats) => __awaiter(this, void 0, void 0, function* () {
|
|||
|
const extname = Path.extname(filePath);
|
|||
|
// 场景和预制体资源
|
|||
|
if (extname === '.fire' || extname === '.prefab') {
|
|||
|
// 将资源数据转为节点树
|
|||
|
const nodeTree = this.getNodeTree(filePath), children = nodeTree.children, refs = [];
|
|||
|
// 遍历节点
|
|||
|
for (let i = 0, l = children.length; i < l; i++) {
|
|||
|
yield this.findRefsInNode(children[i], uuid, nodeTree, refs);
|
|||
|
}
|
|||
|
// 保存当前文件引用结果
|
|||
|
if (refs.length > 0) {
|
|||
|
results.push({
|
|||
|
type: typeMap[extname],
|
|||
|
fileUrl: yield Editor.Message.request('asset-db', 'query-url', filePath), //Editor.assetdb.fspathToUrl(),
|
|||
|
refs: refs
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
// 动画资源
|
|||
|
else if (extname === '.anim') {
|
|||
|
const data = JSON.parse(Fs.readFileSync(filePath)), curveData = data['curveData'], contains = ObjectUtil_1.default.containsValue(curveData, uuid);
|
|||
|
if (contains) {
|
|||
|
results.push({
|
|||
|
type: typeMap[extname],
|
|||
|
fileUrl: yield Editor.Message.request('asset-db', 'query-url', filePath)
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
// 材质和字体资源
|
|||
|
else if (extname === '.mtl' || filePath.indexOf('.fnt.meta') !== -1) {
|
|||
|
const data = JSON.parse(Fs.readFileSync(filePath)), contains = ObjectUtil_1.default.containsValue(data, uuid);
|
|||
|
if (contains && !(data['uuid'] === uuid)) {
|
|||
|
const _extname = (extname === '.mtl') ? '.mtl' : '.fnt.meta';
|
|||
|
results.push({
|
|||
|
type: typeMap[_extname],
|
|||
|
fileUrl: yield Editor.Message.request('asset-db', 'query-url', filePath)
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
// 遍历资源目录下的文件
|
|||
|
const assetsPath = yield Editor.Message.request('asset-db', 'query-path', 'db://assets/');
|
|||
|
yield FileUtil_1.default.map(assetsPath, searchHandler);
|
|||
|
// Done
|
|||
|
return results;
|
|||
|
});
|
|||
|
},
|
|||
|
/**
|
|||
|
* 获取节点树
|
|||
|
* @param {string} filePath 文件路径
|
|||
|
* @returns {object}
|
|||
|
*/
|
|||
|
getNodeTree(filePath) {
|
|||
|
// 获取缓存
|
|||
|
let cache = this.cache;
|
|||
|
if (!cache) {
|
|||
|
cache = this.cache = Object.create(null);
|
|||
|
}
|
|||
|
// 从缓存中读取
|
|||
|
if (!cache[filePath]) {
|
|||
|
// 将资源数据转为节点树
|
|||
|
const data = JSON.parse(Fs.readFileSync(filePath));
|
|||
|
cache[filePath] = this.convertToNodeTree(data);
|
|||
|
}
|
|||
|
// Done
|
|||
|
return cache[filePath];
|
|||
|
},
|
|||
|
/**
|
|||
|
* 读取节点
|
|||
|
* @param {object} source 元数据
|
|||
|
* @param {number} id 节点 ID
|
|||
|
* @param {object} parent 父容器
|
|||
|
*/
|
|||
|
convertNode(source, id, parent) {
|
|||
|
const node = Object.create(null), sourceData = source[id];
|
|||
|
// 基本信息
|
|||
|
node['__id__'] = id;
|
|||
|
node['_name'] = sourceData['_name'];
|
|||
|
node['__type__'] = sourceData['__type__'];
|
|||
|
// 路径
|
|||
|
const parentPath = parent.path || (parent['_name'] || null);
|
|||
|
node.path = (parentPath ? `${parentPath}/` : '') + node['_name'];
|
|||
|
// 组件
|
|||
|
const components = sourceData['_components'];
|
|||
|
if (components && components.length > 0) {
|
|||
|
const _components = node.components = [];
|
|||
|
for (let i = 0, l = components.length; i < l; i++) {
|
|||
|
const sourceComponent = source[components[i]['__id__']];
|
|||
|
_components.push(this.extractValidInfo(sourceComponent));
|
|||
|
}
|
|||
|
}
|
|||
|
// 预制体引用
|
|||
|
const prefab = sourceData['_prefab'];
|
|||
|
if (prefab) {
|
|||
|
const realPrefab = source[prefab['__id__']];
|
|||
|
node.prefab = this.extractValidInfo(realPrefab);
|
|||
|
}
|
|||
|
// 子节点
|
|||
|
const children = sourceData['_children'];
|
|||
|
if (children && children.length > 0) {
|
|||
|
node.children = [];
|
|||
|
for (let i = 0, l = children.length; i < l; i++) {
|
|||
|
const childId = children[i]['__id__'];
|
|||
|
this.convertNode(source, childId, node);
|
|||
|
}
|
|||
|
}
|
|||
|
// 保存数据
|
|||
|
parent.children.push(node);
|
|||
|
},
|
|||
|
/**
|
|||
|
* 提取有效信息(含有 uuid)
|
|||
|
* @param {object} data 元数据
|
|||
|
* @returns {{ __type__: string, _name: string, fileId?: string }}
|
|||
|
*/
|
|||
|
extractValidInfo(data) {
|
|||
|
const info = Object.create(null);
|
|||
|
// 记录有用的属性
|
|||
|
const keys = ['__type__', '_name', 'fileId'];
|
|||
|
for (let i = 0, l = keys.length; i < l; i++) {
|
|||
|
const key = keys[i];
|
|||
|
if (data[key]) {
|
|||
|
info[key] = data[key];
|
|||
|
}
|
|||
|
}
|
|||
|
// 记录包含 uuid 的属性
|
|||
|
for (const key in data) {
|
|||
|
if (ObjectUtil_1.default.containsProperty(data[key], '__uuid__')) {
|
|||
|
info[key] = data[key];
|
|||
|
}
|
|||
|
}
|
|||
|
// Done
|
|||
|
return info;
|
|||
|
},
|
|||
|
/**
|
|||
|
* 将资源数据转为节点树
|
|||
|
* @param {object} source 元数据
|
|||
|
* @returns {object}
|
|||
|
*/
|
|||
|
convertToNodeTree(source) {
|
|||
|
const nodeTree = Object.create(null), type = source[0]['__type__'];
|
|||
|
// 场景资源
|
|||
|
if (type === 'cc.SceneAsset') {
|
|||
|
const sceneId = source[0]['scene']['__id__'], children = source[sceneId]['_children'];
|
|||
|
nodeTree['__type__'] = 'cc.Scene'; // 类型
|
|||
|
nodeTree['__id__'] = sceneId; // ID
|
|||
|
// 遍历节点
|
|||
|
nodeTree.children = [];
|
|||
|
for (let i = 0, l = children.length; i < l; i++) {
|
|||
|
const nodeId = children[i]['__id__'];
|
|||
|
this.convertNode(source, nodeId, nodeTree);
|
|||
|
}
|
|||
|
}
|
|||
|
// 预制体资源
|
|||
|
else if (type === 'cc.Prefab') {
|
|||
|
const uuid = source[source.length - 1]['asset']['__uuid__'];
|
|||
|
nodeTree['__type__'] = 'cc.Prefab'; // 类型
|
|||
|
nodeTree['__uuid__'] = uuid; // uuid
|
|||
|
// 从根节点开始读取
|
|||
|
nodeTree.children = [];
|
|||
|
const rootId = source[0]['data']['__id__'];
|
|||
|
this.convertNode(source, rootId, nodeTree);
|
|||
|
}
|
|||
|
// Done
|
|||
|
return nodeTree;
|
|||
|
},
|
|||
|
printResult(results) {
|
|||
|
if (results.length === 0) {
|
|||
|
console.log(`[${EXTENSION_NAME}]`, 'noRefs');
|
|||
|
console.log(`${'----'.repeat(36)}`);
|
|||
|
return;
|
|||
|
}
|
|||
|
// 添加引用
|
|||
|
const nodeRefs = [];
|
|||
|
let nodeRefsCount = 0;
|
|||
|
const assetRefs = [];
|
|||
|
let assetRefsCount = 0;
|
|||
|
for (let i = 0, l = results.length; i < l; i++) {
|
|||
|
const result = results[i], type = result.type, url = result.fileUrl.replace('db://', '').replace('.meta', '');
|
|||
|
if (type === 'scene' || type === 'prefab') {
|
|||
|
nodeRefs.push(` · ${iconMap[type]} [${translate(type)}] ${url}`);
|
|||
|
const refs = result.refs;
|
|||
|
for (let j = 0, n = refs.length; j < n; j++) {
|
|||
|
nodeRefsCount++;
|
|||
|
{
|
|||
|
const ref = refs[j];
|
|||
|
let item = ` ${iconMap['node']} [${translate('node')}] ${ref.node}`;
|
|||
|
if (ref.component) {
|
|||
|
item += ` → ${iconMap['component']} [${translate('component')}] ${ref.component}`;
|
|||
|
}
|
|||
|
if (ref.property) {
|
|||
|
item += ` → ${iconMap['property']} [${translate('property')}] ${ref.property}`;
|
|||
|
}
|
|||
|
nodeRefs.push(item);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
assetRefsCount++;
|
|||
|
assetRefs.push(` · ${iconMap['asset']} [${translate(type)}] ${url}`);
|
|||
|
}
|
|||
|
}
|
|||
|
// 合并
|
|||
|
const texts = [`[${EXTENSION_NAME}] ${translate('searchResult')} >>>`];
|
|||
|
if (nodeRefs.length > 0) {
|
|||
|
nodeRefs.unshift(` 📙 ${translate('引用数')} x ${nodeRefsCount}`);
|
|||
|
texts.push(...nodeRefs);
|
|||
|
}
|
|||
|
if (assetRefs.length > 0) {
|
|||
|
assetRefs.unshift(` 📘 ${translate('资源引用数')} x ${assetRefsCount}`);
|
|||
|
texts.push(...assetRefs);
|
|||
|
}
|
|||
|
// 分隔
|
|||
|
texts.push(`${'----'.repeat(36)}`);
|
|||
|
// 打印到控制台
|
|||
|
if (this.expand) {
|
|||
|
// 逐行打印
|
|||
|
for (let i = 0, l = texts.length; i < l; i++) {
|
|||
|
console.log(texts[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
// 单行打印
|
|||
|
const content = texts.join('\n');
|
|||
|
console.log(content);
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
/**
|
|||
|
* @en Method Triggered on Extension Startup
|
|||
|
* @zh 扩展启动时触发的方法
|
|||
|
*/
|
|||
|
function load() { }
|
|||
|
/**
|
|||
|
* @en Method triggered when uninstalling the extension
|
|||
|
* @zh 卸载扩展时触发的方法
|
|||
|
*/
|
|||
|
function unload() { }
|