diff --git a/src/lib/data/index.ts b/src/lib/data/index.ts new file mode 100644 index 0000000..6d05e8c --- /dev/null +++ b/src/lib/data/index.ts @@ -0,0 +1,13 @@ +import { parseSpecies, parseCitizenships, parseLanguages } from './parse'; +import citizenshipsXml from '../../../data/citizenships.xml?raw'; +import languagesXml from '../../../data/languages.xml?raw'; + +const speciesModules = import.meta.glob('../../../data/species/*.xml', { + query: '?raw', + import: 'default', + eager: true +}); + +export const species = Object.values(speciesModules).map((xml) => parseSpecies(xml as string)); +export const citizenships = parseCitizenships(citizenshipsXml); +export const languages = parseLanguages(languagesXml); diff --git a/src/lib/data/parse.ts b/src/lib/data/parse.ts new file mode 100644 index 0000000..eaac11d --- /dev/null +++ b/src/lib/data/parse.ts @@ -0,0 +1,120 @@ +import { XMLParser } from 'fast-xml-parser'; +import type { SpeciesData, CitizenshipData, LanguageData } from './types'; +import type { Template, RecordDef, FieldDef, SelectOption } from '../types'; + +const parser = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: '@_', + isArray: (name) => ['entry', 'ref', 'field', 'record', 'option', 'citizenship', 'language'].includes(name), + trimValues: true +}); + +function extractRefs(container: any): string[] { + if (!container?.ref) return []; + return container.ref.map((r: any) => r['@_id']); +} + +export function parseSpecies(xml: string): SpeciesData { + const root = parser.parse(xml).species; + const subspecies = root.subspecies?.entry ?? []; + + return { + id: root['@_id'], + name: root['@_name'], + description: root.description.trim(), + subspeciesLabel: root['@_subspeciesLabel'], + languages: extractRefs(root.languages), + citizenships: extractRefs(root.citizenships), + subspecies: subspecies.map((e: any) => ({ + id: e['@_id'], + name: e['@_name'], + description: e.description.trim() + })) + }; +} + +export function parseCitizenships(xml: string): CitizenshipData[] { + const root = parser.parse(xml).citizenships; + return root.citizenship.map((c: any) => ({ + id: c['@_id'], + name: c['@_name'], + description: c.description?.trim() + })); +} + +export function parseLanguages(xml: string): LanguageData[] { + const root = parser.parse(xml).languages; + return root.language.map((l: any) => ({ + id: l['@_id'], + name: l['@_name'], + description: l.description.trim() + })); +} + +function slugify(label: string): string { + return label.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''); +} + +function parseOptions(field: any): SelectOption[] { + if (!field.option) return []; + return field.option.map((o: any) => ({ + value: o['@_value'], + label: o['@_label'] + })); +} + +function parseField(raw: any): FieldDef { + const base = { + key: slugify(raw['@_label']), + label: raw['@_label'] + }; + const type = raw['@_type']; + + switch (type) { + case 'text': + case 'textarea': + case 'date': + return { ...base, type, placeholder: raw['@_placeholder'] }; + case 'list': + case 'height': + case 'weight': + case 'species': + case 'subspecies': + case 'citizenship': + case 'languages': + return { ...base, type }; + case 'number': + return { + ...base, + type, + min: raw['@_min'] != null ? Number(raw['@_min']) : undefined, + max: raw['@_max'] != null ? Number(raw['@_max']) : undefined, + unit: raw['@_unit'] + }; + case 'select': + case 'multi-select': + case 'checkbox': + return { ...base, type, options: parseOptions(raw) }; + default: + return { ...base, type: 'text' }; + } +} + +export function parseTemplate(xml: string, id: string): Template { + const root = parser.parse(xml).template; + + const records: RecordDef[] = root.record.map((r: any) => ({ + type: r['@_type'], + expanded: r['@_expanded'] === 'true', + preamble: r.preamble?.trim(), + fields: r.field.map(parseField) + })); + + return { + id, + name: root['@_name'], + description: root.description ?? '', + schemaVersion: Number(root['@_schemaVersion'] ?? 1), + records + }; +} diff --git a/src/lib/data/types.ts b/src/lib/data/types.ts new file mode 100644 index 0000000..da92d87 --- /dev/null +++ b/src/lib/data/types.ts @@ -0,0 +1,21 @@ +export interface SpeciesData { + id: string; + name: string; + description: string; + subspeciesLabel: string; + subspecies: { id: string; name: string; description: string }[]; + languages: string[]; + citizenships: string[]; +} + +export interface CitizenshipData { + id: string; + name: string; + description?: string; +} + +export interface LanguageData { + id: string; + name: string; + description: string; +}