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
48 changes: 36 additions & 12 deletions src/command/render/pandoc-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
kQuartoCssVariables,
kTextHighlightingMode,
SassBundle,
SassBundleWithBrand,
SassLayer,
} from "../../config/types.ts";
import { ProjectContext } from "../../project/types.ts";

Expand All @@ -39,6 +41,7 @@ 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";
import { assert } from "testing/asserts";

// The output target for a sass bundle
// (controls the overall style tag that is emitted)
Expand All @@ -57,12 +60,12 @@ export async function resolveSassBundles(
) {
extras = cloneDeep(extras);

const mergedBundles: Record<string, SassBundle[]> = {};
const mergedBundles: Record<string, SassBundleWithBrand[]> = {};

// groups the bundles by dependency name
const group = (
bundles: SassBundle[],
groupedBundles: Record<string, SassBundle[]>,
bundles: SassBundleWithBrand[],
groupedBundles: Record<string, SassBundleWithBrand[]>,
) => {
bundles.forEach((bundle) => {
if (!groupedBundles[bundle.dependency]) {
Expand All @@ -82,19 +85,37 @@ export async function resolveSassBundles(
let defaultStyle: "dark" | "light" | undefined = undefined;
for (const dependency of Object.keys(mergedBundles)) {
// compile the cssPath
const bundles = mergedBundles[dependency];
const bundlesWithBrand = mergedBundles[dependency];
// first, pull out the brand-specific layers
//
// the brand bundle itself doesn't have any 'brand' entries;
// those are used to specify where the brand-specific layers should be inserted
// in the final bundle. We filter
const brandLayersMaybeBrand = bundlesWithBrand.find((bundle) =>
bundle.key === "brand"
)?.user || [];
assert(!brandLayersMaybeBrand.find((v) => v === "brand"));
const brandLayers = brandLayersMaybeBrand as SassLayer[];
const bundles: SassBundle[] = bundlesWithBrand.filter((bundle) =>
bundle.key !== "brand"
).map((bundle) => {
const userBrand = bundle.user?.findIndex((layer) => layer === "brand");
if (userBrand && userBrand !== -1) {
bundle = cloneDeep(bundle);
bundle.user!.splice(userBrand, 1, ...brandLayers);
}
return bundle as SassBundle;
});

// 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: (bundles as any),
attribs: {
"append-hash": "true",
},
Expand All @@ -119,7 +140,7 @@ export async function resolveSassBundles(
});
targets.push({
name: `${dependency}-dark.min.css`,
bundles: darkBundles,
bundles: darkBundles as any,
attribs: {
"append-hash": "true",
...attribForThemeStyle("dark", defaultStyle),
Expand All @@ -141,6 +162,9 @@ export async function resolveSassBundles(
// 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;
if (Deno.readTextFileSync(cssPath).length === 0) {
continue;
}

// Process attributes (forward on to the target)
for (const bundle of target.bundles) {
Expand Down
25 changes: 22 additions & 3 deletions src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,28 @@ export interface SassLayer {
rules: string;
}

export interface SassBundleLayersWithBrand {
key: string;
user?: (SassLayer | "brand")[];
quarto?: SassLayer;
framework?: SassLayer;
loadPaths?: string[];
}

export interface SassBundleWithBrand extends SassBundleLayersWithBrand {
dependency: string;
dark?: {
user?: (SassLayer | "brand")[];
quarto?: SassLayer;
framework?: SassLayer;
default?: boolean;
};
attribs?: Record<string, string>;
}

export interface SassBundleLayers {
key: string;
user?: SassLayer;
user?: SassLayer[];
quarto?: SassLayer;
framework?: SassLayer;
loadPaths?: string[];
Expand All @@ -316,7 +335,7 @@ export interface SassBundleLayers {
export interface SassBundle extends SassBundleLayers {
dependency: string;
dark?: {
user?: SassLayer;
user?: SassLayer[];
quarto?: SassLayer;
framework?: SassLayer;
default?: boolean;
Expand Down Expand Up @@ -376,7 +395,7 @@ export interface FormatExtras {
templateContext?: FormatTemplateContext;
html?: {
[kDependencies]?: FormatDependency[];
[kSassBundles]?: SassBundle[];
[kSassBundles]?: SassBundleWithBrand[];
[kBodyEnvelope]?: BodyEnvelope;
[kHtmlPostprocessors]?: Array<HtmlPostProcessor>;
[kHtmlFinalizers]?: Array<
Expand Down
39 changes: 21 additions & 18 deletions src/core/sass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,18 @@ export async function compileSass(
);
const quartoDefaults = bundles.map((bundle) => bundle.quarto?.defaults || "");
const quartoRules = bundles.map((bundle) => bundle.quarto?.rules || "");

const quartoMixins = bundles.map((bundle) => bundle.quarto?.mixins || "");

const userLayers = mergeLayers(
...bundles.map((bundle) => bundle.user || []).flat(),
);

// Gather sasslayer for the user
const userUses = bundles.map((bundle) => bundle.user?.uses || "");
const userFunctions = bundles.map((bundle) => bundle.user?.functions || "");
const userDefaults = bundles.map((bundle) => bundle.user?.defaults || "");
const userRules = bundles.map((bundle) => bundle.user?.rules || "");
const userMixins = bundles.map((bundle) => bundle.user?.mixins || "");
const userUses = userLayers.uses; //bundles.map((bundle) => bundle.user?.uses || "");
const userFunctions = userLayers.functions; // bundles.map((bundle) => bundle.user?.functions || "");
const userDefaults = userLayers.defaults; // bundles.map((bundle) => bundle.user?.defaults || "");
const userRules = userLayers.rules; // bundles.map((bundle) => bundle.user?.rules || "");
const userMixins = userLayers.mixins; // bundles.map((bundle) => bundle.user?.mixins || "");

// Set any load paths used to resolve imports
const loadPaths: string[] = [];
Expand All @@ -114,15 +117,15 @@ export async function compileSass(
'// quarto-scss-analysis-annotation { "origin": "\'use\' section from Quarto" }',
...quartoUses,
'// quarto-scss-analysis-annotation { "origin": "\'use\' section from user-defined SCSS" }',
...userUses,
userUses,
'// quarto-scss-analysis-annotation { "origin": "\'functions\' section from format" }',
...frameworkFunctions,
'// quarto-scss-analysis-annotation { "origin": "\'functions\' section from Quarto" }',
...quartoFunctions,
'// quarto-scss-analysis-annotation { "origin": "\'functions\' section from user-defined SCSS" }',
...userFunctions,
userFunctions,
'// quarto-scss-analysis-annotation { "origin": "Defaults from user-defined SCSS" }',
...userDefaults.reverse(),
userDefaults,
'// quarto-scss-analysis-annotation { "origin": "Defaults from Quarto\'s SCSS" }',
...quartoDefaults.reverse(),
'// quarto-scss-analysis-annotation { "origin": "Defaults from the format SCSS" }',
Expand All @@ -132,13 +135,13 @@ export async function compileSass(
'// quarto-scss-analysis-annotation { "origin": "\'mixins\' section from Quarto" }',
...quartoMixins,
'// quarto-scss-analysis-annotation { "origin": "\'mixins\' section from user-defined SCSS" }',
...userMixins,
userMixins,
'// quarto-scss-analysis-annotation { "origin": "\'rules\' section from format" }',
...frameworkRules,
'// quarto-scss-analysis-annotation { "origin": "\'rules\' section from Quarto" }',
...quartoRules,
'// quarto-scss-analysis-annotation { "origin": "\'rules\' section from user-defined SCSS" }',
...userRules,
userRules,
'// quarto-scss-analysis-annotation { "origin": null }',
].join("\n\n");

Expand Down Expand Up @@ -191,7 +194,7 @@ const layoutBoundary =
const kLayerBoundaryLine = RegExp(layoutBoundary);
const kLayerBoundaryTest = RegExp(layoutBoundary, "m");

export function mergeLayers(...layers: SassLayer[]) {
export function mergeLayers(...layers: SassLayer[]): SassLayer {
const themeUses: string[] = [];
const themeDefaults: string[] = [];
const themeRules: string[] = [];
Expand All @@ -202,10 +205,7 @@ export function mergeLayers(...layers: SassLayer[]) {
themeUses.push(theme.uses);
}
if (theme.defaults) {
// We need to reverse the order of defaults
// since defaults override one another by being
// set first
themeDefaults.unshift(theme.defaults);
themeDefaults.push(theme.defaults);
}

if (theme.rules) {
Expand All @@ -223,7 +223,10 @@ export function mergeLayers(...layers: SassLayer[]) {

return {
uses: themeUses.join("\n"),
defaults: themeDefaults.join("\n"),
// We need to reverse the order of defaults
// since defaults override one another by being
// set first
defaults: themeDefaults.reverse().join("\n"),
functions: themeFunctions.join("\n"),
mixins: themeMixins.join("\n"),
rules: themeRules.join("\n"),
Expand Down Expand Up @@ -419,5 +422,5 @@ export function cleanSourceMappingUrl(cssPath: string): void {
kSourceMappingRegexes[1],
"",
);
writeTextFileSyncPreserveMode(cssPath, cleaned);
writeTextFileSyncPreserveMode(cssPath, cleaned.trim());
}
Loading
Loading