valaxy 1.0.0-rc.7 → 1.0.0-rc.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import 'node:path';
2
2
  import 'node:process';
3
3
  import 'yargs';
4
4
  import 'yargs/helpers';
5
- export { c as createCli, J as registerDevCommand, Y as run, _ as startValaxyDev } from '../../shared/valaxy.BA1nryio.mjs';
5
+ export { c as createCli, J as registerDevCommand, Y as run, _ as startValaxyDev } from '../../shared/valaxy.Cb_HdczD.mjs';
6
6
  import 'node:os';
7
7
  import 'consola';
8
8
  import 'consola/utils';
@@ -38,7 +38,6 @@ import 'vitepress-plugin-group-icons';
38
38
  import 'p-map';
39
39
  import 'node:buffer';
40
40
  import 'minisearch';
41
- import 'markdown-it-async';
42
41
  import '@shikijs/transformers';
43
42
  import 'shiki';
44
43
  import 'css-i18n';
@@ -9,15 +9,12 @@ import { VitePluginConfig } from 'unocss/vite';
9
9
  import Components from 'unplugin-vue-components/vite';
10
10
  import Markdown from 'unplugin-vue-markdown/vite';
11
11
  import Layouts from 'vite-plugin-vue-layouts-next';
12
- import { groupIconVitePlugin } from 'vitepress-plugin-group-icons';
13
12
  import { EditableTreeNode } from 'vue-router/unplugin';
14
13
  import Router from 'vue-router/vite';
15
14
  import { MarkdownEnv } from 'unplugin-vue-markdown/types';
16
15
  import { Argv } from 'yargs';
17
16
  import { KatexOptions } from 'katex';
18
- import MarkdownIt from 'markdown-it';
19
- import anchorPlugin from 'markdown-it-anchor';
20
- import { Options, MarkdownItAsync } from 'markdown-it-async';
17
+ import MarkdownIt, { MarkdownItOptions, Env, MarkdownIt as MarkdownIt$1, StateCore, Token } from 'markdown-it';
21
18
  import { ThemeRegistration, BuiltinTheme, LanguageInput, ShikiTransformer, Highlighter } from 'shiki';
22
19
  import { Header } from '@valaxyjs/utils';
23
20
  export { CreateCliOptions, createCli, registerDevCommand, run, startValaxyDev } from './cli/index.mjs';
@@ -298,6 +295,23 @@ interface TocPluginOptions {
298
295
  linkClass?: string;
299
296
  }
300
297
 
298
+ type MarkdownItPresetName = 'default' | 'zero' | 'commonmark';
299
+ type AsyncHighlighter = (str: string, lang: string, attrs: string) => string | Promise<string>;
300
+ interface MarkdownItAsyncOptions extends Omit<MarkdownItOptions, 'highlight'> {
301
+ highlight?: AsyncHighlighter | null;
302
+ /** Emit a warning when an async renderer is called through render(). */
303
+ warnOnSyncRender?: boolean;
304
+ }
305
+ declare class MarkdownItAsync extends MarkdownIt {
306
+ private readonly pendingHighlights;
307
+ private readonly warnOnSyncRender;
308
+ private disableSyncRenderWarning;
309
+ constructor(presetName: MarkdownItPresetName, options?: MarkdownItAsyncOptions);
310
+ constructor(options?: MarkdownItAsyncOptions);
311
+ render(src: string, env?: Env): string;
312
+ renderAsync(src: string, env?: Env): Promise<string>;
313
+ }
314
+
301
315
  interface BlockItem {
302
316
  text?: string;
303
317
  icon?: string;
@@ -319,11 +333,31 @@ type ThemeOptions = ThemeRegistration | BuiltinTheme | {
319
333
  light: ThemeRegistration | BuiltinTheme;
320
334
  dark: ThemeRegistration | BuiltinTheme;
321
335
  };
336
+ interface MarkdownAnchorPermalinkOptions {
337
+ class?: string;
338
+ symbol?: string;
339
+ renderHref?: (slug: string, state: StateCore) => string;
340
+ renderAttrs?: (slug: string, state: StateCore) => Record<string, string | number>;
341
+ }
342
+ type MarkdownAnchorPermalinkGenerator = (slug: string, options: MarkdownAnchorPermalinkOptions, state: StateCore, index: number) => void;
343
+ interface MarkdownAnchorOptions {
344
+ level?: number | number[];
345
+ slugify?: (value: string) => string;
346
+ slugifyWithState?: (value: string, state: StateCore) => string;
347
+ getTokensText?: (tokens: Token[]) => string;
348
+ uniqueSlugStartIndex?: number;
349
+ permalink?: MarkdownAnchorPermalinkGenerator;
350
+ callback?: (token: Token, info: {
351
+ slug: string;
352
+ title: string;
353
+ }) => void;
354
+ tabIndex?: number | false;
355
+ }
322
356
  /**
323
357
  * Extend Markdown options
324
358
  * @zh 扩展 Markdown 配置,包含代码高亮、Markdown-it 和插件配置
325
359
  */
326
- interface MarkdownOptions extends Options {
360
+ interface MarkdownOptions extends MarkdownItAsyncOptions {
327
361
  /**
328
362
  * Setup markdown-it instance before applying plugins
329
363
  */
@@ -331,12 +365,12 @@ interface MarkdownOptions extends Options {
331
365
  /**
332
366
  * markdown-it options
333
367
  */
334
- options?: MarkdownIt['options'];
368
+ options?: MarkdownIt$1['options'];
335
369
  /**
336
370
  * config markdown-it
337
371
  */
338
372
  config?: (md: MarkdownItAsync) => void;
339
- anchor?: anchorPlugin.AnchorOptions;
373
+ anchor?: MarkdownAnchorOptions;
340
374
  attrs?: {
341
375
  leftDelimiter?: string;
342
376
  rightDelimiter?: string;
@@ -644,6 +678,14 @@ interface CdnModule {
644
678
  */
645
679
  exports?: string[];
646
680
  }
681
+ type GroupIconValue = string | {
682
+ dark: string;
683
+ light: string;
684
+ };
685
+ interface GroupIconOptions {
686
+ customIcon?: Record<string, GroupIconValue>;
687
+ defaultLabels?: string[];
688
+ }
647
689
  type ValaxyNodeConfig<ThemeConfig = DefaultTheme.Config> = ValaxyConfig<ThemeConfig> & ValaxyExtendConfig;
648
690
  type UserValaxyNodeConfig<ThemeConfig = DefaultTheme.Config> = PartialDeep<ValaxyNodeConfig<ThemeConfig>>;
649
691
  /**
@@ -853,7 +895,7 @@ interface ValaxyExtendConfig {
853
895
  /**
854
896
  * @see https://github.com/yuyinws/vitepress-plugin-group-icons
855
897
  */
856
- groupIcons?: Partial<NonNullable<Parameters<typeof groupIconVitePlugin>[0]>>;
898
+ groupIcons?: GroupIconOptions;
857
899
  /**
858
900
  * unocss presets
859
901
  * @see https://unocss.dev/guide/presets
@@ -1210,4 +1252,4 @@ declare function resolveImportPath(importName: string, ensure?: true): Promise<s
1210
1252
  declare function resolveImportPath(importName: string, ensure?: boolean): Promise<string | undefined>;
1211
1253
 
1212
1254
  export { $t, ALL_ROUTE, EXCERPT_SEPARATOR, GLOBAL_STATE, PATHNAME_PROTOCOL_RE, StateManager, ViteValaxyPlugins, build, createMemoryReleasePlugin, createServer, createValaxyPlugin, customElements, defaultSiteConfig, defaultValaxyConfig, defaultViteConfig, defineAddon, defineConfig, defineContentLoader, defineSiteConfig, defineTheme, defineUnoSetup, defineValaxyAddon, defineValaxyConfig, defineValaxyTheme, encryptContent, generateClientRedirects, getGitTimestamp, getIndexHtml, getServerInfoText, isExternal, isInstalledGlobally, isKatexEnabled, isKatexPluginNeeded, isMathJaxEnabled, isPath, loadConfigFromFile, mergeValaxyConfig, mergeViteConfigs, postProcessForSSG, processValaxyOptions, resolveAddonsConfig, resolveImportPath, resolveImportUrl, resolveOptions, resolveSiteConfig, resolveSiteConfigFromRoot, resolveThemeConfigFromRoot, resolveThemeValaxyConfig, resolveUserThemeConfig, resolveValaxyConfig, resolveValaxyConfigFromRoot, ssgBuild, toAtFS, transformObject, version };
1213
- export type { CdnModule, ContentItem, ContentLoader, ContentLoaderContext, HookResult, LoadConfigFromFileOptions, MdAfterRenderContext, ResolvedConfig, ResolvedValaxyOptions, UnoSetup, UserInputConfig, UserValaxyNodeConfig, ValaxyAddonCliContext, ValaxyAddonExport, ValaxyAddonExtendCli, ValaxyAddonFn, ValaxyAddonLike, ValaxyAddonResolver, ValaxyAddons, ValaxyApp, ValaxyConfigExport, ValaxyConfigExtendKey, ValaxyConfigFn, ValaxyEntryOptions, ValaxyExtendConfig, ValaxyFileInfo, ValaxyHooks, ValaxyNode, ValaxyNodeAddon, ValaxyNodeConfig, ValaxyPickConfig, ValaxyServerOptions, ValaxyTheme };
1255
+ export type { CdnModule, ContentItem, ContentLoader, ContentLoaderContext, GroupIconOptions, GroupIconValue, HookResult, LoadConfigFromFileOptions, MdAfterRenderContext, ResolvedConfig, ResolvedValaxyOptions, UnoSetup, UserInputConfig, UserValaxyNodeConfig, ValaxyAddonCliContext, ValaxyAddonExport, ValaxyAddonExtendCli, ValaxyAddonFn, ValaxyAddonLike, ValaxyAddonResolver, ValaxyAddons, ValaxyApp, ValaxyConfigExport, ValaxyConfigExtendKey, ValaxyConfigFn, ValaxyEntryOptions, ValaxyExtendConfig, ValaxyFileInfo, ValaxyHooks, ValaxyNode, ValaxyNodeAddon, ValaxyNodeConfig, ValaxyPickConfig, ValaxyServerOptions, ValaxyTheme };
@@ -1,4 +1,4 @@
1
- export { A as ALL_ROUTE, E as EXCERPT_SEPARATOR, G as GLOBAL_STATE, P as PATHNAME_PROTOCOL_RE, S as StateManager, V as ViteValaxyPlugins, b as build, c as createCli, a as createMemoryReleasePlugin, d as createServer, e as createValaxyPlugin, f as customElements, g as defaultSiteConfig, h as defaultValaxyConfig, i as defaultViteConfig, j as defineAddon, k as defineConfig, l as defineSiteConfig, m as defineTheme, n as defineValaxyAddon, o as defineValaxyConfig, p as defineValaxyTheme, q as encryptContent, r as generateClientRedirects, s as getGitTimestamp, t as getIndexHtml, u as getServerInfoText, v as isExternal, w as isInstalledGlobally, x as isKatexEnabled, y as isKatexPluginNeeded, z as isMathJaxEnabled, B as isPath, C as loadConfigFromFile, D as mergeValaxyConfig, F as mergeViteConfigs, H as postProcessForSSG, I as processValaxyOptions, J as registerDevCommand, K as resolveAddonsConfig, L as resolveImportPath, M as resolveImportUrl, N as resolveOptions, O as resolveSiteConfig, Q as resolveSiteConfigFromRoot, R as resolveThemeConfigFromRoot, T as resolveThemeValaxyConfig, U as resolveUserThemeConfig, W as resolveValaxyConfig, X as resolveValaxyConfigFromRoot, Y as run, Z as ssgBuild, _ as startValaxyDev, $ as toAtFS, a0 as transformObject, a1 as version } from '../shared/valaxy.BA1nryio.mjs';
1
+ export { A as ALL_ROUTE, E as EXCERPT_SEPARATOR, G as GLOBAL_STATE, P as PATHNAME_PROTOCOL_RE, S as StateManager, V as ViteValaxyPlugins, b as build, c as createCli, a as createMemoryReleasePlugin, d as createServer, e as createValaxyPlugin, f as customElements, g as defaultSiteConfig, h as defaultValaxyConfig, i as defaultViteConfig, j as defineAddon, k as defineConfig, l as defineSiteConfig, m as defineTheme, n as defineValaxyAddon, o as defineValaxyConfig, p as defineValaxyTheme, q as encryptContent, r as generateClientRedirects, s as getGitTimestamp, t as getIndexHtml, u as getServerInfoText, v as isExternal, w as isInstalledGlobally, x as isKatexEnabled, y as isKatexPluginNeeded, z as isMathJaxEnabled, B as isPath, C as loadConfigFromFile, D as mergeValaxyConfig, F as mergeViteConfigs, H as postProcessForSSG, I as processValaxyOptions, J as registerDevCommand, K as resolveAddonsConfig, L as resolveImportPath, M as resolveImportUrl, N as resolveOptions, O as resolveSiteConfig, Q as resolveSiteConfigFromRoot, R as resolveThemeConfigFromRoot, T as resolveThemeValaxyConfig, U as resolveUserThemeConfig, W as resolveValaxyConfig, X as resolveValaxyConfigFromRoot, Y as run, Z as ssgBuild, _ as startValaxyDev, $ as toAtFS, a0 as transformObject, a1 as version } from '../shared/valaxy.Cb_HdczD.mjs';
2
2
  import 'node:path';
3
3
  import 'consola';
4
4
  import 'fs-extra';
@@ -38,7 +38,6 @@ import 'vitepress-plugin-group-icons';
38
38
  import 'p-map';
39
39
  import 'node:buffer';
40
40
  import 'minisearch';
41
- import 'markdown-it-async';
42
41
  import '@shikijs/transformers';
43
42
  import 'shiki';
44
43
  import 'css-i18n';
@@ -37,7 +37,6 @@ import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-i
37
37
  import pMap from 'p-map';
38
38
  import { Buffer as Buffer$1 } from 'node:buffer';
39
39
  import MiniSearch from 'minisearch';
40
- import { createMarkdownItAsync } from 'markdown-it-async';
41
40
  import { transformerNotationDiff, transformerNotationFocus, transformerNotationHighlight, transformerNotationErrorLevel, transformerCompactLineOptions } from '@shikijs/transformers';
42
41
  import { createHighlighter, addClassToHast, isSpecialLang } from 'shiki';
43
42
  import { cssI18nContainer } from 'css-i18n';
@@ -1552,6 +1551,77 @@ function sendText(res, content, headOnly) {
1552
1551
  }
1553
1552
  }
1554
1553
 
1554
+ const placeholderRE = /<pre><!--::valaxy-markdown-async::(\w+)::--><code>[\s\S]*?<\/code><\/pre>/g;
1555
+ let placeholderId = 0;
1556
+ class MarkdownItAsync extends MarkdownIt {
1557
+ pendingHighlights;
1558
+ warnOnSyncRender;
1559
+ disableSyncRenderWarning = false;
1560
+ constructor(presetOrOptions, options) {
1561
+ const pendingHighlights = /* @__PURE__ */ new Map();
1562
+ const asyncOptions = typeof presetOrOptions === "string" ? options : presetOrOptions;
1563
+ if (typeof presetOrOptions === "string")
1564
+ super(presetOrOptions, wrapOptions(options, pendingHighlights));
1565
+ else if (presetOrOptions)
1566
+ super(wrapOptions(presetOrOptions, pendingHighlights));
1567
+ else
1568
+ super();
1569
+ this.pendingHighlights = pendingHighlights;
1570
+ this.warnOnSyncRender = asyncOptions?.warnOnSyncRender ?? false;
1571
+ }
1572
+ render(src, env) {
1573
+ if (this.warnOnSyncRender && !this.disableSyncRenderWarning)
1574
+ console.warn("[valaxy] Use renderAsync() when Markdown highlighting can be asynchronous.");
1575
+ return super.render(src, env);
1576
+ }
1577
+ async renderAsync(src, env) {
1578
+ this.disableSyncRenderWarning = true;
1579
+ let html;
1580
+ try {
1581
+ html = this.render(src, env);
1582
+ } finally {
1583
+ this.disableSyncRenderWarning = false;
1584
+ }
1585
+ const replacements = [];
1586
+ for (const match of html.matchAll(placeholderRE)) {
1587
+ const id = match[1];
1588
+ const pending = this.pendingHighlights.get(id);
1589
+ if (!pending)
1590
+ throw new Error(`Unknown async highlight placeholder: ${id}`);
1591
+ const highlighted = await pending.promise || "";
1592
+ replacements.push(highlighted.startsWith("<pre") ? highlighted : `<pre><code class="language-${pending.lang}">${highlighted}</code></pre>`);
1593
+ this.pendingHighlights.delete(id);
1594
+ }
1595
+ let index = 0;
1596
+ return html.replace(placeholderRE, () => replacements[index++] || "");
1597
+ }
1598
+ }
1599
+ function createMarkdownItAsync(presetOrOptions, options) {
1600
+ return typeof presetOrOptions === "string" ? new MarkdownItAsync(presetOrOptions, options) : new MarkdownItAsync(presetOrOptions);
1601
+ }
1602
+ function wrapOptions(options, pendingHighlights) {
1603
+ if (!options)
1604
+ return void 0;
1605
+ const { highlight, warnOnSyncRender: _, ...markdownItOptions } = options;
1606
+ if (!highlight)
1607
+ return markdownItOptions;
1608
+ return {
1609
+ ...markdownItOptions,
1610
+ highlight(str, lang, attrs) {
1611
+ const result = highlight(str, lang, attrs);
1612
+ if (typeof result === "string")
1613
+ return result;
1614
+ const id = (++placeholderId).toString(36);
1615
+ pendingHighlights.set(id, { lang, promise: result });
1616
+ const code = str.endsWith("\n") ? str.slice(0, -1) : str;
1617
+ return `<pre><!--::valaxy-markdown-async::${id}::--><code>${escapeHtml(code)}</code></pre>`;
1618
+ }
1619
+ };
1620
+ }
1621
+ function escapeHtml(value) {
1622
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1623
+ }
1624
+
1555
1625
  const POOL_SIZE_MULTIPLIER = 128;
1556
1626
  let pool, poolOffset;
1557
1627
 
@@ -1826,6 +1896,74 @@ function disposeSharedHighlighter() {
1826
1896
  highlighterCache.clear();
1827
1897
  }
1828
1898
 
1899
+ //#region src/constants.ts
1900
+ const TAG_NAME_SCRIPT = "script";
1901
+ const TAG_NAME_STYLE = "style";
1902
+ const TAG_NAME_TEMPLATE = "template";
1903
+
1904
+ //#endregion
1905
+ //#region src/sfc-regexp.ts
1906
+ const SCRIPT_SETUP_TAG_OPEN_REGEXP = /^<script\s+.*?\bsetup\b.*?>$/is;
1907
+ /**
1908
+ * Generate RegExp for sfc blocks
1909
+ */
1910
+ const createSfcRegexp = ({ customBlocks }) => {
1911
+ const sfcTags = Array.from(new Set([
1912
+ TAG_NAME_SCRIPT,
1913
+ TAG_NAME_STYLE,
1914
+ ...customBlocks
1915
+ ])).join("|");
1916
+ return new RegExp(`^\\s*(?<content>(?<tagOpen><(?<type>${sfcTags})\\s?.*?>)(?<contentStripped>.*)(?<tagClose><\\/\\k<type>\\s*>))\\s*$`, "is");
1917
+ };
1918
+
1919
+ //#endregion
1920
+ //#region src/sfc-plugin.ts
1921
+ /**
1922
+ * Get Vue SFC blocks
1923
+ *
1924
+ * Extract them into env and avoid rendering them
1925
+ */
1926
+ const sfcPlugin = (md, { customBlocks = [] } = {}) => {
1927
+ const sfcRegexp = createSfcRegexp({ customBlocks });
1928
+ const render = md.render.bind(md);
1929
+ md.render = (src, env = {}) => {
1930
+ env.sfcBlocks = {
1931
+ template: null,
1932
+ script: null,
1933
+ scriptSetup: null,
1934
+ scripts: [],
1935
+ styles: [],
1936
+ customBlocks: []
1937
+ };
1938
+ const rendered = render(src, env);
1939
+ env.sfcBlocks.template = {
1940
+ type: TAG_NAME_TEMPLATE,
1941
+ content: `<${TAG_NAME_TEMPLATE}>${rendered}</${TAG_NAME_TEMPLATE}>`,
1942
+ contentStripped: rendered,
1943
+ tagOpen: `<${TAG_NAME_TEMPLATE}>`,
1944
+ tagClose: `</${TAG_NAME_TEMPLATE}>`
1945
+ };
1946
+ return rendered;
1947
+ };
1948
+ const htmlBlockRule = md.renderer.rules.html_block;
1949
+ md.renderer.rules.html_block = (tokens, idx, options, env, self) => {
1950
+ /* istanbul ignore if -- @preserve */
1951
+ if (!env.sfcBlocks) return htmlBlockRule(tokens, idx, options, env, self);
1952
+ const token = tokens[idx];
1953
+ const content = token.content;
1954
+ const match = content.match(sfcRegexp);
1955
+ if (!match) return htmlBlockRule(tokens, idx, options, env, self);
1956
+ const sfcBlock = match.groups;
1957
+ if (sfcBlock.type === TAG_NAME_SCRIPT) {
1958
+ env.sfcBlocks.scripts.push(sfcBlock);
1959
+ if (SCRIPT_SETUP_TAG_OPEN_REGEXP.test(sfcBlock.tagOpen)) env.sfcBlocks.scriptSetup = sfcBlock;
1960
+ else env.sfcBlocks.script = sfcBlock;
1961
+ } else if (sfcBlock.type === TAG_NAME_STYLE) env.sfcBlocks.styles.push(sfcBlock);
1962
+ else env.sfcBlocks.customBlocks.push(sfcBlock);
1963
+ return "";
1964
+ };
1965
+ };
1966
+
1829
1967
  //#region src/html-escape.ts
1830
1968
  const htmlEscapeMap = {
1831
1969
  "&": "&amp;",
@@ -1917,113 +2055,6 @@ const rCombining = /[\u0300-\u036F]/g;
1917
2055
  */
1918
2056
  const slugify = (str) => str.normalize("NFKD").replace(rCombining, "").replace(rControl, "").replace(rSpecial, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "").replace(/^(\d)/, "_$1").toLowerCase();
1919
2057
 
1920
- //#region src/headers-plugin.ts
1921
- /**
1922
- * Get markdown headers info
1923
- *
1924
- * Extract them into env
1925
- */
1926
- const headersPlugin = (md, { level = [2, 3], shouldAllowNested = false, slugify: slugify$1 = slugify, format } = {}) => {
1927
- const render = md.renderer.render.bind(md.renderer);
1928
- md.renderer.render = (tokens, options, env) => {
1929
- env.headers = resolveHeadersFromTokens(tokens, {
1930
- level,
1931
- shouldAllowHtml: false,
1932
- shouldAllowNested,
1933
- shouldEscapeText: false,
1934
- slugify: slugify$1,
1935
- format
1936
- });
1937
- return render(tokens, options, env);
1938
- };
1939
- };
1940
-
1941
- //#region src/constants.ts
1942
- const TAG_NAME_SCRIPT = "script";
1943
- const TAG_NAME_STYLE = "style";
1944
- const TAG_NAME_TEMPLATE = "template";
1945
-
1946
- //#endregion
1947
- //#region src/sfc-regexp.ts
1948
- const SCRIPT_SETUP_TAG_OPEN_REGEXP = /^<script\s+.*?\bsetup\b.*?>$/is;
1949
- /**
1950
- * Generate RegExp for sfc blocks
1951
- */
1952
- const createSfcRegexp = ({ customBlocks }) => {
1953
- const sfcTags = Array.from(new Set([
1954
- TAG_NAME_SCRIPT,
1955
- TAG_NAME_STYLE,
1956
- ...customBlocks
1957
- ])).join("|");
1958
- return new RegExp(`^\\s*(?<content>(?<tagOpen><(?<type>${sfcTags})\\s?.*?>)(?<contentStripped>.*)(?<tagClose><\\/\\k<type>\\s*>))\\s*$`, "is");
1959
- };
1960
-
1961
- //#endregion
1962
- //#region src/sfc-plugin.ts
1963
- /**
1964
- * Get Vue SFC blocks
1965
- *
1966
- * Extract them into env and avoid rendering them
1967
- */
1968
- const sfcPlugin = (md, { customBlocks = [] } = {}) => {
1969
- const sfcRegexp = createSfcRegexp({ customBlocks });
1970
- const render = md.render.bind(md);
1971
- md.render = (src, env = {}) => {
1972
- env.sfcBlocks = {
1973
- template: null,
1974
- script: null,
1975
- scriptSetup: null,
1976
- scripts: [],
1977
- styles: [],
1978
- customBlocks: []
1979
- };
1980
- const rendered = render(src, env);
1981
- env.sfcBlocks.template = {
1982
- type: TAG_NAME_TEMPLATE,
1983
- content: `<${TAG_NAME_TEMPLATE}>${rendered}</${TAG_NAME_TEMPLATE}>`,
1984
- contentStripped: rendered,
1985
- tagOpen: `<${TAG_NAME_TEMPLATE}>`,
1986
- tagClose: `</${TAG_NAME_TEMPLATE}>`
1987
- };
1988
- return rendered;
1989
- };
1990
- const htmlBlockRule = md.renderer.rules.html_block;
1991
- md.renderer.rules.html_block = (tokens, idx, options, env, self) => {
1992
- /* istanbul ignore if -- @preserve */
1993
- if (!env.sfcBlocks) return htmlBlockRule(tokens, idx, options, env, self);
1994
- const token = tokens[idx];
1995
- const content = token.content;
1996
- const match = content.match(sfcRegexp);
1997
- if (!match) return htmlBlockRule(tokens, idx, options, env, self);
1998
- const sfcBlock = match.groups;
1999
- if (sfcBlock.type === TAG_NAME_SCRIPT) {
2000
- env.sfcBlocks.scripts.push(sfcBlock);
2001
- if (SCRIPT_SETUP_TAG_OPEN_REGEXP.test(sfcBlock.tagOpen)) env.sfcBlocks.scriptSetup = sfcBlock;
2002
- else env.sfcBlocks.script = sfcBlock;
2003
- } else if (sfcBlock.type === TAG_NAME_STYLE) env.sfcBlocks.styles.push(sfcBlock);
2004
- else env.sfcBlocks.customBlocks.push(sfcBlock);
2005
- return "";
2006
- };
2007
- };
2008
-
2009
- //#region src/title-plugin.ts
2010
- /**
2011
- * Get markdown page title info
2012
- *
2013
- * Extract it into env
2014
- */
2015
- const titlePlugin = (md) => {
2016
- const render = md.renderer.render.bind(md.renderer);
2017
- md.renderer.render = (tokens, options, env) => {
2018
- const tokenIdx = tokens.findIndex((token) => token.tag === "h1");
2019
- env.title = tokenIdx > -1 ? resolveTitleFromToken(tokens[tokenIdx + 1], {
2020
- shouldAllowHtml: false,
2021
- shouldEscapeText: false
2022
- }) : "";
2023
- return render(tokens, options, env);
2024
- };
2025
- };
2026
-
2027
2058
  //#region src/create-render-headers.ts
2028
2059
  const createRenderHeaders = ({ listTag, listClass, itemClass, linkTag, linkClass }) => {
2029
2060
  const listTagString = htmlEscape(listTag);
@@ -2130,7 +2161,8 @@ function imagePlugin(md) {
2130
2161
  const imageRule = md.renderer.rules.image;
2131
2162
  md.renderer.rules.image = (tokens, idx, options, env, self) => {
2132
2163
  const token = tokens[idx];
2133
- let url = token.attrGet("src");
2164
+ const src = token.attrGet("src");
2165
+ let url = src === null ? null : String(src);
2134
2166
  if (url && !EXTERNAL_URL_RE.test(url)) {
2135
2167
  if (!/^\.?\//.test(url))
2136
2168
  url = `./${url}`;
@@ -2147,29 +2179,28 @@ function linkPlugin(md, externalAttrs, resolveBase) {
2147
2179
  const hrefIndex = token.attrIndex("href");
2148
2180
  if (hrefIndex >= 0) {
2149
2181
  const hrefAttr = token.attrs[hrefIndex];
2150
- const url = hrefAttr[1];
2182
+ let url = String(hrefAttr[1]);
2151
2183
  const isExternal = EXTERNAL_URL_RE.test(url);
2152
2184
  if (isExternal) {
2153
2185
  Object.entries(externalAttrs).forEach(([key, val]) => {
2154
2186
  token.attrSet(key, val);
2155
2187
  });
2156
- hrefAttr[1] = url.replace(PATHNAME_PROTOCOL_RE, "");
2188
+ url = url.replace(PATHNAME_PROTOCOL_RE, "");
2157
2189
  } else {
2158
2190
  if (
2159
2191
  // internal anchor links
2160
2192
  !url.startsWith("#") && !url.startsWith("mailto:") && !/\.(?!html|md)\w+($|[?#])/i.test(url)
2161
2193
  ) {
2162
- normalizeHref(hrefAttr, env);
2194
+ url = normalizeHref(url, env ?? {});
2163
2195
  }
2164
- if (hrefAttr[1].startsWith("/"))
2165
- hrefAttr[1] = `${resolveBase()}${hrefAttr[1]}`.replace(/\/+/g, "/");
2196
+ if (url.startsWith("/"))
2197
+ url = `${resolveBase()}${url}`.replace(/\/+/g, "/");
2166
2198
  }
2167
- hrefAttr[1] = hrefAttr[1].replace(/\bimport\.meta/g, "import%2Emeta").replace(/\bprocess\.env/g, "process%2Eenv");
2199
+ hrefAttr[1] = url.replace(/\bimport\.meta/g, "import%2Emeta").replace(/\bprocess\.env/g, "process%2Eenv");
2168
2200
  }
2169
2201
  return self.renderToken(tokens, idx, options);
2170
2202
  };
2171
- function normalizeHref(hrefAttr, env) {
2172
- let url = hrefAttr[1];
2203
+ function normalizeHref(url, env) {
2173
2204
  const indexMatch = url.match(indexRE);
2174
2205
  if (indexMatch) {
2175
2206
  const [, path, hash] = indexMatch;
@@ -2191,7 +2222,7 @@ function linkPlugin(md, externalAttrs, resolveBase) {
2191
2222
  if (!url.startsWith("/") && !/^\.\//.test(url))
2192
2223
  url = `./${url}`;
2193
2224
  pushLink(url.replace(/\.html$/, ""), env);
2194
- hrefAttr[1] = decodeURI(url);
2225
+ return decodeURI(url);
2195
2226
  }
2196
2227
  function pushLink(link, env) {
2197
2228
  const links = env.links || (env.links = []);
@@ -2224,7 +2255,7 @@ function preWrapperPlugin(md, options) {
2224
2255
  token.info = token.info.replace(/ active$/, "").replace(/ active /, " ");
2225
2256
  const lang = extractLang(token.info);
2226
2257
  const rawCode = fence(...args);
2227
- const codeHeightLimitClass = getCodeHeightLimitStyle(options, env);
2258
+ const codeHeightLimitClass = getCodeHeightLimitStyle(options, env ?? {});
2228
2259
  const wrap = (code) => `<div class="language-${lang}${active}${codeHeightLimitClass}"><button title="${options.codeCopyButtonTitle || "Copy code"}" class="copy"></button><span class="lang">${lang}</span>${code}<button class="code-block-unfold-btn"></button></div>`;
2229
2260
  return isPromiseLike(rawCode) ? rawCode.then(wrap) : wrap(rawCode);
2230
2261
  });
@@ -2646,7 +2677,7 @@ function findRegion(lines, regionName) {
2646
2677
  }
2647
2678
  return null;
2648
2679
  }
2649
- function snippetPlugin(md, srcDir) {
2680
+ function snippetPlugin(md, srcDir = "") {
2650
2681
  const parser = (state, startLine, endLine, silent) => {
2651
2682
  const CH = "<".charCodeAt(0);
2652
2683
  const pos = state.bMarks[startLine] + state.tShift[startLine];
@@ -2668,7 +2699,7 @@ function snippetPlugin(md, srcDir) {
2668
2699
  const token = state.push("fence", "code", 0);
2669
2700
  token.info = `${lang || extension}${lines ? `{${lines}}` : ""}${title ? `[${title}]` : ""}`;
2670
2701
  const { realPath, path: _path } = state.env;
2671
- const resolvedPath = path$1.resolve(path$1.dirname(realPath ?? _path), filepath);
2702
+ const resolvedPath = path$1.resolve(path$1.dirname(realPath ?? _path ?? "."), filepath);
2672
2703
  token.src = [resolvedPath, region.slice(1)];
2673
2704
  token.markup = "```";
2674
2705
  token.map = [startLine, startLine + 1];
@@ -2676,7 +2707,8 @@ function snippetPlugin(md, srcDir) {
2676
2707
  };
2677
2708
  const fence = md.renderer.rules.fence;
2678
2709
  md.renderer.rules.fence = (...args) => {
2679
- const [tokens, idx, , { includes }] = args;
2710
+ const [tokens, idx, , env] = args;
2711
+ const { includes } = env ?? {};
2680
2712
  const token = tokens[idx];
2681
2713
  const [src, regionName] = token.src ?? [];
2682
2714
  if (!src)
@@ -2706,22 +2738,51 @@ function snippetPlugin(md, srcDir) {
2706
2738
  }
2707
2739
 
2708
2740
  const defaultCodeTheme = { light: "github-light", dark: "github-dark" };
2741
+ const sfcPluginV15 = sfcPlugin;
2742
+ const tocPluginV15 = tocPlugin;
2743
+ const anchorPluginV15 = anchorPlugin;
2744
+ function setupMarkdownPageMetadata(md, options) {
2745
+ const mdOptions = options?.config.markdown || {};
2746
+ const headersOptions = typeof mdOptions.headers === "boolean" ? {} : mdOptions.headers || {};
2747
+ const {
2748
+ level = [2, 3, 4, 5, 6],
2749
+ shouldAllowNested = false,
2750
+ slugify: headersSlugify = slugify,
2751
+ format
2752
+ } = headersOptions;
2753
+ md.core.ruler.push("valaxy_page_metadata", (state) => {
2754
+ const tokens = state.tokens;
2755
+ state.env.headers = resolveHeadersFromTokens(tokens, {
2756
+ level,
2757
+ shouldAllowHtml: false,
2758
+ shouldAllowNested,
2759
+ shouldEscapeText: false,
2760
+ slugify: headersSlugify,
2761
+ format
2762
+ });
2763
+ const titleTokenIndex = state.tokens.findIndex((token) => token.tag === "h1");
2764
+ state.env.title = titleTokenIndex > -1 ? resolveTitleFromToken(state.tokens[titleTokenIndex + 1], {
2765
+ shouldAllowHtml: false,
2766
+ shouldEscapeText: false
2767
+ }) : "";
2768
+ });
2769
+ }
2709
2770
  async function setupMarkdownPlugins(md, options, base = options?.config.vite?.base || "/") {
2710
2771
  const mdOptions = options?.config.markdown || {};
2711
- const theme = mdOptions.theme ?? defaultCodeTheme;
2712
2772
  const siteConfig = options?.config.siteConfig || {};
2773
+ const languages = siteConfig.languages?.filter((language) => Boolean(language));
2713
2774
  const resolveBase = createMarkdownBaseResolver(base);
2714
2775
  if (mdOptions.preConfig)
2715
2776
  mdOptions.preConfig(md);
2716
- md.use(highlightLinePlugin).use(preWrapperPlugin, { theme, siteConfig }).use(snippetPlugin, options?.userRoot).use(containerPlugin, {
2717
- languages: siteConfig.languages,
2777
+ md.use(highlightLinePlugin).use(preWrapperPlugin, { siteConfig }).use(snippetPlugin, options?.userRoot ?? "").use(containerPlugin, {
2778
+ languages,
2718
2779
  ...mdOptions?.container,
2719
2780
  blocks: {
2720
2781
  ...mdOptions.blocks,
2721
2782
  ...mdOptions.container?.blocks
2722
2783
  }
2723
2784
  }).use(cssI18nContainer, {
2724
- languages: options?.config.siteConfig.languages
2785
+ languages
2725
2786
  }).use(
2726
2787
  linkPlugin,
2727
2788
  {
@@ -2735,12 +2796,12 @@ async function setupMarkdownPlugins(md, options, base = options?.config.vite?.ba
2735
2796
  if (!mdOptions.attrs?.disable)
2736
2797
  md.use(attrsPlugin, mdOptions.attrs);
2737
2798
  md.use(full).use(footnotePlugin).use(footnoteTooltipPlugin);
2738
- md.use(anchorPlugin, {
2799
+ md.use(anchorPluginV15, {
2739
2800
  slugify,
2740
2801
  getTokensText: (tokens) => {
2741
2802
  return tokens.filter((t) => !["html_inline", "emoji"].includes(t.type)).map((t) => t.content).join("");
2742
2803
  },
2743
- permalink: anchorPlugin.permalink.linkInsideHeader({
2804
+ permalink: anchorPluginV15.permalink.linkInsideHeader({
2744
2805
  symbol: "&ZeroWidthSpace;",
2745
2806
  renderAttrs: (slug, state) => {
2746
2807
  const idx = state.tokens.findIndex((token) => {
@@ -2756,13 +2817,9 @@ async function setupMarkdownPlugins(md, options, base = options?.config.vite?.ba
2756
2817
  }),
2757
2818
  ...mdOptions.anchor
2758
2819
  });
2759
- md.use(headersPlugin, {
2760
- level: [2, 3, 4, 5, 6],
2761
- slugify,
2762
- ...typeof mdOptions.headers === "boolean" ? void 0 : mdOptions.headers
2763
- }).use(sfcPlugin, {
2820
+ md.use(sfcPluginV15, {
2764
2821
  ...mdOptions.sfc
2765
- }).use(titlePlugin).use(tocPlugin, {
2822
+ }).use(tocPluginV15, {
2766
2823
  slugify,
2767
2824
  ...mdOptions.toc
2768
2825
  });
@@ -2849,7 +2906,7 @@ async function setupMarkdownPlugins(md, options, base = options?.config.vite?.ba
2849
2906
  return md;
2850
2907
  }
2851
2908
 
2852
- const version = "1.0.0-rc.7";
2909
+ const version = "1.0.0-rc.9";
2853
2910
 
2854
2911
  const GLOBAL_STATE = {
2855
2912
  valaxyApp: void 0,
@@ -2962,7 +3019,11 @@ async function createMarkdownPlugin(options, base, state = Valaxy.state) {
2962
3019
  const [highlight, dispose] = mdOptions.highlight ? [mdOptions.highlight, () => {
2963
3020
  }] : await getSharedHighlighter(theme, mdOptions, logger);
2964
3021
  state.onDispose(dispose);
2965
- const { transforms: userTransforms, ...restMdOptions } = mdOptions;
3022
+ const {
3023
+ transforms: userTransforms,
3024
+ markdownItSetup: userMarkdownItSetup,
3025
+ ...restMdOptions
3026
+ } = mdOptions;
2966
3027
  return Markdown({
2967
3028
  include: [/\.md$/],
2968
3029
  wrapperClasses: "",
@@ -2991,15 +3052,19 @@ async function createMarkdownPlugin(options, base, state = Valaxy.state) {
2991
3052
  async markdownItSetup(mdIt) {
2992
3053
  mdIt.linkify.set({ fuzzyLink: false });
2993
3054
  await setupMarkdownPlugins(mdIt, options, base);
2994
- options?.config.markdown?.markdownItSetup?.(mdIt);
3055
+ await userMarkdownItSetup?.(mdIt);
3056
+ setupMarkdownPageMetadata(mdIt, options);
2995
3057
  function initEnv(md) {
2996
3058
  md.core.ruler.push("valaxy_md_env", (mdState) => {
3059
+ const env = mdState.env;
3060
+ if (!env.id)
3061
+ return;
2997
3062
  state.set({
2998
- id: mdState.env.id,
2999
- title: mdState.env.title,
3000
- links: mdState.env.links,
3001
- headers: mdState.env.headers,
3002
- frontmatter: mdState.env.frontmatter
3063
+ id: env.id,
3064
+ title: env.title ?? "",
3065
+ links: env.links ?? [],
3066
+ headers: env.headers ?? [],
3067
+ frontmatter: env.frontmatter ?? {}
3003
3068
  });
3004
3069
  });
3005
3070
  }
@@ -3043,6 +3108,7 @@ async function createMarkdownRenderer(options, base) {
3043
3108
  await mdOptions.markdownItSetup?.(
3044
3109
  md
3045
3110
  );
3111
+ setupMarkdownPageMetadata(md, options);
3046
3112
  return md;
3047
3113
  }
3048
3114
  async function createLightMarkdownRenderer(options, base) {
@@ -3059,6 +3125,7 @@ async function createLightMarkdownRenderer(options, base) {
3059
3125
  });
3060
3126
  md.linkify.set({ fuzzyLink: false });
3061
3127
  await setupMarkdownPlugins(md, options, base);
3128
+ setupMarkdownPageMetadata(md, options);
3062
3129
  return md;
3063
3130
  }
3064
3131
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "valaxy",
3
3
  "type": "module",
4
- "version": "1.0.0-rc.7",
4
+ "version": "1.0.0-rc.9",
5
5
  "description": "📄 Vite & Vue powered static blog generator.",
6
6
  "author": {
7
7
  "email": "me@yunyoujun.cn",
@@ -60,7 +60,7 @@
60
60
  "node": ">=22.12.0"
61
61
  },
62
62
  "peerDependencies": {
63
- "vue": "^3.5.0",
63
+ "vue": "^3.5.41",
64
64
  "vue-router": "^5.0.0"
65
65
  },
66
66
  "dependencies": {
@@ -100,12 +100,11 @@
100
100
  "js-yaml": "^4.3.0",
101
101
  "katex": "^0.16.47",
102
102
  "lru-cache": "^11.5.1",
103
- "markdown-it": "^14.3.0",
104
- "markdown-it-anchor": "^9.2.0",
105
- "markdown-it-async": "^2.2.0",
106
- "markdown-it-attrs": "^4.5.0",
103
+ "markdown-it": "^15.0.1",
104
+ "markdown-it-anchor": "^9.2.1",
105
+ "markdown-it-attrs": "^5.0.1",
107
106
  "markdown-it-container": "^4.0.0",
108
- "markdown-it-emoji": "^3.0.0",
107
+ "markdown-it-emoji": "^3.1.0",
109
108
  "markdown-it-footnote": "^4.0.0",
110
109
  "markdown-it-image-figures": "^2.1.1",
111
110
  "markdown-it-table-of-contents": "^1.2.0",
@@ -136,11 +135,11 @@
136
135
  "vite-plugin-vue-devtools": "^8.1.5",
137
136
  "vite-plugin-vue-layouts-next": "^2.1.0",
138
137
  "vite-ssg-sitemap": "^0.10.0",
139
- "vitepress-plugin-group-icons": "^1.7.5",
138
+ "vitepress-plugin-group-icons": "^1.7.6",
140
139
  "vue-i18n": "^11.4.6",
141
140
  "yargs": "^18.0.0",
142
- "@valaxyjs/devtools": "1.0.0-rc.7",
143
- "@valaxyjs/utils": "1.0.0-rc.7"
141
+ "@valaxyjs/devtools": "1.0.0-rc.9",
142
+ "@valaxyjs/utils": "1.0.0-rc.9"
144
143
  },
145
144
  "devDependencies": {
146
145
  "@mdit-vue/plugin-component": "^3.0.2",
@@ -155,8 +154,6 @@
155
154
  "@types/gh-pages": "^6.1.0",
156
155
  "@types/html-to-text": "^9.0.4",
157
156
  "@types/js-yaml": "^4.0.9",
158
- "@types/markdown-it": "^14.1.2",
159
- "@types/markdown-it-footnote": "^3.0.4",
160
157
  "@types/nprogress": "^0.2.3",
161
158
  "@types/pascalcase": "^1.0.3",
162
159
  "@types/qrcode": "^1.5.6",
@@ -166,7 +163,7 @@
166
163
  "nanoid": "^5.1.16",
167
164
  "rollup-plugin-visualizer": "^7.0.1",
168
165
  "unbuild": "^3.6.1",
169
- "vue": "3.5.22",
166
+ "vue": "^3.5.41",
170
167
  "vue-router": "^5.1.0"
171
168
  },
172
169
  "scripts": {
@@ -1,12 +1,10 @@
1
1
  /**
2
2
  * Type augmentation for markdown-it-container
3
3
  *
4
- * The official @types/markdown-it-container@2.0.11 depends on @types/markdown-it@13.0.9,
5
- * but we use @types/markdown-it@14.1.2. This file provides compatible type definitions.
4
+ * Local definitions keep the plugin compatible with markdown-it's bundled v15 types.
6
5
  */
7
6
  declare module 'markdown-it-container' {
8
- import type MarkdownIt from 'markdown-it'
9
- import type Token from 'markdown-it/lib/token.mjs'
7
+ import type { MarkdownIt, RendererRule } from 'markdown-it'
10
8
 
11
9
  interface ContainerOptions {
12
10
  /**
@@ -17,7 +15,7 @@ declare module 'markdown-it-container' {
17
15
  /**
18
16
  * Renderer for opening/closing tokens.
19
17
  */
20
- render?: (tokens: Token[], idx: number, options: any, env: any, self: any) => string
18
+ render?: RendererRule
21
19
 
22
20
  /**
23
21
  * Character to use in delimiter, default is ":"
@@ -0,0 +1,22 @@
1
+ declare module 'markdown-it-emoji' {
2
+ import type { MarkdownIt } from 'markdown-it'
3
+
4
+ export interface EmojiOptions {
5
+ defs: Record<string, string>
6
+ enabled: string[]
7
+ shortcuts: Record<string, string | string[]>
8
+ }
9
+
10
+ export type EmojiPlugin = (md: MarkdownIt, options?: Partial<EmojiOptions>) => void
11
+
12
+ export const bare: EmojiPlugin
13
+ export const full: EmojiPlugin
14
+ export const light: EmojiPlugin
15
+ }
16
+
17
+ declare module 'markdown-it-footnote' {
18
+ import type { MarkdownIt } from 'markdown-it'
19
+
20
+ const footnotePlugin: (md: MarkdownIt) => void
21
+ export default footnotePlugin
22
+ }