diff --git a/site/generations/library-next.ts b/site/generations/library-next.ts index b2115e78c..077df4efe 100644 --- a/site/generations/library-next.ts +++ b/site/generations/library-next.ts @@ -1,9 +1,9 @@ import {type SitemapData, SitemapDatum} from "@onlyoffice/eleventy-sitemap" import {type Data} from "@onlyoffice/eleventy-types" import {type Entity} from "@onlyoffice/library-declaration/next.js" -import {cutSuffix} from "@onlyoffice/strings" import {LibraryDatum} from "../internal/library.tsx" import {Sitemap} from "../internal/sitemap.ts" +import {Pather} from "../internal/url.ts" export interface Resource { list(): Entity[] @@ -11,8 +11,8 @@ export interface Resource { } export function data(r: Resource): Data { - const sp = new SpecificPather() - const vp = new VirtualPather() + const sp = new Pather() + const vp = new Pather() const sr: Record = {} @@ -50,8 +50,33 @@ export function data(r: Resource): Data { if (!data.pagination || !data.pagination.items) { throw new Error("No pagination") } + const [e]: Entity[] = data.pagination.items - const p = vp.path(r, e) + + let a: string[] = [] + let b: number[] = [] + + let c: Entity | undefined = e + + while (c) { + if (c.type === "group") { + a.push(c.group.name) + b.push(c.id) + } + + if (c.type === "declaration") { + a.push(c.declaration.name) + b.push(c.id) + } + + c = r.retrieve(c.parentId) + } + + a = a.reverse() + b = b.reverse() + + const p = vp.pathify(a, b) + return `${p}/index.html` }, @@ -59,8 +84,28 @@ export function data(r: Resource): Data { if (!data.pagination || !data.pagination.items) { throw new Error("No pagination") } + const [e]: Entity[] = data.pagination.items - const p = sp.path(r, e) + + let a: string[] = [] + let b: number[] = [] + + let c: Entity | undefined = e + + while (c) { + if (c.type === "declaration") { + a.push(c.declaration.name) + b.push(c.id) + } + + c = r.retrieve(c.parentId) + } + + a = a.reverse() + b = b.reverse() + + const p = sp.pathify(a, b) + return `${p}/index.html` }, @@ -151,96 +196,3 @@ export function data(r: Resource): Data { }, } } - -class VirtualPather implements Pather { - #m = new Map() - - path(r: Resource, e: Entity): string { - let s = "" - let i = 0 - - let c: Entity | undefined = e - - while (c) { - if (c.type === "group") { - const n = sanitizeName(c.group.name) - s = `${n}/${s}` - } - - if (c.type === "declaration") { - const n = sanitizeName(c.declaration.name) - s = `${n}/${s}` - } - - c = r.retrieve(c.parentId) - } - - [s] = cutSuffix(s, "/") - - while (true) { - const id = this.#m.get(s) - - if (!id) { - this.#m.set(s, e.id) - break - } - - if (id === e.id) { - break - } - - i += 1 - s = `${s}-${i}` - } - - return s - } -} - -class SpecificPather implements Pather { - #m = new Map() - - path(r: Resource, e: Entity): string { - let s = "" - let i = 0 - - let c: Entity | undefined = e - - while (c) { - if (c.type === "declaration") { - const n = sanitizeName(c.declaration.name) - s = `${n}/${s}` - } - - c = r.retrieve(c.parentId) - } - - [s] = cutSuffix(s, "/") - - while (true) { - const id = this.#m.get(s) - - if (!id) { - this.#m.set(s, e.id) - break - } - - if (id === e.id) { - break - } - - i += 1 - s = `${s}-${i}` - } - - return s - } -} - -interface Pather { - path(r: Resource, e: Entity): string -} - -function sanitizeName(t: string): string { - return t.replaceAll("/", " ") -} diff --git a/site/internal/url.ts b/site/internal/url.ts index 86c189755..c304e878b 100644 --- a/site/internal/url.ts +++ b/site/internal/url.ts @@ -172,3 +172,76 @@ function defaultPath(p: Page): string { const c = posix.dirname(p.filePathStem) return posix.join(c, b) } + +export class Pather { + /** + * The index where the key is a path and the value is a unique identifier. + */ + #x = new Map() + + /** + * The index where the key is a path, and the value is a number of duplicates. + */ + #y = new Map() + + /** + * @param a A list of path segments. + * @param b A corresponding list of unique identifiers. + * @returns A unique path. + */ + pathify(a: string[], b: number[]): string { + const a0: string[] = [] + + for (let s of a) { + s = s.replaceAll("/", " ") + a0.push(s) + } + + const b0 = [...b] + + for (let i = 0; i < a0.length; i += 1) { + const c = a0.slice(0, i + 1) + const s = c[c.length - 1] + + let x0 = b0[i] + let y0 = 0 + + let k = "" + + while (true) { + if (y0 !== 0) { + c[c.length - 1] = `${s}-${y0}` + } + + k = c.join("/") + + const x1 = this.#x.get(k) + if (x1 === undefined) { + break + } + + const y1 = this.#y.get(k) + if (y1 === undefined) { + break + } + + if (x1 === x0) { + break + } + + x0 = x1 + y0 = y1 + 1 + } + + this.#x.set(k, x0) + this.#y.set(k, y0) + + a0[i] = c[c.length - 1] + b0[i] = x0 + } + + const p = a0.join("/") + + return p + } +}