Changes
15 changed files (+223/-474)
-
-
@@ -4,7 +4,7 @@ // SPDX-License-Identifier: Apache-2.0import { DenoFsReader } from "../filesystem_reader/deno_fs.ts"; import { DenoFsWriter } from "../filesystem_writer/deno_fs.ts"; import { MultiLocaleTreeBuilder } from "../tree_builder/multi_locale_tree_builder.ts"; import { DefaultTreeBuilder } from "../tree_builder/default_tree_builder.ts"; import { VaultParser } from "../metadata_parser/vault_parser.ts"; import { DefaultThemeBuilder } from "../page_builder/default_theme/builder.tsx";
-
@@ -26,14 +26,19 @@ await Deno.mkdir(outDir, { recursive: true });const fileSystemReader = new DenoFsReader(srcDir); const fileSystemWriter = new DenoFsWriter(outDir); const treeBuilder = new MultiLocaleTreeBuilder({ defaultLocale: "en", const treeBuilder = new DefaultTreeBuilder({ defaultLanguage: "en", ignore(node) { return node.name.startsWith(".") || (node.path.length === 1 && node.name.endsWith(".ts")); }, }); const metadataParser = new VaultParser(); const metadataParser = new VaultParser({ language(node) { return node.parent.type === "root" && node.type === "directory" && /^[a-z]+(-[a-z]+)*$/.test(node.name); }, }); const pageBuilder = new DefaultThemeBuilder("© 2024 Shota FUJI"); const documentTree = await treeBuilder.build({
-
-
-
@@ -6,7 +6,7 @@## Document metadata Properties describing a *document* or a *document directory* required for generating a website. *Document metadata* consists of a *document name* and *document title*. *Document metadata* consists of a *document name*, *document title*, and language of the document or the document directory. ## Document name
-
@@ -33,4 +33,4 @@ A document directory consists of *document metadata* of its own and zero or more *document metadata* and/or *document directory*.## Document tree Tree structured data contains *document metadata* and *document directories* per locales. Tree structured data contains *document metadata* and *document directories*.
-
-
-
@@ -29,6 +29,13 @@ * control characters.* ([Unicode control characters - Wikipedia](https://en.wikipedia.org/wiki/Unicode_control_characters)) */ readonly title: string; /** * Language for a document directory or a document. * If this is empty, Macana looks up the most closest document directory language set. * If none of the ancestors have a language, Macana will use a user given default language. */ readonly language?: string; } /**
-
-
-
@@ -4,6 +4,7 @@ // SPDX-License-Identifier: Apache-2.0import { assertObjectMatch } from "../deps/deno.land/std/assert/mod.ts"; import type { DirectoryReader } from "../filesystem_reader/interface.ts"; import { MemoryFsReader } from "../filesystem_reader/memory_fs.ts"; import { VaultParser } from "./vault_parser.ts";
-
@@ -150,6 +151,29 @@ },); }); Deno.test("Should use language defined in YAML frontmatter", async () => { const fs = new MemoryFsReader([ { path: "Foo Bar.md", content: `--- lang: en ---`, }, ]); const root = await fs.getRootDirectory(); const [file] = await root.read(); assertObjectMatch( await new VaultParser({ readFrontMatter: true }).parse(file), { name: "foo%20bar", title: "Foo Bar", language: "en", }, ); }); Deno.test("Should use both name and title defined in YAML frontmatter", async () => { const fs = new MemoryFsReader([ {
-
@@ -195,3 +219,44 @@ title: "Foo Bar",}, ); }); Deno.test("Should use the language returned by user provided function", async () => { const fs = new MemoryFsReader([ { path: "en/Foo Bar.md", content: "", }, { path: "ja/Foo Bar.md", content: "", }, ]); const root = await fs.getRootDirectory(); const [en, ja] = await root.read(); const parser = new VaultParser({ language(node) { return node.type === "directory" && node.path.length === 1 && /^(en|ja)$/.test(node.name); }, }); assertObjectMatch( await parser.parse(en as DirectoryReader), { name: "en", title: "en", language: "en", }, ); assertObjectMatch( await parser.parse(ja), { name: "ja", title: "ja", language: "ja", }, ); });
-
-
-
@@ -15,6 +15,22 @@ function escapeNodeName(nodeName: string): string {return encodeURIComponent(nodeName.toLowerCase()); } function getFrontMatterValue( frontmatter: Record<string, unknown>, key: string, ): string | undefined { if (!(key in frontmatter)) { return undefined; } const value = frontmatter[key]; if (typeof value !== "string") { return undefined; } return value; } export interface VaultParserOptions { /** * Whether to read YAML frontmatter of notes.
-
@@ -22,10 +38,23 @@ * When enabled,* * - Use `name` property for document name if defined. * - Use `title` property for document title if defined. * - Use `lang` property or `language` property as a document language if defined. * * This flag is off by-default for performance reasons. */ readFrontMatter?: boolean; /** * A optional function to determine the language of a directory or a file. * Macana does not check whether the resulted language tag is valid. * @returns If this function returned a falsy value, Macana treat the directory or the file * has no specified language. If this function returned `true`, Macana uses the file * name or directory name as a language. If this function returned string, Macana * uses the string as a language. */ language?( node: FileReader | DirectoryReader, ): string | true | null | undefined | false; } /**
-
@@ -36,9 +65,11 @@ * and lowercased escaped one as document name.*/ export class VaultParser implements MetadataParser { #readFrontMatter: boolean; #language: VaultParserOptions["language"]; constructor({ readFrontMatter = false }: VaultParserOptions = {}) { constructor({ readFrontMatter = false, language }: VaultParserOptions = {}) { this.#readFrontMatter = readFrontMatter; this.#language = language; } async parse(
-
@@ -48,6 +79,7 @@ if (node.type === "directory") {return { name: escapeNodeName(node.name), title: node.name, language: this.#getLanguage(node), }; }
-
@@ -59,6 +91,7 @@ case ".md": {const fromFileName: DocumentMetadata = { name: escapeNodeName(basename), title: basename, language: this.#getLanguage(node), }; if (this.#readFrontMatter) {
-
@@ -67,6 +100,7 @@return { name: parsed.name || fromFileName.name, title: parsed.title || fromFileName.title, language: parsed.language || fromFileName.language, }; }
-
@@ -76,6 +110,7 @@ case ".canvas": {return { name: escapeNodeName(basename), title: basename, language: this.#getLanguage(node), }; } // Not an Obsidian document.
-
@@ -87,6 +122,23 @@ }} } #getLanguage(node: FileReader | DirectoryReader): string | undefined { if (!this.#language) { return undefined; } const result = this.#language(node); if (!result) { return undefined; } if (typeof result === "string") { return result; } return node.name; } async #parseFrontMatter( file: FileReader, ): Promise<Partial<DocumentMetadata>> {
-
@@ -95,14 +147,11 @@// Obsidian currently supports YAML frontmatter only. const frontmatter = yamlFrontmatter.extract(markdown); const name = ("name" in frontmatter.attrs && typeof frontmatter.attrs.name === "string" && frontmatter.attrs.name) || undefined; const title = ("title" in frontmatter.attrs && typeof frontmatter.attrs.title === "string" && frontmatter.attrs.title) || undefined; const name = getFrontMatterValue(frontmatter.attrs, "name"); const title = getFrontMatterValue(frontmatter.attrs, "title"); const language = getFrontMatterValue(frontmatter.attrs, "lang") || getFrontMatterValue(frontmatter.attrs, "language"); return { name, title }; return { name, title, language }; } }
-
-
-
@@ -21,11 +21,11 @@import { PathResolverProvider } from "./contexts/path_resolver.tsx"; interface InnerBuildParameters { items: ReadonlyArray<DocumentDirectory | Document>; item: DocumentDirectory | Document; tree: DocumentTree; locale: string; parentLanguage: string; pathPrefix?: readonly string[];
-
@@ -51,69 +51,71 @@ ["assets", "global.css"],new TextEncoder().encode(styles), ); for (const [locale, items] of documentTree.locales) { await this.#buildLocale({ items, await Promise.all(documentTree.nodes.map((item) => this.#build({ item, tree: documentTree, locale, pathPrefix: locale === documentTree.defaultLocale ? [] : [locale], parentLanguage: documentTree.defaultLanguage, pathPrefix: [], buildParameters: { fileSystemWriter, fileSystemReader }, }); } }) )); } async #buildLocale( { items, tree, locale, pathPrefix = [], buildParameters }: async #build( { item, tree, parentLanguage, pathPrefix = [], buildParameters }: InnerBuildParameters, ): Promise<void> { const { fileSystemWriter } = buildParameters; for (const item of items) { if ("file" in item) { const content = await item.file.read(); if ("file" in item) { const content = await item.file.read(); if (item.file.name.endsWith(".md")) { const html = "<!DOCTYPE html>" + renderSSR( () => ( // Adds 1 to depth due to `<name>/index.html` conversion. <PathResolverProvider depth={pathPrefix.length + 1}> <Html.View tree={tree} content={fromMarkdown(content)} document={item} locale={locale} copyright={this.#copyright} /> </PathResolverProvider> ), ); const enc = new TextEncoder(); if (item.file.name.endsWith(".md")) { const html = "<!DOCTYPE html>" + renderSSR( () => ( // Adds 1 to depth due to `<name>/index.html` conversion. <PathResolverProvider depth={pathPrefix.length + 1}> <Html.View tree={tree} content={fromMarkdown(content)} document={item} language={item.metadata.language || parentLanguage} copyright={this.#copyright} /> </PathResolverProvider> ), ); await fileSystemWriter.write([ ...pathPrefix, item.file.name.replace(/\.md$/, ""), "index.html", ], enc.encode(html)); } const enc = new TextEncoder(); if (item.file.name.endsWith(".canvas")) { // TODO: Proper logging console.warn( "Default theme page builder does not support Canvas yet.", ); } await fileSystemWriter.write([ ...pathPrefix, item.file.name.replace(/\.md$/, ""), "index.html", ], enc.encode(html)); return; } continue; if (item.file.name.endsWith(".canvas")) { // TODO: Proper logging console.warn( "Default theme page builder does not support Canvas yet.", ); return; } await this.#buildLocale({ items: item.entries, return; } await Promise.all(item.entries.map((entry) => this.#build({ item: entry, tree, locale, parentLanguage: item.metadata.language || parentLanguage, pathPrefix: [...pathPrefix, item.directory.name], buildParameters, }); } }) )); } }
-
-
-
@@ -61,24 +61,16 @@ * The document's content HTML.*/ content: Mdast.Root; locale: string; language: string; copyright: string; } export function View( { document, locale, content, tree, copyright }: ViewProps, { document, language, content, tree, copyright }: ViewProps, ) { const path = usePathResolver(); const topLevelDocs = tree.locales.get(locale); if (!topLevelDocs) { const locales = Array.from(tree.locales.keys()); throw new Error( `Unknown locale: "${locale}" (available locales = ${locales.join(", ")})`, ); } const hast = toHast(content); const toc = tocMut(hast).map((item) =>
-
@@ -88,7 +80,7 @@const contentNodes = toNode(hast); return ( <html lang={locale}> <html lang={language}> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
-
@@ -104,7 +96,6 @@ aside={toc.length > 0 && <Toc.View toc={toc} />}nav={ <DocumentTreeUI.View tree={tree} currentLocale={locale} currentPath={document.file.path} /> }
-
-
-
@@ -39,43 +39,13 @@export interface ViewProps { tree: DocumentTree; currentLocale: string; currentPath: readonly string[]; } export function View({ tree, currentLocale, currentPath }: ViewProps) { if (tree.locales.size > 1) { return ( <ul className={C.Root}> {Array.from(tree.locales.entries()).map(([locale, items]) => ( <li> <details className={C.Directory} open={locale === currentLocale ? "" : undefined} > <summary>{locale}</summary> <ul className={C.List} lang={locale}> {items.map((item) => ( <Node value={item} currentPath={currentPath} /> ))} </ul> </details> </li> ))} </ul> ); } const singleLocale = tree.locales.get(tree.defaultLocale); if (!singleLocale) { throw new Error(`Locale not found: ${tree.defaultLocale}`); } export function View({ tree, currentPath }: ViewProps) { return ( <ul className={C.Root}> {singleLocale.map((entry) => ( <ul className={C.Root} lang={tree.defaultLanguage}> {tree.nodes.map((entry) => ( <Node value={entry} currentPath={currentPath}
-
@@ -102,7 +72,7 @@ "",]); return ( <li> <li lang={value.metadata.language ?? undefined}> <a href={path}>{value.metadata.title}</a> </li> );
-
@@ -111,7 +81,7 @@const defaultOpened = currentPath[0] === value.directory.name; return ( <li> <li lang={value.metadata.language ?? undefined}> <details className={C.Directory} open={defaultOpened ? "" : undefined}> <summary>{value.metadata.title}</summary>
-
-
tree_builder/assert.test.ts (deleted)
-
@@ -1,19 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 import { assertThrows } from "../deps/deno.land/std/assert/mod.ts"; import { assertDocumentTreeIsValid } from "./assert.ts"; Deno.test("Should throws if locale uses invalid lang subtag format", () => { assertThrows(() => { assertDocumentTreeIsValid({ defaultLocale: "en", locales: new Map([ ["en", []], ["ja_JP", []], ]), }); }); });
-
-
tree_builder/assert.ts (deleted)
-
@@ -1,50 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 import type { Document, DocumentDirectory, DocumentTree } from "./interface.ts"; function fmtDocumentSourceName(node: Document | DocumentDirectory): string { if ("file" in node) { return node.file.path.join("/"); } return node.directory.path.join("/") + "/"; } const LANG_SUBTAG_PATTERN = /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$/; export function assertDocumentTreeIsValid(tree: DocumentTree): void { for (const [locale, entries] of tree.locales) { // Simple BCP 47 language tag check, based on RFC 4646 (Tags for Identifying Languages) // https://www.rfc-editor.org/rfc/rfc4646.txt if (!(LANG_SUBTAG_PATTERN.test(locale))) { throw new Error(`Invalid BCP 47 language tag, found "${locale}".`); } assertEntryNameIsUnique(entries); } } function assertEntryNameIsUnique( entries: ReadonlyArray<DocumentDirectory | Document>, ): void { entries.forEach((entry, i) => { const firstIndex = entries.findIndex((e) => e.metadata.name === entry.metadata.name ); if (firstIndex !== i) { throw new Error( "You can't have more than one document or directory that have same name: found " + `"${fmtDocumentSourceName(entries[firstIndex])}" and "${ fmtDocumentSourceName(entry) }", ` + "which resulted in same document name.", ); } if ("entries" in entry) { assertEntryNameIsUnique(entry.entries); } }); }
-
-
-
@@ -24,12 +24,9 @@ readonly entries: ReadonlyArray<Document | DocumentDirectory>;} export interface DocumentTree { readonly locales: ReadonlyMap< string, ReadonlyArray<Document | DocumentDirectory> >; readonly nodes: ReadonlyArray<Document | DocumentDirectory>; readonly defaultLocale: string; readonly defaultLanguage: string; } export interface BuildParameters {
-
-
-
@@ -1,122 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 import { assertEquals, assertObjectMatch, assertRejects, } from "../deps/deno.land/std/assert/mod.ts"; import { MemoryFsReader } from "../filesystem_reader/memory_fs.ts"; import { VaultParser } from "../metadata_parser/vault_parser.ts"; import { MultiLocaleTreeBuilder } from "./multi_locale_tree_builder.ts"; Deno.test("Should read top-level directories as locales", async () => { const fileSystemReader = new MemoryFsReader([ { path: "en/Foo Bar/Baz.md", content: "" }, { path: "es/Foo Bar/Baz.md", content: "" }, ]); const metadataParser = new VaultParser(); const builder = new MultiLocaleTreeBuilder(); const tree = await builder.build({ fileSystemReader, metadataParser }); assertEquals(tree.defaultLocale, "en"); assertObjectMatch(Object.fromEntries(Array.from(tree.locales.entries())), { en: [ { metadata: { name: "foo%20bar", title: "Foo Bar" }, directory: { name: "Foo Bar" }, entries: [ { metadata: { name: "baz", title: "Baz" }, file: { name: "Baz.md" }, }, ], }, ], es: [ { metadata: { name: "foo%20bar", title: "Foo Bar" }, directory: { name: "Foo Bar" }, entries: [ { metadata: { name: "baz", title: "Baz" }, file: { name: "Baz.md" }, }, ], }, ], }); }); Deno.test("Should abort if top-level file exists", async () => { const fileSystemReader = new MemoryFsReader([ { path: "en/Foo Bar/Baz.md", content: "" }, { path: "es/Foo Bar/Baz.md", content: "" }, { path: "ja.md", content: "" }, ]); const metadataParser = new VaultParser(); const builder = new MultiLocaleTreeBuilder(); await assertRejects(() => builder.build({ fileSystemReader, metadataParser }) ); }); Deno.test("Should abort if locale directory's name was not valid language tag", async () => { const fileSystemReader = new MemoryFsReader([ { path: "en_US/Foo Bar/Baz.md", content: "" }, { path: "es/Foo Bar/Baz.md", content: "" }, ]); const metadataParser = new VaultParser(); const builder = new MultiLocaleTreeBuilder(); await assertRejects(() => builder.build({ fileSystemReader, metadataParser }) ); }); Deno.test("Should abort if defaultLocale uses non-existent locale", async () => { const fileSystemReader = new MemoryFsReader([ { path: "en/Foo Bar/Baz.md", content: "" }, { path: "es/Foo Bar/Baz.md", content: "" }, ]); const metadataParser = new VaultParser(); const builder = new MultiLocaleTreeBuilder({ defaultLocale: "ja" }); await assertRejects(() => builder.build({ fileSystemReader, metadataParser }) ); }); Deno.test("Should abort no locale directory found", async () => { const fileSystemReader = new MemoryFsReader([ { path: "en/Foo Bar/Baz.md", content: "" }, { path: "es/Foo Bar/Baz.md", content: "" }, ]); const metadataParser = new VaultParser(); const builder = new MultiLocaleTreeBuilder({ ignore() { return true; }, }); await assertRejects(() => builder.build({ fileSystemReader, metadataParser }) ); }); Deno.test("Should abort if name conflicts", async () => { const fileSystemReader = new MemoryFsReader([ { path: "en/Foo Bar/Baz.md", content: "" }, { path: "en/Foo Bar.md", content: "" }, ]); const metadataParser = new VaultParser(); const builder = new MultiLocaleTreeBuilder(); await assertRejects(() => builder.build({ fileSystemReader, metadataParser }) ); });
-
-
-
@@ -1,132 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 import type { DirectoryReader, FileReader, } from "../filesystem_reader/interface.ts"; import type { MetadataParser } from "../metadata_parser/interface.ts"; import type { BuildParameters, Document, DocumentDirectory, DocumentTree, TreeBuilder, } from "./interface.ts"; import { assertDocumentTreeIsValid } from "./assert.ts"; export interface MultiLocaleTreeBuilderConfig { defaultLocale?: string; /** * Callback function to be invoked on every file and directory. * If this function returned true, the file or directory is skipped and does not * appear on the resulted document tree. */ ignore?(fileOrDirectory: FileReader | DirectoryReader): boolean; } export class MultiLocaleTreeBuilder implements TreeBuilder { #config: MultiLocaleTreeBuilderConfig; constructor(config: MultiLocaleTreeBuilderConfig = {}) { this.#config = config; } async build( { fileSystemReader, metadataParser }: BuildParameters, ): Promise<DocumentTree> { const root = await fileSystemReader.getRootDirectory(); const nodes = await root.read(); const map = new Map<string, Array<Document | DocumentDirectory>>(); for (const node of nodes) { if (this.#config.ignore && this.#config.ignore(node)) { // TODO: Debug log continue; } if (node.type === "file") { // TODO: Warning instead? throw new Error( `You can't have a regular file at top-level directory, found "${node.name}".`, ); } const locale = node.name; const children = await node.read(); const entries = await Promise.all( children.map((child) => this.#build(child, metadataParser)), ); map.set( locale, entries.filter((entry): entry is NonNullable<typeof entry> => !!entry), ); } const firstLocale = map.keys().next().value; if (typeof firstLocale !== "string") { throw new Error("No locale directories found."); } if (this.#config.defaultLocale && !map.has(this.#config.defaultLocale)) { throw new Error( `Received defaultLocale=${this.#config.defaultLocale}, however we couldn't find that locale (found ${ Array.from(map.keys()).join(", ") }).`, ); } const tree: DocumentTree = { defaultLocale: this.#config.defaultLocale || firstLocale, locales: map, }; assertDocumentTreeIsValid(tree); return tree; } async #build( node: FileReader | DirectoryReader, metadataParser: MetadataParser, ): Promise<DocumentDirectory | Document | null> { if (this.#config.ignore && this.#config.ignore(node)) { // TODO: Debug log return null; } const metadata = await metadataParser.parse(node); // This SHOULD have check for `metadata.skip` being `true`. However, a bug // (or "feature") in TypeScript breaks type-narrowing by doing so. if ("skip" in metadata) { // TODO: Debug log return null; } if (node.type === "file") { return { metadata, file: node, }; } const children = await node.read(); const entries = (await Promise.all( children.map((child) => this.#build(child, metadataParser)), )).filter((child): child is NonNullable<typeof child> => !!child); return { metadata, directory: node, entries, }; } }
-
-
-
@@ -9,7 +9,7 @@ } from "../deps/deno.land/std/assert/mod.ts";import { MemoryFsReader } from "../filesystem_reader/memory_fs.ts"; import { VaultParser } from "../metadata_parser/vault_parser.ts"; import { SingleLocaleTreeBuilder } from "./single_locale_tree_builder.ts"; import { DefaultTreeBuilder } from "./default_tree_builder.ts"; Deno.test("Should read from top-level directory, as-is", async () => { const fileSystemReader = new MemoryFsReader([
-
@@ -17,13 +17,11 @@ { path: "Foo Bar/Baz Qux.md", content: "" },{ path: "Foo.md", content: "" }, ]); const metadataParser = new VaultParser(); const builder = new SingleLocaleTreeBuilder({ locale: "en" }); const builder = new DefaultTreeBuilder({ defaultLanguage: "en" }); const tree = await builder.build({ fileSystemReader, metadataParser }); const items = tree.locales.get("en")!; assertObjectMatch(items[0], { assertObjectMatch(tree.nodes[0], { metadata: { name: "foo%20bar", title: "Foo Bar",
-
@@ -44,7 +42,7 @@ },], }); assertObjectMatch(items[1], { assertObjectMatch(tree.nodes[1], { metadata: { name: "foo", title: "Foo",
-
@@ -64,8 +62,8 @@ { path: "bar/foo.md", content: "" },{ path: "bar/foo/baz.md", content: "" }, ]); const metadataParser = new VaultParser(); const builder = new SingleLocaleTreeBuilder({ locale: "en", const builder = new DefaultTreeBuilder({ defaultLanguage: "en", ignore(node) { return node.name === "foo"; },
-
@@ -73,11 +71,9 @@ });const tree = await builder.build({ fileSystemReader, metadataParser }); const items = tree.locales.get("en")!; assertEquals(tree.nodes.length, 2); assertEquals(items.length, 2); assertObjectMatch(items[0], { assertObjectMatch(tree.nodes[0], { metadata: { name: "foo", title: "foo",
-
@@ -85,7 +81,7 @@ },file: { name: "foo.md" }, }); assertObjectMatch(items[1], { assertObjectMatch(tree.nodes[1], { metadata: { name: "bar", title: "bar",
-
-
-
@@ -15,13 +15,11 @@ DocumentTree,TreeBuilder, } from "./interface.ts"; import { assertDocumentTreeIsValid } from "./assert.ts"; export interface SingleLocaleTreeBuilderConfig { export interface DefaultTreeBuilderConfig { /** * Locale string to use. * Default language tag (BCP 47). */ locale: string; defaultLanguage: string; /** * Callback function to be invoked on every file and directory.
-
@@ -31,12 +29,12 @@ */ignore?(fileOrDirectory: FileReader | DirectoryReader): boolean; } export class SingleLocaleTreeBuilder implements TreeBuilder { #locale: string; export class DefaultTreeBuilder implements TreeBuilder { #defaultLanguage: string; #ignore?: (fileOrDirectory: FileReader | DirectoryReader) => boolean; constructor({ locale, ignore }: SingleLocaleTreeBuilderConfig) { this.#locale = locale; constructor({ defaultLanguage, ignore }: DefaultTreeBuilderConfig) { this.#defaultLanguage = defaultLanguage; this.#ignore = ignore; }
-
@@ -51,20 +49,12 @@ const entries = await Promise.all(children.map((child) => this.#build(child, metadataParser)), ); const map = new Map<string, Array<Document | DocumentDirectory>>(); map.set( this.#locale, entries.filter((entry): entry is NonNullable<typeof entry> => !!entry), ); const tree: DocumentTree = { locales: map, defaultLocale: this.#locale, return { nodes: entries.filter((entry): entry is NonNullable<typeof entry> => !!entry ), defaultLanguage: this.#defaultLanguage, }; assertDocumentTreeIsValid(tree); return tree; } async #build(
-