starlight-links-validator 0.4.2 → 0.5.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/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  <div align="center">
2
2
  <h1>starlight-links-validator 🦺</h1>
3
- <p>Astro integration for Starlight to validate internal links.</p>
3
+ <p>Starlight plugin to validate internal links.</p>
4
4
  <p>
5
- <a href="https://i.imgur.com/EgiTGeR.png" title="Screenshot of starlight-links-validator">
6
- <img alt="Screenshot of starlight-links-validator" src="https://i.imgur.com/EgiTGeR.png" width="520" />
5
+ <a href="https://github.com/HiDeoo/starlight-links-validator/assets/494699/fe5f797a-8089-4271-b090-7158bb053dfa" title="Screenshot of starlight-links-validator">
6
+ <img alt="Screenshot of starlight-links-validator" src="https://github.com/HiDeoo/starlight-links-validator/assets/494699/fe5f797a-8089-4271-b090-7158bb053dfa" width="520" />
7
7
  </a>
8
8
  </p>
9
9
  </div>
@@ -18,9 +18,13 @@
18
18
  <br />
19
19
  </div>
20
20
 
21
+ ## Getting Started
22
+
23
+ Want to get started immediately? Check out the [getting started guide](https://starlight-links-validator.vercel.app/getting-started/).
24
+
21
25
  ## Features
22
26
 
23
- An [Astro](https://astro.build) integration for [Starlight](https://starlight.astro.build) Starlight to validate **_internal_** links in Markdown and MDX files.
27
+ A [Starlight](https://starlight.astro.build) plugin to validate **_internal_** links in Markdown and MDX files.
24
28
 
25
29
  - Validate internal links to other pages
26
30
  - Validate internal links to anchors in other pages
@@ -28,38 +32,6 @@ An [Astro](https://astro.build) integration for [Starlight](https://starlight.as
28
32
  - Ignore external links
29
33
  - Run only during a production build
30
34
 
31
- ## Installation
32
-
33
- Install the Starlight Links Validator integration using your favorite package manager, e.g. with [pnpm](https://pnpm.io):
34
-
35
- ```shell
36
- pnpm add starlight-links-validator
37
- ```
38
-
39
- Update your [Astro configuration](https://docs.astro.build/en/guides/configuring-astro/#supported-config-file-types) to include the Starlight Links Validator integration **_before_** the Starlight integration:
40
-
41
- ```diff
42
- import starlight from '@astrojs/starlight'
43
- import { defineConfig } from 'astro/config'
44
- + import starlightLinksValidator from 'starlight-links-validator'
45
-
46
- export default defineConfig({
47
- // …
48
- integrations: [
49
- + starlightLinksValidator(),
50
- starlight({
51
- sidebar: [
52
- {
53
- label: 'Guides',
54
- items: [{ label: 'Example Guide', link: '/guides/example/' }],
55
- },
56
- ],
57
- title: 'My Docs',
58
- }),
59
- ],
60
- })
61
- ```
62
-
63
35
  ## License
64
36
 
65
37
  Licensed under the MIT License, Copyright © HiDeoo.
package/index.ts CHANGED
@@ -1,36 +1,90 @@
1
- import type { AstroIntegration } from 'astro'
1
+ import type { StarlightPlugin } from '@astrojs/starlight/types'
2
2
  import { AstroError } from 'astro/errors'
3
+ import { z } from 'astro/zod'
3
4
 
4
5
  import { remarkStarlightLinksValidator } from './libs/remark'
5
6
  import { logErrors, validateLinks } from './libs/validation'
6
7
 
7
- export default function starlightLinksValidatorIntegration(): AstroIntegration {
8
+ const starlightLinksValidatorOptionsSchema = z
9
+ .object({
10
+ /**
11
+ * Defines whether the plugin should error on fallback pages.
12
+ *
13
+ * If you do not expect to have all pages translated in all configured locales and want to use the fallback pages
14
+ * feature built-in into Starlight, you should set this option to `false`.
15
+ *
16
+ * @default true
17
+ * @see https://starlight.astro.build/guides/i18n/#fallback-content
18
+ */
19
+ errorOnFallbackPages: z.boolean().default(true),
20
+ /**
21
+ * Defines whether the plugin should error on inconsistent locale links.
22
+ *
23
+ * When set to `true`, the plugin will error on links that are pointing to a page in a different locale.
24
+ *
25
+ * @default false
26
+ */
27
+ errorOnInconsistentLocale: z.boolean().default(false),
28
+ /**
29
+ * Defines whether the plugin should error on internal relative links.
30
+ *
31
+ * When set to `false`, the plugin will ignore relative links (e.g. `./foo` or `../bar`).
32
+ *
33
+ * @default true
34
+ */
35
+ errorOnRelativeLinks: z.boolean().default(true),
36
+ })
37
+ .default({})
38
+
39
+ export default function starlightLinksValidatorPlugin(
40
+ userOptions?: StarlightLinksValidatorUserOptions,
41
+ ): StarlightPlugin {
42
+ const options = starlightLinksValidatorOptionsSchema.safeParse(userOptions)
43
+
44
+ if (!options.success) {
45
+ throwPluginError('Invalid options passed to the starlight-links-validator plugin.')
46
+ }
47
+
8
48
  return {
9
- name: 'starlight-links-validator',
49
+ name: 'starlight-links-validator-plugin',
10
50
  hooks: {
11
- 'astro:config:setup': ({ command, updateConfig }) => {
12
- if (command !== 'build') {
13
- return
14
- }
15
-
16
- updateConfig({
17
- markdown: {
18
- remarkPlugins: [remarkStarlightLinksValidator],
19
- },
20
- })
21
- },
22
- 'astro:build:done': ({ dir, pages }) => {
23
- const errors = validateLinks(pages, dir)
51
+ setup({ addIntegration, astroConfig, config: starlightConfig, logger }) {
52
+ addIntegration({
53
+ name: 'starlight-links-validator-integration',
54
+ hooks: {
55
+ 'astro:config:setup': ({ command, updateConfig }) => {
56
+ if (command !== 'build') {
57
+ return
58
+ }
24
59
 
25
- logErrors(errors)
60
+ updateConfig({
61
+ markdown: {
62
+ remarkPlugins: [[remarkStarlightLinksValidator, astroConfig.base]],
63
+ },
64
+ })
65
+ },
66
+ 'astro:build:done': ({ dir, pages }) => {
67
+ const errors = validateLinks(pages, dir, astroConfig.base, starlightConfig, options.data)
26
68
 
27
- if (errors.size > 0) {
28
- throw new AstroError(
29
- 'Links validation failed.',
30
- `See the error report above for more informations.\n\nIf you believe this is a bug, please file an issue at https://github.com/HiDeoo/starlight-links-validator/issues/new/choose.`,
31
- )
32
- }
69
+ logErrors(logger, errors)
70
+
71
+ if (errors.size > 0) {
72
+ throwPluginError('Links validation failed.')
73
+ }
74
+ },
75
+ },
76
+ })
33
77
  },
34
78
  },
35
79
  }
36
80
  }
81
+
82
+ function throwPluginError(message: string): never {
83
+ throw new AstroError(
84
+ message,
85
+ `See the error report above for more informations.\n\nIf you believe this is a bug, please file an issue at https://github.com/HiDeoo/starlight-links-validator/issues/new/choose.`,
86
+ )
87
+ }
88
+
89
+ type StarlightLinksValidatorUserOptions = z.input<typeof starlightLinksValidatorOptionsSchema>
90
+ export type StarlightLinksValidatorOptions = z.output<typeof starlightLinksValidatorOptionsSchema>
package/libs/i18n.ts ADDED
@@ -0,0 +1,78 @@
1
+ import { ensureLeadingSlash, ensureTrailingSlash } from './path'
2
+ import type { Headings } from './remark'
3
+ import type { StarlightUserConfig } from './validation'
4
+
5
+ export function getLocaleConfig(config: StarlightUserConfig): LocaleConfig | undefined {
6
+ if (!config.locales || Object.keys(config.locales).length === 0) return
7
+
8
+ let defaultLocale = config.defaultLocale
9
+ const locales: string[] = []
10
+
11
+ for (const [dir, locale] of Object.entries(config.locales)) {
12
+ if (!locale) continue
13
+
14
+ if (dir === 'root') {
15
+ if (!locale.lang) continue
16
+
17
+ defaultLocale = ''
18
+ }
19
+
20
+ locales.push(dir)
21
+ }
22
+
23
+ if (defaultLocale === undefined) return
24
+
25
+ return {
26
+ defaultLocale,
27
+ locales,
28
+ }
29
+ }
30
+
31
+ export function getFallbackHeadings(
32
+ path: string,
33
+ headings: Headings,
34
+ localeConfig: LocaleConfig | undefined,
35
+ ): string[] | undefined {
36
+ if (!localeConfig) return
37
+
38
+ for (const locale of localeConfig.locales) {
39
+ if (path.startsWith(`${locale}/`)) {
40
+ const fallbackPath = path.replace(
41
+ new RegExp(`^${locale}/`),
42
+ localeConfig.defaultLocale === '' ? localeConfig.defaultLocale : `${localeConfig.defaultLocale}/`,
43
+ )
44
+
45
+ return headings.get(fallbackPath === '' ? '/' : fallbackPath)
46
+ }
47
+ }
48
+
49
+ return
50
+ }
51
+
52
+ export function isInconsistentLocaleLink(path: string, link: string, localeConfig: LocaleConfig) {
53
+ const pathLocale = getLocale(path, localeConfig)
54
+ const linkLocale = getLocale(link, localeConfig)
55
+
56
+ if (pathLocale !== undefined || linkLocale !== undefined) {
57
+ return pathLocale !== linkLocale
58
+ }
59
+
60
+ return false
61
+ }
62
+
63
+ function getLocale(path: string, localeConfig: LocaleConfig) {
64
+ const normalizedPath = ensureTrailingSlash(ensureLeadingSlash(path))
65
+
66
+ for (const locale of localeConfig.locales) {
67
+ if (normalizedPath.startsWith(`/${locale}/`)) {
68
+ return locale
69
+ }
70
+ }
71
+
72
+ return
73
+ }
74
+
75
+ export interface LocaleConfig {
76
+ defaultLocale: string
77
+ locales: string[]
78
+ }
package/libs/path.ts ADDED
@@ -0,0 +1,11 @@
1
+ export function ensureLeadingSlash(path: string): string {
2
+ return path.startsWith('/') ? path : `/${path}`
3
+ }
4
+
5
+ export function ensureTrailingSlash(path: string): string {
6
+ return path.endsWith('/') ? path : `${path}/`
7
+ }
8
+
9
+ export function stripLeadingSlash(path: string) {
10
+ return path.replace(/^\//, '')
11
+ }
package/libs/remark.ts CHANGED
@@ -12,15 +12,17 @@ import { toString } from 'mdast-util-to-string'
12
12
  import type { Plugin } from 'unified'
13
13
  import { visit } from 'unist-util-visit'
14
14
 
15
+ import { stripLeadingSlash } from './path'
16
+
15
17
  // All the headings keyed by file path.
16
18
  const headings: Headings = new Map()
17
19
  // All the internal links keyed by file path.
18
20
  const links: Links = new Map()
19
21
 
20
- export const remarkStarlightLinksValidator: Plugin<[], Root> = function () {
22
+ export const remarkStarlightLinksValidator: Plugin<[base: string], Root> = function (base) {
21
23
  return (tree, file) => {
22
24
  const slugger = new GitHubSlugger()
23
- const filePath = normalizeFilePath(file.history[0])
25
+ const filePath = normalizeFilePath(base, file.history[0])
24
26
 
25
27
  const fileHeadings: string[] = []
26
28
  const fileLinks: string[] = []
@@ -131,22 +133,28 @@ export function getValidationData() {
131
133
  }
132
134
 
133
135
  function isInternalLink(link: string) {
134
- return nodePath.isAbsolute(link) || link.startsWith('#')
136
+ return nodePath.isAbsolute(link) || link.startsWith('#') || link.startsWith('.')
135
137
  }
136
138
 
137
- function normalizeFilePath(filePath?: string) {
139
+ function normalizeFilePath(base: string, filePath?: string) {
138
140
  if (!filePath) {
139
141
  throw new Error('Missing file path to validate links.')
140
142
  }
141
143
 
142
- return nodePath
144
+ const path = nodePath
143
145
  .relative(nodePath.join(process.cwd(), 'src/content/docs'), filePath)
144
146
  .replace(/\.\w+$/, '')
145
147
  .replace(/index$/, '')
146
- .replace(/\/?$/, '/')
148
+ .replace(/[/\\]?$/, '/')
147
149
  .split(/[/\\]/)
148
150
  .map((segment) => slug(segment))
149
151
  .join('/')
152
+
153
+ if (base !== '/') {
154
+ return nodePath.posix.join(stripLeadingSlash(base), path)
155
+ }
156
+
157
+ return path
150
158
  }
151
159
 
152
160
  function isMdxIdAttribute(attribute: MdxJsxAttribute | MdxJsxExpressionAttribute): attribute is MdxIdAttribute {
@@ -1,24 +1,60 @@
1
1
  import { statSync } from 'node:fs'
2
+ import { posix } from 'node:path'
2
3
  import { fileURLToPath } from 'node:url'
3
4
 
4
- import { bgGreen, black, bold, cyan, dim, red } from 'kleur/colors'
5
+ import type { StarlightPlugin } from '@astrojs/starlight/types'
6
+ import type { AstroIntegrationLogger } from 'astro'
7
+ import { bgGreen, black, blue, dim, green, red } from 'kleur/colors'
5
8
 
9
+ import type { StarlightLinksValidatorOptions } from '..'
10
+
11
+ import { getFallbackHeadings, getLocaleConfig, isInconsistentLocaleLink, type LocaleConfig } from './i18n'
12
+ import { ensureTrailingSlash, stripLeadingSlash } from './path'
6
13
  import { getValidationData, type Headings } from './remark'
7
14
 
8
- export function validateLinks(pages: PageData[], outputDir: URL): ValidationErrors {
15
+ export const ValidationErrorType = {
16
+ InconsistentLocale: 'inconsistent locale',
17
+ InvalidAnchor: 'invalid anchor',
18
+ InvalidLink: 'invalid link',
19
+ RelativeLink: 'relative link',
20
+ } as const
21
+
22
+ export function validateLinks(
23
+ pages: PageData[],
24
+ outputDir: URL,
25
+ base: string,
26
+ starlightConfig: StarlightUserConfig,
27
+ options: StarlightLinksValidatorOptions,
28
+ ): ValidationErrors {
9
29
  process.stdout.write(`\n${bgGreen(black(` validating links `))}\n`)
10
30
 
31
+ const localeConfig = getLocaleConfig(starlightConfig)
11
32
  const { headings, links } = getValidationData()
12
- const allPages: Pages = new Set(pages.map((page) => page.pathname))
33
+ const allPages: Pages = new Set(
34
+ pages.map((page) =>
35
+ ensureTrailingSlash(base === '/' ? page.pathname : posix.join(stripLeadingSlash(base), page.pathname)),
36
+ ),
37
+ )
13
38
 
14
39
  const errors: ValidationErrors = new Map()
15
40
 
16
41
  for (const [filePath, fileLinks] of links) {
17
42
  for (const link of fileLinks) {
43
+ const validationContext: ValidationContext = {
44
+ errors,
45
+ filePath,
46
+ headings,
47
+ link,
48
+ localeConfig,
49
+ options,
50
+ outputDir,
51
+ pages: allPages,
52
+ }
53
+
18
54
  if (link.startsWith('#')) {
19
- validateSelfAnchor(errors, link, filePath, headings)
55
+ validateSelfAnchor(validationContext)
20
56
  } else {
21
- validateLink(errors, link, filePath, headings, allPages, outputDir)
57
+ validateLink(validationContext)
22
58
  }
23
59
  }
24
60
  }
@@ -26,49 +62,46 @@ export function validateLinks(pages: PageData[], outputDir: URL): ValidationErro
26
62
  return errors
27
63
  }
28
64
 
29
- export function logErrors(errors: ValidationErrors) {
65
+ export function logErrors(pluginLogger: AstroIntegrationLogger, errors: ValidationErrors) {
66
+ const logger = pluginLogger.fork('')
67
+
30
68
  if (errors.size === 0) {
31
- process.stdout.write(dim('All internal links are valid.\n\n'))
69
+ logger.info(green('All internal links are valid.\n'))
32
70
  return
33
71
  }
34
72
 
35
73
  const errorCount = [...errors.values()].reduce((acc, links) => acc + links.length, 0)
36
74
 
37
- process.stderr.write(
38
- `${bold(
39
- red(
40
- `Found ${errorCount} invalid ${pluralize(errorCount, 'link')} in ${errors.size} ${pluralize(
41
- errors.size,
42
- 'file',
43
- )}.`,
44
- ),
45
- )}\n\n`,
75
+ logger.error(
76
+ red(
77
+ `✗ Found ${errorCount} invalid ${pluralize(errorCount, 'link')} in ${errors.size} ${pluralize(
78
+ errors.size,
79
+ 'file',
80
+ )}.`,
81
+ ),
46
82
  )
47
83
 
48
- for (const [file, links] of errors) {
49
- process.stderr.write(`${red('▶')} ${file}\n`)
84
+ for (const [file, validationErrors] of errors) {
85
+ logger.info(`${red('▶')} ${blue(file)}`)
50
86
 
51
- for (const [index, link] of links.entries()) {
52
- process.stderr.write(` ${cyan(`${index < links.length - 1 ? '├' : '└'}─`)} ${link}\n`)
87
+ for (const [index, validationError] of validationErrors.entries()) {
88
+ logger.info(
89
+ ` ${blue(`${index < validationErrors.length - 1 ? '├' : '└'}─`)} ${validationError.link}${dim(
90
+ ` - ${validationError.type}`,
91
+ )}`,
92
+ )
53
93
  }
54
-
55
- process.stdout.write(dim('\n'))
56
94
  }
57
95
 
58
- process.stdout.write(dim('\n'))
96
+ process.stdout.write('\n')
59
97
  }
60
98
 
61
99
  /**
62
100
  * Validate a link to another internal page that may or may not have a hash.
63
101
  */
64
- function validateLink(
65
- errors: ValidationErrors,
66
- link: string,
67
- filePath: string,
68
- headings: Headings,
69
- pages: Pages,
70
- outputDir: URL,
71
- ) {
102
+ function validateLink(context: ValidationContext) {
103
+ const { errors, filePath, link, localeConfig, options, outputDir, pages } = context
104
+
72
105
  const sanitizedLink = link.replace(/^\//, '')
73
106
  const segments = sanitizedLink.split('#')
74
107
 
@@ -79,32 +112,53 @@ function validateLink(
79
112
  throw new Error('Failed to validate a link with no path.')
80
113
  }
81
114
 
82
- if (isValidAsset(path, outputDir)) {
115
+ if (path.startsWith('.')) {
116
+ if (options.errorOnRelativeLinks) {
117
+ addError(errors, filePath, link, ValidationErrorType.RelativeLink)
118
+ }
119
+
83
120
  return
84
121
  }
85
122
 
86
- if (path.length > 0 && !path.endsWith('/')) {
87
- path += '/'
123
+ if (isValidAsset(path, outputDir)) {
124
+ return
88
125
  }
89
126
 
127
+ path = ensureTrailingSlash(path)
128
+
90
129
  const isValidPage = pages.has(path)
91
- const fileHeadings = headings.get(path === '' ? '/' : path)
130
+ const fileHeadings = getFileHeadings(path, context)
92
131
 
93
132
  if (!isValidPage || !fileHeadings) {
94
- addError(errors, filePath, link)
133
+ addError(errors, filePath, link, ValidationErrorType.InvalidLink)
134
+ return
135
+ }
136
+
137
+ if (options.errorOnInconsistentLocale && localeConfig && isInconsistentLocaleLink(filePath, link, localeConfig)) {
138
+ addError(errors, filePath, link, ValidationErrorType.InconsistentLocale)
95
139
  return
96
140
  }
97
141
 
98
142
  if (hash && !fileHeadings.includes(hash)) {
99
- addError(errors, filePath, link)
143
+ addError(errors, filePath, link, ValidationErrorType.InvalidAnchor)
100
144
  }
101
145
  }
102
146
 
147
+ function getFileHeadings(path: string, { headings, localeConfig, options }: ValidationContext) {
148
+ let heading = headings.get(path === '' ? '/' : path)
149
+
150
+ if (!options.errorOnFallbackPages && !heading && localeConfig) {
151
+ heading = getFallbackHeadings(path, headings, localeConfig)
152
+ }
153
+
154
+ return heading
155
+ }
156
+
103
157
  /**
104
158
  * Validate a link to an anchor in the same page.
105
159
  */
106
- function validateSelfAnchor(errors: ValidationErrors, hash: string, filePath: string, headings: Headings) {
107
- const sanitizedHash = hash.replace(/^#/, '')
160
+ function validateSelfAnchor({ errors, link, filePath, headings }: ValidationContext) {
161
+ const sanitizedHash = link.replace(/^#/, '')
108
162
  const fileHeadings = headings.get(filePath)
109
163
 
110
164
  if (!fileHeadings) {
@@ -112,7 +166,7 @@ function validateSelfAnchor(errors: ValidationErrors, hash: string, filePath: st
112
166
  }
113
167
 
114
168
  if (!fileHeadings.includes(sanitizedHash)) {
115
- addError(errors, filePath, hash)
169
+ addError(errors, filePath, link, 'invalid anchor')
116
170
  }
117
171
  }
118
172
 
@@ -131,9 +185,9 @@ function isValidAsset(path: string, outputDir: URL) {
131
185
  }
132
186
  }
133
187
 
134
- function addError(errors: ValidationErrors, filePath: string, link: string) {
188
+ function addError(errors: ValidationErrors, filePath: string, link: string, type: ValidationErrorType) {
135
189
  const fileErrors = errors.get(filePath) ?? []
136
- fileErrors.push(link)
190
+ fileErrors.push({ link, type })
137
191
 
138
192
  errors.set(filePath, fileErrors)
139
193
  }
@@ -142,11 +196,31 @@ function pluralize(count: number, singular: string) {
142
196
  return count === 1 ? singular : `${singular}s`
143
197
  }
144
198
 
145
- // The invalid links keyed by file path.
146
- type ValidationErrors = Map<string, string[]>
199
+ // The validation errors keyed by file path.
200
+ type ValidationErrors = Map<string, ValidationError[]>
201
+
202
+ export type ValidationErrorType = (typeof ValidationErrorType)[keyof typeof ValidationErrorType]
203
+
204
+ interface ValidationError {
205
+ link: string
206
+ type: ValidationErrorType
207
+ }
147
208
 
148
209
  interface PageData {
149
210
  pathname: string
150
211
  }
151
212
 
152
213
  type Pages = Set<PageData['pathname']>
214
+
215
+ interface ValidationContext {
216
+ errors: ValidationErrors
217
+ filePath: string
218
+ headings: Headings
219
+ link: string
220
+ localeConfig: LocaleConfig | undefined
221
+ options: StarlightLinksValidatorOptions
222
+ outputDir: URL
223
+ pages: Pages
224
+ }
225
+
226
+ export type StarlightUserConfig = Parameters<StarlightPlugin['hooks']['setup']>['0']['config']
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "starlight-links-validator",
3
- "version": "0.4.2",
3
+ "version": "0.5.1",
4
4
  "license": "MIT",
5
- "description": "Astro integration for Starlight to validate internal links.",
5
+ "description": "Starlight plugin to validate internal links.",
6
6
  "author": "HiDeoo <github@hideoo.dev> (https://hideoo.dev)",
7
7
  "type": "module",
8
8
  "exports": {
@@ -18,19 +18,19 @@
18
18
  "unist-util-visit": "5.0.0"
19
19
  },
20
20
  "devDependencies": {
21
- "@astrojs/starlight": "0.10.1",
22
- "@types/hast": "3.0.1",
23
- "@types/mdast": "4.0.0",
21
+ "@astrojs/starlight": "0.15.0",
22
+ "@types/hast": "3.0.3",
23
+ "@types/mdast": "4.0.3",
24
24
  "@types/node": "18.17.18",
25
- "astro": "3.1.1",
25
+ "astro": "4.0.4",
26
26
  "mdast-util-mdx-jsx": "3.0.0",
27
27
  "typescript": "5.1.3",
28
- "unified": "11.0.3",
29
- "vitest": "0.32.2"
28
+ "unified": "11.0.4",
29
+ "vitest": "1.0.4"
30
30
  },
31
31
  "peerDependencies": {
32
- "@astrojs/starlight": ">=0.9.0",
33
- "astro": ">=3.0.0"
32
+ "@astrojs/starlight": ">=0.15.0",
33
+ "astro": ">=4.0.0"
34
34
  },
35
35
  "engines": {
36
36
  "node": ">=18.14.1"
@@ -42,10 +42,10 @@
42
42
  "sideEffects": false,
43
43
  "keywords": [
44
44
  "starlight",
45
+ "plugin",
45
46
  "links",
46
47
  "validation",
47
- "astro",
48
- "astro-integration"
48
+ "astro"
49
49
  ],
50
50
  "homepage": "https://github.com/HiDeoo/starlight-links-validator",
51
51
  "repository": {
@@ -55,6 +55,6 @@
55
55
  "bugs": "https://github.com/HiDeoo/starlight-links-validator/issues",
56
56
  "scripts": {
57
57
  "test": "vitest",
58
- "lint": "prettier -c --cache . && eslint . --cache --max-warnings=0 && tsc --noEmit"
58
+ "lint": "prettier -c --cache . && eslint . --cache --max-warnings=0"
59
59
  }
60
60
  }