diff --git a/packages/starlight-links-validator/libs/validation.ts b/packages/starlight-links-validator/libs/validation.ts index cbc08bf..dbbc970 100644 --- a/packages/starlight-links-validator/libs/validation.ts +++ b/packages/starlight-links-validator/libs/validation.ts @@ -58,7 +58,7 @@ export function validateLinks( pages: allPages, } - if (link.startsWith('#')) { + if (link.startsWith('#') || link.startsWith('?')) { if (options.errorOnInvalidHashes) { validateSelfHash(validationContext) } @@ -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) } @@ -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) @@ -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) { @@ -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 }) diff --git a/packages/starlight-links-validator/tests/basics.test.ts b/packages/starlight-links-validator/tests/basics.test.ts index c63bbe3..4021023 100644 --- a/packages/starlight-links-validator/tests/basics.test.ts +++ b/packages/starlight-links-validator/tests/basics.test.ts @@ -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], @@ -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/', [ @@ -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/', [ @@ -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], ]) }) diff --git a/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/guides/example.mdx b/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/guides/example.mdx index 972f2b1..66dacab 100644 --- a/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/guides/example.mdx +++ b/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/guides/example.mdx @@ -44,3 +44,19 @@ some content LinkButton: unknown page LinkButton: unknown page and hash LinkButton: unknown hash + +## 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) + +HTML link with query string to unknown page + +Link with query string to invalid asset +Link with query string to another invalid asset + + + + + +LinkButton: unknown page with query string diff --git a/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/relative.md b/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/relative.md index f03f0c3..de78a65 100644 --- a/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/relative.md +++ b/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/relative.md @@ -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) diff --git a/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/test.md b/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/test.md index 5150ea5..235b987 100644 --- a/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/test.md +++ b/packages/starlight-links-validator/tests/fixtures/invalid-links/src/content/docs/test.md @@ -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 diff --git a/packages/starlight-links-validator/tests/fixtures/valid-links/src/content/docs/index.md b/packages/starlight-links-validator/tests/fixtures/valid-links/src/content/docs/index.md index 06af854..7ad6ba6 100644 --- a/packages/starlight-links-validator/tests/fixtures/valid-links/src/content/docs/index.md +++ b/packages/starlight-links-validator/tests/fixtures/valid-links/src/content/docs/index.md @@ -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 diff --git a/packages/starlight-links-validator/tests/utils.ts b/packages/starlight-links-validator/tests/utils.ts index 269555f..e8b65b2 100644 --- a/packages/starlight-links-validator/tests/utils.ts +++ b/packages/starlight-links-validator/tests/utils.ts @@ -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')}`), ) }