Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions src/command/render/pandoc-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
} from "../../config/types.ts";
import { ProjectContext } from "../../project/types.ts";

import { TempContext } from "../../core/temp.ts";
import { cssImports, cssResources } from "../../core/css.ts";
import { cleanSourceMappingUrl, compileSass } from "../../core/sass.ts";

Expand Down Expand Up @@ -91,10 +90,12 @@ export async function resolveSassBundles(
const maybeBrandBundle = bundlesWithBrand.find((bundle) =>
bundle.key === "brand"
);
assert(!maybeBrandBundle ||
!maybeBrandBundle.user?.find((v) => v === "brand") &&
!maybeBrandBundle.dark?.user?.find((v) => v === "brand"));
let foundBrand = {light: false, dark: false};
assert(
!maybeBrandBundle ||
!maybeBrandBundle.user?.find((v) => v === "brand") &&
!maybeBrandBundle.dark?.user?.find((v) => v === "brand"),
);
const foundBrand = { light: false, dark: false };
const bundles: SassBundle[] = bundlesWithBrand.filter((bundle) =>
bundle.key !== "brand"
).map((bundle) => {
Expand All @@ -106,12 +107,18 @@ export async function resolveSassBundles(
bundle.user!.splice(userBrand, 1, ...(maybeBrandBundle?.user || []));
foundBrand.light = true;
}
const darkBrand = bundle.dark?.user?.findIndex((layer) => layer === "brand");
const darkBrand = bundle.dark?.user?.findIndex((layer) =>
layer === "brand"
);
if (darkBrand && darkBrand !== -1) {
if (!cloned) {
bundle = cloneDeep(bundle);
}
bundle.dark!.user!.splice(darkBrand, 1, ...(maybeBrandBundle?.dark?.user || []))
bundle.dark!.user!.splice(
darkBrand,
1,
...(maybeBrandBundle?.dark?.user || []),
);
foundBrand.dark = true;
}
return bundle as SassBundle;
Expand All @@ -122,18 +129,19 @@ export async function resolveSassBundles(
key: "brand",
user: !foundBrand.light && maybeBrandBundle?.user as SassLayer[] || [],
dark: !foundBrand.dark && maybeBrandBundle?.dark?.user && {
user: maybeBrandBundle.dark.user as SassLayer[],
default: maybeBrandBundle.dark.default
} || undefined
user: maybeBrandBundle.dark.user as SassLayer[],
default: maybeBrandBundle.dark.default,
} || undefined,
});
}

// See if any bundles are providing dark specific css
const hasDark = bundles.some((bundle) => bundle.dark !== undefined);
defaultStyle =
bundles.some((bundle) => bundle.dark !== undefined && bundle.dark.default)
? "dark"
: "light";
defaultStyle = bundles.some((bundle) =>
bundle.dark !== undefined && bundle.dark.default
)
? "dark"
: "light";
const targets: SassTarget[] = [{
name: `${dependency}.min.css`,
bundles: (bundles as any),
Expand Down
1 change: 0 additions & 1 deletion src/command/render/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ import { fileExecutionEngine } from "../../execute/engine.ts";
import { projectContextForDirectory } from "../../project/project-context.ts";
import { ProjectType } from "../../project/types/types.ts";
import { ProjectConfig as ProjectConfig_Project } from "../../resources/types/schema-types.ts";
import { Extension } from "../../extension/types.ts";

const noMutationValidations = (
projType: ProjectType,
Expand Down
15 changes: 0 additions & 15 deletions src/command/render/render-contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,6 @@ import {
import { ExtensionContext } from "../../extension/types.ts";
import { NotebookContext } from "../../render/notebook/notebook-types.ts";

// we can't naively ld.cloneDeep everything
// because that destroys class instances
// with private members
//
// Currently, that's ProjectContext.
//
// TODO: Ideally, we shouldn't be copying the RenderContext at all.
export function copyRenderContext(
context: RenderContext,
): RenderContext {
return {
...ld.cloneDeep(context),
project: context.project,
};
}
export async function resolveFormatsFromMetadata(
metadata: Metadata,
input: string,
Expand Down
5 changes: 3 additions & 2 deletions src/command/render/render-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import { outputRecipe } from "./output.ts";

import { renderPandoc } from "./render.ts";
import { PandocRenderCompletion, RenderServices } from "./types.ts";
import { copyRenderContext, renderContexts } from "./render-contexts.ts";
import { renderContexts } from "./render-contexts.ts";
import { renderProgress } from "./render-info.ts";
import {
ExecutedFile,
Expand Down Expand Up @@ -114,6 +114,7 @@ import {
} from "../../project/project-shared.ts";
import { NotebookContext } from "../../render/notebook/notebook-types.ts";
import { setExecuteEnvironment } from "../../execute/environment.ts";
import { safeCloneDeep } from "../../core/safe-clone-deep.ts";

export async function renderExecute(
context: RenderContext,
Expand Down Expand Up @@ -503,7 +504,7 @@ async function renderFileInternal(

for (const format of Object.keys(contexts)) {
pushTiming("render-context");
const context = copyRenderContext(contexts[format]); // since we're going to mutate it...
const context = safeCloneDeep(contexts[format]); // since we're going to mutate it...

// disquality some documents from server: shiny
if (isServerShiny(context.format) && context.project) {
Expand Down
7 changes: 6 additions & 1 deletion src/core/cache/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ import {
type ImmediateBufferCacheEntry,
type ImmediateStringCacheEntry,
} from "./cache-types.ts";
import { Cloneable } from "../safe-clone-deep.ts";
export { type ProjectCache } from "./cache-types.ts";

const currentCacheVersion = "1";
const requiredQuartoVersions: Record<string, string> = {
"1": ">1.7.0",
};

class ProjectCacheImpl {
class ProjectCacheImpl implements Cloneable<ProjectCacheImpl> {
projectScratchDir: string;
index: Deno.Kv | null;

Expand All @@ -35,6 +36,10 @@ class ProjectCacheImpl {
this.index = null;
}

clone() {
return this;
Comment thread
cscheid marked this conversation as resolved.
}

close() {
if (this.index) {
this.index.close();
Expand Down
54 changes: 54 additions & 0 deletions src/core/safe-clone-deep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* safe-clone-deep.ts
*
* CloneDeep that uses object's own cloning methods when available
*
* Copyright (C) 2025 Posit Software, PBC
*/

export interface Cloneable<T> {
clone(): T;
}

export function safeCloneDeep<T>(obj: T): T {
if (obj === null || typeof obj !== "object") {
return obj;
}

// Handle arrays
if (Array.isArray(obj)) {
return obj.map((item) => safeCloneDeep(item)) as T;
}

if (obj && ("clone" in obj) && typeof obj.clone === "function") {
return obj.clone();
}

// Handle Maps
if (obj instanceof Map) {
const clonedMap = new Map();
for (const [key, value] of obj.entries()) {
clonedMap.set(key, safeCloneDeep(value));
}
return clonedMap as T;
}

// Handle Sets
if (obj instanceof Set) {
const clonedSet = new Set();
for (const value of obj.values()) {
clonedSet.add(safeCloneDeep(value));
}
return clonedSet as T;
}

// Handle regular objects
const result = {} as T;
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[key] = safeCloneDeep(obj[key]);
}
}

return result;
}
4 changes: 3 additions & 1 deletion src/core/sass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { existsSync } from "../deno_ral/fs.ts";
import { join } from "../deno_ral/path.ts";

import { quartoCacheDir } from "./appdirs.ts";
import { TempContext } from "./temp.ts";

import { SassBundleLayers, SassLayer } from "../config/types.ts";
import { dartCompile } from "./dart-sass.ts";
Expand Down Expand Up @@ -384,6 +383,9 @@ export async function compileWithCache(
const result = await memoizedGetVarsBlock(project, input);
return input + "\n" + result;
} catch (e) {
if (e.name !== "SCSSParsingError") {
throw e;
}
console.warn("Error adding css vars block", e);
console.warn(
"The resulting CSS file will not have SCSS color variables exported as CSS.",
Expand Down
16 changes: 15 additions & 1 deletion src/core/sass/add-css-vars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,22 @@ import { propagateDeclarationTypes } from "./analyzer/declaration-types.ts";
import { getVariableDependencies } from "./analyzer/get-dependencies.ts";

const { getSassAst } = makeParserModule(parse);

export class SCSSParsingError extends Error {
constructor(message: string) {
super(`SCSS Parsing Error: ${message}`);
this.name = "SCSSParsingError";
}
}

export const cssVarsBlock = (scssSource: string) => {
const ast = propagateDeclarationTypes(cleanSassAst(getSassAst(scssSource)));
let astOriginal;
try {
astOriginal = getSassAst(scssSource);
} catch (e) {
throw new SCSSParsingError(e.message);
}
const ast = propagateDeclarationTypes(cleanSassAst(astOriginal));
const deps = getVariableDependencies(ast);

const output: string[] = [":root {"];
Expand Down
7 changes: 6 additions & 1 deletion src/core/sass/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ import { TempContext } from "../temp.ts";
import { safeRemoveIfExists } from "../path.ts";
import * as log from "../../deno_ral/log.ts";
import { onCleanup } from "../cleanup.ts";
import { Cloneable } from "../safe-clone-deep.ts";

class SassCache {
class SassCache implements Cloneable<SassCache> {
kv: Deno.Kv;
path: string;

clone() {
return this;
}

constructor(kv: Deno.Kv, path: string) {
this.kv = kv;
this.path = path;
Expand Down
3 changes: 1 addition & 2 deletions src/execute/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Copyright (C) 2020-2022 Posit Software, PBC
*/

import { extname, join, resolve } from "../deno_ral/path.ts";
import { extname, join } from "../deno_ral/path.ts";

import * as ld from "../core/lodash.ts";

Expand All @@ -23,7 +23,6 @@ import { kMdExtensions, markdownEngine } from "./markdown.ts";
import { ExecutionEngine, kQmdExtensions } from "./types.ts";
import { languagesInMarkdown } from "./engine-shared.ts";
import { languages as handlerLanguages } from "../core/handlers/base.ts";
import { MappedString } from "../core/lib/text-types.ts";
import { RenderContext, RenderFlags } from "../command/render/types.ts";
import { mergeConfigs } from "../core/config.ts";
import { ProjectContext } from "../project/types.ts";
Expand Down
2 changes: 1 addition & 1 deletion src/project/project-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ import { NotebookContext } from "../render/notebook/notebook-types.ts";
import { MappedString } from "../core/mapped-text.ts";
import { makeTimedFunctionAsync } from "../core/performance/function-times.ts";
import { createProjectCache } from "../core/cache/cache.ts";
import { createTempContext, globalTempContext } from "../core/temp.ts";
import { createTempContext } from "../core/temp.ts";

export async function projectContext(
path: string,
Expand Down
Loading
Loading