From 19cf9efb8b1fa1469e01ced323b2120f837435ca Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Tue, 14 Jan 2025 15:03:23 -0500 Subject: [PATCH 1/3] self-contained: call esbuild in front of pandoc to resolve es6 modules correctly --- src/core/data-url.ts | 2 +- src/core/esbuild.ts | 2 ++ src/core/pandoc/self-contained.ts | 32 +++++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/core/data-url.ts b/src/core/data-url.ts index 80ba56c81a..2cfa68a230 100644 --- a/src/core/data-url.ts +++ b/src/core/data-url.ts @@ -1,4 +1,4 @@ -import { encode as base64Encode } from "encoding/base64"; +import { encodeBase64 as base64Encode } from "encoding/base64"; export function asDataUrl( content: string | ArrayBuffer, diff --git a/src/core/esbuild.ts b/src/core/esbuild.ts index efa2045b29..32a49191c7 100644 --- a/src/core/esbuild.ts +++ b/src/core/esbuild.ts @@ -106,6 +106,8 @@ export async function esbuildCommand( if (result.success) { return result.stdout; } else { + console.error(result.stderr); + throw new Error("esbuild command failed"); } } diff --git a/src/core/pandoc/self-contained.ts b/src/core/pandoc/self-contained.ts index 3601fcf37e..ca3e9f55ae 100644 --- a/src/core/pandoc/self-contained.ts +++ b/src/core/pandoc/self-contained.ts @@ -4,9 +4,34 @@ * Copyright (C) 2020-2023 Posit Software, PBC */ -import { basename, dirname } from "../../deno_ral/path.ts"; +import { basename, dirname, join } from "../../deno_ral/path.ts"; import { formatResourcePath, pandocBinaryPath } from "../../core/resources.ts"; import { execProcess } from "../../core/process.ts"; +import { parseHtml } from "../deno-dom.ts"; +import { Element, HTMLDocument } from "deno_dom/deno-dom-wasm-noinit.ts"; +import { esbuildCompile } from "../esbuild.ts"; +import { asDataUrl } from "../data-url.ts"; + +const bundleModules = async (dom: HTMLDocument, workingDir: string) => { + const modules = dom.querySelectorAll("script[type='module']"); + for (const module of modules) { + const src = (module as Element).getAttribute("src"); + if (src) { + const srcName = join(workingDir, src); + const srcDir = dirname(srcName); + const jsSource = await esbuildCompile( + Deno.readTextFileSync(srcName), + srcDir, + [], + "esm", + ); + (module as Element).setAttribute( + "src", + asDataUrl(jsSource!, "application/javascript"), + ); + } + } +}; export const pandocIngestSelfContainedContent = async ( file: string, @@ -23,9 +48,12 @@ export const pandocIngestSelfContainedContent = async ( // The raw html contents const contents = Deno.readTextFileSync(file); + const dom = await parseHtml(contents); + await bundleModules(dom, workingDir); + const input: string[] = []; input.push("````````{=html}"); - input.push(contents); + input.push(dom.documentElement!.outerHTML); input.push("````````"); // Run pandoc to suck in dependencies From f4daab49b52507a91b2a9ecb28a806fc3f87aa36 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Tue, 14 Jan 2025 16:12:37 -0500 Subject: [PATCH 2/3] regression test --- .../embed-resources/issue-11860/.gitignore | 2 ++ .../embed-resources/issue-11860/hello.js | 2 ++ .../embed-resources/issue-11860/main.js | 1 + .../embed-resources/issue-11860/main.qmd | 11 +++++++++++ tests/integration/playwright-tests.test.ts | 14 +++++++++++++- .../html-embed-resources-es6-modules.spec.ts | 15 +++++++++++++++ 6 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 tests/docs/playwright/embed-resources/issue-11860/.gitignore create mode 100644 tests/docs/playwright/embed-resources/issue-11860/hello.js create mode 100644 tests/docs/playwright/embed-resources/issue-11860/main.js create mode 100644 tests/docs/playwright/embed-resources/issue-11860/main.qmd create mode 100644 tests/integration/playwright/tests/html-embed-resources-es6-modules.spec.ts diff --git a/tests/docs/playwright/embed-resources/issue-11860/.gitignore b/tests/docs/playwright/embed-resources/issue-11860/.gitignore new file mode 100644 index 0000000000..c19d36f89e --- /dev/null +++ b/tests/docs/playwright/embed-resources/issue-11860/.gitignore @@ -0,0 +1,2 @@ +inner +/.quarto/ diff --git a/tests/docs/playwright/embed-resources/issue-11860/hello.js b/tests/docs/playwright/embed-resources/issue-11860/hello.js new file mode 100644 index 0000000000..53a3623b93 --- /dev/null +++ b/tests/docs/playwright/embed-resources/issue-11860/hello.js @@ -0,0 +1,2 @@ +export const hello = 'world'; +alert("We're here!"); \ No newline at end of file diff --git a/tests/docs/playwright/embed-resources/issue-11860/main.js b/tests/docs/playwright/embed-resources/issue-11860/main.js new file mode 100644 index 0000000000..f50f7409cd --- /dev/null +++ b/tests/docs/playwright/embed-resources/issue-11860/main.js @@ -0,0 +1 @@ +import { hello } from './hello.js'; diff --git a/tests/docs/playwright/embed-resources/issue-11860/main.qmd b/tests/docs/playwright/embed-resources/issue-11860/main.qmd new file mode 100644 index 0000000000..a2b8f9a971 --- /dev/null +++ b/tests/docs/playwright/embed-resources/issue-11860/main.qmd @@ -0,0 +1,11 @@ +--- +format: + html: + embed-resources: true +--- + +```{=html} + +``` + +## oh oh \ No newline at end of file diff --git a/tests/integration/playwright-tests.test.ts b/tests/integration/playwright-tests.test.ts index 7cbb4a5f34..8cba2e0f36 100644 --- a/tests/integration/playwright-tests.test.ts +++ b/tests/integration/playwright-tests.test.ts @@ -32,15 +32,27 @@ await initState(); // const promises = []; const fileNames: string[] = []; +const extraOpts = [ + { + pathSuffix: "docs/playwright/embed-resources/issue-11860/main.qmd", + options: ["--output-dir=inner"], + } +] for (const { path: fileName } of globOutput) { const input = fileName; + const options: string[] = []; + for (const extraOpt of extraOpts) { + if (fileName.endsWith(extraOpt.pathSuffix)) { + options.push(...extraOpt.options); + } + } // sigh, we have a race condition somewhere in // mediabag inspection if we don't wait all renders // individually. This is very slow.. await execProcess({ - cmd: [quartoDevCmd(), "render", input], + cmd: [quartoDevCmd(), "render", input, ...options], }); fileNames.push(fileName); } diff --git a/tests/integration/playwright/tests/html-embed-resources-es6-modules.spec.ts b/tests/integration/playwright/tests/html-embed-resources-es6-modules.spec.ts new file mode 100644 index 0000000000..e55b1f065c --- /dev/null +++ b/tests/integration/playwright/tests/html-embed-resources-es6-modules.spec.ts @@ -0,0 +1,15 @@ +import { expect, Page, test } from "@playwright/test"; + +test("es6 modules to be bundled correctly in documents with embed-resources=true", async ({ page }) => { + const messages: string[] = []; + page.on('response', (response) => { + if (!response.ok()) { + messages.push(`[${response.status()}] ${response.url()}`); + } + }); + page.on('pageerror', (error) => { + messages.push(`[${error.name}] ${error.message}`); + }); + await page.goto('./embed-resources/issue-11860/inner/main.html'); + expect(messages).toStrictEqual([]); +}); From 0636a19fdbaa1e7d99256ad18945238fda351f93 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Tue, 14 Jan 2025 16:15:08 -0500 Subject: [PATCH 3/3] changelog --- news/changelog-1.7.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/news/changelog-1.7.md b/news/changelog-1.7.md index 4c02248f42..b4a259c006 100644 --- a/news/changelog-1.7.md +++ b/news/changelog-1.7.md @@ -25,13 +25,17 @@ All changes included in 1.7: - ([#11608](https://github.com/quarto-dev/quarto-cli/pull/11608)): Do not issue error message when calling `quarto check info`. -## `typst` Format +## `html` format + +- ([#11860])(https://github.com/quarto-dev/quarto-cli/issues/11860)): ES6 modules that import other local JS modules in documents with `embed-resources: true` are now correctly embedded. + +## `pdf` format -- ([#11578](https://github.com/quarto-dev/quarto-cli/issues/11578)): Typst column layout widths use fractional `fr` units instead of percent `%` units for unitless and default widths in order to fill the enclosing block and not spill outside it. - ([#11835](https://github.com/quarto-dev/quarto-cli/issues/11835)): Take markdown structure into account when detecting minimum heading level. -## `pdf` Format +## `typst` format +- ([#11578](https://github.com/quarto-dev/quarto-cli/issues/11578)): Typst column layout widths use fractional `fr` units instead of percent `%` units for unitless and default widths in order to fill the enclosing block and not spill outside it. - ([#11835](https://github.com/quarto-dev/quarto-cli/issues/11835)): Take markdown structure into account when detecting minimum heading level. ## Lua Filters and extensions