Skip to content

brand,project - store and forward per-file brand information #10623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 27, 2024
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
2 changes: 1 addition & 1 deletion src/command/render/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export async function filterParamsJson(
[kIsShinyPython]: isShinyPython,
[kShinyPythonExec]: isShinyPython ? await pythonExec() : undefined,
[kExecutionEngine]: options.executionEngine,
[kBrand]: options.format.render[kBrand],
[kBrand]: await options.project.resolveBrand(options.source),
};
return JSON.stringify(params);
}
Expand Down
3 changes: 0 additions & 3 deletions src/command/render/render-contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -599,9 +599,6 @@ async function resolveFormats(
);
};

// resolve brand in project and forward it to format
mergedFormats[format].render.brand = await project.resolveBrand();

// ensure that we have a valid forma
const formatIsValid = isValidFormat(
formatDesc,
Expand Down
1 change: 0 additions & 1 deletion src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,6 @@ export interface FormatRender {
[kValidateYaml]?: boolean;
[kCanonicalUrl]?: boolean | string;
[kBodyClasses]?: string;
[kBrand]?: Brand;
}

export interface FormatExecute {
Expand Down
9 changes: 8 additions & 1 deletion src/core/sass/brand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ const defaultColorNameMap: Record<string, string> = {
};

export async function brandBootstrapSassBundles(
fileName: string | undefined,
project: ProjectContext,
key: string,
): Promise<SassBundle[]> {
return (await brandBootstrapSassBundleLayers(
fileName,
project,
key,
defaultColorNameMap,
Expand All @@ -42,11 +44,12 @@ export async function brandBootstrapSassBundles(
);
}
export async function brandBootstrapSassBundleLayers(
fileName: string | undefined,
project: ProjectContext,
key: string,
nameMap: Record<string, string> = {},
): Promise<SassBundleLayers[]> {
const brand = await project.resolveBrand();
const brand = await project.resolveBrand(fileName);
const sassBundles: SassBundleLayers[] = [];

if (brand?.data.color) {
Expand Down Expand Up @@ -92,21 +95,25 @@ export async function brandBootstrapSassBundleLayers(
}

export async function brandRevealSassBundleLayers(
input: string | undefined,
_format: Format,
project: ProjectContext,
): Promise<SassBundleLayers[]> {
return brandBootstrapSassBundleLayers(
input,
project,
"reveal-theme",
defaultColorNameMap,
);
}

export async function brandSassFormatExtras(
input: string | undefined,
_format: Format,
project: ProjectContext,
): Promise<FormatExtras> {
const htmlSassBundleLayers = await brandBootstrapSassBundleLayers(
input,
project,
"brand",
defaultColorNameMap,
Expand Down
2 changes: 1 addition & 1 deletion src/format/dashboard/format-dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export function dashboardFormat() {

// add _brand.yml sass bundle
extras.html[kSassBundles].push(
...await brandBootstrapSassBundles(project, "bootstrap"),
...await brandBootstrapSassBundles(input, project, "bootstrap"),
);

const scripts: DependencyHtmlFile[] = [];
Expand Down
2 changes: 1 addition & 1 deletion src/format/html/format-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export function htmlFormat(
project,
quiet,
),
await brandSassFormatExtras(format, project),
await brandSassFormatExtras(input, format, project),
{ [kFilterParams]: htmlFilterParams },
);
},
Expand Down
1 change: 1 addition & 0 deletions src/format/reveal/format-reveal-theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ export async function revealTheme(
};

const brandLayers: SassBundleLayers[] = await brandRevealSassBundleLayers(
input,
format,
project,
);
Expand Down
9 changes: 6 additions & 3 deletions src/project/project-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ export async function projectContext(
}

const result: ProjectContext = {
resolveBrand: async () => projectResolveBrand(result),
resolveBrand: async (fileName?: string) =>
projectResolveBrand(result, fileName),
resolveFullMarkdownForFile: (
engine: ExecutionEngine | undefined,
file: string,
Expand Down Expand Up @@ -339,7 +340,8 @@ export async function projectContext(
} else {
debug(`projectContext: Found Quarto project in ${dir}`);
const result: ProjectContext = {
resolveBrand: async () => projectResolveBrand(result),
resolveBrand: async (fileName?: string) =>
projectResolveBrand(result, fileName),
resolveFullMarkdownForFile: (
engine: ExecutionEngine | undefined,
file: string,
Expand Down Expand Up @@ -400,7 +402,8 @@ export async function projectContext(
configResolvers.shift();
} else if (force) {
const context: ProjectContext = {
resolveBrand: async () => projectResolveBrand(context),
resolveBrand: async (fileName?: string) =>
projectResolveBrand(context, fileName),
resolveFullMarkdownForFile: (
engine: ExecutionEngine | undefined,
file: string,
Expand Down
68 changes: 58 additions & 10 deletions src/project/project-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { QuartoJSONSchema, readYamlFromMarkdown } from "../core/yaml.ts";
import { refSchema } from "../core/lib/yaml-schema/common.ts";
import { Brand as BrandJson } from "../resources/types/schema-types.ts";
import { Brand } from "../core/brand/brand.ts";
import { warnOnce } from "../core/log.ts";

export function projectExcludeDirs(context: ProjectContext): string[] {
const outputDir = projectOutputDir(context);
Expand Down Expand Up @@ -507,22 +508,69 @@ export const ensureFileInformationCache = (
return project.fileInformationCache.get(file)!;
};

export async function projectResolveBrand(project: ProjectContext) {
if (project.brandCache) {
export async function projectResolveBrand(
project: ProjectContext,
fileName?: string,
) {
if (fileName === undefined) {
if (project.brandCache) {
return project.brandCache.brand;
}
project.brandCache = {};
let fileNames = ["_brand.yml", "_brand.yaml"].map((file) =>
join(project.dir, file)
);
if (project?.config?.brand === false) {
project.brandCache.brand = undefined;
return project.brandCache.brand;
}
if (typeof project?.config?.brand === "string") {
fileNames = [join(project.dir, project.config.brand)];
}

for (const brandPath of fileNames) {
if (!existsSync(brandPath)) {
continue;
}
const brand = await readAndValidateYamlFromFile(
brandPath,
refSchema("brand", "Format-independent brand configuration."),
"Brand validation failed for " + brandPath + ".",
) as BrandJson;
project.brandCache.brand = new Brand(brand);
}
return project.brandCache.brand;
}
project.brandCache = {};
for (const brandFile of ["_brand.yml", "_brand.yaml"]) {
const brandPath = join(project.dir, brandFile);
if (!existsSync(brandPath)) {
continue;
} else {
const metadata = await project.fileMetadata(fileName);
if (metadata.brand === false) {
return undefined;
}
if (metadata.brand === true || metadata.brand === undefined) {
return project.resolveBrand();
}
const fileInformation = ensureFileInformationCache(project, fileName);
if (fileInformation.brand) {
return fileInformation.brand;
}
if (typeof metadata.brand !== "string") {
warnOnce(
`Brand metadata must be a filename, but is of type ${typeof metadata
.brand} in file ${fileName}. Will ignore brand information`,
);
return project.resolveBrand();
}
let brandPath: string = "";
if (brandPath.startsWith("/")) {
brandPath = join(project.dir, metadata.brand);
} else {
brandPath = join(dirname(fileName), metadata.brand);
}
const brand = await readAndValidateYamlFromFile(
brandPath,
refSchema("brand", "Format-independent brand configuration."),
"Brand validation failed for " + brandPath + ".",
) as BrandJson;
project.brandCache.brand = new Brand(brand);
fileInformation.brand = new Brand(brand);
return fileInformation.brand;
}
return project.brandCache.brand;
}
3 changes: 2 additions & 1 deletion src/project/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export type FileInformation = {
engine?: ExecutionEngine;
target?: ExecutionTarget;
metadata?: Metadata;
brand?: Brand;
};

export interface ProjectContext {
Expand All @@ -67,7 +68,7 @@ export interface ProjectContext {

// This is a cache of _brand.yml for a project
brandCache?: { brand?: Brand };
resolveBrand: () => Promise<Brand | undefined>;
resolveBrand: (fileName?: string) => Promise<Brand | undefined>;

// expands markdown for a file
// input file doesn't have to be markdown; it can be, for example, a knitr spin file
Expand Down
2 changes: 1 addition & 1 deletion src/project/types/single-file/single-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function singleFileProjectContext(
const environmentMemoizer = makeProjectEnvironmentMemoizer(notebookContext);

const result: ProjectContext = {
resolveBrand: () => projectResolveBrand(result),
resolveBrand: (fileName?: string) => projectResolveBrand(result, fileName),
dir: normalizePath(dirname(source)),
engines: [],
files: {
Expand Down
9 changes: 8 additions & 1 deletion src/resources/filters/modules/brand/brand.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
-- Copyright (C) 2020-2024 Posit Software, PBC

local function get_color(name)
local brand = param("brand").processedData -- from src/core/brand/brand.ts
local p = param("brand")
if p == nil then
return nil
end
local brand = p.processedData -- from src/core/brand/brand.ts
if brand == nil then
return nil
end
return brand.color[name]
end

Expand Down
Loading