starlight-links-validator 0.14.0 → 0.14.2
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/CHANGELOG.md +16 -0
- package/index.ts +34 -9
- package/libs/path.ts +27 -0
- package/libs/remark.ts +1 -1
- package/libs/validation.ts +19 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# starlight-links-validator
|
|
2
2
|
|
|
3
|
+
## 0.14.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#85](https://github.com/HiDeoo/starlight-links-validator/pull/85) [`57fdb1b`](https://github.com/HiDeoo/starlight-links-validator/commit/57fdb1b2f85f023e4b053480fd9ea5adb69a9e2a) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Improves error message for invalid links to custom pages.
|
|
8
|
+
|
|
9
|
+
## 0.14.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#82](https://github.com/HiDeoo/starlight-links-validator/pull/82) [`b3cbee8`](https://github.com/HiDeoo/starlight-links-validator/commit/b3cbee83fb54f5bd6dd06b01bb8397758c081752) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes regresion introduced in version [`0.14.0`](https://github.com/HiDeoo/starlight-links-validator/releases/tag/starlight-links-validator%400.14.0) of the plugin regarding validation of links to pages with [custom IDs/slugs](https://docs.astro.build/en/guides/content-collections/#defining-custom-ids).
|
|
14
|
+
|
|
15
|
+
Note that you must use at least Astro version [`5.1.1`](https://github.com/withastro/astro/releases/tag/astro%405.1.1) to benefit from this fix.
|
|
16
|
+
|
|
17
|
+
- [#80](https://github.com/HiDeoo/starlight-links-validator/pull/80) [`876cb50`](https://github.com/HiDeoo/starlight-links-validator/commit/876cb5094d10a56a1be04b7cdc27e4f89fb1b681) Thanks [@lukekarrys](https://github.com/lukekarrys)! - Fixes validation issues for pages ending in `index`, e.g. `module_index`.
|
|
18
|
+
|
|
3
19
|
## 0.14.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/index.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { StarlightPlugin } from '@astrojs/starlight/types'
|
|
2
|
+
import type { IntegrationResolvedRoute } from 'astro'
|
|
2
3
|
import { AstroError } from 'astro/errors'
|
|
3
4
|
import { z } from 'astro/zod'
|
|
4
5
|
|
|
5
6
|
import { clearContentLayerCache } from './libs/astro'
|
|
7
|
+
import { pathnameToSlug } from './libs/path'
|
|
6
8
|
import { remarkStarlightLinksValidator } from './libs/remark'
|
|
7
9
|
import { logErrors, validateLinks } from './libs/validation'
|
|
8
10
|
|
|
@@ -72,6 +74,8 @@ export default function starlightLinksValidatorPlugin(
|
|
|
72
74
|
name: 'starlight-links-validator-plugin',
|
|
73
75
|
hooks: {
|
|
74
76
|
setup({ addIntegration, astroConfig, config: starlightConfig, logger }) {
|
|
77
|
+
let routes: IntegrationResolvedRoute[] = []
|
|
78
|
+
|
|
75
79
|
addIntegration({
|
|
76
80
|
name: 'starlight-links-validator-integration',
|
|
77
81
|
hooks: {
|
|
@@ -90,13 +94,32 @@ export default function starlightLinksValidatorPlugin(
|
|
|
90
94
|
},
|
|
91
95
|
})
|
|
92
96
|
},
|
|
93
|
-
'astro:
|
|
94
|
-
|
|
97
|
+
'astro:routes:resolved': (params) => {
|
|
98
|
+
routes = params.routes
|
|
99
|
+
},
|
|
100
|
+
'astro:build:done': ({ dir, pages, assets }) => {
|
|
101
|
+
const customPages = new Set<string>()
|
|
95
102
|
|
|
96
|
-
|
|
103
|
+
for (const [pattern, urls] of assets) {
|
|
104
|
+
const route = routes.find((route) => route.pattern === pattern)
|
|
105
|
+
if (!route || route.origin !== 'project') continue
|
|
106
|
+
|
|
107
|
+
for (const url of urls) {
|
|
108
|
+
customPages.add(pathnameToSlug(url.pathname.replace(astroConfig.outDir.pathname, '')))
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const errors = validateLinks(pages, customPages, dir, astroConfig, starlightConfig, options.data)
|
|
113
|
+
|
|
114
|
+
const hasInvalidLinkToCustomPage = logErrors(logger, errors)
|
|
97
115
|
|
|
98
116
|
if (errors.size > 0) {
|
|
99
|
-
throwPluginError(
|
|
117
|
+
throwPluginError(
|
|
118
|
+
'Links validation failed.',
|
|
119
|
+
hasInvalidLinkToCustomPage
|
|
120
|
+
? 'Some invalid links point to custom pages which cannot be validated, see the `exclude` option for more informations at https://starlight-links-validator.vercel.app/configuration#exclude'
|
|
121
|
+
: undefined,
|
|
122
|
+
)
|
|
100
123
|
}
|
|
101
124
|
},
|
|
102
125
|
},
|
|
@@ -106,11 +129,13 @@ export default function starlightLinksValidatorPlugin(
|
|
|
106
129
|
}
|
|
107
130
|
}
|
|
108
131
|
|
|
109
|
-
function throwPluginError(message: string): never {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
132
|
+
function throwPluginError(message: string, additionalHint?: string): never {
|
|
133
|
+
let hint = 'See the error report above for more informations.\n\n'
|
|
134
|
+
if (additionalHint) hint += `${additionalHint}\n\n`
|
|
135
|
+
hint +=
|
|
136
|
+
'If you believe this is a bug, please file an issue at https://github.com/HiDeoo/starlight-links-validator/issues/new/choose'
|
|
137
|
+
|
|
138
|
+
throw new AstroError(message, hint)
|
|
114
139
|
}
|
|
115
140
|
|
|
116
141
|
type StarlightLinksValidatorUserOptions = z.input<typeof starlightLinksValidatorOptionsSchema>
|
package/libs/path.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const htmlExtension = '.html'
|
|
2
|
+
|
|
1
3
|
export function ensureLeadingSlash(path: string): string {
|
|
2
4
|
return path.startsWith('/') ? path : `/${path}`
|
|
3
5
|
}
|
|
@@ -9,3 +11,28 @@ export function ensureTrailingSlash(path: string): string {
|
|
|
9
11
|
export function stripLeadingSlash(path: string) {
|
|
10
12
|
return path.replace(/^\//, '')
|
|
11
13
|
}
|
|
14
|
+
|
|
15
|
+
export function stripTrailingSlash(path: string) {
|
|
16
|
+
return path.replace(/\/$/, '')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function pathnameToSlug(pathname: string): string {
|
|
20
|
+
const base = stripTrailingSlash(import.meta.env.BASE_URL)
|
|
21
|
+
|
|
22
|
+
if (pathname.startsWith(base)) {
|
|
23
|
+
pathname = pathname.replace(base, '')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const segments = pathname.split('/')
|
|
27
|
+
|
|
28
|
+
if (segments.at(-1) === 'index.html') {
|
|
29
|
+
segments.pop()
|
|
30
|
+
} else if (segments.at(-1)?.endsWith(htmlExtension)) {
|
|
31
|
+
const last = segments.pop()
|
|
32
|
+
if (last) {
|
|
33
|
+
segments.push(last.slice(0, -1 * htmlExtension.length))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return segments.filter(Boolean).join('/')
|
|
38
|
+
}
|
package/libs/remark.ts
CHANGED
|
@@ -178,7 +178,7 @@ function normalizeFilePath(base: string, srcDir: URL, filePath?: string) {
|
|
|
178
178
|
const path = nodePath
|
|
179
179
|
.relative(nodePath.join(fileURLToPath(srcDir), 'content/docs'), filePath)
|
|
180
180
|
.replace(/\.\w+$/, '')
|
|
181
|
-
.replace(/index$/, '')
|
|
181
|
+
.replace(/(^|[/\\])index$/, '')
|
|
182
182
|
.replace(/[/\\]?$/, '/')
|
|
183
183
|
.split(/[/\\]/)
|
|
184
184
|
.map((segment) => slug(segment))
|
package/libs/validation.ts
CHANGED
|
@@ -10,13 +10,14 @@ import picomatch from 'picomatch'
|
|
|
10
10
|
import type { StarlightLinksValidatorOptions } from '..'
|
|
11
11
|
|
|
12
12
|
import { getFallbackHeadings, getLocaleConfig, isInconsistentLocaleLink, type LocaleConfig } from './i18n'
|
|
13
|
-
import { ensureTrailingSlash, stripLeadingSlash } from './path'
|
|
13
|
+
import { ensureTrailingSlash, stripLeadingSlash, stripTrailingSlash } from './path'
|
|
14
14
|
import { getValidationData, type Headings } from './remark'
|
|
15
15
|
|
|
16
16
|
export const ValidationErrorType = {
|
|
17
17
|
InconsistentLocale: 'inconsistent locale',
|
|
18
18
|
InvalidHash: 'invalid hash',
|
|
19
19
|
InvalidLink: 'invalid link',
|
|
20
|
+
InvalidLinkToCustomPage: 'invalid link to custom page',
|
|
20
21
|
LocalLink: 'local link',
|
|
21
22
|
RelativeLink: 'relative link',
|
|
22
23
|
TrailingSlashMissing: 'missing trailing slash',
|
|
@@ -25,6 +26,7 @@ export const ValidationErrorType = {
|
|
|
25
26
|
|
|
26
27
|
export function validateLinks(
|
|
27
28
|
pages: PageData[],
|
|
29
|
+
customPages: Set<string>,
|
|
28
30
|
outputDir: URL,
|
|
29
31
|
astroConfig: AstroConfig,
|
|
30
32
|
starlightConfig: StarlightUserConfig,
|
|
@@ -50,6 +52,7 @@ export function validateLinks(
|
|
|
50
52
|
for (const link of fileLinks) {
|
|
51
53
|
const validationContext: ValidationContext = {
|
|
52
54
|
astroConfig,
|
|
55
|
+
customPages,
|
|
53
56
|
errors,
|
|
54
57
|
filePath,
|
|
55
58
|
headings,
|
|
@@ -92,6 +95,8 @@ export function logErrors(pluginLogger: AstroIntegrationLogger, errors: Validati
|
|
|
92
95
|
),
|
|
93
96
|
)
|
|
94
97
|
|
|
98
|
+
let hasInvalidLinkToCustomPage = false
|
|
99
|
+
|
|
95
100
|
for (const [file, validationErrors] of errors) {
|
|
96
101
|
logger.info(`${red('▶')} ${blue(file)}`)
|
|
97
102
|
|
|
@@ -101,17 +106,20 @@ export function logErrors(pluginLogger: AstroIntegrationLogger, errors: Validati
|
|
|
101
106
|
` - ${validationError.type}`,
|
|
102
107
|
)}`,
|
|
103
108
|
)
|
|
109
|
+
hasInvalidLinkToCustomPage = validationError.type === ValidationErrorType.InvalidLinkToCustomPage
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
process.stdout.write('\n')
|
|
114
|
+
|
|
115
|
+
return hasInvalidLinkToCustomPage
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
/**
|
|
111
119
|
* Validate a link to another internal page that may or may not have a hash.
|
|
112
120
|
*/
|
|
113
121
|
function validateLink(context: ValidationContext) {
|
|
114
|
-
const { astroConfig, errors, filePath, link, localeConfig, options, pages } = context
|
|
122
|
+
const { astroConfig, customPages, errors, filePath, link, localeConfig, options, pages } = context
|
|
115
123
|
|
|
116
124
|
if (isExcludedLink(link, context)) {
|
|
117
125
|
return
|
|
@@ -153,7 +161,14 @@ function validateLink(context: ValidationContext) {
|
|
|
153
161
|
const fileHeadings = getFileHeadings(sanitizedPath, context)
|
|
154
162
|
|
|
155
163
|
if (!isValidPage || !fileHeadings) {
|
|
156
|
-
addError(
|
|
164
|
+
addError(
|
|
165
|
+
errors,
|
|
166
|
+
filePath,
|
|
167
|
+
link,
|
|
168
|
+
customPages.has(stripTrailingSlash(sanitizedPath))
|
|
169
|
+
? ValidationErrorType.InvalidLinkToCustomPage
|
|
170
|
+
: ValidationErrorType.InvalidLink,
|
|
171
|
+
)
|
|
157
172
|
return
|
|
158
173
|
}
|
|
159
174
|
|
|
@@ -271,6 +286,7 @@ type Pages = Set<PageData['pathname']>
|
|
|
271
286
|
|
|
272
287
|
interface ValidationContext {
|
|
273
288
|
astroConfig: AstroConfig
|
|
289
|
+
customPages: Set<string>
|
|
274
290
|
errors: ValidationErrors
|
|
275
291
|
filePath: string
|
|
276
292
|
headings: Headings
|