import fs from 'fs'; import { IKanji, IRadical, WaniKani } from '@/wanikani'; import yaml from 'js-yaml'; import { toKatakana } from 'wanakana'; interface IItem { document_url: string; level: number; meaning: string; reading?: string; components: { [source: string]: string[]; }; similar: string[]; image?: { src: string; content_type: string; width: string | undefined; height: string | undefined; }; } interface IBeautifiedRadicals { [id: string]: IItem; } export const radicals = { data: {} as IBeautifiedRadicals, filename: 'radicals.yaml', load() { this.data = yaml.load( fs.readFileSync(this.filename, 'utf-8'), ) as IBeautifiedRadicals; return this.data; }, dump(d: IBeautifiedRadicals) { this.data = d; this.finalize(); }, finalize() { fs.writeFileSync( this.filename, yaml.dump(this.data, { skipInvalid: true, }), ); }, }; async function main() { const rs = await new WaniKani().subjects(); const idMap = new Map(); radicals.data = {}; rs.filter((r) => r.object === 'radical').map((r0) => { const r = r0 as IRadical; const d = { document_url: r.data.document_url, level: r.data.level, meaning: r.data.meanings.filter((m) => m.primary)[0]!.meaning, components: {}, sup: r.data.amalgamation_subject_ids.map((it) => it.toString()), similar: [], }; let id = r.data.characters; if (id) { radicals.data[id] = d; } else { id = d.meaning; const st = (im: { content_type: string; metadata: { dimensions?: string }; }) => im.content_type === 'image/svg+xml' ? 5000 : im.metadata.dimensions ? Math.min(...im.metadata.dimensions.split('x').map(Number)) : 0; const im = r.data.character_images.sort((a, b) => st(a) - st(b))[0]!; const dim = im.metadata.dimensions?.split('x') || []; radicals.data[id] = { ...d, image: { src: im.url, content_type: im.content_type, width: dim[0], height: dim[1], }, }; } idMap.set(r.id, id); }); rs.filter((r) => r.object === 'kanji').map((r0) => { const r = r0 as IKanji; const reading = r.data.readings.filter((m) => m.primary)[0]!; const d = { document_url: r.data.document_url, level: r.data.level, meaning: r.data.meanings.filter((m) => m.primary)[0]!.meaning, reading: reading.type === 'onyomi' ? toKatakana(reading.reading) : reading.reading, components: { wanikani: r.data.component_subject_ids.map((it) => it.toString()), }, similar: r.data.visually_similar_subject_ids.map((it) => it.toString()), }; const id = r.data.characters; idMap.set(r.id, id); // const prev = radicals.data[id] // if (prev) { // radicals.data[id] = { // ...d, // sup: prev.sup // } // } else { // radicals.data[id] = { // ...d, // sup: [] // } // } radicals.data[id] = d; }); for (const [k, v] of Object.entries(radicals.data)) { if (v.components?.['wanikani']) { v.components['wanikani'] = v.components['wanikani'] .map((s) => idMap.get(Number(s)) || s) .filter((s) => s !== k); } v.similar = v.similar.map((s) => idMap.get(Number(s)) || s); // v.sup = v.sup.map((s) => idMap.get(Number(s)) || s) } radicals.finalize(); } if (require.main === module) { main(); }