starlight-links-validator 0.2.0 → 0.4.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/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { AstroIntegration } from 'astro'
2
+ import { AstroError } from 'astro/errors'
2
3
 
3
4
  import { remarkStarlightLinksValidator } from './libs/remark'
4
5
  import { logErrors, validateLinks } from './libs/validation'
@@ -18,13 +19,16 @@ export default function starlightLinksValidatorIntegration(): AstroIntegration {
18
19
  },
19
20
  })
20
21
  },
21
- 'astro:build:done': ({ pages }) => {
22
- const errors = validateLinks(pages)
22
+ 'astro:build:done': ({ dir, pages }) => {
23
+ const errors = validateLinks(pages, dir)
23
24
 
24
25
  logErrors(errors)
25
26
 
26
27
  if (errors.size > 0) {
27
- throw new Error('Links validation failed.')
28
+ throw new AstroError(
29
+ 'Links validation failed.',
30
+ `See the error report above for more informations.\n\nIf you believe this is a bug, please file an issue at https://github.com/HiDeoo/starlight-links-validator/issues/new/choose.`,
31
+ )
28
32
  }
29
33
  },
30
34
  },
package/libs/remark.ts CHANGED
@@ -85,7 +85,6 @@ export const remarkStarlightLinksValidator: Plugin<[], Root> = function () {
85
85
  case 'html': {
86
86
  const htmlTree = fromHtml(node.value, { fragment: true })
87
87
 
88
- // @ts-expect-error - https://github.com/microsoft/TypeScript/issues/51188
89
88
  visit(htmlTree, (htmlNode: Nodes) => {
90
89
  if (hasProperty(htmlNode, 'id') && typeof htmlNode.properties.id === 'string') {
91
90
  fileHeadings.push(htmlNode.properties.id)
@@ -1,8 +1,11 @@
1
+ import { statSync } from 'node:fs'
2
+ import { fileURLToPath } from 'node:url'
3
+
1
4
  import { bgGreen, black, bold, cyan, dim, red } from 'kleur/colors'
2
5
 
3
6
  import { getValidationData, type Headings } from './remark'
4
7
 
5
- export function validateLinks(pages: PageData[]): ValidationErrors {
8
+ export function validateLinks(pages: PageData[], outputDir: URL): ValidationErrors {
6
9
  process.stdout.write(`\n${bgGreen(black(` validating links `))}\n`)
7
10
 
8
11
  const { headings, links } = getValidationData()
@@ -15,7 +18,7 @@ export function validateLinks(pages: PageData[]): ValidationErrors {
15
18
  if (link.startsWith('#')) {
16
19
  validateSelfAnchor(errors, link, filePath, headings)
17
20
  } else {
18
- validateLink(errors, link, filePath, headings, allPages)
21
+ validateLink(errors, link, filePath, headings, allPages, outputDir)
19
22
  }
20
23
  }
21
24
  }
@@ -36,10 +39,10 @@ export function logErrors(errors: ValidationErrors) {
36
39
  red(
37
40
  `Found ${errorCount} invalid ${pluralize(errorCount, 'link')} in ${errors.size} ${pluralize(
38
41
  errors.size,
39
- 'file'
40
- )}.`
41
- )
42
- )}\n\n`
42
+ 'file',
43
+ )}.`,
44
+ ),
45
+ )}\n\n`,
43
46
  )
44
47
 
45
48
  for (const [file, links] of errors) {
@@ -58,7 +61,14 @@ export function logErrors(errors: ValidationErrors) {
58
61
  /**
59
62
  * Validate a link to another internal page that may or may not have a hash.
60
63
  */
61
- function validateLink(errors: ValidationErrors, link: string, filePath: string, headings: Headings, pages: Pages) {
64
+ function validateLink(
65
+ errors: ValidationErrors,
66
+ link: string,
67
+ filePath: string,
68
+ headings: Headings,
69
+ pages: Pages,
70
+ outputDir: URL,
71
+ ) {
62
72
  const sanitizedLink = link.replace(/^\//, '')
63
73
  const segments = sanitizedLink.split('#')
64
74
 
@@ -67,7 +77,13 @@ function validateLink(errors: ValidationErrors, link: string, filePath: string,
67
77
 
68
78
  if (path === undefined) {
69
79
  throw new Error('Failed to validate a link with no path.')
70
- } else if (path.length > 0 && !path.endsWith('/')) {
80
+ }
81
+
82
+ if (isValidAsset(path, outputDir)) {
83
+ return
84
+ }
85
+
86
+ if (path.length > 0 && !path.endsWith('/')) {
71
87
  path += '/'
72
88
  }
73
89
 
@@ -100,6 +116,21 @@ function validateSelfAnchor(errors: ValidationErrors, hash: string, filePath: st
100
116
  }
101
117
  }
102
118
 
119
+ /**
120
+ * Check if a link is a valid asset in the build output directory.
121
+ */
122
+ function isValidAsset(path: string, outputDir: URL) {
123
+ const filePath = fileURLToPath(new URL(path, outputDir))
124
+
125
+ try {
126
+ const stats = statSync(filePath)
127
+
128
+ return stats.isFile()
129
+ } catch {
130
+ return false
131
+ }
132
+ }
133
+
103
134
  function addError(errors: ValidationErrors, filePath: string, link: string) {
104
135
  const fileErrors = errors.get(filePath) ?? []
105
136
  fileErrors.push(link)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-links-validator",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "license": "MIT",
5
5
  "description": "Astro integration for Starlight to validate internal links.",
6
6
  "author": "HiDeoo <github@hideoo.dev> (https://hideoo.dev)",
@@ -14,25 +14,26 @@
14
14
  "hast-util-from-html": "2.0.1",
15
15
  "hast-util-has-property": "3.0.0",
16
16
  "kleur": "4.1.5",
17
- "mdast-util-to-string": "3.2.0",
18
- "unist-util-visit": "4.1.2"
17
+ "mdast-util-to-string": "4.0.0",
18
+ "unist-util-visit": "5.0.0"
19
19
  },
20
20
  "devDependencies": {
21
- "@types/hast": "3.0.0",
22
- "@types/mdast": "3.0.11",
23
- "@types/node": "18.16.18",
24
- "astro": "2.10.9",
25
- "mdast-util-mdx-jsx": "2.1.4",
21
+ "@astrojs/starlight": "0.10.1",
22
+ "@types/hast": "3.0.1",
23
+ "@types/mdast": "4.0.0",
24
+ "@types/node": "18.17.18",
25
+ "astro": "3.1.1",
26
+ "mdast-util-mdx-jsx": "3.0.0",
26
27
  "typescript": "5.1.3",
27
- "unified": "10.1.2",
28
+ "unified": "11.0.3",
28
29
  "vitest": "0.32.2"
29
30
  },
30
31
  "peerDependencies": {
31
- "@astrojs/starlight": ">=0.0.1",
32
- "astro": ">=2.5.0"
32
+ "@astrojs/starlight": ">=0.9.0",
33
+ "astro": ">=3.0.0"
33
34
  },
34
35
  "engines": {
35
- "node": ">=18"
36
+ "node": ">=18.14.1"
36
37
  },
37
38
  "packageManager": "pnpm@8.6.3",
38
39
  "publishConfig": {