diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx index dae81c731..8292acc05 100644 --- a/quartz/components/Head.tsx +++ b/quartz/components/Head.tsx @@ -1,5 +1,5 @@ import { i18n } from "../i18n" -import { FullSlug, _stripSlashes, joinSegments, pathToRoot } from "../util/path" +import { FullSlug, joinSegments, pathToRoot } from "../util/path" import { JSResourceToScriptElement } from "../util/resources" import { QuartzComponentConstructor, QuartzComponentProps } from "./types" diff --git a/quartz/components/pages/FolderContent.tsx b/quartz/components/pages/FolderContent.tsx index b954ea21c..d3f28ddf1 100644 --- a/quartz/components/pages/FolderContent.tsx +++ b/quartz/components/pages/FolderContent.tsx @@ -3,7 +3,7 @@ import path from "path" import style from "../styles/listPage.scss" import { PageList } from "../PageList" -import { _stripSlashes, simplifySlug } from "../../util/path" +import { stripSlashes, simplifySlug } from "../../util/path" import { Root } from "hast" import { htmlToJsx } from "../../util/jsx" import { i18n } from "../../i18n" @@ -24,9 +24,9 @@ export default ((opts?: Partial) => { function FolderContent(props: QuartzComponentProps) { const { tree, fileData, allFiles, cfg } = props - const folderSlug = _stripSlashes(simplifySlug(fileData.slug!)) + const folderSlug = stripSlashes(simplifySlug(fileData.slug!)) const allPagesInFolder = allFiles.filter((file) => { - const fileSlug = _stripSlashes(simplifySlug(file.slug!)) + const fileSlug = stripSlashes(simplifySlug(file.slug!)) const prefixed = fileSlug.startsWith(folderSlug) && fileSlug !== folderSlug const folderParts = folderSlug.split(path.posix.sep) const fileParts = fileSlug.split(path.posix.sep) diff --git a/quartz/plugins/emitters/folderPage.tsx b/quartz/plugins/emitters/folderPage.tsx index 7a62cda56..690fa56f7 100644 --- a/quartz/plugins/emitters/folderPage.tsx +++ b/quartz/plugins/emitters/folderPage.tsx @@ -10,7 +10,7 @@ import { FilePath, FullSlug, SimpleSlug, - _stripSlashes, + stripSlashes, joinSegments, pathToRoot, simplifySlug, @@ -38,7 +38,7 @@ export const FolderPage: QuartzEmitterPlugin> = (userOpt getQuartzComponents() { return [Head, Header, Body, ...header, ...beforeBody, pageBody, ...left, ...right, Footer] }, - async getDependencyGraph(ctx, content, _resources) { + async getDependencyGraph(_ctx, _content, _resources) { // Example graph: // nested/file.md --> nested/file.html // \-------> nested/index.html @@ -75,7 +75,7 @@ export const FolderPage: QuartzEmitterPlugin> = (userOpt ) for (const [tree, file] of content) { - const slug = _stripSlashes(simplifySlug(file.data.slug!)) as SimpleSlug + const slug = stripSlashes(simplifySlug(file.data.slug!)) as SimpleSlug if (folders.has(slug)) { folderDescriptions[slug] = [tree, file] } diff --git a/quartz/plugins/transformers/links.ts b/quartz/plugins/transformers/links.ts index 1ba0c8e58..2d43be1b2 100644 --- a/quartz/plugins/transformers/links.ts +++ b/quartz/plugins/transformers/links.ts @@ -4,7 +4,7 @@ import { RelativeURL, SimpleSlug, TransformOptions, - _stripSlashes, + stripSlashes, simplifySlug, splitAnchor, transformLink, @@ -115,7 +115,7 @@ export const CrawlLinks: QuartzTransformerPlugin | undefined> = } // need to decodeURIComponent here as WHATWG URL percent-encodes everything - const full = decodeURIComponent(_stripSlashes(destCanonical, true)) as FullSlug + const full = decodeURIComponent(stripSlashes(destCanonical, true)) as FullSlug const simple = simplifySlug(full) outgoing.add(simple) node.properties["data-slug"] = full diff --git a/quartz/util/path.ts b/quartz/util/path.ts index 5cd79ba5f..dceb89bfa 100644 --- a/quartz/util/path.ts +++ b/quartz/util/path.ts @@ -23,22 +23,22 @@ export type FullSlug = SlugLike<"full"> export function isFullSlug(s: string): s is FullSlug { const validStart = !(s.startsWith(".") || s.startsWith("/")) const validEnding = !s.endsWith("/") - return validStart && validEnding && !_containsForbiddenCharacters(s) + return validStart && validEnding && !containsForbiddenCharacters(s) } /** Shouldn't be a relative path and shouldn't have `/index` as an ending or a file extension. It _can_ however have a trailing slash to indicate a folder path. */ export type SimpleSlug = SlugLike<"simple"> export function isSimpleSlug(s: string): s is SimpleSlug { const validStart = !(s.startsWith(".") || (s.length > 1 && s.startsWith("/"))) - const validEnding = !(s.endsWith("/index") || s === "index") - return validStart && !_containsForbiddenCharacters(s) && validEnding && !_hasFileExtension(s) + const validEnding = !endsWith(s, "index") + return validStart && !containsForbiddenCharacters(s) && validEnding && !_hasFileExtension(s) } /** Can be found on `href`s but can also be constructed for client-side navigation (e.g. search and graph) */ export type RelativeURL = SlugLike<"relative"> export function isRelativeURL(s: string): s is RelativeURL { const validStart = /^\.{1,2}/.test(s) - const validEnding = !(s.endsWith("/index") || s === "index") + const validEnding = !endsWith(s, "index") return validStart && validEnding && ![".md", ".html"].includes(_getFileExtension(s) ?? "") } @@ -63,7 +63,7 @@ function sluggify(s: string): string { } export function slugifyFilePath(fp: FilePath, excludeExt?: boolean): FullSlug { - fp = _stripSlashes(fp) as FilePath + fp = stripSlashes(fp) as FilePath let ext = _getFileExtension(fp) const withoutFileExt = fp.replace(new RegExp(ext + "$"), "") if (excludeExt || [".md", ".html", undefined].includes(ext)) { @@ -73,7 +73,7 @@ export function slugifyFilePath(fp: FilePath, excludeExt?: boolean): FullSlug { let slug = sluggify(withoutFileExt) // treat _index as index - if (_endsWith(slug, "_index")) { + if (endsWith(slug, "_index")) { slug = slug.replace(/_index$/, "index") } @@ -81,21 +81,21 @@ export function slugifyFilePath(fp: FilePath, excludeExt?: boolean): FullSlug { } export function simplifySlug(fp: FullSlug): SimpleSlug { - const res = _stripSlashes(_trimSuffix(fp, "index"), true) + const res = stripSlashes(trimSuffix(fp, "index"), true) return (res.length === 0 ? "/" : res) as SimpleSlug } export function transformInternalLink(link: string): RelativeURL { let [fplike, anchor] = splitAnchor(decodeURI(link)) - const folderPath = _isFolderPath(fplike) + const folderPath = isFolderPath(fplike) let segments = fplike.split("/").filter((x) => x.length > 0) - let prefix = segments.filter(_isRelativeSegment).join("/") - let fp = segments.filter((seg) => !_isRelativeSegment(seg) && seg !== "").join("/") + let prefix = segments.filter(isRelativeSegment).join("/") + let fp = segments.filter((seg) => !isRelativeSegment(seg) && seg !== "").join("/") // manually add ext here as we want to not strip 'index' if it has an extension const simpleSlug = simplifySlug(slugifyFilePath(fp as FilePath)) - const joined = joinSegments(_stripSlashes(prefix), _stripSlashes(simpleSlug)) + const joined = joinSegments(stripSlashes(prefix), stripSlashes(simpleSlug)) const trail = folderPath ? "/" : "" const res = (_addRelativeToStart(joined) + trail + anchor) as RelativeURL return res @@ -206,8 +206,8 @@ export function transformLink(src: FullSlug, target: string, opts: TransformOpti if (opts.strategy === "relative") { return targetSlug as RelativeURL } else { - const folderTail = _isFolderPath(targetSlug) ? "/" : "" - const canonicalSlug = _stripSlashes(targetSlug.slice(".".length)) + const folderTail = isFolderPath(targetSlug) ? "/" : "" + const canonicalSlug = stripSlashes(targetSlug.slice(".".length)) let [targetCanonical, targetAnchor] = splitAnchor(canonicalSlug) if (opts.strategy === "shortest") { @@ -230,28 +230,29 @@ export function transformLink(src: FullSlug, target: string, opts: TransformOpti } } -function _isFolderPath(fplike: string): boolean { +// path helpers +function isFolderPath(fplike: string): boolean { return ( fplike.endsWith("/") || - _endsWith(fplike, "index") || - _endsWith(fplike, "index.md") || - _endsWith(fplike, "index.html") + endsWith(fplike, "index") || + endsWith(fplike, "index.md") || + endsWith(fplike, "index.html") ) } -function _endsWith(s: string, suffix: string): boolean { +export function endsWith(s: string, suffix: string): boolean { return s === suffix || s.endsWith("/" + suffix) } -function _trimSuffix(s: string, suffix: string): string { - if (_endsWith(s, suffix)) { +function trimSuffix(s: string, suffix: string): string { + if (endsWith(s, suffix)) { s = s.slice(0, -suffix.length) } return s } -function _containsForbiddenCharacters(s: string): boolean { - return s.includes(" ") || s.includes("#") || s.includes("?") +function containsForbiddenCharacters(s: string): boolean { + return s.includes(" ") || s.includes("#") || s.includes("?") || s.includes("&") } function _hasFileExtension(s: string): boolean { @@ -262,11 +263,11 @@ function _getFileExtension(s: string): string | undefined { return s.match(/\.[A-Za-z0-9]+$/)?.[0] } -function _isRelativeSegment(s: string): boolean { +function isRelativeSegment(s: string): boolean { return /^\.{0,2}$/.test(s) } -export function _stripSlashes(s: string, onlyStripPrefix?: boolean): string { +export function stripSlashes(s: string, onlyStripPrefix?: boolean): string { if (s.startsWith("/")) { s = s.substring(1) }