commit 4e31f18045d1a1b4a3b4db5619ff92ad2d16c6dd Author: Endeavorance Date: Wed Feb 12 10:31:22 2025 -0500 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b1ee42 --- /dev/null +++ b/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..fbd02e1 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# lang + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.2.2. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..bcfca3b --- /dev/null +++ b/biome.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "ignore": [ + "dist", + ".next", + "public", + "*.d.ts", + "*.json", + "build", + "*.svelte", + ".svelte-kit" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noArrayIndexKey": "off" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "css": { + "parser": { + "cssModules": true + } + } +} diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..f00cd95 --- /dev/null +++ b/bun.lock @@ -0,0 +1,54 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "lang", + "dependencies": { + "yaml": "^2.7.0", + "zod": "^3.24.1", + }, + "devDependencies": { + "@biomejs/biome": "^1.9.4", + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5.0.0", + }, + }, + }, + "packages": { + "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], + + "@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="], + + "@types/node": ["@types/node@22.13.1", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="], + + "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], + + "bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="], + + "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], + + "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], + + "yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="], + + "zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="], + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e07fe5f --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "lang", + "module": "index.ts", + "type": "module", + "devDependencies": { + "@biomejs/biome": "^1.9.4", + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "yaml": "^2.7.0", + "zod": "^3.24.1" + } +} \ No newline at end of file diff --git a/playbill/binding.yaml b/playbill/binding.yaml new file mode 100644 index 0000000..21980ec --- /dev/null +++ b/playbill/binding.yaml @@ -0,0 +1,3 @@ +id: the-great-spires +name: The Great Spires +version: "1" diff --git a/playbill/methods/abilities/identify-poison.yaml b/playbill/methods/abilities/identify-poison.yaml new file mode 100644 index 0000000..cc200c9 --- /dev/null +++ b/playbill/methods/abilities/identify-poison.yaml @@ -0,0 +1,24 @@ +$define: ability +id: identify-poison +name: Identify Poison +type: action +costs: + ap: 1 +roll: focus +boons: + - You know the antidote to the detected poison + - Your detection is imperceptible +banes: + - It is obvious you are checking for poison +description: |- + Focus your senses on a food or drink to determine if it is poisoned. + + {dmg:1,magic} + You can also identify the type of poison used. +--- +$define: ability +id: neutralize-poison +name: Neutralize Poison +type: action +roll: cunning +description: Neutralize a poison diff --git a/playbill/methods/gourmond.yaml b/playbill/methods/gourmond.yaml new file mode 100644 index 0000000..744f7b4 --- /dev/null +++ b/playbill/methods/gourmond.yaml @@ -0,0 +1,6 @@ +$define: method +id: gourmond +name: Gourmond +curator: Lump +abilities: [["identify-poison"]] +description: Your prowess in cooking is second only to your prowess in eating. diff --git a/playbill/world.yaml b/playbill/world.yaml new file mode 100644 index 0000000..5522c03 --- /dev/null +++ b/playbill/world.yaml @@ -0,0 +1,6 @@ +$define: lore +id: world +description: |- + This is a lore file. + + It has some information in it, and is mostly just to be read. diff --git a/src/binding.ts b/src/binding.ts new file mode 100644 index 0000000..c8ddcaf --- /dev/null +++ b/src/binding.ts @@ -0,0 +1,19 @@ +import YAML from "yaml"; +import z from "zod"; + +const BindingSchema = z.object({ + id: z.string(), + playbill: z.string().default("Unnamed playbill"), + author: z.string().optional(), + version: z.number(), + files: z.array(z.string()).default(["**/*.yaml"]), +}); + +type Binding = z.infer; + +export async function loadBindingFile(filePath: string): Promise { + const file = Bun.file(filePath); + const text = await file.text(); + const parsed = YAML.parse(text); + return BindingSchema.parse(parsed); +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..1658ec1 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,34 @@ +import { Glob } from "bun"; +import { loadBindingFile } from "./binding"; +import type { AnyResource } from "./playbill-schema"; +import { loadResourceFile } from "./resource"; + +const bindingPath = Bun.argv[2]; +const binding = await loadBindingFile(bindingPath); + +const fileGlobs = binding.files; +const bindingFileDirname = bindingPath.split("/").slice(0, -1).join("/"); + +const allFilePaths: string[] = []; + +for (const thisGlob of fileGlobs) { + const glob = new Glob(thisGlob); + + const results = glob.scanSync({ + cwd: bindingFileDirname, + absolute: true, + followSymlinks: true, + onlyFiles: true, + }); + + allFilePaths.push(...results); +} + +const loadedResources: AnyResource[] = []; + +for (const filepath of allFilePaths) { + const loaded = await loadResourceFile(filepath); + loadedResources.push(...loaded); +} + +console.log(loadedResources); diff --git a/src/lang.ts b/src/lang.ts new file mode 100644 index 0000000..8d89722 --- /dev/null +++ b/src/lang.ts @@ -0,0 +1,52 @@ +import type { ZodSchema } from "zod"; + +const directiveHandlers = { + dmg: (amount = "1", type = "physical") => { + return `${amount}`; + }, +}; + +type DirectiveHandler = (...args: string[]) => string; + +function processCommands( + text: string, + handlers: { [key: string]: DirectiveHandler }, +): string { + const commandPattern = /\{(\w+):([^}]+)\}/g; + + return text.replace(commandPattern, (match, command, args) => { + const handler = handlers[command]; + if (handler) { + const parsedArgs = []; + let currentArg = ""; + let inQuotes = false; + + for (let i = 0; i < args.length; i++) { + const char = args[i]; + if (char === '"' && (i === 0 || args[i - 1] !== "\\")) { + inQuotes = !inQuotes; + } else if (char === "," && !inQuotes) { + parsedArgs.push(currentArg.trim().replace(/^"|"$/g, "")); + currentArg = ""; + } else { + currentArg += char; + } + } + parsedArgs.push(currentArg.trim().replace(/^"|"$/g, "")); + + return handler(...parsedArgs); + } + return match; + }); +} + +export async function processLang(input: string): Promise { + const lines = input.split("\n"); + const processedLines: string[] = []; + for (const line of lines) { + const processed = processCommands(line, directiveHandlers); + processedLines.push(processed); + } + + return processedLines.join("\n"); +} diff --git a/src/playbill-schema.ts b/src/playbill-schema.ts new file mode 100644 index 0000000..52a288c --- /dev/null +++ b/src/playbill-schema.ts @@ -0,0 +1,264 @@ +import { type ZodSchema, z } from "zod"; + +/* + * TYPE REFERENCES + * ------------------------ + * Ability N/A + * Tag N/A + * Lore N/A + * Method Ability + * Item Tag + * Species Ability + * Entity Ability, Species + */ + +// String shapes +export const IDSchema = z.string().regex(/[a-z\-]+/); +export const NameSchema = z.string().max(40).default("Unnamed"); +export const DescriptionSchema = z.string().default("No description"); + +// Constants +export const STAT_ABBREVIATIONS = ["ap", "ep", "hp", "xp"] as const; +export const ITEM_TYPES = ["trinket", "worn", "wielded"] as const; +export const ROLL_TYPES = ["none", "attack", "fate"] as const; +export const TALENTS = [ + "muscle", + "knowledge", + "focus", + "charm", + "cunning", + "spark", +] as const; +export const ABILITY_TYPES = ["action", "cue", "trait"] as const; +export const TALENT_PROWESS = ["novice", "adept", "master"] as const; + +// Enums +export const StatsSchema = z.enum(STAT_ABBREVIATIONS); +export const ItemTypeSchema = z.enum(ITEM_TYPES); +export const RollTypeSchema = z.enum(ROLL_TYPES); +export const AbilityTypeSchema = z.enum(ABILITY_TYPES); + +export const TalentSchema = z.enum(TALENTS); + +export const TalentProwessSchema = z.enum(TALENT_PROWESS); + +const ResourceTypes = [ + "ability", + "species", + "entity", + "item", + "tag", + "lore", + "method", + "playbill", +] as const; +const ResourceTypeSchema = z.enum(ResourceTypes); +type ResourceType = z.infer; + +// Inner utility shapes +export const StatSpreadSchema = z.object({ + ap: z.number().default(0), + ep: z.number().default(0), + hp: z.number().default(0), + xp: z.number().default(0), +}); + +export const TalentSpreadSchema = z.object({ + muscle: TalentProwessSchema.default("novice"), + knowledge: TalentProwessSchema.default("novice"), + focus: TalentProwessSchema.default("novice"), + charm: TalentProwessSchema.default("novice"), + cunning: TalentProwessSchema.default("novice"), + spark: TalentProwessSchema.default("novice"), +}); + +export const PlaybillResourceSchema = z.object({ + $define: ResourceTypeSchema, + id: IDSchema, + name: NameSchema, + description: DescriptionSchema, +}); + +// Playbill component shapes +export const AbilitySchema = PlaybillResourceSchema.extend({ + $define: z.literal("ability"), + name: NameSchema.default("Unnamed Ability"), + type: AbilityTypeSchema, + costs: StatSpreadSchema.default({}), + roll: RollTypeSchema.or(TalentSchema).default("none"), + banes: z.array(z.string()).default([]), + boons: z.array(z.string()).default([]), +}); +export type PlaybillAbility = z.infer; + +export const EntitySchema = PlaybillResourceSchema.extend({ + $define: z.literal("entity"), + name: NameSchema.default("Unnamed Species"), + species: z.string(IDSchema), + abilities: z.array(IDSchema), + stats: StatSpreadSchema, + statMaxModifiers: StatSpreadSchema, + talents: TalentSpreadSchema, +}); + +export const ItemTagSchema = PlaybillResourceSchema.extend({ + $define: z.literal("tag"), + name: NameSchema.default("Unnamed Item Tag"), + icon: z.string().url(), +}); + +export const ItemSchema = PlaybillResourceSchema.extend({ + $define: z.literal("item"), + name: NameSchema.default("Unnamed Item"), + type: ItemTypeSchema, + slots: z.number().min(0).max(5).default(0), + tags: z.array(IDSchema).default([]), +}); + +export const LoreSchema = PlaybillResourceSchema.extend({ + $define: z.literal("lore"), + name: NameSchema.default("Unnamed Lore Entry"), + categories: z.array(z.string()).default([]), +}); + +export const MethodSchema = PlaybillResourceSchema.extend({ + $define: z.literal("method"), + name: NameSchema.default("Unnamed Method"), + curator: NameSchema, + abilities: z.array(z.array(IDSchema)).default([]), +}); + +export const SpeciesSchema = PlaybillResourceSchema.extend({ + $define: z.literal("species"), + name: NameSchema.default("Unnamed Species"), + abilities: z.array(IDSchema).default([]), + statMaxModifiers: StatSpreadSchema.default({}), + talentMinimums: TalentSpreadSchema.default({}), +}); + +// Full Playbill Schema +export const PlaybillSchema = PlaybillResourceSchema.extend({ + $define: z.literal("playbill"), + version: z.string().default("0"), + name: NameSchema.default("Unnamed Playbill"), + author: z.string().default("Anonymous"), + + abilities: z.array(AbilitySchema).default([]), + entities: z.array(EntitySchema).default([]), + items: z.array(ItemSchema).default([]), + tags: z.array(ItemTagSchema).default([]), + lore: z.array(LoreSchema).default([]), + methods: z.array(MethodSchema).default([]), + species: z.array(SpeciesSchema).default([]), +}); + +export const ValidatedPlaybillSchema = PlaybillSchema.superRefine( + (val, ctx) => { + // For each method, ensure its abilities exist + for (const method of val.methods) { + const methodAbilityIds = method.abilities.flat(); + + for (const abilityId of methodAbilityIds) { + if (!val.abilities.some((a) => a.id === abilityId)) { + ctx.addIssue({ + message: `Method ${method.id} references undefined ability ${abilityId}`, + code: z.ZodIssueCode.custom, + }); + } + } + } + + // For each species, ensure its abilities + for (const species of val.species) { + const speciesAbilityIds = species.abilities.flat(); + + for (const abilityId of speciesAbilityIds) { + if (!val.abilities.some((a) => a.id === abilityId)) { + ctx.addIssue({ + message: `Species ${species.id} references undefined ability ${abilityId}`, + code: z.ZodIssueCode.custom, + }); + } + } + } + + // For each entity, ensure its species and known abilities exist + for (const entity of val.entities) { + for (const abilityId of entity.abilities) { + if (!val.abilities.some((a) => a.id === abilityId)) { + ctx.addIssue({ + message: `Entity ${entity.id} references undefined ability ${abilityId}`, + code: z.ZodIssueCode.custom, + }); + } + } + + if (!val.species.some((s) => s.id === entity.species)) { + ctx.addIssue({ + message: `Entity ${entity.id} references undefined species ${entity.species}`, + code: z.ZodIssueCode.custom, + }); + } + } + + // For each item, ensure its tags exist + for (const item of val.items) { + for (const tag of item.tags) { + if (!val.tags.some((t) => t.id === tag)) { + ctx.addIssue({ + message: `Item ${item.id} references undefined tag ${tag}`, + code: z.ZodIssueCode.custom, + }); + } + } + } + }, +); + +export type Playbill = z.infer; +export type ValidatedPlaybill = z.infer; + +export const ResourceMap: Record = { + ability: AbilitySchema, + species: SpeciesSchema, + entity: EntitySchema, + item: ItemSchema, + tag: ItemTagSchema, + method: MethodSchema, + lore: LoreSchema, + playbill: PlaybillSchema, +}; + +export const AnyResourceSchema = z.discriminatedUnion("$define", [ + AbilitySchema, + SpeciesSchema, + EntitySchema, + ItemSchema, + ItemTagSchema, + MethodSchema, + LoreSchema, + PlaybillSchema, +]); + +export type AnyResource = z.infer; + +export function getEmptyPlaybill(): Playbill { + return PlaybillSchema.parse({ + $define: "playbill", + id: "empty", + abilities: [ + { + id: "fireball", + name: "Fireball", + type: "action", + description: "Cast a fireball! My goodness.", + }, + { + id: "magic-shield", + name: "Magic Shield", + type: "cue", + description: "Defend yerself", + }, + ], + }); +} diff --git a/src/resource.ts b/src/resource.ts new file mode 100644 index 0000000..c55e144 --- /dev/null +++ b/src/resource.ts @@ -0,0 +1,36 @@ +import YAML from "yaml"; +import { + type AnyResource, + AnyResourceSchema, + ResourceMap, +} from "./playbill-schema"; + +export async function loadResourceFile( + filePath: string, +): Promise { + try { + const file = Bun.file(filePath); + const text = await file.text(); + + const parsedDocs = YAML.parseAllDocuments(text); + + const collection: AnyResource[] = []; + + for (const doc of parsedDocs) { + if (doc.errors.length > 0) { + throw new Error(`Error parsing ${filePath}: ${doc.errors}`); + } + + const raw = doc.toJS(); + const parsed = AnyResourceSchema.parse(raw); + const type = parsed.$define; + const schemaToUse = ResourceMap[type]; + + collection.push(schemaToUse.parse(parsed)); + } + + return collection; + } catch (error) { + throw new Error(`Error loading ${filePath}: ${error}`); + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..238655f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}