Skip to content

Commit 8a9bb3b

Browse files
authored
feat(web): allow JS in template, absolute URL support (#751)
1 parent 2680e84 commit 8a9bb3b

File tree

16 files changed

+253
-36
lines changed

16 files changed

+253
-36
lines changed

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/orama-db/generate.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export async function generate(input) {
3737
description: paragraph
3838
? transformNodeToString(paragraph, true)
3939
: undefined,
40-
href: `${entry.path.slice(1)}.html#${entry.heading.data.slug}`,
40+
href: `${entry.path}.html#${entry.heading.data.slug}`,
4141
siteSection: headings[0].heading.data.name,
4242
};
4343
})

src/generators/web/README.md

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@ The `web` generator transforms JSX AST entries into complete web bundles, produc
66

77
The `web` generator accepts the following configuration options:
88

9-
| Name | Type | Default | Description |
10-
| ---------------- | -------- | --------------------------------------------- | --------------------------------------------------------------------- |
11-
| `output` | `string` | - | The directory where HTML, JavaScript, and CSS files will be written |
12-
| `templatePath` | `string` | `'template.html'` | Path to the HTML template file |
13-
| `project` | `string` | `'Node.js'` | Project name used in page titles and the version selector |
14-
| `title` | `string` | `'{project} v{version} Documentation'` | Title template for HTML pages (supports `{project}`, `{version}`) |
15-
| `editURL` | `string` | `'${GITHUB_EDIT_URL}/doc/api{path}.md'` | URL template for "edit this page" links |
16-
| `pageURL` | `string` | `'{baseURL}/latest-{version}/api{path}.html'` | URL template for documentation page links |
17-
| `imports` | `object` | See below | Object mapping `#theme/` aliases to component paths for customization |
18-
| `virtualImports` | `object` | `{}` | Additional virtual module mappings merged into the build |
9+
| Name | Type | Default | Description |
10+
| ----------------- | --------- | --------------------------------------------- | --------------------------------------------------------------------- |
11+
| `output` | `string` | - | The directory where HTML, JavaScript, and CSS files will be written |
12+
| `templatePath` | `string` | `'template.html'` | Path to the HTML template file |
13+
| `project` | `string` | `'Node.js'` | Project name used in page titles and the version selector |
14+
| `title` | `string` | `'{project} v{version} Documentation'` | Title template for HTML pages (supports `{project}`, `{version}`) |
15+
| `useAbsoluteURLs` | `boolean` | `false` | When `true`, all internal links use absolute URLs based on `baseURL` |
16+
| `editURL` | `string` | `'${GITHUB_EDIT_URL}/doc/api{path}.md'` | URL template for "edit this page" links |
17+
| `pageURL` | `string` | `'{baseURL}/latest-{version}/api{path}.html'` | URL template for documentation page links |
18+
| `imports` | `object` | See below | Object mapping `#theme/` aliases to component paths for customization |
19+
| `virtualImports` | `object` | `{}` | Additional virtual module mappings merged into the build |
1920

2021
#### Default `imports`
2122

@@ -60,6 +61,8 @@ All scalar (non-object) configuration values are automatically exported. The def
6061
| `versions` | `Array<{ url, label, major }>` | Pre-computed version entries with labels and URL templates (only `{path}` remains for per-page use) |
6162
| `editURL` | `string` | Partially populated "edit this page" URL template (only `{path}` remains) |
6263
| `pages` | `Array<[string, string]>` | Sorted `[name, path]` tuples for sidebar navigation |
64+
| `useAbsoluteURLs` | `boolean` | Whether internal links use absolute URLs (mirrors config value) |
65+
| `baseURL` | `string` | Base URL for the documentation site (used when `useAbsoluteURLs` is `true`) |
6366
| `languageDisplayNameMap` | `Map<string, string>` | Shiki language alias → display name map for code blocks |
6467

6568
#### Usage in custom components
@@ -96,3 +99,30 @@ The `Layout` component receives the following props:
9699
| `children` | `ComponentChildren` | Processed page content |
97100

98101
Custom Layout components can use any combination of these props alongside `#theme/config` imports.
102+
103+
### HTML template
104+
105+
The HTML template file (set via `templatePath`) uses JavaScript template literal syntax (`${...}` placeholders) and is evaluated at build time with full expression support.
106+
107+
#### Available template variables
108+
109+
| Variable | Type | Description |
110+
| ------------------ | -------- | ----------------------------------------------------------------- |
111+
| `title` | `string` | Fully resolved page title (e.g. `'File system \| Node.js v22.x'`) |
112+
| `dehydrated` | `string` | Server-rendered HTML for the page content |
113+
| `importMap` | `string` | JSON import map for client-side module resolution |
114+
| `entrypoint` | `string` | Client-side entry point filename with cache-bust query |
115+
| `speculationRules` | `string` | Speculation rules JSON for prefetching |
116+
| `root` | `string` | Relative or absolute path to the site root |
117+
| `metadata` | `object` | Full page metadata (frontmatter, path, heading, etc.) |
118+
| `config` | `object` | The resolved web generator configuration |
119+
120+
Since the template supports arbitrary JS expressions, you can use conditionals and method calls:
121+
122+
```html
123+
<title>${title}</title>
124+
<link rel="stylesheet" href="${root}styles.css" />
125+
<script type="importmap">
126+
${importMap}
127+
</script>
128+
```

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
}, []);

0 commit comments

Comments
 (0)