starlight-cannoli-plugins 1.2.18 → 2.0.1

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.
@@ -1,6 +1,6 @@
1
- // src/plugins/remark-latex-compile/compile.ts
1
+ // src/plugins/astro-latex-compile/utils.ts
2
2
  import { createHash } from "crypto";
3
- import { spawnSync } from "child_process";
3
+ import { spawn } from "child_process";
4
4
  import {
5
5
  existsSync,
6
6
  mkdirSync,
@@ -11,7 +11,7 @@ import {
11
11
  import { join } from "path";
12
12
  import { tmpdir } from "os";
13
13
 
14
- // src/plugins/remark-latex-compile/error-parser.ts
14
+ // src/plugins/astro-latex-compile/error-parser.ts
15
15
  function parseLatexError(latexOutput) {
16
16
  const lines = latexOutput.split("\n");
17
17
  const errors = [];
@@ -161,7 +161,7 @@ ${formattedSource}
161
161
  `;
162
162
  }
163
163
 
164
- // src/plugins/remark-latex-compile/compile.ts
164
+ // src/plugins/astro-latex-compile/utils.ts
165
165
  var LATEX_BLOCK_REGEX = /```(?:tex|latex)\s+compile[^\r\n]*\r?\n([\s\S]*?)\r?\n```/g;
166
166
  function hashLatexCode(code) {
167
167
  const normalized = code.split("\n").map((line) => line.trim()).filter((line) => !line.startsWith("%")).filter(Boolean).join("\n").trim();
@@ -188,7 +188,26 @@ function buildLatexSource(latexCode) {
188
188
  "\\end{document}"
189
189
  ].join("\n");
190
190
  }
191
- function compileLatexToSvg(latexCode, svgOutputDir) {
191
+ function execProcess(command, args) {
192
+ return new Promise((resolve, reject) => {
193
+ let stdout = "";
194
+ let stderr = "";
195
+ const proc = spawn(command, args);
196
+ proc.stdout.on("data", (data) => {
197
+ stdout += data.toString();
198
+ });
199
+ proc.stderr.on("data", (data) => {
200
+ stderr += data.toString();
201
+ });
202
+ proc.on("error", (err) => {
203
+ reject(err);
204
+ });
205
+ proc.on("close", (code) => {
206
+ resolve({ status: code ?? 1, stdout, stderr });
207
+ });
208
+ });
209
+ }
210
+ async function compileLatexToSvg(latexCode, svgOutputDir) {
192
211
  const hash = hashLatexCode(latexCode);
193
212
  const svgPath = join(svgOutputDir, `${hash}.svg`);
194
213
  if (existsSync(svgPath)) {
@@ -201,41 +220,47 @@ function compileLatexToSvg(latexCode, svgOutputDir) {
201
220
  const latexSource = buildLatexSource(latexCode);
202
221
  try {
203
222
  writeFileSync(texFile, latexSource, "utf-8");
204
- const latexResult = spawnSync("pdflatex", [
205
- "-interaction=nonstopmode",
206
- "-output-directory",
207
- workDir,
208
- texFile
209
- ]);
210
- if (latexResult.error) {
211
- const code = latexResult.error.code;
223
+ let result;
224
+ try {
225
+ result = await execProcess("pdflatex", [
226
+ "-interaction=nonstopmode",
227
+ "-output-directory",
228
+ workDir,
229
+ texFile
230
+ ]);
231
+ } catch (err) {
232
+ const code = err.code;
212
233
  throw new Error(
213
- `[remark-latex-compile] pdflatex not found on PATH (${code}).`
234
+ `[remark-latex-compile] pdflatex not found on PATH (${code}).`,
235
+ { cause: err }
214
236
  );
215
237
  }
216
- if (latexResult.status !== 0) {
217
- const errorOutput = latexResult.stderr?.toString() || latexResult.stdout?.toString() || "";
238
+ if (result.status !== 0) {
239
+ const errorOutput = result.stderr || result.stdout || "";
218
240
  const userMessage = createCompilationErrorMessage(
219
241
  latexSource,
220
242
  errorOutput
221
243
  );
222
244
  throw new Error(userMessage);
223
245
  }
224
- const dvisvgmResult = spawnSync("dvisvgm", [
225
- "--pdf",
226
- "--bbox=dvi",
227
- pdfFile,
228
- "-o",
229
- svgPath
230
- ]);
231
- if (dvisvgmResult.error) {
232
- const code = dvisvgmResult.error.code;
246
+ let svgResult;
247
+ try {
248
+ svgResult = await execProcess("dvisvgm", [
249
+ "--pdf",
250
+ "--bbox=dvi",
251
+ pdfFile,
252
+ "-o",
253
+ svgPath
254
+ ]);
255
+ } catch (err) {
256
+ const code = err.code;
233
257
  throw new Error(
234
- `[remark-latex-compile] dvisvgm not found on PATH (${code}).`
258
+ `[remark-latex-compile] dvisvgm not found on PATH (${code}).`,
259
+ { cause: err }
235
260
  );
236
261
  }
237
- if (dvisvgmResult.status !== 0) {
238
- const errorOutput = dvisvgmResult.stderr?.toString() || dvisvgmResult.stdout?.toString() || "";
262
+ if (svgResult.status !== 0) {
263
+ const errorOutput = svgResult.stderr || svgResult.stdout || "";
239
264
  throw new Error(
240
265
  `[remark-latex-compile] PDF to SVG conversion failed (hash: ${hash}).
241
266
  Error: ${errorOutput}`
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  LATEX_BLOCK_REGEX,
4
4
  hashLatexCode
5
- } from "../chunk-KQM5EAEK.js";
5
+ } from "../chunk-TTQY54Q6.js";
6
6
  import "../chunk-QGM4M3NI.js";
7
7
 
8
8
  // scripts/cli/cannoli-latex-cleanup.ts
package/dist/index.d.ts CHANGED
@@ -1,19 +1,25 @@
1
1
  export { starlightIndexOnlySidebar } from './plugins/starlight-index-only-sidebar.js';
2
- export { starlightSyncDocsToPublic } from './plugins/starlight-sync-docs-to-public.js';
2
+ export { SyncDocsToPublicOptions, syncDocsToPublic } from './plugins/astro-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/astro-latex-compile.js';
6
+ export { rehypeLatexCompile, default as remarkLatexCompile } from './plugins/astro-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
- * Astro integration that normalizes img src and anchor href attributes to absolute paths in HTML files after build.
12
- * This processes resources in Starlight docs that aren't handled by the rehype plugin.
13
- *
14
- * Handles the case where Astro converts markdown files into index.html files in subdirectories,
15
- * which changes the relative path structure.
16
- */
17
- declare function astroNormalizePaths(): AstroIntegration;
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 { astroNormalizePaths };
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-HRUZQZ5L.js";
6
+ } from "./chunk-FQLJMU2J.js";
7
7
  import {
8
8
  rehypeLatexCompile,
9
- remarkLatexCompile,
10
- starlightLatexCompile
11
- } from "./chunk-DEXMXUQL.js";
9
+ remarkLatexCompile
10
+ } from "./chunk-IX5UFISR.js";
11
+ import {
12
+ astroNormalizePaths
13
+ } from "./chunk-AZPHBHBE.js";
12
14
  import {
13
- starlightSyncDocsToPublic
14
- } from "./chunk-SBGY6FD3.js";
15
+ syncDocsToPublic
16
+ } from "./chunk-JOXQGMUL.js";
15
17
  import "./chunk-3ATSZG6H.js";
16
- import "./chunk-KQM5EAEK.js";
18
+ import "./chunk-TTQY54Q6.js";
17
19
  import "./chunk-QGM4M3NI.js";
18
20
 
19
- // src/plugins/astro-normalize-paths.ts
20
- import { readFileSync, writeFileSync, existsSync } from "fs";
21
- import { sync as globSync } from "glob";
22
- import { dirname, resolve } from "path";
23
- function astroNormalizePaths() {
21
+ // src/plugins/astro-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-normalize-paths",
39
+ name: "astro-latex-compile",
26
40
  hooks: {
27
- "astro:build:done": async ({ dir }) => {
28
- const htmlFiles = globSync(`${dir.pathname}/**/*.html`);
29
- for (const htmlFile of htmlFiles) {
30
- let content = readFileSync(htmlFile, "utf-8");
31
- const originalContent = content;
32
- const imgRegex = /<img([^>]*?)src=["']([^"']+)["']/g;
33
- let match;
34
- while ((match = imgRegex.exec(content)) !== null) {
35
- const attrs = match[1];
36
- const src = match[2];
37
- const normalized = normalizeAssetPath(src, htmlFile, dir.pathname);
38
- if (normalized && src !== normalized) {
39
- console.log(`[astro-normalize-paths] Img path resolution:`);
40
- console.log(` Original: ${src}`);
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
- starlightLatexCompile,
92
- starlightSyncDocsToPublic
89
+ syncDocsToPublic
93
90
  };
@@ -0,0 +1,41 @@
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
- starlightLatexCompile
5
- } from "../chunk-DEXMXUQL.js";
3
+ remarkLatexCompile
4
+ } from "../chunk-IX5UFISR.js";
6
5
  import {
7
6
  compileLatexToSvg
8
- } from "../chunk-KQM5EAEK.js";
7
+ } from "../chunk-TTQY54Q6.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
  };
@@ -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 };
@@ -0,0 +1,9 @@
1
+ import {
2
+ astroNormalizePaths,
3
+ astro_normalize_paths_default
4
+ } from "../chunk-AZPHBHBE.js";
5
+ import "../chunk-QGM4M3NI.js";
6
+ export {
7
+ astroNormalizePaths,
8
+ astro_normalize_paths_default as default
9
+ };
@@ -1,7 +1,7 @@
1
- import { HookParameters } from '@astrojs/starlight/types';
1
+ import { AstroIntegration } from 'astro';
2
2
 
3
3
  /**
4
- * Astro/Starlight plugin that syncs a source docs directory to the public directory.
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 starlightSyncDocsToPublic(options: SyncDocsToPublicOptions): {
24
- name: string;
25
- hooks: {
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 };
@@ -0,0 +1,10 @@
1
+ import {
2
+ starlightSyncDocsToPublic,
3
+ syncDocsToPublic
4
+ } from "../chunk-JOXQGMUL.js";
5
+ import "../chunk-3ATSZG6H.js";
6
+ import "../chunk-QGM4M3NI.js";
7
+ export {
8
+ starlightSyncDocsToPublic,
9
+ syncDocsToPublic
10
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  rehypeValidateLinks,
3
3
  rehype_validate_links_default
4
- } from "../chunk-HRUZQZ5L.js";
4
+ } from "../chunk-FQLJMU2J.js";
5
5
  import "../chunk-QGM4M3NI.js";
6
6
  export {
7
7
  rehype_validate_links_default as default,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "starlight-cannoli-plugins",
3
3
  "type": "module",
4
- "version": "1.2.18",
4
+ "version": "2.0.1",
5
5
  "description": "Starlight plugins for automatic sidebar generation and link validation",
6
6
  "license": "ISC",
7
7
  "main": "./dist/index.js",
@@ -26,17 +26,13 @@
26
26
  "import": "./dist/plugins/astro-normalize-paths.js",
27
27
  "types": "./dist/plugins/astro-normalize-paths.d.ts"
28
28
  },
29
- "./remark-latex-compile": {
30
- "import": "./dist/plugins/remark-latex-compile.js",
31
- "types": "./dist/plugins/remark-latex-compile.d.ts"
29
+ "./astro-latex-compile": {
30
+ "import": "./dist/plugins/astro-latex-compile.js",
31
+ "types": "./dist/plugins/astro-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
- "./starlight-sync-docs-to-public": {
38
- "import": "./dist/plugins/starlight-sync-docs-to-public.js",
39
- "types": "./dist/plugins/starlight-sync-docs-to-public.d.ts"
33
+ "./astro-sync-docs-to-public": {
34
+ "import": "./dist/plugins/astro-sync-docs-to-public.js",
35
+ "types": "./dist/plugins/astro-sync-docs-to-public.d.ts"
40
36
  },
41
37
  "./styles": "./dist/styles/",
42
38
  "./styles/*": "./dist/styles/*"