Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/command/dev-call/build-artifacts/cmd.ts
3587 views
1
/*
2
* cmd.ts
3
*
4
* Copyright (C) 2021-2022 Posit Software, PBC
5
*/
6
7
import { Command } from "cliffy/command/mod.ts";
8
9
import {
10
ESBuildAnalysis,
11
esbuildAnalyze,
12
esbuildCompile,
13
} from "../../../core/esbuild.ts";
14
import { buildIntelligenceResources } from "../../../core/schema/build-schema-file.ts";
15
import { formatResourcePath, resourcePath } from "../../../core/resources.ts";
16
import { simple } from "acorn/walk";
17
import { Parser } from "acorn/acorn";
18
import classFields from "acorn-class-fields";
19
import { initYamlIntelligenceResourcesFromFilesystem } from "../../../core/schema/utils.ts";
20
21
// initialize language handlers
22
import "../../../core/handlers/handlers.ts";
23
import { join } from "../../../deno_ral/path.ts";
24
25
function ensureAllowableIDESyntax(src: string, filename: string) {
26
const ast = Parser.extend(classFields).parse(src, {
27
ecmaVersion: "2020",
28
sourceType: "module",
29
});
30
let failed = false;
31
simple(ast, {
32
// deno-lint-ignore no-explicit-any
33
ChainExpression(_node: any) {
34
console.error(
35
`Failure: Chain expression \`?.\` not allowed in ${filename}`,
36
);
37
failed = true;
38
},
39
// deno-lint-ignore no-explicit-any
40
LogicalExpression(node: any) {
41
if (node.operator === "??") {
42
console.error(
43
`Failure: Nullish coalescing operator \`??\` not allows in ${filename}`,
44
);
45
failed = true;
46
}
47
},
48
});
49
if (failed) {
50
throw new Error("Found syntax that is not allowed");
51
}
52
}
53
54
async function buildYAMLJS() {
55
// convert tree-sitter-yaml to a JSON object that can be imported directly.
56
// In principle this never changes so running it every time is overkill, but this
57
// way we document the generation of the JSON object.
58
//
59
// inexplicably, using TextEncoder inside a web worker on QtWebEngine freezes
60
// the entire thread. That means we can't use base64 strings to encode the wasm
61
// values. So we will encode them as plain js numbers. facepalm
62
const treeSitterYamlJson = {
63
"data": Array.from(Deno.readFileSync(
64
resourcePath("editor/tools/yaml/tree-sitter-yaml.wasm"),
65
)),
66
};
67
Deno.writeTextFileSync(
68
resourcePath("editor/tools/yaml/tree-sitter-yaml.json"),
69
JSON.stringify(treeSitterYamlJson),
70
);
71
72
const esbuild = async (
73
cwd: string,
74
filename: string,
75
outputType: "esm" | "iife" | "cjs" = "iife",
76
checkIde = true,
77
) => {
78
const result = (await esbuildCompile(
79
"",
80
cwd,
81
[filename],
82
outputType,
83
))!;
84
if (checkIde) {
85
ensureAllowableIDESyntax(result, filename);
86
}
87
return result;
88
};
89
90
Deno.writeTextFileSync(
91
resourcePath("editor/tools/yaml/yaml-intelligence.js"),
92
await esbuild(
93
resourcePath("../core/lib/yaml-intelligence"),
94
"ide-main.ts",
95
"esm",
96
),
97
);
98
99
Deno.writeTextFileSync(
100
resourcePath("editor/tools/yaml/yaml.js"),
101
await esbuild(
102
resourcePath("editor/tools/yaml"),
103
"automation.js",
104
),
105
);
106
107
const webWorkerSrc = await esbuild(
108
resourcePath("../core/lib/yaml-intelligence"),
109
"web-worker.ts",
110
);
111
const vsCodeSrc = await esbuild(
112
resourcePath("../core/lib/yaml-intelligence"),
113
"vs-code.ts",
114
"esm",
115
false,
116
);
117
118
const treeSitter = Deno.readTextFileSync(
119
resourcePath("editor/tools/yaml/tree-sitter.js"),
120
);
121
ensureAllowableIDESyntax(treeSitter, "tree-sitter.js");
122
123
Deno.writeTextFileSync(
124
resourcePath("editor/tools/vs-code.mjs"),
125
[treeSitter, vsCodeSrc].join(""),
126
);
127
128
Deno.writeTextFileSync(
129
resourcePath("editor/tools/yaml/web-worker.js"),
130
[treeSitter, webWorkerSrc].join(""),
131
);
132
}
133
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
152
export async function buildAssets() {
153
// this has to come first because buildYAMLJS depends on it.
154
await buildIntelligenceResources();
155
await buildYAMLJS();
156
await buildEsbuildAnalysisCache();
157
}
158
159
export const buildJsCommand = new Command()
160
.name("build-artifacts")
161
.hidden()
162
.description(
163
"Builds all the javascript assets necessary for IDE support.\n\n",
164
)
165
.action(async () => {
166
await initYamlIntelligenceResourcesFromFilesystem();
167
await buildAssets();
168
});
169
170