From 1490a986475786d2ea43355070536bf309973c04 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Wed, 26 Mar 2025 11:20:56 -0400 Subject: [PATCH 1/2] use safeCloneDeep instead of cloneDeep, update schemas etc --- .../render/pandoc-dependencies-html.ts | 3 +- src/command/render/pandoc-html.ts | 49 +++++++++++-------- src/command/render/pandoc.ts | 26 +++++++--- src/command/render/render-contexts.ts | 28 +++-------- src/command/render/render-files.ts | 5 +- src/config/types.ts | 2 +- src/resources/editor/tools/vs-code.mjs | 19 +++++-- src/resources/editor/tools/yaml/web-worker.js | 19 +++++-- .../yaml/yaml-intelligence-resources.json | 19 +++++-- src/resources/schema/definitions.yml | 3 ++ src/resources/schema/document-attributes.yml | 5 ++ src/resources/schema/json-schemas.json | 3 ++ src/resources/types/schema-types.ts | 2 + 13 files changed, 115 insertions(+), 68 deletions(-) diff --git a/src/command/render/pandoc-dependencies-html.ts b/src/command/render/pandoc-dependencies-html.ts index 0f98cd31455..ce2ccbc971e 100644 --- a/src/command/render/pandoc-dependencies-html.ts +++ b/src/command/render/pandoc-dependencies-html.ts @@ -35,6 +35,7 @@ import { ensureDirSync } from "../../deno_ral/fs.ts"; import { ProjectContext } from "../../project/types.ts"; import { projectOutputDir } from "../../project/project-shared.ts"; import { insecureHash } from "../../core/hash.ts"; +import { safeCloneDeep } from "../../core/safe-clone-deep.ts"; export async function writeDependencies( dependenciesFile: string, @@ -146,7 +147,7 @@ export function resolveDependencies( project?: ProjectContext, ) { // deep copy to not mutate caller's object - extras = ld.cloneDeep(extras); + extras = safeCloneDeep(extras); const lines: string[] = []; const afterBodyLines: string[] = []; diff --git a/src/command/render/pandoc-html.ts b/src/command/render/pandoc-html.ts index 4d162adb803..a6a2340a9b6 100644 --- a/src/command/render/pandoc-html.ts +++ b/src/command/render/pandoc-html.ts @@ -5,7 +5,7 @@ */ import { join } from "../../deno_ral/path.ts"; -import { cloneDeep, uniqBy } from "../../core/lodash.ts"; +import { uniqBy } from "../../core/lodash.ts"; import { Format, @@ -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"; @@ -40,6 +39,7 @@ import { md5HashBytes } from "../../core/hash.ts"; import { InternalError } from "../../core/lib/error.ts"; import { assert } from "testing/asserts"; import { safeModeFromFile } from "../../deno_ral/fs.ts"; +import { safeCloneDeep } from "../../core/safe-clone-deep.ts"; // The output target for a sass bundle // (controls the overall style tag that is emitted) @@ -55,7 +55,7 @@ export async function resolveSassBundles( format: Format, project: ProjectContext, ) { - extras = cloneDeep(extras); + extras = safeCloneDeep(extras); const mergedBundles: Record = {}; @@ -91,27 +91,35 @@ 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"), + ); + let foundBrand = { light: false, dark: false }; const bundles: SassBundle[] = bundlesWithBrand.filter((bundle) => bundle.key !== "brand" ).map((bundle) => { const userBrand = bundle.user?.findIndex((layer) => layer === "brand"); let cloned = false; if (userBrand && userBrand !== -1) { - bundle = cloneDeep(bundle); + bundle = safeCloneDeep(bundle); cloned = true; 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 = safeCloneDeep(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; @@ -122,18 +130,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), @@ -150,7 +159,7 @@ export async function resolveSassBundles( // Provide a dark bundle for this const darkBundles = bundles.map((bundle) => { - bundle = cloneDeep(bundle); + bundle = safeCloneDeep(bundle); bundle.user = bundle.dark?.user || bundle.user; bundle.quarto = bundle.dark?.quarto || bundle.quarto; bundle.framework = bundle.dark?.framework || bundle.framework; @@ -332,7 +341,7 @@ async function resolveQuartoSyntaxHighlighting( return extras; } - extras = cloneDeep(extras); + extras = safeCloneDeep(extras); // If we're using default highlighting, use theme darkness to select highlight style const mediaAttr = attribForThemeStyle(style); diff --git a/src/command/render/pandoc.ts b/src/command/render/pandoc.ts index 158e271402d..9562c74a3f5 100644 --- a/src/command/render/pandoc.ts +++ b/src/command/render/pandoc.ts @@ -207,6 +207,8 @@ import { isWindows } from "../../deno_ral/platform.ts"; import { appendToCombinedLuaProfile } from "../../core/performance/perfetto-utils.ts"; import { makeTimedFunctionAsync } from "../../core/performance/function-times.ts"; import { walkJson } from "../../core/json.ts"; +import { safeCloneDeep } from "../../core/safe-clone-deep.ts"; +import { assert } from "testing/asserts"; // in case we are running multiple pandoc processes // we need to make sure we capture all of the trace files @@ -330,7 +332,9 @@ export async function runPandoc( JSON.stringify(paramsJson), ); - const traceFilters = pandocMetadata?.["_quarto"]?.["trace-filters"] || + // deno-lint-ignore no-explicit-any + const traceFilters = + (pandocMetadata as any)?.["_quarto"]?.["trace-filters"] || Deno.env.get("QUARTO_TRACE_FILTERS"); if (traceFilters) { @@ -445,7 +449,7 @@ export async function runPandoc( // generate defaults and capture defaults to be printed let allDefaults = (await generateDefaults(options)) || {}; - let printAllDefaults = ld.cloneDeep(allDefaults) as FormatPandoc; + let printAllDefaults = safeCloneDeep(allDefaults) as FormatPandoc; // capture any filterParams in the FormatExtras const formatFilterParams = {} as Record; @@ -1127,7 +1131,7 @@ export async function runPandoc( // selectively overwrite some resolved metadata (e.g. ensure that metadata // computed from inline r expressions gets included @ the bottom). - const pandocMetadata = ld.cloneDeep(options.format.metadata || {}); + const pandocMetadata = safeCloneDeep(options.format.metadata || {}); for (const key of Object.keys(engineMetadata)) { const isChapterTitle = key === kTitle && projectIsBook(options.project); @@ -1161,6 +1165,7 @@ export async function runPandoc( dateFields.forEach((dateField) => { const date = pandocMetadata[dateField]; const format = pandocMetadata[kDateFormat]; + assert(format === undefined || typeof format === "string"); pandocMetadata[dateField] = resolveAndFormatDate( options.source, date, @@ -1180,15 +1185,19 @@ export async function runPandoc( // Expand citation dates into CSL dates const citationMetadata = pandocMetadata[kCitation]; if (citationMetadata) { + assert(typeof citationMetadata === "object"); + // ideally we should be asserting non-arrayness here but that's not very fast. + // assert(!Array.isArray(citationMetadata)); + const citationMetadataObj = citationMetadata as Record; const docCSLDate = dateRaw ? cslDate(resolveDate(options.source, dateRaw)) : undefined; const fields = ["issued", "available-date"]; fields.forEach((field) => { - if (citationMetadata[field]) { - citationMetadata[field] = cslDate(citationMetadata[field]); + if (citationMetadataObj[field]) { + citationMetadataObj[field] = cslDate(citationMetadataObj[field]); } else if (docCSLDate) { - citationMetadata[field] = docCSLDate; + citationMetadataObj[field] = docCSLDate; } }); } @@ -1228,6 +1237,7 @@ export async function runPandoc( !isBeamerOutput(options.format.pandoc) ) { const docClass = pandocMetadata[kDocumentClass]; + assert(typeof docClass === "string"); const isPrintDocumentClass = docClass && ["book", "scrbook"].includes(docClass); @@ -1281,7 +1291,7 @@ export async function runPandoc( prefix: "quarto-metadata", suffix: ".yml", }); - const pandocPassedMetadata = ld.cloneDeep(pandocMetadata); + const pandocPassedMetadata = safeCloneDeep(pandocMetadata); delete pandocPassedMetadata.format; delete pandocPassedMetadata.project; delete pandocPassedMetadata.website; @@ -1679,7 +1689,7 @@ function runPandocMessage( const keys = Object.keys(metadata); if (keys.length > 0) { - const printMetadata = ld.cloneDeep(metadata) as Metadata; + const printMetadata = safeCloneDeep(metadata) as Metadata; delete printMetadata.format; // print message diff --git a/src/command/render/render-contexts.ts b/src/command/render/render-contexts.ts index 0b3aec77acd..440ca8e1f9e 100644 --- a/src/command/render/render-contexts.ts +++ b/src/command/render/render-contexts.ts @@ -88,22 +88,8 @@ import { } from "../../core/pandoc/pandoc-formats.ts"; import { ExtensionContext } from "../../extension/types.ts"; import { NotebookContext } from "../../render/notebook/notebook-types.ts"; +import { safeCloneDeep } from "../../core/safe-clone-deep.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, @@ -230,7 +216,7 @@ export async function renderContexts( // we make it optional because some of the callers have // actually just cloned it themselves and don't need to preserve // the original - options = ld.cloneDeep(options) as RenderOptions; + options = safeCloneDeep(options) as RenderOptions; } const { engine, target } = await fileExecutionEngineAndTarget( @@ -357,8 +343,8 @@ function mergeQuartoConfigs( ...configs: Array ): Metadata { // copy all configs so we don't mutate them - config = ld.cloneDeep(config); - configs = ld.cloneDeep(configs); + config = safeCloneDeep(config); + configs = safeCloneDeep(configs); // bibliography needs to always be an array so it can be merged const fixupMergeableScalars = (metadata: Metadata) => { @@ -460,7 +446,7 @@ async function resolveFormats( // Remove any 'to' information that will force the // rendering to a particular format - options = ld.cloneDeep(options); + options = safeCloneDeep(options); delete options.flags?.to; } @@ -763,10 +749,10 @@ export async function projectMetadataForInputFile( projectType(project.config?.project?.[kProjectType]), project.dir, dirname(input), - ld.cloneDeep(project.config), + safeCloneDeep(project.config), ) as Metadata; } else { // Just return the config or empty metadata - return ld.cloneDeep(project.config) || {}; + return safeCloneDeep(project.config) || {}; } } diff --git a/src/command/render/render-files.ts b/src/command/render/render-files.ts index 316f6cc899a..98527326688 100644 --- a/src/command/render/render-files.ts +++ b/src/command/render/render-files.ts @@ -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, @@ -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, @@ -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) { diff --git a/src/config/types.ts b/src/config/types.ts index 87545267822..09daafe951d 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -457,7 +457,7 @@ export interface Format { export interface LightDarkBrand { [kLight]?: Brand; - [kDark]?: Brand + [kDark]?: Brand; } export interface FormatRender { diff --git a/src/resources/editor/tools/vs-code.mjs b/src/resources/editor/tools/vs-code.mjs index e1b259a61c0..d6dc203ad2d 100644 --- a/src/resources/editor/tools/vs-code.mjs +++ b/src/resources/editor/tools/vs-code.mjs @@ -8371,6 +8371,10 @@ var require_yaml_intelligence_resources = __commonJS({ } ] }, + { + id: "date-format", + schema: "string" + }, { id: "math-methods", enum: { @@ -12674,6 +12678,13 @@ var require_yaml_intelligence_resources = __commonJS({ }, description: "Document date" }, + { + name: "date-format", + schema: { + ref: "date-format" + }, + descript: "Date format for the document" + }, { name: "date-modified", tags: { @@ -22914,8 +22925,6 @@ var require_yaml_intelligence_resources = __commonJS({ "If true, force the presence of the OJS runtime. If\nfalse, force the absence instead. If unset, the OJS runtime\nis included only if OJS cells are present in the document.", "Use the specified file as a style reference in producing a docx,\npptx, or odt file.", "Branding information to use for this document. If a string, the path\nto a brand file. If false, don\u2019t use branding on this document. If an\nobject, an inline brand definition, or an object with light and dark\nbrand paths or definitions.", - "The path to a light brand file or an inline light brand\ndefinition.", - "The path to a dark brand file or an inline dark brand definition.", "Theme name, theme scss file, or a mix of both.", "The light theme name, theme scss file, or a mix of both.", "The light theme name, theme scss file, or a mix of both.", @@ -24248,12 +24257,12 @@ var require_yaml_intelligence_resources = __commonJS({ mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 196120, + _internalId: 194255, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 196112, + _internalId: 194247, type: "enum", enum: [ "png", @@ -24269,7 +24278,7 @@ var require_yaml_intelligence_resources = __commonJS({ exhaustiveCompletions: true }, theme: { - _internalId: 196119, + _internalId: 194254, type: "anyOf", anyOf: [ { diff --git a/src/resources/editor/tools/yaml/web-worker.js b/src/resources/editor/tools/yaml/web-worker.js index d666a0a15db..6286fef419c 100644 --- a/src/resources/editor/tools/yaml/web-worker.js +++ b/src/resources/editor/tools/yaml/web-worker.js @@ -8372,6 +8372,10 @@ try { } ] }, + { + id: "date-format", + schema: "string" + }, { id: "math-methods", enum: { @@ -12675,6 +12679,13 @@ try { }, description: "Document date" }, + { + name: "date-format", + schema: { + ref: "date-format" + }, + descript: "Date format for the document" + }, { name: "date-modified", tags: { @@ -22915,8 +22926,6 @@ try { "If true, force the presence of the OJS runtime. If\nfalse, force the absence instead. If unset, the OJS runtime\nis included only if OJS cells are present in the document.", "Use the specified file as a style reference in producing a docx,\npptx, or odt file.", "Branding information to use for this document. If a string, the path\nto a brand file. If false, don\u2019t use branding on this document. If an\nobject, an inline brand definition, or an object with light and dark\nbrand paths or definitions.", - "The path to a light brand file or an inline light brand\ndefinition.", - "The path to a dark brand file or an inline dark brand definition.", "Theme name, theme scss file, or a mix of both.", "The light theme name, theme scss file, or a mix of both.", "The light theme name, theme scss file, or a mix of both.", @@ -24249,12 +24258,12 @@ try { mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 196120, + _internalId: 194255, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 196112, + _internalId: 194247, type: "enum", enum: [ "png", @@ -24270,7 +24279,7 @@ try { exhaustiveCompletions: true }, theme: { - _internalId: 196119, + _internalId: 194254, type: "anyOf", anyOf: [ { diff --git a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json index 663646e453a..98954d29bbf 100644 --- a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json +++ b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json @@ -1343,6 +1343,10 @@ } ] }, + { + "id": "date-format", + "schema": "string" + }, { "id": "math-methods", "enum": { @@ -5646,6 +5650,13 @@ }, "description": "Document date" }, + { + "name": "date-format", + "schema": { + "ref": "date-format" + }, + "descript": "Date format for the document" + }, { "name": "date-modified", "tags": { @@ -15886,8 +15897,6 @@ "If true, force the presence of the OJS runtime. If\nfalse, force the absence instead. If unset, the OJS runtime\nis included only if OJS cells are present in the document.", "Use the specified file as a style reference in producing a docx,\npptx, or odt file.", "Branding information to use for this document. If a string, the path\nto a brand file. If false, don’t use branding on this document. If an\nobject, an inline brand definition, or an object with light and dark\nbrand paths or definitions.", - "The path to a light brand file or an inline light brand\ndefinition.", - "The path to a dark brand file or an inline dark brand definition.", "Theme name, theme scss file, or a mix of both.", "The light theme name, theme scss file, or a mix of both.", "The light theme name, theme scss file, or a mix of both.", @@ -17220,12 +17229,12 @@ "mermaid": "%%" }, "handlers/mermaid/schema.yml": { - "_internalId": 196120, + "_internalId": 194255, "type": "object", "description": "be an object", "properties": { "mermaid-format": { - "_internalId": 196112, + "_internalId": 194247, "type": "enum", "enum": [ "png", @@ -17241,7 +17250,7 @@ "exhaustiveCompletions": true }, "theme": { - "_internalId": 196119, + "_internalId": 194254, "type": "anyOf", "anyOf": [ { diff --git a/src/resources/schema/definitions.yml b/src/resources/schema/definitions.yml index 6fe40176b5c..b8ff2030bf6 100644 --- a/src/resources/schema/definitions.yml +++ b/src/resources/schema/definitions.yml @@ -7,6 +7,9 @@ format: string required: [value] +- id: date-format + schema: string + - id: math-methods enum: values: [plain, webtex, gladtex, mathml, mathjax, katex] diff --git a/src/resources/schema/document-attributes.yml b/src/resources/schema/document-attributes.yml index aa9fe60eb93..7ad303d78ea 100644 --- a/src/resources/schema/document-attributes.yml +++ b/src/resources/schema/document-attributes.yml @@ -13,6 +13,11 @@ ref: date description: "Document date" +- name: date-format + schema: + ref: date-format + descript: "Date format for the document" + - name: date-modified tags: formats: [$html-doc] diff --git a/src/resources/schema/json-schemas.json b/src/resources/schema/json-schemas.json index 06040e4dcaf..a9c885cdd8e 100644 --- a/src/resources/schema/json-schemas.json +++ b/src/resources/schema/json-schemas.json @@ -20,6 +20,9 @@ } ] }, + "DateFormat": { + "type": "string" + }, "MathMethods": { "enum": { "values": [ diff --git a/src/resources/types/schema-types.ts b/src/resources/types/schema-types.ts index 1d95382ed38..7325fb886f1 100644 --- a/src/resources/types/schema-types.ts +++ b/src/resources/types/schema-types.ts @@ -11,6 +11,8 @@ export type JsonObject = { [key: string]: unknown }; export type Date = string | { format?: string; value: string }; +export type DateFormat = string; + export type MathMethods = | "plain" | "webtex" From d2ea782ba4ad6ee1b36b9b0d17eed84844f01989 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Wed, 26 Mar 2025 15:46:24 -0400 Subject: [PATCH 2/2] remove redundant type casts --- src/command/render/pandoc.ts | 4 ++-- src/command/render/render-contexts.ts | 2 +- src/render/notebook/notebook-contributor-html.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/command/render/pandoc.ts b/src/command/render/pandoc.ts index 9562c74a3f5..03176154440 100644 --- a/src/command/render/pandoc.ts +++ b/src/command/render/pandoc.ts @@ -449,7 +449,7 @@ export async function runPandoc( // generate defaults and capture defaults to be printed let allDefaults = (await generateDefaults(options)) || {}; - let printAllDefaults = safeCloneDeep(allDefaults) as FormatPandoc; + let printAllDefaults = safeCloneDeep(allDefaults); // capture any filterParams in the FormatExtras const formatFilterParams = {} as Record; @@ -1689,7 +1689,7 @@ function runPandocMessage( const keys = Object.keys(metadata); if (keys.length > 0) { - const printMetadata = safeCloneDeep(metadata) as Metadata; + const printMetadata = safeCloneDeep(metadata); delete printMetadata.format; // print message diff --git a/src/command/render/render-contexts.ts b/src/command/render/render-contexts.ts index 440ca8e1f9e..be807267c9a 100644 --- a/src/command/render/render-contexts.ts +++ b/src/command/render/render-contexts.ts @@ -216,7 +216,7 @@ export async function renderContexts( // we make it optional because some of the callers have // actually just cloned it themselves and don't need to preserve // the original - options = safeCloneDeep(options) as RenderOptions; + options = safeCloneDeep(options); } const { engine, target } = await fileExecutionEngineAndTarget( diff --git a/src/render/notebook/notebook-contributor-html.ts b/src/render/notebook/notebook-contributor-html.ts index 2505c52d29d..8bc20433d8e 100644 --- a/src/render/notebook/notebook-contributor-html.ts +++ b/src/render/notebook/notebook-contributor-html.ts @@ -84,7 +84,7 @@ function resolveHtmlNotebook( executedFile: ExecutedFile, notebookMetadata?: NotebookMetadata, ) { - const resolved = safeCloneDeep(executedFile) as ExecutedFile; + const resolved = safeCloneDeep(executedFile); // Set the output file resolved.recipe.format.pandoc[kOutputFile] = `${outputFile(nbAbsPath)}`;