Skip to content

Commit 9c9eff3

Browse files
committed
feat(web): allow JS in template, absolute URL support
1 parent 2680e84 commit 9c9eff3

13 files changed

Lines changed: 232 additions & 26 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@node-core/doc-kit",
33
"type": "module",
4-
"version": "1.3.2",
4+
"version": "1.3.3",
55
"repository": {
66
"type": "git",
77
"url": "git+https://github.com/nodejs/doc-kit.git"

src/generators/web/index.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export default createLazyGenerator({
3131
templatePath: join(import.meta.dirname, 'template.html'),
3232
project: 'Node.js',
3333
title: '{project} v{version} Documentation',
34+
useAbsoluteURLs: false,
3435
editURL: `${GITHUB_EDIT_URL}/doc/api{path}.md`,
3536
pageURL: '{baseURL}/latest-{version}/api{path}.html',
3637
imports: {

src/generators/web/template.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
<meta charset="UTF-8" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<link rel="icon" href="https://nodejs.org/static/images/favicons/favicon.png"/>
8-
<title>{title}</title>
8+
<title>${title}</title>
99
<meta name="description" content="Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.">
10-
<link rel="stylesheet" href="{root}styles.css" />
11-
<meta property="og:title" content="{title}">
10+
<link rel="stylesheet" href="${root}styles.css" />
11+
<meta property="og:title" content="${title}">
1212
<meta property="og:description" content="Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.">
1313
<meta property="og:image" content="https://nodejs.org/en/next-data/og/announcement/Node.js%20%E2%80%94%20Run%20JavaScript%20Everywhere" />
1414
<meta property="og:type" content="website">
@@ -20,12 +20,12 @@
2020

2121
<!-- Apply theme before paint to avoid Flash of Unstyled Content -->
2222
<script>document.documentElement.setAttribute("data-theme", document.documentElement.style.colorScheme = localStorage.getItem("theme") || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"));</script>
23-
<script type="importmap">{importMap}</script>
24-
<script type="speculationrules">{speculationRules}</script>
23+
<script type="importmap">${importMap}</script>
24+
<script type="speculationrules">${speculationRules}</script>
2525
</head>
2626

2727
<body>
28-
<div id="root">{dehydrated}</div>
29-
<script type="module" src="{root}{entrypoint}"></script>
28+
<div id="root">${dehydrated}</div>
29+
<script type="module" src="${root}${entrypoint}"></script>
3030
</body>
3131
</html>

src/generators/web/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { JSXContent } from '../jsx-ast/utils/buildContent.mjs';
33
export type Configuration = {
44
templatePath: string;
55
title: string;
6+
useAbsoluteURLs: boolean;
67
imports: Record<string, string>;
78
virtualImports: Record<string, string>;
89
};

src/generators/web/ui/components/SearchBox/index.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import SearchResults from '@node-core/ui-components/Common/Search/Results';
88
import SearchHit from '@node-core/ui-components/Common/Search/Results/Hit';
99

1010
import styles from './index.module.css';
11-
import { relative } from '../../../../../utils/url.mjs';
1211
import useOrama from '../../hooks/useOrama.mjs';
12+
import { relativeOrAbsolute } from '../../utils/relativeOrAbsolute.mjs';
1313

1414
const SearchBox = ({ pathname }) => {
1515
const client = useOrama(pathname);
@@ -23,7 +23,7 @@ const SearchBox = ({ pathname }) => {
2323
<SearchHit
2424
document={{
2525
...hit.document,
26-
href: relative(hit.document.href, pathname),
26+
href: relativeOrAbsolute(hit.document.href, pathname),
2727
}}
2828
/>
2929
)}

src/generators/web/ui/components/SideBar/index.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Select from '@node-core/ui-components/Common/Select';
22
import SideBar from '@node-core/ui-components/Containers/Sidebar';
33

44
import styles from './index.module.css';
5-
import { relative } from '../../../../../utils/url.mjs';
5+
import { relativeOrAbsolute } from '../../utils/relativeOrAbsolute.mjs';
66

77
import { project, version, versions, pages } from '#theme/config';
88

@@ -41,7 +41,7 @@ export default ({ metadata }) => {
4141
link:
4242
metadata.path === path
4343
? `${metadata.basename}.html`
44-
: `${relative(path, metadata.path)}.html`,
44+
: `${relativeOrAbsolute(path, metadata.path)}.html`,
4545
}));
4646

4747
return (

src/generators/web/ui/hooks/useOrama.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { create, search, load } from '@orama/orama';
22
import { useState, useEffect } from 'react';
33

4-
import { relative } from '../../../../utils/url.mjs';
4+
import { relativeOrAbsolute } from '../utils/relativeOrAbsolute.mjs';
55

66
/**
77
* Hook for initializing and managing Orama search database
@@ -26,7 +26,7 @@ export default pathname => {
2626
setClient(db);
2727

2828
// Load the search data
29-
fetch(relative('/orama-db.json', pathname))
29+
fetch(relativeOrAbsolute('/orama-db.json', pathname))
3030
.then(response => response.ok && response.json())
3131
.then(data => load(db, data));
3232
}, []);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { relative } from '../../../../utils/url.mjs';
2+
3+
import { useAbsoluteURLs, baseURL } from '#theme/config';
4+
5+
/**
6+
* Returns an absolute URL (based on baseURL) or a relative URL,
7+
* depending on the useAbsoluteURLs configuration option.
8+
*
9+
* @param {string} to - Target path (e.g., '/fs', '/orama-db.json')
10+
* @param {string} from - Current page path (e.g., '/api/fs')
11+
* @returns {string}
12+
*/
13+
export const relativeOrAbsolute = (to, from) =>
14+
useAbsoluteURLs
15+
? `${baseURL.replace(/\/$/, '')}${to.replace(/\/$/, '')}`
16+
: relative(to, from);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import assert from 'node:assert/strict';
2+
import { describe, it } from 'node:test';
3+
4+
import { populateWithEvaluation } from '../processing.mjs';
5+
6+
describe('populateWithEvaluation', () => {
7+
it('substitutes simple ${variable} placeholders', () => {
8+
const result = populateWithEvaluation('Hello ${name}!', { name: 'World' });
9+
assert.strictEqual(result, 'Hello World!');
10+
});
11+
12+
it('supports multiple variables', () => {
13+
const result = populateWithEvaluation('${greeting} ${name}!', {
14+
greeting: 'Hi',
15+
name: 'Node',
16+
});
17+
assert.strictEqual(result, 'Hi Node!');
18+
});
19+
20+
it('supports JavaScript expressions', () => {
21+
const result = populateWithEvaluation('${value > 5 ? "big" : "small"}', {
22+
value: 10,
23+
});
24+
assert.strictEqual(result, 'big');
25+
});
26+
27+
it('supports ternary expressions for conditional content', () => {
28+
const result = populateWithEvaluation(
29+
'${showExtra ? "extra content" : ""}',
30+
{ showExtra: false }
31+
);
32+
assert.strictEqual(result, '');
33+
});
34+
35+
it('handles JSON.stringify for objects', () => {
36+
const obj = { key: 'value' };
37+
const result = populateWithEvaluation('${JSON.stringify(data)}', {
38+
data: obj,
39+
});
40+
assert.strictEqual(result, '{"key":"value"}');
41+
});
42+
43+
it('preserves surrounding HTML content', () => {
44+
const result = populateWithEvaluation(
45+
'<title>${title}</title><link href="${root}styles.css" />',
46+
{ title: 'Test Page', root: '../' }
47+
);
48+
assert.strictEqual(
49+
result,
50+
'<title>Test Page</title><link href="../styles.css" />'
51+
);
52+
});
53+
54+
it('handles empty string values', () => {
55+
const result = populateWithEvaluation('[${content}]', { content: '' });
56+
assert.strictEqual(result, '[]');
57+
});
58+
59+
it('handles numeric values', () => {
60+
const result = populateWithEvaluation('count: ${count}', { count: 42 });
61+
assert.strictEqual(result, 'count: 42');
62+
});
63+
});

0 commit comments

Comments
 (0)