fix relative path resolution in router and link crawling

This commit is contained in:
Jacky Zhao 2023-08-16 22:04:15 -07:00
parent 53732b901d
commit b5ab38cb7e
44 changed files with 160 additions and 106 deletions

View File

@ -8,8 +8,6 @@ draft: true
- debounce cfg rebuild on large repos - debounce cfg rebuild on large repos
- investigate content rebuild triggering multiple times even when debounced, causing an esbuild deadlock - investigate content rebuild triggering multiple times even when debounced, causing an esbuild deadlock
- dereference symlink for npx quartz sync - dereference symlink for npx quartz sync
- test/fix with subpath
- fix docs with deploy from github
## high priority backlog ## high priority backlog

View File

@ -80,7 +80,10 @@ jobs:
uses: actions/deploy-pages@v2 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 ### Custom Domain

10
package-lock.json generated
View File

@ -72,7 +72,6 @@
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
"@types/node": "^20.1.2", "@types/node": "^20.1.2",
"@types/pretty-time": "^1.1.2", "@types/pretty-time": "^1.1.2",
"@types/serve-handler": "^6.1.1",
"@types/source-map-support": "^0.5.6", "@types/source-map-support": "^0.5.6",
"@types/workerpool": "^6.4.0", "@types/workerpool": "^6.4.0",
"@types/ws": "^8.5.5", "@types/ws": "^8.5.5",
@ -1478,15 +1477,6 @@
"integrity": "sha512-4i+Y+O5H80Rh01lY/3Z0hB/UWc4R64ReE83joEpVsIG3iQWpYx66k6pQh1amJNZquKtJQyu/RcfkTtvL0KwssA==", "integrity": "sha512-4i+Y+O5H80Rh01lY/3Z0hB/UWc4R64ReE83joEpVsIG3iQWpYx66k6pQh1amJNZquKtJQyu/RcfkTtvL0KwssA==",
"dev": true "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": { "node_modules/@types/source-map-support": {
"version": "0.5.6", "version": "0.5.6",
"resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.5.6.tgz", "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.5.6.tgz",

View File

@ -14,7 +14,7 @@
"scripts": { "scripts": {
"check": "tsc --noEmit && npx prettier . --check", "check": "tsc --noEmit && npx prettier . --check",
"format": "npx prettier . --write", "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" "profile": "0x -D prof ./quartz/bootstrap-cli.mjs build --concurrency=1"
}, },
"keywords": [ "keywords": [
@ -89,7 +89,6 @@
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
"@types/node": "^20.1.2", "@types/node": "^20.1.2",
"@types/pretty-time": "^1.1.2", "@types/pretty-time": "^1.1.2",
"@types/serve-handler": "^6.1.1",
"@types/source-map-support": "^0.5.6", "@types/source-map-support": "^0.5.6",
"@types/workerpool": "^6.4.0", "@types/workerpool": "^6.4.0",
"@types/ws": "^8.5.5", "@types/ws": "^8.5.5",

View File

@ -74,6 +74,10 @@ const BuildArgv = {
default: false, default: false,
describe: "run a local server to live-preview your Quartz", describe: "run a local server to live-preview your Quartz",
}, },
baseDir: {
string: true,
describe: "base path to serve your local server on",
},
port: { port: {
number: true, number: true,
default: 8080, default: 8080,
@ -384,19 +388,63 @@ See the [documentation](https://quartz.jzhao.xyz) for how to get started.
await build(clientRefresh) await build(clientRefresh)
const server = http.createServer(async (req, res) => { const server = http.createServer(async (req, res) => {
await serveHandler(req, res, { const serve = async (fp) => {
public: argv.output, await serveHandler(req, res, {
directoryListing: false, public: argv.output,
trailingSlash: true, directoryListing: false,
}) })
const status = res.statusCode const status = res.statusCode
const statusString = const statusString =
status >= 200 && status < 300 status >= 200 && status < 300 ? chalk.green(`[${status}]`) : chalk.red(`[${status}]`)
? chalk.green(`[${status}]`) console.log(statusString + chalk.grey(` ${req.url}`))
: status >= 300 && status < 400 }
? chalk.yellow(`[${status}]`)
: chalk.red(`[${status}]`) const redirect = (newFp) => {
console.log(statusString + chalk.grey(` ${req.url}`)) 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) server.listen(argv.port)
console.log(chalk.cyan(`Started a Quartz server listening at http://localhost:${argv.port}`)) console.log(chalk.cyan(`Started a Quartz server listening at http://localhost:${argv.port}`))

View File

@ -1,7 +1,7 @@
import sourceMapSupport from "source-map-support" import sourceMapSupport from "source-map-support"
sourceMapSupport.install(options) sourceMapSupport.install(options)
import path from "path" import path from "path"
import { PerfTimer } from "./perf" import { PerfTimer } from "./util/perf"
import { rimraf } from "rimraf" import { rimraf } from "rimraf"
import { isGitIgnored } from "globby" import { isGitIgnored } from "globby"
import chalk from "chalk" import chalk from "chalk"
@ -9,13 +9,13 @@ import { parseMarkdown } from "./processors/parse"
import { filterContent } from "./processors/filter" import { filterContent } from "./processors/filter"
import { emitContent } from "./processors/emit" import { emitContent } from "./processors/emit"
import cfg from "../quartz.config" import cfg from "../quartz.config"
import { FilePath, joinSegments, slugifyFilePath } from "./path" import { FilePath, joinSegments, slugifyFilePath } from "./util/path"
import chokidar from "chokidar" import chokidar from "chokidar"
import { ProcessedContent } from "./plugins/vfile" import { ProcessedContent } from "./plugins/vfile"
import { Argv, BuildCtx } from "./ctx" import { Argv, BuildCtx } from "./util/ctx"
import { glob, toPosixPath } from "./glob" import { glob, toPosixPath } from "./util/glob"
import { trace } from "./trace" import { trace } from "./util/trace"
import { options } from "./sourcemap" import { options } from "./util/sourcemap"
async function buildQuartz(argv: Argv, clientRefresh: () => void) { async function buildQuartz(argv: Argv, clientRefresh: () => void) {
const ctx: BuildCtx = { const ctx: BuildCtx = {

View File

@ -1,6 +1,6 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types" import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/backlinks.scss" import style from "./styles/backlinks.scss"
import { canonicalizeServer, resolveRelative } from "../path" import { canonicalizeServer, resolveRelative } from "../util/path"
function Backlinks({ fileData, allFiles }: QuartzComponentProps) { function Backlinks({ fileData, allFiles }: QuartzComponentProps) {
const slug = canonicalizeServer(fileData.slug!) const slug = canonicalizeServer(fileData.slug!)

View File

@ -1,5 +1,5 @@
import { canonicalizeServer, pathToRoot } from "../path" import { canonicalizeServer, pathToRoot } from "../util/path"
import { JSResourceToScriptElement } from "../resources" import { JSResourceToScriptElement } from "../util/resources"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types" import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
export default (() => { export default (() => {

View File

@ -1,4 +1,4 @@
import { CanonicalSlug, canonicalizeServer, resolveRelative } from "../path" import { CanonicalSlug, canonicalizeServer, resolveRelative } from "../util/path"
import { QuartzPluginData } from "../plugins/vfile" import { QuartzPluginData } from "../plugins/vfile"
import { Date } from "./Date" import { Date } from "./Date"
import { QuartzComponentProps } from "./types" import { QuartzComponentProps } from "./types"

View File

@ -1,4 +1,4 @@
import { canonicalizeServer, pathToRoot } from "../path" import { canonicalizeServer, pathToRoot } from "../util/path"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types" import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
function PageTitle({ fileData, cfg }: QuartzComponentProps) { function PageTitle({ fileData, cfg }: QuartzComponentProps) {

View File

@ -1,4 +1,4 @@
import { canonicalizeServer, pathToRoot, slugTag } from "../path" import { canonicalizeServer, pathToRoot, slugTag } from "../util/path"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types" import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
function TagList({ fileData }: QuartzComponentProps) { function TagList({ fileData }: QuartzComponentProps) {

View File

@ -5,7 +5,7 @@ import path from "path"
import style from "../styles/listPage.scss" import style from "../styles/listPage.scss"
import { PageList } from "../PageList" import { PageList } from "../PageList"
import { canonicalizeServer } from "../../path" import { canonicalizeServer } from "../../util/path"
function FolderContent(props: QuartzComponentProps) { function FolderContent(props: QuartzComponentProps) {
const { tree, fileData, allFiles } = props const { tree, fileData, allFiles } = props

View File

@ -3,7 +3,7 @@ import { Fragment, jsx, jsxs } from "preact/jsx-runtime"
import { toJsxRuntime } from "hast-util-to-jsx-runtime" import { toJsxRuntime } from "hast-util-to-jsx-runtime"
import style from "../styles/listPage.scss" import style from "../styles/listPage.scss"
import { PageList } from "../PageList" import { PageList } from "../PageList"
import { ServerSlug, canonicalizeServer, getAllSegmentPrefixes } from "../../path" import { ServerSlug, canonicalizeServer, getAllSegmentPrefixes } from "../../util/path"
import { QuartzPluginData } from "../../plugins/vfile" import { QuartzPluginData } from "../../plugins/vfile"
const numPages = 10 const numPages = 10

View File

@ -2,8 +2,8 @@ import { render } from "preact-render-to-string"
import { QuartzComponent, QuartzComponentProps } from "./types" import { QuartzComponent, QuartzComponentProps } from "./types"
import HeaderConstructor from "./Header" import HeaderConstructor from "./Header"
import BodyConstructor from "./Body" import BodyConstructor from "./Body"
import { JSResourceToScriptElement, StaticResources } from "../resources" import { JSResourceToScriptElement, StaticResources } from "../util/resources"
import { CanonicalSlug, pathToRoot } from "../path" import { CanonicalSlug, pathToRoot } from "../util/path"
interface RenderComponents { interface RenderComponents {
head: QuartzComponent head: QuartzComponent

View File

@ -1,7 +1,7 @@
import type { ContentDetails } from "../../plugins/emitters/contentIndex" import type { ContentDetails } from "../../plugins/emitters/contentIndex"
import * as d3 from "d3" import * as d3 from "d3"
import { registerEscapeHandler, removeAllChildren } from "./util" import { registerEscapeHandler, removeAllChildren } from "./util"
import { CanonicalSlug, getCanonicalSlug, getClientSlug, resolveRelative } from "../../path" import { CanonicalSlug, getCanonicalSlug, getClientSlug, resolveRelative } from "../../util/path"
type NodeData = { type NodeData = {
id: CanonicalSlug id: CanonicalSlug

View File

@ -1,7 +1,7 @@
import { Document } from "flexsearch" import { Document } from "flexsearch"
import { ContentDetails } from "../../plugins/emitters/contentIndex" import { ContentDetails } from "../../plugins/emitters/contentIndex"
import { registerEscapeHandler, removeAllChildren } from "./util" import { registerEscapeHandler, removeAllChildren } from "./util"
import { CanonicalSlug, getClientSlug, resolveRelative } from "../../path" import { CanonicalSlug, getClientSlug, resolveRelative } from "../../util/path"
interface Item { interface Item {
id: number id: number

View File

@ -1,5 +1,5 @@
import micromorph from "micromorph" import micromorph from "micromorph"
import { CanonicalSlug, RelativeURL, getCanonicalSlug } from "../../path" import { CanonicalSlug, RelativeURL, getCanonicalSlug } from "../../util/path"
// adapted from `micromorph` // adapted from `micromorph`
// https://github.com/natemoo-re/micromorph // https://github.com/natemoo-re/micromorph

View File

@ -4,7 +4,7 @@ import {
ServerSlug, ServerSlug,
canonicalizeServer, canonicalizeServer,
resolveRelative, resolveRelative,
} from "../../path" } from "../../util/path"
import { QuartzEmitterPlugin } from "../types" import { QuartzEmitterPlugin } from "../types"
import path from "path" import path from "path"

View File

@ -1,8 +1,8 @@
import { FilePath, joinSegments, slugifyFilePath } from "../../path" import { FilePath, joinSegments, slugifyFilePath } from "../../util/path"
import { QuartzEmitterPlugin } from "../types" import { QuartzEmitterPlugin } from "../types"
import path from "path" import path from "path"
import fs from "fs" import fs from "fs"
import { glob } from "../../glob" import { glob } from "../../util/glob"
export const Assets: QuartzEmitterPlugin = () => { export const Assets: QuartzEmitterPlugin = () => {
return { return {

View File

@ -1,4 +1,4 @@
import { FilePath, ServerSlug } from "../../path" import { FilePath, ServerSlug } from "../../util/path"
import { QuartzEmitterPlugin } from "../types" import { QuartzEmitterPlugin } from "../types"
// @ts-ignore // @ts-ignore
@ -9,10 +9,10 @@ import plausibleScript from "../../components/scripts/plausible.inline"
import popoverScript from "../../components/scripts/popover.inline" import popoverScript from "../../components/scripts/popover.inline"
import styles from "../../styles/base.scss" import styles from "../../styles/base.scss"
import popoverStyle from "../../components/styles/popover.scss" import popoverStyle from "../../components/styles/popover.scss"
import { BuildCtx } from "../../ctx" import { BuildCtx } from "../../util/ctx"
import { StaticResources } from "../../resources" import { StaticResources } from "../../util/resources"
import { QuartzComponent } from "../../components/types" import { QuartzComponent } from "../../components/types"
import { googleFontHref, joinStyles } from "../../theme" import { googleFontHref, joinStyles } from "../../util/theme"
import { Features, transform } from "lightningcss" import { Features, transform } from "lightningcss"
type ComponentResources = { type ComponentResources = {

View File

@ -1,5 +1,11 @@
import { GlobalConfiguration } from "../../cfg" 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 { QuartzEmitterPlugin } from "../types"
import path from "path" import path from "path"

View File

@ -4,7 +4,7 @@ import HeaderConstructor from "../../components/Header"
import BodyConstructor from "../../components/Body" import BodyConstructor from "../../components/Body"
import { pageResources, renderPage } from "../../components/renderPage" import { pageResources, renderPage } from "../../components/renderPage"
import { FullPageLayout } from "../../cfg" import { FullPageLayout } from "../../cfg"
import { FilePath, canonicalizeServer } from "../../path" import { FilePath, canonicalizeServer } from "../../util/path"
import { defaultContentPageLayout, sharedPageComponents } from "../../../quartz.layout" import { defaultContentPageLayout, sharedPageComponents } from "../../../quartz.layout"
import { Content } from "../../components" import { Content } from "../../components"

View File

@ -6,7 +6,13 @@ import { pageResources, renderPage } from "../../components/renderPage"
import { ProcessedContent, defaultProcessedContent } from "../vfile" import { ProcessedContent, defaultProcessedContent } from "../vfile"
import { FullPageLayout } from "../../cfg" import { FullPageLayout } from "../../cfg"
import path from "path" 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 { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
import { FolderContent } from "../../components" import { FolderContent } from "../../components"

View File

@ -1,7 +1,7 @@
import { FilePath, QUARTZ, joinSegments } from "../../path" import { FilePath, QUARTZ, joinSegments } from "../../util/path"
import { QuartzEmitterPlugin } from "../types" import { QuartzEmitterPlugin } from "../types"
import fs from "fs" import fs from "fs"
import { glob } from "../../glob" import { glob } from "../../util/glob"
export const Static: QuartzEmitterPlugin = () => ({ export const Static: QuartzEmitterPlugin = () => ({
name: "Static", name: "Static",

View File

@ -11,7 +11,7 @@ import {
ServerSlug, ServerSlug,
getAllSegmentPrefixes, getAllSegmentPrefixes,
joinSegments, joinSegments,
} from "../../path" } from "../../util/path"
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout" import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
import { TagContent } from "../../components" import { TagContent } from "../../components"
@ -41,7 +41,7 @@ export const TagPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes), allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes),
) )
// add base tag // add base tag
tags.add("") tags.add("index")
const tagDescriptions: Record<string, ProcessedContent> = Object.fromEntries( const tagDescriptions: Record<string, ProcessedContent> = Object.fromEntries(
[...tags].map((tag) => { [...tags].map((tag) => {

View File

@ -1,6 +1,6 @@
import { StaticResources } from "../resources" import { StaticResources } from "../util/resources"
import { FilePath, ServerSlug } from "../path" import { FilePath, ServerSlug } from "../util/path"
import { BuildCtx } from "../ctx" import { BuildCtx } from "../util/ctx"
export function getStaticResourcesFromPlugins(ctx: BuildCtx) { export function getStaticResourcesFromPlugins(ctx: BuildCtx) {
const staticResources: StaticResources = { const staticResources: StaticResources = {

View File

@ -2,7 +2,7 @@ import matter from "gray-matter"
import remarkFrontmatter from "remark-frontmatter" import remarkFrontmatter from "remark-frontmatter"
import { QuartzTransformerPlugin } from "../types" import { QuartzTransformerPlugin } from "../types"
import yaml from "js-yaml" import yaml from "js-yaml"
import { slugTag } from "../../path" import { slugTag } from "../../util/path"
export interface Options { export interface Options {
delims: string | string[] delims: string | string[]

View File

@ -8,7 +8,7 @@ import {
joinSegments, joinSegments,
splitAnchor, splitAnchor,
transformLink, transformLink,
} from "../../path" } from "../../util/path"
import path from "path" import path from "path"
import { visit } from "unist-util-visit" import { visit } from "unist-util-visit"
import isAbsoluteUrl from "is-absolute-url" import isAbsoluteUrl from "is-absolute-url"

View File

@ -6,10 +6,10 @@ import { slug as slugAnchor } from "github-slugger"
import rehypeRaw from "rehype-raw" import rehypeRaw from "rehype-raw"
import { visit } from "unist-util-visit" import { visit } from "unist-util-visit"
import path from "path" import path from "path"
import { JSResource } from "../../resources" import { JSResource } from "../../util/resources"
// @ts-ignore // @ts-ignore
import calloutScript from "../../components/scripts/callout.inline.ts" 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 { toHast } from "mdast-util-to-hast"
import { toHtml } from "hast-util-to-html" import { toHtml } from "hast-util-to-html"
import { PhrasingContent } from "mdast-util-find-and-replace/lib" import { PhrasingContent } from "mdast-util-find-and-replace/lib"
@ -294,7 +294,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
} }
const text = firstChild.children[0].value 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 [firstLine, ...remainingLines] = text.split("\n")
const remainingText = remainingLines.join("\n") const remainingText = remainingLines.join("\n")

View File

@ -1,9 +1,9 @@
import { PluggableList } from "unified" import { PluggableList } from "unified"
import { StaticResources } from "../resources" import { StaticResources } from "../util/resources"
import { ProcessedContent } from "./vfile" import { ProcessedContent } from "./vfile"
import { QuartzComponent } from "../components/types" import { QuartzComponent } from "../components/types"
import { FilePath, ServerSlug } from "../path" import { FilePath, ServerSlug } from "../util/path"
import { BuildCtx } from "../ctx" import { BuildCtx } from "../util/ctx"
export interface PluginTypes { export interface PluginTypes {
transformers: QuartzTransformerPluginInstance[] transformers: QuartzTransformerPluginInstance[]

View File

@ -1,13 +1,13 @@
import path from "path" import path from "path"
import fs from "fs" import fs from "fs"
import { PerfTimer } from "../perf" import { PerfTimer } from "../util/perf"
import { getStaticResourcesFromPlugins } from "../plugins" import { getStaticResourcesFromPlugins } from "../plugins"
import { EmitCallback } from "../plugins/types" import { EmitCallback } from "../plugins/types"
import { ProcessedContent } from "../plugins/vfile" import { ProcessedContent } from "../plugins/vfile"
import { FilePath, joinSegments } from "../path" import { FilePath, joinSegments } from "../util/path"
import { QuartzLogger } from "../log" import { QuartzLogger } from "../util/log"
import { trace } from "../trace" import { trace } from "../util/trace"
import { BuildCtx } from "../ctx" import { BuildCtx } from "../util/ctx"
export async function emitContent(ctx: BuildCtx, content: ProcessedContent[]) { export async function emitContent(ctx: BuildCtx, content: ProcessedContent[]) {
const { argv, cfg } = ctx const { argv, cfg } = ctx

View File

@ -1,5 +1,5 @@
import { BuildCtx } from "../ctx" import { BuildCtx } from "../util/ctx"
import { PerfTimer } from "../perf" import { PerfTimer } from "../util/perf"
import { ProcessedContent } from "../plugins/vfile" import { ProcessedContent } from "../plugins/vfile"
export function filterContent(ctx: BuildCtx, content: ProcessedContent[]): ProcessedContent[] { export function filterContent(ctx: BuildCtx, content: ProcessedContent[]): ProcessedContent[] {

View File

@ -5,14 +5,14 @@ import { Processor, unified } from "unified"
import { Root as MDRoot } from "remark-parse/lib" import { Root as MDRoot } from "remark-parse/lib"
import { Root as HTMLRoot } from "hast" import { Root as HTMLRoot } from "hast"
import { ProcessedContent } from "../plugins/vfile" import { ProcessedContent } from "../plugins/vfile"
import { PerfTimer } from "../perf" import { PerfTimer } from "../util/perf"
import { read } from "to-vfile" import { read } from "to-vfile"
import { FilePath, QUARTZ, slugifyFilePath } from "../path" import { FilePath, QUARTZ, slugifyFilePath } from "../util/path"
import path from "path" import path from "path"
import workerpool, { Promise as WorkerPromise } from "workerpool" import workerpool, { Promise as WorkerPromise } from "workerpool"
import { QuartzLogger } from "../log" import { QuartzLogger } from "../util/log"
import { trace } from "../trace" import { trace } from "../util/trace"
import { BuildCtx } from "../ctx" import { BuildCtx } from "../util/ctx"
export type QuartzProcessor = Processor<MDRoot, HTMLRoot, void> export type QuartzProcessor = Processor<MDRoot, HTMLRoot, void>
export function createProcessor(ctx: BuildCtx): QuartzProcessor { export function createProcessor(ctx: BuildCtx): QuartzProcessor {

View File

@ -1,4 +1,4 @@
import { QuartzConfig } from "./cfg" import { QuartzConfig } from "../cfg"
import { ServerSlug } from "./path" import { ServerSlug } from "./path"
export interface Argv { export interface Argv {

View File

@ -53,8 +53,6 @@ describe("typeguards", () => {
assert(!path.isRelativeURL("abc")) assert(!path.isRelativeURL("abc"))
assert(!path.isRelativeURL("/abc/def")) assert(!path.isRelativeURL("/abc/def"))
assert(!path.isRelativeURL("")) assert(!path.isRelativeURL(""))
assert(!path.isRelativeURL("../"))
assert(!path.isRelativeURL("./"))
assert(!path.isRelativeURL("./abc/def.html")) assert(!path.isRelativeURL("./abc/def.html"))
assert(!path.isRelativeURL("./abc/def.md")) assert(!path.isRelativeURL("./abc/def.md"))
}) })
@ -160,17 +158,18 @@ describe("transforms", () => {
[ [
["", "."], ["", "."],
[".", "."], [".", "."],
["./", "."], ["./", "./"],
["./index", "."], ["./index", "./"],
["./index.html", "."], ["./index.html", "./"],
["./index.md", "."], ["./index.md", "./"],
["content", "./content"], ["content", "./content"],
["content/test.md", "./content/test"], ["content/test.md", "./content/test"],
["./content/test.md", "./content/test"], ["./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", "./content/with-spaces"],
["content/with spaces/index", "./content/with-spaces/"],
["content/with spaces#and Anchor!", "./content/with-spaces#and-anchor"], ["content/with spaces#and Anchor!", "./content/with-spaces#and-anchor"],
], ],
path.transformInternalLink, path.transformInternalLink,
@ -269,16 +268,16 @@ describe("link strategies", () => {
test("from a/b/c", () => { test("from a/b/c", () => {
const cur = "a/b/c" as CanonicalSlug const cur = "a/b/c" as CanonicalSlug
assert.strictEqual(path.transformLink(cur, "d", opts), "./d") 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, "../../index", opts), "../..") assert.strictEqual(path.transformLink(cur, "../../index", opts), "../../")
assert.strictEqual(path.transformLink(cur, "../../", opts), "../..") assert.strictEqual(path.transformLink(cur, "../../", opts), "../../")
assert.strictEqual(path.transformLink(cur, "../../e/g/h", opts), "../../e/g/h") assert.strictEqual(path.transformLink(cur, "../../e/g/h", opts), "../../e/g/h")
}) })
test("from a/b/index", () => { test("from a/b/index", () => {
const cur = "a/b" as CanonicalSlug const cur = "a/b" as CanonicalSlug
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, "../../", opts), "../../")
assert.strictEqual(path.transformLink(cur, "../../e/g/h", opts), "../../e/g/h") assert.strictEqual(path.transformLink(cur, "../../e/g/h", opts), "../../e/g/h")
assert.strictEqual(path.transformLink(cur, "c", opts), "./c") assert.strictEqual(path.transformLink(cur, "c", opts), "./c")
}) })
@ -286,7 +285,7 @@ describe("link strategies", () => {
test("from index", () => { test("from index", () => {
const cur = "" as CanonicalSlug const cur = "" as CanonicalSlug
assert.strictEqual(path.transformLink(cur, "e/g/h", opts), "./e/g/h") 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/")
}) })
}) })
}) })

View File

@ -71,7 +71,7 @@ export function isCanonicalSlug(s: string): s is CanonicalSlug {
export type RelativeURL = SlugLike<"relative"> export type RelativeURL = SlugLike<"relative">
export function isRelativeURL(s: string): s is RelativeURL { export function isRelativeURL(s: string): s is RelativeURL {
const validStart = /^\.{1,2}/.test(s) 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) return validStart && validEnding && !_hasFileExtension(s)
} }
@ -133,6 +133,12 @@ export function slugifyFilePath(fp: FilePath): ServerSlug {
export function transformInternalLink(link: string): RelativeURL { export function transformInternalLink(link: string): RelativeURL {
let [fplike, anchor] = splitAnchor(decodeURI(link)) 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 segments = fplike.split("/").filter((x) => x.length > 0)
let prefix = segments.filter(_isRelativeSegment).join("/") let prefix = segments.filter(_isRelativeSegment).join("/")
let fp = segments.filter((seg) => !_isRelativeSegment(seg)).join("/") let fp = segments.filter((seg) => !_isRelativeSegment(seg)).join("/")
@ -143,14 +149,13 @@ export function transformInternalLink(link: string): RelativeURL {
} }
fp = canonicalizeServer(slugifyFilePath(fp as FilePath)) fp = canonicalizeServer(slugifyFilePath(fp as FilePath))
fp = _trimSuffix(fp, "index") const joined = joinSegments(_stripSlashes(prefix), _stripSlashes(fp))
const trail = folderPath ? "/" : ""
let joined = joinSegments(_stripSlashes(prefix), _stripSlashes(fp)) const res = (_addRelativeToStart(joined) + anchor + trail) as RelativeURL
const res = (_addRelativeToStart(joined) + anchor) as RelativeURL
return res return res
} }
// resolve /a/b/c to ../../ // resolve /a/b/c to ../../..
export function pathToRoot(slug: CanonicalSlug): RelativeURL { export function pathToRoot(slug: CanonicalSlug): RelativeURL {
let rootPath = slug let rootPath = slug
.split("/") .split("/")

View File

@ -1,7 +1,7 @@
import sourceMapSupport from "source-map-support" import sourceMapSupport from "source-map-support"
sourceMapSupport.install(options) sourceMapSupport.install(options)
import cfg from "../quartz.config" import cfg from "../quartz.config"
import { Argv, BuildCtx } from "./ctx" import { Argv, BuildCtx } from "./util/ctx"
import { FilePath, ServerSlug } from "./path" import { FilePath, ServerSlug } from "./path"
import { createFileParser, createProcessor } from "./processors/parse" import { createFileParser, createProcessor } from "./processors/parse"
import { options } from "./sourcemap" import { options } from "./sourcemap"