diff --git a/src/command/create/artifacts/artifact-shared.ts b/src/command/create/artifacts/artifact-shared.ts index 4e8a259a2b..a12ef2dbb7 100644 --- a/src/command/create/artifacts/artifact-shared.ts +++ b/src/command/create/artifacts/artifact-shared.ts @@ -1,9 +1,8 @@ /* -* artifact-shared.ts -* -* Copyright (C) 2020-2022 Posit Software, PBC -* -*/ + * artifact-shared.ts + * + * Copyright (C) 2020-2022 Posit Software, PBC + */ import { capitalizeTitle } from "../../../core/text.ts"; import { quartoConfig } from "../../../core/quarto.ts"; @@ -16,24 +15,7 @@ import { basename, dirname, join, relative } from "path/mod.ts"; import { ensureDirSync, walkSync } from "fs/mod.ts"; import { renderEjs } from "../../../core/ejs.ts"; import { safeExistsSync } from "../../../core/path.ts"; - -export interface CreateDirective { - displayType: string; - name: string; - directory: string; - template: string; - options: Record; -} - -export interface CreateDirectiveData extends Record { - name: string; - filesafename: string; - classname: string; - title: string; - author: string; - version: string; - quartoversion: string; -} +import { CreateDirective, CreateDirectiveData } from "../cmd-types.ts"; // File paths that include this string will get fixed up // and the value from the ejs data will be substituted diff --git a/src/command/create/artifacts/extension.ts b/src/command/create/artifacts/extension.ts index be3f7de877..778d96a2ec 100644 --- a/src/command/create/artifacts/extension.ts +++ b/src/command/create/artifacts/extension.ts @@ -1,17 +1,16 @@ /* -* extension.ts -* -* Copyright (C) 2020-2022 Posit Software, PBC -* -*/ - -import { ArtifactCreator, CreateContext } from "../cmd.ts"; + * extension.ts + * + * Copyright (C) 2020-2022 Posit Software, PBC + */ import { + ArtifactCreator, + CreateContext, CreateDirective, - ejsData, - renderAndCopyArtifacts, -} from "./artifact-shared.ts"; +} from "../cmd-types.ts"; + +import { ejsData, renderAndCopyArtifacts } from "./artifact-shared.ts"; import { resourcePath } from "../../../core/resources.ts"; @@ -46,7 +45,7 @@ const kExtensionSubtypes: Record = { "format": ["html", "pdf", "docx", "revealjs", "typst"], }; -const kExtensionValues = kExtensionTypes.filter((t) => typeof (t) === "object") +const kExtensionValues = kExtensionTypes.filter((t) => typeof t === "object") .map((t) => (t as { name: string; value: string }).value); export const extensionArtifactCreator: ArtifactCreator = { @@ -183,7 +182,7 @@ async function createArtifact( // Find the type using the template const createType = typeFromTemplate(createDirective.template); const extType = kExtensionTypes.find((type) => { - if (typeof (type) === "object") { + if (typeof type === "object") { return type.value === createType; } else { return false; diff --git a/src/command/create/artifacts/project.ts b/src/command/create/artifacts/project.ts index f4481d1a21..94f8bba8b7 100644 --- a/src/command/create/artifacts/project.ts +++ b/src/command/create/artifacts/project.ts @@ -1,13 +1,14 @@ /* -* project.ts -* -* Copyright (C) 2020-2022 Posit Software, PBC -* -*/ + * project.ts + * + * Copyright (C) 2020-2022 Posit Software, PBC + */ -import { ArtifactCreator, CreateContext } from "../cmd.ts"; - -import { CreateDirective } from "./artifact-shared.ts"; +import { + ArtifactCreator, + CreateContext, + CreateDirective, +} from "../cmd-types.ts"; import { capitalizeTitle } from "../../../core/text.ts"; import { kMarkdownEngine } from "../../../execute/types.ts"; diff --git a/src/command/create/cmd-types.ts b/src/command/create/cmd-types.ts new file mode 100644 index 0000000000..6d64450bca --- /dev/null +++ b/src/command/create/cmd-types.ts @@ -0,0 +1,68 @@ +/* + * cmd-types.ts + * + * Copyright (C) 2020-2023 Posit Software, PBC + */ + +export interface CreateContext { + cwd: string; + options: Record; +} + +export interface CreateResult { + // Path to the directory or document + path: string; + + // Files to open + openfiles: string[]; +} + +export interface ArtifactCreator { + // The name that is displayed to users + displayName: string; + + // The identifier for this artifact type + type: string; + + // artifact creators are passed any leftover args from the create command + // and may use those arguments to populate the options + resolveOptions: (args: string[]) => Record; + + // this will always be called, giving the artifact creator + // a change to finalize / transform options + finalizeOptions: (options: CreateContext) => CreateDirective; + + // As long as prompting is allowed, allow the artifact creator prompting to populate + // the options. This will be called until it return undefined, at which point + // the artifact will be created using the options + // deno-lint-ignore no-explicit-any + nextPrompt: (options: CreateContext) => any | undefined; // TODO: this any is a nightmare + + // Creates the artifact using the specified options + // Returns the path to the created artifact + createArtifact: ( + directive: CreateDirective, + quiet?: boolean, + ) => Promise; + + // Set this to false to exclude this artifact type from the create command + enabled?: boolean; +} + +export interface CreateDirective { + displayType: string; + name: string; + directory: string; + template: string; + options: Record; +} + +export interface CreateDirectiveData extends Record { + name: string; + filesafename: string; + classname: string; + title: string; + author: string; + version: string; + quartoversion: string; +} diff --git a/src/command/create/cmd.ts b/src/command/create/cmd.ts index 369e796c32..7af845dbef 100644 --- a/src/command/create/cmd.ts +++ b/src/command/create/cmd.ts @@ -1,9 +1,8 @@ /* -* cmd.ts -* -* Copyright (C) 2020-2022 Posit Software, PBC -* -*/ + * cmd.ts + * + * Copyright (C) 2020-2022 Posit Software, PBC + */ import { extensionArtifactCreator } from "./artifacts/extension.ts"; import { projectArtifactCreator } from "./artifacts/project.ts"; @@ -12,61 +11,11 @@ import { kEditorInfos, scanForEditors } from "./editor.ts"; import { isInteractiveTerminal } from "../../core/platform.ts"; import { runningInCI } from "../../core/ci-info.ts"; -import { CreateDirective } from "./artifacts/artifact-shared.ts"; - import { Command } from "cliffy/command/mod.ts"; -import { - prompt, - Select, - SelectValueOptions, -} from "cliffy/prompt/mod.ts"; +import { prompt, Select, SelectValueOptions } from "cliffy/prompt/mod.ts"; import { readLines } from "io/mod.ts"; import { info } from "log/mod.ts"; - -export interface CreateContext { - cwd: string; - options: Record; -} - -export interface CreateResult { - // Path to the directory or document - path: string; - - // Files to open - openfiles: string[]; -} - -export interface ArtifactCreator { - // The name that is displayed to users - displayName: string; - - // The identifier for this artifact type - type: string; - - // artifact creators are passed any leftover args from the create command - // and may use those arguments to populate the options - resolveOptions: (args: string[]) => Record; - - // this will always be called, giving the artifact creator - // a change to finalize / transform options - finalizeOptions: (options: CreateContext) => CreateDirective; - - // As long as prompting is allowed, allow the artifact creator prompting to populate - // the options. This will be called until it return undefined, at which point - // the artifact will be created using the options - // deno-lint-ignore no-explicit-any - nextPrompt: (options: CreateContext) => any | undefined; // TODO: this any is a nightmare - - // Creates the artifact using the specified options - // Returns the path to the created artifact - createArtifact: ( - directive: CreateDirective, - quiet?: boolean, - ) => Promise; - - // Set this to false to exclude this artifact type from the create command - enabled?: boolean; -} +import { ArtifactCreator, CreateDirective, CreateResult } from "./cmd-types.ts"; // The registered artifact creators const kArtifactCreators: ArtifactCreator[] = [ @@ -210,7 +159,6 @@ const resolveArtifact = async (type?: string, prompt?: boolean) => { }; }; - // Wrapper that will provide keyboard selection hint (if necessary) async function promptSelect( message: string, diff --git a/src/command/create/editor.ts b/src/command/create/editor.ts index 1fe3b85a8c..0a35a90229 100644 --- a/src/command/create/editor.ts +++ b/src/command/create/editor.ts @@ -1,11 +1,10 @@ /* -* cmd.ts -* -* Copyright (C) 2020-2022 Posit Software, PBC -* -*/ + * cmd.ts + * + * Copyright (C) 2020-2022 Posit Software, PBC + */ -import { CreateResult } from "./cmd.ts"; +import { CreateResult } from "./cmd-types.ts"; import { which } from "../../core/path.ts"; import { isRStudioTerminal, isVSCodeTerminal } from "../../core/platform.ts"; diff --git a/src/command/render/output-shared.ts b/src/command/render/output-shared.ts new file mode 100644 index 0000000000..897fee1667 --- /dev/null +++ b/src/command/render/output-shared.ts @@ -0,0 +1,18 @@ +/* + * output-shared.ts + * + * Copyright (C) 2023 Posit Software, PBC + */ + +import { dirname, isAbsolute, relative } from "path/mod.ts"; + +export function normalizeOutputPath(input: string, output: string) { + if (isAbsolute(output)) { + return output; + } else { + return relative( + dirname(input), + output, + ); + } +} diff --git a/src/command/render/output-tex.ts b/src/command/render/output-tex.ts index 9d368d0f83..c3ca9a857b 100644 --- a/src/command/render/output-tex.ts +++ b/src/command/render/output-tex.ts @@ -20,7 +20,7 @@ import { OutputRecipe } from "./types.ts"; import { pdfEngine } from "../../config/pdf.ts"; import { execProcess } from "../../core/process.ts"; import { parseFormatString } from "../../core/pandoc/pandoc-formats.ts"; -import { normalizeOutputPath } from "./output.ts"; +import { normalizeOutputPath } from "./output-shared.ts"; export interface PdfGenerator { generate: ( diff --git a/src/command/render/output-typst.ts b/src/command/render/output-typst.ts index 34c905b0ce..eb9ba2d2a2 100644 --- a/src/command/render/output-typst.ts +++ b/src/command/render/output-typst.ts @@ -18,7 +18,7 @@ import { writeFileToStdout } from "../../core/console.ts"; import { dirAndStem, expandPath } from "../../core/path.ts"; import { kStdOut, replacePandocOutputArg } from "./flags.ts"; import { OutputRecipe, RenderOptions } from "./types.ts"; -import { normalizeOutputPath } from "./output.ts"; +import { normalizeOutputPath } from "./output-shared.ts"; import { typstCompile, validateRequiredTypstVersion, diff --git a/src/command/render/output.ts b/src/command/render/output.ts index 9d42b8d2c9..a74714afdb 100644 --- a/src/command/render/output.ts +++ b/src/command/render/output.ts @@ -204,15 +204,4 @@ export function outputRecipe( } } -export function normalizeOutputPath(input: string, output: string) { - if (isAbsolute(output)) { - return output; - } else { - return relative( - dirname(input), - output, - ); - } -} - const kOutExt = "out"; diff --git a/src/core/handlers/mermaid.ts b/src/core/handlers/mermaid.ts index 67f33d13e5..77effcbfdf 100644 --- a/src/core/handlers/mermaid.ts +++ b/src/core/handlers/mermaid.ts @@ -38,7 +38,7 @@ import { Element } from "../deno-dom.ts"; import { convertFromYaml } from "../lib/yaml-schema/from-yaml.ts"; import { readYamlFromString } from "../yaml.ts"; import { pandocHtmlBlock, pandocRawStr } from "../pandoc/codegen.ts"; -import { LocalizedError } from "../lib/error.ts"; +import { LocalizedError } from "../lib/located-error.ts"; import { warning } from "log/mod.ts"; import { FormatDependency } from "../../config/types.ts"; import { mappedDiff } from "../mapped-text.ts"; diff --git a/src/core/lib/error.ts b/src/core/lib/error.ts index 0a8b50c0af..0ea6ead51e 100644 --- a/src/core/lib/error.ts +++ b/src/core/lib/error.ts @@ -5,8 +5,6 @@ * Copyright (C) 2020-2022 Posit Software, PBC */ -import { mappedIndexToLineCol, MappedString } from "./mapped-text.ts"; - export class InternalError extends Error { constructor( message: string, @@ -46,32 +44,6 @@ export class ErrorEx extends Error { public readonly printStack: boolean; } -export class LocalizedError extends Error { - constructor( - name: string, - message: string, - source: MappedString, - position = 0, - printName = true, - printStack = true, - ) { - const fileName = source.map(position)?.originalString?.fileName; - if (fileName) { - const { line, column } = mappedIndexToLineCol(source)(position); - message = `In file ${fileName} (${line + 1}:${column + 1}): -${message}`; - } - - super(message); - this.name = name; - this.printName = printName; - this.printStack = printStack; - } - - public readonly printName: boolean; - public readonly printStack: boolean; -} - export function asErrorEx(e: unknown) { if (e instanceof ErrorEx) { return e; diff --git a/src/core/lib/located-error.ts b/src/core/lib/located-error.ts new file mode 100644 index 0000000000..6d980bfa04 --- /dev/null +++ b/src/core/lib/located-error.ts @@ -0,0 +1,34 @@ +/* + * located-error.ts + * + * Copyright (C) 2020-2022 Posit Software, PBC + */ + +import { mappedIndexToLineCol, MappedString } from "./mapped-text.ts"; + +// TODO this is LocatedError, not LocalizedError +export class LocalizedError extends Error { + constructor( + name: string, + message: string, + source: MappedString, + position = 0, + printName = true, + printStack = true, + ) { + const fileName = source.map(position)?.originalString?.fileName; + if (fileName) { + const { line, column } = mappedIndexToLineCol(source)(position); + message = `In file ${fileName} (${line + 1}:${column + 1}): +${message}`; + } + + super(message); + this.name = name; + this.printName = printName; + this.printStack = printStack; + } + + public readonly printName: boolean; + public readonly printStack: boolean; +} diff --git a/src/core/path.ts b/src/core/path.ts index 57839358ef..f43506ca40 100644 --- a/src/core/path.ts +++ b/src/core/path.ts @@ -9,13 +9,14 @@ import { dirname, extname, fromFileUrl, - globToRegExp, isAbsolute, isGlob, join, normalize, } from "path/mod.ts"; +import { globToRegExp } from "https://deno.land/std@0.204.0/path/glob.ts"; + import { warning } from "log/mod.ts"; import { existsSync } from "fs/exists.ts"; diff --git a/src/core/puppeteer.ts b/src/core/puppeteer.ts index 37d6672072..1b41ae5a5d 100644 --- a/src/core/puppeteer.ts +++ b/src/core/puppeteer.ts @@ -7,9 +7,9 @@ import { readRegistryKey } from "./windows.ts"; import { which } from "./path.ts"; import { error, info } from "log/mod.ts"; -import { fetcher } from "../tools/impl/chromium.ts"; import { existsSync } from "fs/mod.ts"; import { UnreachableError } from "./lib/error.ts"; +import { quartoDataDir } from "./appdirs.ts"; // deno-lint-ignore no-explicit-any let puppeteerImport: any = undefined; @@ -280,3 +280,15 @@ async function fetchBrowser() { executablePath, }); } + +export async function fetcher() { + const options = { + path: chromiumInstallDir(), + }; + const fetcher = (await getPuppeteer()).createBrowserFetcher(options); + return fetcher; +} + +export function chromiumInstallDir(): string | undefined { + return quartoDataDir("chromium"); +} diff --git a/src/execute/engine.ts b/src/execute/engine.ts index deabace737..338b7fe2c0 100644 --- a/src/execute/engine.ts +++ b/src/execute/engine.ts @@ -28,6 +28,7 @@ import { RenderContext, RenderFlags } from "../command/render/types.ts"; import { mergeConfigs } from "../core/config.ts"; import { ProjectContext } from "../project/types.ts"; import { pandocBuiltInFormats } from "../core/pandoc/pandoc-formats.ts"; +import { gitignoreEntries } from "../project/project-gitignore.ts"; const kEngines: ExecutionEngine[] = [ knitrEngine, @@ -238,3 +239,9 @@ export function engineIgnoreDirs() { export function engineIgnoreGlobs() { return engineIgnoreDirs().map((ignore) => `**/${ignore}/**`); } + +export function projectIgnoreGlobs(dir: string) { + return engineIgnoreGlobs().concat( + gitignoreEntries(dir).map((ignore) => `**/${ignore}**`), + ); +} diff --git a/src/extension/extension.ts b/src/extension/extension.ts index addcaeadcd..c923ba2fdb 100644 --- a/src/extension/extension.ts +++ b/src/extension/extension.ts @@ -48,7 +48,7 @@ import { import { cloneDeep } from "../core/lodash.ts"; import { readAndValidateYamlFromFile } from "../core/schema/validated-yaml.ts"; import { getExtensionConfigSchema } from "../core/lib/yaml-schema/project-config.ts"; -import { projectIgnoreGlobs } from "../project/project-shared.ts"; +import { projectIgnoreGlobs } from "../execute/engine.ts"; import { ProjectType } from "../project/types/types.ts"; import { copyResourceFile } from "../project/project-resources.ts"; import { @@ -603,7 +603,7 @@ function validateExtension(extension: Extension) { if (contrib) { if (Array.isArray(contrib)) { contribCount = contribCount + contrib.length; - } else if (typeof (contrib) === "object") { + } else if (typeof contrib === "object") { contribCount = contribCount + Object.keys(contrib).length; } } @@ -774,7 +774,7 @@ function resolveRevealJSPlugin( dir: string, plugin: string | RevealPluginBundle | RevealPlugin, ) { - if (typeof (plugin) === "string") { + if (typeof plugin === "string") { // First attempt to load this plugin from an embedded extension const extensionId = toExtensionId(plugin); const extensions = findExtensions( @@ -811,7 +811,7 @@ function resolveRevealPlugin( plugin: string | RevealPluginBundle | RevealPluginInline, ): string | RevealPluginBundle | RevealPlugin { // Filters are expected to be absolute - if (typeof (plugin) === "string") { + if (typeof plugin === "string") { return join(extensionDir, plugin); } else if (isPluginRaw(plugin)) { return resolveRevealPluginInline(plugin, extensionDir); @@ -843,7 +843,7 @@ function resolveRevealPluginInline( ? plugin.script : [plugin.script]; resolvedPlugin.script = pluginArr.map((plug) => { - if (typeof (plug) === "string") { + if (typeof plug === "string") { return { path: plug, } as RevealPluginScript; @@ -911,7 +911,7 @@ function resolveFilter( dir: string, filter: QuartoFilter, ) { - if (typeof (filter) === "string") { + if (typeof filter === "string") { // First attempt to load this shortcode from an embedded extension const extensionId = toExtensionId(filter); const extensions = findExtensions( @@ -940,7 +940,7 @@ function resolveFilterPath( filter: QuartoFilter, ): QuartoFilter { // Filters are expected to be absolute - if (typeof (filter) === "string") { + if (typeof filter === "string") { if (isAbsolute(filter)) { return filter; } else { diff --git a/src/project/project-context.ts b/src/project/project-context.ts index ffe1a0b328..3f5dca37d7 100644 --- a/src/project/project-context.ts +++ b/src/project/project-context.ts @@ -4,14 +4,9 @@ * Copyright (C) 2020-2022 Posit Software, PBC */ -import { - dirname, - globToRegExp, - isAbsolute, - join, - relative, - SEP, -} from "path/mod.ts"; +import { dirname, isAbsolute, join, relative, SEP } from "path/mod.ts"; +import { globToRegExp } from "https://deno.land/std@0.204.0/path/glob.ts"; + import { existsSync, walkSync } from "fs/mod.ts"; import * as ld from "../core/lodash.ts"; @@ -60,6 +55,7 @@ import { engineIgnoreDirs, executionEngineIntermediateFiles, fileExecutionEngine, + projectIgnoreGlobs, } from "../execute/engine.ts"; import { kMarkdownEngine } from "../execute/types.ts"; @@ -69,9 +65,7 @@ import { ignoreFieldsForProjectType, normalizeFormatYaml, projectConfigFile, - projectIgnoreGlobs, projectVarsFile, - toInputRelativePaths, } from "./project-shared.ts"; import { RenderFlags } from "../command/render/types.ts"; import { kWebsite } from "./types/website/website-constants.ts"; diff --git a/src/project/project-shared.ts b/src/project/project-shared.ts index 44c33f7954..fa29b63639 100644 --- a/src/project/project-shared.ts +++ b/src/project/project-shared.ts @@ -13,8 +13,6 @@ import { getFrontMatterSchema } from "../core/lib/yaml-schema/front-matter.ts"; import { normalizePath, pathWithForwardSlashes } from "../core/path.ts"; import { readAndValidateYamlFromFile } from "../core/schema/validated-yaml.ts"; -import { engineIgnoreGlobs } from "../execute/engine.ts"; -import { gitignoreEntries } from "./project-gitignore.ts"; import { kProjectOutputDir, kProjectType, @@ -36,12 +34,6 @@ export function projectExcludeDirs(context: ProjectContext): string[] { } } -export function projectIgnoreGlobs(dir: string) { - return engineIgnoreGlobs().concat( - gitignoreEntries(dir).map((ignore) => `**/${ignore}**`), - ); -} - export function projectFormatOutputDir( format: Format, context: ProjectContext, @@ -235,7 +227,7 @@ export function deleteProjectMetadata(metadata: Metadata) { ); if (projType.metadataFields) { for (const field of projType.metadataFields().concat("project")) { - if (typeof (field) === "string") { + if (typeof field === "string") { delete metadata[field]; } else { for (const key of Object.keys(metadata)) { @@ -253,11 +245,11 @@ export function deleteProjectMetadata(metadata: Metadata) { export function normalizeFormatYaml(yamlFormat: unknown) { if (yamlFormat) { - if (typeof (yamlFormat) === "string") { + if (typeof yamlFormat === "string") { yamlFormat = { [yamlFormat]: {}, }; - } else if (typeof (yamlFormat) === "object") { + } else if (typeof yamlFormat === "object") { const formats = Object.keys(yamlFormat); for (const format of formats) { if ( diff --git a/src/project/types/manuscript/manuscript-meca.ts b/src/project/types/manuscript/manuscript-meca.ts index b058492d56..eedba937bd 100644 --- a/src/project/types/manuscript/manuscript-meca.ts +++ b/src/project/types/manuscript/manuscript-meca.ts @@ -14,14 +14,9 @@ import { contentType } from "../../../core/mime.ts"; import { kProjectType, ProjectContext } from "../../types.ts"; import { ProjectOutputFile } from "../types.ts"; -import { - dirname, - globToRegExp, - isAbsolute, - join, - relative, - SEP, -} from "path/mod.ts"; +import { globToRegExp } from "https://deno.land/std@0.204.0/path/glob.ts"; + +import { dirname, isAbsolute, join, relative, SEP } from "path/mod.ts"; import { copySync, ensureDirSync, moveSync, walkSync } from "fs/mod.ts"; import { kMecaVersion, MecaItem, MecaManifest, toXml } from "./meca.ts"; import { zip } from "../../../core/zip.ts"; @@ -70,7 +65,7 @@ export const shouldMakeMecaBundle = ( // See if we're producing JATS, then enable it return formats.find((format) => { - if (typeof (format) === "string") { + if (typeof format === "string") { return isJatsOutput(format); } else { return isJatsOutput(format.pandoc); diff --git a/src/project/types/website/listing/website-listing-read.ts b/src/project/types/website/listing/website-listing-read.ts index 45a44c1da9..cbf6872c02 100644 --- a/src/project/types/website/listing/website-listing-read.ts +++ b/src/project/types/website/listing/website-listing-read.ts @@ -107,7 +107,7 @@ import { projectOutputDir, } from "../../../project-shared.ts"; import { mergeConfigs } from "../../../../core/config.ts"; -import { globToRegExp } from "../../../../core/lib/glob.ts"; +import { globToRegExp } from "https://deno.land/std@0.204.0/path/glob.ts"; import { cslNames } from "../../../../core/csl.ts"; import { isHttpUrl } from "../../../../core/url.ts"; import { InternalError } from "../../../../core/lib/error.ts"; @@ -203,7 +203,7 @@ export async function readListings( const categories = firstListingValue(kFieldCategories, false); const parseCategoryStyle = (categories: unknown) => { - if (typeof (categories) === "string") { + if (typeof categories === "string") { switch (categories) { case "unnumbered": return "category-unnumbered"; @@ -226,7 +226,7 @@ export async function readListings( const feed = firstListingValue(kFeed, undefined); if (feed !== undefined) { - if (typeof (feed) === "object") { + if (typeof feed === "object") { // If is an object, forward it along sharedOptions[kFeed] = feed as ListingFeedOptions; } else if (feed) { @@ -317,7 +317,7 @@ export function completeListingItems( : [outputFile.format.metadata[kListing]]; listings.forEach((listing) => { - if (typeof (listing) === "object") { + if (typeof listing === "object") { debug(`[listing] Processing listing`); const listingMetadata = listing as Metadata; // See if there is a default image @@ -677,7 +677,7 @@ async function readContents( debug(`[listing] Reading listing '${listing.id}' from ${source}`); debug(`[listing] Contents: ${ listing.contents.map((lst) => { - return typeof (lst) === "string" ? lst : ""; + return typeof lst === "string" ? lst : ""; }).join(",") }`); @@ -747,11 +747,11 @@ async function readContents( }; const contentGlobs = listing.contents.filter((content) => { - return typeof (content) === "string"; + return typeof content === "string"; }) as string[]; const contentMetadatas = listing.contents.filter((content) => { - return typeof (content) !== "string"; + return typeof content !== "string"; }) as Metadata[]; if (contentGlobs.length > 0) { @@ -777,7 +777,7 @@ async function readContents( if (Array.isArray(yaml)) { const items = yaml as Array; for (const yamlItem of items) { - if (typeof (yamlItem) === "object") { + if (typeof yamlItem === "object") { const { item, source } = await listItemFromMeta( yamlItem as Metadata, project, @@ -795,7 +795,7 @@ async function readContents( ); } } - } else if (typeof (yaml) === "object") { + } else if (typeof yaml === "object") { const { item, source } = await listItemFromMeta( yaml as Metadata, project, @@ -857,7 +857,7 @@ async function readContents( listingValue: unknown, ) => { if ( - typeof (itemValue) === "string" && typeof (listingValue) === "string" + typeof itemValue === "string" && typeof listingValue === "string" ) { const regex = globToRegExp(listingValue); return itemValue.match(regex); @@ -1162,7 +1162,7 @@ function readDehydratedListings( ): ListingDehydrated[] { const listingConfig = format.metadata[kListing]; const listings: ListingDehydrated[] = []; - if (typeof (listingConfig) == "string") { + if (typeof listingConfig == "string") { // Resolve this string const listing = listingForType(listingType(listingConfig)); if (listing) { @@ -1171,7 +1171,7 @@ function readDehydratedListings( } else if (Array.isArray(listingConfig)) { // Process an array of listings const listingConfigs = listingConfig.filter((listing) => - typeof (listing) === "object" + typeof listing === "object" ); let count = 0; listings.push(...listingConfigs.map((listing) => { @@ -1184,7 +1184,7 @@ function readDehydratedListings( source, ); })); - } else if (listingConfig && typeof (listingConfig) === "object") { + } else if (listingConfig && typeof listingConfig === "object") { // Process an individual listing listings.push( listingForMetadata( @@ -1257,7 +1257,7 @@ function computeListingSort(rawValue: unknown): ListingSort[] | undefined { return undefined; } - if (typeof (sortValue) === "string") { + if (typeof sortValue === "string") { const sortStr = sortValue as string; const parts = sortStr.split(" "); if (parts.length === 2) { @@ -1274,7 +1274,7 @@ function computeListingSort(rawValue: unknown): ListingSort[] | undefined { } }; - if (typeof (rawValue) === "boolean") { + if (typeof rawValue === "boolean") { if (rawValue) { // Apply default sorting behavior return undefined; diff --git a/src/project/types/website/website-navigation.ts b/src/project/types/website/website-navigation.ts index 130b7ddc9e..e835b22626 100644 --- a/src/project/types/website/website-navigation.ts +++ b/src/project/types/website/website-navigation.ts @@ -17,6 +17,11 @@ import { warnOnce } from "../../../core/log.ts"; import { asHtmlId } from "../../../core/html.ts"; import { sassLayer } from "../../../core/sass.ts"; import { removeChapterNumber } from "./website-utils.ts"; +import { + breadCrumbs, + itemHasNavTarget, + sidebarForHref, +} from "./website-shared.ts"; import { Format, @@ -135,10 +140,7 @@ import { expandAutoSidebarItems } from "./website-sidebar-auto.ts"; import { resolveProjectInputLinks } from "../project-utilities.ts"; import { dashboardScssLayer } from "../../../format/dashboard/format-dashboard-shared.ts"; -// static navigation (initialized during project preRender) -const navigation: Navigation = { - sidebars: [], -}; +import { navigation } from "./website-shared.ts"; export const kSidebarLogo = "logo"; @@ -835,7 +837,7 @@ function makeBreadCrumbs(doc: Document, clz?: string[]) { if (item.href) { const linkEl = doc.createElement("a"); linkEl.setAttribute("href", item.href); - if (typeof (contents) === "string") { + if (typeof contents === "string") { linkEl.innerHTML = contents; } else { linkEl.appendChild(contents); @@ -844,7 +846,7 @@ function makeBreadCrumbs(doc: Document, clz?: string[]) { liEl.appendChild(linkEl); return liEl; } else { - if (typeof (contents) === "string") { + if (typeof contents === "string") { liEl.innerHTML = item.text || ""; } else { liEl.appendChild(contents); @@ -1044,29 +1046,6 @@ function validateTool(tool: SidebarTool) { } } -export function sidebarForHref(href: string, format: Format) { - // if there is a single sidebar then it applies to all hrefs - if (navigation.sidebars.length === 1) { - return navigation.sidebars[0]; - } else { - const explicitSidebar = navigation.sidebars.find((sidebar) => { - return sidebar.id === format.metadata[kSiteSidebar]; - }); - if (explicitSidebar) { - return explicitSidebar; - } else { - const containingSidebar = navigation.sidebars.find((sidebar) => { - return containsHref(href, sidebar.contents); - }); - if (containingSidebar) { - return containingSidebar; - } else { - return undefined; - } - } - } -} - function sidebarStyle() { if (navigation.sidebars.length > 0) { return navigation.sidebars[0].style; @@ -1075,25 +1054,6 @@ function sidebarStyle() { } } -function containsHref(href: string, items: SidebarItem[]) { - for (let i = 0; i < items.length; i++) { - if (items[i].href && items[i].href === href) { - return true; - } else if (Object.keys(items[i]).includes("contents")) { - const subItems = items[i].contents || []; - const subItemsHasHref = containsHref(href, subItems); - if (subItemsHasHref) { - return true; - } - } else { - if (itemHasNavTarget(items[i], href)) { - return true; - } - } - } - return false; -} - function expandedSidebar(href: string, sidebar?: Sidebar): Sidebar | undefined { if (sidebar) { // Walk through menu and mark any items as 'expanded' if they @@ -1124,11 +1084,6 @@ function expandedSidebar(href: string, sidebar?: Sidebar): Sidebar | undefined { } } -function itemHasNavTarget(item: SidebarItem, href: string) { - return item.href === href || - item.href === href.replace(/\/index\.html/, "/"); -} - function isSeparator(item?: SidebarItem) { return !!item && !!item.text?.match(/^\-\-\-[\-\s]*$/); } @@ -1174,48 +1129,6 @@ function nextAndPrevious( } } -export function breadCrumbs(href: string, sidebar?: Sidebar) { - if (sidebar?.contents) { - const crumbs: SidebarItem[] = []; - - // find the href in the sidebar - const makeBreadCrumbs = (href: string, sidebarItems?: SidebarItem[]) => { - if (sidebarItems) { - for (const item of sidebarItems) { - if (item.href === href) { - crumbs.push(item); - return true; - } else { - if (item.contents) { - if (makeBreadCrumbs(href, item.contents)) { - // If this 'section' doesn't have an href, then just use the first - // child as the href - const breadCrumbItem = { ...item }; - if ( - !breadCrumbItem.href && breadCrumbItem.contents && - breadCrumbItem.contents.length > 0 - ) { - breadCrumbItem.href = breadCrumbItem.contents[0].href; - } - - crumbs.push(breadCrumbItem); - return true; - } - } - } - } - return false; - } else { - return false; - } - }; - makeBreadCrumbs(href, sidebar.contents); - return crumbs.reverse(); - } else { - return []; - } -} - async function navbarEjsData( project: ProjectContext, navbar: Navbar, @@ -1545,17 +1458,17 @@ function resolveNavReferences( collection: Array | Record, ) => { const assign = (value: unknown) => { - if (typeof (index) === "number") { + if (typeof index === "number") { (collection as Array)[index] = value; - } else if (typeof (index) === "string") { + } else if (typeof index === "string") { (collection as Record)[index] = value; } }; if (Array.isArray(value)) { assign(resolveNavReferences(value)); - } else if (typeof (value) === "object") { + } else if (typeof value === "object") { assign(resolveNavReferences(value as Record)); - } else if (typeof (value) === "string") { + } else if (typeof value === "string") { const navRef = resolveNavReference(value); if (navRef) { const navItem = collection as Record; diff --git a/src/project/types/website/website-search.ts b/src/project/types/website/website-search.ts index 9c8edb6272..732cae7acf 100644 --- a/src/project/types/website/website-search.ts +++ b/src/project/types/website/website-search.ts @@ -51,7 +51,7 @@ import { pathWithForwardSlashes } from "../../../core/path.ts"; import { isHtmlFileOutput } from "../../../config/format.ts"; import { projectIsBook } from "../../project-shared.ts"; import { encodeHtml } from "../../../core/html.ts"; -import { breadCrumbs, sidebarForHref } from "./website-navigation.ts"; +import { breadCrumbs, sidebarForHref } from "./website-shared.ts"; // The main search key export const kSearch = "search"; @@ -428,9 +428,9 @@ export function searchOptions( function searchInputLimit( searchConfig: string | Record | undefined, ) { - if (searchConfig && typeof (searchConfig) === "object") { + if (searchConfig && typeof searchConfig === "object") { const limit = searchConfig[kLimit]; - if (typeof (limit) === "number") { + if (typeof limit === "number") { return limit; } } @@ -440,7 +440,7 @@ function searchInputLimit( function searchKbdShortcut( searchConfig: string | Record | undefined, ) { - if (searchConfig && typeof (searchConfig) === "object") { + if (searchConfig && typeof searchConfig === "object") { const kbd = searchConfig[kKbShortcutSearch]; if (Array.isArray(kbd)) { return kbd; @@ -454,7 +454,7 @@ function searchKbdShortcut( function searchShowItemContext( searchConfig: string | Record | undefined, ) { - if (searchConfig && typeof (searchConfig) === "object") { + if (searchConfig && typeof searchConfig === "object") { return searchConfig[kShowItemContext] as | boolean | "tree" @@ -469,7 +469,7 @@ function searchType( userType: unknown, location: SearchInputLocation, ): "overlay" | "textbox" { - if (userType && typeof (userType) === "string") { + if (userType && typeof userType === "string") { switch (userType) { case "overlay": return "overlay"; @@ -491,7 +491,7 @@ function algoliaOptions( project: ProjectContext, ) { const algoliaRaw = searchConfig[kAlgolia]; - if (algoliaRaw && typeof (algoliaRaw) === "object") { + if (algoliaRaw && typeof algoliaRaw === "object") { const algoliaObj = algoliaRaw as SearchOptionsAlgolia; const applicationId = algoliaObj[kSearchApplicationId]; const apiKey = algoliaObj[kSearchOnlyApiKey]; diff --git a/src/project/types/website/website-shared.ts b/src/project/types/website/website-shared.ts index 596e68cc78..099aa892ad 100644 --- a/src/project/types/website/website-shared.ts +++ b/src/project/types/website/website-shared.ts @@ -1,9 +1,8 @@ /* -* website-shared.ts -* -* Copyright (C) 2020-2022 Posit Software, PBC -* -*/ + * website-shared.ts + * + * Copyright (C) 2020-2022 Posit Software, PBC + */ import { join } from "path/mod.ts"; @@ -103,9 +102,9 @@ export function websiteNavigationConfig(project: ProjectContext) { let navbar = websiteConfig(kSiteNavbar, project.config) as | Navbar | undefined; - if (typeof (navbar) === "boolean" && navbar) { + if (typeof navbar === "boolean" && navbar) { navbar = { background: "primary" }; - } else if (typeof (navbar) !== "object") { + } else if (typeof navbar !== "object") { navbar = undefined; } @@ -114,7 +113,7 @@ export function websiteNavigationConfig(project: ProjectContext) { const sidebars = (Array.isArray(sidebar) ? sidebar - : typeof (sidebar) == "object" + : typeof sidebar == "object" ? [sidebar] : undefined) as Sidebar[] | undefined; @@ -213,7 +212,7 @@ export function resolvePageFooter(config?: ProjectConfig) { const footerValue = ( value?: unknown, ): string | NavItem[] | undefined => { - if (typeof (value) === "string") { + if (typeof value === "string") { return value as string; } else if (Array.isArray(value)) { return value as NavItem[]; @@ -224,7 +223,7 @@ export function resolvePageFooter(config?: ProjectConfig) { const footer: NavigationFooter = {}; const footerConfig = websiteConfig(kPageFooter, config); - if (typeof (footerConfig) === "string") { + if (typeof footerConfig === "string") { // place the markdown in the center footer.center = footerConfig; } else if (!Array.isArray(footerConfig)) { @@ -252,3 +251,97 @@ export function flattenItems( sidebarItems.forEach(flatten); return items; } + +export function breadCrumbs(href: string, sidebar?: Sidebar) { + if (sidebar?.contents) { + const crumbs: SidebarItem[] = []; + + // find the href in the sidebar + const makeBreadCrumbs = (href: string, sidebarItems?: SidebarItem[]) => { + if (sidebarItems) { + for (const item of sidebarItems) { + if (item.href === href) { + crumbs.push(item); + return true; + } else { + if (item.contents) { + if (makeBreadCrumbs(href, item.contents)) { + // If this 'section' doesn't have an href, then just use the first + // child as the href + const breadCrumbItem = { ...item }; + if ( + !breadCrumbItem.href && breadCrumbItem.contents && + breadCrumbItem.contents.length > 0 + ) { + breadCrumbItem.href = breadCrumbItem.contents[0].href; + } + + crumbs.push(breadCrumbItem); + return true; + } + } + } + } + return false; + } else { + return false; + } + }; + makeBreadCrumbs(href, sidebar.contents); + return crumbs.reverse(); + } else { + return []; + } +} + +// static navigation (initialized during project preRender) +export const navigation: Navigation = { + sidebars: [], +}; + +export function sidebarForHref(href: string, format: Format) { + // if there is a single sidebar then it applies to all hrefs + if (navigation.sidebars.length === 1) { + return navigation.sidebars[0]; + } else { + const explicitSidebar = navigation.sidebars.find((sidebar) => { + return sidebar.id === format.metadata[kSiteSidebar]; + }); + if (explicitSidebar) { + return explicitSidebar; + } else { + const containingSidebar = navigation.sidebars.find((sidebar) => { + return containsHref(href, sidebar.contents); + }); + if (containingSidebar) { + return containingSidebar; + } else { + return undefined; + } + } + } +} + +export function containsHref(href: string, items: SidebarItem[]) { + for (let i = 0; i < items.length; i++) { + if (items[i].href && items[i].href === href) { + return true; + } else if (Object.keys(items[i]).includes("contents")) { + const subItems = items[i].contents || []; + const subItemsHasHref = containsHref(href, subItems); + if (subItemsHasHref) { + return true; + } + } else { + if (itemHasNavTarget(items[i], href)) { + return true; + } + } + } + return false; +} + +export function itemHasNavTarget(item: SidebarItem, href: string) { + return item.href === href || + item.href === href.replace(/\/index\.html/, "/"); +} diff --git a/src/publish/gh-pages/gh-pages.ts b/src/publish/gh-pages/gh-pages.ts index ef7349dbc2..68e557d3ad 100644 --- a/src/publish/gh-pages/gh-pages.ts +++ b/src/publish/gh-pages/gh-pages.ts @@ -17,10 +17,10 @@ import { execProcess } from "../../core/process.ts"; import { ProjectContext } from "../../project/types.ts"; import { AccountToken, + AccountTokenType, PublishFiles, PublishProvider, } from "../provider-types.ts"; -import { anonymousAccount } from "../provider.ts"; import { PublishOptions, PublishRecord } from "../types.ts"; import { shortUuid } from "../../core/uuid.ts"; import { sleep } from "../../core/wait.ts"; @@ -50,6 +50,15 @@ export const ghpagesProvider: PublishProvider = { isNotFound, }; +function anonymousAccount(): AccountToken { + return { + type: AccountTokenType.Anonymous, + name: "anonymous", + server: null, + token: "anonymous", + }; +} + function accountTokens() { return Promise.resolve([anonymousAccount()]); } @@ -419,11 +428,11 @@ const throwUnableToPublish = (reason: string) => { async function gitHubContextForPublish(input: string | ProjectContext) { // Create the base context - const dir = typeof (input) === "string" ? dirname(input) : input.dir; + const dir = typeof input === "string" ? dirname(input) : input.dir; const context = await gitHubContext(dir); // always prefer configured website URL - if (typeof (input) !== "string") { + if (typeof input !== "string") { const configSiteUrl = websiteBaseurl(input?.config); if (configSiteUrl) { context.siteUrl = configSiteUrl; diff --git a/src/publish/provider.ts b/src/publish/provider.ts index 05c56a0e52..e4d3c2e461 100644 --- a/src/publish/provider.ts +++ b/src/publish/provider.ts @@ -11,21 +11,12 @@ import { rsconnectProvider } from "./rsconnect/rsconnect.ts"; import { positCloudProvider } from "./posit-cloud/posit-cloud.ts"; import { confluenceProvider } from "./confluence/confluence.ts"; import { PublishProvider } from "./provider-types.ts"; -import { AccountToken, AccountTokenType } from "./provider-types.ts"; +import { AccountToken } from "./provider-types.ts"; export function accountTokenText(token: AccountToken) { return token.name + (token.server ? ` (${token.server})` : ""); } -export function anonymousAccount(): AccountToken { - return { - type: AccountTokenType.Anonymous, - name: "anonymous", - server: null, - token: "anonymous", - }; -} - const kPublishProviders = [ quartoPubProvider, ghpagesProvider, diff --git a/src/tools/impl/chromium.ts b/src/tools/impl/chromium.ts index 8a3af2be37..1ffd3047a8 100644 --- a/src/tools/impl/chromium.ts +++ b/src/tools/impl/chromium.ts @@ -2,10 +2,13 @@ * chromium.ts * * Copyright (C) 2020-2022 Posit Software, PBC - * */ -import { getPuppeteer } from "../../core/puppeteer.ts"; +import { + chromiumInstallDir, + fetcher, + getPuppeteer, +} from "../../core/puppeteer.ts"; import { clearLine, progressBar, @@ -14,11 +17,6 @@ import { } from "../../core/console.ts"; import { InstallableTool, InstallContext, PackageInfo } from "../types.ts"; -import { quartoDataDir } from "../../core/appdirs.ts"; - -export function chromiumInstallDir(): string | undefined { - return quartoDataDir("chromium"); -} async function installDir() { if (await installed()) { @@ -76,7 +74,7 @@ async function preparePackage(_ctx: InstallContext): Promise { `Downloading Chromium ${revision}`, ); // const spin = spinner("Installing"); - let spinnerStatus: ((() => void) | undefined); + let spinnerStatus: (() => void) | undefined; const fetcherObj = await fetcher(); const revisionInfo = await fetcherObj.download( revision, @@ -120,14 +118,6 @@ async function uninstall(_ctx: InstallContext): Promise { return Promise.resolve(); } -export async function fetcher() { - const options = { - path: chromiumInstallDir(), - }; - const fetcher = (await getPuppeteer()).createBrowserFetcher(options); - return fetcher; -} - // TODO: https://github.com/puppeteer/puppeteer/blob/main/versions.js async function supportedRevision(): Promise { return (await getPuppeteer())._preferredRevision;