Skip to content

Commit

Permalink
fix: fixes validation issues with links containing query strings
Browse files Browse the repository at this point in the history
  • Loading branch information
HiDeoo committed Oct 4, 2024
1 parent 757833a commit 47b384d
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 6 deletions.
13 changes: 9 additions & 4 deletions packages/starlight-links-validator/libs/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function validateLinks(
pages: allPages,
}

if (link.startsWith('#')) {
if (link.startsWith('#') || link.startsWith('?')) {
if (options.errorOnInvalidHashes) {
validateSelfHash(validationContext)
}
Expand Down Expand Up @@ -125,7 +125,7 @@ function validateLink(context: ValidationContext) {
throw new Error('Failed to validate a link with no path.')
}

if (path.startsWith('.') || !link.startsWith('/')) {
if (path.startsWith('.') || (!link.startsWith('/') && !link.startsWith('?'))) {
if (options.errorOnRelativeLinks) {
addError(errors, filePath, link, ValidationErrorType.RelativeLink)
}
Expand All @@ -137,7 +137,7 @@ function validateLink(context: ValidationContext) {
return
}

const sanitizedPath = ensureTrailingSlash(path)
const sanitizedPath = ensureTrailingSlash(stripQueryString(path))

const isValidPage = pages.has(sanitizedPath)
const fileHeadings = getFileHeadings(sanitizedPath, context)
Expand Down Expand Up @@ -183,7 +183,8 @@ function getFileHeadings(path: string, { headings, localeConfig, options }: Vali
* Validate a link to an hash in the same page.
*/
function validateSelfHash({ errors, link, filePath, headings }: ValidationContext) {
const sanitizedHash = link.replace(/^#/, '')
const hash = link.split('#')[1] ?? link
const sanitizedHash = hash.replace(/^#/, '')
const fileHeadings = headings.get(filePath)

if (!fileHeadings) {
Expand Down Expand Up @@ -226,6 +227,10 @@ function isExcludedLink(link: string, context: ValidationContext) {
return picomatch(context.options.exclude)(link)
}

function stripQueryString(path: string): string {
return path.split('?')[0] ?? path
}

function addError(errors: ValidationErrors, filePath: string, link: string, type: ValidationErrorType) {
const fileErrors = errors.get(filePath) ?? []
fileErrors.push({ link, type })
Expand Down
26 changes: 25 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, 35, 4)
expectValidationErrorCount(output, 59, 4)

expectValidationErrors(output, 'test/', [
['/https://starlight.astro.build/', ValidationErrorType.InvalidLink],
Expand All @@ -39,6 +39,17 @@ test('does not build with invalid links', async () => {
['#anotherDiv', ValidationErrorType.InvalidHash],
['/guides/page-with-custom-slug', ValidationErrorType.InvalidLink],
['/release/@pkg/v0.2.0', ValidationErrorType.InvalidLink],
['/?query=string', ValidationErrorType.InvalidLink],
['/unknown?query=string', ValidationErrorType.InvalidLink],
['/unknown/?query=string', ValidationErrorType.InvalidLink],
['/unknown?query=string#title', ValidationErrorType.InvalidLink],
['/unknown/?query=string#title', ValidationErrorType.InvalidLink],
['?query=string#links', ValidationErrorType.InvalidHash],
['/guides/example/?query=string#links', ValidationErrorType.InvalidHash],
['/icon.svg?query=string', ValidationErrorType.InvalidLink],
['/guidelines/ui.pdf?query=string', ValidationErrorType.InvalidLink],
['/unknown-ref?query=string', ValidationErrorType.InvalidLink],
['?query=string#unknown-ref', ValidationErrorType.InvalidHash],
])

expectValidationErrors(output, 'guides/example/', [
Expand All @@ -54,6 +65,13 @@ test('does not build with invalid links', async () => {
['/linkbutton/', ValidationErrorType.InvalidLink],
['/linkbutton/#links', ValidationErrorType.InvalidLink],
['#linkbutton', ValidationErrorType.InvalidHash],
['?query=string#links', ValidationErrorType.InvalidHash],
['/unknown/?query=string#links', ValidationErrorType.InvalidLink],
['/unknown?query=string', ValidationErrorType.InvalidLink],
['/icon.svg?query=string', ValidationErrorType.InvalidLink],
['/guidelines/ui.pdf?query=string', ValidationErrorType.InvalidLink],
['/linkcard/?query=string', ValidationErrorType.InvalidLink],
['/linkbutton/?query=string', ValidationErrorType.InvalidLink],
])

expectValidationErrors(output, 'guides/namespacetest/', [
Expand All @@ -68,5 +86,11 @@ test('does not build with invalid links', async () => {
['./guides/example', ValidationErrorType.RelativeLink],
['../test', ValidationErrorType.RelativeLink],
['test', ValidationErrorType.RelativeLink],
['.?query=string', ValidationErrorType.RelativeLink],
['./relative?query=string', ValidationErrorType.RelativeLink],
['./test?query=string', ValidationErrorType.RelativeLink],
['./guides/example?query=string', ValidationErrorType.RelativeLink],
['../test?query=string', ValidationErrorType.RelativeLink],
['test?query=string', ValidationErrorType.RelativeLink],
])
})
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,19 @@ some content
<LinkButton href="/linkbutton/">LinkButton: unknown page</LinkButton>
<LinkButton href="/linkbutton/#links">LinkButton: unknown page and hash</LinkButton>
<LinkButton href="#linkbutton">LinkButton: unknown hash</LinkButton>

## Query strings

- [Link with query string to invalid hash in the same page](?query=string#links)
- [Link with query string to invalid hash in another page](/unknown/?query=string#links)

<a href="/unknown?query=string">HTML link with query string to unknown page</a>

<a href="/icon.svg?query=string">Link with query string to invalid asset</a>
<a href="/guidelines/ui.pdf?query=string">Link with query string to another invalid asset</a>

<CardGrid>
<LinkCard title="LinkCard: unknown page with query string" href="/linkcard/?query=string" />
</CardGrid>

<LinkButton href="/linkbutton/?query=string">LinkButton: unknown page with query string</LinkButton>
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,12 @@ title: Relative
- [Link to another page in another directory](./guides/example)
- [Link to another page in a parent directory](../test)
- [Another link to another page in the same directory](test)

## Query strings

- [Link with query string to the same page](.?query=string)
- [Link with query string to the same page with its name](./relative?query=string)
- [Link with query string to another page in the same directory](./test?query=string)
- [Link with query string to another page in another directory](./guides/example?query=string)
- [Link with query string to another page in a parent directory](../test?query=string)
- [Another link with query string to another page in the same directory](test?query=string)
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,26 @@ some content

- [Link to page not using its custom slug](/guides/page-with-custom-slug)
- [Link to page using an invalid custom slug](/release/@pkg/v0.2.0)

## Query strings

- [Home page with query string](/?query=string)

- [Unknown page with query string](/unknown?query=string)
- [Unknown page with query string](/unknown/?query=string)

- [Unknown page with query string and hash](/unknown?query=string#title)
- [Unknown page with query string and hash](/unknown/?query=string#title)

- [Link with query string to valid hash in this page](?query=string#some-links)
- [Link with query string to invalid hash in this page](?query=string#links)
- [Link with query string to valid hash in another MDX page](/guides/example/?query=string#some-links)
- [Link with query string to invalid hash in another MDX page](/guides/example/?query=string#links)
- [Link with query string to invalid asset](/icon.svg?query=string)
- [Link with query string to another invalid asset](/guidelines/ui.pdf?query=string)

- [Link reference with query string to unknwon page][ref-with-query-string-unknown-page]
- [Link reference with query string to invalid hash][ref-with-query-string-invalid-hash]

[ref-with-query-string-unknown-page]: /unknown-ref?query=string
[ref-with-query-string-invalid-hash]: ?query=string#unknown-ref
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,27 @@ title: Index
## Link to page with custom slug

- [A page with custom slug](/release/@pkg/v0.1.0)

## Query strings

- [Home page with query string](/?query=string)

- [Test page with query string](/test?query=string)
- [Test page with query string](/test/?query=string)

- [Test page with query string and hash](/test?query=string#title)
- [Test page with query string and hash](/test/?query=string#title)

- [Link to hash in this page with query string](?query=string#some-links)

- [Link to an asset with query string](/favicon.svg?query=string)
- [Link to another asset with query string](/guidelines/dummy.pdf?query=string)

- [ref-with-query-string]
- [Link reference with query string][ref-with-query-string]
- [Link reference with query string and hash in this page][ref-with-query-string-and-hash-internal]
- [Link reference with query string and hash in another page][ref-with-query-string-and-hash-external]

[ref-with-query-string]: /test?query=string
[ref-with-query-string-and-hash-internal]: ?query=string#some-links
[ref-with-query-string-and-hash-external]: /test?query=string#title
5 changes: 4 additions & 1 deletion packages/starlight-links-validator/tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ export function expectValidationErrors(
expect(output).toMatch(
new RegExp(`▶ ${path}
${validationErrors
.map(([link, type], index) => `.* ${index < validationErrors.length - 1 ? '├' : '└'}${link} - ${type}`)
.map(
([link, type], index) =>
`.* ${index < validationErrors.length - 1 ? '├' : '└'}${link.replaceAll('?', '\\?')} - ${type}`,
)
.join('\n')}`),
)
}

0 comments on commit 47b384d

Please sign in to comment.