finder-refferenct3/dist/main.js
2024-10-31 10:37:08 +08:00

473 lines
18 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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() { }