@astrojs/markdown-remark 7.0.0-beta.7 → 7.0.0-beta.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.
@@ -11,7 +11,6 @@ export declare const defaultExcludeLanguages: string[];
11
11
  * @param highlighter
12
12
  * A function which receives the code and language, and returns the HTML of a syntax
13
13
  * highlighted `<pre>` element.
14
- * @returns The number of code blocks that were highlighted.
15
14
  */
16
- export declare function highlightCodeBlocks(tree: Root, highlighter: Highlighter, excludeLanguages?: string[]): Promise<number>;
15
+ export declare function highlightCodeBlocks(tree: Root, highlighter: Highlighter, excludeLanguages?: string[]): Promise<void>;
17
16
  export {};
package/dist/highlight.js CHANGED
@@ -54,7 +54,6 @@ async function highlightCodeBlocks(tree, highlighter, excludeLanguages = []) {
54
54
  const index = grandParent.children.indexOf(parent);
55
55
  grandParent.children[index] = replacement;
56
56
  }
57
- return nodes.length;
58
57
  }
59
58
  export {
60
59
  defaultExcludeLanguages,
package/dist/index.d.ts CHANGED
@@ -5,8 +5,6 @@ export { rehypePrism } from './rehype-prism.js';
5
5
  export { rehypeShiki } from './rehype-shiki.js';
6
6
  export { remarkCollectImages } from './remark-collect-images.js';
7
7
  export { type CreateShikiHighlighterOptions, createShikiHighlighter, type ShikiHighlighter, type ShikiHighlighterHighlightOptions, } from './shiki.js';
8
- export { globalShikiStyleCollector } from './shiki-style-collector.js';
9
- export { transformerStyleToClass, type ShikiTransformerStyleToClass, } from './transformers/style-to-class.js';
10
8
  export * from './types.js';
11
9
  export declare const syntaxHighlightDefaults: Required<SyntaxHighlightConfig>;
12
10
  export declare const markdownConfigDefaults: Required<AstroMarkdownOptions>;
package/dist/index.js CHANGED
@@ -25,10 +25,6 @@ import { remarkCollectImages as remarkCollectImages2 } from "./remark-collect-im
25
25
  import {
26
26
  createShikiHighlighter
27
27
  } from "./shiki.js";
28
- import { globalShikiStyleCollector } from "./shiki-style-collector.js";
29
- import {
30
- transformerStyleToClass
31
- } from "./transformers/style-to-class.js";
32
28
  export * from "./types.js";
33
29
  const syntaxHighlightDefaults = {
34
30
  type: "shiki",
@@ -122,8 +118,7 @@ async function createMarkdownProcessor(opts) {
122
118
  headings: result.data.astro?.headings ?? [],
123
119
  localImagePaths: result.data.astro?.localImagePaths ?? [],
124
120
  remoteImagePaths: result.data.astro?.remoteImagePaths ?? [],
125
- frontmatter: result.data.astro?.frontmatter ?? {},
126
- hasCodeBlocks: result.data.astro?.hasCodeBlocks ?? false
121
+ frontmatter: result.data.astro?.frontmatter ?? {}
127
122
  }
128
123
  };
129
124
  }
@@ -150,7 +145,6 @@ export {
150
145
  createMarkdownProcessor,
151
146
  createShikiHighlighter,
152
147
  extractFrontmatter,
153
- globalShikiStyleCollector,
154
148
  isFrontmatterValid,
155
149
  markdownConfigDefaults,
156
150
  parseFrontmatter,
@@ -158,6 +152,5 @@ export {
158
152
  rehypePrism2 as rehypePrism,
159
153
  rehypeShiki2 as rehypeShiki,
160
154
  remarkCollectImages2 as remarkCollectImages,
161
- syntaxHighlightDefaults,
162
- transformerStyleToClass
155
+ syntaxHighlightDefaults
163
156
  };
@@ -2,7 +2,7 @@ import { highlightCodeBlocks } from "./highlight.js";
2
2
  import { createShikiHighlighter } from "./shiki.js";
3
3
  const rehypeShiki = (config, excludeLangs) => {
4
4
  let highlighterAsync;
5
- return async (tree, vfile) => {
5
+ return async (tree) => {
6
6
  highlighterAsync ??= createShikiHighlighter({
7
7
  langs: config?.langs,
8
8
  theme: config?.theme,
@@ -10,7 +10,7 @@ const rehypeShiki = (config, excludeLangs) => {
10
10
  langAlias: config?.langAlias
11
11
  });
12
12
  const highlighter = await highlighterAsync;
13
- const codeBlockCount = await highlightCodeBlocks(
13
+ await highlightCodeBlocks(
14
14
  tree,
15
15
  (code, language, options) => {
16
16
  return highlighter.codeToHast(code, language, {
@@ -22,10 +22,6 @@ const rehypeShiki = (config, excludeLangs) => {
22
22
  },
23
23
  excludeLangs
24
24
  );
25
- if (codeBlockCount > 0) {
26
- vfile.data.astro ??= {};
27
- vfile.data.astro.hasCodeBlocks = true;
28
- }
29
25
  };
30
26
  };
31
27
  export {
@@ -0,0 +1,2 @@
1
+ import type { RegexEngine } from 'shiki';
2
+ export declare function loadShikiEngine(): Promise<RegexEngine>;
@@ -0,0 +1,7 @@
1
+ import { createOnigurumaEngine } from "shiki/engine/oniguruma";
2
+ function loadShikiEngine() {
3
+ return createOnigurumaEngine(import("shiki/wasm"));
4
+ }
5
+ export {
6
+ loadShikiEngine
7
+ };
@@ -0,0 +1,2 @@
1
+ import type { RegexEngine } from 'shiki';
2
+ export declare function loadShikiEngine(): Promise<RegexEngine>;
@@ -0,0 +1,7 @@
1
+ import { createOnigurumaEngine } from "shiki/engine/oniguruma";
2
+ function loadShikiEngine() {
3
+ return createOnigurumaEngine(import("shiki/onig.wasm"));
4
+ }
5
+ export {
6
+ loadShikiEngine
7
+ };
package/dist/shiki.d.ts CHANGED
@@ -40,4 +40,5 @@ export interface ShikiHighlighterHighlightOptions {
40
40
  */
41
41
  meta?: string;
42
42
  }
43
- export declare function createShikiHighlighter({ langs, theme, themes, langAlias, }?: CreateShikiHighlighterOptions): Promise<ShikiHighlighter>;
43
+ export declare function createShikiHighlighter(options?: CreateShikiHighlighterOptions): Promise<ShikiHighlighter>;
44
+ export type { ThemePresets };
package/dist/shiki.js CHANGED
@@ -3,33 +3,69 @@ import {
3
3
  createHighlighter,
4
4
  isSpecialLang
5
5
  } from "shiki";
6
- import { globalShikiStyleCollector } from "./shiki-style-collector.js";
7
- import { transformerStyleToClass } from "./transformers/style-to-class.js";
6
+ import { loadShikiEngine } from "#shiki-engine";
8
7
  let _cssVariablesTheme;
9
8
  const cssVariablesTheme = () => _cssVariablesTheme ?? (_cssVariablesTheme = createCssVariablesTheme({
10
9
  variablePrefix: "--astro-code-"
11
10
  }));
12
11
  const cachedHighlighters = /* @__PURE__ */ new Map();
13
- async function createShikiHighlighter({
12
+ function clearShikiHighlighterCache() {
13
+ cachedHighlighters.clear();
14
+ }
15
+ function createShikiHighlighter(options) {
16
+ const key = getCacheKey(options);
17
+ let highlighterPromise = cachedHighlighters.get(key);
18
+ if (!highlighterPromise) {
19
+ highlighterPromise = createShikiHighlighterInternal(options);
20
+ cachedHighlighters.set(key, highlighterPromise);
21
+ }
22
+ return ensureLanguagesLoaded(highlighterPromise, options?.langs);
23
+ }
24
+ function getCacheKey(options) {
25
+ const keyCache = [];
26
+ const { theme, themes, langAlias } = options ?? {};
27
+ if (theme) {
28
+ keyCache.push(theme);
29
+ }
30
+ if (themes) {
31
+ keyCache.push(Object.entries(themes).sort());
32
+ }
33
+ if (langAlias) {
34
+ keyCache.push(Object.entries(langAlias).sort());
35
+ }
36
+ return keyCache.length > 0 ? JSON.stringify(keyCache) : "";
37
+ }
38
+ async function ensureLanguagesLoaded(promise, langs) {
39
+ const highlighter = await promise;
40
+ if (!langs) {
41
+ return highlighter;
42
+ }
43
+ const loadedLanguages = highlighter.getLoadedLanguages();
44
+ for (const lang of langs) {
45
+ if (typeof lang === "string" && (isSpecialLang(lang) || loadedLanguages.includes(lang))) {
46
+ continue;
47
+ }
48
+ await highlighter.loadLanguage(lang);
49
+ }
50
+ return highlighter;
51
+ }
52
+ let shikiEngine = void 0;
53
+ async function createShikiHighlighterInternal({
14
54
  langs = [],
15
55
  theme = "github-dark",
16
56
  themes = {},
17
57
  langAlias = {}
18
58
  } = {}) {
19
59
  theme = theme === "css-variables" ? cssVariablesTheme() : theme;
20
- const highlighterOptions = {
60
+ if (shikiEngine === void 0) {
61
+ shikiEngine = await loadShikiEngine();
62
+ }
63
+ const highlighter = await createHighlighter({
21
64
  langs: ["plaintext", ...langs],
22
65
  langAlias,
23
- themes: Object.values(themes).length ? Object.values(themes) : [theme]
24
- };
25
- const key = JSON.stringify(highlighterOptions, Object.keys(highlighterOptions).sort());
26
- let highlighter;
27
- if (cachedHighlighters.has(key)) {
28
- highlighter = cachedHighlighters.get(key);
29
- } else {
30
- highlighter = await createHighlighter(highlighterOptions);
31
- cachedHighlighters.set(key, highlighter);
32
- }
66
+ themes: Object.values(themes).length ? Object.values(themes) : [theme],
67
+ engine: shikiEngine
68
+ });
33
69
  async function highlight(code, lang = "plaintext", options, to) {
34
70
  const resolvedLang = langAlias[lang] ?? lang;
35
71
  const loadedLanguages = highlighter.getLoadedLanguages();
@@ -54,8 +90,6 @@ async function createShikiHighlighter({
54
90
  // they're technically not meta, nor parsed from Shiki's `parseMetaString` API.
55
91
  meta: options?.meta ? { __raw: options?.meta } : void 0,
56
92
  transformers: [
57
- // Extract inline styles to CSS classes for better performance and CSP compliance
58
- globalShikiStyleCollector.register(transformerStyleToClass()),
59
93
  {
60
94
  pre(node) {
61
95
  if (inline) {
@@ -68,15 +102,13 @@ async function createShikiHighlighter({
68
102
  } = options?.attributes ?? {};
69
103
  Object.assign(node.properties, rest);
70
104
  const classValue = (normalizePropAsString(node.properties.class) ?? "") + (attributesClass ? ` ${attributesClass}` : "");
105
+ const styleValue = (normalizePropAsString(node.properties.style) ?? "") + (attributesStyle ? `; ${attributesStyle}` : "");
71
106
  node.properties.class = classValue.replace(/shiki/g, "astro-code");
72
107
  node.properties.dataLanguage = lang;
73
108
  if (options.wrap === false || options.wrap === void 0) {
74
- this.addClassToHast(node, "astro-code-overflow");
109
+ node.properties.style = styleValue + "; overflow-x: auto;";
75
110
  } else if (options.wrap === true) {
76
- this.addClassToHast(node, "astro-code-overflow astro-code-wrap");
77
- }
78
- if (attributesStyle) {
79
- node.properties.style = attributesStyle;
111
+ node.properties.style = styleValue + "; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;";
80
112
  }
81
113
  },
82
114
  line(node) {
@@ -90,7 +122,7 @@ async function createShikiHighlighter({
90
122
  innerSpanNode.children.unshift({
91
123
  type: "element",
92
124
  tagName: "span",
93
- properties: { class: "astro-code-no-select" },
125
+ properties: { style: "user-select: none;" },
94
126
  children: [{ type: "text", value: start }]
95
127
  });
96
128
  }
@@ -113,6 +145,12 @@ async function createShikiHighlighter({
113
145
  },
114
146
  codeToHtml(code, lang, options = {}) {
115
147
  return highlight(code, lang, options, "html");
148
+ },
149
+ loadLanguage(...newLangs) {
150
+ return highlighter.loadLanguage(...newLangs);
151
+ },
152
+ getLoadedLanguages() {
153
+ return highlighter.getLoadedLanguages();
116
154
  }
117
155
  };
118
156
  }
@@ -120,5 +158,6 @@ function normalizePropAsString(value) {
120
158
  return Array.isArray(value) ? value.join(" ") : value;
121
159
  }
122
160
  export {
161
+ clearShikiHighlighterCache,
123
162
  createShikiHighlighter
124
163
  };
package/dist/types.d.ts CHANGED
@@ -13,7 +13,6 @@ declare module 'vfile' {
13
13
  localImagePaths?: string[];
14
14
  remoteImagePaths?: string[];
15
15
  frontmatter?: Record<string, any>;
16
- hasCodeBlocks?: boolean;
17
16
  };
18
17
  }
19
18
  }
@@ -67,8 +66,6 @@ export interface MarkdownProcessorRenderResult {
67
66
  localImagePaths: string[];
68
67
  remoteImagePaths: string[];
69
68
  frontmatter: Record<string, any>;
70
- /** Whether the markdown contained code blocks that were syntax highlighted */
71
- hasCodeBlocks?: boolean;
72
69
  };
73
70
  }
74
71
  export interface MarkdownHeading {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/markdown-remark",
3
- "version": "7.0.0-beta.7",
3
+ "version": "7.0.0-beta.9",
4
4
  "type": "module",
5
5
  "author": "withastro",
6
6
  "license": "MIT",
@@ -13,12 +13,17 @@
13
13
  "homepage": "https://astro.build",
14
14
  "main": "./dist/index.js",
15
15
  "exports": {
16
- ".": "./dist/index.js"
16
+ ".": "./dist/index.js",
17
+ "./shiki": "./dist/shiki.js"
17
18
  },
18
19
  "imports": {
19
20
  "#import-plugin": {
20
21
  "browser": "./dist/import-plugin-browser.js",
21
22
  "default": "./dist/import-plugin-default.js"
23
+ },
24
+ "#shiki-engine": {
25
+ "workerd": "./dist/shiki-engine-workerd.js",
26
+ "default": "./dist/shiki-engine-default.js"
22
27
  }
23
28
  },
24
29
  "files": [
@@ -36,7 +41,7 @@
36
41
  "remark-parse": "^11.0.0",
37
42
  "remark-rehype": "^11.1.2",
38
43
  "remark-smartypants": "^3.0.2",
39
- "shiki": "^3.22.0",
44
+ "shiki": "^4.0.0",
40
45
  "smol-toml": "^1.6.0",
41
46
  "unified": "^11.0.5",
42
47
  "unist-util-remove-position": "^5.0.0",
@@ -52,7 +57,7 @@
52
57
  "@types/js-yaml": "^4.0.9",
53
58
  "@types/mdast": "^4.0.4",
54
59
  "@types/unist": "^3.0.3",
55
- "esbuild": "^0.24.2",
60
+ "esbuild": "^0.27.3",
56
61
  "mdast-util-mdx-expression": "^2.0.1",
57
62
  "astro-scripts": "0.0.14"
58
63
  },
@@ -1,32 +0,0 @@
1
- import type { ShikiTransformerStyleToClass } from './transformers/style-to-class.js';
2
- /**
3
- * Global singleton to collect Shiki styles from multiple transformer instances.
4
- *
5
- * Each code block (whether from Code.astro or markdown) creates a new transformer instance.
6
- * This collector aggregates styles from all instances so they can be bundled into a single
7
- * CSS file via the virtual module system.
8
- */
9
- declare class ShikiStyleCollector {
10
- private transformers;
11
- /**
12
- * Register a transformer instance to collect styles from.
13
- * Returns the same transformer to allow chaining.
14
- */
15
- register(transformer: ShikiTransformerStyleToClass): ShikiTransformerStyleToClass;
16
- /**
17
- * Collect CSS from all registered transformers.
18
- * This is called by the virtual CSS module to generate the final stylesheet.
19
- */
20
- collectCSS(): string;
21
- /**
22
- * Clear all registered transformers and their style registries.
23
- * Called during HMR in dev mode to prevent stale styles.
24
- */
25
- clear(): void;
26
- }
27
- /**
28
- * Global instance of the style collector.
29
- * Shared between Code.astro component and markdown processing.
30
- */
31
- export declare const globalShikiStyleCollector: ShikiStyleCollector;
32
- export {};
@@ -1,36 +0,0 @@
1
- class ShikiStyleCollector {
2
- transformers = /* @__PURE__ */ new Set();
3
- /**
4
- * Register a transformer instance to collect styles from.
5
- * Returns the same transformer to allow chaining.
6
- */
7
- register(transformer) {
8
- this.transformers.add(transformer);
9
- return transformer;
10
- }
11
- /**
12
- * Collect CSS from all registered transformers.
13
- * This is called by the virtual CSS module to generate the final stylesheet.
14
- */
15
- collectCSS() {
16
- let css = "";
17
- for (const transformer of this.transformers) {
18
- css += transformer.getCSS();
19
- }
20
- return css;
21
- }
22
- /**
23
- * Clear all registered transformers and their style registries.
24
- * Called during HMR in dev mode to prevent stale styles.
25
- */
26
- clear() {
27
- for (const transformer of this.transformers) {
28
- transformer.clearRegistry();
29
- }
30
- this.transformers.clear();
31
- }
32
- }
33
- const globalShikiStyleCollector = new ShikiStyleCollector();
34
- export {
35
- globalShikiStyleCollector
36
- };
@@ -1,18 +0,0 @@
1
- /**
2
- * Copied and adapted from https://github.com/shikijs/shiki/blob/main/packages/transformers/src/transformers/style-to-class.ts
3
- */
4
- import type { ShikiTransformer } from 'shiki';
5
- export interface ShikiTransformerStyleToClass extends ShikiTransformer {
6
- getClassRegistry: () => Map<string, Record<string, string> | string>;
7
- getCSS: () => string;
8
- clearRegistry: () => void;
9
- }
10
- /**
11
- * Transform Shiki inline styles to CSS classes.
12
- * Based on @shikijs/transformers style-to-class transformer.
13
- *
14
- * This transformer extracts inline styles from Shiki-generated HTML and converts them
15
- * to unique class names, allowing CSS to be collected and bundled separately for better
16
- * performance and CSP compliance.
17
- */
18
- export declare function transformerStyleToClass(): ShikiTransformerStyleToClass;
@@ -1,75 +0,0 @@
1
- function transformerStyleToClass() {
2
- const classToStyle = /* @__PURE__ */ new Map();
3
- function stringifyStyle(style) {
4
- return Object.entries(style).map(([key, value]) => `${key}:${value}`).join(";");
5
- }
6
- function registerStyle(style) {
7
- const str = typeof style === "string" ? style : stringifyStyle(style);
8
- let className = "__a_" + cyrb53(str);
9
- if (!classToStyle.has(className)) {
10
- classToStyle.set(className, typeof style === "string" ? style : { ...style });
11
- }
12
- return className;
13
- }
14
- return {
15
- name: "@astrojs/markdown-remark:style-to-class",
16
- pre(node) {
17
- if (!node.properties.style) return;
18
- const className = registerStyle(node.properties.style);
19
- delete node.properties.style;
20
- this.addClassToHast(node, className);
21
- },
22
- tokens(lines) {
23
- for (const line of lines) {
24
- for (const token of line) {
25
- let className;
26
- if (token.htmlStyle) {
27
- className = registerStyle(token.htmlStyle);
28
- token.htmlStyle = {};
29
- } else {
30
- className = registerStyle({ color: token.color });
31
- token.color = "";
32
- }
33
- token.htmlAttrs ||= {};
34
- if (!token.htmlAttrs.class) {
35
- token.htmlAttrs.class = className;
36
- } else {
37
- token.htmlAttrs.class += ` ${className}`;
38
- }
39
- }
40
- }
41
- },
42
- getClassRegistry() {
43
- return classToStyle;
44
- },
45
- getCSS() {
46
- let css = ".astro-code-overflow{overflow-x:auto}";
47
- css += ".astro-code-wrap{white-space:pre-wrap;word-wrap:break-word}";
48
- css += ".astro-code-no-select{user-select:none}";
49
- for (const [className, style] of classToStyle.entries()) {
50
- css += `.${className}{${typeof style === "string" ? style : stringifyStyle(style)}}`;
51
- }
52
- return css;
53
- },
54
- clearRegistry() {
55
- classToStyle.clear();
56
- }
57
- };
58
- }
59
- function cyrb53(str, seed = 0) {
60
- let h1 = 3735928559 ^ seed;
61
- let h2 = 1103547991 ^ seed;
62
- for (let i = 0, ch; i < str.length; i++) {
63
- ch = str.charCodeAt(i);
64
- h1 = Math.imul(h1 ^ ch, 2654435761);
65
- h2 = Math.imul(h2 ^ ch, 1597334677);
66
- }
67
- h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
68
- h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
69
- h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
70
- h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
71
- return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36).slice(0, 6);
72
- }
73
- export {
74
- transformerStyleToClass
75
- };