Skip to content

Commit 1490a98

Browse files
committed
use safeCloneDeep instead of cloneDeep, update schemas etc
1 parent 2c8963a commit 1490a98

File tree

13 files changed

+115
-68
lines changed

13 files changed

+115
-68
lines changed

src/command/render/pandoc-dependencies-html.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { ensureDirSync } from "../../deno_ral/fs.ts";
3535
import { ProjectContext } from "../../project/types.ts";
3636
import { projectOutputDir } from "../../project/project-shared.ts";
3737
import { insecureHash } from "../../core/hash.ts";
38+
import { safeCloneDeep } from "../../core/safe-clone-deep.ts";
3839

3940
export async function writeDependencies(
4041
dependenciesFile: string,
@@ -146,7 +147,7 @@ export function resolveDependencies(
146147
project?: ProjectContext,
147148
) {
148149
// deep copy to not mutate caller's object
149-
extras = ld.cloneDeep(extras);
150+
extras = safeCloneDeep(extras);
150151

151152
const lines: string[] = [];
152153
const afterBodyLines: string[] = [];

src/command/render/pandoc-html.ts

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import { join } from "../../deno_ral/path.ts";
8-
import { cloneDeep, uniqBy } from "../../core/lodash.ts";
8+
import { uniqBy } from "../../core/lodash.ts";
99

1010
import {
1111
Format,
@@ -19,7 +19,6 @@ import {
1919
} from "../../config/types.ts";
2020
import { ProjectContext } from "../../project/types.ts";
2121

22-
import { TempContext } from "../../core/temp.ts";
2322
import { cssImports, cssResources } from "../../core/css.ts";
2423
import { cleanSourceMappingUrl, compileSass } from "../../core/sass.ts";
2524

@@ -40,6 +39,7 @@ import { md5HashBytes } from "../../core/hash.ts";
4039
import { InternalError } from "../../core/lib/error.ts";
4140
import { assert } from "testing/asserts";
4241
import { safeModeFromFile } from "../../deno_ral/fs.ts";
42+
import { safeCloneDeep } from "../../core/safe-clone-deep.ts";
4343

4444
// The output target for a sass bundle
4545
// (controls the overall style tag that is emitted)
@@ -55,7 +55,7 @@ export async function resolveSassBundles(
5555
format: Format,
5656
project: ProjectContext,
5757
) {
58-
extras = cloneDeep(extras);
58+
extras = safeCloneDeep(extras);
5959

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

@@ -91,27 +91,35 @@ export async function resolveSassBundles(
9191
const maybeBrandBundle = bundlesWithBrand.find((bundle) =>
9292
bundle.key === "brand"
9393
);
94-
assert(!maybeBrandBundle ||
95-
!maybeBrandBundle.user?.find((v) => v === "brand") &&
96-
!maybeBrandBundle.dark?.user?.find((v) => v === "brand"));
97-
let foundBrand = {light: false, dark: false};
94+
assert(
95+
!maybeBrandBundle ||
96+
!maybeBrandBundle.user?.find((v) => v === "brand") &&
97+
!maybeBrandBundle.dark?.user?.find((v) => v === "brand"),
98+
);
99+
let foundBrand = { light: false, dark: false };
98100
const bundles: SassBundle[] = bundlesWithBrand.filter((bundle) =>
99101
bundle.key !== "brand"
100102
).map((bundle) => {
101103
const userBrand = bundle.user?.findIndex((layer) => layer === "brand");
102104
let cloned = false;
103105
if (userBrand && userBrand !== -1) {
104-
bundle = cloneDeep(bundle);
106+
bundle = safeCloneDeep(bundle);
105107
cloned = true;
106108
bundle.user!.splice(userBrand, 1, ...(maybeBrandBundle?.user || []));
107109
foundBrand.light = true;
108110
}
109-
const darkBrand = bundle.dark?.user?.findIndex((layer) => layer === "brand");
111+
const darkBrand = bundle.dark?.user?.findIndex((layer) =>
112+
layer === "brand"
113+
);
110114
if (darkBrand && darkBrand !== -1) {
111115
if (!cloned) {
112-
bundle = cloneDeep(bundle);
116+
bundle = safeCloneDeep(bundle);
113117
}
114-
bundle.dark!.user!.splice(darkBrand, 1, ...(maybeBrandBundle?.dark?.user || []))
118+
bundle.dark!.user!.splice(
119+
darkBrand,
120+
1,
121+
...(maybeBrandBundle?.dark?.user || []),
122+
);
115123
foundBrand.dark = true;
116124
}
117125
return bundle as SassBundle;
@@ -122,18 +130,19 @@ export async function resolveSassBundles(
122130
key: "brand",
123131
user: !foundBrand.light && maybeBrandBundle?.user as SassLayer[] || [],
124132
dark: !foundBrand.dark && maybeBrandBundle?.dark?.user && {
125-
user: maybeBrandBundle.dark.user as SassLayer[],
126-
default: maybeBrandBundle.dark.default
127-
} || undefined
133+
user: maybeBrandBundle.dark.user as SassLayer[],
134+
default: maybeBrandBundle.dark.default,
135+
} || undefined,
128136
});
129137
}
130138

131139
// See if any bundles are providing dark specific css
132140
const hasDark = bundles.some((bundle) => bundle.dark !== undefined);
133-
defaultStyle =
134-
bundles.some((bundle) => bundle.dark !== undefined && bundle.dark.default)
135-
? "dark"
136-
: "light";
141+
defaultStyle = bundles.some((bundle) =>
142+
bundle.dark !== undefined && bundle.dark.default
143+
)
144+
? "dark"
145+
: "light";
137146
const targets: SassTarget[] = [{
138147
name: `${dependency}.min.css`,
139148
bundles: (bundles as any),
@@ -150,7 +159,7 @@ export async function resolveSassBundles(
150159

151160
// Provide a dark bundle for this
152161
const darkBundles = bundles.map((bundle) => {
153-
bundle = cloneDeep(bundle);
162+
bundle = safeCloneDeep(bundle);
154163
bundle.user = bundle.dark?.user || bundle.user;
155164
bundle.quarto = bundle.dark?.quarto || bundle.quarto;
156165
bundle.framework = bundle.dark?.framework || bundle.framework;
@@ -332,7 +341,7 @@ async function resolveQuartoSyntaxHighlighting(
332341
return extras;
333342
}
334343

335-
extras = cloneDeep(extras);
344+
extras = safeCloneDeep(extras);
336345

337346
// If we're using default highlighting, use theme darkness to select highlight style
338347
const mediaAttr = attribForThemeStyle(style);

src/command/render/pandoc.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ import { isWindows } from "../../deno_ral/platform.ts";
207207
import { appendToCombinedLuaProfile } from "../../core/performance/perfetto-utils.ts";
208208
import { makeTimedFunctionAsync } from "../../core/performance/function-times.ts";
209209
import { walkJson } from "../../core/json.ts";
210+
import { safeCloneDeep } from "../../core/safe-clone-deep.ts";
211+
import { assert } from "testing/asserts";
210212

211213
// in case we are running multiple pandoc processes
212214
// we need to make sure we capture all of the trace files
@@ -330,7 +332,9 @@ export async function runPandoc(
330332
JSON.stringify(paramsJson),
331333
);
332334

333-
const traceFilters = pandocMetadata?.["_quarto"]?.["trace-filters"] ||
335+
// deno-lint-ignore no-explicit-any
336+
const traceFilters =
337+
(pandocMetadata as any)?.["_quarto"]?.["trace-filters"] ||
334338
Deno.env.get("QUARTO_TRACE_FILTERS");
335339

336340
if (traceFilters) {
@@ -445,7 +449,7 @@ export async function runPandoc(
445449

446450
// generate defaults and capture defaults to be printed
447451
let allDefaults = (await generateDefaults(options)) || {};
448-
let printAllDefaults = ld.cloneDeep(allDefaults) as FormatPandoc;
452+
let printAllDefaults = safeCloneDeep(allDefaults) as FormatPandoc;
449453

450454
// capture any filterParams in the FormatExtras
451455
const formatFilterParams = {} as Record<string, unknown>;
@@ -1127,7 +1131,7 @@ export async function runPandoc(
11271131

11281132
// selectively overwrite some resolved metadata (e.g. ensure that metadata
11291133
// computed from inline r expressions gets included @ the bottom).
1130-
const pandocMetadata = ld.cloneDeep(options.format.metadata || {});
1134+
const pandocMetadata = safeCloneDeep(options.format.metadata || {});
11311135
for (const key of Object.keys(engineMetadata)) {
11321136
const isChapterTitle = key === kTitle && projectIsBook(options.project);
11331137

@@ -1161,6 +1165,7 @@ export async function runPandoc(
11611165
dateFields.forEach((dateField) => {
11621166
const date = pandocMetadata[dateField];
11631167
const format = pandocMetadata[kDateFormat];
1168+
assert(format === undefined || typeof format === "string");
11641169
pandocMetadata[dateField] = resolveAndFormatDate(
11651170
options.source,
11661171
date,
@@ -1180,15 +1185,19 @@ export async function runPandoc(
11801185
// Expand citation dates into CSL dates
11811186
const citationMetadata = pandocMetadata[kCitation];
11821187
if (citationMetadata) {
1188+
assert(typeof citationMetadata === "object");
1189+
// ideally we should be asserting non-arrayness here but that's not very fast.
1190+
// assert(!Array.isArray(citationMetadata));
1191+
const citationMetadataObj = citationMetadata as Record<string, unknown>;
11831192
const docCSLDate = dateRaw
11841193
? cslDate(resolveDate(options.source, dateRaw))
11851194
: undefined;
11861195
const fields = ["issued", "available-date"];
11871196
fields.forEach((field) => {
1188-
if (citationMetadata[field]) {
1189-
citationMetadata[field] = cslDate(citationMetadata[field]);
1197+
if (citationMetadataObj[field]) {
1198+
citationMetadataObj[field] = cslDate(citationMetadataObj[field]);
11901199
} else if (docCSLDate) {
1191-
citationMetadata[field] = docCSLDate;
1200+
citationMetadataObj[field] = docCSLDate;
11921201
}
11931202
});
11941203
}
@@ -1228,6 +1237,7 @@ export async function runPandoc(
12281237
!isBeamerOutput(options.format.pandoc)
12291238
) {
12301239
const docClass = pandocMetadata[kDocumentClass];
1240+
assert(typeof docClass === "string");
12311241
const isPrintDocumentClass = docClass &&
12321242
["book", "scrbook"].includes(docClass);
12331243

@@ -1281,7 +1291,7 @@ export async function runPandoc(
12811291
prefix: "quarto-metadata",
12821292
suffix: ".yml",
12831293
});
1284-
const pandocPassedMetadata = ld.cloneDeep(pandocMetadata);
1294+
const pandocPassedMetadata = safeCloneDeep(pandocMetadata);
12851295
delete pandocPassedMetadata.format;
12861296
delete pandocPassedMetadata.project;
12871297
delete pandocPassedMetadata.website;
@@ -1679,7 +1689,7 @@ function runPandocMessage(
16791689

16801690
const keys = Object.keys(metadata);
16811691
if (keys.length > 0) {
1682-
const printMetadata = ld.cloneDeep(metadata) as Metadata;
1692+
const printMetadata = safeCloneDeep(metadata) as Metadata;
16831693
delete printMetadata.format;
16841694

16851695
// print message

src/command/render/render-contexts.ts

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -88,22 +88,8 @@ import {
8888
} from "../../core/pandoc/pandoc-formats.ts";
8989
import { ExtensionContext } from "../../extension/types.ts";
9090
import { NotebookContext } from "../../render/notebook/notebook-types.ts";
91+
import { safeCloneDeep } from "../../core/safe-clone-deep.ts";
9192

92-
// we can't naively ld.cloneDeep everything
93-
// because that destroys class instances
94-
// with private members
95-
//
96-
// Currently, that's ProjectContext.
97-
//
98-
// TODO: Ideally, we shouldn't be copying the RenderContext at all.
99-
export function copyRenderContext(
100-
context: RenderContext,
101-
): RenderContext {
102-
return {
103-
...ld.cloneDeep(context),
104-
project: context.project,
105-
};
106-
}
10793
export async function resolveFormatsFromMetadata(
10894
metadata: Metadata,
10995
input: string,
@@ -230,7 +216,7 @@ export async function renderContexts(
230216
// we make it optional because some of the callers have
231217
// actually just cloned it themselves and don't need to preserve
232218
// the original
233-
options = ld.cloneDeep(options) as RenderOptions;
219+
options = safeCloneDeep(options) as RenderOptions;
234220
}
235221

236222
const { engine, target } = await fileExecutionEngineAndTarget(
@@ -357,8 +343,8 @@ function mergeQuartoConfigs(
357343
...configs: Array<Metadata>
358344
): Metadata {
359345
// copy all configs so we don't mutate them
360-
config = ld.cloneDeep(config);
361-
configs = ld.cloneDeep(configs);
346+
config = safeCloneDeep(config);
347+
configs = safeCloneDeep(configs);
362348

363349
// bibliography needs to always be an array so it can be merged
364350
const fixupMergeableScalars = (metadata: Metadata) => {
@@ -460,7 +446,7 @@ async function resolveFormats(
460446

461447
// Remove any 'to' information that will force the
462448
// rendering to a particular format
463-
options = ld.cloneDeep(options);
449+
options = safeCloneDeep(options);
464450
delete options.flags?.to;
465451
}
466452

@@ -763,10 +749,10 @@ export async function projectMetadataForInputFile(
763749
projectType(project.config?.project?.[kProjectType]),
764750
project.dir,
765751
dirname(input),
766-
ld.cloneDeep(project.config),
752+
safeCloneDeep(project.config),
767753
) as Metadata;
768754
} else {
769755
// Just return the config or empty metadata
770-
return ld.cloneDeep(project.config) || {};
756+
return safeCloneDeep(project.config) || {};
771757
}
772758
}

src/command/render/render-files.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import { outputRecipe } from "./output.ts";
4848

4949
import { renderPandoc } from "./render.ts";
5050
import { PandocRenderCompletion, RenderServices } from "./types.ts";
51-
import { copyRenderContext, renderContexts } from "./render-contexts.ts";
51+
import { renderContexts } from "./render-contexts.ts";
5252
import { renderProgress } from "./render-info.ts";
5353
import {
5454
ExecutedFile,
@@ -114,6 +114,7 @@ import {
114114
} from "../../project/project-shared.ts";
115115
import { NotebookContext } from "../../render/notebook/notebook-types.ts";
116116
import { setExecuteEnvironment } from "../../execute/environment.ts";
117+
import { safeCloneDeep } from "../../core/safe-clone-deep.ts";
117118

118119
export async function renderExecute(
119120
context: RenderContext,
@@ -503,7 +504,7 @@ async function renderFileInternal(
503504

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

508509
// disquality some documents from server: shiny
509510
if (isServerShiny(context.format) && context.project) {

src/config/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ export interface Format {
457457

458458
export interface LightDarkBrand {
459459
[kLight]?: Brand;
460-
[kDark]?: Brand
460+
[kDark]?: Brand;
461461
}
462462

463463
export interface FormatRender {

src/resources/editor/tools/vs-code.mjs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8371,6 +8371,10 @@ var require_yaml_intelligence_resources = __commonJS({
83718371
}
83728372
]
83738373
},
8374+
{
8375+
id: "date-format",
8376+
schema: "string"
8377+
},
83748378
{
83758379
id: "math-methods",
83768380
enum: {
@@ -12674,6 +12678,13 @@ var require_yaml_intelligence_resources = __commonJS({
1267412678
},
1267512679
description: "Document date"
1267612680
},
12681+
{
12682+
name: "date-format",
12683+
schema: {
12684+
ref: "date-format"
12685+
},
12686+
descript: "Date format for the document"
12687+
},
1267712688
{
1267812689
name: "date-modified",
1267912690
tags: {
@@ -22914,8 +22925,6 @@ var require_yaml_intelligence_resources = __commonJS({
2291422925
"If <code>true</code>, force the presence of the OJS runtime. If\n<code>false</code>, force the absence instead. If unset, the OJS runtime\nis included only if OJS cells are present in the document.",
2291522926
"Use the specified file as a style reference in producing a docx,\npptx, or odt file.",
2291622927
"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.",
22917-
"The path to a light brand file or an inline light brand\ndefinition.",
22918-
"The path to a dark brand file or an inline dark brand definition.",
2291922928
"Theme name, theme scss file, or a mix of both.",
2292022929
"The light theme name, theme scss file, or a mix of both.",
2292122930
"The light theme name, theme scss file, or a mix of both.",
@@ -24248,12 +24257,12 @@ var require_yaml_intelligence_resources = __commonJS({
2424824257
mermaid: "%%"
2424924258
},
2425024259
"handlers/mermaid/schema.yml": {
24251-
_internalId: 196120,
24260+
_internalId: 194255,
2425224261
type: "object",
2425324262
description: "be an object",
2425424263
properties: {
2425524264
"mermaid-format": {
24256-
_internalId: 196112,
24265+
_internalId: 194247,
2425724266
type: "enum",
2425824267
enum: [
2425924268
"png",
@@ -24269,7 +24278,7 @@ var require_yaml_intelligence_resources = __commonJS({
2426924278
exhaustiveCompletions: true
2427024279
},
2427124280
theme: {
24272-
_internalId: 196119,
24281+
_internalId: 194254,
2427324282
type: "anyOf",
2427424283
anyOf: [
2427524284
{

0 commit comments

Comments
 (0)