Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/package/src/common/prepare-dist.ts
12925 views
1
/*
2
* prepare-dist.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*
6
*/
7
8
import { dirname, join } from "../../../src/deno_ral/path.ts";
9
import { copySync, ensureDirSync, existsSync } from "../../../src/deno_ral/fs.ts";
10
11
import { Configuration } from "../common/config.ts";
12
import { buildFilter } from "./package-filters.ts";
13
import { bundle } from "../util/deno.ts";
14
import { info } from "../../../src/deno_ral/log.ts";
15
import { buildAssets } from "../../../src/command/dev-call/build-artifacts/cmd.ts";
16
import { initTreeSitter } from "../../../src/core/schema/deno-init-tree-sitter.ts";
17
import {
18
Dependency,
19
configureDependency,
20
kDependencies,
21
} from "./dependencies/dependencies.ts";
22
import { copyQuartoScript } from "./configure.ts";
23
import { deno } from "./dependencies/deno.ts";
24
import { buildQuartoPreviewJs } from "./previewjs.ts";
25
26
export async function prepareDist(
27
config: Configuration,
28
) {
29
// run esbuild
30
// copy from resources dir to the 'share' dir (which is resources)
31
// config.directoryInfo.share
32
33
// Moving appropriate binaries into place
34
35
// Get each dependency extracted into the 'bin' folder
36
// Download dependencies
37
38
// Ensure that the pkgWorkingDir is clean
39
if (existsSync(config.directoryInfo.pkgWorking.root)) {
40
Deno.removeSync(config.directoryInfo.pkgWorking.root, { recursive: true });
41
}
42
43
// Ensure that the working directory exists
44
ensureDirSync(config.directoryInfo.pkgWorking.bin);
45
ensureDirSync(config.directoryInfo.pkgWorking.share);
46
const toolsDir = join(
47
config.directoryInfo.pkgWorking.bin,
48
"tools",
49
);
50
ensureDirSync(toolsDir);
51
52
// binary tools dir
53
const targetDir = join(
54
config.directoryInfo.pkgWorking.bin,
55
"tools",
56
);
57
58
// Function to wrap architecture specific configuration
59
const configArchDependency = async (dep: Dependency,
60
dir: string,
61
config: Configuration) => {
62
if (config.os === "darwin") {
63
// add a secondary config specifically for Mac
64
await configureDependency(dep, dir, {
65
os: config.os,
66
arch: "aarch64",
67
});
68
69
await configureDependency(dep, dir, {
70
os: config.os,
71
arch: "x86_64",
72
});
73
} else {
74
await configureDependency(dep, targetDir, config);
75
}
76
}
77
78
// Download Deno
79
const denoVersion = Deno.env.get("DENO");
80
if (denoVersion) {
81
const denoDependency = deno(denoVersion);
82
await configArchDependency(denoDependency, targetDir, config)
83
}
84
85
// Download the dependencies
86
for (const dependency of kDependencies) {
87
try {
88
await configArchDependency(dependency, targetDir, config)
89
} catch (e) {
90
if (!(e instanceof Error)) { throw e; }
91
if (
92
e.message ===
93
"The architecture aarch64 is missing the dependency deno_dom"
94
) {
95
info("\nIgnoring deno_dom dependency on Apple Silicon");
96
continue;
97
} else {
98
throw e;
99
}
100
}
101
}
102
103
// build quarto-preview.js
104
info("Building Quarto Web UI");
105
const result = buildQuartoPreviewJs(config.directoryInfo.src, undefined, true);
106
if (!result.success) {
107
throw new Error();
108
}
109
110
111
// Place the quarto sciprt
112
// Move the quarto script into place
113
info("Moving Quarto script");
114
copyQuartoScript(config, config.directoryInfo.pkgWorking.bin);
115
116
// Move the supporting files into place
117
info("\nMoving supporting files");
118
supportingFiles(config);
119
info("");
120
121
// Update extension-build import map for distribution
122
info("Updating extension-build import map");
123
updateImportMap(config);
124
info("");
125
126
// Create the deno bundle
127
// const input = join(config.directoryInfo.src, "quarto.ts");
128
const output = join(config.directoryInfo.pkgWorking.bin, "quarto.js");
129
info("\nCreating Deno Bundle");
130
info(output);
131
await bundle(
132
config,
133
);
134
info("");
135
136
// Inline the LUA Filters and move them into place
137
info("\nCreating Inlined LUA Filters");
138
inlineFilters(config);
139
info("");
140
141
// Write a version file to share
142
info(`Writing version: ${config.version}`);
143
Deno.writeTextFileSync(
144
join(config.directoryInfo.pkgWorking.share, "version"),
145
config.version,
146
);
147
info("");
148
149
info("\nBuilding JS assets");
150
await initTreeSitter();
151
await buildAssets();
152
const buildAssetFiles = [
153
"formats/html/ojs/quarto-ojs-runtime.js",
154
"editor/tools/yaml/yaml-intelligence-resources.json",
155
"editor/tools/yaml/web-worker.js",
156
"editor/tools/yaml/yaml.js",
157
];
158
for (const file of buildAssetFiles) {
159
copySync(
160
join(config.directoryInfo.src, "resources", file),
161
join(config.directoryInfo.pkgWorking.share, file),
162
{ overwrite: true },
163
);
164
}
165
166
// Copy quarto-types to extension-build directory
167
// Note: deno.json and import-map.json are copied by supportingFiles() and
168
// import-map.json is then updated by updateImportMap() for distribution
169
info("Copying quarto-types.d.ts to extension-build directory");
170
const extensionBuildDir = join(
171
config.directoryInfo.pkgWorking.share,
172
"extension-build",
173
);
174
copySync(
175
join(config.directoryInfo.root, "packages/quarto-types/dist/index.d.ts"),
176
join(extensionBuildDir, "quarto-types.d.ts"),
177
{ overwrite: true },
178
);
179
180
// Remove the config directory, if present
181
info(`Cleaning config`);
182
const configDir = join(config.directoryInfo.dist, "config");
183
info(configDir);
184
if (existsSync(configDir)) {
185
Deno.removeSync(configDir, { recursive: true });
186
}
187
188
info("");
189
}
190
191
function supportingFiles(config: Configuration) {
192
// Move information and share resources into place
193
const filesToCopy = [
194
{
195
from: join(config.directoryInfo.root, "COPYING.md"),
196
to: join(config.directoryInfo.pkgWorking.share, "COPYING.md"),
197
},
198
{
199
from: join(config.directoryInfo.root, "COPYRIGHT"),
200
to: join(config.directoryInfo.pkgWorking.share, "COPYRIGHT"),
201
},
202
{
203
from: join(config.directoryInfo.src, "resources"),
204
to: config.directoryInfo.pkgWorking.share,
205
},
206
];
207
208
// Gather supporting files
209
filesToCopy.forEach((fileToCopy) => {
210
const dir = dirname(fileToCopy.to);
211
info(`Ensuring dir ${dir} exists`);
212
ensureDirSync(dir);
213
214
info(`Copying ${fileToCopy.from} to ${fileToCopy.to}`);
215
copySync(fileToCopy.from, fileToCopy.to, { overwrite: true });
216
});
217
218
// Cleanup the filters directory, which contains filter source that will be
219
// compiled later
220
const pathsToClean = [
221
join(config.directoryInfo.pkgWorking.share, "filters"),
222
];
223
pathsToClean.forEach((path) => Deno.removeSync(path, { recursive: true }));
224
}
225
226
function updateImportMap(config: Configuration) {
227
// Read the import map that was copied from src/resources/extension-build/
228
const importMapPath = join(
229
config.directoryInfo.pkgWorking.share,
230
"extension-build",
231
"import-map.json",
232
);
233
const importMapContent = JSON.parse(Deno.readTextFileSync(importMapPath));
234
235
// Read the source import map to get current Deno std versions
236
const sourceImportMapPath = join(config.directoryInfo.src, "import_map.json");
237
const sourceImportMap = JSON.parse(Deno.readTextFileSync(sourceImportMapPath));
238
const sourceImports = sourceImportMap.imports as Record<string, string>;
239
240
// Update the import map for distribution:
241
// 1. Change @quarto/types path from dev (../../../packages/...) to dist (./quarto-types.d.ts)
242
// 2. Update all other imports (Deno std versions) from source import map
243
const updatedImports: Record<string, string> = {
244
"@quarto/types": "./quarto-types.d.ts",
245
};
246
247
// Copy all other imports from source, updating versions
248
for (const key in importMapContent.imports) {
249
if (key !== "@quarto/types") {
250
const sourceValue = sourceImports[key];
251
if (!sourceValue) {
252
throw new Error(
253
`Import map key "${key}" not found in source import_map.json`,
254
);
255
}
256
updatedImports[key] = sourceValue;
257
}
258
}
259
260
importMapContent.imports = updatedImports;
261
262
// Write back the updated import map
263
Deno.writeTextFileSync(
264
importMapPath,
265
JSON.stringify(importMapContent, null, 2) + "\n",
266
);
267
}
268
269
interface Filter {
270
// The name of the filter (the LUA file and perhaps the directory)
271
name: string;
272
273
// An optional name of the directory, if it is not the name of the LUA filter
274
dir?: string;
275
}
276
277
function inlineFilters(config: Configuration) {
278
info("Building inlined filters");
279
const outDir = join(config.directoryInfo.pkgWorking.share, "filters");
280
const filtersToInline: Filter[] = [
281
{ name: "main", dir: "." },
282
{ name: "pagebreak", dir: "rmarkdown" },
283
{ name: "quarto-init" },
284
{ name: "crossref" },
285
{ name: "customwriter" },
286
{ name: "qmd-reader", dir: "." },
287
{ name: "llms", dir: "llms" },
288
{ name: "leveloneanalysis", dir: "quarto-internals"}
289
];
290
291
filtersToInline.forEach((filter) => {
292
info(filter);
293
buildFilter(
294
join(
295
config.directoryInfo.src,
296
"resources",
297
"filters",
298
filter.dir || filter.name,
299
`${filter.name}.lua`,
300
),
301
join(outDir, filter.dir || filter.name, `${filter.name}.lua`),
302
);
303
});
304
305
const modules = "modules";
306
const modulesIn = join(
307
config.directoryInfo.src,
308
"resources",
309
"filters", modules);
310
const modulesOut = join(outDir, modules);
311
312
// move the modules directory
313
copySync(modulesIn, modulesOut)
314
315
316
}
317
318