make path and globbing more platform invariant
1 files added
6 files modified
| | |
| | | enableSiteMap: true, |
| | | enableRSS: true, |
| | | }), |
| | | Plugin.Assets({ attachmentsFolder: "attachments" }), |
| | | Plugin.Assets(), |
| | | Plugin.Static(), |
| | | ], |
| | | }, |
| | |
| | | import path from "path" |
| | | import { PerfTimer } from "./perf" |
| | | import { rimraf } from "rimraf" |
| | | import { globby, isGitIgnored } from "globby" |
| | | import { isGitIgnored } from "globby" |
| | | import chalk from "chalk" |
| | | import http from "http" |
| | | import serveHandler from "serve-handler" |
| | |
| | | import { ProcessedContent } from "./plugins/vfile" |
| | | import WebSocket, { WebSocketServer } from "ws" |
| | | import { Argv, BuildCtx } from "./ctx" |
| | | import { glob, toPosixPath } from "./glob" |
| | | |
| | | async function buildQuartz(argv: Argv, version: string) { |
| | | const ctx: BuildCtx = { |
| | |
| | | console.log(`Cleaned output directory \`${output}\` in ${perf.timeSince("clean")}`) |
| | | |
| | | perf.addEvent("glob") |
| | | const fps = ( |
| | | await globby("**/*.md", { |
| | | cwd: argv.directory, |
| | | ignore: cfg.configuration.ignorePatterns, |
| | | gitignore: true, |
| | | }) |
| | | ).map((fp) => fp.split(path.sep).join(path.posix.sep)) |
| | | const fps = await glob("**/*.md", argv.directory, cfg.configuration.ignorePatterns) |
| | | console.log( |
| | | `Found ${fps.length} input files from \`${argv.directory}\` in ${perf.timeSince("glob")}`, |
| | | ) |
| | |
| | | let toRebuild: Set<FilePath> = new Set() |
| | | let toRemove: Set<FilePath> = new Set() |
| | | async function rebuild(fp: string, action: "add" | "change" | "delete") { |
| | | fp = fp.split(path.sep).join(path.posix.sep) |
| | | fp = toPosixPath(fp) |
| | | if (!ignored(fp)) { |
| | | const filePath = joinSegments(argv.directory, fp) as FilePath |
| | | if (action === "add" || action === "change") { |
| New file |
| | |
| | | import path from "path"; |
| | | import { FilePath } from "./path"; |
| | | import { globby } from "globby"; |
| | | |
| | | export function toPosixPath(fp: string): string { |
| | | return fp.split(path.sep).join("/") |
| | | } |
| | | |
| | | export async function glob(pattern: string, cwd: string, ignorePatterns: string[]): Promise<FilePath[]> { |
| | | const fps = ( |
| | | await globby(pattern, { |
| | | cwd, |
| | | ignore: ignorePatterns, |
| | | gitignore: true, |
| | | }) |
| | | ).map(toPosixPath) |
| | | return fps as FilePath[] |
| | | } |
| | |
| | | import { globbyStream } from "globby" |
| | | import { FilePath, slugifyFilePath } from "../../path" |
| | | import { FilePath, joinSegments, slugifyFilePath } from "../../path" |
| | | import { QuartzEmitterPlugin } from "../types" |
| | | import path from "path" |
| | | import fs from "fs" |
| | | import { glob } from "../../glob" |
| | | |
| | | interface Options { |
| | | attachmentsFolder: string | null |
| | | } |
| | | |
| | | const defaultOptions: Options = { |
| | | attachmentsFolder: null, |
| | | } |
| | | |
| | | export const Assets: QuartzEmitterPlugin<Options> = (userOpts?: Options) => { |
| | | const { attachmentsFolder } = { ...defaultOptions, ...userOpts } |
| | | |
| | | export const Assets: QuartzEmitterPlugin = () => { |
| | | return { |
| | | name: "Assets", |
| | | getQuartzComponents() { |
| | | return [] |
| | | }, |
| | | async emit({ argv }, _content, _resources, _emit): Promise<FilePath[]> { |
| | | async emit({ argv, cfg }, _content, _resources, _emit): Promise<FilePath[]> { |
| | | // glob all non MD/MDX/HTML files in content folder and copy it over |
| | | const assetsPath = path.join(argv.output, "assets") |
| | | |
| | | const fps: FilePath[] = [] |
| | | for await (const rawFp of globbyStream("**", { |
| | | ignore: ["**/*.md"], |
| | | cwd: argv.directory, |
| | | })) { |
| | | const fp = rawFp as FilePath |
| | | const assetsPath = joinSegments(argv.output, "assets") |
| | | const fps = await glob("**", argv.directory, ["**/*.md", ...cfg.configuration.ignorePatterns]) |
| | | const res: FilePath[] = [] |
| | | for (const fp of fps) { |
| | | const ext = path.extname(fp) |
| | | const src = path.join(argv.directory, fp) as FilePath |
| | | let name = (slugifyFilePath(fp as FilePath) + ext) as FilePath |
| | | const src = joinSegments(argv.directory, fp) as FilePath |
| | | const name = (slugifyFilePath(fp as FilePath) + ext) as FilePath |
| | | |
| | | if (attachmentsFolder) { |
| | | const segments = name.split("/") |
| | | if (segments.at(-2) === attachmentsFolder) { |
| | | segments.splice(-2, 1) |
| | | name = segments.join("/") as FilePath |
| | | } |
| | | } |
| | | |
| | | const dest = path.join(assetsPath, name) as FilePath |
| | | const dest = joinSegments(assetsPath, name) as FilePath |
| | | const dir = path.dirname(dest) as FilePath |
| | | await fs.promises.mkdir(dir, { recursive: true }) // ensure dir exists |
| | | await fs.promises.copyFile(src, dest) |
| | | fps.push(path.join("assets", fp) as FilePath) |
| | | res.push(joinSegments("assets", fp) as FilePath) |
| | | } |
| | | |
| | | return fps |
| | | return res |
| | | }, |
| | | } |
| | | } |
| | |
| | | import { globby } from "globby" |
| | | import { FilePath, QUARTZ } from "../../path" |
| | | import { FilePath, QUARTZ, joinSegments } from "../../path" |
| | | import { QuartzEmitterPlugin } from "../types" |
| | | import path from "path" |
| | | import fs from "fs" |
| | | import { glob } from "../../glob" |
| | | |
| | | export const Static: QuartzEmitterPlugin = () => ({ |
| | | name: "Static", |
| | | getQuartzComponents() { |
| | | return [] |
| | | }, |
| | | async emit({ argv }, _content, _resources, _emit): Promise<FilePath[]> { |
| | | async emit({ argv, cfg }, _content, _resources, _emit): Promise<FilePath[]> { |
| | | const staticPath = path.join(QUARTZ, "static") |
| | | const fps = await globby("*", { cwd: staticPath }) |
| | | await fs.promises.cp(staticPath, path.join(argv.output, "static"), { recursive: true }) |
| | | return fps.map((fp) => path.join("static", fp)) as FilePath[] |
| | | const fps = await glob("**", staticPath, cfg.configuration.ignorePatterns) |
| | | await fs.promises.cp(staticPath, joinSegments(argv.output, "static"), { recursive: true }) |
| | | return fps.map((fp) => joinSegments("static", fp)) as FilePath[] |
| | | }, |
| | | }) |
| | |
| | | if (!isAbsoluteUrl(node.properties.src)) { |
| | | const ext = path.extname(node.properties.src) |
| | | node.properties.src = |
| | | transformLink(path.join("assets", node.properties.src)) + ext |
| | | transformLink(joinSegments("assets", node.properties.src)) + ext |
| | | } |
| | | } |
| | | }) |
| | |
| | | import { getStaticResourcesFromPlugins } from "../plugins" |
| | | import { EmitCallback } from "../plugins/types" |
| | | import { ProcessedContent } from "../plugins/vfile" |
| | | import { FilePath } from "../path" |
| | | import { FilePath, joinSegments } from "../path" |
| | | import { QuartzLogger } from "../log" |
| | | import { trace } from "../trace" |
| | | import { BuildCtx } from "../ctx" |
| | |
| | | |
| | | log.start(`Emitting output files`) |
| | | const emit: EmitCallback = async ({ slug, ext, content }) => { |
| | | const pathToPage = path.join(argv.output, slug + ext) as FilePath |
| | | const pathToPage = joinSegments(argv.output, slug + ext) as FilePath |
| | | const dir = path.dirname(pathToPage) |
| | | await fs.promises.mkdir(dir, { recursive: true }) |
| | | await fs.promises.writeFile(pathToPage, content) |