diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index ed2a819..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: "Build and deploy" - -on: - # Trigger the workflow every time you push to the `main` branch - # Using a different branch name? Replace `main` with your branch's name - push: - tags: - - "*" - - # Allows you to run this workflow manually from the Actions tab on GitHub. - workflow_dispatch: - -# Allow this job to clone the repo and create a page deployment -permissions: - contents: write -jobs: - build-and-deploy: - concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession. - runs-on: ubuntu-latest - steps: - - name: Checkout 🛎️ - uses: actions/checkout@v4 - - - name: Install and Build 🔧 - run: | - npm ci - npm run docs - - - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@v4 - with: - branch: gh-pages # The branch the action should deploy to. - folder: docs-dist # The folder the action should deploy. - - # When running the Astro build, types and content are updated automatically - # This action commits the changes - - name: Commit changes from Astro 🚀 - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: Update astro files (automated) - branch: main diff --git a/.gitignore b/.gitignore index 9495685..fb42542 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ node_modules *.test.js /dist /docs/src/content/docs/api/**/* +/docs/.astro/ /docs-dist \ No newline at end of file diff --git a/docs/.astro/astro/content.d.ts b/docs/.astro/astro/content.d.ts deleted file mode 100644 index 1fcbb32..0000000 --- a/docs/.astro/astro/content.d.ts +++ /dev/null @@ -1,291 +0,0 @@ -declare module 'astro:content' { - interface Render { - '.mdx': Promise<{ - Content: import('astro').MarkdownInstance<{}>['Content']; - headings: import('astro').MarkdownHeading[]; - remarkPluginFrontmatter: Record; - }>; - } -} - -declare module 'astro:content' { - interface RenderResult { - Content: import('astro/runtime/server/index.js').AstroComponentFactory; - headings: import('astro').MarkdownHeading[]; - remarkPluginFrontmatter: Record; - } - interface Render { - '.md': Promise; - } - - export interface RenderedContent { - html: string; - metadata?: { - imagePaths: Array; - [key: string]: unknown; - }; - } -} - -declare module 'astro:content' { - type Flatten = T extends { [K: string]: infer U } ? U : never; - - export type CollectionKey = keyof AnyEntryMap; - export type CollectionEntry = Flatten; - - export type ContentCollectionKey = keyof ContentEntryMap; - export type DataCollectionKey = keyof DataEntryMap; - - type AllValuesOf = T extends any ? T[keyof T] : never; - type ValidContentEntrySlug = AllValuesOf< - ContentEntryMap[C] - >['slug']; - - /** @deprecated Use `getEntry` instead. */ - export function getEntryBySlug< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - // Note that this has to accept a regular string too, for SSR - entrySlug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - - /** @deprecated Use `getEntry` instead. */ - export function getDataEntryById( - collection: C, - entryId: E, - ): Promise>; - - export function getCollection>( - collection: C, - filter?: (entry: CollectionEntry) => entry is E, - ): Promise; - export function getCollection( - collection: C, - filter?: (entry: CollectionEntry) => unknown, - ): Promise[]>; - - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >(entry: { - collection: C; - slug: E; - }): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >(entry: { - collection: C; - id: E; - }): E extends keyof DataEntryMap[C] - ? Promise - : Promise | undefined>; - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - slug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - collection: C, - id: E, - ): E extends keyof DataEntryMap[C] - ? Promise - : Promise | undefined>; - - /** Resolve an array of entry references from the same collection */ - export function getEntries( - entries: { - collection: C; - slug: ValidContentEntrySlug; - }[], - ): Promise[]>; - export function getEntries( - entries: { - collection: C; - id: keyof DataEntryMap[C]; - }[], - ): Promise[]>; - - export function render( - entry: AnyEntryMap[C][string], - ): Promise; - - export function reference( - collection: C, - ): import('astro/zod').ZodEffects< - import('astro/zod').ZodString, - C extends keyof ContentEntryMap - ? { - collection: C; - slug: ValidContentEntrySlug; - } - : { - collection: C; - id: keyof DataEntryMap[C]; - } - >; - // Allow generic `string` to avoid excessive type errors in the config - // if `dev` is not running to update as you edit. - // Invalid collection names will be caught at build time. - export function reference( - collection: C, - ): import('astro/zod').ZodEffects; - - type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; - type InferEntrySchema = import('astro/zod').infer< - ReturnTypeOrOriginal['schema']> - >; - - type ContentEntryMap = { - "docs": { -"api/README.md": { - id: "api/README.md"; - slug: "api/readme"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/classes/SBL.md": { - id: "api/classes/SBL.md"; - slug: "api/classes/sbl"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/classes/Schema.md": { - id: "api/classes/Schema.md"; - slug: "api/classes/schema"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/classes/Text.md": { - id: "api/classes/Text.md"; - slug: "api/classes/text"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/functions/remove.md": { - id: "api/functions/remove.md"; - slug: "api/functions/remove"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/functions/sequence.md": { - id: "api/functions/sequence.md"; - slug: "api/functions/sequence"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/functions/transliterate.md": { - id: "api/functions/transliterate.md"; - slug: "api/functions/transliterate"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/interfaces/ClusterFeature.md": { - id: "api/interfaces/ClusterFeature.md"; - slug: "api/interfaces/clusterfeature"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/interfaces/HebrewFeature.md": { - id: "api/interfaces/HebrewFeature.md"; - slug: "api/interfaces/hebrewfeature"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/interfaces/PassThrough.md": { - id: "api/interfaces/PassThrough.md"; - slug: "api/interfaces/passthrough"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/interfaces/RemoveOptions.md": { - id: "api/interfaces/RemoveOptions.md"; - slug: "api/interfaces/removeoptions"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/interfaces/SylOpts.md": { - id: "api/interfaces/SylOpts.md"; - slug: "api/interfaces/sylopts"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/interfaces/SyllableFeature.md": { - id: "api/interfaces/SyllableFeature.md"; - slug: "api/interfaces/syllablefeature"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/interfaces/WordFeature.md": { - id: "api/interfaces/WordFeature.md"; - slug: "api/interfaces/wordfeature"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/type-aliases/ClusterCallback.md": { - id: "api/type-aliases/ClusterCallback.md"; - slug: "api/type-aliases/clustercallback"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/type-aliases/SyllableCallback.md": { - id: "api/type-aliases/SyllableCallback.md"; - slug: "api/type-aliases/syllablecallback"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"api/type-aliases/WordCallback.md": { - id: "api/type-aliases/WordCallback.md"; - slug: "api/type-aliases/wordcallback"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".md"] }; -"getting-started/quick-start.mdx": { - id: "getting-started/quick-start.mdx"; - slug: "getting-started/quick-start"; - body: string; - collection: "docs"; - data: InferEntrySchema<"docs"> -} & { render(): Render[".mdx"] }; -}; - - }; - - type DataEntryMap = { - - }; - - type AnyEntryMap = ContentEntryMap & DataEntryMap; - - export type ContentConfig = typeof import("../../src/content/config.js"); -} diff --git a/docs/.astro/settings.json b/docs/.astro/settings.json deleted file mode 100644 index e10acec..0000000 --- a/docs/.astro/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "_variables": { - "lastUpdateCheck": 1732324633605 - } -} \ No newline at end of file diff --git a/docs/.astro/types.d.ts b/docs/.astro/types.d.ts deleted file mode 100644 index 9a2a78c..0000000 --- a/docs/.astro/types.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -/// \ No newline at end of file diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 39e4b07..5035c9a 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -3,7 +3,7 @@ import { defineConfig } from "astro/config"; import starlightTypeDoc, { typeDocSidebarGroup } from "starlight-typedoc"; import { remarkBasePath } from "./prepend_base_path.js"; -const basePath = process.env.NODE_ENV === "production" ? "/hebrew-transliteration" : "/"; +const basePath = process.env.NODE_ENV === "production" ? "/hebrew-transliteration/" : "/"; // https://astro.build/config export default defineConfig({ @@ -22,7 +22,7 @@ export default defineConfig({ redirects: { "/": { status: 302, - destination: `${basePath}/getting-started/quick-start` + destination: `${basePath}getting-started/quick-start` } }, integrations: [ diff --git a/src/schema.ts b/src/schema.ts index 65ef6a7..270f460 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -2048,7 +2048,27 @@ export class SBL extends Schema { declare DIVINE_NAME_ELOHIM: Schema["DIVINE_NAME_ELOHIM"]; /** @category Orthographic Features @default undefined */ declare SYLLABLE_SEPARATOR: Schema["SYLLABLE_SEPARATOR"]; - /** @category Orthographic Features @default undefined */ + /** + * @category Orthographic Features + * @default + * ```js + * [ + * { + * FEATURE: "syllable", + * HEBREW: /[\u{05B4}\u{05BB}]/u, + * TRANSLITERATION: (syllable, heb, schema) => { + * const hasMater = syllable.clusters.some((cluster) => cluster.isMater); + * if (syllable.isAccented && !hasMater) { + * const macron = "\u0304"; + * const output = syllable.hasVowelName("HIRIQ") ? schema["HIRIQ"] + macron : schema["QUBUTS"] + macron; + * return syllable.text.replace(heb, output.normalize("NFC")); + * } + * return syllable.text; + * } + * } + * ] + * ``` + */ declare ADDITIONAL_FEATURES: Schema["ADDITIONAL_FEATURES"]; /** @category Orthographic Features @default undefined */ declare STRESS_MARKER: Schema["STRESS_MARKER"]; @@ -2087,7 +2107,7 @@ export class SBL extends Schema { QAMATS: schema.QAMATS ?? "ā", HOLAM: schema.HOLAM ?? "ō", HOLAM_HASER: schema.HOLAM_HASER ?? "ō", - QUBUTS: schema.QUBUTS ?? "ū", + QUBUTS: schema.QUBUTS ?? "u", DAGESH: schema.DAGESH ?? "", DAGESH_CHAZAQ: schema.DAGESH_CHAZAQ ?? true, MAQAF: schema.MAQAF ?? "-", @@ -2167,7 +2187,7 @@ export class SBL extends Schema { this.QAMATS = schema.QAMATS ?? "ā"; this.HOLAM = schema.HOLAM ?? "ō"; this.HOLAM_HASER = schema.HOLAM_HASER ?? "ō"; - this.QUBUTS = schema.QUBUTS ?? "ū"; + this.QUBUTS = schema.QUBUTS ?? "u"; this.DAGESH = schema.DAGESH ?? ""; this.DAGESH_CHAZAQ = schema.DAGESH_CHAZAQ ?? true; this.MAQAF = schema.MAQAF ?? "-"; @@ -2221,7 +2241,21 @@ export class SBL extends Schema { this.DIVINE_NAME = schema.DIVINE_NAME ?? "yhwh"; this.DIVINE_NAME_ELOHIM = schema.DIVINE_NAME_ELOHIM ?? undefined; this.SYLLABLE_SEPARATOR = schema.SYLLABLE_SEPARATOR ?? undefined; - this.ADDITIONAL_FEATURES = schema.ADDITIONAL_FEATURES ?? undefined; + this.ADDITIONAL_FEATURES = schema.ADDITIONAL_FEATURES ?? [ + { + FEATURE: "syllable", + HEBREW: /[\u{05B4}\u{05BB}]/u, + TRANSLITERATION: (syllable, heb, schema) => { + const hasMater = syllable.clusters.some((cluster) => cluster.isMater); + if (syllable.isAccented && !hasMater) { + const macron = "\u0304"; + const output = syllable.hasVowelName("HIRIQ") ? schema["HIRIQ"] + macron : schema["QUBUTS"] + macron; + return syllable.text.replace(heb, output.normalize("NFC")); + } + return syllable.text; + } + } + ]; this.STRESS_MARKER = schema.STRESS_MARKER ?? undefined; this.longVowels = schema.longVowels ?? true; this.qametsQatan = schema.qametsQatan ?? true; diff --git a/test/transliterate.test.ts b/test/transliterate.test.ts index 9054926..d2adade 100644 --- a/test/transliterate.test.ts +++ b/test/transliterate.test.ts @@ -82,13 +82,13 @@ describe("using default options", () => { describe("shewa", () => { test.each` description | hebrew | transliteration - ${"vocal shewa"} | ${"סְלִ֣ק"} | ${"səliq"} + ${"vocal shewa"} | ${"שְׁמֹֽר"} | ${"šəmōr"} ${"silent shewa"} | ${"סַלְכָ֣ה"} | ${"salkâ"} ${"final shewa"} | ${"כָּ֣ךְ"} | ${"kāk"} ${"two final shewas"} | ${"קָטַ֣לְתְּ"} | ${"qāṭalt"} ${"omitted dagesh chazaq after article, yod"} | ${"הַיְאֹ֗ר"} | ${"hayəʾōr"} ${"omitted dagesh chazaq after article, mem"} | ${"הַמְיַלֶּ֗דֶת"} | ${"haməyalledet"} - ${"omitted dagesh chazaq after article, lamed"} | ${"הַלְוִיִּ֔ם"} | ${"haləwiyyim"} + ${"omitted dagesh chazaq after article, lamed"} | ${"הַלְוִיִּ֔ם"} | ${"haləwiyyīm"} ${"silent shewa and ligature consonant"} | ${"אַשְׁכְּנַזִּי"} | ${"ʾaškənazzî"} `("$description", (inputs: Inputs) => { const { hebrew, transliteration } = inputs; @@ -97,6 +97,19 @@ describe("using default options", () => { }); }); + describe("vowel features", () => { + test.each` + description | hebrew | transliteration + ${"short hiriq"} | ${"מִנְחָה"} | ${"minḥâ"} + ${"long hiriq"} | ${"דָּוִ֑ד"} | ${"dāwīd"} + ${"short qubuts"} | ${"וַיֻּגַּ֖ד"} | ${"wayyuggad"} + ${"long qubuts"} | ${"וַיֹּצִאֻ֥הוּ"} | ${"wayyōṣiʾūhû"} + `("$description", (inputs: Inputs) => { + const { hebrew, transliteration } = inputs; + expect(transliterate(hebrew)).toBe(transliteration); + }); + }); + describe("mater features", () => { describe("typical", () => { test.each` @@ -125,7 +138,7 @@ describe("using default options", () => { ${"consonantal vav with holem as vowel"} | ${"עָוֺ֖ן"} | ${"ʿāwōn"} ${"consonantal vav with holem vav as vowel"} | ${"עָו֑וֹן"} | ${"ʿāwôn"} ${"consonantal vav with holem, holem vav, and shureq (post biblical)"} | ${"עֲוֹנוֹתֵינוּ"} | ${"ʿăwōnôtênû"} - ${"initial shureq"} | ${"וּמִן"} | ${"ûmin"} + ${"initial shureq"} | ${"וּמִן"} | ${"ûmīn"} ${"bgdkpt letter with mater"} | ${"בִּיטוֹן"} | ${"bîṭôn"} `("$description", (inputs: Inputs) => { const { hebrew, transliteration } = inputs; @@ -411,7 +424,7 @@ describe("extending SBL schema for optional arguments", () => { ${"exclude never"} | ${"בֹּ֖קֶר י֥וֹם אֶחָֽד׃"} | ${"bṓqer yốm ʾeḥā́d"} | ${{ STRESS_MARKER: { location: "after-vowel", mark: "\u0301", exclude: "never" } }} ${"exclude single"} | ${"בֹּ֖קֶר י֥וֹם אֶחָֽד׃"} | ${"bṓqer yôm ʾeḥā́d"} | ${{ STRESS_MARKER: { location: "after-vowel", mark: "\u0301", exclude: "single" } }} ${"exclude final"} | ${"בֹּ֖קֶר י֥וֹם אֶחָֽד׃"} | ${"bṓqer yôm ʾeḥād"} | ${{ STRESS_MARKER: { location: "after-vowel", mark: "\u0301", exclude: "final" } }} - ${"ignore paseq"} | ${"לְפָנַ֨י ׀ שֻׁלְחָ֗ן"} | ${"ləpāˈnay šūlˈḥān"} | ${{ STRESS_MARKER: { location: "before-syllable", mark: "ˈ" } }} + ${"ignore paseq"} | ${"לְפָנַ֨י ׀ שֻׁלְחָ֗ן"} | ${"ləpāˈnay šulˈḥān"} | ${{ STRESS_MARKER: { location: "before-syllable", mark: "ˈ" } }} `("$description", (inputs: Inputs) => { const { hebrew, transliteration, options } = inputs; expect(transliterate(hebrew, options)).toBe(transliteration);