Update files
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getMarkdownListMarker } from "./markdown-list";
|
||||
|
||||
describe("getMarkdownListMarker", () => {
|
||||
it("returns a bullet marker for unordered list items", () => {
|
||||
expect(
|
||||
getMarkdownListMarker(
|
||||
{ index: 0 },
|
||||
[{ type: "bullet_list" }]
|
||||
)
|
||||
).toEqual({
|
||||
isOrdered: false,
|
||||
marker: "•",
|
||||
});
|
||||
});
|
||||
|
||||
it("returns numbered markers for ordered list items", () => {
|
||||
expect(
|
||||
getMarkdownListMarker(
|
||||
{ index: 1, markup: "." },
|
||||
[{ type: "ordered_list" }]
|
||||
)
|
||||
).toEqual({
|
||||
isOrdered: true,
|
||||
marker: "2.",
|
||||
});
|
||||
});
|
||||
|
||||
it("respects ordered list start attribute", () => {
|
||||
expect(
|
||||
getMarkdownListMarker(
|
||||
{ index: 2, markup: ")" },
|
||||
[{ type: "ordered_list", attributes: { start: "5" } }]
|
||||
)
|
||||
).toEqual({
|
||||
isOrdered: true,
|
||||
marker: "7)",
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers the nearest list ancestor in nested lists", () => {
|
||||
expect(
|
||||
getMarkdownListMarker(
|
||||
{ index: 0, markup: "." },
|
||||
[{ type: "ordered_list" }, { type: "bullet_list" }]
|
||||
)
|
||||
).toEqual({
|
||||
isOrdered: true,
|
||||
marker: "1.",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
type MarkdownNode = {
|
||||
type?: string;
|
||||
index?: number;
|
||||
markup?: string;
|
||||
attributes?: {
|
||||
start?: number | string;
|
||||
};
|
||||
children?: MarkdownNode[];
|
||||
};
|
||||
|
||||
const LIST_BULLET = "•";
|
||||
const DEFAULT_ORDERED_LIST_MARKUP = ".";
|
||||
|
||||
function toParentNodes(parent: unknown): MarkdownNode[] {
|
||||
if (Array.isArray(parent)) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
if (parent && typeof parent === "object") {
|
||||
return [parent as MarkdownNode];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
function getNearestListParent(parent: unknown): MarkdownNode | undefined {
|
||||
return toParentNodes(parent).find(
|
||||
(ancestor) =>
|
||||
ancestor?.type === "ordered_list" || ancestor?.type === "bullet_list"
|
||||
);
|
||||
}
|
||||
|
||||
function getOrderedListItemIndex(node: MarkdownNode, listParent: MarkdownNode): number {
|
||||
if (typeof node.index === "number" && Number.isFinite(node.index) && node.index >= 0) {
|
||||
return node.index;
|
||||
}
|
||||
|
||||
if (Array.isArray(listParent.children)) {
|
||||
const fallbackIndex = listParent.children.indexOf(node);
|
||||
if (fallbackIndex >= 0) {
|
||||
return fallbackIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function parseOrderedListStart(node: MarkdownNode): number {
|
||||
const rawStart = node.attributes?.start;
|
||||
if (typeof rawStart === "number" && Number.isFinite(rawStart)) {
|
||||
return rawStart;
|
||||
}
|
||||
|
||||
if (typeof rawStart === "string") {
|
||||
const parsed = Number.parseInt(rawStart, 10);
|
||||
if (Number.isFinite(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
export function getMarkdownListMarker(node: MarkdownNode, parent: unknown): {
|
||||
isOrdered: boolean;
|
||||
marker: string;
|
||||
} {
|
||||
const listParent = getNearestListParent(parent);
|
||||
if (!listParent || listParent.type !== "ordered_list") {
|
||||
return {
|
||||
isOrdered: false,
|
||||
marker: LIST_BULLET,
|
||||
};
|
||||
}
|
||||
|
||||
const orderedIndex = getOrderedListItemIndex(node, listParent);
|
||||
const orderedStart = parseOrderedListStart(listParent);
|
||||
const orderedMarkup =
|
||||
typeof node.markup === "string" && node.markup.length > 0
|
||||
? node.markup
|
||||
: DEFAULT_ORDERED_LIST_MARKUP;
|
||||
|
||||
return {
|
||||
isOrdered: true,
|
||||
marker: `${orderedStart + orderedIndex}${orderedMarkup}`,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user