mirror of
https://github.com/quartz-community/utils.git
synced 2026-07-22 02:50:27 +00:00
fix: support multi-segment partial paths in shortest link resolution
The 'shortest' strategy in transformLink() only matched by filename (last segment), causing wiki-links like [[Species/Elf/Wood]] to fail resolution when the target lived under a deeper path (e.g. Compendium/Species/Elf/Wood). Add suffix-based matching for multi-segment partial paths and handle the /index variant for folder targets whose suffix gets stripped by transformInternalLink().
This commit is contained in:
parent
1bc315eb6e
commit
1ad4e4f2f6
2 changed files with 104 additions and 0 deletions
14
src/path.ts
14
src/path.ts
|
|
@ -237,7 +237,21 @@ export function transformLink(src: FullSlug, target: string, opts: TransformOpti
|
|||
const [targetCanonical, targetAnchor] = splitAnchor(canonicalSlug);
|
||||
|
||||
if (opts.strategy === "shortest") {
|
||||
const isMultiSegment = targetCanonical.includes("/");
|
||||
const isFolderTarget = isFolderPath(targetSlug);
|
||||
const matchingFileNames = opts.allSlugs.filter((slug) => {
|
||||
if (isMultiSegment) {
|
||||
// Multi-segment partial path: match by suffix
|
||||
if (slug === targetCanonical || slug.endsWith("/" + targetCanonical)) {
|
||||
return true;
|
||||
}
|
||||
// Folder targets have "/index" stripped — also match the "/index" variant
|
||||
if (isFolderTarget) {
|
||||
const withIndex = targetCanonical + "/index";
|
||||
return slug === withIndex || slug.endsWith("/" + withIndex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const parts = slug.split("/");
|
||||
const fileName = parts.at(-1);
|
||||
return targetCanonical === fileName;
|
||||
|
|
|
|||
|
|
@ -247,6 +247,96 @@ describe("transformLink", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("multi-segment partial path (shortest)", () => {
|
||||
const multiSegSlugs = [
|
||||
"Compendium/Species/Elf/Wood",
|
||||
"Compendium/Species/Elf/index",
|
||||
"Compendium/Species/Elf/Eladrin",
|
||||
"Compendium/Spells/index",
|
||||
"Compendium/Spells/Bane",
|
||||
"Campaigns/Unnamed/People/index",
|
||||
"index",
|
||||
] as FullSlug[];
|
||||
|
||||
const opts: TransformOptions = { strategy: "shortest", allSlugs: multiSegSlugs };
|
||||
|
||||
it("resolves multi-segment path Species/Elf/Wood to unique match", () => {
|
||||
const cur = "Campaigns/Unnamed/People/index" as FullSlug;
|
||||
expect(transformLink(cur, "Species/Elf/Wood", opts)).toBe(
|
||||
"../../../Compendium/Species/Elf/Wood",
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves multi-segment path Species/Elf/index to folder page", () => {
|
||||
const cur = "Campaigns/Unnamed/People/index" as FullSlug;
|
||||
expect(transformLink(cur, "Species/Elf/index", opts)).toBe(
|
||||
"../../../Compendium/Species/Elf/",
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to absolute when multi-segment path has no match", () => {
|
||||
const cur = "index" as FullSlug;
|
||||
expect(transformLink(cur, "Species/Elf/NoSuchPage", opts)).toBe("./Species/Elf/NoSuchPage");
|
||||
});
|
||||
|
||||
it("resolves single-segment name that is unique", () => {
|
||||
const cur = "index" as FullSlug;
|
||||
expect(transformLink(cur, "Bane", opts)).toBe("./Compendium/Spells/Bane");
|
||||
});
|
||||
|
||||
it("resolves exact full path as multi-segment match", () => {
|
||||
const cur = "index" as FullSlug;
|
||||
expect(transformLink(cur, "Compendium/Species/Elf/Eladrin", opts)).toBe(
|
||||
"./Compendium/Species/Elf/Eladrin",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("multi-segment partial path (shortest)", () => {
|
||||
const multiSegSlugs = [
|
||||
"Compendium/Species/Elf/Wood",
|
||||
"Compendium/Species/Elf/index",
|
||||
"Compendium/Species/Elf/Eladrin",
|
||||
"Compendium/Spells/index",
|
||||
"Compendium/Spells/Bane",
|
||||
"Campaigns/Unnamed/People/index",
|
||||
"index",
|
||||
] as FullSlug[];
|
||||
|
||||
const opts: TransformOptions = { strategy: "shortest", allSlugs: multiSegSlugs };
|
||||
|
||||
it("resolves multi-segment path Species/Elf/Wood to unique match", () => {
|
||||
const cur = "Campaigns/Unnamed/People/index" as FullSlug;
|
||||
expect(transformLink(cur, "Species/Elf/Wood", opts)).toBe(
|
||||
"../../../Compendium/Species/Elf/Wood",
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves multi-segment path Species/Elf/index to folder page", () => {
|
||||
const cur = "Campaigns/Unnamed/People/index" as FullSlug;
|
||||
expect(transformLink(cur, "Species/Elf/index", opts)).toBe(
|
||||
"../../../Compendium/Species/Elf/",
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to absolute when multi-segment path has no match", () => {
|
||||
const cur = "index" as FullSlug;
|
||||
expect(transformLink(cur, "Species/Elf/NoSuchPage", opts)).toBe("./Species/Elf/NoSuchPage");
|
||||
});
|
||||
|
||||
it("resolves single-segment name that is unique", () => {
|
||||
const cur = "index" as FullSlug;
|
||||
expect(transformLink(cur, "Bane", opts)).toBe("./Compendium/Spells/Bane");
|
||||
});
|
||||
|
||||
it("resolves exact full path as multi-segment match", () => {
|
||||
const cur = "index" as FullSlug;
|
||||
expect(transformLink(cur, "Compendium/Species/Elf/Eladrin", opts)).toBe(
|
||||
"./Compendium/Species/Elf/Eladrin",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("folder page bug regression", () => {
|
||||
const spellSlugs = [
|
||||
"Compendium/Spells/index",
|
||||
|
|
|
|||
Loading…
Reference in a new issue