移除死文件
This commit is contained in:
1 parent
6366635feb
commit
ff7ae2cd84
3 files changed
+75
-36
No files matched your search
@@ -1,17 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
// Preserve name + '.' + ext exactly; only split the compound suffix correctly.
|
|
||||||
// Return through Eagle's thumbnail callback so Eagle persists its own metadata.
|
|
||||||
function normalizeItem(src, item) {
|
|
||||||
const filename = path.basename(src);
|
|
||||||
const match = filename.match(/^(.*)\.(atlas\.txt|skel\.bytes)$/i);
|
|
||||||
if (!match || !item || !item.ext) return false;
|
|
||||||
if (item.name + '.' + item.ext !== filename) return false;
|
|
||||||
if (item.name === match[1] && item.ext === match[2]) return false;
|
|
||||||
item.name = match[1]; item.ext = match[2];
|
|
||||||
delete item.text;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { normalizeItem };
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
// Preserve name + '.' + ext exactly; only split the compound suffix correctly.
|
|
||||||
// Return through Eagle's thumbnail callback so Eagle persists its own metadata.
|
|
||||||
function normalizeItem(src, item) {
|
|
||||||
const filename = path.basename(src);
|
|
||||||
const match = filename.match(/^(.*)\.(atlas\.txt|skel\.bytes)$/i);
|
|
||||||
if (!match || !item || !item.ext) return false;
|
|
||||||
if (item.name + '.' + item.ext !== filename) return false;
|
|
||||||
if (item.name === match[1] && item.ext === match[2]) return false;
|
|
||||||
item.name = match[1]; item.ext = match[2];
|
|
||||||
delete item.text;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { normalizeItem };
|
|
||||||
@@ -8,7 +8,9 @@ const path = require('node:path');
|
|||||||
const root = path.resolve(__dirname, '..');
|
const root = path.resolve(__dirname, '..');
|
||||||
const mirror = path.join(root, 'release', 'UNIVERSAL_SpineViewer');
|
const mirror = path.join(root, 'release', 'UNIVERSAL_SpineViewer');
|
||||||
const SHARED_TREES = ['viewer', 'js', 'lib', '_locales', 'assets'];
|
const SHARED_TREES = ['viewer', 'js', 'lib', '_locales', 'assets'];
|
||||||
const SHARED_FILES = ['manifest.json', 'package.json', 'package-lock.json', 'logo-spineviewer.png'];
|
// package.json 是“发行版专有”文件:不再要求逐字节一致(发行版删掉了指向 tests/ 的 scripts),
|
||||||
|
// 但 name/version/type 必须与源一致,下面单独校验。
|
||||||
|
const SHARED_FILES = ['manifest.json', 'package-lock.json', 'logo-spineviewer.png'];
|
||||||
// 只留在源目录、刻意不进入安装包的文件(新增这类文件时必须显式登记,避免漏打包)。
|
// 只留在源目录、刻意不进入安装包的文件(新增这类文件时必须显式登记,避免漏打包)。
|
||||||
const SOURCE_ONLY = [
|
const SOURCE_ONLY = [
|
||||||
'viewer/icns.html',
|
'viewer/icns.html',
|
||||||
@@ -18,6 +20,17 @@ const SOURCE_ONLY = [
|
|||||||
'assets/README.md',
|
'assets/README.md',
|
||||||
'assets/spineviewer-icon-master.png',
|
'assets/spineviewer-icon-master.png',
|
||||||
];
|
];
|
||||||
|
// 临时豁免清单:包内暂时无人引用的脚本。历史上曾登记过旧「导入缩略图处理器」的
|
||||||
|
// js/eagle-compound.js,已随死代码清理删除。新增的无用脚本会被下面的断言拦住。
|
||||||
|
const UNREFERENCED_SCRIPT_OK = [];
|
||||||
|
// 语言文件里未被任何代码引用的键(旧的导入缩略图/元素提示文案遗留)。
|
||||||
|
const UNUSED_MESSAGE_OK = [
|
||||||
|
'viewer.groupSkipCustom',
|
||||||
|
'viewer.thumbWrittenNoRefresh',
|
||||||
|
'viewer.compoundName',
|
||||||
|
'viewer.txtRegistered',
|
||||||
|
'viewer.noItemId',
|
||||||
|
];
|
||||||
const JUNK = /(\.log|\.tmp|\.bak|\.orig|\.swp|~)$|^\.DS_Store$|^Thumbs\.db$|^node_modules$|^\.git$|^\.idea$|^\.vscode$/i;
|
const JUNK = /(\.log|\.tmp|\.bak|\.orig|\.swp|~)$|^\.DS_Store$|^Thumbs\.db$|^node_modules$|^\.git$|^\.idea$|^\.vscode$/i;
|
||||||
|
|
||||||
function walk(directory, prefix = '') {
|
function walk(directory, prefix = '') {
|
||||||
@@ -119,4 +132,64 @@ for (const license of fs.readdirSync(path.join(root, 'LICENSES'))) {
|
|||||||
assert.ok(fs.existsSync(path.join(mirror, 'LICENSES', license)), '缺少 LICENSES/' + license);
|
assert.ok(fs.existsSync(path.join(mirror, 'LICENSES', license)), '缺少 LICENSES/' + license);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('PASS: 镜像与源逐字节一致、无垃圾文件、无断链引用,manifest/语言/运行时/许可齐全');
|
// 8) 发行版 package.json:不能指向包内不存在的文件(曾经的 check:release -> tests/ 死链)
|
||||||
|
const rootPackage = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
||||||
|
const mirrorPackage = JSON.parse(fs.readFileSync(path.join(mirror, 'package.json'), 'utf8'));
|
||||||
|
for (const field of ['name', 'version', 'type']) {
|
||||||
|
assert.equal(mirrorPackage[field], rootPackage[field], '发行版 package.json 的 ' + field + ' 与源不一致');
|
||||||
|
}
|
||||||
|
const deadScripts = [];
|
||||||
|
for (const [name, command] of Object.entries(mirrorPackage.scripts || {})) {
|
||||||
|
for (const token of String(command).split(/\s+/)) {
|
||||||
|
if (!/^[\w./-]+\.(js|mjs|cjs|json)$/.test(token)) continue;
|
||||||
|
if (!fs.existsSync(path.join(mirror, token))) deadScripts.push('scripts.' + name + ' -> ' + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.deepEqual(deadScripts, [], '发行版 package.json 的 scripts 指向包内不存在的文件:' + deadScripts.join(', '));
|
||||||
|
|
||||||
|
// 9) 死脚本:包内每个 js 模块都必须被入口或其他模块引用
|
||||||
|
const entrySource = fs.readFileSync(path.join(mirror, 'viewer', 'spine.html'), 'utf8');
|
||||||
|
const scriptNames = fs.readdirSync(path.join(mirror, 'js'));
|
||||||
|
const unreferenced = [];
|
||||||
|
for (const file of scriptNames) {
|
||||||
|
const others = [entrySource]
|
||||||
|
.concat(scriptNames.filter((name) => name !== file).map((name) => fs.readFileSync(path.join(mirror, 'js', name), 'utf8')))
|
||||||
|
.join('\n');
|
||||||
|
if (!others.includes(file)) unreferenced.push('js/' + file);
|
||||||
|
}
|
||||||
|
assert.deepEqual(
|
||||||
|
unreferenced.filter((file) => !UNREFERENCED_SCRIPT_OK.includes(file)),
|
||||||
|
[],
|
||||||
|
'包内存在无人引用的脚本:' + unreferenced.join(', '),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 10) 死文案:语言文件里的键必须被入口/脚本/manifest 引用(manifest 的 name 用 {{manifest.app.name}})
|
||||||
|
const referenced = new Set();
|
||||||
|
const sources = [entrySource, fs.readFileSync(path.join(mirror, 'manifest.json'), 'utf8')]
|
||||||
|
.concat(scriptNames.map((name) => fs.readFileSync(path.join(mirror, 'js', name), 'utf8')));
|
||||||
|
for (const text of sources) {
|
||||||
|
for (const match of text.matchAll(/[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z0-9_]+)+/g)) referenced.add(match[0]);
|
||||||
|
}
|
||||||
|
const unusedMessages = [];
|
||||||
|
for (const lang of manifest.languages) {
|
||||||
|
const table = JSON.parse(fs.readFileSync(path.join(mirror, '_locales', lang + '.json'), 'utf8'));
|
||||||
|
const keys = [];
|
||||||
|
const collect = (node, prefix) => {
|
||||||
|
for (const key of Object.keys(node)) {
|
||||||
|
const value = node[key];
|
||||||
|
if (value && typeof value === 'object') collect(value, prefix + key + '.');
|
||||||
|
else keys.push(prefix + key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
collect(table, '');
|
||||||
|
for (const key of keys) {
|
||||||
|
if (!referenced.has(key) && !unusedMessages.includes(key)) unusedMessages.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.deepEqual(
|
||||||
|
unusedMessages.filter((key) => !UNUSED_MESSAGE_OK.includes(key)),
|
||||||
|
[],
|
||||||
|
'语言文件里存在无人引用的死文案:' + unusedMessages.join(', '),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('PASS: 镜像与源一致、无垃圾文件、无断链引用、无死脚本/死文案,manifest/语言/运行时/许可齐全');
|
||||||
Reference in new issue
Block a user