73 lines
3.2 KiB
JavaScript
73 lines
3.2 KiB
JavaScript
/**
|
||
* 临时调研:Iconify 官方集合 vs 本地已收录,找出未收录且许可证合规的候选源
|
||
* 输出:未收录集合清单(id / 图标数 / license / 是否合规 / 收录价值初判)
|
||
*/
|
||
'use strict';
|
||
const https = require('https');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const ROOT = process.cwd();
|
||
const SOURCES = require(path.join(ROOT, 'js', 'data', 'icon-sources.js'));
|
||
|
||
// 本地已收录的 iconify prefix(npm: '@iconify-json/xxx' 的 xxx)
|
||
const localPrefixes = new Set();
|
||
for (const s of SOURCES) {
|
||
const m = String(s.npm || '').match(/@iconify-json\/([\w-]+)/);
|
||
if (m) localPrefixes.add(m[1]);
|
||
// 有些源 id 即 prefix
|
||
localPrefixes.add(s.id);
|
||
}
|
||
|
||
function fetch(url, redirects) {
|
||
return new Promise((resolve, reject) => {
|
||
const req = https.get(url, { timeout: 60000 }, (res) => {
|
||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||
const next = res.headers.location;
|
||
if (next.startsWith('/')) return resolve(fetch('https://api.iconify.design' + next, (redirects || 0) + 1));
|
||
return resolve(fetch(next, (redirects || 0) + 1));
|
||
}
|
||
if (res.statusCode !== 200) { reject(new Error('HTTP ' + res.statusCode)); res.resume(); return; }
|
||
const chunks = [];
|
||
res.on('data', (c) => chunks.push(c));
|
||
res.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
||
});
|
||
req.on('error', reject);
|
||
req.setTimeout(60000, () => { req.destroy(new Error('timeout')); });
|
||
});
|
||
}
|
||
|
||
(async () => {
|
||
console.log('拉取 Iconify 官方集合列表…');
|
||
const raw = await fetch('https://api.iconify.design/collections');
|
||
const json = JSON.parse(raw);
|
||
const all = Object.entries(json.collections || json);
|
||
console.log('顶层键:', Object.keys(json).slice(0, 10).join(', '));
|
||
|
||
// 许可证过滤:排除 BY-SA / BY-NC / BY-ND / GPL / -NC-(兼容空格分隔形式)
|
||
const badRe = /by[\s-]?sa|by[\s-]?nc|by[\s-]?nd|gpl|nc-|cc[\s-]?by[\s-]?nc/i;
|
||
const missing = [];
|
||
for (const [prefix, info] of all) {
|
||
if (localPrefixes.has(prefix)) continue;
|
||
const license = info.license ? info.license.title : 'unknown';
|
||
const bad = badRe.test(license);
|
||
missing.push({ prefix, total: info.total, license, bad, author: info.author && info.author.name });
|
||
}
|
||
|
||
// 未收录 + 合规(或 license unknown 单独列出)
|
||
const good = missing.filter(m => !m.bad);
|
||
const bad = missing.filter(m => m.bad);
|
||
good.sort((a, b) => b.total - a.total);
|
||
bad.sort((a, b) => b.total - a.total);
|
||
|
||
console.log(`\n=== 官方集合总数: ${all.length} | 本地已收录 iconify prefix: ${localPrefixes.size} ===`);
|
||
console.log(`\n=== 未收录 + 许可证合规(${good.length} 个)===\n`);
|
||
for (const m of good) {
|
||
console.log(` ${m.prefix.padEnd(28)} ${String(m.total).padStart(6)} ${m.license.padEnd(28)} ${m.author || ''}`);
|
||
}
|
||
console.log(`\n=== 未收录 + 许可证不合规(${bad.length} 个,仅供参考)===\n`);
|
||
for (const m of bad.slice(0, 60)) {
|
||
console.log(` ${m.prefix.padEnd(28)} ${String(m.total).padStart(6)} ${m.license.padEnd(28)} ${m.author || ''}`);
|
||
}
|
||
})().catch((e) => { console.error('❌', e.message); process.exit(1); });
|