diff --git a/src/index.ts b/src/index.ts index 8c0508d..72fccba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,89 +1,198 @@ -// TODO file handle? -import { FileHandle, open } from 'fs/promises'; -// const file = await fsPromise.open('./words.txt', 'r'); -const TOC_PREFIX_TAG = '## ' -const TOC_PREFIX_COMMENT = '#' -const TOC_TAG_SEPARATOR = ':' -const TOC_LINE_MAX_LENGTH = 1024 +/* +https://wowwiki-archive.fandom.com/wiki/TOC_format +https://wowpedia.fandom.com/wiki/TOC_format +*/ +import { FileHandle } from 'fs/promises'; +export const TOC_PREFIX_TAG = '## ' +export const TOC_PREFIX_COMMENT = '#' +export const TOC_TAG_DELIMITER = ':' +export const TOC_LINE_MAX_LENGTH = 1024 -export type TOC = {/*interface: number, title: string, titleLocalized: Record, xTags: Record,*/ allTags: Record,files: string[]} +export const TOC_TAG_NAME_INTERFACE = 'Interface' +export const TOC_TAG_NAME_TITLE = 'Title' +export const TOC_TAG_NAME_NOTES = 'Notes' +export const TOC_TAG_NAME_ICONTEXTURE = 'IconTexture' +export const TOC_TAG_NAME_ICONATLAS = 'IconAtlas' +export const TOC_TAG_NAME_ADDONCOMPARTMENTFUNC = 'AddonCompartmentFunc' +export const TOC_TAG_NAME_ADDONCOMPARTMENTFUNCONENTER = 'AddonCompartmentFuncOnEnter' +export const TOC_TAG_NAME_ADDONCOMPARTMENTFUNCONLEAVE = 'AddonCompartmentFuncOnLeave' +export const TOC_TAG_NAME_LOADONDEMAND = 'LoadOnDemand' +export const TOC_TAG_NAME_OPTIONALDEPS = 'OptionalDeps' +export const TOC_TAG_NAME_LOADWITH = 'LoadWith' +export const TOC_TAG_NAME_LOADMANAGERS = 'LoadManagers' +export const TOC_TAG_NAME_DEFAULTSTATE = 'DefaultState' +export const TOC_TAG_NAME_SAVEDVARIABLES = 'SavedVariables' +export const TOC_TAG_NAME_SAVEDVARIABESPERCHARACTER = 'SavedVariablesPerCharacter' +export const TOC_TAG_NAME_AUTHOR = 'Author' +export const TOC_TAG_NAME_VERSION = 'Version' -export const parse = async function(file: FileHandle, strict = true) { - const result: TOC = { - // interface: 0, - // title: '', - // titleLocalized: {}, - // xTags: {}, - allTags: {}, - files: [], +export const TOC_TAG_PREFIX_TITLELOCALIZED = 'title-' +export const TOC_TAG_PREFIX_NOTESLOCALIZED = 'notes-' +export const TOC_TAG_PREFIX_DEP = 'dep' +export const TOC_TAG_PREFIX_X = 'x-' + +export const TOC_TAG_LIST_DELIMITER = ',' + +export const TOC_TAG_VALUE_LOADONDEMAND = '1' +export const TOC_TAG_VALUE_DEFAULTSTATE_DISABLED = 'disabled' + +export enum WOW_LOCALES { + frFR = 'frFR', + deDE = 'deDE', + enGB = 'enGB', + enUS = 'enUS', + itIT = 'itIT', + koKR = 'koKR', + zhCN = 'zhCN', + zhTW = 'zhTW', + ruRU = 'ruRU', + esES = 'esES', + esMX = 'esMX', + ptBR = 'ptBR', +} + +export class TOC { + Interface: number | null = null + Title: string | null = null + TitleLocalized: Partial> = {} + Notes: string | null = null + NotesLocalized: Partial> = {} + IconTexture: string | null = null + IconAtlas: string | null = null + AddonCompartmentFunc: string | null = null + AddonCompartmentFuncOnEnter: string | null = null + AddonCompartmentFuncOnLeave: string | null = null + LoadOnDemand: boolean = false + Dependencies: string[] = [] + OptionalDeps: string[] = [] + LoadWith: string[] = [] + LoadManagers: string[] = [] + DefaultState: boolean = true + SavedVariables: string[] = [] + SavedVariablesPerCharacter: string[] = [] + Author: string | null = null + Version: string | null = null + xTags: Record = {} + files: string[] = [] + + /* + toString(){ + var result: string[] = []; + for(const tag in toc.allTags) { + result.push(`${TOC_PREFIX_TAG}${tag}${TOC_TAG_SEPARATOR} ${toc.allTags[tag]}`) + } + result.concat(this.files) + return result.join('\n'); }; + */ +} + +// import { open } from 'fs/promises'; +// const file = await open('./words.txt', 'r'); +export const parse = async function(file: FileHandle, strict = true) { + const result = new TOC(); + const allTags: Record = {} for await (const line of file.readLines()) { if (strict && line.length > TOC_LINE_MAX_LENGTH) { throw new Error('Line is longer than TOC_LINE_MAX_LENGTH characters, WoW will truncate!'); } if (line.slice(0, TOC_PREFIX_TAG.length) === TOC_PREFIX_TAG) { - const tagData = line.slice(TOC_PREFIX_TAG.length+1).split(TOC_TAG_SEPARATOR) + const tagData = line.slice(TOC_PREFIX_TAG.length+1).split(TOC_TAG_DELIMITER) if(tagData.length !== 2){ - throw new Error(`Tag could not be parsed: ${tagData}`) + if(strict){ + throw new Error(`Tag could not be parsed: ${tagData}`) + } else { + continue + } } const tagName = tagData[0].trim() const tagValue = tagData[1].trim() - if(strict && result.allTags[tagName]){ + if(strict && allTags[tagName]){ throw new Error(`Duplicate value: ${tagName}`) } - result.allTags[tagName] = tagValue; + allTags[tagName] = tagValue; - // specific tags - /*switch(tagName.toLowerCase()){ - case 'interface': - result.interface = Number(tagValue) + switch(tagName.toLowerCase()){ + case TOC_TAG_NAME_INTERFACE.toLowerCase(): + result.Interface = Number(tagValue) break; - case 'title': - result.title + case TOC_TAG_NAME_TITLE.toLowerCase(): + result.Title = tagValue break; - }*/ - - // + case TOC_TAG_NAME_NOTES.toLocaleLowerCase(): + result.Notes = tagValue + break + case TOC_TAG_NAME_ICONTEXTURE.toLowerCase(): + result.IconTexture = tagValue + break + case TOC_TAG_NAME_ICONATLAS.toLowerCase(): + result.IconAtlas = tagValue + break + case TOC_TAG_NAME_ADDONCOMPARTMENTFUNC.toLowerCase(): + result.AddonCompartmentFunc = tagValue + break + case TOC_TAG_NAME_ADDONCOMPARTMENTFUNCONENTER.toLowerCase(): + result.AddonCompartmentFuncOnEnter = tagValue + break + case TOC_TAG_NAME_ADDONCOMPARTMENTFUNCONLEAVE.toLowerCase(): + result.AddonCompartmentFuncOnLeave = tagValue + break + case TOC_TAG_NAME_LOADONDEMAND.toLowerCase(): + result.LoadOnDemand = tagValue === TOC_TAG_VALUE_LOADONDEMAND + break + case TOC_TAG_NAME_OPTIONALDEPS.toLowerCase(): + result.OptionalDeps = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim()) + break + case TOC_TAG_NAME_LOADWITH.toLowerCase(): + result.LoadWith = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim()) + break + case TOC_TAG_NAME_LOADMANAGERS.toLowerCase(): + result.LoadManagers = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim()) + break + case TOC_TAG_NAME_DEFAULTSTATE.toLowerCase(): + result.DefaultState = tagValue.toLowerCase() !== TOC_TAG_VALUE_DEFAULTSTATE_DISABLED + break + case TOC_TAG_NAME_SAVEDVARIABLES.toLowerCase(): + result.SavedVariables = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim()) + break + case TOC_TAG_NAME_SAVEDVARIABESPERCHARACTER.toLowerCase(): + result.SavedVariablesPerCharacter = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim()) + break + case TOC_TAG_NAME_AUTHOR.toLowerCase(): + result.Author = tagValue + break + case TOC_TAG_NAME_VERSION.toLowerCase(): + result.Version = tagValue + break + default: + if(tagName.toLowerCase().startsWith(TOC_TAG_PREFIX_TITLELOCALIZED) && tagName.length === TOC_TAG_PREFIX_TITLELOCALIZED.length+4){ + const locale = tagName.slice(TOC_TAG_PREFIX_TITLELOCALIZED.length,TOC_TAG_PREFIX_TITLELOCALIZED.length+4) + if(Object.keys(WOW_LOCALES).includes(locale)){ + result.TitleLocalized[locale as WOW_LOCALES] = tagValue + } else if(strict){ + throw new Error(`locale not found ${locale}`) + } + } else if(tagName.toLowerCase().startsWith(TOC_TAG_PREFIX_NOTESLOCALIZED) && tagName.length === TOC_TAG_PREFIX_NOTESLOCALIZED.length+4){ + const locale = tagName.slice(TOC_TAG_PREFIX_NOTESLOCALIZED.length,TOC_TAG_PREFIX_NOTESLOCALIZED.length+4) + if(Object.keys(WOW_LOCALES).includes(locale)){ + result.NotesLocalized[locale as WOW_LOCALES] = tagValue + } else if(strict){ + throw new Error(`locale not found ${locale}`) + } + } else if(tagName.toLowerCase().startsWith(TOC_TAG_PREFIX_DEP)){ + result.Dependencies = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim()) + } else if(tagName.toLowerCase().startsWith(TOC_TAG_PREFIX_X)){ + result.xTags[tagName.slice(TOC_TAG_PREFIX_X.length)] = tagValue + } else if(strict){ + throw new Error(`Unknown Tag name: ${tagName}`) + } + } } else if (line.slice(0, 1) !== TOC_PREFIX_COMMENT && !line.trim()) { - result.files.push(line.trimEnd())); + result.files.push(line.trimEnd()); } } return result; }; - -/* -https://wowpedia.fandom.com/wiki/TOC_format -## Interface: 100007 -## Title: Waiting for Godot -## Title-frFR: En attendant Godot -## Notes: This word is |cFFFF0000red|r -## IconTexture: Interface\Icons\TEMP -## IconAtlas: TaskPOI-Icon -// ## AddonCompartmentFunc: MyAddon_OnAddonCompartmentClick -// ## AddonCompartmentFuncOnEnter: MyAddon_OnAddonCompartmentEnter -// ## AddonCompartmentFuncOnLeave: MyAddon_OnAddonCompartmentLeave -## LoadOnDemand: 1 -## Dependencies: someAddOn, someOtherAddOn -## OptionalDeps: someAddOn, someOtherAddOn -## LoadWith: someAddOn, someOtherAddOn -// ## LoadManagers: someAddOn, someOtherAddOn -## DefaultState: disabled -## SavedVariables: MyAddOnNameFoo, MyAddOnNameBar -## SavedVariablesPerCharacter: MyAddOnNameAnotherVariable -## Author -## Version -## X-_____ - -*/ - -export const stringify = function(toc: TOC) { - var result: string[] = []; - for(const tag in toc.allTags) { - result.push(`${TOC_PREFIX_TAG}${tag}${TOC_TAG_SEPARATOR} ${toc.allTags[tag]}`) - } - result.concat(toc.files) - return result.join('\n'); -};