Skip to content

Commit ca7636a

Browse files
Filmbostock
andauthored
fix local script resolution (#1270)
* test the scripts option * fix local scripts * remove unused imports * re-enable tests --------- Co-authored-by: Mike Bostock <[email protected]>
1 parent 59ec90c commit ca7636a

File tree

9 files changed

+132
-19
lines changed

9 files changed

+132
-19
lines changed

src/markdown.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {Config} from "./config.js";
1111
import {mergeStyle} from "./config.js";
1212
import type {FrontMatter} from "./frontMatter.js";
1313
import {readFrontMatter} from "./frontMatter.js";
14-
import {rewriteHtmlPaths} from "./html.js";
14+
import {html, rewriteHtmlPaths} from "./html.js";
1515
import {parseInfo} from "./info.js";
1616
import type {JavaScriptNode} from "./javascript/parse.js";
1717
import {parseJavaScript} from "./javascript/parse.js";
@@ -301,6 +301,7 @@ export interface ParseOptions {
301301
md: MarkdownIt;
302302
path: string;
303303
style?: Config["style"];
304+
scripts?: Config["scripts"];
304305
head?: Config["head"];
305306
header?: Config["header"];
306307
footer?: Config["footer"];
@@ -339,10 +340,10 @@ export function parseMarkdown(input: string, options: ParseOptions): MarkdownPag
339340
const tokens = md.parse(content, context);
340341
const body = md.renderer.render(tokens, md.options, context); // Note: mutates code!
341342
return {
342-
head: getHtml("head", data, options),
343-
header: getHtml("header", data, options),
343+
head: getHead(data, options),
344+
header: getHeader(data, options),
344345
body,
345-
footer: getHtml("footer", data, options),
346+
footer: getFooter(data, options),
346347
data,
347348
title: data.title !== undefined ? data.title : findTitle(tokens),
348349
style: getStyle(data, options),
@@ -363,6 +364,28 @@ export function parseMarkdownMetadata(input: string, options: ParseOptions): Pic
363364
};
364365
}
365366

367+
function getHead(data: FrontMatter, options: ParseOptions): string | null {
368+
const {scripts, path} = options;
369+
let head = getHtml("head", data, options);
370+
if (scripts?.length) {
371+
head ??= "";
372+
for (const {type, async, src} of scripts) {
373+
head += html`${head ? "\n" : ""}<script${type ? html` type="${type}"` : null}${
374+
async ? html` async` : null
375+
} src="${isAssetPath(src) ? relativePath(path, src) : src}"></script>`;
376+
}
377+
}
378+
return head;
379+
}
380+
381+
function getHeader(data: FrontMatter, options: ParseOptions): string | null {
382+
return getHtml("header", data, options);
383+
}
384+
385+
function getFooter(data: FrontMatter, options: ParseOptions): string | null {
386+
return getHtml("footer", data, options);
387+
}
388+
366389
function getHtml(
367390
key: "head" | "header" | "footer",
368391
data: FrontMatter,

src/render.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import mime from "mime";
2-
import type {Config, Page, Script, Section} from "./config.js";
2+
import type {Config, Page, Section} from "./config.js";
33
import {mergeToc} from "./config.js";
44
import {getClientPath} from "./files.js";
55
import type {Html, HtmlResolvers} from "./html.js";
@@ -39,7 +39,7 @@ ${
3939
.filter((title): title is string => !!title)
4040
.join(" | ")}</title>\n`
4141
: ""
42-
}${renderHead(page.head, resolvers, options)}${
42+
}${renderHead(page.head, resolvers)}${
4343
path === "/404"
4444
? html.unsafe(`\n<script type="module">
4545
@@ -207,28 +207,19 @@ function renderListItem(page: Page, path: string, normalizeLink: (href: string)
207207
}"><a href="${normalizeLink(relativePath(path, page.path))}">${page.name}</a></li>`;
208208
}
209209

210-
function renderHead(head: MarkdownPage["head"], resolvers: Resolvers, {scripts, root}: RenderOptions): Html {
210+
function renderHead(head: MarkdownPage["head"], resolvers: Resolvers): Html {
211211
const {stylesheets, staticImports, resolveImport, resolveStylesheet} = resolvers;
212-
const resolveScript = (src: string) => (/^\w+:/.test(src) ? src : resolveImport(relativePath(root, src)));
213212
return html`<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>${
214-
Array.from(new Set(Array.from(stylesheets, (i) => resolveStylesheet(i))), renderStylesheetPreload) // <link rel=preload as=style>
213+
Array.from(new Set(Array.from(stylesheets, resolveStylesheet)), renderStylesheetPreload) // <link rel=preload as=style>
215214
}${
216-
Array.from(new Set(Array.from(stylesheets, (i) => resolveStylesheet(i))), renderStylesheet) // <link rel=stylesheet>
215+
Array.from(new Set(Array.from(stylesheets, resolveStylesheet)), renderStylesheet) // <link rel=stylesheet>
217216
}${
218-
Array.from(new Set(Array.from(staticImports, (i) => resolveImport(i))), renderModulePreload) // <link rel=modulepreload>
217+
Array.from(new Set(Array.from(staticImports, resolveImport)), renderModulePreload) // <link rel=modulepreload>
219218
}${
220219
head ? html`\n${html.unsafe(rewriteHtml(head, resolvers))}` : null // arbitrary user content
221-
}${
222-
Array.from(scripts, (s) => renderScript(s, resolveScript)) // <script src>
223220
}`;
224221
}
225222

226-
function renderScript(script: Script, resolve: (specifier: string) => string): Html {
227-
return html`\n<script${script.type ? html` type="${script.type}"` : null}${
228-
script.async ? html` async` : null
229-
} src="${resolve(script.src)}"></script>`;
230-
}
231-
232223
function renderStylesheet(href: string): Html {
233224
return html`\n<link rel="stylesheet" type="text/css" href="${href}"${/^\w+:/.test(href) ? " crossorigin" : ""}>`;
234225
}

test/input/build/scripts/hello.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log("Hello, script!");

test/input/build/scripts/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Home page
2+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
scripts: [
3+
{type: "module", async: true, src: "https://example.com/hello.js"},
4+
{type: "module", async: true, src: "hello.js"}
5+
]
6+
};

test/input/build/scripts/sub/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Subdirectory
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log("Hello, script!");

test/output/build/scripts/index.html

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<!DOCTYPE html>
2+
<meta charset="utf-8">
3+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
4+
<title>Home page</title>
5+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
6+
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
7+
<link rel="preload" as="style" href="./_observablehq/theme-air,near-midnight.css">
8+
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
9+
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-air,near-midnight.css">
10+
<link rel="modulepreload" href="./_observablehq/client.js">
11+
<link rel="modulepreload" href="./_observablehq/runtime.js">
12+
<link rel="modulepreload" href="./_observablehq/stdlib.js">
13+
<script type="module" async="" src="https://example.com/hello.js"></script>
14+
<script type="module" async="" src="./_import/hello.a3683207.js"></script>
15+
<script type="module">
16+
17+
import "./_observablehq/client.js";
18+
19+
</script>
20+
<input id="observablehq-sidebar-toggle" type="checkbox" title="Toggle sidebar">
21+
<label id="observablehq-sidebar-backdrop" for="observablehq-sidebar-toggle"></label>
22+
<nav id="observablehq-sidebar">
23+
<ol>
24+
<label id="observablehq-sidebar-close" for="observablehq-sidebar-toggle"></label>
25+
<li class="observablehq-link observablehq-link-active"><a href="./">Home</a></li>
26+
</ol>
27+
<ol>
28+
<li class="observablehq-link"><a href="./sub/">Subdirectory</a></li>
29+
</ol>
30+
</nav>
31+
<script>{/* redacted init script */}</script>
32+
<aside id="observablehq-toc" data-selector="h1:not(:first-of-type), h2:first-child, :not(h1) + h2">
33+
<nav>
34+
</nav>
35+
</aside>
36+
<div id="observablehq-center">
37+
<main id="observablehq-main" class="observablehq">
38+
<h1 id="home-page" tabindex="-1"><a class="observablehq-header-anchor" href="#home-page">Home page</a></h1>
39+
</main>
40+
<footer id="observablehq-footer">
41+
<nav><a rel="next" href="./sub/"><span>Subdirectory</span></a></nav>
42+
<div>Built with <a href="https://observablehq.com/" target="_blank" rel="noopener noreferrer">Observable</a> on <a title="2024-01-10T16:00:00">Jan 10, 2024</a>.</div>
43+
</footer>
44+
</div>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<!DOCTYPE html>
2+
<meta charset="utf-8">
3+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
4+
<title>Subdirectory</title>
5+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
6+
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
7+
<link rel="preload" as="style" href="../_observablehq/theme-air,near-midnight.css">
8+
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
9+
<link rel="stylesheet" type="text/css" href="../_observablehq/theme-air,near-midnight.css">
10+
<link rel="modulepreload" href="../_observablehq/client.js">
11+
<link rel="modulepreload" href="../_observablehq/runtime.js">
12+
<link rel="modulepreload" href="../_observablehq/stdlib.js">
13+
<script type="module" async="" src="https://example.com/hello.js"></script>
14+
<script type="module" async="" src="../_import/hello.a3683207.js"></script>
15+
<script type="module">
16+
17+
import "../_observablehq/client.js";
18+
19+
</script>
20+
<input id="observablehq-sidebar-toggle" type="checkbox" title="Toggle sidebar">
21+
<label id="observablehq-sidebar-backdrop" for="observablehq-sidebar-toggle"></label>
22+
<nav id="observablehq-sidebar">
23+
<ol>
24+
<label id="observablehq-sidebar-close" for="observablehq-sidebar-toggle"></label>
25+
<li class="observablehq-link"><a href="../">Home</a></li>
26+
</ol>
27+
<ol>
28+
<li class="observablehq-link observablehq-link-active"><a href="./">Subdirectory</a></li>
29+
</ol>
30+
</nav>
31+
<script>{/* redacted init script */}</script>
32+
<aside id="observablehq-toc" data-selector="h1:not(:first-of-type), h2:first-child, :not(h1) + h2">
33+
<nav>
34+
</nav>
35+
</aside>
36+
<div id="observablehq-center">
37+
<main id="observablehq-main" class="observablehq">
38+
<h1 id="subdirectory" tabindex="-1"><a class="observablehq-header-anchor" href="#subdirectory">Subdirectory</a></h1>
39+
</main>
40+
<footer id="observablehq-footer">
41+
<nav><a rel="prev" href="../"><span>Home</span></a></nav>
42+
<div>Built with <a href="https://observablehq.com/" target="_blank" rel="noopener noreferrer">Observable</a> on <a title="2024-01-10T16:00:00">Jan 10, 2024</a>.</div>
43+
</footer>
44+
</div>

0 commit comments

Comments
 (0)