@astrojs/markdown-remark 7.0.0-beta.5 → 7.0.0-beta.7
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.
- package/dist/highlight.d.ts +2 -1
- package/dist/highlight.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -2
- package/dist/rehype-shiki.js +6 -2
- package/dist/shiki-style-collector.d.ts +32 -0
- package/dist/shiki-style-collector.js +36 -0
- package/dist/shiki.js +10 -4
- package/dist/transformers/style-to-class.d.ts +18 -0
- package/dist/transformers/style-to-class.js +75 -0
- package/dist/types.d.ts +3 -0
- package/package.json +2 -2
package/dist/highlight.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ 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.
|
|
14
15
|
*/
|
|
15
|
-
export declare function highlightCodeBlocks(tree: Root, highlighter: Highlighter, excludeLanguages?: string[]): Promise<
|
|
16
|
+
export declare function highlightCodeBlocks(tree: Root, highlighter: Highlighter, excludeLanguages?: string[]): Promise<number>;
|
|
16
17
|
export {};
|
package/dist/highlight.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ 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';
|
|
8
10
|
export * from './types.js';
|
|
9
11
|
export declare const syntaxHighlightDefaults: Required<SyntaxHighlightConfig>;
|
|
10
12
|
export declare const markdownConfigDefaults: Required<AstroMarkdownOptions>;
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,10 @@ 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";
|
|
28
32
|
export * from "./types.js";
|
|
29
33
|
const syntaxHighlightDefaults = {
|
|
30
34
|
type: "shiki",
|
|
@@ -118,7 +122,8 @@ async function createMarkdownProcessor(opts) {
|
|
|
118
122
|
headings: result.data.astro?.headings ?? [],
|
|
119
123
|
localImagePaths: result.data.astro?.localImagePaths ?? [],
|
|
120
124
|
remoteImagePaths: result.data.astro?.remoteImagePaths ?? [],
|
|
121
|
-
frontmatter: result.data.astro?.frontmatter ?? {}
|
|
125
|
+
frontmatter: result.data.astro?.frontmatter ?? {},
|
|
126
|
+
hasCodeBlocks: result.data.astro?.hasCodeBlocks ?? false
|
|
122
127
|
}
|
|
123
128
|
};
|
|
124
129
|
}
|
|
@@ -145,6 +150,7 @@ export {
|
|
|
145
150
|
createMarkdownProcessor,
|
|
146
151
|
createShikiHighlighter,
|
|
147
152
|
extractFrontmatter,
|
|
153
|
+
globalShikiStyleCollector,
|
|
148
154
|
isFrontmatterValid,
|
|
149
155
|
markdownConfigDefaults,
|
|
150
156
|
parseFrontmatter,
|
|
@@ -152,5 +158,6 @@ export {
|
|
|
152
158
|
rehypePrism2 as rehypePrism,
|
|
153
159
|
rehypeShiki2 as rehypeShiki,
|
|
154
160
|
remarkCollectImages2 as remarkCollectImages,
|
|
155
|
-
syntaxHighlightDefaults
|
|
161
|
+
syntaxHighlightDefaults,
|
|
162
|
+
transformerStyleToClass
|
|
156
163
|
};
|
package/dist/rehype-shiki.js
CHANGED
|
@@ -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) => {
|
|
5
|
+
return async (tree, vfile) => {
|
|
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
|
-
await highlightCodeBlocks(
|
|
13
|
+
const codeBlockCount = await highlightCodeBlocks(
|
|
14
14
|
tree,
|
|
15
15
|
(code, language, options) => {
|
|
16
16
|
return highlighter.codeToHast(code, language, {
|
|
@@ -22,6 +22,10 @@ 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
|
+
}
|
|
25
29
|
};
|
|
26
30
|
};
|
|
27
31
|
export {
|
|
@@ -0,0 +1,32 @@
|
|
|
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 {};
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
};
|
package/dist/shiki.js
CHANGED
|
@@ -3,6 +3,8 @@ 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
8
|
let _cssVariablesTheme;
|
|
7
9
|
const cssVariablesTheme = () => _cssVariablesTheme ?? (_cssVariablesTheme = createCssVariablesTheme({
|
|
8
10
|
variablePrefix: "--astro-code-"
|
|
@@ -52,6 +54,8 @@ async function createShikiHighlighter({
|
|
|
52
54
|
// they're technically not meta, nor parsed from Shiki's `parseMetaString` API.
|
|
53
55
|
meta: options?.meta ? { __raw: options?.meta } : void 0,
|
|
54
56
|
transformers: [
|
|
57
|
+
// Extract inline styles to CSS classes for better performance and CSP compliance
|
|
58
|
+
globalShikiStyleCollector.register(transformerStyleToClass()),
|
|
55
59
|
{
|
|
56
60
|
pre(node) {
|
|
57
61
|
if (inline) {
|
|
@@ -64,13 +68,15 @@ async function createShikiHighlighter({
|
|
|
64
68
|
} = options?.attributes ?? {};
|
|
65
69
|
Object.assign(node.properties, rest);
|
|
66
70
|
const classValue = (normalizePropAsString(node.properties.class) ?? "") + (attributesClass ? ` ${attributesClass}` : "");
|
|
67
|
-
const styleValue = (normalizePropAsString(node.properties.style) ?? "") + (attributesStyle ? `; ${attributesStyle}` : "");
|
|
68
71
|
node.properties.class = classValue.replace(/shiki/g, "astro-code");
|
|
69
72
|
node.properties.dataLanguage = lang;
|
|
70
73
|
if (options.wrap === false || options.wrap === void 0) {
|
|
71
|
-
node
|
|
74
|
+
this.addClassToHast(node, "astro-code-overflow");
|
|
72
75
|
} else if (options.wrap === true) {
|
|
73
|
-
node
|
|
76
|
+
this.addClassToHast(node, "astro-code-overflow astro-code-wrap");
|
|
77
|
+
}
|
|
78
|
+
if (attributesStyle) {
|
|
79
|
+
node.properties.style = attributesStyle;
|
|
74
80
|
}
|
|
75
81
|
},
|
|
76
82
|
line(node) {
|
|
@@ -84,7 +90,7 @@ async function createShikiHighlighter({
|
|
|
84
90
|
innerSpanNode.children.unshift({
|
|
85
91
|
type: "element",
|
|
86
92
|
tagName: "span",
|
|
87
|
-
properties: {
|
|
93
|
+
properties: { class: "astro-code-no-select" },
|
|
88
94
|
children: [{ type: "text", value: start }]
|
|
89
95
|
});
|
|
90
96
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
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;
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ declare module 'vfile' {
|
|
|
13
13
|
localImagePaths?: string[];
|
|
14
14
|
remoteImagePaths?: string[];
|
|
15
15
|
frontmatter?: Record<string, any>;
|
|
16
|
+
hasCodeBlocks?: boolean;
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
}
|
|
@@ -66,6 +67,8 @@ export interface MarkdownProcessorRenderResult {
|
|
|
66
67
|
localImagePaths: string[];
|
|
67
68
|
remoteImagePaths: string[];
|
|
68
69
|
frontmatter: Record<string, any>;
|
|
70
|
+
/** Whether the markdown contained code blocks that were syntax highlighted */
|
|
71
|
+
hasCodeBlocks?: boolean;
|
|
69
72
|
};
|
|
70
73
|
}
|
|
71
74
|
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.
|
|
3
|
+
"version": "7.0.0-beta.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "withastro",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"unist-util-visit": "^5.1.0",
|
|
44
44
|
"unist-util-visit-parents": "^6.0.2",
|
|
45
45
|
"vfile": "^6.0.3",
|
|
46
|
-
"@astrojs/internal-helpers": "0.8.0-beta.
|
|
46
|
+
"@astrojs/internal-helpers": "0.8.0-beta.1",
|
|
47
47
|
"@astrojs/prism": "4.0.0-beta.2"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|