Add resolveRefs transform
Provide a transformation function that maps absolute references to resources into relative references based on the location of a page. This makes it possible to use the same links across multiple pages in the hierarchy that works when loaded as files from the filesystem.
This commit is contained in:
parent
7aa937a7e9
commit
17f8693eae
4 changed files with 105 additions and 2 deletions
|
@ -7,7 +7,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "tsc",
|
"prepare": "tsc",
|
||||||
"build": "node --enable-source-maps build/node/cli.js",
|
"build": "node --enable-source-maps build/node/cli.js",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "node --test --enable-source-maps --experimental-test-coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"antihtml": "^0.3.0"
|
"antihtml": "^0.3.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"files": ["cli.ts"],
|
"files": ["cli.ts"],
|
||||||
"include": ["content"],
|
"include": ["content", "utils"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "build/node",
|
"outDir": "build/node",
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
|
|
51
utils/resolve-refs.test.tsx
Normal file
51
utils/resolve-refs.test.tsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { suite, test } from "node:test";
|
||||||
|
import { resolveRefs } from "./resolve-refs.js";
|
||||||
|
import type { Element } from "antihtml";
|
||||||
|
|
||||||
|
|
||||||
|
suite("function resolveRefs", () => {
|
||||||
|
test("root to root relative href", () => {
|
||||||
|
const el = resolveRefs(<a href="/page.html">Link</a>, "/");
|
||||||
|
assert.equal((el as Element).attributes.get("href"), "page.html");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("root to subdir relative href", () => {
|
||||||
|
const el = resolveRefs(<a href="/dir/page.html">Link</a>, "/");
|
||||||
|
assert.equal((el as Element).attributes.get("href"), "dir/page.html");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("subdir to root relative href", () => {
|
||||||
|
const el = resolveRefs(<a href="/page.html">Link</a>, "/subdir");
|
||||||
|
assert.equal((el as Element).attributes.get("href"), "../page.html");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("subdir to subdir relative href", () => {
|
||||||
|
const el = resolveRefs(<a href="/alt/page.html">Link</a>, "/subdir");
|
||||||
|
assert.equal((el as Element).attributes.get("href"), "../alt/page.html");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("nested element", () => {
|
||||||
|
const el = resolveRefs(<div>Content with <a href="/page.html">Link</a></div>, "/");
|
||||||
|
assert.equal((el.childNodes[1] as Element).attributes.get("href"), "page.html");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns element if no changes", () => {
|
||||||
|
const el = <div>Content with <em>emphasis</em></div>;
|
||||||
|
const resEl = resolveRefs(el, "/");
|
||||||
|
assert.equal(el, resEl);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns new element if changed", () => {
|
||||||
|
const el = <div>Content with <a href="/page.html">Link</a></div>;
|
||||||
|
const resEl = resolveRefs(el, "/");
|
||||||
|
assert.notEqual(el, resEl);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not modify input", () => {
|
||||||
|
const elFn = () => <div>Content with <a href="/page.html">Link</a></div>;
|
||||||
|
const el = elFn();
|
||||||
|
resolveRefs(el, "/");
|
||||||
|
assert.deepEqual(el, elFn());
|
||||||
|
});
|
||||||
|
});
|
52
utils/resolve-refs.ts
Normal file
52
utils/resolve-refs.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import * as posix from "node:path/posix";
|
||||||
|
import { Node, Element } from "antihtml";
|
||||||
|
|
||||||
|
function shallowCopyElement(element: Element) {
|
||||||
|
const copy = new Element(element.name);
|
||||||
|
copy.attributes = new Map(element.attributes);
|
||||||
|
copy.childNodes = element.childNodes;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Resolves absolute href attributes in a and link elements the Node tree into relative references from the given directory.
|
||||||
|
|
||||||
|
@param node Node tree to transform
|
||||||
|
@param dir Absolute path to directory to resolve references from.
|
||||||
|
@returns new node tree with href attributes transformed, or the original node if no transformations took place.
|
||||||
|
*/
|
||||||
|
export function resolveRefs(node: Node, dir: string) {
|
||||||
|
if (!(node instanceof Element)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
let resolvedNode = node;
|
||||||
|
const name = node.name;
|
||||||
|
if (
|
||||||
|
(name === "link" || name === "a")
|
||||||
|
&& node.attributes.has("href")
|
||||||
|
) {
|
||||||
|
const original = node.attributes.get("href")!
|
||||||
|
/* node:coverage ignore next 3 */
|
||||||
|
if (!original.startsWith("/")) {
|
||||||
|
console.log(`Warning: found relative href to ${original}`);
|
||||||
|
} else {
|
||||||
|
const ref = posix.relative(dir, original);
|
||||||
|
resolvedNode = shallowCopyElement(node);
|
||||||
|
resolvedNode.attributes.set("href", ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const resolvedChildren: Node[] = [];
|
||||||
|
let modifiedChildren = false;
|
||||||
|
for (const child of resolvedNode.childNodes) {
|
||||||
|
const resolvedChild = resolveRefs(child, dir);
|
||||||
|
if (child !== resolvedChild) {
|
||||||
|
modifiedChildren = true;
|
||||||
|
}
|
||||||
|
resolvedChildren.push(resolvedChild);
|
||||||
|
}
|
||||||
|
if (modifiedChildren) {
|
||||||
|
resolvedNode = shallowCopyElement(node);
|
||||||
|
resolvedNode.childNodes = resolvedChildren;
|
||||||
|
}
|
||||||
|
return resolvedNode;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue