@astrojs/starlight 0.5.5 → 0.6.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.
- package/404.astro +17 -17
- package/CHANGELOG.md +52 -2
- package/components/CallToAction.astro +32 -35
- package/components/ContentPanel.astro +18 -18
- package/components/EditLink.astro +23 -23
- package/components/FallbackContentNotice.astro +16 -18
- package/components/Footer.astro +28 -34
- package/components/HeadSEO.astro +69 -75
- package/components/Header.astro +48 -55
- package/components/Hero.astro +108 -121
- package/components/Icons.ts +73 -71
- package/components/LanguageSelect.astro +31 -31
- package/components/LastUpdated.astro +16 -18
- package/components/MarkdownContent.astro +105 -117
- package/components/MobileMenuToggle.astro +80 -80
- package/components/PrevNextLinks.astro +60 -60
- package/components/RightSidebar.astro +17 -17
- package/components/RightSidebarPanel.astro +41 -41
- package/components/Search.astro +293 -303
- package/components/Select.astro +64 -65
- package/components/Sidebar.astro +23 -23
- package/components/SidebarSublist.astro +96 -95
- package/components/SiteTitle.astro +65 -63
- package/components/SkipLink.astro +17 -17
- package/components/SocialIcons.astro +36 -34
- package/components/TableOfContents/MobileTableOfContents.astro +124 -124
- package/components/TableOfContents/TableOfContentsList.astro +64 -69
- package/components/TableOfContents/generateToC.ts +41 -43
- package/components/TableOfContents/starlight-toc.ts +84 -90
- package/components/TableOfContents.astro +8 -8
- package/components/ThemeProvider.astro +28 -32
- package/components/ThemeSelect.astro +66 -71
- package/global.d.ts +3 -3
- package/index.astro +1 -1
- package/index.ts +55 -111
- package/integrations/asides.ts +109 -111
- package/integrations/sitemap.ts +10 -13
- package/integrations/virtual-user-config.ts +52 -0
- package/layout/Page.astro +84 -72
- package/layout/PageFrame.astro +67 -69
- package/layout/TwoColumnContent.astro +40 -42
- package/package.json +8 -3
- package/schema.ts +126 -113
- package/schemas/favicon.ts +40 -0
- package/schemas/head.ts +12 -23
- package/schemas/i18n.ts +146 -160
- package/schemas/logo.ts +22 -22
- package/schemas/prevNextLink.ts +14 -14
- package/schemas/tableOfContents.ts +14 -18
- package/style/asides.css +27 -27
- package/style/props.css +168 -173
- package/style/reset.css +13 -13
- package/style/shiki.css +11 -11
- package/style/util.css +30 -30
- package/translations/fr.json +21 -21
- package/translations/index.ts +4 -4
- package/translations/it.json +20 -20
- package/translations/tr.json +1 -1
- package/translations/zh.json +0 -1
- package/types.ts +1 -1
- package/user-components/Card.astro +50 -54
- package/user-components/CardGrid.astro +21 -21
- package/user-components/Icon.astro +19 -21
- package/user-components/TabItem.astro +3 -3
- package/user-components/Tabs.astro +113 -117
- package/user-components/rehype-tabs.ts +72 -72
- package/utils/base.ts +6 -6
- package/utils/git.ts +55 -55
- package/utils/head.ts +56 -54
- package/utils/i18n.ts +3 -3
- package/utils/localizedUrl.ts +25 -25
- package/utils/navigation.ts +225 -203
- package/utils/routing.ts +74 -85
- package/utils/slugs.ts +41 -51
- package/utils/translations.ts +26 -32
- package/utils/user-config.ts +278 -278
- package/virtual.d.ts +8 -8
package/utils/navigation.ts
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
import { basename, dirname } from 'node:path';
|
|
2
2
|
import config from 'virtual:starlight/user-config';
|
|
3
|
+
import type { PrevNextLinkConfig } from '../schemas/prevNextLink';
|
|
3
4
|
import { pathWithBase } from './base';
|
|
4
5
|
import { pickLang } from './i18n';
|
|
5
|
-
import { type Route
|
|
6
|
+
import { getLocaleRoutes, type Route } from './routing';
|
|
6
7
|
import { localeToLang, slugToPathname } from './slugs';
|
|
7
|
-
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
SidebarLinkItem,
|
|
11
|
-
} from './user-config';
|
|
12
|
-
import type { PrevNextLinkConfig } from '../schemas/prevNextLink';
|
|
8
|
+
import type { AutoSidebarGroup, SidebarItem, SidebarLinkItem } from './user-config';
|
|
9
|
+
|
|
10
|
+
const DirKey = Symbol('DirKey');
|
|
13
11
|
|
|
14
12
|
export interface Link {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
type: 'link';
|
|
14
|
+
label: string;
|
|
15
|
+
href: string;
|
|
16
|
+
isCurrent: boolean;
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
interface Group {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
type: 'group';
|
|
21
|
+
label: string;
|
|
22
|
+
entries: (Link | Group)[];
|
|
23
|
+
collapsed: boolean;
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
export type SidebarEntry = Link | Group;
|
|
@@ -31,58 +29,70 @@ export type SidebarEntry = Link | Group;
|
|
|
31
29
|
* A representation of the route structure. For each object entry:
|
|
32
30
|
* if it’s a folder, the key is the directory name, and value is the directory
|
|
33
31
|
* content; if it’s a route entry, the key is the last segment of the route, and value
|
|
34
|
-
* is the
|
|
32
|
+
* is the full entry.
|
|
35
33
|
*/
|
|
36
34
|
interface Dir {
|
|
37
|
-
|
|
35
|
+
[DirKey]: undefined;
|
|
36
|
+
[item: string]: Dir | Route;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Create a new directory object. */
|
|
40
|
+
function makeDir(): Dir {
|
|
41
|
+
const dir = {} as Dir;
|
|
42
|
+
// Add DirKey as a non-enumerable property so that `Object.entries(dir)` ignores it.
|
|
43
|
+
Object.defineProperty(dir, DirKey, { enumerable: false });
|
|
44
|
+
return dir;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Test if the passed object is a directory record. */
|
|
48
|
+
function isDir(data: Record<string, unknown>): data is Dir {
|
|
49
|
+
return DirKey in data;
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
/** Convert an item in a user’s sidebar config to a sidebar entry. */
|
|
41
53
|
function configItemToEntry(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
item: SidebarItem,
|
|
55
|
+
currentPathname: string,
|
|
56
|
+
locale: string | undefined,
|
|
57
|
+
routes: Route[]
|
|
46
58
|
): SidebarEntry {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
};
|
|
60
|
-
}
|
|
59
|
+
if ('link' in item) {
|
|
60
|
+
return linkFromConfig(item, locale, currentPathname);
|
|
61
|
+
} else if ('autogenerate' in item) {
|
|
62
|
+
return groupFromAutogenerateConfig(item, locale, routes, currentPathname);
|
|
63
|
+
} else {
|
|
64
|
+
return {
|
|
65
|
+
type: 'group',
|
|
66
|
+
label: pickLang(item.translations, localeToLang(locale)) || item.label,
|
|
67
|
+
entries: item.items.map((i) => configItemToEntry(i, currentPathname, locale, routes)),
|
|
68
|
+
collapsed: item.collapsed,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
61
71
|
}
|
|
62
72
|
|
|
63
73
|
/** Autogenerate a group of links from a user’s sidebar config. */
|
|
64
74
|
function groupFromAutogenerateConfig(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
item: AutoSidebarGroup,
|
|
76
|
+
locale: string | undefined,
|
|
77
|
+
routes: Route[],
|
|
78
|
+
currentPathname: string
|
|
69
79
|
): Group {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
const { collapsed: subgroupCollapsed, directory } = item.autogenerate;
|
|
81
|
+
const localeDir = locale ? locale + '/' + directory : directory;
|
|
82
|
+
const dirDocs = routes.filter(
|
|
83
|
+
(doc) =>
|
|
84
|
+
// Match against `foo.md` or `foo/index.md`.
|
|
85
|
+
stripExtension(doc.id) === localeDir ||
|
|
86
|
+
// Match against `foo/anything/else.md`.
|
|
87
|
+
doc.id.startsWith(localeDir + '/')
|
|
88
|
+
);
|
|
89
|
+
const tree = treeify(dirDocs, localeDir);
|
|
90
|
+
return {
|
|
91
|
+
type: 'group',
|
|
92
|
+
label: pickLang(item.translations, localeToLang(locale)) || item.label,
|
|
93
|
+
entries: sidebarFromDir(tree, currentPathname, locale, subgroupCollapsed ?? item.collapsed),
|
|
94
|
+
collapsed: item.collapsed,
|
|
95
|
+
};
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
/** Check if a string starts with one of `http://` or `https://`. */
|
|
@@ -90,207 +100,219 @@ const isAbsolute = (link: string) => /^https?:\/\//.test(link);
|
|
|
90
100
|
|
|
91
101
|
/** Ensure the passed path starts and ends with trailing slashes. */
|
|
92
102
|
function ensureLeadingAndTrailingSlashes(href: string): string {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
if (href[0] !== '/') href = '/' + href;
|
|
104
|
+
if (href[href.length - 1] !== '/') href += '/';
|
|
105
|
+
return href;
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
/** Create a link entry from a user config object. */
|
|
99
109
|
function linkFromConfig(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
item: SidebarLinkItem,
|
|
111
|
+
locale: string | undefined,
|
|
112
|
+
currentPathname: string
|
|
103
113
|
) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
let href = item.link;
|
|
115
|
+
if (!isAbsolute(href)) {
|
|
116
|
+
href = ensureLeadingAndTrailingSlashes(href);
|
|
117
|
+
// Inject current locale into link.
|
|
118
|
+
if (locale) href = '/' + locale + href;
|
|
119
|
+
}
|
|
120
|
+
const label = pickLang(item.translations, localeToLang(locale)) || item.label;
|
|
121
|
+
return makeLink(href, label, currentPathname);
|
|
112
122
|
}
|
|
113
123
|
|
|
114
124
|
/** Create a link entry. */
|
|
115
125
|
function makeLink(href: string, label: string, currentPathname: string): Link {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
126
|
+
if (!isAbsolute(href)) href = pathWithBase(href);
|
|
127
|
+
const isCurrent = href === currentPathname;
|
|
128
|
+
return { type: 'link', label, href, isCurrent };
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
/** Get the segments leading to a page. */
|
|
122
132
|
function getBreadcrumbs(path: string, baseDir: string): string[] {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
// Strip extension from path.
|
|
134
|
+
const pathWithoutExt = stripExtension(path);
|
|
135
|
+
// Index paths will match `baseDir` and don’t include breadcrumbs.
|
|
136
|
+
if (pathWithoutExt === baseDir) return [];
|
|
137
|
+
// Ensure base directory ends in a trailing slash.
|
|
138
|
+
if (!baseDir.endsWith('/')) baseDir += '/';
|
|
139
|
+
// Strip base directory from path if present.
|
|
140
|
+
const relativePath = pathWithoutExt.startsWith(baseDir)
|
|
141
|
+
? pathWithoutExt.replace(baseDir, '')
|
|
142
|
+
: pathWithoutExt;
|
|
143
|
+
let dir = dirname(relativePath);
|
|
144
|
+
// Return no breadcrumbs for items in the root directory.
|
|
145
|
+
if (dir === '.') return [];
|
|
146
|
+
return dir.split('/');
|
|
137
147
|
}
|
|
138
148
|
|
|
139
149
|
/** Turn a flat array of routes into a tree structure. */
|
|
140
150
|
function treeify(routes: Route[], baseDir: string): Dir {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
151
|
+
const treeRoot: Dir = makeDir();
|
|
152
|
+
routes.forEach((doc) => {
|
|
153
|
+
const breadcrumbs = getBreadcrumbs(doc.id, baseDir);
|
|
144
154
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
// Walk down the route’s path to generate the tree.
|
|
156
|
+
let currentDir = treeRoot;
|
|
157
|
+
breadcrumbs.forEach((dir) => {
|
|
158
|
+
// Create new folder if needed.
|
|
159
|
+
if (typeof currentDir[dir] === 'undefined') currentDir[dir] = makeDir();
|
|
160
|
+
// Go into the subdirectory.
|
|
161
|
+
currentDir = currentDir[dir] as Dir;
|
|
162
|
+
});
|
|
163
|
+
// We’ve walked through the path. Register the route in this directory.
|
|
164
|
+
currentDir[basename(doc.slug)] = doc;
|
|
165
|
+
});
|
|
166
|
+
return treeRoot;
|
|
157
167
|
}
|
|
158
168
|
|
|
159
169
|
/** Create a link entry for a given content collection entry. */
|
|
160
|
-
function
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
170
|
+
function linkFromRoute(route: Route, currentPathname: string): Link {
|
|
171
|
+
return makeLink(
|
|
172
|
+
slugToPathname(route.slug),
|
|
173
|
+
route.entry.data.sidebar.label || route.entry.data.title,
|
|
174
|
+
currentPathname
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get the sort weight for a given route or directory. Lower numbers rank higher.
|
|
180
|
+
* Directories have the weight of the lowest weighted route they contain.
|
|
181
|
+
*/
|
|
182
|
+
function getOrder(routeOrDir: Route | Dir): number {
|
|
183
|
+
return isDir(routeOrDir)
|
|
184
|
+
? Math.min(...Object.values(routeOrDir).flatMap(getOrder))
|
|
185
|
+
: // If no order value is found, set it to the largest number possible.
|
|
186
|
+
routeOrDir.entry.data.sidebar.order ?? Number.MAX_VALUE;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Sort a directory’s entries by user-specified order or alphabetically if no order specified. */
|
|
190
|
+
function sortDirEntries(dir: [string, Dir | Route][]): [string, Dir | Route][] {
|
|
191
|
+
return dir.sort(([, a], [, b]) => {
|
|
192
|
+
const [aOrder, bOrder] = [getOrder(a), getOrder(b)];
|
|
193
|
+
// Pages are sorted by order in ascending order.
|
|
194
|
+
if (aOrder !== bOrder) return aOrder < bOrder ? -1 : 1;
|
|
195
|
+
// If two pages have the same order value they will be sorted by their slug.
|
|
196
|
+
return a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0;
|
|
197
|
+
});
|
|
167
198
|
}
|
|
168
199
|
|
|
169
200
|
/** Create a group entry for a given content collection directory. */
|
|
170
201
|
function groupFromDir(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
202
|
+
dir: Dir,
|
|
203
|
+
fullPath: string,
|
|
204
|
+
dirName: string,
|
|
205
|
+
currentPathname: string,
|
|
206
|
+
locale: string | undefined,
|
|
207
|
+
collapsed: boolean
|
|
177
208
|
): Group {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
209
|
+
const entries = sortDirEntries(Object.entries(dir)).map(([key, dirOrRoute]) =>
|
|
210
|
+
dirToItem(dirOrRoute, `${fullPath}/${key}`, key, currentPathname, locale, collapsed)
|
|
211
|
+
);
|
|
212
|
+
return {
|
|
213
|
+
type: 'group',
|
|
214
|
+
label: dirName,
|
|
215
|
+
entries,
|
|
216
|
+
collapsed,
|
|
217
|
+
};
|
|
187
218
|
}
|
|
188
219
|
|
|
189
|
-
/** Create a sidebar entry for a directory or content
|
|
220
|
+
/** Create a sidebar entry for a directory or content entry. */
|
|
190
221
|
function dirToItem(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
222
|
+
dirOrRoute: Dir[string],
|
|
223
|
+
fullPath: string,
|
|
224
|
+
dirName: string,
|
|
225
|
+
currentPathname: string,
|
|
226
|
+
locale: string | undefined,
|
|
227
|
+
collapsed: boolean
|
|
197
228
|
): SidebarEntry {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
229
|
+
return isDir(dirOrRoute)
|
|
230
|
+
? groupFromDir(dirOrRoute, fullPath, dirName, currentPathname, locale, collapsed)
|
|
231
|
+
: linkFromRoute(dirOrRoute, currentPathname);
|
|
201
232
|
}
|
|
202
233
|
|
|
203
234
|
/** Create a sidebar entry for a given content directory. */
|
|
204
235
|
function sidebarFromDir(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
236
|
+
tree: Dir,
|
|
237
|
+
currentPathname: string,
|
|
238
|
+
locale: string | undefined,
|
|
239
|
+
collapsed: boolean
|
|
209
240
|
) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
241
|
+
return sortDirEntries(Object.entries(tree)).map(([key, dirOrRoute]) =>
|
|
242
|
+
dirToItem(dirOrRoute, key, key, currentPathname, locale, collapsed)
|
|
243
|
+
);
|
|
213
244
|
}
|
|
214
245
|
|
|
215
246
|
/** Get the sidebar for the current page. */
|
|
216
|
-
export function getSidebar(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
);
|
|
225
|
-
} else {
|
|
226
|
-
const tree = treeify(routes, locale || '');
|
|
227
|
-
return sidebarFromDir(tree, pathname, locale, false);
|
|
228
|
-
}
|
|
247
|
+
export function getSidebar(pathname: string, locale: string | undefined): SidebarEntry[] {
|
|
248
|
+
const routes = getLocaleRoutes(locale);
|
|
249
|
+
if (config.sidebar) {
|
|
250
|
+
return config.sidebar.map((group) => configItemToEntry(group, pathname, locale, routes));
|
|
251
|
+
} else {
|
|
252
|
+
const tree = treeify(routes, locale || '');
|
|
253
|
+
return sidebarFromDir(tree, pathname, locale, false);
|
|
254
|
+
}
|
|
229
255
|
}
|
|
230
256
|
|
|
231
257
|
/** Turn the nested tree structure of a sidebar into a flat list of all the links. */
|
|
232
258
|
export function flattenSidebar(sidebar: SidebarEntry[]): Link[] {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
259
|
+
return sidebar.flatMap((entry) =>
|
|
260
|
+
entry.type === 'group' ? flattenSidebar(entry.entries) : entry
|
|
261
|
+
);
|
|
236
262
|
}
|
|
237
263
|
|
|
238
264
|
/** Get previous/next pages in the sidebar or the ones from the frontmatter if any. */
|
|
239
265
|
export function getPrevNextLinks(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
266
|
+
sidebar: SidebarEntry[],
|
|
267
|
+
paginationEnabled: boolean,
|
|
268
|
+
config: {
|
|
269
|
+
prev?: PrevNextLinkConfig;
|
|
270
|
+
next?: PrevNextLinkConfig;
|
|
271
|
+
}
|
|
246
272
|
): {
|
|
247
|
-
|
|
248
|
-
|
|
273
|
+
prev: Link | undefined;
|
|
274
|
+
next: Link | undefined;
|
|
249
275
|
} {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
paginationEnabled,
|
|
260
|
-
config.next
|
|
261
|
-
);
|
|
262
|
-
return { prev, next };
|
|
276
|
+
const entries = flattenSidebar(sidebar);
|
|
277
|
+
const currentIndex = entries.findIndex((entry) => entry.isCurrent);
|
|
278
|
+
const prev = applyPrevNextLinkConfig(entries[currentIndex - 1], paginationEnabled, config.prev);
|
|
279
|
+
const next = applyPrevNextLinkConfig(
|
|
280
|
+
currentIndex > -1 ? entries[currentIndex + 1] : undefined,
|
|
281
|
+
paginationEnabled,
|
|
282
|
+
config.next
|
|
283
|
+
);
|
|
284
|
+
return { prev, next };
|
|
263
285
|
}
|
|
264
286
|
|
|
265
287
|
/** Apply a prev/next link config to a navigation link. */
|
|
266
288
|
function applyPrevNextLinkConfig(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
289
|
+
link: Link | undefined,
|
|
290
|
+
paginationEnabled: boolean,
|
|
291
|
+
config: PrevNextLinkConfig | undefined
|
|
270
292
|
): Link | undefined {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
// Explicitly remove the link.
|
|
294
|
+
if (config === false) return undefined;
|
|
295
|
+
// Use the generated link if any.
|
|
296
|
+
else if (config === true) return link;
|
|
297
|
+
// If a link exists, update its label if needed.
|
|
298
|
+
else if (typeof config === 'string' && link) {
|
|
299
|
+
return { ...link, label: config };
|
|
300
|
+
} else if (typeof config === 'object') {
|
|
301
|
+
if (link) {
|
|
302
|
+
// If a link exists, update both its label and href if needed.
|
|
303
|
+
return {
|
|
304
|
+
...link,
|
|
305
|
+
label: config.label ?? link.label,
|
|
306
|
+
href: config.link ?? link.href,
|
|
307
|
+
};
|
|
308
|
+
} else if (config.link && config.label) {
|
|
309
|
+
// If there is no link and the frontmatter contains both a URL and a label,
|
|
310
|
+
// create a new link.
|
|
311
|
+
return makeLink(config.link, config.label, config.link);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// Otherwise, if the global config is enabled, return the generated link if any.
|
|
315
|
+
return paginationEnabled ? link : undefined;
|
|
294
316
|
}
|
|
295
317
|
|
|
296
318
|
/** Remove the extension from a path. */
|