starlight-links-validator 0.6.0 → 0.7.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.
- package/index.ts +1 -1
- package/libs/validation.ts +25 -12
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -64,7 +64,7 @@ export default function starlightLinksValidatorPlugin(
|
|
|
64
64
|
})
|
|
65
65
|
},
|
|
66
66
|
'astro:build:done': ({ dir, pages }) => {
|
|
67
|
-
const errors = validateLinks(pages, dir, astroConfig
|
|
67
|
+
const errors = validateLinks(pages, dir, astroConfig, starlightConfig, options.data)
|
|
68
68
|
|
|
69
69
|
logErrors(logger, errors)
|
|
70
70
|
|
package/libs/validation.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { posix } from 'node:path'
|
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
4
|
|
|
5
5
|
import type { StarlightPlugin } from '@astrojs/starlight/types'
|
|
6
|
-
import type { AstroIntegrationLogger } from 'astro'
|
|
6
|
+
import type { AstroConfig, AstroIntegrationLogger } from 'astro'
|
|
7
7
|
import { bgGreen, black, blue, dim, green, red } from 'kleur/colors'
|
|
8
8
|
|
|
9
9
|
import type { StarlightLinksValidatorOptions } from '..'
|
|
@@ -17,12 +17,13 @@ export const ValidationErrorType = {
|
|
|
17
17
|
InvalidAnchor: 'invalid anchor',
|
|
18
18
|
InvalidLink: 'invalid link',
|
|
19
19
|
RelativeLink: 'relative link',
|
|
20
|
+
TrailingSlash: 'trailing slash',
|
|
20
21
|
} as const
|
|
21
22
|
|
|
22
23
|
export function validateLinks(
|
|
23
24
|
pages: PageData[],
|
|
24
25
|
outputDir: URL,
|
|
25
|
-
|
|
26
|
+
astroConfig: AstroConfig,
|
|
26
27
|
starlightConfig: StarlightUserConfig,
|
|
27
28
|
options: StarlightLinksValidatorOptions,
|
|
28
29
|
): ValidationErrors {
|
|
@@ -33,7 +34,9 @@ export function validateLinks(
|
|
|
33
34
|
const allPages: Pages = new Set(
|
|
34
35
|
pages.map((page) =>
|
|
35
36
|
ensureTrailingSlash(
|
|
36
|
-
base === '/'
|
|
37
|
+
astroConfig.base === '/'
|
|
38
|
+
? stripLeadingSlash(page.pathname)
|
|
39
|
+
: posix.join(stripLeadingSlash(astroConfig.base), page.pathname),
|
|
37
40
|
),
|
|
38
41
|
),
|
|
39
42
|
)
|
|
@@ -43,7 +46,7 @@ export function validateLinks(
|
|
|
43
46
|
for (const [filePath, fileLinks] of links) {
|
|
44
47
|
for (const link of fileLinks) {
|
|
45
48
|
const validationContext: ValidationContext = {
|
|
46
|
-
|
|
49
|
+
astroConfig,
|
|
47
50
|
errors,
|
|
48
51
|
filePath,
|
|
49
52
|
headings,
|
|
@@ -103,12 +106,12 @@ export function logErrors(pluginLogger: AstroIntegrationLogger, errors: Validati
|
|
|
103
106
|
* Validate a link to another internal page that may or may not have a hash.
|
|
104
107
|
*/
|
|
105
108
|
function validateLink(context: ValidationContext) {
|
|
106
|
-
const { errors, filePath, link, localeConfig, options, pages } = context
|
|
109
|
+
const { astroConfig, errors, filePath, link, localeConfig, options, pages } = context
|
|
107
110
|
|
|
108
111
|
const sanitizedLink = link.replace(/^\//, '')
|
|
109
112
|
const segments = sanitizedLink.split('#')
|
|
110
113
|
|
|
111
|
-
|
|
114
|
+
const path = segments[0]
|
|
112
115
|
const hash = segments[1]
|
|
113
116
|
|
|
114
117
|
if (path === undefined) {
|
|
@@ -127,10 +130,10 @@ function validateLink(context: ValidationContext) {
|
|
|
127
130
|
return
|
|
128
131
|
}
|
|
129
132
|
|
|
130
|
-
|
|
133
|
+
const sanitizedPath = ensureTrailingSlash(path)
|
|
131
134
|
|
|
132
|
-
const isValidPage = pages.has(
|
|
133
|
-
const fileHeadings = getFileHeadings(
|
|
135
|
+
const isValidPage = pages.has(sanitizedPath)
|
|
136
|
+
const fileHeadings = getFileHeadings(sanitizedPath, context)
|
|
134
137
|
|
|
135
138
|
if (!isValidPage || !fileHeadings) {
|
|
136
139
|
addError(errors, filePath, link, ValidationErrorType.InvalidLink)
|
|
@@ -144,6 +147,16 @@ function validateLink(context: ValidationContext) {
|
|
|
144
147
|
|
|
145
148
|
if (hash && !fileHeadings.includes(hash)) {
|
|
146
149
|
addError(errors, filePath, link, ValidationErrorType.InvalidAnchor)
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (
|
|
154
|
+
path.length > 0 &&
|
|
155
|
+
((astroConfig.trailingSlash === 'always' && !path.endsWith('/')) ||
|
|
156
|
+
(astroConfig.trailingSlash === 'never' && path.endsWith('/')))
|
|
157
|
+
) {
|
|
158
|
+
addError(errors, filePath, link, ValidationErrorType.TrailingSlash)
|
|
159
|
+
return
|
|
147
160
|
}
|
|
148
161
|
}
|
|
149
162
|
|
|
@@ -177,8 +190,8 @@ function validateSelfAnchor({ errors, link, filePath, headings }: ValidationCont
|
|
|
177
190
|
* Check if a link is a valid asset in the build output directory.
|
|
178
191
|
*/
|
|
179
192
|
function isValidAsset(path: string, context: ValidationContext) {
|
|
180
|
-
if (context.base !== '/') {
|
|
181
|
-
const base = stripLeadingSlash(context.base)
|
|
193
|
+
if (context.astroConfig.base !== '/') {
|
|
194
|
+
const base = stripLeadingSlash(context.astroConfig.base)
|
|
182
195
|
|
|
183
196
|
if (path.startsWith(base)) {
|
|
184
197
|
path = path.replace(new RegExp(`^${stripLeadingSlash(base)}/?`), '')
|
|
@@ -226,7 +239,7 @@ interface PageData {
|
|
|
226
239
|
type Pages = Set<PageData['pathname']>
|
|
227
240
|
|
|
228
241
|
interface ValidationContext {
|
|
229
|
-
|
|
242
|
+
astroConfig: AstroConfig
|
|
230
243
|
errors: ValidationErrors
|
|
231
244
|
filePath: string
|
|
232
245
|
headings: Headings
|