diff --git a/news/changelog-1.6.md b/news/changelog-1.6.md index 3ed40d1e6c..748574276d 100644 --- a/news/changelog-1.6.md +++ b/news/changelog-1.6.md @@ -14,7 +14,9 @@ All changes included in 1.6: ## `revealjs` Format -- Update to Reveal JS 5.1.0 +- Update to Reveal JS 5.1.0. +- Prevent empty SASS built css file to be included in header. +- Remove wrong `sourceMappingUrl` entry in SASS built css. - ([#7715](https://github.com/quarto-dev/quarto-cli/issues/7715)): Revealjs don't support anymore special Pandoc syntax making BulletList in Blockquotes become incremental list. This was confusing and unexpected behavior. Supported syntax for incremental list is documented at . - ([#9742](https://github.com/quarto-dev/quarto-cli/issues/9742)): Links to cross-referenced images correctly works. diff --git a/src/command/render/pandoc-html.ts b/src/command/render/pandoc-html.ts index 2f21943d1a..20cac21e86 100644 --- a/src/command/render/pandoc-html.ts +++ b/src/command/render/pandoc-html.ts @@ -19,7 +19,7 @@ import { ProjectContext } from "../../project/types.ts"; import { TempContext } from "../../core/temp.ts"; import { cssImports, cssResources } from "../../core/css.ts"; -import { compileSass } from "../../core/sass.ts"; +import { cleanSourceMappingUrl, compileSass } from "../../core/sass.ts"; import { kSourceMappingRegexes } from "../../config/constants.ts"; @@ -38,6 +38,7 @@ import { kMinimal } from "../../format/html/format-html-shared.ts"; import { kSassBundles } from "../../config/types.ts"; import { md5HashBytes } from "../../core/hash.ts"; import { InternalError } from "../../core/lib/error.ts"; +import { writeTextFileSyncPreserveMode } from "../../core/write.ts"; // The output target for a sass bundle // (controls the overall style tag that is emitted) @@ -126,11 +127,18 @@ export async function resolveSassBundles( } for (const target of targets) { - let cssPath = await compileSass(target.bundles, temp); - + let cssPath: string | undefined; + cssPath = await compileSass(target.bundles, temp); + // First, Clean CSS + cleanSourceMappingUrl(cssPath); // look for a sentinel 'dark' value, extract variables const cssResult = processCssIntoExtras(cssPath, extras, temp); cssPath = cssResult.path; + + // it can happen that processing generate an empty css file (e.g quarto-html deps with Quarto CSS variables) + // in that case, no need to insert the cssPath in the dependency + if (!cssPath) continue; + // Process attributes (forward on to the target) for (const bundle of target.bundles) { if (bundle.attribs) { @@ -425,7 +433,7 @@ function generateThemeCssClasses( } interface CSSResult { - path: string; + path: string | undefined; dark: boolean; } @@ -436,13 +444,8 @@ function processCssIntoExtras( temp: TempContext, ): CSSResult { extras.html = extras.html || {}; - const css = Deno.readTextFileSync(cssPath).replaceAll( - kSourceMappingRegexes[0], - "", - ).replaceAll( - kSourceMappingRegexes[1], - "", - ); + + const css = Deno.readTextFileSync(cssPath); // Extract dark sentinel value const hasDarkSentinel = cssHasDarkModeSentinel(css); @@ -471,23 +474,13 @@ function processCssIntoExtras( if (dirty) { const cleanedCss = css.replaceAll(kVariablesRegex, ""); - const hash = md5HashBytes(new TextEncoder().encode(cleanedCss)); - const newCssPath = temp.createFile({ suffix: `-${hash}.css` }); - - // Preserve the existing permissions if possible - // See https://github.com/quarto-dev/quarto-cli/issues/660 - let mode; - if (Deno.build.os !== "windows") { - const stat = Deno.statSync(cssPath); - if (stat.mode !== null) { - mode = stat.mode; - } - } - - if (mode !== undefined) { - Deno.writeTextFileSync(newCssPath, cleanedCss, { mode }); + let newCssPath: string | undefined; + if (cleanedCss.trim() === "") { + newCssPath = undefined; } else { - Deno.writeTextFileSync(newCssPath, cleanedCss); + const hash = md5HashBytes(new TextEncoder().encode(cleanedCss)); + newCssPath = temp.createFile({ suffix: `-${hash}.css` }); + writeTextFileSyncPreserveMode(newCssPath, cleanedCss); } return { diff --git a/src/core/sass.ts b/src/core/sass.ts index d50f20f0d5..6196d47ac0 100644 --- a/src/core/sass.ts +++ b/src/core/sass.ts @@ -17,6 +17,8 @@ import * as ld from "./lodash.ts"; import { lines } from "./text.ts"; import { sassCache } from "./sass/cache.ts"; import { md5HashBytes } from "./hash.ts"; +import { kSourceMappingRegexes } from "../config/constants.ts"; +import { writeTextFileSyncPreserveMode } from "./write.ts"; export interface SassVariable { name: string; @@ -327,3 +329,15 @@ export async function compileWithCache( return outputFilePath; } } + +// Clean sourceMappingUrl from css after saas compilation +export function cleanSourceMappingUrl(cssPath: string): void { + const cleaned = Deno.readTextFileSync(cssPath).replaceAll( + kSourceMappingRegexes[0], + "", + ).replaceAll( + kSourceMappingRegexes[1], + "", + ); + writeTextFileSyncPreserveMode(cssPath, cleaned); +} diff --git a/src/core/write.ts b/src/core/write.ts new file mode 100644 index 0000000000..64e128f590 --- /dev/null +++ b/src/core/write.ts @@ -0,0 +1,27 @@ +/* + * write.ts + * + * Copyright (C)2024 Posit Software, PBC + */ + +export function writeTextFileSyncPreserveMode( + path: string, + data: string, + options?: Deno.WriteFileOptions | undefined, +): void { + // Preserve the existing permissions if possible + // See https://github.com/quarto-dev/quarto-cli/issues/660 + let mode; + if (Deno.build.os !== "windows") { + const stat = Deno.statSync(path); + if (stat.mode !== null) { + mode = stat.mode; + } + } + + if (mode !== undefined) { + options = { ...options, mode }; // Merge provided options with mode + } + + Deno.writeTextFileSync(path, data, options); +} diff --git a/src/format/reveal/format-reveal-theme.ts b/src/format/reveal/format-reveal-theme.ts index 34de077e59..cef4f79a7a 100644 --- a/src/format/reveal/format-reveal-theme.ts +++ b/src/format/reveal/format-reveal-theme.ts @@ -20,6 +20,7 @@ import { isFileRef } from "../../core/http.ts"; import { pathWithForwardSlashes } from "../../core/path.ts"; import { formatResourcePath } from "../../core/resources.ts"; import { + cleanSourceMappingUrl, compileSass, mergeLayers, outputVariable, @@ -188,6 +189,8 @@ export async function revealTheme( // compile sass const css = await compileSass([bundleLayers, ...brandLayers], temp); + // Remove sourcemap information + cleanSourceMappingUrl(css); // convert from string to bytes const hash = md5HashBytes(Deno.readFileSync(css)); const fileName = `quarto-${hash}`; diff --git a/tests/docs/smoke-all/2024/05/03/9548.qmd b/tests/docs/smoke-all/2024/05/03/9548.qmd index bc44ec5db3..80329e8370 100644 --- a/tests/docs/smoke-all/2024/05/03/9548.qmd +++ b/tests/docs/smoke-all/2024/05/03/9548.qmd @@ -6,8 +6,10 @@ _quarto: tests: revealjs: ensureHtmlElements: - - ['head > link[rel="stylesheet"][href$="quarto-4f64e0fc78c89fc90127583bb01c366c.css"]'] + - [] - ['head > link[rel="stylesheet"][href$="beige.css"]'] + ensureFileRegexMatches: + - ['