Store the promise of the extracted loadData() directly in the
per-id map rather than resolving a hand-made pending promise at the
end of the load hook. This also propagates loader errors to the
other build's pending consumer, which previously awaited forever.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collapse the duplicated listen branches and await the listening
event; serve() now also rejects on listen errors like EADDRINUSE
instead of crashing in the callback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Iterate the log parser with for-await instead of data/end/error
callbacks (spawn errors are forwarded by destroying the stream), and
await the single-file lookup with events.once, which rejects on
child error by itself.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pipeline() replaces the manual finish/error promise and also
surfaces SitemapStream errors, which pipe() never forwarded to the
write stream.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Content, include, route-template and search reads now run
concurrently, so large sites can hit EMFILE/ENFILE. Route all async
reads through a shared readFile util that retries with backoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Expanding an include that is its own ancestor recursed forever.
Track the ancestor chain and leave such includes unexpanded; the
same file can still be included repeatedly as a sibling.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolve includes through replaceAsync and fs/promises so include
files are read without blocking the event loop while pages render
concurrently.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the existsSync pre-check in favor of catching ENOENT from the
read itself, and skip indexing when a page renders to nothing (file
gone or excluded via search: false) instead of feeding empty html to
the section splitter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Split the block parser and fence renderer into factories, extract
region extraction, and type the snippet token instead of ts-ignoring
its src field.
Reading the file now handles ENOENT/EISDIR explicitly: a missing
snippet previously threw from statSync before the 'path not found'
message could render, so that branch was unreachable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
It is internal and its only caller passes every argument; apply the
fallbacks at the call site instead of hiding them in the signature
after the required siteConfig parameter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Read the 404 page with fs/promises, replace the trimChar helper with
a regex, and flatten the if/else around listen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Swap the remaining sync fs calls in renderPage for fs/promises,
switch head-script minification to the async minify API, and make
resolvePageImports async for the realpath call. Pages render under
buildConcurrency, so blocking calls here stall the whole batch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
createContentLoader already maps files with bounded concurrency, but
the sync stat/read calls serialized the actual I/O.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A fixed ui-custom name still gives adblock filter lists a stable
string to match on. Emit the Carbon/BuySellAds chunks with just the
content hash instead. Changes the emitted filenames for those chunks
and, transitively, the hashes of chunks that import them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Now that dev updates actually reach the index, any changed markdown
file was being ingested — including srcExclude'd include partials and
dynamic route templates, which are not pages and produced dead search
results. Mirror the initial scan and only index files present in
siteConfig.pages.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
createContentLoader called md.renderAsync without a MarkdownEnv, so
the internal link plugin fell back to defaults and always emitted
.html links in rendered content and excerpts even with cleanUrls
enabled, and plugins reading env.path/relativePath broke. Build the
same env as a normal page render and forward it to both calls.
fixes#4331closes#5299
Co-authored-by: TowyTowy <towy@airreps.link>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-indexing only invalidated the bare @localSearchIndex module; the
per-locale modules that carry the data were still served from the
transform cache, and the browser module cache pinned them too. Version
the locale import in dev so every index update loads fresh data.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rewrites were computed from the locally globbed pages only, so pages
injected via the config were present in siteConfig.pages but never
entered the rewrites map.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
importerMap values were srcDir-relative paths computed with
path.posix.relative, which garbles on Windows drive-letter case
mismatches, and the delete cleanup used a relative key against the
absolute-keyed map so it never removed anything. Store module ids
instead, derive the cache key with a win32-aware relative, and prune
both the include entry and page references after invalidating.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The dead link check compares resolved links against source-space page
paths, so a link to the original location of a rewritten page passed
the check but 404s in the built site.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Discovered config files are keyed by their source directory, but the
lookup walked the rewrite-applied relativePath, so directories covered
by rewrites silently lost their additional config. Resolve against
pageData.filePath on both server and client, which also makes dynamic
route pages inherit configs from their route template's directory.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
URLs were derived from the source path only, so loader-generated links
pointed at the pre-rewrite location and 404ed for rewritten pages.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
indexFile expects a srcDir-relative page and joins it with srcDir, but
hotUpdate passed the absolute path, producing a doubled path that
failed the existence check — so edits never updated the local search
index. Regression from #3374.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The markdownToVue compile cache is keyed by the rewrite-applied
relativePath, but include-triggered invalidation passed the source
path, so pages with rewrites served stale content in dev when an
included file changed.
closes#5035
Co-authored-by: yyznm <yyz_nm@163.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A markdown or Vue file whose path contains the word "docsearch" was
intercepted by the @docsearch/css transform and never compiled,
failing the build. Check page extensions first.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Equivalent to re-applying the rewrite map manually, but hardened
against drive-letter case mismatches on Windows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Previously generateSitemap re-read and re-parsed every source file to
compute lastmod, reconstructing source paths from URLs. The transform
hook now records the already-computed pageData.lastUpdated per page,
keyed by its rewrite-applied relativePath. Dynamic route pages now get
a lastmod too (from their route template), which the old file lookup
could not resolve.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`markdown.codeCopyButton` now holds both the button tooltip
(`tooltipText`) and the text shown after copying (`copiedText`),
per-locale overridable via `locales.<index>.markdown.codeCopyButton`.
The copied text is emitted as a `data-copied` attribute on the button
and rendered by the theme with `content: attr(data-copied)`.
BREAKING CHANGE: `markdown.codeCopyButtonTitle` is now
`markdown.codeCopyButton.tooltipText`, and its default changed from
"Copy Code" to "Copy code". The `--vp-code-copy-copied-text-content`
CSS variable and its built-in per-language `:lang()` defaults are
removed - set `codeCopyButton.copiedText` (per locale) instead.
close#4431
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Strings baked into pages by the markdown renderer can now be overridden
per locale via `locales.<index>.markdown` in the site config: container
titles (including GitHub-flavored alerts and custom containers) and the
code copy button title. Values resolve against `env.localeIndex` at
render time and fall back to the root markdown options, so the single
shared renderer keeps working unchanged.
close#4431
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New containers can be registered via
`markdown.container.customContainers`, mapping a container name to its
default title. Registered names work as `::: name` blocks and as
GitHub-style alerts (`> [!NAME]`), with custom titles, attributes, and
no-title behaving like the built-in types. Styling is left to the theme
via `.custom-block.name`.
close#3591close#3603close#4228
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A no-title attribute (`::: tip {no-title}`) renders a container without
its title element, and the theme reduces the top padding for containers
without one. It has no effect on `details`, which always needs its
summary. The docs previously hand-rolled this with a raw div and an
inline style - those are now switched to the new syntax.
close#4928close#4932close#4929
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces markdown-it-container and merges the GitHub alerts plugin into
the containers module so both share one title map. Rendered output is
byte-compatible with the previous implementation.
This also restores fence-line attributes on containers ({open}, {.class},
etc.), which broke when markdown-it-attrs was replaced with
@mdit/plugin-attrs - its fence rule handled containers too, but had to be
disabled to keep code block meta intact. The container plugin now parses
the trailing braces itself, so the old markdown-it-attrs patch is not
needed in any form.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
base.css is now Tailwind's preflight (pinned to their e48c5e80) wrapped
in the vitepress cascade layer, with the font stacks pointing at the
vitepress variables and the bold weight at 600, followed by a section
with the vitepress-specific overrides. Compared to the old base this
picks up the universal margin/padding/border reset, tab-size: 4, iOS
tap-highlight removal, and the modern form and date-input
normalizations, and drops fossils like the touch-action click-delay
hack and main { display: block }.
BREAKING CHANGE: normalize-level defaults differ from the old reset -
native select and number-input chrome is restored, headings inherit
font size and weight, the hidden attribute is enforced with !important,
and every element starts with zero margin/padding and border-style:
solid. Sites layering custom CSS on the default theme may notice.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Inter Core faces (previously Inter4CJK) leave East Asian ambiguous
punctuation and symbols uncovered so that the document's CJK font can
render them, but the next family in the stack was a Latin system font,
which claimed most of those characters before the language-aware system
fallback could pick the right CJK font - and characters it lacked could
land in an unrelated one (like Apple SD Gothic Neo on Chinese pages in
Chrome on macOS). The zh/ja/ko stacks now name the platform defaults
explicitly (PingFang SC / Microsoft YaHei / Noto Sans CJK SC, Hiragino
Sans / Meiryo / Yu Gothic / Noto Sans CJK JP, Apple SD Gothic Neo /
Malgun Gothic / Noto Sans CJK KR), so per-glyph matching lands there
deterministically, and sites can splice a CJK webfont between
'Inter Core' and the system families. Traditional Chinese currently
shares the zh (Hans) rule; the TC variant is documented in the script.
BREAKING CHANGE: the Inter4CJK font family has been renamed to
'Inter Core'. Custom --vp-font-family-base overrides referencing
Inter4CJK must be updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-module hooks (resolveId / load / transform) now declare filters,
so rolldown skips the Rust-to-JS call for modules they cannot act on
instead of invoking every hook for every module in the graph.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>