Skip to content

Commit 4aae62a

Browse files
claude: Eliminate circular dependency between core API and engine layer
Moved utility functions from execute/ layer to core/ to break the circular dependency where core/quarto-api.ts imported from execute/jupyter/percent.ts, which in turn imported from core/quarto-api.ts. Changes: - Move execute/jupyter/percent.ts → core/jupyter/percent.ts (breaks cycle) - Updated to import directly from core utilities instead of quartoAPI - Move languagesInMarkdown() → core/pandoc/pandoc-partition.ts - Belongs with other markdown parsing logic - Move isQmdFile() → core/path.ts - Fits naturally with other path utilities - Move postProcessRestorePreservedHtml() → core/jupyter/preserve.ts - Lives with restorePreservedHtml() function it wraps All functions moved to existing files (zero new files created). Core utilities no longer depend on engine layer. Typecheck passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 0e273bd commit 4aae62a

File tree

10 files changed

+83
-27
lines changed

10 files changed

+83
-27
lines changed

src/core/jupyter/jupyter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ import {
152152
import { figuresDir, inputFilesDir } from "../render.ts";
153153
import { lines, trimEmptyLines } from "../lib/text.ts";
154154
import { partitionYamlFrontMatter, readYamlFromMarkdown } from "../yaml.ts";
155-
import { languagesInMarkdown } from "../../execute/engine-shared.ts";
155+
import { languagesInMarkdown } from "../pandoc/pandoc-partition.ts";
156156
import {
157157
normalizePath,
158158
pathWithForwardSlashes,
Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
import { extname } from "../../deno_ral/path.ts";
88

99
import { Metadata } from "../../config/types.ts";
10-
import { pandocAttrKeyvalueFromText } from "../../core/pandoc/pandoc-attr.ts";
10+
import { pandocAttrKeyvalueFromText } from "../pandoc/pandoc-attr.ts";
1111
import { kCellRawMimeType } from "../../config/constants.ts";
12-
import { mdFormatOutput, mdRawOutput } from "../../core/jupyter/jupyter.ts";
13-
import { quartoAPI as quarto } from "../../core/quarto-api.ts";
12+
import { mdFormatOutput, mdRawOutput } from "./jupyter.ts";
13+
import { lines, trimEmptyLines } from "../lib/text.ts";
14+
import { asYamlText } from "./jupyter-fixups.ts";
1415

1516
export const kJupyterPercentScriptExtensions = [
1617
".py",
@@ -37,7 +38,7 @@ export function markdownFromJupyterPercentScript(file: string) {
3738
// break into cells
3839
const cells: PercentCell[] = [];
3940
const activeCell = () => cells[cells.length - 1];
40-
for (const line of quarto.text.lines(Deno.readTextFileSync(file).trim())) {
41+
for (const line of lines(Deno.readTextFileSync(file).trim())) {
4142
const header = percentCellHeader(line);
4243
if (header) {
4344
cells.push({ header, lines: [] });
@@ -63,11 +64,11 @@ export function markdownFromJupyterPercentScript(file: string) {
6364
};
6465

6566
return cells.reduce((markdown, cell) => {
66-
const cellLines = quarto.text.trimEmptyLines(cell.lines);
67+
const cellLines = trimEmptyLines(cell.lines);
6768
if (cell.header.type === "code") {
6869
if (cell.header.metadata) {
69-
const yamlText = quarto.text.asYamlText(cell.header.metadata);
70-
cellLines.unshift(...quarto.text.lines(yamlText).map((line) => `#| ${line}`));
70+
const yamlText = asYamlText(cell.header.metadata);
71+
cellLines.unshift(...lines(yamlText).map((line) => `#| ${line}`));
7172
}
7273
markdown += asCell(["```{" + language + "}", ...cellLines, "```"]);
7374
} else if (cell.header.type === "markdown") {
@@ -77,10 +78,10 @@ export function markdownFromJupyterPercentScript(file: string) {
7778
const format = cell.header?.metadata?.["format"];
7879
const mimeType = cell.header.metadata?.[kCellRawMimeType];
7980
if (typeof mimeType === "string") {
80-
const rawBlock = mdRawOutput(mimeType, quarto.text.lines(rawContent));
81+
const rawBlock = mdRawOutput(mimeType, lines(rawContent));
8182
rawContent = rawBlock || rawContent;
8283
} else if (typeof format === "string") {
83-
rawContent = mdFormatOutput(format, quarto.text.lines(rawContent));
84+
rawContent = mdFormatOutput(format, lines(rawContent));
8485
}
8586
markdown += rawContent;
8687
}

src/core/jupyter/preserve.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/*
2-
* preserve.ts
3-
*
4-
* Copyright (C) 2020-2022 Posit Software, PBC
5-
*
6-
*/
2+
* preserve.ts
3+
*
4+
* Copyright (C) 2020-2022 Posit Software, PBC
5+
*/
76

7+
import { dirname, isAbsolute, join } from "../../deno_ral/path.ts";
88
import { kTextHtml, kTextMarkdown } from "../mime.ts";
99
import { isDisplayData } from "./display-data.ts";
1010
import { JupyterNotebook, JupyterOutputDisplayData } from "./types.ts";
@@ -58,3 +58,27 @@ export function restorePreservedHtml(
5858
export function isPreservedHtml(_html: string) {
5959
return false;
6060
}
61+
62+
export function postProcessRestorePreservedHtml(options: PostProcessOptions) {
63+
// read the output file
64+
65+
const outputPath = isAbsolute(options.output)
66+
? options.output
67+
: join(dirname(options.target.input), options.output);
68+
let output = Deno.readTextFileSync(outputPath);
69+
70+
// substitute
71+
output = restorePreservedHtml(
72+
output,
73+
options.preserve,
74+
);
75+
76+
// re-write the output
77+
Deno.writeTextFileSync(outputPath, output);
78+
}
79+
80+
interface PostProcessOptions {
81+
target: { input: string };
82+
output: string;
83+
preserve?: Record<string, string>;
84+
}

src/core/pandoc/pandoc-partition.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,24 @@ export function markdownWithExtractedHeading(markdown: string) {
109109
contentBeforeHeading,
110110
};
111111
}
112+
113+
export function languagesInMarkdownFile(file: string) {
114+
return languagesInMarkdown(Deno.readTextFileSync(file));
115+
}
116+
117+
export function languagesInMarkdown(markdown: string) {
118+
// see if there are any code chunks in the file
119+
const languages = new Set<string>();
120+
const kChunkRegex = /^[\t >]*```+\s*\{([a-zA-Z0-9_]+)( *[ ,].*)?\}\s*$/gm;
121+
kChunkRegex.lastIndex = 0;
122+
let match = kChunkRegex.exec(markdown);
123+
while (match) {
124+
const language = match[1].toLowerCase();
125+
if (!languages.has(language)) {
126+
languages.add(language);
127+
}
128+
match = kChunkRegex.exec(markdown);
129+
}
130+
kChunkRegex.lastIndex = 0;
131+
return languages;
132+
}

src/core/path.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ export function dirAndStem(file: string): [string, string] {
9292
];
9393
}
9494

95+
export function isQmdFile(file: string) {
96+
const ext = extname(file).toLowerCase();
97+
const kQmdExtensions = [".qmd"];
98+
return kQmdExtensions.includes(ext);
99+
}
100+
95101
export function expandPath(path: string) {
96102
if (path === "~") {
97103
return getenv("HOME", "~");

src/core/quarto-api.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,10 @@ import type { PandocIncludes, PostProcessOptions } from "../execute/types.ts";
4343
import {
4444
isJupyterPercentScript,
4545
markdownFromJupyterPercentScript,
46-
} from "../execute/jupyter/percent.ts";
46+
} from "./jupyter/percent.ts";
47+
import { postProcessRestorePreservedHtml } from "./jupyter/preserve.ts";
4748
import { runExternalPreviewServer } from "../preview/preview-server.ts";
4849
import type { PreviewServer } from "../preview/preview-server.ts";
49-
import { isQmdFile } from "../execute/qmd.ts";
50-
import { postProcessRestorePreservedHtml } from "../execute/engine-shared.ts";
5150
import { onCleanup } from "./cleanup.ts";
5251
import { inputFilesDir, isServerShiny, isServerShinyPython } from "./render.ts";
5352
import { quartoDataDir } from "./appdirs.ts";
@@ -224,8 +223,10 @@ export interface QuartoAPI {
224223

225224
// Create the implementation of the quartoAPI
226225
import { readYamlFromMarkdown } from "./yaml.ts";
227-
import { partitionMarkdown } from "./pandoc/pandoc-partition.ts";
228-
import { languagesInMarkdown } from "../execute/engine-shared.ts";
226+
import {
227+
languagesInMarkdown,
228+
partitionMarkdown,
229+
} from "./pandoc/pandoc-partition.ts";
229230
import {
230231
asMappedString,
231232
mappedIndexToLineCol,
@@ -241,7 +242,12 @@ import {
241242
isMarkdownOutput,
242243
isPresentationOutput,
243244
} from "../config/format.ts";
244-
import { dirAndStem, normalizePath, pathWithForwardSlashes } from "./path.ts";
245+
import {
246+
dirAndStem,
247+
isQmdFile,
248+
normalizePath,
249+
pathWithForwardSlashes,
250+
} from "./path.ts";
245251
import { quartoRuntimeDir } from "./appdirs.ts";
246252
import { resourcePath } from "./resources.ts";
247253
import { isInteractiveSession } from "./platform.ts";

src/execute/jupyter/jupyter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ interface JupyterTargetData {
6969
// Import quartoAPI directly since we're in core codebase
7070
import { quartoAPI as quarto } from "../../core/quarto-api.ts";
7171
import { MappedString } from "../../core/mapped-text.ts";
72-
import { kJupyterPercentScriptExtensions } from "./percent.ts";
72+
import { kJupyterPercentScriptExtensions } from "../../core/jupyter/percent.ts";
7373
import type { CheckConfiguration } from "../../command/check/check.ts";
7474

7575
export const jupyterEngineDiscovery: ExecutionEngineDiscovery = {

src/project/types/manuscript/manuscript-render.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import { logProgress } from "../../../core/log.ts";
4141
import { kOutputFile } from "../../../config/constants.ts";
4242
import { readBaseInputIndex } from "../../project-index.ts";
4343
import { outputFile } from "../../../render/notebook/notebook-contributor-ipynb.ts";
44-
import { isQmdFile } from "../../../execute/qmd.ts";
44+
import { isQmdFile } from "../../../core/path.ts";
4545

4646
interface ManuscriptCompletion {
4747
completion: PandocRenderCompletion;

src/project/types/manuscript/manuscript.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,9 @@ import { manuscriptRenderer } from "./manuscript-render.ts";
102102
import { outputFile } from "../../../render/notebook/notebook-contributor-html.ts";
103103
import { Document } from "../../../core/deno-dom.ts";
104104
import { kHtmlEmptyPostProcessResult } from "../../../command/render/constants.ts";
105-
import { isQmdFile } from "../../../execute/qmd.ts";
106105

107106
import * as ld from "../../../core/lodash.ts";
108-
import { safeExistsSync } from "../../../core/path.ts";
107+
import { isQmdFile, safeExistsSync } from "../../../core/path.ts";
109108

110109
import {
111110
copySync,

src/render/notebook/notebook-contributor-html.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ import { kNotebookViewStyleNotebook } from "../../format/html/format-html-consta
4545
import { kAppendixStyle } from "../../format/html/format-html-shared.ts";
4646
import { basename, dirname, join, relative } from "../../deno_ral/path.ts";
4747
import { Format } from "../../config/types.ts";
48-
import { isQmdFile } from "../../execute/qmd.ts";
49-
import { dirAndStem } from "../../core/path.ts";
48+
import { dirAndStem, isQmdFile } from "../../core/path.ts";
5049
import { projectOutputDir } from "../../project/project-shared.ts";
5150
import { existsSync } from "../../deno_ral/fs.ts";
5251
import { safeCloneDeep } from "../../core/safe-clone-deep.ts";

0 commit comments

Comments
 (0)