fix relative path resolution in router and link crawling
34 files modified
10 files renamed
| | |
| | | - debounce cfg rebuild on large repos |
| | | - investigate content rebuild triggering multiple times even when debounced, causing an esbuild deadlock |
| | | - dereference symlink for npx quartz sync |
| | | - test/fix with subpath |
| | | - fix docs with deploy from github |
| | | |
| | | ## high priority backlog |
| | | |
| | |
| | | uses: actions/deploy-pages@v2 |
| | | ``` |
| | | |
| | | Then, commit these changes by doing `npx quartz sync`. This should deploy your site to `<github-username>.github.io/<repository-name>`. |
| | | Then: |
| | | |
| | | 1. Head to "Settings" tab of your forked repository and in the sidebar, click "Pages". Under "Source", select "GitHub Actions". |
| | | 2. Commit these changes by doing `npx quartz sync`. This should deploy your site to `<github-username>.github.io/<repository-name>`. |
| | | |
| | | ### Custom Domain |
| | | |
| | |
| | | "@types/js-yaml": "^4.0.5", |
| | | "@types/node": "^20.1.2", |
| | | "@types/pretty-time": "^1.1.2", |
| | | "@types/serve-handler": "^6.1.1", |
| | | "@types/source-map-support": "^0.5.6", |
| | | "@types/workerpool": "^6.4.0", |
| | | "@types/ws": "^8.5.5", |
| | |
| | | "integrity": "sha512-4i+Y+O5H80Rh01lY/3Z0hB/UWc4R64ReE83joEpVsIG3iQWpYx66k6pQh1amJNZquKtJQyu/RcfkTtvL0KwssA==", |
| | | "dev": true |
| | | }, |
| | | "node_modules/@types/serve-handler": { |
| | | "version": "6.1.1", |
| | | "resolved": "https://registry.npmjs.org/@types/serve-handler/-/serve-handler-6.1.1.tgz", |
| | | "integrity": "sha512-bIwSmD+OV8w0t2e7EWsuQYlGoS1o5aEdVktgkXaa43Zm0qVWi21xaSRb3DQA1UXD+DJ5bRq1Rgu14ZczB+CjIQ==", |
| | | "dev": true, |
| | | "dependencies": { |
| | | "@types/node": "*" |
| | | } |
| | | }, |
| | | "node_modules/@types/source-map-support": { |
| | | "version": "0.5.6", |
| | | "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.5.6.tgz", |
| | |
| | | "scripts": { |
| | | "check": "tsc --noEmit && npx prettier . --check", |
| | | "format": "npx prettier . --write", |
| | | "test": "tsx ./quartz/path.test.ts", |
| | | "test": "tsx ./quartz/util/path.test.ts", |
| | | "profile": "0x -D prof ./quartz/bootstrap-cli.mjs build --concurrency=1" |
| | | }, |
| | | "keywords": [ |
| | |
| | | "@types/js-yaml": "^4.0.5", |
| | | "@types/node": "^20.1.2", |
| | | "@types/pretty-time": "^1.1.2", |
| | | "@types/serve-handler": "^6.1.1", |
| | | "@types/source-map-support": "^0.5.6", |
| | | "@types/workerpool": "^6.4.0", |
| | | "@types/ws": "^8.5.5", |
| | |
| | | default: false, |
| | | describe: "run a local server to live-preview your Quartz", |
| | | }, |
| | | baseDir: { |
| | | string: true, |
| | | describe: "base path to serve your local server on", |
| | | }, |
| | | port: { |
| | | number: true, |
| | | default: 8080, |
| | |
| | | |
| | | await build(clientRefresh) |
| | | const server = http.createServer(async (req, res) => { |
| | | const serve = async (fp) => { |
| | | await serveHandler(req, res, { |
| | | public: argv.output, |
| | | directoryListing: false, |
| | | trailingSlash: true, |
| | | }) |
| | | const status = res.statusCode |
| | | const statusString = |
| | | status >= 200 && status < 300 |
| | | ? chalk.green(`[${status}]`) |
| | | : status >= 300 && status < 400 |
| | | ? chalk.yellow(`[${status}]`) |
| | | : chalk.red(`[${status}]`) |
| | | status >= 200 && status < 300 ? chalk.green(`[${status}]`) : chalk.red(`[${status}]`) |
| | | console.log(statusString + chalk.grey(` ${req.url}`)) |
| | | } |
| | | |
| | | const redirect = (newFp) => { |
| | | res.writeHead(301, { |
| | | Location: newFp, |
| | | }) |
| | | console.log(chalk.yellow("[301]") + chalk.grey(` ${req.url} -> ${newFp}`)) |
| | | return res.end() |
| | | } |
| | | |
| | | let fp = req.url?.split("?")[0] ?? "/" |
| | | |
| | | // handle redirects |
| | | if (fp.endsWith("/")) { |
| | | // /trailing/ |
| | | // does /trailing/index.html exist? if so, serve it |
| | | const indexFp = path.posix.join(fp, "index.html") |
| | | if (fs.existsSync(path.posix.join(argv.output, indexFp))) { |
| | | return serve(indexFp) |
| | | } |
| | | |
| | | // does /trailing.html exist? if so, redirect to /trailing |
| | | let base = fp.slice(0, -1) |
| | | if (path.extname(base) === "") { |
| | | base += ".html" |
| | | } |
| | | if (fs.existsSync(path.posix.join(argv.output, base))) { |
| | | return redirect(base) |
| | | } |
| | | } else { |
| | | // /regular |
| | | // does /regular.html exist? if so, serve it |
| | | let base = fp |
| | | if (path.extname(base) === "") { |
| | | base += ".html" |
| | | } |
| | | if (fs.existsSync(path.posix.join(argv.output, base))) { |
| | | return serve(base) |
| | | } |
| | | |
| | | // does /regular/index.html exist? if so, redirect to /regular/ |
| | | let indexFp = path.posix.join(fp, "index.html") |
| | | if (fs.existsSync(path.posix.join(argv.output, indexFp))) { |
| | | return redirect(fp + "/") |
| | | } |
| | | } |
| | | |
| | | return serve(fp) |
| | | }) |
| | | server.listen(argv.port) |
| | | console.log(chalk.cyan(`Started a Quartz server listening at http://localhost:${argv.port}`)) |
| | |
| | | import sourceMapSupport from "source-map-support" |
| | | sourceMapSupport.install(options) |
| | | import path from "path" |
| | | import { PerfTimer } from "./perf" |
| | | import { PerfTimer } from "./util/perf" |
| | | import { rimraf } from "rimraf" |
| | | import { isGitIgnored } from "globby" |
| | | import chalk from "chalk" |
| | |
| | | import { filterContent } from "./processors/filter" |
| | | import { emitContent } from "./processors/emit" |
| | | import cfg from "../quartz.config" |
| | | import { FilePath, joinSegments, slugifyFilePath } from "./path" |
| | | import { FilePath, joinSegments, slugifyFilePath } from "./util/path" |
| | | import chokidar from "chokidar" |
| | | import { ProcessedContent } from "./plugins/vfile" |
| | | import { Argv, BuildCtx } from "./ctx" |
| | | import { glob, toPosixPath } from "./glob" |
| | | import { trace } from "./trace" |
| | | import { options } from "./sourcemap" |
| | | import { Argv, BuildCtx } from "./util/ctx" |
| | | import { glob, toPosixPath } from "./util/glob" |
| | | import { trace } from "./util/trace" |
| | | import { options } from "./util/sourcemap" |
| | | |
| | | async function buildQuartz(argv: Argv, clientRefresh: () => void) { |
| | | const ctx: BuildCtx = { |
| | |
| | | import { QuartzComponentConstructor, QuartzComponentProps } from "./types" |
| | | import style from "./styles/backlinks.scss" |
| | | import { canonicalizeServer, resolveRelative } from "../path" |
| | | import { canonicalizeServer, resolveRelative } from "../util/path" |
| | | |
| | | function Backlinks({ fileData, allFiles }: QuartzComponentProps) { |
| | | const slug = canonicalizeServer(fileData.slug!) |
| | |
| | | import { canonicalizeServer, pathToRoot } from "../path" |
| | | import { JSResourceToScriptElement } from "../resources" |
| | | import { canonicalizeServer, pathToRoot } from "../util/path" |
| | | import { JSResourceToScriptElement } from "../util/resources" |
| | | import { QuartzComponentConstructor, QuartzComponentProps } from "./types" |
| | | |
| | | export default (() => { |
| | |
| | | import { CanonicalSlug, canonicalizeServer, resolveRelative } from "../path" |
| | | import { CanonicalSlug, canonicalizeServer, resolveRelative } from "../util/path" |
| | | import { QuartzPluginData } from "../plugins/vfile" |
| | | import { Date } from "./Date" |
| | | import { QuartzComponentProps } from "./types" |
| | |
| | | import { canonicalizeServer, pathToRoot } from "../path" |
| | | import { canonicalizeServer, pathToRoot } from "../util/path" |
| | | import { QuartzComponentConstructor, QuartzComponentProps } from "./types" |
| | | |
| | | function PageTitle({ fileData, cfg }: QuartzComponentProps) { |
| | |
| | | import { canonicalizeServer, pathToRoot, slugTag } from "../path" |
| | | import { canonicalizeServer, pathToRoot, slugTag } from "../util/path" |
| | | import { QuartzComponentConstructor, QuartzComponentProps } from "./types" |
| | | |
| | | function TagList({ fileData }: QuartzComponentProps) { |
| | |
| | | |
| | | import style from "../styles/listPage.scss" |
| | | import { PageList } from "../PageList" |
| | | import { canonicalizeServer } from "../../path" |
| | | import { canonicalizeServer } from "../../util/path" |
| | | |
| | | function FolderContent(props: QuartzComponentProps) { |
| | | const { tree, fileData, allFiles } = props |
| | |
| | | import { toJsxRuntime } from "hast-util-to-jsx-runtime" |
| | | import style from "../styles/listPage.scss" |
| | | import { PageList } from "../PageList" |
| | | import { ServerSlug, canonicalizeServer, getAllSegmentPrefixes } from "../../path" |
| | | import { ServerSlug, canonicalizeServer, getAllSegmentPrefixes } from "../../util/path" |
| | | import { QuartzPluginData } from "../../plugins/vfile" |
| | | |
| | | const numPages = 10 |
| | |
| | | import { QuartzComponent, QuartzComponentProps } from "./types" |
| | | import HeaderConstructor from "./Header" |
| | | import BodyConstructor from "./Body" |
| | | import { JSResourceToScriptElement, StaticResources } from "../resources" |
| | | import { CanonicalSlug, pathToRoot } from "../path" |
| | | import { JSResourceToScriptElement, StaticResources } from "../util/resources" |
| | | import { CanonicalSlug, pathToRoot } from "../util/path" |
| | | |
| | | interface RenderComponents { |
| | | head: QuartzComponent |
| | |
| | | import type { ContentDetails } from "../../plugins/emitters/contentIndex" |
| | | import * as d3 from "d3" |
| | | import { registerEscapeHandler, removeAllChildren } from "./util" |
| | | import { CanonicalSlug, getCanonicalSlug, getClientSlug, resolveRelative } from "../../path" |
| | | import { CanonicalSlug, getCanonicalSlug, getClientSlug, resolveRelative } from "../../util/path" |
| | | |
| | | type NodeData = { |
| | | id: CanonicalSlug |
| | |
| | | import { Document } from "flexsearch" |
| | | import { ContentDetails } from "../../plugins/emitters/contentIndex" |
| | | import { registerEscapeHandler, removeAllChildren } from "./util" |
| | | import { CanonicalSlug, getClientSlug, resolveRelative } from "../../path" |
| | | import { CanonicalSlug, getClientSlug, resolveRelative } from "../../util/path" |
| | | |
| | | interface Item { |
| | | id: number |
| | |
| | | import micromorph from "micromorph" |
| | | import { CanonicalSlug, RelativeURL, getCanonicalSlug } from "../../path" |
| | | import { CanonicalSlug, RelativeURL, getCanonicalSlug } from "../../util/path" |
| | | |
| | | // adapted from `micromorph` |
| | | // https://github.com/natemoo-re/micromorph |
| | |
| | | ServerSlug, |
| | | canonicalizeServer, |
| | | resolveRelative, |
| | | } from "../../path" |
| | | } from "../../util/path" |
| | | import { QuartzEmitterPlugin } from "../types" |
| | | import path from "path" |
| | | |
| | |
| | | import { FilePath, joinSegments, slugifyFilePath } from "../../path" |
| | | import { FilePath, joinSegments, slugifyFilePath } from "../../util/path" |
| | | import { QuartzEmitterPlugin } from "../types" |
| | | import path from "path" |
| | | import fs from "fs" |
| | | import { glob } from "../../glob" |
| | | import { glob } from "../../util/glob" |
| | | |
| | | export const Assets: QuartzEmitterPlugin = () => { |
| | | return { |
| | |
| | | import { FilePath, ServerSlug } from "../../path" |
| | | import { FilePath, ServerSlug } from "../../util/path" |
| | | import { QuartzEmitterPlugin } from "../types" |
| | | |
| | | // @ts-ignore |
| | |
| | | import popoverScript from "../../components/scripts/popover.inline" |
| | | import styles from "../../styles/base.scss" |
| | | import popoverStyle from "../../components/styles/popover.scss" |
| | | import { BuildCtx } from "../../ctx" |
| | | import { StaticResources } from "../../resources" |
| | | import { BuildCtx } from "../../util/ctx" |
| | | import { StaticResources } from "../../util/resources" |
| | | import { QuartzComponent } from "../../components/types" |
| | | import { googleFontHref, joinStyles } from "../../theme" |
| | | import { googleFontHref, joinStyles } from "../../util/theme" |
| | | import { Features, transform } from "lightningcss" |
| | | |
| | | type ComponentResources = { |
| | |
| | | import { GlobalConfiguration } from "../../cfg" |
| | | import { CanonicalSlug, ClientSlug, FilePath, ServerSlug, canonicalizeServer } from "../../path" |
| | | import { |
| | | CanonicalSlug, |
| | | ClientSlug, |
| | | FilePath, |
| | | ServerSlug, |
| | | canonicalizeServer, |
| | | } from "../../util/path" |
| | | import { QuartzEmitterPlugin } from "../types" |
| | | import path from "path" |
| | | |
| | |
| | | import BodyConstructor from "../../components/Body" |
| | | import { pageResources, renderPage } from "../../components/renderPage" |
| | | import { FullPageLayout } from "../../cfg" |
| | | import { FilePath, canonicalizeServer } from "../../path" |
| | | import { FilePath, canonicalizeServer } from "../../util/path" |
| | | import { defaultContentPageLayout, sharedPageComponents } from "../../../quartz.layout" |
| | | import { Content } from "../../components" |
| | | |
| | |
| | | import { ProcessedContent, defaultProcessedContent } from "../vfile" |
| | | import { FullPageLayout } from "../../cfg" |
| | | import path from "path" |
| | | import { CanonicalSlug, FilePath, ServerSlug, canonicalizeServer, joinSegments } from "../../path" |
| | | import { |
| | | CanonicalSlug, |
| | | FilePath, |
| | | ServerSlug, |
| | | canonicalizeServer, |
| | | joinSegments, |
| | | } from "../../util/path" |
| | | import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout" |
| | | import { FolderContent } from "../../components" |
| | | |
| | |
| | | import { FilePath, QUARTZ, joinSegments } from "../../path" |
| | | import { FilePath, QUARTZ, joinSegments } from "../../util/path" |
| | | import { QuartzEmitterPlugin } from "../types" |
| | | import fs from "fs" |
| | | import { glob } from "../../glob" |
| | | import { glob } from "../../util/glob" |
| | | |
| | | export const Static: QuartzEmitterPlugin = () => ({ |
| | | name: "Static", |
| | |
| | | ServerSlug, |
| | | getAllSegmentPrefixes, |
| | | joinSegments, |
| | | } from "../../path" |
| | | } from "../../util/path" |
| | | import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout" |
| | | import { TagContent } from "../../components" |
| | | |
| | |
| | | allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes), |
| | | ) |
| | | // add base tag |
| | | tags.add("") |
| | | tags.add("index") |
| | | |
| | | const tagDescriptions: Record<string, ProcessedContent> = Object.fromEntries( |
| | | [...tags].map((tag) => { |
| | |
| | | import { StaticResources } from "../resources" |
| | | import { FilePath, ServerSlug } from "../path" |
| | | import { BuildCtx } from "../ctx" |
| | | import { StaticResources } from "../util/resources" |
| | | import { FilePath, ServerSlug } from "../util/path" |
| | | import { BuildCtx } from "../util/ctx" |
| | | |
| | | export function getStaticResourcesFromPlugins(ctx: BuildCtx) { |
| | | const staticResources: StaticResources = { |
| | |
| | | import remarkFrontmatter from "remark-frontmatter" |
| | | import { QuartzTransformerPlugin } from "../types" |
| | | import yaml from "js-yaml" |
| | | import { slugTag } from "../../path" |
| | | import { slugTag } from "../../util/path" |
| | | |
| | | export interface Options { |
| | | delims: string | string[] |
| | |
| | | joinSegments, |
| | | splitAnchor, |
| | | transformLink, |
| | | } from "../../path" |
| | | } from "../../util/path" |
| | | import path from "path" |
| | | import { visit } from "unist-util-visit" |
| | | import isAbsoluteUrl from "is-absolute-url" |
| | |
| | | import rehypeRaw from "rehype-raw" |
| | | import { visit } from "unist-util-visit" |
| | | import path from "path" |
| | | import { JSResource } from "../../resources" |
| | | import { JSResource } from "../../util/resources" |
| | | // @ts-ignore |
| | | import calloutScript from "../../components/scripts/callout.inline.ts" |
| | | import { FilePath, canonicalizeServer, pathToRoot, slugTag, slugifyFilePath } from "../../path" |
| | | import { FilePath, canonicalizeServer, pathToRoot, slugTag, slugifyFilePath } from "../../util/path" |
| | | import { toHast } from "mdast-util-to-hast" |
| | | import { toHtml } from "hast-util-to-html" |
| | | import { PhrasingContent } from "mdast-util-find-and-replace/lib" |
| | |
| | | } |
| | | |
| | | const text = firstChild.children[0].value |
| | | const restChildren = firstChild.children.splice(1) |
| | | const restChildren = firstChild.children.slice(1) |
| | | const [firstLine, ...remainingLines] = text.split("\n") |
| | | const remainingText = remainingLines.join("\n") |
| | | |
| | |
| | | import { PluggableList } from "unified" |
| | | import { StaticResources } from "../resources" |
| | | import { StaticResources } from "../util/resources" |
| | | import { ProcessedContent } from "./vfile" |
| | | import { QuartzComponent } from "../components/types" |
| | | import { FilePath, ServerSlug } from "../path" |
| | | import { BuildCtx } from "../ctx" |
| | | import { FilePath, ServerSlug } from "../util/path" |
| | | import { BuildCtx } from "../util/ctx" |
| | | |
| | | export interface PluginTypes { |
| | | transformers: QuartzTransformerPluginInstance[] |
| | |
| | | import path from "path" |
| | | import fs from "fs" |
| | | import { PerfTimer } from "../perf" |
| | | import { PerfTimer } from "../util/perf" |
| | | import { getStaticResourcesFromPlugins } from "../plugins" |
| | | import { EmitCallback } from "../plugins/types" |
| | | import { ProcessedContent } from "../plugins/vfile" |
| | | import { FilePath, joinSegments } from "../path" |
| | | import { QuartzLogger } from "../log" |
| | | import { trace } from "../trace" |
| | | import { BuildCtx } from "../ctx" |
| | | import { FilePath, joinSegments } from "../util/path" |
| | | import { QuartzLogger } from "../util/log" |
| | | import { trace } from "../util/trace" |
| | | import { BuildCtx } from "../util/ctx" |
| | | |
| | | export async function emitContent(ctx: BuildCtx, content: ProcessedContent[]) { |
| | | const { argv, cfg } = ctx |
| | |
| | | import { BuildCtx } from "../ctx" |
| | | import { PerfTimer } from "../perf" |
| | | import { BuildCtx } from "../util/ctx" |
| | | import { PerfTimer } from "../util/perf" |
| | | import { ProcessedContent } from "../plugins/vfile" |
| | | |
| | | export function filterContent(ctx: BuildCtx, content: ProcessedContent[]): ProcessedContent[] { |
| | |
| | | import { Root as MDRoot } from "remark-parse/lib" |
| | | import { Root as HTMLRoot } from "hast" |
| | | import { ProcessedContent } from "../plugins/vfile" |
| | | import { PerfTimer } from "../perf" |
| | | import { PerfTimer } from "../util/perf" |
| | | import { read } from "to-vfile" |
| | | import { FilePath, QUARTZ, slugifyFilePath } from "../path" |
| | | import { FilePath, QUARTZ, slugifyFilePath } from "../util/path" |
| | | import path from "path" |
| | | import workerpool, { Promise as WorkerPromise } from "workerpool" |
| | | import { QuartzLogger } from "../log" |
| | | import { trace } from "../trace" |
| | | import { BuildCtx } from "../ctx" |
| | | import { QuartzLogger } from "../util/log" |
| | | import { trace } from "../util/trace" |
| | | import { BuildCtx } from "../util/ctx" |
| | | |
| | | export type QuartzProcessor = Processor<MDRoot, HTMLRoot, void> |
| | | export function createProcessor(ctx: BuildCtx): QuartzProcessor { |
| File was renamed from quartz/ctx.ts |
| | |
| | | import { QuartzConfig } from "./cfg" |
| | | import { QuartzConfig } from "../cfg" |
| | | import { ServerSlug } from "./path" |
| | | |
| | | export interface Argv { |
| File was renamed from quartz/path.test.ts |
| | |
| | | assert(!path.isRelativeURL("abc")) |
| | | assert(!path.isRelativeURL("/abc/def")) |
| | | assert(!path.isRelativeURL("")) |
| | | assert(!path.isRelativeURL("../")) |
| | | assert(!path.isRelativeURL("./")) |
| | | assert(!path.isRelativeURL("./abc/def.html")) |
| | | assert(!path.isRelativeURL("./abc/def.md")) |
| | | }) |
| | |
| | | [ |
| | | ["", "."], |
| | | [".", "."], |
| | | ["./", "."], |
| | | ["./index", "."], |
| | | ["./index.html", "."], |
| | | ["./index.md", "."], |
| | | ["./", "./"], |
| | | ["./index", "./"], |
| | | ["./index.html", "./"], |
| | | ["./index.md", "./"], |
| | | ["content", "./content"], |
| | | ["content/test.md", "./content/test"], |
| | | ["./content/test.md", "./content/test"], |
| | | ["../content/test.md", "../content/test"], |
| | | ["tags/", "./tags"], |
| | | ["/tags/", "./tags"], |
| | | ["tags/", "./tags/"], |
| | | ["/tags/", "./tags/"], |
| | | ["content/with spaces", "./content/with-spaces"], |
| | | ["content/with spaces/index", "./content/with-spaces/"], |
| | | ["content/with spaces#and Anchor!", "./content/with-spaces#and-anchor"], |
| | | ], |
| | | path.transformInternalLink, |
| | |
| | | test("from a/b/c", () => { |
| | | const cur = "a/b/c" as CanonicalSlug |
| | | assert.strictEqual(path.transformLink(cur, "d", opts), "./d") |
| | | assert.strictEqual(path.transformLink(cur, "index", opts), ".") |
| | | assert.strictEqual(path.transformLink(cur, "../../index", opts), "../..") |
| | | assert.strictEqual(path.transformLink(cur, "../../", opts), "../..") |
| | | assert.strictEqual(path.transformLink(cur, "index", opts), "./") |
| | | assert.strictEqual(path.transformLink(cur, "../../index", opts), "../../") |
| | | assert.strictEqual(path.transformLink(cur, "../../", opts), "../../") |
| | | assert.strictEqual(path.transformLink(cur, "../../e/g/h", opts), "../../e/g/h") |
| | | }) |
| | | |
| | | test("from a/b/index", () => { |
| | | const cur = "a/b" as CanonicalSlug |
| | | assert.strictEqual(path.transformLink(cur, "../../index", opts), "../..") |
| | | assert.strictEqual(path.transformLink(cur, "../../", opts), "../..") |
| | | assert.strictEqual(path.transformLink(cur, "../../index", opts), "../../") |
| | | assert.strictEqual(path.transformLink(cur, "../../", opts), "../../") |
| | | assert.strictEqual(path.transformLink(cur, "../../e/g/h", opts), "../../e/g/h") |
| | | assert.strictEqual(path.transformLink(cur, "c", opts), "./c") |
| | | }) |
| | |
| | | test("from index", () => { |
| | | const cur = "" as CanonicalSlug |
| | | assert.strictEqual(path.transformLink(cur, "e/g/h", opts), "./e/g/h") |
| | | assert.strictEqual(path.transformLink(cur, "a/b/index", opts), "./a/b") |
| | | assert.strictEqual(path.transformLink(cur, "a/b/index", opts), "./a/b/") |
| | | }) |
| | | }) |
| | | }) |
| File was renamed from quartz/path.ts |
| | |
| | | export type RelativeURL = SlugLike<"relative"> |
| | | export function isRelativeURL(s: string): s is RelativeURL { |
| | | const validStart = /^\.{1,2}/.test(s) |
| | | const validEnding = !(s.endsWith("/") || s.endsWith("/index") || s === "index") |
| | | const validEnding = !(s.endsWith("/index") || s === "index") |
| | | return validStart && validEnding && !_hasFileExtension(s) |
| | | } |
| | | |
| | |
| | | |
| | | export function transformInternalLink(link: string): RelativeURL { |
| | | let [fplike, anchor] = splitAnchor(decodeURI(link)) |
| | | |
| | | const folderPath = |
| | | fplike.endsWith("index") || |
| | | fplike.endsWith("index.md") || |
| | | fplike.endsWith("index.html") || |
| | | fplike.endsWith("/") |
| | | let segments = fplike.split("/").filter((x) => x.length > 0) |
| | | let prefix = segments.filter(_isRelativeSegment).join("/") |
| | | let fp = segments.filter((seg) => !_isRelativeSegment(seg)).join("/") |
| | |
| | | } |
| | | |
| | | fp = canonicalizeServer(slugifyFilePath(fp as FilePath)) |
| | | fp = _trimSuffix(fp, "index") |
| | | |
| | | let joined = joinSegments(_stripSlashes(prefix), _stripSlashes(fp)) |
| | | const res = (_addRelativeToStart(joined) + anchor) as RelativeURL |
| | | const joined = joinSegments(_stripSlashes(prefix), _stripSlashes(fp)) |
| | | const trail = folderPath ? "/" : "" |
| | | const res = (_addRelativeToStart(joined) + anchor + trail) as RelativeURL |
| | | return res |
| | | } |
| | | |
| | | // resolve /a/b/c to ../../ |
| | | // resolve /a/b/c to ../../.. |
| | | export function pathToRoot(slug: CanonicalSlug): RelativeURL { |
| | | let rootPath = slug |
| | | .split("/") |
| | |
| | | import sourceMapSupport from "source-map-support" |
| | | sourceMapSupport.install(options) |
| | | import cfg from "../quartz.config" |
| | | import { Argv, BuildCtx } from "./ctx" |
| | | import { Argv, BuildCtx } from "./util/ctx" |
| | | import { FilePath, ServerSlug } from "./path" |
| | | import { createFileParser, createProcessor } from "./processors/parse" |
| | | import { options } from "./sourcemap" |