@dukebot/astro-html-validator 1.1.1 → 1.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dukebot/astro-html-validator",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Validate Astro-generated HTML output for SEO metadata, JSON-LD, and internal links.",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -19,6 +19,8 @@
19
19
  ],
20
20
  "scripts": {
21
21
  "check": "node ./bin/cli.mjs --help",
22
+ "test": "node --test",
23
+ "test:links": "node --test tests/links-utils.test.mjs",
22
24
  "validate:dist": "node ./bin/cli.mjs"
23
25
  },
24
26
  "keywords": [
@@ -35,5 +37,8 @@
35
37
  },
36
38
  "publishConfig": {
37
39
  "access": "public"
40
+ },
41
+ "dependencies": {
42
+ "parse5": "^8.0.0"
38
43
  }
39
44
  }
@@ -1,4 +1,5 @@
1
1
  import path from 'node:path';
2
+ import { parse } from 'parse5';
2
3
  import { pathExists } from './common.mjs';
3
4
 
4
5
  /**
@@ -29,17 +30,46 @@ function toLocalPathFromAbsolute(rawUrl, absolutePrefixes) {
29
30
  return null;
30
31
  }
31
32
 
33
+ /**
34
+ * Collects href/src attribute values from parsed HTML element nodes.
35
+ */
36
+ function collectHtmlLinkAttributes(html = '') {
37
+ if (!html) return [];
38
+
39
+ const urls = [];
40
+ const document = parse(html, { sourceCodeLocationInfo: false });
41
+ const queue = [document];
42
+
43
+ while (queue.length > 0) {
44
+ const node = queue.shift();
45
+ if (!node) continue;
46
+
47
+ if (Array.isArray(node.attrs)) {
48
+ for (const attr of node.attrs) {
49
+ if (!attr?.name || !attr?.value) continue;
50
+ if (attr.name !== 'href' && attr.name !== 'src') continue;
51
+ urls.push(attr.value);
52
+ }
53
+ }
54
+
55
+ if (node.content) queue.push(node.content);
56
+ if (Array.isArray(node.childNodes) && node.childNodes.length > 0) {
57
+ queue.push(...node.childNodes);
58
+ }
59
+ }
60
+
61
+ return urls;
62
+ }
63
+
32
64
  /**
33
65
  * Extracts local (root-relative) URLs from href/src attributes.
34
66
  */
35
67
  export function extractInternalUrls(html, { absoluteUrlPrefixes = [] } = {}) {
36
68
  const urls = new Set();
37
- const regex = /(?:href|src)=["']([^"']+)["']/gi;
38
69
  const absolutePrefixes = normalizeAbsolutePrefixes(absoluteUrlPrefixes);
39
70
 
40
- let match;
41
- while ((match = regex.exec(html)) !== null) {
42
- const raw = match[1]?.trim();
71
+ for (const value of collectHtmlLinkAttributes(html)) {
72
+ const raw = value?.trim();
43
73
  if (!raw) continue;
44
74
 
45
75
  if (
@@ -57,7 +87,7 @@ export function extractInternalUrls(html, { absoluteUrlPrefixes = [] } = {}) {
57
87
  if (!clean) continue;
58
88
 
59
89
  if (clean.startsWith('/')) {
60
- if (clean) urls.add(clean);
90
+ urls.add(clean);
61
91
  continue;
62
92
  }
63
93