Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/core/handlers/include-standalone.ts
3583 views
1
/*
2
* include-standalone.ts
3
*
4
* Copyright (C) 2023 Posit Software, PBC
5
*/
6
7
import { IncludeState, LanguageCellHandlerContext } from "./types.ts";
8
9
import {
10
asMappedString,
11
EitherString,
12
mappedConcat,
13
MappedString,
14
mappedString,
15
} from "../lib/mapped-text.ts";
16
17
import { rangedLines } from "../lib/ranged-text.ts";
18
import { isBlockShortcode } from "../lib/parse-shortcode.ts";
19
20
export const standaloneInclude = async (
21
handlerContext: LanguageCellHandlerContext,
22
filename: string,
23
): Promise<MappedString> => {
24
const source = handlerContext.options.context.target.source;
25
const retrievedFiles: string[] = [source];
26
27
const textFragments: EitherString[] = [];
28
if (!handlerContext.options.state) {
29
handlerContext.options.state = {};
30
}
31
if (!handlerContext.options.state.include) {
32
handlerContext.options.state.include = {
33
includes: [],
34
};
35
}
36
const includeState = handlerContext.options.state.include as IncludeState;
37
38
const retrieveInclude = async (filename: string) => {
39
const path = handlerContext.resolvePath(filename);
40
41
if (retrievedFiles.indexOf(path) !== -1) {
42
throw new Error(
43
`Include directive found circular include of file ${filename}.`,
44
);
45
}
46
47
let includeSrc;
48
try {
49
includeSrc = asMappedString(
50
Deno.readTextFileSync(path),
51
path,
52
);
53
} catch (_e) {
54
const errMsg: string[] = [`Include directive failed.`];
55
errMsg.push(...retrievedFiles.map((s) => ` in file ${s}, `));
56
errMsg.push(
57
` could not find file ${path
58
// relative(handlerContext.options.context.target.source, path)
59
}.`,
60
);
61
throw new Error(errMsg.join("\n"));
62
}
63
64
retrievedFiles.push(filename);
65
66
let rangeStart = 0;
67
for (const { substring, range } of rangedLines(includeSrc.value)) {
68
const m = isBlockShortcode(substring);
69
if (m && m.name.toLocaleLowerCase() === "include") {
70
textFragments.push(
71
mappedString(includeSrc, [{
72
start: rangeStart,
73
end: range.start,
74
}]),
75
);
76
rangeStart = range.end;
77
const params = m.params;
78
if (params.length === 0) {
79
throw new Error("Include directive needs file parameter");
80
}
81
82
includeState.includes.push({ source: filename, target: params[0] });
83
await retrieveInclude(params[0]);
84
}
85
}
86
if (rangeStart !== includeSrc.value.length) {
87
textFragments.push(
88
mappedString(includeSrc, [{
89
start: rangeStart,
90
end: includeSrc.value.length,
91
}]),
92
);
93
}
94
textFragments.push(includeSrc.value.endsWith("\n") ? "\n" : "\n\n");
95
96
retrievedFiles.pop();
97
};
98
99
await retrieveInclude(filename);
100
101
return Promise.resolve(mappedConcat(textFragments));
102
};
103
104