@jaamd/jaad 0.11.0 → 1.0.0

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.
Files changed (138) hide show
  1. package/README.md +8 -2
  2. package/dist/jaad.css +232 -347
  3. package/docs/00-overview.md +25 -0
  4. package/docs/01-getting-started/01-installation.md +216 -0
  5. package/docs/01-getting-started/02-pages.md +109 -0
  6. package/docs/01-getting-started/03-deployment.md +100 -0
  7. package/docs/02-configurations/01-reference.md +215 -0
  8. package/docs/02-configurations/02-frontmatter.md +105 -0
  9. package/docs/02-configurations/03-i18n.md +210 -0
  10. package/docs/02-configurations/04-search.md +58 -0
  11. package/docs/02-configurations/05-themes.md +104 -0
  12. package/docs/02-configurations/06-styles.md +156 -0
  13. package/docs/02-configurations/07-custom-pages.md +119 -0
  14. package/docs/02-configurations/08-advanced-layout.md +139 -0
  15. package/docs/03-upgrading.md +208 -0
  16. package/index.ts +8 -22
  17. package/package.json +10 -9
  18. package/site.ts +18 -1
  19. package/src/config.ts +25 -176
  20. package/src/{advanced.ts → docs/advanced.ts} +6 -4
  21. package/src/docs/collection.ts +109 -0
  22. package/src/docs/components/Breadcrumbs.astro +29 -0
  23. package/src/docs/components/DocsMobileNav.astro +90 -0
  24. package/src/docs/components/DocsNavTree.astro +57 -0
  25. package/src/docs/components/DocsPage.astro +66 -0
  26. package/src/{components/docs → docs/components}/DocsPageNav.astro +4 -4
  27. package/src/docs/components/DocsSidebarLeft.astro +53 -0
  28. package/src/{components/docs → docs/components}/PageActions.astro +10 -8
  29. package/src/{components/docs → docs/components}/StructuredData.astro +3 -3
  30. package/src/{components/docs → docs/components}/TableOfContents.astro +20 -22
  31. package/src/docs/config.ts +167 -0
  32. package/src/docs/content.ts +36 -0
  33. package/src/docs/endpoints.ts +89 -0
  34. package/src/docs/integration.ts +249 -0
  35. package/src/{layouts → docs/layouts}/DefaultDocsFrame.astro +7 -5
  36. package/src/{layouts → docs/layouts}/Docs.astro +44 -43
  37. package/src/docs/layouts/Page.astro +18 -0
  38. package/src/docs/link-plugins.ts +52 -0
  39. package/src/docs/links.ts +42 -0
  40. package/src/{locale-tree.ts → docs/locale-tree.ts} +1 -1
  41. package/src/docs/model/ids.ts +87 -0
  42. package/src/docs/model/rendered.ts +185 -0
  43. package/src/docs/model/tree.ts +124 -0
  44. package/src/docs/model/validate.ts +75 -0
  45. package/src/docs/navigation.ts +75 -0
  46. package/src/{routes → docs/routes}/404.astro +1 -1
  47. package/src/docs/routes/docs-index.astro +19 -0
  48. package/src/docs/routes/docs-slug.astro +54 -0
  49. package/src/{routes → docs/routes}/docs-slug.md.ts +8 -10
  50. package/src/{routes → docs/routes}/llms-locale.txt.ts +1 -1
  51. package/src/{routes → docs/routes}/llms.txt.ts +1 -1
  52. package/src/{routes → docs/routes}/search-index-locale.json.ts +1 -1
  53. package/src/{routes → docs/routes}/search-index.json.ts +1 -1
  54. package/src/docs/runtime.ts +17 -0
  55. package/src/docs/search-index.ts +39 -0
  56. package/src/docs/styles/components/breadcrumbs.css +26 -0
  57. package/src/docs/styles/components/docs-mobile-nav.css +47 -0
  58. package/src/docs/styles/components/docs-page-nav.css +41 -0
  59. package/src/docs/styles/components/docs-sidebar.css +59 -0
  60. package/src/docs/styles/components/page-actions.css +51 -0
  61. package/src/docs/styles/components/toc.css +38 -0
  62. package/src/docs/styles/components.css +7 -0
  63. package/src/docs/styles/docs.css +81 -0
  64. package/src/{@types/docs.ts → docs/types.ts} +8 -16
  65. package/src/{urls.ts → docs/urls.ts} +2 -16
  66. package/src/env.d.ts +1 -1
  67. package/src/integration.ts +15 -249
  68. package/src/{components → site/components}/Footer.astro +1 -1
  69. package/src/{components → site/components}/Header.astro +14 -11
  70. package/src/{components → site/components}/LocaleSwitcher.astro +2 -2
  71. package/src/{components → site/components}/NavMobile.astro +1 -1
  72. package/src/{components → site/components}/SearchPalette.astro +160 -79
  73. package/src/{components → site/components}/ThemeSwitcher.astro +1 -1
  74. package/src/site/config.ts +149 -0
  75. package/src/{infer.ts → site/infer.ts} +3 -3
  76. package/src/site/integration.ts +59 -0
  77. package/src/{layouts/Page.astro → site/layouts/Shell.astro} +6 -3
  78. package/src/{locales.ts → site/locales.ts} +9 -1
  79. package/src/{docs-config.ts → site/runtime.ts} +1 -12
  80. package/src/site/search.ts +227 -0
  81. package/src/{social.ts → site/social.ts} +2 -2
  82. package/src/site/styles/components/header.css +31 -0
  83. package/src/{styles → site/styles}/components/search.css +21 -1
  84. package/src/{styles → site/styles}/components/shell.css +4 -0
  85. package/src/site/styles/components.css +10 -0
  86. package/src/{styles → site/styles}/global.css +4 -26
  87. package/src/{themes/index.ts → site/themes.ts} +1 -1
  88. package/src/{ui → site/ui}/en.ts +3 -5
  89. package/src/{ui → site/ui}/it.ts +2 -4
  90. package/src/site/urls.ts +14 -0
  91. package/src/{virtual.ts → site/virtual.ts} +12 -18
  92. package/src/styles.css +5 -0
  93. package/src/collection.ts +0 -97
  94. package/src/components/ScrollToTop.astro +0 -43
  95. package/src/components/docs/Breadcrumbs.astro +0 -36
  96. package/src/components/docs/DocsMobileNav.astro +0 -93
  97. package/src/components/docs/DocsPage.astro +0 -68
  98. package/src/components/docs/DocsSidebarLeft.astro +0 -82
  99. package/src/content.ts +0 -42
  100. package/src/navigation.ts +0 -97
  101. package/src/routes/docs-index.astro +0 -19
  102. package/src/routes/docs-slug.astro +0 -58
  103. package/src/styles/components/breadcrumbs.css +0 -13
  104. package/src/styles/components/docs-mobile-nav.css +0 -9
  105. package/src/styles/components/docs-page-nav.css +0 -31
  106. package/src/styles/components/docs-sidebar.css +0 -42
  107. package/src/styles/components/header.css +0 -20
  108. package/src/styles/components/page-actions.css +0 -13
  109. package/src/styles/components/scroll-to-top.css +0 -11
  110. package/src/styles/components/toc.css +0 -21
  111. package/src/styles/docs.css +0 -112
  112. package/src/utils/docs.ts +0 -300
  113. package/src/utils/endpoints.ts +0 -80
  114. package/src/utils/search.ts +0 -83
  115. /package/src/{styles → docs/styles}/components/docs-page.css +0 -0
  116. /package/src/{styles → docs/styles}/jaamd.css +0 -0
  117. /package/src/{components → site/components}/Head.astro +0 -0
  118. /package/src/{components → site/components}/Nav.astro +0 -0
  119. /package/src/{components → site/components}/SocialLinks.astro +0 -0
  120. /package/src/{components → site/components}/locale-switcher.ts +0 -0
  121. /package/src/{fonts → site/fonts}/OFL.txt +0 -0
  122. /package/src/{fonts → site/fonts}/inter.woff2 +0 -0
  123. /package/src/{fonts → site/fonts}/merriweather-italic.woff2 +0 -0
  124. /package/src/{fonts → site/fonts}/merriweather.woff2 +0 -0
  125. /package/src/{fonts.ts → site/fonts.ts} +0 -0
  126. /package/src/{forges.ts → site/forges.ts} +0 -0
  127. /package/src/{utils → site}/helpers.ts +0 -0
  128. /package/src/{styles → site/styles}/components/footer.css +0 -0
  129. /package/src/{styles → site/styles}/components/locale-switcher.css +0 -0
  130. /package/src/{styles → site/styles}/components/nav-mobile.css +0 -0
  131. /package/src/{styles → site/styles}/components/nav.css +0 -0
  132. /package/src/{styles → site/styles}/components/not-found.css +0 -0
  133. /package/src/{styles → site/styles}/components/social-links.css +0 -0
  134. /package/src/{styles → site/styles}/components/theme-switcher.css +0 -0
  135. /package/src/{styles → site/styles}/jaamd-forward.css +0 -0
  136. /package/src/{styles → site/styles}/jaamd-reverse.css +0 -0
  137. /package/src/{styles → site/styles}/tailwind.css +0 -0
  138. /package/src/{ui.ts → site/ui.ts} +0 -0
@@ -1,235 +1,24 @@
1
- import type { JaadResolvedConfig, resolveStylesheets } from './config.ts'
2
- import type { AstroIntegration, AstroUserConfig } from 'astro'
3
- import { existsSync, readFileSync, readdirSync } from 'node:fs'
4
- import { createRequire } from 'node:module'
5
- import { join, relative, resolve } from 'node:path'
6
- import { fileURLToPath, pathToFileURL } from 'node:url'
7
- import { fonts } from './fonts.ts'
8
- import { prefixedLocales } from './locales.ts'
9
- import { docsPageHref, normaliseBasePath, type DocsUrlOptions } from './urls.ts'
10
- import { getCleanSlug, getDocCollectionId, sortDocPages, splitDocLocale } from './utils/docs.ts'
11
- import { jaadVirtualPlugin } from './virtual.ts'
12
-
13
- type Stylesheets = ReturnType<typeof resolveStylesheets>
14
- type VitePlugins = NonNullable<NonNullable<AstroUserConfig['vite']>['plugins']>
15
-
16
- const NOT_INSTALLED = new Set(['MODULE_NOT_FOUND', 'ERR_PACKAGE_PATH_NOT_EXPORTED'])
17
-
18
- /** The site's own `@tailwindcss/vite`, unless it already registered one. */
19
- export function loadTailwindPlugin(root: string, registered: VitePlugins | undefined): VitePlugins {
20
- const plugins = ([registered] as unknown[]).flat(Infinity) as ({
21
- name?: string
22
- } | null)[]
23
- if (plugins.some((p) => p?.name?.startsWith('@tailwindcss/vite'))) return []
24
-
25
- const load = createRequire(join(root, 'package.json'))
26
- let tailwindcss: () => VitePlugins
27
- try {
28
- load.resolve('tailwindcss/package.json')
29
- ;({ default: tailwindcss } = load('@tailwindcss/vite') as {
30
- default: () => VitePlugins
31
- })
32
- } catch (error) {
33
- if (!NOT_INSTALLED.has((error as { code?: string }).code ?? '')) throw error
34
- throw new Error(
35
- 'jaad: `tailwind: true` needs Tailwind in your project:\n npm i -D tailwindcss @tailwindcss/vite',
36
- { cause: error },
37
- )
38
- }
39
- return tailwindcss()
40
- }
41
-
42
- /** One per locale: each has its own first page, and its own redirect. */
43
- export interface OpeningPage {
44
- locale?: string
45
- slug: string
46
- }
47
-
48
- export interface IntegrationState {
49
- deploymentBase: string
50
- openingPages: OpeningPage[]
51
- docsFrame: string
52
- }
53
-
54
- const INDEX_PAGE_NAMES = ['index.astro', 'index.md', 'index.mdx', 'index.html', 'index.js', 'index.ts']
55
-
56
- const NOT_FOUND_PAGE_NAMES = ['404.astro', '404.md', '404.mdx', '404.html', '404.js', '404.ts']
57
-
58
- const routeEntrypoint = (file: string) => fileURLToPath(new URL(`./routes/${file}`, import.meta.url))
59
-
60
- export function shouldInjectNotFound(docsBase: string, srcDir: URL): boolean {
61
- return docsBase === '' && !NOT_FOUND_PAGE_NAMES.some((name) => existsSync(new URL(`pages/${name}`, srcDir)))
62
- }
63
-
64
- /** The two fields that move the opening page. The content collection is not
65
- * available this early, so `order` and `draft` are read from the file. */
66
- function readFrontmatter(source: string): { order?: number; draft?: boolean } {
67
- const block = /^---\r?\n([\s\S]*?)\r?\n---/.exec(source)
68
- if (!block) return {}
69
-
70
- const order = /^order:\s*(-?\d+)\s*$/m.exec(block[1])
71
- const draft = /^draft:\s*(true|false)\s*$/m.exec(block[1])
72
- return {
73
- order: order ? Number(order[1]) : undefined,
74
- draft: draft ? draft[1] === 'true' : undefined,
75
- }
76
- }
77
-
78
- export function findOpeningPages(dir: string, locales: string[] = []): OpeningPage[] {
79
- if (!existsSync(dir)) return []
80
-
81
- const pages: {
82
- id: string
83
- localeId: string
84
- locale: string | null
85
- data: { order?: number }
86
- }[] = []
87
-
88
- const visit = (current: string) => {
89
- for (const entry of readdirSync(current, { withFileTypes: true }).toSorted((a, b) =>
90
- a.name.localeCompare(b.name),
91
- )) {
92
- const file = join(current, entry.name)
93
- if (entry.isDirectory()) visit(file)
94
- else if (entry.isFile() && entry.name.endsWith('.md')) {
95
- const front = readFrontmatter(readFileSync(file, 'utf8'))
96
- if (front.draft) continue
97
-
98
- const id = getDocCollectionId(relative(dir, file))
99
- const split = splitDocLocale(id, locales)
100
- pages.push({
101
- id,
102
- localeId: split.id,
103
- locale: split.locale,
104
- data: { order: front.order },
105
- })
106
- }
107
- }
108
- }
109
-
110
- visit(dir)
111
-
112
- const groups = locales.length > 0 ? locales : [null]
113
- return groups.flatMap((locale) => {
114
- const first = sortDocPages(pages.filter((page) => page.locale === locale))[0]
115
- // Indexed access is typed as always present; a locale can have no pages.
116
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
117
- if (!first) return []
118
- return [{ locale: locale ?? undefined, slug: getCleanSlug(first.localeId) }]
119
- })
120
- }
121
-
122
- export function getInjectedRoutes(docsBase: string, locales: string[] = []): [string, string][] {
123
- // `[...slug]` already absorbs "it/guide" and "it". Only the two fixed
124
- // endpoints need a route of their own, and `[locale]` hands them the code.
125
- const perLocale: [string, string][] =
126
- locales.length > 0
127
- ? [
128
- [`${docsBase}/[locale]/search-index.json`, 'search-index-locale.json.ts'],
129
- [`${docsBase}/[locale]/llms.txt`, 'llms-locale.txt.ts'],
130
- ]
131
- : []
132
-
133
- return [
134
- [docsBase, 'docs-index.astro'],
135
- [`${docsBase}/search-index.json`, 'search-index.json.ts'],
136
- [`${docsBase}/llms.txt`, 'llms.txt.ts'],
137
- ...perLocale,
138
- [`${docsBase}/[...slug]`, 'docs-slug.astro'],
139
- [`${docsBase}/[...slug].md`, 'docs-slug.md.ts'],
140
- ]
141
- }
142
-
143
- export function createIntegrationState(): IntegrationState {
144
- return {
145
- deploymentBase: '',
146
- openingPages: [],
147
- docsFrame: fileURLToPath(new URL('./layouts/DefaultDocsFrame.astro', import.meta.url)),
148
- }
149
- }
150
-
151
- export function createCoreIntegration(
1
+ import type { JaadResolvedConfig } from './config.ts'
2
+ import type { IntegrationState } from './docs/integration.ts'
3
+ import type { SiteStylesheets } from './site/virtual.ts'
4
+ import type { AstroIntegration } from 'astro'
5
+ import { setupDocs } from './docs/integration.ts'
6
+ import { setupSite } from './site/integration.ts'
7
+ import { normaliseBasePath } from './site/urls.ts'
8
+
9
+ /** The site, then the docs on it. */
10
+ export function createJaadIntegration(
152
11
  config: JaadResolvedConfig,
153
- stylesheets: Stylesheets,
12
+ stylesheets: SiteStylesheets,
154
13
  state: IntegrationState,
155
- discoverOpeningPage: boolean,
14
+ discoverAliases: boolean,
156
15
  ): AstroIntegration {
157
16
  return {
158
17
  name: 'jaad',
159
18
  hooks: {
160
- 'astro:config:setup': ({ config: astroConfig, updateConfig, injectRoute, addWatchFile, logger }) => {
161
- updateConfig({
162
- vite: {
163
- plugins: [
164
- jaadVirtualPlugin(
165
- config,
166
- stylesheets.user,
167
- stylesheets.theme,
168
- stylesheets.bridge,
169
- stylesheets.tailwind,
170
- () => state.docsFrame,
171
- ),
172
- ...(config.tailwind
173
- ? [loadTailwindPlugin(fileURLToPath(astroConfig.root), astroConfig.vite.plugins)]
174
- : []),
175
- ],
176
- // Package .astro sources must reach the Astro compiler.
177
- ssr: { noExternal: ['@jaamd/jaad'] },
178
- },
179
- fonts,
180
- })
181
-
182
- for (const [pattern, entrypoint] of getInjectedRoutes(
183
- config.docsBase,
184
- prefixedLocales(config.docsLocales, config.defaultLocale),
185
- )) {
186
- injectRoute({
187
- pattern,
188
- entrypoint: routeEntrypoint(entrypoint),
189
- prerender: true,
190
- })
191
- }
192
-
193
- const customDocsFrame = new URL('jaad/DocsFrame.astro', astroConfig.srcDir)
194
- if (existsSync(customDocsFrame)) {
195
- state.docsFrame = fileURLToPath(customDocsFrame)
196
- addWatchFile(customDocsFrame)
197
- }
198
-
199
- if (shouldInjectNotFound(config.docsBase, astroConfig.srcDir)) {
200
- injectRoute({
201
- pattern: '/404',
202
- entrypoint: routeEntrypoint('404.astro'),
203
- prerender: true,
204
- })
205
- }
206
-
207
- const docsDirPath = resolve(fileURLToPath(astroConfig.root), config.docsDir)
208
- const docsDir = pathToFileURL(docsDirPath)
209
- if (!existsSync(docsDir)) {
210
- logger.warn(
211
- `${config.docsDir} does not exist, so the site has no pages. ` +
212
- 'Create it, or point docsDir at your markdown.',
213
- )
214
- } else if (discoverOpeningPage) {
215
- state.openingPages = findOpeningPages(
216
- docsDirPath,
217
- config.docsLocales.map((locale) => locale.code),
218
- )
219
- }
220
- addWatchFile(docsDir)
221
-
222
- if (config.docsBase === '') {
223
- const ownIndex = INDEX_PAGE_NAMES.find((name) =>
224
- existsSync(new URL(`pages/${name}`, astroConfig.srcDir)),
225
- )
226
- if (ownIndex) {
227
- logger.warn(
228
- `${ownIndex} owns the site root while JAAD docs are mounted there. ` +
229
- 'Set routeBase: "/docs" to keep a landing page and mount the docs below it.',
230
- )
231
- }
232
- }
19
+ 'astro:config:setup': (params) => {
20
+ setupSite(params, config, stylesheets)
21
+ setupDocs(params, config, state, discoverAliases)
233
22
  },
234
23
 
235
24
  'astro:config:done': ({ config: astroConfig, injectTypes }) => {
@@ -245,26 +34,3 @@ export function createCoreIntegration(
245
34
  },
246
35
  }
247
36
  }
248
-
249
- const withoutTrailingSlash = (value: string) => (value.length > 1 ? value.replace(/\/$/, '') : value)
250
-
251
- export function createSitemapFilter(
252
- docsBase: string,
253
- state: Pick<IntegrationState, 'deploymentBase' | 'openingPages'>,
254
- defaultLocale?: string,
255
- ): (page: string) => boolean {
256
- return (page) => {
257
- if (state.openingPages.length === 0) return true
258
-
259
- const pathname = withoutTrailingSlash(new URL(page).pathname)
260
- return !state.openingPages.some((opening) => {
261
- const urls: DocsUrlOptions = {
262
- docsBase,
263
- deploymentBase: state.deploymentBase,
264
- locale: opening.locale,
265
- defaultLocale,
266
- }
267
- return pathname === withoutTrailingSlash(docsPageHref(opening.slug, urls))
268
- })
269
- }
270
- }
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import config from 'virtual:jaad/config'
3
3
 
4
- const CREDIT = 'Built with <a href="https://jaamd.org/docs/jaad" rel="noopener">JAAD</a>.'
4
+ const CREDIT = 'Built with <a href="https://docs.jaamd.org/jaad" rel="noopener">JAAD</a>.'
5
5
 
6
6
  // Unset is the credit alone; `footer: false` removes it.
7
7
  const line = config.footer === undefined ? ':credit' : config.footer
@@ -1,7 +1,7 @@
1
1
  ---
2
+ import type { LocaleAlternate } from '../locales.ts'
2
3
  import config from 'virtual:jaad/config'
3
- import { getLocaleAlternates, type LocaleAlternate } from '../collection.ts'
4
- import { DEFAULT_LOCALE, ui } from '../docs-config.ts'
4
+ import { DEFAULT_LOCALE, ui } from '../runtime.ts'
5
5
  import { withDeploymentBase } from '../urls.ts'
6
6
  import LocaleSwitcher from './LocaleSwitcher.astro'
7
7
  import Nav from './Nav.astro'
@@ -11,21 +11,24 @@ import SocialLinks from './SocialLinks.astro'
11
11
  import ThemeSwitcher from './ThemeSwitcher.astro'
12
12
 
13
13
  interface Props {
14
- /** Whether this page is part of the docs. */
15
- inDocs?: boolean
16
14
  locale?: string
17
- alternates?: LocaleAlternate[]
15
+ /** The page in every locale, for the switcher. */
16
+ alternates: LocaleAlternate[]
17
+ search: {
18
+ /** `search-index.json` of the reader's locale. */
19
+ indexUrl: string
20
+ /** The full search field on wide screens, not just its icon. */
21
+ prominent?: boolean
22
+ }
18
23
  }
19
24
 
20
- const { inDocs = false, locale = DEFAULT_LOCALE } = Astro.props
21
- // A page of the reader's own has no translations: offer each locale's opener.
22
- const alternates = Astro.props.alternates ?? (await getLocaleAlternates(''))
25
+ const { locale = DEFAULT_LOCALE, alternates, search } = Astro.props
23
26
  const t = ui(locale)
24
- const homeHref = withDeploymentBase('/', import.meta.env.BASE_URL)
27
+ const homeHref = withDeploymentBase(config.homeLink, import.meta.env.BASE_URL)
25
28
  const logoSrc = config.logo ? withDeploymentBase(config.logo, import.meta.env.BASE_URL) : null
26
29
  ---
27
30
 
28
- <header class="jaad-chrome" transition:persist>
31
+ <header class="jaad-header" transition:persist>
29
32
  <div class="jaad-header-inner">
30
33
  <div class="jaad-header-row">
31
34
  <span class="jaad-brand font-brand">
@@ -35,7 +38,7 @@ const logoSrc = config.logo ? withDeploymentBase(config.logo, import.meta.env.BA
35
38
  </span>
36
39
 
37
40
  <div class="jaad-header-actions">
38
- <SearchPalette inDocs={inDocs} locale={locale} />
41
+ <SearchPalette indexUrl={search.indexUrl} prominent={search.prominent} {locale} />
39
42
  <Nav />
40
43
  <SocialLinks />
41
44
  {config.appearance === 'auto' && <ThemeSwitcher {locale} />}
@@ -1,6 +1,6 @@
1
1
  ---
2
- import type { LocaleAlternate } from '../collection.ts'
3
- import { ui } from '../docs-config.ts'
2
+ import type { LocaleAlternate } from '../locales.ts'
3
+ import { ui } from '../runtime.ts'
4
4
 
5
5
  interface Props {
6
6
  alternates: LocaleAlternate[]
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import config from 'virtual:jaad/config'
3
- import { ui } from '../docs-config.ts'
3
+ import { ui } from '../runtime.ts'
4
4
  import { withDeploymentBase } from '../urls.ts'
5
5
 
6
6
  const links = config.nav