forked from github/quartz
fix(ox-hugo): serve gitignored content and static assets, fix figure shortcode order
- glob.ts: add optional respectGitignore param (default true) so callers
can opt out of gitignore filtering for generated directories
- build.ts: pass respectGitignore=false when globbing content/ so
gitignored-but-generated markdown files are always picked up
- static.ts: copy top-level static/ → output/ root with gitignore disabled,
mirroring Hugo's convention so ox-hugo images at static/ox-hugo/* are
served at /ox-hugo/* as expected by the exported markdown src paths
- oxhugofm.ts: run replaceFigureWithMdImg before removeHugoShortcode so the
precise figure regex sees the intact shortcode before the generic {{.*}}
stripper destroys it; also upgrade figureTagRegex to figureShortcodeRegex
which matches the full shortcode form ox-hugo actually emits
This commit is contained in:
@@ -71,7 +71,7 @@ async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) {
|
|||||||
console.log(`Cleaned output directory \`${output}\` in ${perf.timeSince("clean")}`)
|
console.log(`Cleaned output directory \`${output}\` in ${perf.timeSince("clean")}`)
|
||||||
|
|
||||||
perf.addEvent("glob")
|
perf.addEvent("glob")
|
||||||
const allFiles = await glob("**/*.*", argv.directory, cfg.configuration.ignorePatterns)
|
const allFiles = await glob("**/*.*", argv.directory, cfg.configuration.ignorePatterns, false)
|
||||||
const markdownPaths = allFiles.filter((fp) => fp.endsWith(".md")).sort()
|
const markdownPaths = allFiles.filter((fp) => fp.endsWith(".md")).sort()
|
||||||
console.log(
|
console.log(
|
||||||
`Found ${markdownPaths.length} input files from \`${argv.directory}\` in ${perf.timeSince("glob")}`,
|
`Found ${markdownPaths.length} input files from \`${argv.directory}\` in ${perf.timeSince("glob")}`,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { dirname } from "path"
|
|||||||
export const Static: QuartzEmitterPlugin = () => ({
|
export const Static: QuartzEmitterPlugin = () => ({
|
||||||
name: "Static",
|
name: "Static",
|
||||||
async *emit({ argv, cfg }) {
|
async *emit({ argv, cfg }) {
|
||||||
|
// Copy Quartz's own internal static assets (quartz/static/) → output/static/
|
||||||
const staticPath = joinSegments(QUARTZ, "static")
|
const staticPath = joinSegments(QUARTZ, "static")
|
||||||
const fps = await glob("**", staticPath, cfg.configuration.ignorePatterns)
|
const fps = await glob("**", staticPath, cfg.configuration.ignorePatterns)
|
||||||
const outputStaticPath = joinSegments(argv.output, "static")
|
const outputStaticPath = joinSegments(argv.output, "static")
|
||||||
@@ -18,6 +19,21 @@ export const Static: QuartzEmitterPlugin = () => ({
|
|||||||
await fs.promises.copyFile(src, dest)
|
await fs.promises.copyFile(src, dest)
|
||||||
yield dest
|
yield dest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy user-facing static assets (static/) → output/ preserving paths.
|
||||||
|
// This mirrors Hugo's convention: static/ox-hugo/foo.png is served at /ox-hugo/foo.png,
|
||||||
|
// which matches the src="/ox-hugo/..." paths that ox-hugo writes into exported markdown.
|
||||||
|
const userStaticPath = "static"
|
||||||
|
if (fs.existsSync(userStaticPath)) {
|
||||||
|
const userFps = await glob("**", userStaticPath, cfg.configuration.ignorePatterns, false)
|
||||||
|
for (const fp of userFps) {
|
||||||
|
const src = joinSegments(userStaticPath, fp) as FilePath
|
||||||
|
const dest = joinSegments(argv.output, fp) as FilePath
|
||||||
|
await fs.promises.mkdir(dirname(dest), { recursive: true })
|
||||||
|
await fs.promises.copyFile(src, dest)
|
||||||
|
yield dest
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async *partialEmit() {},
|
async *partialEmit() {},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ const defaultOptions: Options = {
|
|||||||
const relrefRegex = new RegExp(/\[([^\]]+)\]\(\{\{< relref "([^"]+)" >\}\}\)/, "g")
|
const relrefRegex = new RegExp(/\[([^\]]+)\]\(\{\{< relref "([^"]+)" >\}\}\)/, "g")
|
||||||
const predefinedHeadingIdRegex = new RegExp(/(.*) {#(?:.*)}/, "g")
|
const predefinedHeadingIdRegex = new RegExp(/(.*) {#(?:.*)}/, "g")
|
||||||
const hugoShortcodeRegex = new RegExp(/{{(.*)}}/, "g")
|
const hugoShortcodeRegex = new RegExp(/{{(.*)}}/, "g")
|
||||||
const figureTagRegex = new RegExp(/< ?figure src="(.*)" ?>/, "g")
|
// Matches the full Hugo {{< figure src="..." ... >}} shortcode and captures src.
|
||||||
|
// Must run before the generic shortcode stripper to avoid partial-match issues
|
||||||
|
// with captions that contain HTML (e.g. <span class="figure-number">).
|
||||||
|
const figureShortcodeRegex = new RegExp(/{{<\s*figure\b[^}]*\bsrc="([^"]*)"[^}]*>}}/, "g")
|
||||||
// \\\\\( -> matches \\(
|
// \\\\\( -> matches \\(
|
||||||
// (.+?) -> Lazy match for capturing the equation
|
// (.+?) -> Lazy match for capturing the equation
|
||||||
// \\\\\) -> matches \\)
|
// \\\\\) -> matches \\)
|
||||||
@@ -70,6 +73,14 @@ export const OxHugoFlavouredMarkdown: QuartzTransformerPlugin<Partial<Options>>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.replaceFigureWithMdImg) {
|
||||||
|
src = src.toString()
|
||||||
|
src = src.replaceAll(figureShortcodeRegex, (_value, ...capture) => {
|
||||||
|
const [imgSrc] = capture
|
||||||
|
return ``
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (opts.removeHugoShortcode) {
|
if (opts.removeHugoShortcode) {
|
||||||
src = src.toString()
|
src = src.toString()
|
||||||
src = src.replaceAll(hugoShortcodeRegex, (_value, ...capture) => {
|
src = src.replaceAll(hugoShortcodeRegex, (_value, ...capture) => {
|
||||||
@@ -78,14 +89,6 @@ export const OxHugoFlavouredMarkdown: QuartzTransformerPlugin<Partial<Options>>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.replaceFigureWithMdImg) {
|
|
||||||
src = src.toString()
|
|
||||||
src = src.replaceAll(figureTagRegex, (_value, ...capture) => {
|
|
||||||
const [src] = capture
|
|
||||||
return ``
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.replaceOrgLatex) {
|
if (opts.replaceOrgLatex) {
|
||||||
src = src.toString()
|
src = src.toString()
|
||||||
src = src.replaceAll(inlineLatexRegex, (_value, ...capture) => {
|
src = src.replaceAll(inlineLatexRegex, (_value, ...capture) => {
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ export async function glob(
|
|||||||
pattern: string,
|
pattern: string,
|
||||||
cwd: string,
|
cwd: string,
|
||||||
ignorePatterns: string[],
|
ignorePatterns: string[],
|
||||||
|
respectGitignore: boolean = true,
|
||||||
): Promise<FilePath[]> {
|
): Promise<FilePath[]> {
|
||||||
const fps = (
|
const fps = (
|
||||||
await globby(pattern, {
|
await globby(pattern, {
|
||||||
cwd,
|
cwd,
|
||||||
ignore: ignorePatterns,
|
ignore: ignorePatterns,
|
||||||
gitignore: true,
|
gitignore: respectGitignore,
|
||||||
})
|
})
|
||||||
).map(toPosixPath)
|
).map(toPosixPath)
|
||||||
return fps as FilePath[]
|
return fps as FilePath[]
|
||||||
|
|||||||
Reference in New Issue
Block a user