isFolderPath() was checked on the raw input before slugifyFilePath()
rewrote folder notes (e.g. "My Folder/My Folder" -> "my-folder/index").
Now checks isFolderPath on both the raw input and the slugified result,
so the trailing slash is correctly emitted for folder note links.
The initial commit added normalizeHastElement to src/path.ts but
forgot to include it in src/index.ts's top-level re-export list,
so importers doing `import { normalizeHastElement } from '@quartz-community/utils'`
would see 'has no exported member'. Imports via the /path subpath
worked, but the top-level entrypoint is the conventional import
surface for most consumers.
Moved from Quartz core (quartz/util/path.ts) into the shared utils
package so that plugins performing cross-slug HAST embedding can
re-use the same rebase helper as Quartz's internal transclude
expansion, rather than reimplementing or skipping the rebase.
The primary consumer outside of Quartz core is canvas-page, whose
buildEmbeddedContent() pulls vfile.data.htmlAst from arbitrary target
pages and serializes it verbatim into canvas pages - producing broken
relative hrefs that survive accidentally on root deployments but fail
under non-trivial basePath configurations. Exposing the helper here
lets canvas-page (and any future embedding plugin) rebase correctly
without duplicating the logic.
Implementation details:
- Deep-clones the input via structuredClone (Node >=22 builtin) so
callers don't need to bring their own clone function
- Absolute URLs (http://, https://, /root-relative) pass through
untouched; only relative URLs are rebased
- Recurses into children so nested anchors inside a rebased subtree
are also normalized
Backed by 10 new tests covering same-slug, cross-slug (both directions),
nested depth, anchor preservation, absolute URL passthrough, root-relative
passthrough, src-attribute rebasing, elements without href/src, nested
children recursion, and input-immutability.
slugifyPath now lowercases its output, which cascades through
slugifyFilePath, slugTag, and transformInternalLink (all of which
funnel through _sluggify).
This matches Obsidian's case-insensitive link and tag matching:
[[MyNote]], [[mynote]], and [[MYNOTE]] all resolve to the slug
'my-note', and the tags #MyTag, #mytag, and #MYTAG collapse into a
single tag page.
BREAKING CHANGE: all generated URLs are now lowercase. Users
upgrading with mixed-case source filenames will see their URLs
change (e.g. /MyNote -> /my-note). Also eliminates silent data
loss on case-insensitive filesystems (macOS APFS, Windows NTFS)
where Apple.md and apple.md previously produced conflicting HTML
outputs that overwrote each other without warning.
The 'shortest' strategy in transformLink() only matched by filename (last
segment), causing wiki-links like [[Species/Elf/Wood]] to fail resolution
when the target lived under a deeper path (e.g. Compendium/Species/Elf/Wood).
Add suffix-based matching for multi-segment partial paths and handle the
/index variant for folder targets whose suffix gets stripped by
transformInternalLink().
When src is a folder page (e.g. 'Compendium/Spells' backed by index.md),
pathToRoot miscalculated the depth because the browser serves folder pages
at '/Compendium/Spells/' (trailing slash). Now detects folder pages via
allSlugs and uses the index slug for correct relative URL generation.
Add utility functions to resolve links correctly when Quartz is deployed
to a subdirectory (e.g., user.github.io/repository). getBasePath() reads
the base path from a data attribute on <body>, and resolveBasePath()
prepends it to slugs for client-side navigation.
With build tools (tsup, typescript) moved to devDependencies, the prepare
script can no longer build when npm fetches this package from GitHub (npm
does not install devDependencies for git dependencies). Commit the pre-built
dist/ output so GitHub-based installs work without a build step.
tsup and typescript are only needed at build time — move to devDependencies
so they can be pruned after build. hast-util-to-jsx-runtime and github-slugger
are available from the host Quartz installation — move to peerDependencies
to avoid duplication across 42+ plugins.
Provides a wrapped toJsxRuntime that prevents HTML-escaping of <style>
and <script> content by using dangerouslySetInnerHTML overrides.
Exported as @quartz-community/utils/jsx entry point.
When installed as a git dependency, npm skips devDependencies. Since
dist/ is gitignored and the prepare script needs tsup to build, these
must be regular dependencies so the prepare hook works on install.
Import branded types from the canonical types package instead of
re-defining them with different brand names. This ensures plugins
using both @quartz-community/types and @quartz-community/utils
have structurally compatible FullSlug and FilePath types.
Add splitAnchor, transformLink, transformInternalLink, resolveRelative,
pathToRoot, slugifyFilePath, slugTag, FilePath type, and various type
guards to path.ts. Add escapeHTML/unescapeHTML in new escape.ts module.
Add capitalize to lang.ts. Add github-slugger dependency.