diff --git a/datam8-model b/datam8-model index 591b796..002aa65 160000 --- a/datam8-model +++ b/datam8-model @@ -1 +1 @@ -Subproject commit 591b796a962117ef2a1f9d807074bf3a4f5cc159 +Subproject commit 002aa65f6f127f91d06c8dc0fc4ea50ce75616fc diff --git a/pyproject.toml b/pyproject.toml index 2608a9d..50515bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,8 +47,8 @@ Issues = "https://github.com/oraylis/datam8/issues" [dependency-groups] dev = [ - "pytest~=8.3.0", # TODO: upgrade to 9.x.x - "pytest-cases~=3.8.0", + "pytest~=9.0.3", + "pytest-cases~=3.10.1", "coverage~=7.13.5", "hatchling~=1.27.0", "httpx~=0.25.0", @@ -61,6 +61,8 @@ build-backend = "hatchling.build" [tool.uv] exclude-newer = "1 weeks" +# Allow critical CA trust-store fixes without waiting for the global cooldown (GHSA-6q48-6c7w-9gw2). +exclude-newer-package = { certifi = false } ############# # tool.ruff # diff --git a/scripts/check-docs.mjs b/scripts/check-docs.mjs index d7af701..d1e0a69 100644 --- a/scripts/check-docs.mjs +++ b/scripts/check-docs.mjs @@ -23,7 +23,14 @@ export function headingAnchors(source) { for (const line of proseLines(source)) { const heading = line.match(/^#{1,6}\s+(.+?)\s*#*$/)?.[1]; if (!heading) continue; - const slug = heading.toLowerCase().replace(/<[^>]*>/g, "") + let text = heading.toLowerCase(); + let previous; + // Remove innermost tags until stripping cannot expose another tag. + do { + previous = text; + text = text.replace(/<[^<>]*>/g, ""); + } while (text !== previous); + const slug = text .replace(/[^\p{L}\p{N}\p{M}_\-\s]/gu, "").replace(/\s/g, "-"); const count = counts.get(slug) || 0; counts.set(slug, count + 1); diff --git a/scripts/check-docs.test.mjs b/scripts/check-docs.test.mjs index 03136e7..7200d39 100644 --- a/scripts/check-docs.test.mjs +++ b/scripts/check-docs.test.mjs @@ -25,6 +25,19 @@ test("reports empty alt text and heading spacing", () => { assert.equal(errors.length, 2); }); +test("heading anchors remove inline and reintroduced HTML tags", () => { + const source = "# Guide\n\n## Überblick 中文\n\n## Save\n\n## Save\n\n## t>Read\n\n## is removedt>alert(123)\n"; + assert.deepEqual([...headingAnchors(source)], [ + "guide", "überblick-中文", "save", "save-1", "read", "alert123", + ]); +}); + +test("heading anchors handle exposed and incomplete tags", () => { + assert.ok(headingAnchors("# ipt>Save\n").has("save")); + assert.ok(headingAnchors("# <em>Save\n").has("save")); + assert.ok(headingAnchors("#