starlight-cannoli-plugins 1.0.2 → 1.0.4

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,16 +1,34 @@
1
1
  // src/plugins/rehype-validate-links.ts
2
2
  import { existsSync } from "fs";
3
3
  import { sync as globSync } from "glob";
4
- import { minimatch } from "minimatch";
5
- import { dirname, join, relative, resolve } from "path";
4
+ import { dirname as dirname2, join, relative, resolve as resolve2 } from "path";
6
5
  import { visit } from "unist-util-visit";
6
+
7
+ // src/plugins/utils/path-utils.ts
8
+ import { dirname, resolve } from "path";
9
+ import { minimatch } from "minimatch";
7
10
  var PROJECT_DOCS_DIR = "src/content/docs";
11
+ function isExternalPath(path) {
12
+ return path.startsWith("http") || path.startsWith("data:") || path.startsWith("/");
13
+ }
8
14
  function matchesSkipPattern(path, patterns) {
9
15
  if (!patterns || patterns.length === 0) {
10
16
  return false;
11
17
  }
12
18
  return patterns.some((pattern) => minimatch(path, pattern));
13
19
  }
20
+ function normalizePath(path, fromFilePath, siteRootPath) {
21
+ if (isExternalPath(path)) {
22
+ return null;
23
+ }
24
+ const fileDir = dirname(fromFilePath);
25
+ const resolvedPath = resolve(fileDir, path);
26
+ const siteRoot = resolve(siteRootPath);
27
+ const relativePath = resolve(resolvedPath).slice(siteRoot.length).replace(/\\/g, "/").replace(/^\/+/, "");
28
+ return "/" + relativePath;
29
+ }
30
+
31
+ // src/plugins/rehype-validate-links.ts
14
32
  function getResolvedLink(href, currentFilePath) {
15
33
  let skipValidation = false;
16
34
  let processedHref = href;
@@ -38,10 +56,10 @@ function getResolvedLink(href, currentFilePath) {
38
56
  relativePath = withoutFragment.slice(1);
39
57
  projectAbsolute = join(PROJECT_DOCS_DIR, relativePath);
40
58
  } else {
41
- const currentFileDir = dirname(currentFilePath);
42
- const resolvedAbsPath = resolve(currentFileDir, withoutFragment);
59
+ const currentFileDir = dirname2(currentFilePath);
60
+ const resolvedAbsPath = resolve2(currentFileDir, withoutFragment);
43
61
  projectAbsolute = resolvedAbsPath;
44
- const docsRootAbsolute = resolve(PROJECT_DOCS_DIR);
62
+ const docsRootAbsolute = resolve2(PROJECT_DOCS_DIR);
45
63
  relativePath = relative(docsRootAbsolute, resolvedAbsPath);
46
64
  }
47
65
  const hasExtension = /\.[a-z0-9]+$/i.test(projectAbsolute);
@@ -96,51 +114,23 @@ function rehypeValidateLinks(options) {
96
114
  );
97
115
  return;
98
116
  }
99
- visit(tree, "element", (node, index, parent) => {
100
- let resourcePath;
101
- let attributeName = null;
102
- if (node.tagName === "a") {
103
- resourcePath = node.properties?.href;
104
- attributeName = "href";
105
- } else if (node.tagName === "img") {
106
- resourcePath = node.properties?.src;
107
- attributeName = "src";
108
- }
109
- if (!resourcePath || !attributeName) return;
110
- const link = getResolvedLink(resourcePath, filePath);
117
+ visit(tree, "element", (node) => {
118
+ if (node.tagName !== "a") return;
119
+ const href = node.properties?.href;
120
+ if (!href) return;
121
+ const link = getResolvedLink(href, filePath);
111
122
  if (!link) return;
112
- if (link.skipValidation) {
113
- node.properties = node.properties || {};
114
- node.properties[attributeName] = link.site_absolute_href;
115
- return;
116
- }
117
- if (node.properties?.["data-no-link-check"] !== void 0) {
118
- node.properties = node.properties || {};
119
- node.properties[attributeName] = link.site_absolute_href;
120
- return;
121
- }
122
- if (matchesSkipPattern(link.site_absolute_href, options?.skipPatterns)) {
123
- node.properties = node.properties || {};
124
- node.properties[attributeName] = link.site_absolute_href;
125
- return;
126
- }
127
- if (index !== void 0 && parent && "children" in parent && Array.isArray(parent.children)) {
128
- const nextNode = parent.children[index + 1];
129
- if (nextNode && "type" in nextNode && nextNode.type === "comment" && "value" in nextNode && typeof nextNode.value === "string" && nextNode.value.includes("no-link-check")) {
130
- node.properties = node.properties || {};
131
- node.properties[attributeName] = link.site_absolute_href;
132
- return;
133
- }
134
- }
123
+ if (link.skipValidation) return;
124
+ if (node.properties?.["data-no-link-check"] !== void 0) return;
125
+ if (matchesSkipPattern(link.site_absolute_href, options?.skipPatterns)) return;
135
126
  validateLink(link);
136
- node.properties = node.properties || {};
137
- node.properties[attributeName] = link.site_absolute_href;
138
127
  });
139
128
  };
140
129
  }
141
130
  var rehype_validate_links_default = rehypeValidateLinks;
142
131
 
143
132
  export {
133
+ normalizePath,
144
134
  rehypeValidateLinks,
145
135
  rehype_validate_links_default
146
136
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  export { starlightIndexOnlySidebar } from './plugins/starlight-index-only-sidebar.js';
2
2
  export { default as rehypeValidateLinks } from './plugins/rehype-validate-links.js';
3
+ import { AstroIntegration } from 'astro';
3
4
  import '@astrojs/starlight/types';
4
5
  import 'hast';
5
6
  import 'vfile';
7
+
8
+ /**
9
+ * Astro integration that normalizes img src and anchor href attributes to absolute paths in HTML files after build.
10
+ * This processes resources in Starlight docs that aren't handled by the rehype plugin.
11
+ */
12
+ declare function astroNormalizePaths(): AstroIntegration;
13
+
14
+ export { astroNormalizePaths };
package/dist/index.js CHANGED
@@ -2,9 +2,55 @@ import {
2
2
  starlightIndexOnlySidebar
3
3
  } from "./chunk-NTIYGHZG.js";
4
4
  import {
5
+ normalizePath,
5
6
  rehypeValidateLinks
6
- } from "./chunk-TXRBCETT.js";
7
+ } from "./chunk-DMZXDINW.js";
8
+
9
+ // src/plugins/astro-normalize-paths.ts
10
+ import { readFileSync, writeFileSync } from "fs";
11
+ import { sync as globSync } from "glob";
12
+ function astroNormalizePaths() {
13
+ return {
14
+ name: "astro-normalize-paths",
15
+ hooks: {
16
+ "astro:build:done": async ({ dir }) => {
17
+ const htmlFiles = globSync(`${dir.pathname}/**/*.html`);
18
+ for (const htmlFile of htmlFiles) {
19
+ let content = readFileSync(htmlFile, "utf-8");
20
+ const originalContent = content;
21
+ const imgRegex = /<img([^>]*?)src=["']([^"']+)["']/g;
22
+ let match;
23
+ while ((match = imgRegex.exec(content)) !== null) {
24
+ const attrs = match[1];
25
+ const src = match[2];
26
+ const normalized = normalizePath(src, htmlFile, dir.pathname);
27
+ if (normalized && src !== normalized) {
28
+ const oldTag = `<img${attrs}src="${src}"`;
29
+ const newTag = `<img${attrs}src="${normalized}"`;
30
+ content = content.replace(oldTag, newTag);
31
+ }
32
+ }
33
+ const anchorRegex = /<a([^>]*?)href=["']([^"']+)["']/g;
34
+ while ((match = anchorRegex.exec(content)) !== null) {
35
+ const attrs = match[1];
36
+ const href = match[2];
37
+ const normalized = normalizePath(href, htmlFile, dir.pathname);
38
+ if (normalized && href !== normalized) {
39
+ const oldTag = `<a${attrs}href="${href}"`;
40
+ const newTag = `<a${attrs}href="${normalized}"`;
41
+ content = content.replace(oldTag, newTag);
42
+ }
43
+ }
44
+ if (content !== originalContent) {
45
+ writeFileSync(htmlFile, content, "utf-8");
46
+ }
47
+ }
48
+ }
49
+ }
50
+ };
51
+ }
7
52
  export {
53
+ astroNormalizePaths,
8
54
  rehypeValidateLinks,
9
55
  starlightIndexOnlySidebar
10
56
  };
@@ -5,7 +5,8 @@ type TRehypeValidateLinksOptions = {
5
5
  skipPatterns?: string[];
6
6
  };
7
7
  /**
8
- * Rehype plugin to validate all internal links and convert them to absolute paths
8
+ * Rehype plugin to validate all internal links (checks validity only, no path normalization).
9
+ * This plugin verifies that link targets exist; path normalization is handled by separate integrations.
9
10
  */
10
11
  declare function rehypeValidateLinks(options?: TRehypeValidateLinksOptions): (tree: Root, file: VFile) => void;
11
12
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  rehypeValidateLinks,
3
3
  rehype_validate_links_default
4
- } from "../chunk-TXRBCETT.js";
4
+ } from "../chunk-DMZXDINW.js";
5
5
  export {
6
6
  rehype_validate_links_default as default,
7
7
  rehypeValidateLinks
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "starlight-cannoli-plugins",
3
3
  "type": "module",
4
- "version": "1.0.2",
4
+ "version": "1.0.4",
5
5
  "description": "Starlight plugins for automatic sidebar generation and link validation",
6
6
  "license": "ISC",
7
7
  "main": "./dist/index.js",
@@ -19,6 +19,10 @@
19
19
  "import": "./dist/plugins/rehype-validate-links.js",
20
20
  "types": "./dist/plugins/rehype-validate-links.d.ts"
21
21
  },
22
+ "./astro-normalize-paths": {
23
+ "import": "./dist/plugins/astro-normalize-paths.js",
24
+ "types": "./dist/plugins/astro-normalize-paths.d.ts"
25
+ },
22
26
  "./styles": "./src/styles/",
23
27
  "./styles/*": "./src/styles/*"
24
28
  },