starlight-versions 0.10.1 → 0.10.2

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/.npmignore ADDED
@@ -0,0 +1,5 @@
1
+ eslint.config.mjs
2
+ fixtures
3
+ tests
4
+ tsconfig.json
5
+ tsconfig.tsbuildinfo
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # starlight-versions
2
2
 
3
+ ## 0.10.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#86](https://github.com/HiDeoo/starlight-versions/pull/86) [`27959fb`](https://github.com/HiDeoo/starlight-versions/commit/27959fb72e19fbad44a79fe673a2246ab64ad05a) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes versioning of public assets in media elements using both `src` and `srcset` attributes.
8
+
9
+ - [#84](https://github.com/HiDeoo/starlight-versions/pull/84) [`7cc6fdf`](https://github.com/HiDeoo/starlight-versions/commit/7cc6fdfe49f66df7a77b1f1b18891ebe50a37b1e) Thanks [@dgesteves](https://github.com/dgesteves)! - Fixes versioning of Markdown files using syntax that is not valid in MDX, like `<->` arrows.
10
+
11
+ - [#86](https://github.com/HiDeoo/starlight-versions/pull/86) [`27959fb`](https://github.com/HiDeoo/starlight-versions/commit/27959fb72e19fbad44a79fe673a2246ab64ad05a) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes versioning of public assets in `srcset` attributes with multiple images or descriptors.
12
+
13
+ - [#86](https://github.com/HiDeoo/starlight-versions/pull/86) [`27959fb`](https://github.com/HiDeoo/starlight-versions/commit/27959fb72e19fbad44a79fe673a2246ab64ad05a) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes versioning of images inside links.
14
+
15
+ - [#86](https://github.com/HiDeoo/starlight-versions/pull/86) [`27959fb`](https://github.com/HiDeoo/starlight-versions/commit/27959fb72e19fbad44a79fe673a2246ab64ad05a) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes versioning of public assets in inline media elements in MDX files.
16
+
3
17
  ## 0.10.1
4
18
 
5
19
  ### Patch Changes
package/libs/markdown.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import assert from 'node:assert'
2
2
 
3
- import type { Image, Link, Root } from 'mdast'
3
+ import type { Element } from 'hast'
4
+ import { fromHtml } from 'hast-util-from-html'
5
+ import type { Html, Image, Link, Root } from 'mdast'
4
6
  import type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx-jsx'
5
7
  import type { MdxjsEsm } from 'mdast-util-mdxjs-esm'
6
8
  import { remark } from 'remark'
@@ -12,6 +14,7 @@ import type { VFile } from 'vfile'
12
14
 
13
15
  import {
14
16
  ensureTrailingSlash,
17
+ getExtension,
15
18
  isAbsoluteLink,
16
19
  stripLeadingAndTrailingSlashes,
17
20
  stripLeadingSlash,
@@ -22,12 +25,17 @@ import type { Version, VersionAsset } from './versions'
22
25
 
23
26
  const importPathRegex = /(from ?["'])([^"']*)(["'];?\s?)$/gm
24
27
  const astroAssetRegex = /\.(png|jpg|jpeg|tiff|webp|gif|svg|avif|.+\?raw)$/i
28
+ const htmlAttributeRegex = /^(?<prefix>[^=]+=\s*(?<quote>["']?))(?<value>.*)\k<quote>$/s
29
+ const srcsetUrlRegex = /(?<=(?:^|,)\s*)[^\s,]\S*?(?=,*(?:\s|$))/g
25
30
 
26
31
  const mediaElements = new Set(['img', 'source', 'Image', 'audio', 'video'])
27
32
 
28
- const processor = remark().use(remarkDirective).use(remarkMdx).use(remarkFrontmatter).use(remarkStarlightVersions)
33
+ const markdownProcessor = remark().use(remarkDirective).use(remarkFrontmatter).use(remarkStarlightVersions)
34
+ const mdxProcessor = markdownProcessor().use(remarkMdx)
29
35
 
30
36
  export async function transformMarkdown(markdown: string, context: TransformContext) {
37
+ const processor = getExtension(context.url.pathname) === '.mdx' ? mdxProcessor : markdownProcessor
38
+
31
39
  const file = await processor.process({
32
40
  data: { ...context },
33
41
  value: markdown,
@@ -46,17 +54,11 @@ export function remarkStarlightVersions() {
46
54
  case 'link': {
47
55
  return handleLinks(node, file)
48
56
  }
49
- case 'mdxJsxTextElement': {
50
- if (node.name === 'a') {
51
- return handleLinkElements(node, file)
52
- }
53
-
54
- return CONTINUE
55
- }
56
57
  case 'mdxjsEsm': {
57
58
  return handleImports(node, file)
58
59
  }
59
- case 'mdxJsxFlowElement': {
60
+ case 'mdxJsxFlowElement':
61
+ case 'mdxJsxTextElement': {
60
62
  if (node.name === 'a') {
61
63
  return handleLinkElements(node, file)
62
64
  }
@@ -70,6 +72,9 @@ export function remarkStarlightVersions() {
70
72
  case 'image': {
71
73
  return handleImages(node, file)
72
74
  }
75
+ case 'html': {
76
+ return handleHtml(node, file)
77
+ }
73
78
  default: {
74
79
  return CONTINUE
75
80
  }
@@ -102,7 +107,7 @@ function handleFrontmatter(tree: Root, file: VFile) {
102
107
  if (file.data.locale && segments[0] === file.data.locale) {
103
108
  segments.splice(1, 0, file.data.version.slug)
104
109
  } else {
105
- segments.splice(0, 0, file.data.version.slug)
110
+ segments.unshift(file.data.version.slug)
106
111
  }
107
112
 
108
113
  frontmatter.slug = segments.join('/')
@@ -144,7 +149,7 @@ function handleFrontmatter(tree: Root, file: VFile) {
144
149
  function handleLinks(node: Link, file: VFile) {
145
150
  node.url = transformLink(node.url, file)
146
151
 
147
- return SKIP
152
+ return CONTINUE
148
153
  }
149
154
 
150
155
  function handleLinkElements(node: MdxJsxFlowElement | MdxJsxTextElement, file: VFile) {
@@ -194,18 +199,74 @@ function handleImages(node: Image, file: VFile) {
194
199
  return SKIP
195
200
  }
196
201
 
197
- function handleMediaElements(node: MdxJsxFlowElement, file: VFile) {
198
- const srcOrSrcset = node.attributes.find(
199
- (attribute) => attribute.type === 'mdxJsxAttribute' && (attribute.name === 'src' || attribute.name === 'srcset'),
200
- )
202
+ function handleMediaElements(node: MdxJsxFlowElement | MdxJsxTextElement, file: VFile) {
203
+ for (const attribute of node.attributes) {
204
+ if (attribute.type !== 'mdxJsxAttribute' || typeof attribute.value !== 'string') continue
201
205
 
202
- if (!srcOrSrcset || typeof srcOrSrcset.value !== 'string' || !isPublicAsset(srcOrSrcset.value)) return CONTINUE
206
+ if (attribute.name === 'src' && isPublicAsset(attribute.value)) {
207
+ attribute.value = addVersionToPublicAsset(attribute.value, file)
208
+ } else if (attribute.name === 'srcset' || attribute.name === 'srcSet') {
209
+ attribute.value = addVersionToPublicSrcset(attribute.value, file)
210
+ }
211
+ }
203
212
 
204
- srcOrSrcset.value = addVersionToPublicAsset(srcOrSrcset.value, file)
213
+ return CONTINUE
214
+ }
215
+
216
+ // Raw HTML nodes only exist in non-MDX files (MDX parses HTML as JSX) and are not re-serialized to preserve them as
217
+ // authored, e.g. unclosed inline tags, and only the transformed attribute values are updated.
218
+ function handleHtml(node: Html, file: VFile) {
219
+ const tree = fromHtml(node.value, { fragment: true, verbose: true })
220
+ // Updates are keyed by attribute offset as misnested HTML can lead the parser to recreate elements which share the
221
+ // source position of the original element and would be updated multiple times.
222
+ const updates = new Map<number, HtmlAttribute>()
223
+
224
+ visit(tree, 'element', (element) => {
225
+ if (element.tagName === 'a') {
226
+ const href = getHtmlAttribute(node.value, element, 'href')
227
+ if (!href || updates.has(href.start)) return
228
+
229
+ updates.set(href.start, { ...href, value: transformLink(href.value, file) })
230
+ } else if (mediaElements.has(element.tagName)) {
231
+ const src = getHtmlAttribute(node.value, element, 'src')
232
+
233
+ if (src && !updates.has(src.start) && isPublicAsset(src.value)) {
234
+ updates.set(src.start, { ...src, value: addVersionToPublicAsset(src.value, file) })
235
+ }
236
+
237
+ const srcset = getHtmlAttribute(node.value, element, 'srcSet')
238
+
239
+ if (srcset && !updates.has(srcset.start)) {
240
+ updates.set(srcset.start, { ...srcset, value: addVersionToPublicSrcset(srcset.value, file) })
241
+ }
242
+ }
243
+ })
244
+
245
+ // Update attributes from the end to keep the offsets of the previous ones valid.
246
+ const sortedUpdates = updates
247
+ .values()
248
+ .toArray()
249
+ .toSorted((a, b) => b.start - a.start)
250
+
251
+ for (const { end, start, value } of sortedUpdates) {
252
+ node.value = `${node.value.slice(0, start)}${value}${node.value.slice(end)}`
253
+ }
205
254
 
206
255
  return SKIP
207
256
  }
208
257
 
258
+ function getHtmlAttribute(html: string, element: Element, property: string): HtmlAttribute | undefined {
259
+ const position = element.data?.position.properties?.[property]
260
+ if (position?.start.offset === undefined || position.end.offset === undefined) return
261
+
262
+ const match = htmlAttributeRegex.exec(html.slice(position.start.offset, position.end.offset))
263
+ if (!match?.groups?.['prefix'] || !match.groups['value']) return
264
+
265
+ const start = position.start.offset + match.groups['prefix'].length
266
+
267
+ return { end: start + match.groups['value'].length, start, value: match.groups['value'] }
268
+ }
269
+
209
270
  function handleImports(node: MdxjsEsm, file: VFile) {
210
271
  node.value = node.value.replaceAll(importPathRegex, (match, start, importPath: string, end) => {
211
272
  if (!importPath.startsWith('../')) return match
@@ -232,8 +293,7 @@ function addVersionToLink(link: string, file: VFile) {
232
293
 
233
294
  const segments = link.split('/')
234
295
 
235
- let slugVersionIndex = 1
236
- if (file.data.locale && segments[1] === file.data.locale) slugVersionIndex = 2
296
+ const slugVersionIndex = file.data.locale && segments[1] === file.data.locale ? 2 : 1
237
297
 
238
298
  segments.splice(slugVersionIndex, 0, file.data.version.slug)
239
299
 
@@ -252,11 +312,11 @@ function addVersionToAstroAsset(asset: string, file: VFile) {
252
312
  if (source.href.startsWith(file.data.docsDir.href)) return asset
253
313
 
254
314
  const segments = asset.split('/')
255
- segments.splice(-1, 0, file.data.version.slug)
315
+ segments.splice(Math.max(segments.length - 1, 0), 0, file.data.version.slug)
256
316
 
257
317
  addVersionAsset(file, { source, dest: new URL(segments.join('/'), file.data.url) })
258
318
 
259
- segments.splice(0, 0, '..')
319
+ segments.unshift('..')
260
320
 
261
321
  return segments.join('/')
262
322
  }
@@ -267,13 +327,17 @@ function addVersionToPublicAsset(asset: string, file: VFile) {
267
327
  const source = new URL(`.${asset}`, file.data.publicDir)
268
328
 
269
329
  const segments = asset.split('/')
270
- segments.splice(-1, 0, file.data.version.slug)
330
+ segments.splice(Math.max(segments.length - 1, 0), 0, file.data.version.slug)
271
331
 
272
332
  addVersionAsset(file, { source, dest: new URL(`.${segments.join('/')}`, file.data.publicDir) })
273
333
 
274
334
  return segments.join('/')
275
335
  }
276
336
 
337
+ function addVersionToPublicSrcset(srcset: string, file: VFile) {
338
+ return srcset.replaceAll(srcsetUrlRegex, (url) => (isPublicAsset(url) ? addVersionToPublicAsset(url, file) : url))
339
+ }
340
+
277
341
  function addVersionAsset(file: VFile, asset: VersionAsset) {
278
342
  file.data.assets?.push(asset)
279
343
  }
@@ -298,6 +362,12 @@ export interface TransformContext {
298
362
  version: Version
299
363
  }
300
364
 
365
+ interface HtmlAttribute {
366
+ end: number
367
+ start: number
368
+ value: string
369
+ }
370
+
301
371
  declare module 'vfile' {
302
372
  interface DataMap extends TransformContext {}
303
373
  }
package/libs/plugin.ts CHANGED
@@ -17,7 +17,8 @@ export function overrideComponents(
17
17
  ): StarlightUserConfig['components'] {
18
18
  const components = { ...starlightConfig.components }
19
19
  for (const { name, fallback } of overrides) {
20
- if (starlightConfig.components?.[name]) {
20
+ const override = starlightConfig.components?.[name]
21
+ if (override) {
21
22
  logger.warn(`A \`<${name}>\` component override is already defined in your Starlight configuration.`)
22
23
  logger.warn(
23
24
  `To use \`starlight-versions\`, either remove this override or manually render the content from \`starlight-versions/components/${fallback}.astro\`.`,
package/libs/starlight.ts CHANGED
@@ -59,7 +59,7 @@ export function getURLSlug(url: URL) {
59
59
  } else if (segments.at(-1)?.endsWith(htmlExt)) {
60
60
  // Remove trailing `.html`.
61
61
  const last = segments.pop()
62
- if (last) segments.push(last.slice(0, -1 * htmlExt.length))
62
+ if (last) segments.push(last.slice(0, -htmlExt.length))
63
63
  }
64
64
 
65
65
  return segments.filter(Boolean).join('/')
@@ -70,7 +70,7 @@ export function getDocLocale(slug: string, starlightConfig: StarlightUserConfig)
70
70
 
71
71
  if (locales.length === 0) return undefined
72
72
 
73
- const localeSegment = slug.split('/')[0]
73
+ const localeSegment = slug.split('/', 1)[0]
74
74
 
75
75
  if (!localeSegment) return undefined
76
76
 
@@ -89,24 +89,29 @@ export function addPrefixToSidebarConfig(
89
89
  return sidebar.map((item) => {
90
90
  if (typeof item === 'string') {
91
91
  return isExcludedInternalSidebarSlug(item, excludedSlugs) ? item : addPrefixToSlug(prefix, item)
92
- } else if ('items' in item) {
92
+ }
93
+ if ('items' in item) {
93
94
  return {
94
95
  ...item,
95
96
  items: addPrefixToSidebarConfig(prefix, item.items, excludedSlugs),
96
97
  }
97
- } else if (isSidebarLegacyAutoGroup(item)) {
98
+ }
99
+ if (isSidebarLegacyAutoGroup(item)) {
98
100
  return addPrefixToLegacyAutogeneratedGroup(prefix, item)
99
- } else if ('autogenerate' in item) {
101
+ }
102
+ if ('autogenerate' in item) {
100
103
  return {
101
104
  ...item,
102
105
  autogenerate: addPrefixToAutogenerate(prefix, item.autogenerate),
103
106
  }
104
- } else if ('slug' in item) {
107
+ }
108
+ if ('slug' in item) {
105
109
  return {
106
110
  ...item,
107
111
  slug: isExcludedInternalSidebarSlug(item.slug, excludedSlugs) ? item.slug : addPrefixToSlug(prefix, item.slug),
108
112
  }
109
- } else if (isAbsoluteLink(item.link)) {
113
+ }
114
+ if (isAbsoluteLink(item.link)) {
110
115
  return item
111
116
  }
112
117
 
package/libs/versions.ts CHANGED
@@ -82,16 +82,17 @@ export async function ensureNewVersion(
82
82
  ) {
83
83
  const docsDir = new URL('content/docs/', astroConfig.srcDir)
84
84
  const newVersion = await checkForNewVersion(config, docsDir)
85
- const locales = Object.keys(starlightConfig.locales ?? {})
86
85
 
87
86
  if (!newVersion) return
88
87
 
88
+ const locales = Object.keys(starlightConfig.locales ?? {})
89
+
89
90
  const excludedDocs = await getExcludedDocs(
90
91
  config,
91
92
  locales.filter((locale) => locale !== 'root'),
92
93
  docsDir,
93
94
  )
94
- const excludedSlugs = [...excludedDocs.values()].toSorted()
95
+ const excludedSlugs = excludedDocs.values().toArray().toSorted()
95
96
 
96
97
  const assets: VersionAsset[] = []
97
98
 
@@ -112,7 +113,7 @@ export async function ensureNewVersion(
112
113
  }
113
114
 
114
115
  // Skip root version directories.
115
- if (entry.name in config.versionsBySlug) return true
116
+ if (Object.hasOwn(config.versionsBySlug, entry.name)) return true
116
117
 
117
118
  const localeDir = locales.find((locale) => locale === entry.name)
118
119
 
@@ -213,12 +214,8 @@ export function getVersionURL(
213
214
  const canonicalSlug = getCanonicalSlug(config, starlightConfig, url)
214
215
 
215
216
  if (version && isSlugExcludedFromVersion(config, version, canonicalSlug)) {
216
- return getVersionURL(
217
- { ...config, current: { ...config.current, redirect: 'same-page' } },
218
- starlightConfig,
219
- url,
220
- undefined,
221
- )
217
+ config = { ...config, current: { ...config.current, redirect: 'same-page' } }
218
+ version = undefined
222
219
  }
223
220
 
224
221
  const versionURL = new URL(url)
@@ -236,7 +233,7 @@ export function getVersionURL(
236
233
  let localeSegment: string | undefined
237
234
 
238
235
  const isHTML = getExtension(versionURL.pathname) === '.html'
239
- const [, firstSegment, secondSegment] = versionURL.pathname.split('/')
236
+ const [, firstSegment, secondSegment] = versionURL.pathname.split('/', 3)
240
237
 
241
238
  if (starlightConfig.isMultilingual || starlightConfig.locales) {
242
239
  const versionOrLocale = firstSegment?.replace('.html', '')
@@ -254,11 +251,11 @@ export function getVersionURL(
254
251
  const isRootHTML = baseSegment && getExtension(baseSegment) === '.html'
255
252
  const baseSlug = baseSegment && isRootHTML ? stripExtension(baseSegment) : baseSegment
256
253
 
257
- if (baseSlug && baseSlug in config.versionsBySlug) {
254
+ if (baseSlug && Object.hasOwn(config.versionsBySlug, baseSlug)) {
258
255
  if (versionSlug) {
259
256
  versionURL.pathname =
260
257
  versionRedirect === 'same-page'
261
- ? versionURL.pathname.replace(baseSlug, versionSlug)
258
+ ? versionURL.pathname.replace(baseSlug, () => versionSlug)
262
259
  : `${versionSlug}${isHTML ? '.html' : '/'}`
263
260
  } else if (isRootHTML) {
264
261
  versionURL.pathname = '/index.html'
@@ -458,7 +455,7 @@ export function getVersionFromPaginationLink(
458
455
 
459
456
  if (locale) {
460
457
  // Remove the locale segment if the current locale is not a root locale.
461
- segments.splice(0, 1)
458
+ segments.shift()
462
459
  }
463
460
 
464
461
  const versionSegment = segments[0]
@@ -488,11 +485,14 @@ async function getExcludedDocs(config: StarlightVersionsConfig, locales: string[
488
485
  const docPath = getDocPath(docsDir, entryURL)
489
486
 
490
487
  if (isDirectory) {
491
- const [firstSegment, secondSegment] = docPath.split('/')
488
+ const [firstSegment, secondSegment] = docPath.split('/', 2)
492
489
 
493
490
  if (
494
- (firstSegment && firstSegment in config.versionsBySlug) ||
495
- (firstSegment && locales.includes(firstSegment) && secondSegment && secondSegment in config.versionsBySlug)
491
+ (firstSegment && Object.hasOwn(config.versionsBySlug, firstSegment)) ||
492
+ (firstSegment &&
493
+ secondSegment &&
494
+ locales.includes(firstSegment) &&
495
+ Object.hasOwn(config.versionsBySlug, secondSegment))
496
496
  ) {
497
497
  continue
498
498
  }
@@ -505,7 +505,7 @@ async function getExcludedDocs(config: StarlightVersionsConfig, locales: string[
505
505
  if (
506
506
  !entry.isFile() ||
507
507
  !docsExtensions.has(getExtension(docPath)) ||
508
- !config.exclude.some((excludeGlob) => path.posix.matchesGlob(docPath, excludeGlob))
508
+ config.exclude.every((excludeGlob) => !path.posix.matchesGlob(docPath, excludeGlob))
509
509
  ) {
510
510
  continue
511
511
  }
@@ -532,12 +532,15 @@ async function checkForNewVersion(config: StarlightVersionsConfig, docsDir: URL)
532
532
  }
533
533
 
534
534
  for (const version of config.versions) {
535
- if (!docsDirDirectories.has(version.slug)) {
536
- if (newVersion) {
537
- throw new Error('Only one new version can be configured at a time.')
538
- }
539
- newVersion = version
535
+ if (docsDirDirectories.has(version.slug)) {
536
+ continue
537
+ }
538
+
539
+ if (newVersion) {
540
+ throw new Error('Only one new version can be configured at a time.')
540
541
  }
542
+
543
+ newVersion = version
541
544
  }
542
545
 
543
546
  return newVersion
package/middleware.ts CHANGED
@@ -74,7 +74,7 @@ function getPaginationFromSidebar(
74
74
  route.pagination.prev,
75
75
  )
76
76
  const next = applyPaginationConfig(
77
- currentIndex > -1 ? entries[currentIndex + 1] : undefined,
77
+ currentIndex === -1 ? undefined : entries[currentIndex + 1],
78
78
  starlightConfig.pagination,
79
79
  route.entry.data.next,
80
80
  route.pagination.next,
@@ -93,11 +93,12 @@ function applyPaginationConfig(
93
93
  // Explicitly remove the link.
94
94
  if (config === false) return undefined
95
95
  // Use the generated link if any.
96
- else if (config === true) return link
96
+ if (config === true) return link
97
97
  // If a link exists, update its label if needed.
98
- else if (typeof config === 'string' && link) {
98
+ if (typeof config === 'string' && link) {
99
99
  return { ...link, label: config }
100
- } else if (typeof config === 'object') {
100
+ }
101
+ if (typeof config === 'object') {
101
102
  if (link) {
102
103
  // If a link exists, update both its label and href if needed.
103
104
  return {
@@ -107,7 +108,8 @@ function applyPaginationConfig(
107
108
  // Explicitly remove sidebar link attributes for prev/next links.
108
109
  attrs: {},
109
110
  }
110
- } else if (config.link && config.label) {
111
+ }
112
+ if (config.link && config.label) {
111
113
  // If there is no link and the frontmatter contains both a URL and a label, use it.
112
114
  return existing
113
115
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-versions",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "license": "MIT",
5
5
  "description": "Starlight plugin to version documentation pages.",
6
6
  "author": "HiDeoo <github@hideoo.dev> (https://hideoo.dev)",
@@ -22,6 +22,7 @@
22
22
  "dependencies": {
23
23
  "@pagefind/default-ui": "^1.3.0",
24
24
  "github-slugger": "^2.0.0",
25
+ "hast-util-from-html": "^2.0.3",
25
26
  "mdast-util-mdx-jsx": "^3.2.0",
26
27
  "mdast-util-mdxjs-esm": "^2.0.1",
27
28
  "remark": "^15.0.1",
@@ -33,6 +34,7 @@
33
34
  "yaml": "^2.8.2"
34
35
  },
35
36
  "devDependencies": {
37
+ "@types/hast": "^3.0.4",
36
38
  "@types/mdast": "^4.0.4",
37
39
  "fast-glob": "^3.3.3",
38
40
  "vitest": "^4.0.18"
@@ -40,9 +42,6 @@
40
42
  "peerDependencies": {
41
43
  "@astrojs/starlight": ">=0.39.0"
42
44
  },
43
- "engines": {
44
- "node": ">=22.12.0"
45
- },
46
45
  "publishConfig": {
47
46
  "access": "public"
48
47
  },
@@ -64,6 +63,6 @@
64
63
  "bugs": "https://github.com/HiDeoo/starlight-versions/issues",
65
64
  "scripts": {
66
65
  "test": "vitest",
67
- "lint": "eslint . --cache --max-warnings=0"
66
+ "lint": "eslint . --max-warnings=0"
68
67
  }
69
68
  }