Path: blob/main/package/src/common/prepare-dist.ts
6450 views
/*1* prepare-dist.ts2*3* Copyright (C) 2020-2022 Posit Software, PBC4*5*/67import { dirname, join } from "../../../src/deno_ral/path.ts";8import { copySync, ensureDirSync, existsSync } from "../../../src/deno_ral/fs.ts";910import { Configuration } from "../common/config.ts";11import { buildFilter } from "./package-filters.ts";12import { bundle } from "../util/deno.ts";13import { info } from "../../../src/deno_ral/log.ts";14import { buildAssets } from "../../../src/command/dev-call/build-artifacts/cmd.ts";15import { initTreeSitter } from "../../../src/core/schema/deno-init-tree-sitter.ts";16import {17Dependency,18configureDependency,19kDependencies,20} from "./dependencies/dependencies.ts";21import { copyQuartoScript } from "./configure.ts";22import { deno } from "./dependencies/deno.ts";23import { buildQuartoPreviewJs } from "./previewjs.ts";2425export async function prepareDist(26config: Configuration,27) {28// run esbuild29// copy from resources dir to the 'share' dir (which is resources)30// config.directoryInfo.share3132// Moving appropriate binaries into place3334// Get each dependency extracted into the 'bin' folder35// Download dependencies3637// Ensure that the pkgWorkingDir is clean38if (existsSync(config.directoryInfo.pkgWorking.root)) {39Deno.removeSync(config.directoryInfo.pkgWorking.root, { recursive: true });40}4142// Ensure that the working directory exists43ensureDirSync(config.directoryInfo.pkgWorking.bin);44ensureDirSync(config.directoryInfo.pkgWorking.share);45const toolsDir = join(46config.directoryInfo.pkgWorking.bin,47"tools",48);49ensureDirSync(toolsDir);5051// binary tools dir52const targetDir = join(53config.directoryInfo.pkgWorking.bin,54"tools",55);5657// Function to wrap architecture specific configuration58const configArchDependency = async (dep: Dependency,59dir: string,60config: Configuration) => {61if (config.os === "darwin") {62// add a secondary config specifically for Mac63await configureDependency(dep, dir, {64os: config.os,65arch: "aarch64",66});6768await configureDependency(dep, dir, {69os: config.os,70arch: "x86_64",71});72} else {73await configureDependency(dep, targetDir, config);74}75}7677// Download Deno78const denoVersion = Deno.env.get("DENO");79if (denoVersion) {80const denoDependency = deno(denoVersion);81await configArchDependency(denoDependency, targetDir, config)82}8384// Download the dependencies85for (const dependency of kDependencies) {86try {87await configArchDependency(dependency, targetDir, config)88} catch (e) {89if (!(e instanceof Error)) { throw e; }90if (91e.message ===92"The architecture aarch64 is missing the dependency deno_dom"93) {94info("\nIgnoring deno_dom dependency on Apple Silicon");95continue;96} else {97throw e;98}99}100}101102// Stage typst-gather binary (built by configure.sh)103// Only stage if the build machine architecture matches the target architecture104// (cross-compilation is not currently supported)105const buildArch = Deno.build.arch === "aarch64" ? "aarch64" : "x86_64";106if (buildArch === config.arch) {107const typstGatherBinaryName = config.os === "windows" ? "typst-gather.exe" : "typst-gather";108const typstGatherSrc = join(109config.directoryInfo.root,110"package/typst-gather/target/release",111typstGatherBinaryName,112);113if (!existsSync(typstGatherSrc)) {114throw new Error(115`typst-gather binary not found at ${typstGatherSrc}\n` +116"Run ./configure.sh to build it.",117);118}119info("\nStaging typst-gather binary");120const typstGatherDest = join(targetDir, config.arch, typstGatherBinaryName);121ensureDirSync(join(targetDir, config.arch));122copySync(typstGatherSrc, typstGatherDest, { overwrite: true });123info(`Copied ${typstGatherSrc} to ${typstGatherDest}`);124} else {125info(`\nNote: Skipping typst-gather staging (build arch ${buildArch} != target arch ${config.arch})`);126}127128// build quarto-preview.js129info("Building Quarto Web UI");130const result = buildQuartoPreviewJs(config.directoryInfo.src, undefined, true);131if (!result.success) {132throw new Error();133}134135136// Place the quarto sciprt137// Move the quarto script into place138info("Moving Quarto script");139copyQuartoScript(config, config.directoryInfo.pkgWorking.bin);140141// Move the supporting files into place142info("\nMoving supporting files");143supportingFiles(config);144info("");145146// Update extension-build import map for distribution147info("Updating extension-build import map");148updateImportMap(config);149info("");150151// Create the deno bundle152// const input = join(config.directoryInfo.src, "quarto.ts");153const output = join(config.directoryInfo.pkgWorking.bin, "quarto.js");154info("\nCreating Deno Bundle");155info(output);156await bundle(157config,158);159info("");160161// Inline the LUA Filters and move them into place162info("\nCreating Inlined LUA Filters");163inlineFilters(config);164info("");165166// Write a version file to share167info(`Writing version: ${config.version}`);168Deno.writeTextFileSync(169join(config.directoryInfo.pkgWorking.share, "version"),170config.version,171);172info("");173174info("\nBuilding JS assets");175await initTreeSitter();176await buildAssets();177const buildAssetFiles = [178"formats/html/ojs/quarto-ojs-runtime.js",179"editor/tools/yaml/yaml-intelligence-resources.json",180"editor/tools/yaml/web-worker.js",181"editor/tools/yaml/yaml.js",182];183for (const file of buildAssetFiles) {184copySync(185join(config.directoryInfo.src, "resources", file),186join(config.directoryInfo.pkgWorking.share, file),187{ overwrite: true },188);189}190191// Copy quarto-types to extension-build directory192// Note: deno.json and import-map.json are copied by supportingFiles() and193// import-map.json is then updated by updateImportMap() for distribution194info("Copying quarto-types.d.ts to extension-build directory");195const extensionBuildDir = join(196config.directoryInfo.pkgWorking.share,197"extension-build",198);199copySync(200join(config.directoryInfo.root, "packages/quarto-types/dist/index.d.ts"),201join(extensionBuildDir, "quarto-types.d.ts"),202{ overwrite: true },203);204205// Remove the config directory, if present206info(`Cleaning config`);207const configDir = join(config.directoryInfo.dist, "config");208info(configDir);209if (existsSync(configDir)) {210Deno.removeSync(configDir, { recursive: true });211}212213info("");214}215216function supportingFiles(config: Configuration) {217// Move information and share resources into place218const filesToCopy = [219{220from: join(config.directoryInfo.root, "COPYING.md"),221to: join(config.directoryInfo.pkgWorking.share, "COPYING.md"),222},223{224from: join(config.directoryInfo.root, "COPYRIGHT"),225to: join(config.directoryInfo.pkgWorking.share, "COPYRIGHT"),226},227{228from: join(config.directoryInfo.src, "resources"),229to: config.directoryInfo.pkgWorking.share,230},231];232233// Gather supporting files234filesToCopy.forEach((fileToCopy) => {235const dir = dirname(fileToCopy.to);236info(`Ensuring dir ${dir} exists`);237ensureDirSync(dir);238239info(`Copying ${fileToCopy.from} to ${fileToCopy.to}`);240copySync(fileToCopy.from, fileToCopy.to, { overwrite: true });241});242243// Cleanup the filters directory, which contains filter source that will be244// compiled later245const pathsToClean = [246join(config.directoryInfo.pkgWorking.share, "filters"),247];248pathsToClean.forEach((path) => Deno.removeSync(path, { recursive: true }));249}250251function updateImportMap(config: Configuration) {252// Read the import map that was copied from src/resources/extension-build/253const importMapPath = join(254config.directoryInfo.pkgWorking.share,255"extension-build",256"import-map.json",257);258const importMapContent = JSON.parse(Deno.readTextFileSync(importMapPath));259260// Read the source import map to get current Deno std versions261const sourceImportMapPath = join(config.directoryInfo.src, "import_map.json");262const sourceImportMap = JSON.parse(Deno.readTextFileSync(sourceImportMapPath));263const sourceImports = sourceImportMap.imports as Record<string, string>;264265// Update the import map for distribution:266// 1. Change @quarto/types path from dev (../../../packages/...) to dist (./quarto-types.d.ts)267// 2. Update all other imports (Deno std versions) from source import map268const updatedImports: Record<string, string> = {269"@quarto/types": "./quarto-types.d.ts",270};271272// Copy all other imports from source, updating versions273for (const key in importMapContent.imports) {274if (key !== "@quarto/types") {275const sourceValue = sourceImports[key];276if (!sourceValue) {277throw new Error(278`Import map key "${key}" not found in source import_map.json`,279);280}281updatedImports[key] = sourceValue;282}283}284285importMapContent.imports = updatedImports;286287// Write back the updated import map288Deno.writeTextFileSync(289importMapPath,290JSON.stringify(importMapContent, null, 2) + "\n",291);292}293294interface Filter {295// The name of the filter (the LUA file and perhaps the directory)296name: string;297298// An optional name of the directory, if it is not the name of the LUA filter299dir?: string;300}301302function inlineFilters(config: Configuration) {303info("Building inlined filters");304const outDir = join(config.directoryInfo.pkgWorking.share, "filters");305const filtersToInline: Filter[] = [306{ name: "main", dir: "." },307{ name: "pagebreak", dir: "rmarkdown" },308{ name: "quarto-init" },309{ name: "crossref" },310{ name: "customwriter" },311{ name: "qmd-reader", dir: "." },312{ name: "leveloneanalysis", dir: "quarto-internals"}313];314315filtersToInline.forEach((filter) => {316info(filter);317buildFilter(318join(319config.directoryInfo.src,320"resources",321"filters",322filter.dir || filter.name,323`${filter.name}.lua`,324),325join(outDir, filter.dir || filter.name, `${filter.name}.lua`),326);327});328329const modules = "modules";330const modulesIn = join(331config.directoryInfo.src,332"resources",333"filters", modules);334const modulesOut = join(outDir, modules);335336// move the modules directory337copySync(modulesIn, modulesOut)338339340}341342343