Skip to content

Commit e734132

Browse files
authored
Merge pull request #11821 from quarto-dev/feature/7260
tabsets - support `.active`
2 parents 8ad252e + ca10597 commit e734132

File tree

14 files changed

+481
-169
lines changed

14 files changed

+481
-169
lines changed

news/changelog-1.7.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ All changes included in 1.7:
4747

4848
## Other Fixes and Improvements
4949

50+
- ([#7260](https://github.com/quarto-dev/quarto-cli/issues/7260)): Add support for `active` class in tabsets so the `.active` tab shows up by default.
5051
- ([#8613](https://github.com/quarto-dev/quarto-cli/issues/8613)): Fix `giscus` color on load to support dark mode (by @kv9898).
5152
- ([#11441](https://github.com/quarto-dev/quarto-cli/issues/11441)): Don't add newlines around shortcodes during processing.
5253
- ([#11643](https://github.com/quarto-dev/quarto-cli/issues/11643)): Improve highlighting of nested code block inside markdown code block, i.e. using ` ```{{python}} ` or ` ```python ` inside ` ````markdown` fenced code block.

src/command/build-js/cmd.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,21 @@
66

77
import { Command } from "cliffy/command/mod.ts";
88

9-
import { esbuildCompile } from "../../core/esbuild.ts";
9+
import {
10+
ESBuildAnalysis,
11+
esbuildAnalyze,
12+
esbuildCompile,
13+
} from "../../core/esbuild.ts";
1014
import { buildIntelligenceResources } from "../../core/schema/build-schema-file.ts";
11-
import { resourcePath } from "../../core/resources.ts";
15+
import { formatResourcePath, resourcePath } from "../../core/resources.ts";
1216
import { simple } from "acorn/walk";
1317
import { Parser } from "acorn/acorn";
1418
import classFields from "acorn-class-fields";
1519
import { initYamlIntelligenceResourcesFromFilesystem } from "../../core/schema/utils.ts";
1620

1721
// initialize language handlers
1822
import "../../core/handlers/handlers.ts";
23+
import { join } from "../../deno_ral/path.ts";
1924

2025
function ensureAllowableIDESyntax(src: string, filename: string) {
2126
const ast = Parser.extend(classFields).parse(src, {
@@ -126,11 +131,29 @@ async function buildYAMLJS() {
126131
);
127132
}
128133

134+
async function buildEsbuildAnalysisCache() {
135+
// build the necessary esbuild analysis cache
136+
const inputFiles = [
137+
"quarto.js",
138+
];
139+
const analysisCache: Record<string, ESBuildAnalysis> = {};
140+
for (const file of inputFiles) {
141+
analysisCache[file] = await esbuildAnalyze(
142+
formatResourcePath("html", file),
143+
resourcePath(join("formats", "html")),
144+
);
145+
}
146+
Deno.writeTextFileSync(
147+
formatResourcePath("html", "esbuild-analysis-cache.json"),
148+
JSON.stringify(analysisCache, null, 2),
149+
);
150+
}
151+
129152
export async function buildAssets() {
130153
// this has to come first because buildYAMLJS depends on it.
131154
await buildIntelligenceResources();
132-
133155
await buildYAMLJS();
156+
await buildEsbuildAnalysisCache();
134157
}
135158

136159
export const buildJsCommand = new Command()

src/core/esbuild.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,68 @@
44
* Copyright (C) 2021-2022 Posit Software, PBC
55
*/
66

7+
import { assert } from "testing/asserts";
78
import { execProcess } from "./process.ts";
89
import { architectureToolsPath } from "./resources.ts";
10+
import { TempContext } from "./temp-types.ts";
11+
import { createTempContext } from "./temp.ts";
12+
import { nullDevice } from "./platform.ts";
13+
14+
type ESBuildAnalysisImport = {
15+
path: string;
16+
kind: string;
17+
external: boolean;
18+
};
19+
20+
type ESBuildOutputValue = {
21+
imports: ESBuildAnalysisImport[];
22+
entryPoint: string;
23+
inputs: Record<string, { bytesInOutput: number }>;
24+
bytes: number;
25+
};
26+
27+
export type ESBuildAnalysis = {
28+
inputs: Record<string, { bytes: number; format: string }>;
29+
outputs: Record<string, ESBuildOutputValue>;
30+
};
31+
32+
export async function esbuildAnalyze(
33+
input: string,
34+
workingDir: string,
35+
tempContext?: TempContext,
36+
): Promise<ESBuildAnalysis> {
37+
let mustCleanup = false;
38+
if (!tempContext) {
39+
tempContext = createTempContext();
40+
mustCleanup = true;
41+
}
42+
43+
try {
44+
const tempName = tempContext.createFile({ suffix: ".json" });
45+
await esbuildCommand(
46+
[
47+
"--analyze=verbose",
48+
`--metafile=${tempName}`,
49+
`--outfile=${nullDevice()}`,
50+
input,
51+
],
52+
"",
53+
workingDir,
54+
);
55+
const result = JSON.parse(
56+
Deno.readTextFileSync(tempName),
57+
) as ESBuildAnalysis;
58+
assert(Object.entries(result.outputs).length === 1);
59+
result.outputs = {
60+
"<output>": Object.values(result.outputs)[0],
61+
};
62+
return result;
63+
} finally {
64+
if (mustCleanup) {
65+
tempContext.cleanup();
66+
}
67+
}
68+
}
969

1070
export async function esbuildCompile(
1171
input: string,
@@ -38,6 +98,7 @@ export async function esbuildCommand(
3898
cmd,
3999
cwd: workingDir,
40100
stdout: "piped",
101+
stderr: "piped",
41102
},
42103
input,
43104
);

src/core/platform.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,7 @@ export function isInteractiveSession() {
9797
export function isGithubAction() {
9898
return Deno.env.get("GITHUB_ACTIONS") === "true";
9999
}
100+
101+
export function nullDevice() {
102+
return isWindows ? "NUL" : "/dev/null";
103+
}

src/format/html/format-html.ts

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* Copyright (C) 2020-2022 Posit Software, PBC
77
*/
8-
import { join } from "../../deno_ral/path.ts";
8+
import { dirname, join, relative } from "../../deno_ral/path.ts";
99
import { warning } from "../../deno_ral/log.ts";
1010

1111
import * as ld from "../../core/lodash.ts";
@@ -14,7 +14,7 @@ import { Document, Element } from "../../core/deno-dom.ts";
1414

1515
import { renderEjs } from "../../core/ejs.ts";
1616
import { mergeConfigs } from "../../core/config.ts";
17-
import { formatResourcePath } from "../../core/resources.ts";
17+
import { formatResourcePath, resourcePath } from "../../core/resources.ts";
1818
import { TempContext } from "../../core/temp.ts";
1919
import { asCssSize } from "../../core/css.ts";
2020

@@ -114,6 +114,53 @@ import {
114114
import { kQuartoHtmlDependency } from "./format-html-constants.ts";
115115
import { registerWriterFormatHandler } from "../format-handlers.ts";
116116
import { brandSassFormatExtras } from "../../core/sass/brand.ts";
117+
import { ESBuildAnalysis, esbuildAnalyze } from "../../core/esbuild.ts";
118+
import { assert } from "testing/asserts";
119+
120+
let esbuildAnalysisCache: Record<string, ESBuildAnalysis> | undefined;
121+
export function esbuildCachedAnalysis(
122+
input: string,
123+
): ESBuildAnalysis {
124+
if (!esbuildAnalysisCache) {
125+
esbuildAnalysisCache = JSON.parse(
126+
Deno.readTextFileSync(
127+
formatResourcePath("html", "esbuild-analysis-cache.json"),
128+
),
129+
) as Record<string, ESBuildAnalysis>;
130+
}
131+
const result = esbuildAnalysisCache[input];
132+
assert(result, `Cached analysis not found for ${input}`);
133+
return result;
134+
}
135+
136+
function recursiveModuleDependencies(
137+
path: string,
138+
): DependencyHtmlFile[] {
139+
const result: DependencyHtmlFile[] = [];
140+
const inpRelPath = relative(join(resourcePath("formats"), "html"), path);
141+
142+
result.push({
143+
name: inpRelPath,
144+
path: formatResourcePath("html", inpRelPath),
145+
attribs: { type: "module" },
146+
});
147+
148+
const analysis = esbuildCachedAnalysis(inpRelPath);
149+
// console.log(JSON.stringify(analysis, null, 2));
150+
for (const [_key, value] of Object.entries(analysis.outputs)) {
151+
for (const imp of value.imports) {
152+
if (imp.external) {
153+
const relPath = relative(path, join(path, imp.path));
154+
result.push({
155+
name: relPath,
156+
path: formatResourcePath("html", relPath),
157+
attribs: { type: "module" },
158+
});
159+
}
160+
}
161+
}
162+
return result;
163+
}
117164

118165
export function htmlFormat(
119166
figwidth: number,
@@ -313,10 +360,10 @@ export async function htmlFormatExtras(
313360

314361
// quarto.js helpers
315362
if (bootstrap) {
316-
scripts.push({
317-
name: "quarto.js",
318-
path: formatResourcePath("html", "quarto.js"),
319-
});
363+
const deps = recursiveModuleDependencies(
364+
formatResourcePath("html", "quarto.js"),
365+
);
366+
scripts.push(...deps);
320367
}
321368

322369
// tabby if required

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

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21651,7 +21651,7 @@ var require_yaml_intelligence_resources = __commonJS({
2165121651
"Short/abbreviated form of container-title;",
2165221652
"A minor contributor to the item; typically cited using \u201Cwith\u201D before\nthe name when listed in a bibliography.",
2165321653
"Curator of an exhibit or collection (e.g.&nbsp;in a museum).",
21654-
"Physical (e.g.&nbsp;size) or temporal (e.g.&nbsp;running time) dimensions of\nthe item.",
21654+
"Physical (e.g.&nbsp;size) or temporal (e.g.\uFFFD\uFFFDrunning time) dimensions of\nthe item.",
2165521655
"Director (e.g.&nbsp;of a film).",
2165621656
"Minor subdivision of a court with a <code>jurisdiction</code> for a\nlegal item",
2165721657
"(Container) edition holding the item (e.g.&nbsp;\u201C3\u201D when citing a chapter\nin the third edition of a book).",
@@ -23616,6 +23616,14 @@ var require_yaml_intelligence_resources = __commonJS({
2361623616
"Disambiguating year suffix in author-date styles (e.g.&nbsp;\u201Ca\u201D in \u201CDoe,\n1999a\u201D).",
2361723617
"Manuscript configuration",
2361823618
"internal-schema-hack",
23619+
{
23620+
short: "Include an automatically generated table of contents",
23621+
long: ""
23622+
},
23623+
{
23624+
short: "Use smart quotes in document output. Defaults to true.",
23625+
long: ""
23626+
},
2361923627
"Project configuration.",
2362023628
"Project type (<code>default</code>, <code>website</code>,\n<code>book</code>, or <code>manuscript</code>)",
2362123629
"Files to render (defaults to all files)",
@@ -24188,12 +24196,12 @@ var require_yaml_intelligence_resources = __commonJS({
2418824196
mermaid: "%%"
2418924197
},
2419024198
"handlers/mermaid/schema.yml": {
24191-
_internalId: 193535,
24199+
_internalId: 194269,
2419224200
type: "object",
2419324201
description: "be an object",
2419424202
properties: {
2419524203
"mermaid-format": {
24196-
_internalId: 193527,
24204+
_internalId: 194261,
2419724205
type: "enum",
2419824206
enum: [
2419924207
"png",
@@ -24209,7 +24217,7 @@ var require_yaml_intelligence_resources = __commonJS({
2420924217
exhaustiveCompletions: true
2421024218
},
2421124219
theme: {
24212-
_internalId: 193534,
24220+
_internalId: 194268,
2421324221
type: "anyOf",
2421424222
anyOf: [
2421524223
{
@@ -24249,7 +24257,42 @@ var require_yaml_intelligence_resources = __commonJS({
2424924257
"case-detection": true
2425024258
},
2425124259
$id: "handlers/mermaid"
24252-
}
24260+
},
24261+
"schema/document-typst.yml": [
24262+
{
24263+
name: "page-numbering",
24264+
tags: {
24265+
formats: [
24266+
"typst"
24267+
]
24268+
},
24269+
schema: {
24270+
anyOf: [
24271+
"string",
24272+
{
24273+
enum: [
24274+
false
24275+
]
24276+
}
24277+
]
24278+
},
24279+
description: {
24280+
short: "Include an automatically generated table of contents"
24281+
}
24282+
},
24283+
{
24284+
name: "smart",
24285+
tags: {
24286+
formats: [
24287+
"typst"
24288+
]
24289+
},
24290+
schema: "boolean",
24291+
description: {
24292+
short: "Use smart quotes in document output. Defaults to true."
24293+
}
24294+
}
24295+
]
2425324296
};
2425424297
}
2425524298
});
@@ -24774,8 +24817,8 @@ function mappedIndexToLineCol(eitherText) {
2477424817
};
2477524818
}
2477624819
function mappedLines(str2, keepNewLines = false) {
24777-
const lines2 = rangedLines(str2.value, keepNewLines);
24778-
return lines2.map((v) => mappedString(str2, [v.range]));
24820+
const lines3 = rangedLines(str2.value, keepNewLines);
24821+
return lines3.map((v) => mappedString(str2, [v.range]));
2477924822
}
2478024823

2478124824
// parsing.ts
@@ -33575,9 +33618,7 @@ async function breakQuartoMd(src, validate2 = false, lenient = false) {
3357533618
} else if (cell_type === "directive") {
3357633619
cell.source = mappedString(src, mappedChunks.slice(1, -1), fileName);
3357733620
}
33578-
if (mdTrimEmptyLines(lines(cell.sourceVerbatim.value)).length > 0 || cell.options !== void 0) {
33579-
nb.cells.push(cell);
33580-
}
33621+
nb.cells.push(cell);
3358133622
lineBuffer.splice(0, lineBuffer.length);
3358233623
}
3358333624
};
@@ -33639,24 +33680,6 @@ async function breakQuartoMd(src, validate2 = false, lenient = false) {
3363933680
await flushLineBuffer("markdown", srcLines.length);
3364033681
return nb;
3364133682
}
33642-
function mdTrimEmptyLines(lines2) {
33643-
const firstNonEmpty = lines2.findIndex((line) => line.trim().length > 0);
33644-
if (firstNonEmpty === -1) {
33645-
return [];
33646-
}
33647-
lines2 = lines2.slice(firstNonEmpty);
33648-
let lastNonEmpty = -1;
33649-
for (let i = lines2.length - 1; i >= 0; i--) {
33650-
if (lines2[i].trim().length > 0) {
33651-
lastNonEmpty = i;
33652-
break;
33653-
}
33654-
}
33655-
if (lastNonEmpty > -1) {
33656-
lines2 = lines2.slice(0, lastNonEmpty + 1);
33657-
}
33658-
return lines2;
33659-
}
3366033683

3366133684
// ../yaml-schema/format-aliases.ts
3366233685
var formatAliases = void 0;

0 commit comments

Comments
 (0)