Skip to content

Commit

Permalink
feat!: adds errors for local links, e.g. URLs with a hostname of `loc…
Browse files Browse the repository at this point in the history
…alhost` or `127.0.0.1`
  • Loading branch information
HiDeoo committed Nov 3, 2024
1 parent 3182cfa commit 806366c
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 7 deletions.
22 changes: 22 additions & 0 deletions docs/src/content/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,28 @@ export default defineConfig({
})
```

### `errorOnLocalLinks`

**Type:** `boolean`
**Default:** `true`

By default, the Starlight Links Validator plugin will error on local links, e.g. URLs with a hostname of `localhost` or `127.0.0.1`, as they are usually used for development purposes and should not be present in production.
If you want to allow such links, you can set this option to `false`.

```js {6}
export default defineConfig({
integrations: [
starlight({
plugins: [
starlightLinksValidator({
errorOnLocalLinks: false,
}),
],
}),
],
})
```

### `exclude`

**Type:** `string[]`
Expand Down
6 changes: 6 additions & 0 deletions packages/starlight-links-validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ const starlightLinksValidatorOptionsSchema = z
* @default true
*/
errorOnInvalidHashes: z.boolean().default(true),
/**
* Defines whether the plugin should error on local links, e.g. URLs with a hostname of `localhost` or `127.0.0.1`.
*
* @default true
*/
errorOnLocalLinks: z.boolean().default(true),
/**
* Defines a list of links or glob patterns that should be excluded from validation.
*
Expand Down
22 changes: 16 additions & 6 deletions packages/starlight-links-validator/libs/remark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const remarkStarlightLinksValidator: Plugin<[{ base: string; srcDir: URL
break
}
case 'link': {
if (isInternalLink(node.url)) {
if (shouldValidateLink(node.url)) {
fileLinks.push(node.url)
}

Expand All @@ -70,7 +70,7 @@ export const remarkStarlightLinksValidator: Plugin<[{ base: string; srcDir: URL
case 'linkReference': {
const definition = fileDefinitions.get(node.identifier)

if (definition && isInternalLink(definition)) {
if (definition && shouldValidateLink(definition)) {
fileLinks.push(definition)
}

Expand All @@ -96,7 +96,7 @@ export const remarkStarlightLinksValidator: Plugin<[{ base: string; srcDir: URL
continue
}

if (isInternalLink(attribute.value)) {
if (shouldValidateLink(attribute.value)) {
fileLinks.push(attribute.value)
}
}
Expand Down Expand Up @@ -125,7 +125,7 @@ export const remarkStarlightLinksValidator: Plugin<[{ base: string; srcDir: URL
htmlNode.tagName === 'a' &&
hasProperty(htmlNode, 'href') &&
typeof htmlNode.properties.href === 'string' &&
isInternalLink(htmlNode.properties.href)
shouldValidateLink(htmlNode.properties.href)
) {
fileLinks.push(htmlNode.properties.href)
}
Expand All @@ -145,8 +145,18 @@ export function getValidationData() {
return { headings, links }
}

function isInternalLink(link: string) {
return !isAbsoluteUrl(link)
function shouldValidateLink(link: string) {
if (!isAbsoluteUrl(link)) {
return true
}

try {
const url = new URL(link)

return url.hostname === 'localhost' || url.hostname === '127.0.0.1'
} catch {
return false
}
}

function getFilePath(base: string, filePath: string, slug: string | undefined) {
Expand Down
9 changes: 9 additions & 0 deletions packages/starlight-links-validator/libs/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const ValidationErrorType = {
InconsistentLocale: 'inconsistent locale',
InvalidHash: 'invalid hash',
InvalidLink: 'invalid link',
LocalLink: 'local link',
RelativeLink: 'relative link',
TrailingSlash: 'trailing slash',
} as const
Expand Down Expand Up @@ -115,6 +116,14 @@ function validateLink(context: ValidationContext) {
return
}

if (/^https?:\/\//.test(link)) {
if (options.errorOnLocalLinks) {
addError(errors, filePath, link, ValidationErrorType.LocalLink)
}

return
}

const sanitizedLink = link.replace(/^\//, '')
const segments = sanitizedLink.split('#')

Expand Down
5 changes: 4 additions & 1 deletion packages/starlight-links-validator/tests/basics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ test('does not build with invalid links', async () => {

expect(status).toBe('error')

expectValidationErrorCount(output, 61, 4)
expectValidationErrorCount(output, 64, 4)

expectValidationErrors(output, 'test/', [
['/https://starlight.astro.build/', ValidationErrorType.InvalidLink],
Expand Down Expand Up @@ -52,6 +52,9 @@ test('does not build with invalid links', async () => {
['/guidelines/ui.pdf?query=string', ValidationErrorType.InvalidLink],
['/unknown-ref?query=string', ValidationErrorType.InvalidLink],
['?query=string#unknown-ref', ValidationErrorType.InvalidHash],
['http://localhost', ValidationErrorType.LocalLink],
['http://localhost:4321/', ValidationErrorType.LocalLink],
['https://127.0.0.1:4321/getting-started', ValidationErrorType.LocalLink],
])

expectValidationErrors(output, 'guides/example/', [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,9 @@ some content

[ref-with-query-string-unknown-page]: /unknown-ref?query=string
[ref-with-query-string-invalid-hash]: ?query=string#unknown-ref

## Local links

- [Local link](http://localhost)
- [Local link with port](http://localhost:4321/)
- [Local link using HTTPS](https://127.0.0.1:4321/getting-started)

0 comments on commit 806366c

Please sign in to comment.