starlight-cannoli-plugins 1.2.18 → 2.0.0
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/README.md +25 -39
- package/dist/chunk-3OL3VUEB.js +143 -0
- package/dist/{chunk-KQM5EAEK.js → chunk-47X5MKFJ.js} +52 -27
- package/dist/chunk-AZPHBHBE.js +71 -0
- package/dist/{chunk-HRUZQZ5L.js → chunk-FQLJMU2J.js} +15 -13
- package/dist/chunk-S4YMC25A.js +149 -0
- package/dist/cli/cannoli-latex-cleanup.js +1 -1
- package/dist/index.d.ts +17 -11
- package/dist/index.js +64 -67
- package/dist/plugins/astro-normalize-paths.d.ts +12 -0
- package/dist/plugins/astro-normalize-paths.js +9 -0
- package/dist/plugins/rehype-validate-links.js +1 -1
- package/dist/plugins/remark-latex-compile.d.ts +41 -4
- package/dist/plugins/remark-latex-compile.js +4 -6
- package/dist/plugins/starlight-sync-docs-to-public.d.ts +6 -9
- package/dist/plugins/starlight-sync-docs-to-public.js +5 -3
- package/package.json +1 -5
- package/dist/chunk-DEXMXUQL.js +0 -229
- package/dist/chunk-SBGY6FD3.js +0 -191
- package/dist/index-Ce1VCMrW.d.ts +0 -45
- package/dist/plugins/starlight-latex-compile.d.ts +0 -4
- package/dist/plugins/starlight-latex-compile.js +0 -10
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
export { starlightIndexOnlySidebar } from './plugins/starlight-index-only-sidebar.js';
|
|
2
|
-
export {
|
|
2
|
+
export { SyncDocsToPublicOptions, syncDocsToPublic } from './plugins/starlight-sync-docs-to-public.js';
|
|
3
3
|
export { default as rehypeValidateLinks } from './plugins/rehype-validate-links.js';
|
|
4
|
+
export { astroNormalizePaths } from './plugins/astro-normalize-paths.js';
|
|
5
|
+
import { RemarkLatexCompileOptions } from './plugins/remark-latex-compile.js';
|
|
6
|
+
export { rehypeLatexCompile, default as remarkLatexCompile } from './plugins/remark-latex-compile.js';
|
|
4
7
|
import { AstroIntegration } from 'astro';
|
|
5
|
-
export { r as rehypeLatexCompile, a as remarkLatexCompile, s as starlightLatexCompile } from './index-Ce1VCMrW.js';
|
|
6
8
|
import '@astrojs/starlight/types';
|
|
7
9
|
import 'hast';
|
|
8
10
|
import 'vfile';
|
|
11
|
+
import 'mdast';
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
interface LatexCompileOptions extends RemarkLatexCompileOptions {
|
|
14
|
+
/**
|
|
15
|
+
* When `true`, SVG files in `svgOutputDir` that are no longer referenced by
|
|
16
|
+
* any `tex compile` block are deleted automatically. In dev mode, stale SVGs
|
|
17
|
+
* are removed immediately when a block is edited. On build, any remaining
|
|
18
|
+
* orphans are swept at the end.
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
removeOrphanedSvgs?: boolean;
|
|
22
|
+
}
|
|
23
|
+
declare function astroLatexCompile(options: LatexCompileOptions): AstroIntegration;
|
|
18
24
|
|
|
19
|
-
export {
|
|
25
|
+
export { type LatexCompileOptions, astroLatexCompile };
|
package/dist/index.js
CHANGED
|
@@ -3,91 +3,88 @@ import {
|
|
|
3
3
|
} from "./chunk-HDL4DZNT.js";
|
|
4
4
|
import {
|
|
5
5
|
rehypeValidateLinks
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FQLJMU2J.js";
|
|
7
7
|
import {
|
|
8
8
|
rehypeLatexCompile,
|
|
9
|
-
remarkLatexCompile
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
remarkLatexCompile
|
|
10
|
+
} from "./chunk-S4YMC25A.js";
|
|
11
|
+
import {
|
|
12
|
+
astroNormalizePaths
|
|
13
|
+
} from "./chunk-AZPHBHBE.js";
|
|
12
14
|
import {
|
|
13
|
-
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
syncDocsToPublic
|
|
16
|
+
} from "./chunk-3OL3VUEB.js";
|
|
15
17
|
import "./chunk-3ATSZG6H.js";
|
|
16
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-47X5MKFJ.js";
|
|
17
19
|
import "./chunk-QGM4M3NI.js";
|
|
18
20
|
|
|
19
|
-
// src/plugins/astro-
|
|
20
|
-
import
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
|
|
21
|
+
// src/plugins/remark-latex-compile/astro-integration.ts
|
|
22
|
+
import fs from "fs";
|
|
23
|
+
import { readdir, rm } from "fs/promises";
|
|
24
|
+
import { join, resolve } from "path";
|
|
25
|
+
var DATA_STORE_FILE = "data-store.json";
|
|
26
|
+
function getDataStoreFile(config) {
|
|
27
|
+
return new URL(DATA_STORE_FILE, config.cacheDir);
|
|
28
|
+
}
|
|
29
|
+
async function clearContentLayerCache(config) {
|
|
30
|
+
const dataStore = getDataStoreFile(config);
|
|
31
|
+
if (fs.existsSync(dataStore)) {
|
|
32
|
+
await fs.promises.rm(dataStore, { force: true });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function astroLatexCompile(options) {
|
|
36
|
+
const referencedHashes = /* @__PURE__ */ new Set();
|
|
37
|
+
const fileHashMap = /* @__PURE__ */ new Map();
|
|
24
38
|
return {
|
|
25
|
-
name: "astro-
|
|
39
|
+
name: "astro-latex-compile",
|
|
26
40
|
hooks: {
|
|
27
|
-
"astro:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
console.log(` HTML file: ${htmlFile}`);
|
|
42
|
-
console.log(` Normalized: ${normalized}`);
|
|
43
|
-
const oldTag = `<img${attrs}src="${src}"`;
|
|
44
|
-
const newTag = `<img${attrs}src="${normalized}"`;
|
|
45
|
-
content = content.replace(oldTag, newTag);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const anchorRegex = /<a([^>]*?)href=["']([^"']+)["']/g;
|
|
49
|
-
while ((match = anchorRegex.exec(content)) !== null) {
|
|
50
|
-
const attrs = match[1];
|
|
51
|
-
const href = match[2];
|
|
52
|
-
const normalized = normalizeAssetPath(href, htmlFile, dir.pathname);
|
|
53
|
-
if (normalized && href !== normalized) {
|
|
54
|
-
const oldTag = `<a${attrs}href="${href}"`;
|
|
55
|
-
const newTag = `<a${attrs}href="${normalized}"`;
|
|
56
|
-
content = content.replace(oldTag, newTag);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (content !== originalContent) {
|
|
60
|
-
writeFileSync(htmlFile, content, "utf-8");
|
|
41
|
+
"astro:config:setup": async ({ command, config, updateConfig }) => {
|
|
42
|
+
if (command !== "build" && command !== "dev") return;
|
|
43
|
+
if (command === "build") {
|
|
44
|
+
await clearContentLayerCache(config);
|
|
45
|
+
}
|
|
46
|
+
const existingPlugins = Array.isArray(config.markdown?.remarkPlugins) ? config.markdown.remarkPlugins.filter(Boolean) : [];
|
|
47
|
+
const remarkOptions = {
|
|
48
|
+
...options,
|
|
49
|
+
_fileHashMap: options.removeOrphanedSvgs ? fileHashMap : void 0,
|
|
50
|
+
_referencedHashes: command === "build" && options.removeOrphanedSvgs ? referencedHashes : void 0
|
|
51
|
+
};
|
|
52
|
+
updateConfig({
|
|
53
|
+
markdown: {
|
|
54
|
+
remarkPlugins: [...existingPlugins, [remarkLatexCompile, remarkOptions]]
|
|
61
55
|
}
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
"astro:build:done": async () => {
|
|
59
|
+
if (!options.removeOrphanedSvgs) return;
|
|
60
|
+
const svgDir = resolve(options.svgOutputDir);
|
|
61
|
+
let entries;
|
|
62
|
+
try {
|
|
63
|
+
entries = await readdir(svgDir);
|
|
64
|
+
} catch {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const orphans = entries.filter(
|
|
68
|
+
(f) => f.endsWith(".svg") && !referencedHashes.has(f.slice(0, -4))
|
|
69
|
+
);
|
|
70
|
+
await Promise.all(
|
|
71
|
+
orphans.map((f) => rm(join(svgDir, f), { force: true }))
|
|
72
|
+
);
|
|
73
|
+
if (orphans.length > 0) {
|
|
74
|
+
console.log(
|
|
75
|
+
`[astro-latex-compile] Removed ${orphans.length} orphaned SVG${orphans.length === 1 ? "" : "s"}.`
|
|
76
|
+
);
|
|
62
77
|
}
|
|
63
78
|
}
|
|
64
79
|
}
|
|
65
80
|
};
|
|
66
81
|
}
|
|
67
|
-
function normalizeAssetPath(path, htmlFile, siteRootPath) {
|
|
68
|
-
if (path.startsWith("http") || path.startsWith("data:") || path.startsWith("/")) {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
const htmlDir = dirname(htmlFile);
|
|
72
|
-
const resolvedPath = resolve(htmlDir, path);
|
|
73
|
-
const siteRoot = resolve(siteRootPath);
|
|
74
|
-
let absolutePath = resolvedPath.slice(siteRoot.length).replace(/\\/g, "/");
|
|
75
|
-
if (!existsSync(resolvedPath)) {
|
|
76
|
-
const parentDir = dirname(htmlDir);
|
|
77
|
-
const alternativePath = resolve(parentDir, path);
|
|
78
|
-
if (existsSync(alternativePath)) {
|
|
79
|
-
absolutePath = alternativePath.slice(siteRoot.length).replace(/\\/g, "/");
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
const finalPath = "/" + absolutePath.replace(/^\/+/, "");
|
|
83
|
-
return finalPath;
|
|
84
|
-
}
|
|
85
82
|
export {
|
|
83
|
+
astroLatexCompile,
|
|
86
84
|
astroNormalizePaths,
|
|
87
85
|
rehypeLatexCompile,
|
|
88
86
|
rehypeValidateLinks,
|
|
89
87
|
remarkLatexCompile,
|
|
90
88
|
starlightIndexOnlySidebar,
|
|
91
|
-
|
|
92
|
-
starlightSyncDocsToPublic
|
|
89
|
+
syncDocsToPublic
|
|
93
90
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AstroIntegration } from 'astro';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Astro integration that normalizes img src and anchor href attributes to absolute paths in HTML files after build.
|
|
5
|
+
* This processes resources in Starlight docs that aren't handled by the rehype plugin.
|
|
6
|
+
*
|
|
7
|
+
* Handles the case where Astro converts markdown files into index.html files in subdirectories,
|
|
8
|
+
* which changes the relative path structure.
|
|
9
|
+
*/
|
|
10
|
+
declare function astroNormalizePaths(): AstroIntegration;
|
|
11
|
+
|
|
12
|
+
export { astroNormalizePaths, astroNormalizePaths as default };
|
|
@@ -1,4 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
import '
|
|
3
|
-
import 'hast';
|
|
4
|
-
|
|
1
|
+
import { Root as Root$1 } from 'mdast';
|
|
2
|
+
import { VFile } from 'vfile';
|
|
3
|
+
import { Root } from 'hast';
|
|
4
|
+
|
|
5
|
+
interface CompilationResult {
|
|
6
|
+
hash: string;
|
|
7
|
+
svgPath: string;
|
|
8
|
+
wasCompiled: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Compile LaTeX code to SVG.
|
|
12
|
+
*
|
|
13
|
+
* @param latexCode - The LaTeX code to compile (e.g., TikZ, pgfplots, etc.)
|
|
14
|
+
* @param svgOutputDir - The directory where SVG files should be written
|
|
15
|
+
* @returns Result object with hash, svgPath, and whether compilation occurred
|
|
16
|
+
* @throws Error if compilation fails
|
|
17
|
+
*/
|
|
18
|
+
declare function compileLatexToSvg(latexCode: string, svgOutputDir: string): Promise<CompilationResult>;
|
|
19
|
+
|
|
20
|
+
declare function rehypeLatexCompile(): (tree: Root, _file: VFile) => void;
|
|
21
|
+
|
|
22
|
+
interface RemarkLatexCompileOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Directory where SVG files should be written.
|
|
25
|
+
* Must be inside `public/` so Astro serves them as static assets.
|
|
26
|
+
*/
|
|
27
|
+
svgOutputDir: string;
|
|
28
|
+
/**
|
|
29
|
+
* @internal Populated by the Astro integration to track which hashes were
|
|
30
|
+
* referenced during a build, used for full orphan cleanup at build:done.
|
|
31
|
+
*/
|
|
32
|
+
_referencedHashes?: Set<string>;
|
|
33
|
+
/**
|
|
34
|
+
* @internal Maps each file path to the set of hashes it produced on the
|
|
35
|
+
* previous remark run. Used to delete stale SVGs when a block changes.
|
|
36
|
+
*/
|
|
37
|
+
_fileHashMap?: Map<string, Set<string>>;
|
|
38
|
+
}
|
|
39
|
+
declare function remarkLatexCompile(options: RemarkLatexCompileOptions): (tree: Root$1, file: VFile) => Promise<void>;
|
|
40
|
+
|
|
41
|
+
export { type RemarkLatexCompileOptions, compileLatexToSvg, remarkLatexCompile as default, rehypeLatexCompile };
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
rehypeLatexCompile,
|
|
3
|
-
remarkLatexCompile
|
|
4
|
-
|
|
5
|
-
} from "../chunk-DEXMXUQL.js";
|
|
3
|
+
remarkLatexCompile
|
|
4
|
+
} from "../chunk-S4YMC25A.js";
|
|
6
5
|
import {
|
|
7
6
|
compileLatexToSvg
|
|
8
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-47X5MKFJ.js";
|
|
9
8
|
import "../chunk-QGM4M3NI.js";
|
|
10
9
|
export {
|
|
11
10
|
compileLatexToSvg,
|
|
12
11
|
remarkLatexCompile as default,
|
|
13
|
-
rehypeLatexCompile
|
|
14
|
-
starlightLatexCompile
|
|
12
|
+
rehypeLatexCompile
|
|
15
13
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AstroIntegration } from 'astro';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Astro
|
|
4
|
+
* Astro integration that syncs a source docs directory to the public directory.
|
|
5
5
|
*
|
|
6
6
|
* Runs once at build start, and in dev mode watches the source directory for
|
|
7
7
|
* changes and incrementally syncs files — no full directory deletion/recreation.
|
|
@@ -20,11 +20,8 @@ interface SyncDocsToPublicOptions {
|
|
|
20
20
|
*/
|
|
21
21
|
ignorePatterns?: string[];
|
|
22
22
|
}
|
|
23
|
-
declare function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"config:setup": (hook: HookParameters<"config:setup">) => void;
|
|
27
|
-
};
|
|
28
|
-
};
|
|
23
|
+
declare function syncDocsToPublic(options: SyncDocsToPublicOptions): AstroIntegration;
|
|
24
|
+
/** @deprecated Use {@link syncDocsToPublic} instead. */
|
|
25
|
+
declare const starlightSyncDocsToPublic: typeof syncDocsToPublic;
|
|
29
26
|
|
|
30
|
-
export { type SyncDocsToPublicOptions, starlightSyncDocsToPublic };
|
|
27
|
+
export { type SyncDocsToPublicOptions, starlightSyncDocsToPublic, syncDocsToPublic };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
starlightSyncDocsToPublic
|
|
3
|
-
|
|
2
|
+
starlightSyncDocsToPublic,
|
|
3
|
+
syncDocsToPublic
|
|
4
|
+
} from "../chunk-3OL3VUEB.js";
|
|
4
5
|
import "../chunk-3ATSZG6H.js";
|
|
5
6
|
import "../chunk-QGM4M3NI.js";
|
|
6
7
|
export {
|
|
7
|
-
starlightSyncDocsToPublic
|
|
8
|
+
starlightSyncDocsToPublic,
|
|
9
|
+
syncDocsToPublic
|
|
8
10
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starlight-cannoli-plugins",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.0",
|
|
5
5
|
"description": "Starlight plugins for automatic sidebar generation and link validation",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -30,10 +30,6 @@
|
|
|
30
30
|
"import": "./dist/plugins/remark-latex-compile.js",
|
|
31
31
|
"types": "./dist/plugins/remark-latex-compile.d.ts"
|
|
32
32
|
},
|
|
33
|
-
"./starlight-latex-compile": {
|
|
34
|
-
"import": "./dist/plugins/starlight-latex-compile.js",
|
|
35
|
-
"types": "./dist/plugins/starlight-latex-compile.d.ts"
|
|
36
|
-
},
|
|
37
33
|
"./starlight-sync-docs-to-public": {
|
|
38
34
|
"import": "./dist/plugins/starlight-sync-docs-to-public.js",
|
|
39
35
|
"types": "./dist/plugins/starlight-sync-docs-to-public.d.ts"
|
package/dist/chunk-DEXMXUQL.js
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LATEX_BLOCK_REGEX,
|
|
3
|
-
compileLatexToSvg,
|
|
4
|
-
hashLatexCode
|
|
5
|
-
} from "./chunk-KQM5EAEK.js";
|
|
6
|
-
|
|
7
|
-
// src/plugins/remark-latex-compile/index.ts
|
|
8
|
-
import { resolve } from "path";
|
|
9
|
-
|
|
10
|
-
// src/plugins/remark-latex-compile/rehype-converter.ts
|
|
11
|
-
import { visit } from "unist-util-visit";
|
|
12
|
-
function rehypeLatexCompile() {
|
|
13
|
-
return (tree, _file) => {
|
|
14
|
-
visit(tree, "element", (node, index, parent) => {
|
|
15
|
-
if (node.tagName !== "pre") return;
|
|
16
|
-
const codeChild = node.children?.[0];
|
|
17
|
-
if (!codeChild || codeChild.tagName !== "code") return;
|
|
18
|
-
const classes = Array.isArray(codeChild.properties?.className) ? codeChild.properties.className : [];
|
|
19
|
-
if (!classes.includes("language-tex") && !classes.includes("language-latex")) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const codeContent = codeChild.children?.map((child) => typeof child === "string" ? child : child.value || "").join("").trim();
|
|
23
|
-
if (!codeContent) return;
|
|
24
|
-
const dataAttribute = codeChild.properties?.["data-meta"];
|
|
25
|
-
const isCompileBlock = dataAttribute && dataAttribute.includes("compile") || codeContent.includes("compile");
|
|
26
|
-
if (!isCompileBlock) return;
|
|
27
|
-
try {
|
|
28
|
-
const hash = hashLatexCode(codeContent);
|
|
29
|
-
const svgPath = `/static/tex-svgs/${hash}.svg`;
|
|
30
|
-
const imgElement = {
|
|
31
|
-
type: "element",
|
|
32
|
-
tagName: "img",
|
|
33
|
-
properties: {
|
|
34
|
-
src: svgPath,
|
|
35
|
-
alt: "LaTeX diagram",
|
|
36
|
-
className: ["tex-compiled"]
|
|
37
|
-
},
|
|
38
|
-
children: []
|
|
39
|
-
};
|
|
40
|
-
const paragraphElement = {
|
|
41
|
-
type: "element",
|
|
42
|
-
tagName: "p",
|
|
43
|
-
properties: {},
|
|
44
|
-
children: [imgElement]
|
|
45
|
-
};
|
|
46
|
-
if (parent && typeof index === "number") {
|
|
47
|
-
parent.children[index] = paragraphElement;
|
|
48
|
-
}
|
|
49
|
-
} catch (err) {
|
|
50
|
-
console.error(`[rehype-latex-compile] Error processing code block:`, err);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// src/plugins/remark-latex-compile/index.ts
|
|
57
|
-
function extractClassesFromMeta(meta) {
|
|
58
|
-
const classMatch = meta.match(/class="([^"]+)"/);
|
|
59
|
-
if (classMatch && classMatch[1]) {
|
|
60
|
-
return classMatch[1].split(/\s+/).filter(Boolean);
|
|
61
|
-
}
|
|
62
|
-
return [];
|
|
63
|
-
}
|
|
64
|
-
function traverseTree(node, svgOutputDir, filePath, depth = 0) {
|
|
65
|
-
if (!node) return;
|
|
66
|
-
const children = node.children;
|
|
67
|
-
if (Array.isArray(children)) {
|
|
68
|
-
for (let i = 0; i < children.length; i++) {
|
|
69
|
-
const child = children[i];
|
|
70
|
-
if (child.type === "code" && (child.lang === "tex" || child.lang === "latex") && String(child.meta || "").includes("compile")) {
|
|
71
|
-
const position = child.position;
|
|
72
|
-
const lineNumber = position?.start?.line || "?";
|
|
73
|
-
try {
|
|
74
|
-
const result = compileLatexToSvg(String(child.value), svgOutputDir);
|
|
75
|
-
if (result.wasCompiled) {
|
|
76
|
-
console.log(
|
|
77
|
-
`[remark-latex-compile] ${filePath}:${lineNumber}: compiled ${result.hash}.svg`
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
const customClasses = extractClassesFromMeta(
|
|
81
|
-
String(child.meta || "")
|
|
82
|
-
);
|
|
83
|
-
const allClasses = ["tex-compiled", ...customClasses];
|
|
84
|
-
children[i] = {
|
|
85
|
-
type: "paragraph",
|
|
86
|
-
children: [
|
|
87
|
-
{
|
|
88
|
-
type: "image",
|
|
89
|
-
url: `/static/tex-svgs/${result.hash}.svg`,
|
|
90
|
-
alt: "LaTeX diagram",
|
|
91
|
-
data: {
|
|
92
|
-
hProperties: {
|
|
93
|
-
className: allClasses
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
]
|
|
98
|
-
};
|
|
99
|
-
} catch (err) {
|
|
100
|
-
if (process.env.NODE_ENV !== "production") {
|
|
101
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
102
|
-
const match = errorMsg.match(/\n\n([\s\S]+)/);
|
|
103
|
-
const details = match ? match[1] : errorMsg;
|
|
104
|
-
console.error(`${filePath}:${lineNumber}
|
|
105
|
-
${details}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
} else {
|
|
109
|
-
traverseTree(child, svgOutputDir, filePath, depth + 1);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
function remarkLatexCompile(options) {
|
|
115
|
-
const svgOutputDir = resolve(options.svgOutputDir);
|
|
116
|
-
return (tree, file) => {
|
|
117
|
-
const fileObj = file;
|
|
118
|
-
const filePath = String(fileObj?.path || fileObj?.filename || "unknown");
|
|
119
|
-
traverseTree(tree, svgOutputDir, filePath, 0);
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// src/plugins/remark-latex-compile/astro-integration.ts
|
|
124
|
-
import { readdir, readFile, cp } from "fs/promises";
|
|
125
|
-
import { resolve as resolve2, join, extname } from "path";
|
|
126
|
-
function createAstroLatexIntegration(options) {
|
|
127
|
-
const svgOutputDir = resolve2(options.svgOutputDir);
|
|
128
|
-
const contentDir = options?.contentDir ? resolve2(options.contentDir) : resolve2("src/content/docs");
|
|
129
|
-
return {
|
|
130
|
-
name: "astro-latex-compile",
|
|
131
|
-
hooks: {
|
|
132
|
-
"astro:build:start": async () => {
|
|
133
|
-
console.log(
|
|
134
|
-
"[astro-latex-compile] Build start, scanning for tex/latex compile blocks"
|
|
135
|
-
);
|
|
136
|
-
await scanAndCompileLatex(contentDir, svgOutputDir);
|
|
137
|
-
},
|
|
138
|
-
"astro:build:done": async ({ dir }) => {
|
|
139
|
-
const srcSvgDir = resolve2(svgOutputDir);
|
|
140
|
-
const outSvgDir = resolve2(dir.pathname, "static/tex-svgs");
|
|
141
|
-
try {
|
|
142
|
-
await cp(srcSvgDir, outSvgDir, { recursive: true, force: true });
|
|
143
|
-
console.log("[astro-latex-compile] Copied SVGs to build output");
|
|
144
|
-
} catch (err) {
|
|
145
|
-
console.error("[astro-latex-compile] Error copying SVGs:", err);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
async function scanAndCompileLatex(dir, svgOutputDir) {
|
|
152
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
153
|
-
for (const entry of entries) {
|
|
154
|
-
const fullPath = join(dir, entry.name);
|
|
155
|
-
if (entry.isDirectory()) {
|
|
156
|
-
await scanAndCompileLatex(fullPath, svgOutputDir);
|
|
157
|
-
} else if (entry.isFile()) {
|
|
158
|
-
const ext = extname(entry.name);
|
|
159
|
-
if (ext === ".md" || ext === ".mdx") {
|
|
160
|
-
console.log(`[astro-latex-compile] Found markdown file: ${fullPath}`);
|
|
161
|
-
await processMarkdownFile(fullPath, svgOutputDir);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
function getLineNumber(content, position) {
|
|
167
|
-
return content.substring(0, position).split("\n").length;
|
|
168
|
-
}
|
|
169
|
-
async function processMarkdownFile(filePath, svgOutputDir) {
|
|
170
|
-
const content = await readFile(filePath, "utf-8");
|
|
171
|
-
const latexBlockRegex = new RegExp(
|
|
172
|
-
LATEX_BLOCK_REGEX.source,
|
|
173
|
-
LATEX_BLOCK_REGEX.flags
|
|
174
|
-
);
|
|
175
|
-
const matches = content.matchAll(latexBlockRegex);
|
|
176
|
-
for (const match of matches) {
|
|
177
|
-
const latexCode = match[1];
|
|
178
|
-
const lineNumber = getLineNumber(content, match.index || 0);
|
|
179
|
-
try {
|
|
180
|
-
const result = compileLatexToSvg(latexCode, svgOutputDir);
|
|
181
|
-
const status = result.wasCompiled ? "compiled" : "used cached";
|
|
182
|
-
console.log(
|
|
183
|
-
`[astro-latex-compile] ${filePath}:${lineNumber}: ${status} ${result.hash}.svg`
|
|
184
|
-
);
|
|
185
|
-
} catch (err) {
|
|
186
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
187
|
-
error.message = `${filePath}:${lineNumber}
|
|
188
|
-
${error.message}`;
|
|
189
|
-
throw error;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// src/plugins/remark-latex-compile/starlight-plugin.ts
|
|
195
|
-
function starlightLatexCompile(options) {
|
|
196
|
-
return {
|
|
197
|
-
name: "starlight-latex-compile",
|
|
198
|
-
hooks: {
|
|
199
|
-
"config:setup": (hook) => {
|
|
200
|
-
hook.addIntegration({
|
|
201
|
-
name: "latex-compile-remark-integration",
|
|
202
|
-
hooks: {
|
|
203
|
-
"astro:config:setup": ({ updateConfig, config }) => {
|
|
204
|
-
const existingPlugins = (Array.isArray(config.markdown?.remarkPlugins) ? config.markdown.remarkPlugins : []).filter((p) => p !== void 0 && p !== null);
|
|
205
|
-
updateConfig({
|
|
206
|
-
markdown: {
|
|
207
|
-
remarkPlugins: [...existingPlugins, [remarkLatexCompile, options]]
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
hook.addIntegration(
|
|
214
|
-
createAstroLatexIntegration({
|
|
215
|
-
svgOutputDir: options.svgOutputDir
|
|
216
|
-
})
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
var starlight_plugin_default = starlightLatexCompile;
|
|
223
|
-
|
|
224
|
-
export {
|
|
225
|
-
starlightLatexCompile,
|
|
226
|
-
starlight_plugin_default,
|
|
227
|
-
rehypeLatexCompile,
|
|
228
|
-
remarkLatexCompile
|
|
229
|
-
};
|