mirror of
https://gitee.com/abc126655/finder-refferenct3
synced 2024-12-24 02:38:23 +00:00
第一版本的初略修改,
This commit is contained in:
parent
5dee50969c
commit
824da453f3
22
base.tsconfig.json
Normal file
22
base.tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"experimentalDecorators": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./source",
|
||||
"types": [
|
||||
"node",
|
||||
"@cocos/creator-types/editor",
|
||||
]
|
||||
}
|
||||
}
|
472
dist/main.js
vendored
Normal file
472
dist/main.js
vendored
Normal file
@ -0,0 +1,472 @@
|
||||
"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() { }
|
1
dist/panels/default/index.js
vendored
Normal file
1
dist/panels/default/index.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const fs_extra_1=require("fs-extra"),path_1=require("path"),vue_1=require("vue"),panelDataMap=new WeakMap;module.exports=Editor.Panel.define({listeners:{show(){console.log("show")},hide(){console.log("hide")}},template:(0,fs_extra_1.readFileSync)((0,path_1.join)(__dirname,"../../../static/template/default/index.html"),"utf-8"),style:(0,fs_extra_1.readFileSync)((0,path_1.join)(__dirname,"../../../static/style/default/index.css"),"utf-8"),$:{app:"#app",text:"#text"},methods:{hello(){this.$.text&&(this.$.text.innerHTML="hello",console.log("[cocos-panel-html.default]: hello"))}},ready(){var e;this.$.text&&(this.$.text.innerHTML="Hello Cocos."),this.$.app&&((e=(0,vue_1.createApp)({})).config.compilerOptions.isCustomElement=e=>e.startsWith("ui-"),e.component("MyCounter",{template:(0,fs_extra_1.readFileSync)((0,path_1.join)(__dirname,"../../../static/template/vue/counter.html"),"utf-8"),data(){return{counter:0}},methods:{addition(){this.counter+=1},subtraction(){--this.counter}}}),e.mount(this.$.app),panelDataMap.set(this,e))},beforeClose(){},close(){var e=panelDataMap.get(this);e&&e.unmount()}});
|
80
dist/util/FileUtil.js
vendored
Normal file
80
dist/util/FileUtil.js
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
"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 });
|
||||
const Fs = require('fs');
|
||||
const Path = require('path');
|
||||
/**
|
||||
* 文件工具
|
||||
*/
|
||||
class FileUtil {
|
||||
/**
|
||||
* 复制文件/文件夹
|
||||
* @param {Fs.PathLike} srcPath 源路径
|
||||
* @param {Fs.PathLike} destPath 目标路径
|
||||
*/
|
||||
static copy(srcPath, destPath) {
|
||||
if (!Fs.existsSync(srcPath))
|
||||
return;
|
||||
const stats = Fs.statSync(srcPath);
|
||||
if (stats.isDirectory()) {
|
||||
if (!Fs.existsSync(destPath))
|
||||
Fs.mkdirSync(destPath);
|
||||
const names = Fs.readdirSync(srcPath);
|
||||
for (const name of names) {
|
||||
this.copy(Path.join(srcPath, name), Path.join(destPath, name));
|
||||
}
|
||||
}
|
||||
else if (stats.isFile()) {
|
||||
Fs.writeFileSync(destPath, Fs.readFileSync(srcPath));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 删除文件/文件夹
|
||||
* @param {Fs.PathLike} path 路径
|
||||
*/
|
||||
static delete(path) {
|
||||
if (!Fs.existsSync(path))
|
||||
return;
|
||||
const stats = Fs.statSync(path);
|
||||
if (stats.isDirectory()) {
|
||||
const names = Fs.readdirSync(path);
|
||||
for (const name of names) {
|
||||
this.delete(Path.join(path, name));
|
||||
}
|
||||
Fs.rmdirSync(path);
|
||||
}
|
||||
else if (stats.isFile()) {
|
||||
Fs.unlinkSync(path);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 遍历文件/文件夹并执行函数
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @param {(filePath: Fs.PathLike, stat: Fs.Stats) => void} handler 处理函数
|
||||
*/
|
||||
static map(path, handler) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!Fs.existsSync(path))
|
||||
return;
|
||||
const stats = Fs.statSync(path);
|
||||
if (stats.isDirectory()) {
|
||||
const names = Fs.readdirSync(path);
|
||||
for (const name of names) {
|
||||
yield this.map(Path.join(path, name), handler);
|
||||
}
|
||||
}
|
||||
else if (stats.isFile()) {
|
||||
yield handler(path, stats);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.default = FileUtil;
|
67
dist/util/ObjectUtil.js
vendored
Normal file
67
dist/util/ObjectUtil.js
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* 对象工具
|
||||
*/
|
||||
class ObjectUtil {
|
||||
/**
|
||||
* 判断指定值是否是一个对象
|
||||
* @param {any} arg 参数
|
||||
*/
|
||||
static isObject(arg) {
|
||||
return Object.prototype.toString.call(arg) === '[object Object]';
|
||||
}
|
||||
/**
|
||||
* 对象中是否包含指定的属性
|
||||
* @param {object} object 对象
|
||||
* @param {any} name 属性名
|
||||
*/
|
||||
static containsProperty(object, name) {
|
||||
let result = false;
|
||||
const search = (_object) => {
|
||||
if (this.isObject(_object)) {
|
||||
for (const key in _object) {
|
||||
if (key == name) {
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
search(_object[key]);
|
||||
}
|
||||
}
|
||||
else if (Array.isArray(_object)) {
|
||||
for (let i = 0; i < _object.length; i++) {
|
||||
search(_object[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
search(object);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* 对象中是否包含指定的值
|
||||
* @param {object} object 对象
|
||||
* @param {any} value 值
|
||||
*/
|
||||
static containsValue(object, value) {
|
||||
let result = false;
|
||||
const search = (_object) => {
|
||||
if (this.isObject(_object)) {
|
||||
for (const key in _object) {
|
||||
if (_object[key] === value) {
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
search(_object[key]);
|
||||
}
|
||||
}
|
||||
else if (Array.isArray(_object)) {
|
||||
for (let i = 0; i < _object.length; i++) {
|
||||
search(_object[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
search(object);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
exports.default = ObjectUtil;
|
1
i18n/en.js
Normal file
1
i18n/en.js
Normal file
@ -0,0 +1 @@
|
||||
"use strict";module.exports={open_panel:"Default Panel",send_to_panel:"Send message to Default Panel",description:"Extension with a panel based on Vue3.x"};
|
1
i18n/zh.js
Normal file
1
i18n/zh.js
Normal file
@ -0,0 +1 @@
|
||||
"use strict";module.exports={open_panel:"默认面板",send_to_panel:"发送消息给面板",description:"含有一个基于Vue3.x开发的面板的扩展"};
|
277
package-lock.json
generated
Normal file
277
package-lock.json
generated
Normal file
@ -0,0 +1,277 @@
|
||||
{
|
||||
"name": "references-finder",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "references-finder",
|
||||
"version": "1.0.0",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"fs-extra": "^10.0.0",
|
||||
"vue": "^3.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cocos/creator-types": "^3.8.4",
|
||||
"@types/fs-extra": "^9.0.5",
|
||||
"@types/node": "^18.17.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.23.0",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cocos/creator-types": {
|
||||
"version": "3.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@cocos/creator-types/-/creator-types-3.8.4.tgz",
|
||||
"integrity": "sha512-z+8qx726Zl/8rUUpbLjVAiNPn4XweRACEaY1vHeR3EHIcSQ9wtFlIFd2SZX5rOE8lt1S95nxv8OeLYXfbN4/2Q==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/fs-extra": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
||||
"integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.19.61",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.61.tgz",
|
||||
"integrity": "sha512-z8fH66NcVkDzBItOao+Nyh0fiy7CYdxIyxnNCcZ60aY0I+EA/y4TSi/S/W9i8DIQvwVo7a0pgzAxmDeNnqrpkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.21.3",
|
||||
"@vue/shared": "3.3.4",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-dom": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.3.4",
|
||||
"@vue/shared": "3.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.20.15",
|
||||
"@vue/compiler-core": "3.3.4",
|
||||
"@vue/compiler-dom": "3.3.4",
|
||||
"@vue/compiler-ssr": "3.3.4",
|
||||
"@vue/reactivity-transform": "3.3.4",
|
||||
"@vue/shared": "3.3.4",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.0",
|
||||
"postcss": "^8.1.10",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.3.4",
|
||||
"@vue/shared": "3.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/reactivity-transform": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.20.15",
|
||||
"@vue/compiler-core": "3.3.4",
|
||||
"@vue/shared": "3.3.4",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.3.4",
|
||||
"@vue/shared": "3.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/runtime-core": "3.3.4",
|
||||
"@vue/shared": "3.3.4",
|
||||
"csstype": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.3.4",
|
||||
"@vue/shared": "3.3.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.2",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "10.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/jsonfile": {
|
||||
"version": "6.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.6",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.30",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.3.4",
|
||||
"@vue/compiler-sfc": "3.3.4",
|
||||
"@vue/runtime-dom": "3.3.4",
|
||||
"@vue/server-renderer": "3.3.4",
|
||||
"@vue/shared": "3.3.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
package.json
Normal file
41
package.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"$schema": "./@types/schema/package/index.json",
|
||||
"package_version": 2,
|
||||
"name": "references-finder",
|
||||
"version": "1.0.0",
|
||||
"author": "abc126500",
|
||||
"editor": ">=3.8.4",
|
||||
"scripts": {
|
||||
"preinstall": "node ./scripts/preinstall.js",
|
||||
"build": "tsc"
|
||||
},
|
||||
"description": "i18n:references-finder.description",
|
||||
"main": "./dist/main.js",
|
||||
"dependencies": {
|
||||
"vue": "^3.1.4",
|
||||
"fs-extra": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cocos/creator-types": "^3.8.4",
|
||||
"@types/fs-extra": "^9.0.5",
|
||||
"@types/node": "^18.17.1"
|
||||
},
|
||||
"contributions": {
|
||||
"messages": {
|
||||
"findSelection": {
|
||||
"public": true,
|
||||
"description": "find the selected Referrend",
|
||||
"methods": [
|
||||
"findCurrentSelection"
|
||||
]
|
||||
}
|
||||
},
|
||||
"shortcuts": [
|
||||
{
|
||||
"message": "findSelection",
|
||||
"win": "F6",
|
||||
"mac": "F6"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
1
scripts/preinstall.js
Normal file
1
scripts/preinstall.js
Normal file
@ -0,0 +1 @@
|
||||
const readFileSync=require("fs")["readFileSync"],join=require("path")["join"],spawnSync=require("child_process")["spawnSync"],PATH={packageJSON:join(__dirname,"../package.json")};function checkCreatorTypesVersion(e){var o="win32"===process.platform?"npm.cmd":"npm";let n=spawnSync(o,["view","@cocos/creator-types","versions"]).stdout.toString();try{n=JSON.parse(listString)}catch(e){}return!!n.includes(e)}try{const e=readFileSync(PATH.packageJSON,"utf8"),f=JSON.parse(e),g=f.devDependencies["@cocos/creator-types"].replace(/^[^\d]+/,"");checkCreatorTypesVersion(g)||(console.log("[33mWarning:[0m"),console.log(" @en"),console.log(" Version check of @cocos/creator-types failed."),console.log(` The definition of ${g} has not been released yet. Please export the definition to the ./node_modules directory by selecting "Developer -> Export Interface Definition" in the menu of the Creator editor.`),console.log(" The definition of the corresponding version will be released on npm after the editor is officially released."),console.log(" @zh"),console.log(" @cocos/creator-types 版本检查失败。"),console.log(` ${g} 定义还未发布,请先通过 Creator 编辑器菜单 "开发者 -> 导出接口定义",导出定义到 ./node_modules 目录。`),console.log(" 对应版本的定义会在编辑器正式发布后同步发布到 npm 上。"))}catch(e){console.error(e)}
|
455
source/main.ts
Normal file
455
source/main.ts
Normal file
@ -0,0 +1,455 @@
|
||||
|
||||
import ObjectUtil from './util/ObjectUtil';
|
||||
import FileUtil from './util/FileUtil';
|
||||
import PJSON from '../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 为扩展的主进程的注册方法
|
||||
*/
|
||||
export const methods: { [key: string]: (...any: any) => any } = {
|
||||
openDebug() {
|
||||
console.log('openDebug')
|
||||
Editor.Panel.open(PJSON.name);
|
||||
},
|
||||
/**
|
||||
* @en A method that can be triggered by message
|
||||
* @zh 通过 message 触发的方法
|
||||
*/
|
||||
async findCurrentSelection() {
|
||||
const uuids = Editor.Selection.getSelected('asset');
|
||||
if (uuids.length === 0) {
|
||||
console.log(`[${EXTENSION_NAME}]`, '先选择资源');
|
||||
return;
|
||||
}
|
||||
// 根据 uuid 查找
|
||||
for (let i = 0; i < uuids.length; i++) {
|
||||
await this.module.methods.findViaUuid(uuids[i]);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 使用 uuid 进行查找
|
||||
* @param {string} uuid
|
||||
*/
|
||||
async findViaUuid(uuid) {
|
||||
// 是否为有效 uuid
|
||||
if (!Editor.Utils.UUID.isUUID(uuid)) {
|
||||
console.log(`[${EXTENSION_NAME}]`, 'invalidUuid' + uuid);
|
||||
return;
|
||||
}
|
||||
// 获取资源信息
|
||||
const assetInfo = await 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 ? await this.findReferences(uuid) : [];
|
||||
// Done
|
||||
this.printResult(results);
|
||||
} else {
|
||||
// 不存在的资源,直接查找 uuid
|
||||
console.log(`[${EXTENSION_NAME}]`, 'findAssetRefs' + uuid);
|
||||
const result = await 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.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 结果容器
|
||||
*/
|
||||
async findRefsInNode(node, uuid, nodeTree, container) {
|
||||
// 检查节点上的组件是否有引用
|
||||
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 = await 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.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++) {
|
||||
await this.findRefsInNode(children[i], uuid, nodeTree, container);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 查找引用
|
||||
* @param {string} uuid
|
||||
* @returns {object[]}
|
||||
*/
|
||||
async findReferences(uuid) {
|
||||
const results = [];
|
||||
/**
|
||||
* 文件处理函数
|
||||
* @param {string} filePath 文件路径
|
||||
* @param {Fs.Stats} stats
|
||||
*/
|
||||
const searchHandler = async (filePath, stats) => {
|
||||
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++) {
|
||||
await this.findRefsInNode(children[i], uuid, nodeTree, refs);
|
||||
}
|
||||
// 保存当前文件引用结果
|
||||
if (refs.length > 0) {
|
||||
results.push({
|
||||
type: typeMap[extname],
|
||||
fileUrl: await 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.containsValue(curveData, uuid);
|
||||
if (contains) {
|
||||
results.push({
|
||||
type: typeMap[extname],
|
||||
fileUrl: await 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.containsValue(data, uuid);
|
||||
if (contains && !(data['uuid'] === uuid)) {
|
||||
const _extname = (extname === '.mtl') ? '.mtl' : '.fnt.meta';
|
||||
results.push({
|
||||
type: typeMap[_extname],
|
||||
fileUrl: await Editor.Message.request('asset-db', 'query-url', filePath)
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
// 遍历资源目录下的文件
|
||||
const assetsPath = await Editor.Message.request('asset-db', 'query-path', 'db://assets/');
|
||||
await FileUtil.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.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 扩展启动时触发的方法
|
||||
*/
|
||||
export function load() { }
|
||||
|
||||
/**
|
||||
* @en Method triggered when uninstalling the extension
|
||||
* @zh 卸载扩展时触发的方法
|
||||
*/
|
||||
export function unload() { }
|
64
source/util/FileUtil.ts
Normal file
64
source/util/FileUtil.ts
Normal file
@ -0,0 +1,64 @@
|
||||
const Fs = require('fs');
|
||||
const Path = require('path');
|
||||
|
||||
/**
|
||||
* 文件工具
|
||||
*/
|
||||
export default class FileUtil {
|
||||
|
||||
/**
|
||||
* 复制文件/文件夹
|
||||
* @param {Fs.PathLike} srcPath 源路径
|
||||
* @param {Fs.PathLike} destPath 目标路径
|
||||
*/
|
||||
static copy(srcPath, destPath) {
|
||||
if (!Fs.existsSync(srcPath)) return;
|
||||
const stats = Fs.statSync(srcPath);
|
||||
if (stats.isDirectory()) {
|
||||
if (!Fs.existsSync(destPath)) Fs.mkdirSync(destPath);
|
||||
const names = Fs.readdirSync(srcPath);
|
||||
for (const name of names) {
|
||||
this.copy(Path.join(srcPath, name), Path.join(destPath, name));
|
||||
}
|
||||
} else if (stats.isFile()) {
|
||||
Fs.writeFileSync(destPath, Fs.readFileSync(srcPath));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件/文件夹
|
||||
* @param {Fs.PathLike} path 路径
|
||||
*/
|
||||
static delete(path) {
|
||||
if (!Fs.existsSync(path)) return;
|
||||
const stats = Fs.statSync(path);
|
||||
if (stats.isDirectory()) {
|
||||
const names = Fs.readdirSync(path);
|
||||
for (const name of names) {
|
||||
this.delete(Path.join(path, name));
|
||||
}
|
||||
Fs.rmdirSync(path);
|
||||
} else if (stats.isFile()) {
|
||||
Fs.unlinkSync(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历文件/文件夹并执行函数
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @param {(filePath: Fs.PathLike, stat: Fs.Stats) => void} handler 处理函数
|
||||
*/
|
||||
static async map(path:string, handler: (string,any)=>Promise<void>) {
|
||||
if (!Fs.existsSync(path)) return
|
||||
const stats = Fs.statSync(path);
|
||||
if (stats.isDirectory()) {
|
||||
const names = Fs.readdirSync(path);
|
||||
for (const name of names) {
|
||||
await this.map(Path.join(path, name), handler);
|
||||
}
|
||||
} else if (stats.isFile()) {
|
||||
await handler(path, stats);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
66
source/util/ObjectUtil.ts
Normal file
66
source/util/ObjectUtil.ts
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 对象工具
|
||||
*/
|
||||
export default class ObjectUtil {
|
||||
|
||||
/**
|
||||
* 判断指定值是否是一个对象
|
||||
* @param {any} arg 参数
|
||||
*/
|
||||
static isObject(arg) {
|
||||
return Object.prototype.toString.call(arg) === '[object Object]';
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象中是否包含指定的属性
|
||||
* @param {object} object 对象
|
||||
* @param {any} name 属性名
|
||||
*/
|
||||
static containsProperty(object, name) {
|
||||
let result = false;
|
||||
const search = (_object) => {
|
||||
if (this.isObject(_object)) {
|
||||
for (const key in _object) {
|
||||
if (key == name) {
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
search(_object[key]);
|
||||
}
|
||||
} else if (Array.isArray(_object)) {
|
||||
for (let i = 0; i < _object.length; i++) {
|
||||
search(_object[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
search(object);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象中是否包含指定的值
|
||||
* @param {object} object 对象
|
||||
* @param {any} value 值
|
||||
*/
|
||||
static containsValue(object, value) {
|
||||
let result = false;
|
||||
const search = (_object) => {
|
||||
if (this.isObject(_object)) {
|
||||
for (const key in _object) {
|
||||
if (_object[key] === value) {
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
search(_object[key]);
|
||||
}
|
||||
} else if (Array.isArray(_object)) {
|
||||
for (let i = 0; i < _object.length; i++) {
|
||||
search(_object[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
search(object);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
29
tsconfig.json
Normal file
29
tsconfig.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "CommonJS",
|
||||
"inlineSourceMap": false,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./source",
|
||||
"allowJs": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es7",
|
||||
"ES2017"
|
||||
],
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"types": [
|
||||
"@cocos/creator-types/editor/protected",
|
||||
"@cocos/creator-types/editor/packages/builder/@types/public",
|
||||
"@cocos/creator-types/editor/packages/builder/@types/public/global"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"./dist"
|
||||
],
|
||||
"include": [
|
||||
"./source"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user