Files
刘宇飞 d393302388 Complete Cocos Creator port with level bundles, themes, and tooling.
Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 15:30:58 +08:00

584 lines
24 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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
// @ts-ignore
const electron_1 = require("electron");
// @ts-ignore
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
const convertor_1 = require("./convertor");
const utlis_1 = require("./common/utlis");
const MigrateManager_1 = require("./components/MigrateManager");
const base_1 = require("./common/base");
exports.style = (0, fs_extra_1.readFileSync)((0, path_1.join)(__dirname, '../creator/index.css'), 'utf8');
const { I18n, Dialog, Message } = Editor;
const MANUAL = 'https://github.com/cocos-creator/migrate-cocos-creator2.x-plugin';
const PackageJSON = (0, fs_extra_1.readJSONSync)((0, path_1.join)(__dirname, '../package.json'));
function t(key, args) {
return args ? I18n.t(`${PackageJSON.name}.${key}`, args) : I18n.t(`${PackageJSON.name}.${key}`);
}
exports.linteners = {
resize() {
this.$.tree.render(true);
},
};
exports.template = `
<header>
<h1>
<ui-label value="i18n:${PackageJSON.name}.title"></ui-label>
</h1>
<ui-progress></ui-progress>
<ui-file
class="project"
type="directory"
placeholder="i18n:${PackageJSON.name}.select_dialog.title"
></ui-file>
</header>
<section>
<ui-tree></ui-tree>
</section>
<footer>
<ui-label class="version"></ui-label>
<ui-button class="import">
<ui-label value="i18n:${PackageJSON.name}.btnImport"></ui-label>
</ui-button>
</footer>
<iframe class="engine2D" style="display: none"></iframe>
`;
exports.$ = {
tree: 'ui-tree',
project: '.project',
import: '.import',
progress: 'ui-progress',
// serialize: '.serialize',
search: '.search',
engine2D: '.engine2D',
version: '.version',
};
exports.methods = {
updateTreeState() {
this.$.tree.list.forEach((data) => {
const detail = data.detail;
detail.isNew = base_1.ImporterBase.isNew(this.projectRoot, detail.file);
});
this.$.tree.render(true);
},
updateTree(root) {
return __awaiter(this, void 0, void 0, function* () {
const tree = [];
function step(file, children) {
try {
if (file.endsWith('.DS_Store')) {
return;
}
if (utlis_1.SKIPS_SCRIPT.includes((0, path_1.basename)(file))) {
return;
}
const stat = (0, fs_extra_1.statSync)(file);
const ext = (0, path_1.extname)(file);
const elements = file.split('assets');
let url = '';
if (elements) {
url = 'db://assets' + elements[1].replace(ext, '');
}
const item = {
detail: {
value: (0, path_1.basename)(file),
file: file,
checked: true,
extname: ext,
isDirectory: stat.isDirectory(),
legal: !!(0, convertor_1.getConverter)(ext) && !stat.isDirectory(),
isNew: base_1.ImporterBase.isNew(root, file),
path: url,
},
showArrow: false,
children: [],
};
if (stat.isDirectory()) {
item.showArrow = true;
const names = (0, fs_extra_1.readdirSync)(file);
names.forEach((name) => {
const tempPath = (0, path_1.join)(file, name);
if (name.endsWith('.meta')) {
(0, utlis_1.addImportProjectAssets)(root, tempPath);
return;
}
step(tempPath, item.children);
});
if (item.children.length > 0) {
children.push(item);
}
}
else {
if (item.detail.legal) {
children.push(item);
}
}
}
catch (error) {
}
}
const path = (0, path_1.join)(root, 'assets');
step(path, tree);
const state = yield Editor.Message.request('assets', 'unstaging');
let sortType = 'type';
if (state) {
sortType = state.sortType;
}
this.sortTree(tree, sortType);
this.$.tree.tree = this.baseTree = tree;
});
},
sortTree(tree, sortType) {
tree.sort((a, b) => {
const detailA = a.detail, detailB = b.detail;
if (detailA.isDirectory && !detailB.isDirectory) {
return -1;
}
else if (!detailA.isDirectory && detailB.isDirectory) {
return 1;
}
else {
if (sortType === 'type' && detailA.extname !== detailB.extname) {
return utlis_1.collator.compare(detailA.extname, detailB.extname);
}
else {
return utlis_1.collator.compare(detailA.path, detailB.path);
}
}
});
for (const item of tree) {
this.sortTree(item.children, sortType);
}
},
scanProject(root) {
return __awaiter(this, void 0, void 0, function* () {
if (!root) {
return;
}
const path = (0, path_1.join)(root, 'assets');
const projectPath = (0, path_1.join)(root, 'project.json');
if (!(0, fs_extra_1.existsSync)(path) || !(0, fs_extra_1.existsSync)(projectPath)) {
Dialog.warn(t('warn_dialog.message'), {
detail: t('warn_dialog.detail', {
path: root,
}),
});
return false;
}
const project = (0, fs_extra_1.readJSONSync)(projectPath);
if (!project.version || (0, utlis_1.compareVersion)(project.version, '2.4.3') === -1) {
Dialog.warn(t('warn_dialog.title'), {
detail: t('warn_dialog.version', { version: project.version }),
});
}
this.projectRoot = root;
this.$.import.removeAttribute('disabled');
// 导入时的初始化
utlis_1.uuidList.clear();
utlis_1.importProjectAssets.clear();
utlis_1.importSubAssets.clear();
(0, utlis_1.initGroupList)(path);
(0, utlis_1.scanningDefaultAssets2D)();
yield this.updateTree(this.projectRoot);
this.updateList();
return true;
});
},
updateList() {
// 更新 list
this.totalTree = [];
this.list = [];
this.$.tree.list.forEach((data) => {
if (data.detail.checked) {
if (!data.detail.legal) {
// 统计统一弹窗提示,打印太多 log 了
// console.warn(`无法导入文件: ${data.detail.file}`);
return;
}
this.list.push(data);
}
this.totalTree.push(data);
});
this.progressCurrentIdx = 0;
this.$.progress.message = t('import_message_init', {
current: this.progressCurrentIdx,
total: this.list.length,
});
if (this.list.length === 0) {
this.$.import.setAttribute('disabled', '');
}
else {
this.$.import.removeAttribute('disabled');
}
},
getScripts() {
const scripts = new Map();
const root = (0, path_1.join)(Editor.Project.path, 'assets');
function step(file) {
const files = (0, fs_extra_1.readdirSync)(file);
files.forEach((item) => {
const fPath = (0, path_1.join)(file, item);
const stat = (0, fs_extra_1.statSync)(fPath);
if (stat.isDirectory()) {
step(fPath);
}
if (!stat.isDirectory() && fPath.endsWith('.ts')) {
try {
const content = (0, fs_extra_1.readFileSync)(fPath, 'utf8');
const matchArray = content.match(/@ccclass\('(.*)'\)/);
const className = matchArray && matchArray[1];
if (className) {
scripts.set((0, path_1.basename)(fPath, (0, path_1.extname)(fPath)), {
className: className,
path: fPath,
});
}
}
catch (e) {
console.error(e + ',\n this file path: ' + fPath);
}
}
});
}
step(root);
return scripts.size > 0 ? scripts : utlis_1.scriptList;
},
replaceScript() {
const scripts = this.getScripts();
for (const obj of utlis_1.replaceScriptList) {
// @ts-ignore
const hasReplaceExtendClass = obj.extendClassName !== undefined;
// @ts-ignore
const key = hasReplaceExtendClass ? obj.extendClassName : obj.importPath;
const name = key.replace(/[.|\\/]/g, '');
const script = scripts.get(name);
if (!script) {
continue;
}
// @ts-ignore
if (script.className) {
// @ts-ignore
let code = (0, fs_extra_1.readFileSync)(obj.path, 'utf8');
// @ts-ignore
const dirOne = (0, path_1.dirname)(obj.path);
const dirTwo = (0, path_1.dirname)(script.path);
// @ts-ignore
let relativePath = (0, path_1.relative)((0, path_1.dirname)(obj.path), script.path);
if (dirOne === dirTwo) {
relativePath = './' + relativePath;
}
if (hasReplaceExtendClass) {
// @ts-ignore
code = code.replace(obj.extendClassName, relativePath.replace('.ts', ''));
code = code.replace(/@@@@@@/g, script.className);
}
else {
const newPath = `from '${relativePath.replace('.ts', '')}'`;
// @ts-ignore
code = code.replace(`from '${obj.importPath}'`, newPath);
// @ts-ignore
code = code.replace(`from "${obj.importPath}"`, newPath);
}
// @ts-ignore
(0, fs_extra_1.writeFileSync)(obj.path, code, { encoding: 'utf8' });
}
}
},
replaceFbx() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
try {
let idx = 0;
if (utlis_1.replaceFbxUuidMap.size === 0) {
resolve(true);
}
utlis_1.replaceFbxUuidMap.forEach((data, path) => {
let done = false;
const meta = (0, fs_extra_1.readJSONSync)(path);
if (meta) {
for (const value of data) {
const subMeta = meta.subMetas[value.name];
if (subMeta) {
subMeta.uuid = value.uuid;
done = true;
}
}
}
if (done) {
(0, fs_extra_1.writeJSONSync)(path, meta, { spaces: 2 });
}
idx++;
if (idx === utlis_1.replaceFbxUuidMap.size) {
resolve(true);
}
});
}
catch (e) {
reject(e);
console.error(e);
}
});
});
},
importProject() {
return __awaiter(this, void 0, void 0, function* () {
let list = this.list;
// 根据勾选的情况进行剔除
list = list.filter((item) => {
return item.detail.checked;
});
// 排序基础资源先导入
(0, convertor_1.bubbleSort)(list);
(0, utlis_1.clear)();
this.progressCurrentIdx = 0;
this.progressCurrentName = '';
for (let i = 0; i < list.length; i++) {
const detail = list[i].detail;
this.progressCurrentIdx = i + 1;
this.progressCurrentName = detail.value;
this.$.progress.value = this.progressCurrentIdx / list.length * 100;
this.$.progress.message = t('.import_message', {
// @ts-ignore
current: this.progressCurrentIdx,
total: list.length,
name: detail.value,
});
const converter = (0, convertor_1.getConverter)(detail.extname);
if (converter) {
if ((converter.type === 'mtl' || converter.type === 'effect')) {
yield (0, utlis_1.import2DChunks)();
}
let isDone = yield converter.beforeImport(this.projectRoot, detail.file);
// if (!converter.needImport()) {
// continue;
// }
if (isDone) {
isDone = yield converter.import(this);
}
if (isDone) {
yield converter.afterImport();
}
// 无需每次都进行刷新,导入完后统一一次刷新
// const next = list[i + 1];
// if (next) {
// const extname = next.detail.extname;
// const nextConverter = getConverter(extname);
// if (nextConverter) {
// if (nextConverter.type !== converter.type) {
// if (converter.type === 'script' || nextConverter.type === 'script') {
// continue;
// }
// await Message.request('asset-db', 'refresh-asset', 'db://assets');
// }
// }
// }
}
}
this.replaceScript();
console.log(t('import_refresh'));
// await Message.request('asset-db', 'refresh-asset', 'db://assets');
console.log(t('import_refreshend'));
try {
yield this.replaceFbx();
}
catch (e) { }
MigrateManager_1.MigrateManager.logs.length > 0 && console.log(t('no_support_type', {
type: MigrateManager_1.MigrateManager.logs.toString()
}));
this.$.progress.value = 100;
this.$.progress.message = t('complete_message');
// 统一刷新
const { response } = yield Dialog.info(t('imported_dialog.message'), {
default: 0,
buttons: [
t('imported_dialog.btn_refresh'),
t('imported_dialog.btn_continue'),
]
});
if (0 === response) {
yield Editor.Panel.close(`${PackageJSON.name}.creator`);
Editor.Message.send('asset-db', 'refresh');
}
});
},
onSerializeComponent() {
return __awaiter(this, void 0, void 0, function* () {
yield Editor.Message.request('scene', 'execute-scene-script', {
name: 'importer',
method: 'onSerializeComponent',
});
});
},
};
exports.ready = function () {
return __awaiter(this, void 0, void 0, function* () {
this.$.engine2D.src = (0, path_1.join)(__dirname, '../creator/engine/engine2D.html');
// 通过后缀名来注册转换器
(0, convertor_1.registerConverter)();
Message.addBroadcastListener('i18n:change', () => {
if (this.$.progress.message) {
const total = this.list.length;
if (this.progressCurrentIdx === total) {
this.$.progress.message = t('complete_message');
}
else if (this.progressCurrentIdx === 0) {
this.$.progress.message = t('import_message_init', {
// @ts-ignore
current: 0,
total: total,
});
}
else {
this.$.progress.value = this.progressCurrentIdx / total * 100;
this.$.progress.message = t('import_message', {
// @ts-ignore
current: this.progressCurrentIdx,
total: total,
name: this.progressCurrentName,
});
}
}
});
this.$.version.value = t('import_version', { version: PackageJSON.version });
this.$.tree.setTemplateInit('item', ($item) => {
$item.addEventListener('contextmenu', () => {
Editor.Menu.popup({
menu: [{
label: t('menu.popup_open'),
click() {
electron_1.shell.showItemInFolder($item.data.detail.file);
},
}],
});
});
});
const newIcon = (0, path_1.join)(__dirname, `../creator/icon/new.png`);
this.$.tree.setTemplate('text', `<ui-checkbox></ui-checkbox><ui-icon></ui-icon><span class="name"></span><image class="new" ></image>`);
this.$.tree.setTemplateInit('text', ($text) => {
$text.$checkbox = $text.querySelector('ui-checkbox');
$text.$icon = $text.querySelector('ui-icon');
$text.$name = $text.querySelector('.name');
$text.$new = $text.querySelector('.new');
$text.$new.style.display = 'none';
$text.$new.src = newIcon;
$text.$checkbox.addEventListener('confirm', () => {
const data = $text.data;
data.detail.checked = !data.detail.checked;
// 将配置同步给子元素
function stepChildren(data, boolean) {
data.detail.checked = boolean;
data.children && data.children.forEach((child) => {
stepChildren(child, boolean);
});
}
stepChildren(data, data.detail.checked);
// 更新父级勾选状态
function stepParent(info) {
if (!info) {
return;
}
const checked = info.children.some((data) => {
return data.detail.checked;
});
if (checked !== info.detail.checked) {
info.detail.checked = checked;
stepParent(info.parent);
}
}
stepParent(data.parent);
this.updateList();
this.$.tree.render(true);
});
});
this.$.tree.setRender('text', ($text, data) => {
$text.$checkbox.value = data.detail.checked;
$text.$name.innerHTML = data.detail.value;
if (data.detail.isDirectory) {
$text.$icon.setAttribute('value', 'folder');
}
else {
$text.$icon.setAttribute('value', 'file');
}
$text.$new.style.display = data.detail.isNew ? '' : 'none';
});
this.$.tree.setRender('item', ($item) => {
const data = $item.data;
if (data.detail.legal) {
$item.removeAttribute('disabled');
}
else {
$item.setAttribute('disabled', '');
}
});
this.$.tree.css = (0, fs_extra_1.readFileSync)((0, path_1.join)(__dirname, '../creator/tree.css'), 'utf8');
this.$.serialize && this.$.serialize.addEventListener('confirm', () => {
this.onSerializeComponent();
});
this.$.search && this.$.search.addEventListener('change', (event) => {
// @ts-ignore
const value = event.target.value;
if (!value) {
this.$.tree.tree = this.baseTree;
}
else {
this.$.tree.tree = this.totalTree.map((item) => {
if ((item.detail.value.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1)) {
return item;
}
return false;
}).filter(Boolean);
}
this.$.tree.render(true);
});
// 选择项目按钮
this.$.import.setAttribute('disabled', '');
this.$.project.addEventListener('change', (event) => __awaiter(this, void 0, void 0, function* () {
// @ts-ignore
const path = event.target.value;
const done = yield this.scanProject(path);
if (done) {
Editor.Profile.setConfig(PackageJSON.name, 'import-path', path);
}
else {
this.$.project.value = this.projectRoot;
}
}));
// 开始导入按钮
this.$.import.addEventListener('confirm', () => __awaiter(this, void 0, void 0, function* () {
this.$.import.setAttribute('disabled', '');
yield this.importProject();
console.log(t('complete_message'));
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
this.updateTreeState();
this.$.import.removeAttribute('disabled');
}));
}));
// this.$.notes && this.$.notes.addEventListener('confirm', () => {
// const electron = require('electron');
// electron.shell.openExternal(MANUAL);
// });
const path = yield Editor.Profile.getConfig(PackageJSON.name, 'import-path');
this.$.project.value = path;
yield this.scanProject(path);
// await this.scanProject('/Users/huangyanbin/helloworld-typescript');
// await this.scanProject('/Users/huangyanbin/test-cases/v2_4_0');
});
};
exports.beforeClose = function () {
};
exports.close = function () {
};