@brillout/docpress 0.6.8 → 0.6.10

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/MobileHeader.tsx CHANGED
@@ -28,7 +28,7 @@ function MobileHeader() {
28
28
  borderBottom: '1px solid #ddd',
29
29
  }}
30
30
  >
31
- <MenuToggle />
31
+ <MobileShowNavigationToggle />
32
32
  <a
33
33
  href="/"
34
34
  style={{
@@ -47,9 +47,9 @@ function MobileHeader() {
47
47
  )
48
48
  }
49
49
 
50
- function MenuToggle() {
50
+ function MobileShowNavigationToggle() {
51
51
  return (
52
- <div style={{ padding: 20, lineHeight: 0, cursor: 'pointer' }} id="menu-toggle">
52
+ <div style={{ padding: 20, lineHeight: 0, cursor: 'pointer' }} id="mobile-show-navigation-toggle">
53
53
  <svg
54
54
  style={{ width: 20 }}
55
55
  className="icon"
package/PageLayout.tsx CHANGED
@@ -17,7 +17,7 @@ function PageLayout({ pageContext, children }: { pageContext: PageContextResolve
17
17
  <PageContextProvider pageContext={pageContext}>
18
18
  <div id="page-layout" className={isLandingPage ? 'landing-page' : 'doc-page'}>
19
19
  <div id="navigation-wrapper">
20
- <Navigation pageContext={pageContext} />
20
+ <Navigation {...pageContext.navigationData} />
21
21
  </div>
22
22
  <NavigationFullscreenButton />
23
23
  <div id="page-wrapper">
@@ -1,12 +1,13 @@
1
1
  export { Link }
2
+ export type { LinkData }
2
3
 
3
4
  import React from 'react'
4
5
  import { isRepoLink, RepoLink } from './RepoLink'
5
- import type { Heading, HeadingDetached } from '../types/Heading'
6
6
  import type { PageContextResolved } from '../config/resolvePageContext'
7
7
  import { usePageContext } from '../renderer/usePageContext'
8
8
  import { assert, assertUsage, determineSectionTitle, determineSectionUrlHash } from '../utils/server'
9
9
  import { parseTitle } from '../parseTitle'
10
+ import pc from '@brillout/picocolors'
10
11
 
11
12
  function Link({
12
13
  href,
@@ -48,54 +49,27 @@ function getLinkText({
48
49
  pageContext: PageContextResolved
49
50
  doNotInferSectionTitle: true | undefined
50
51
  }): string | JSX.Element {
51
- let urlHash: string | null = null
52
- let hrefWithoutHash: string = href
53
- if (href.includes('#')) {
54
- ;[hrefWithoutHash, urlHash] = href.split('#')
55
- assert(hrefWithoutHash || urlHash)
56
- }
52
+ const { hrefPathname, hrefHash } = parseHref(href)
57
53
 
58
- let heading: Heading | HeadingDetached
59
- let isLinkOnSamePage: boolean = false
60
- if (hrefWithoutHash) {
61
- heading = findHeading(hrefWithoutHash, pageContext)
62
- if (heading.url === pageContext.urlPathname) {
63
- isLinkOnSamePage = true
64
- // heading !== pageContext.activeHeading because activeHeading is a different object holding on-this-page subheadings
65
- heading = pageContext.activeHeading
66
- }
67
- } else {
68
- assert(urlHash)
69
- isLinkOnSamePage = true
70
- heading = pageContext.activeHeading
71
- }
72
- assert(heading)
73
- assert(isLinkOnSamePage === (heading.url === pageContext.urlPathname))
74
- assert(isLinkOnSamePage === (heading.url === pageContext.activeHeading.url))
75
- assert(isLinkOnSamePage === (heading === pageContext.activeHeading))
54
+ const linkData = findLinkData(hrefPathname || pageContext.urlPathname, pageContext)
55
+ const isLinkOnSamePage = linkData.url === pageContext.urlPathname
56
+ if (!hrefPathname) assert(isLinkOnSamePage)
76
57
 
77
58
  const breadcrumbParts: (string | JSX.Element)[] = []
78
-
79
- if (heading.headingsBreadcrumb) {
80
- breadcrumbParts.push(
81
- ...(heading.headingsBreadcrumb ?? [])
82
- .slice()
83
- .reverse()
84
- .map(({ title }) => title),
85
- )
59
+ if (linkData.linkBreadcrumb) {
60
+ breadcrumbParts.push(...(linkData.linkBreadcrumb ?? []).slice().reverse())
86
61
  }
62
+ breadcrumbParts.push(linkData.title)
87
63
 
88
- breadcrumbParts.push(heading.title)
89
-
90
- if (urlHash) {
64
+ if (hrefHash) {
91
65
  let sectionTitle: string | JSX.Element | undefined = undefined
92
- assert(!urlHash.startsWith('#'))
66
+ assert(!hrefHash.startsWith('#'))
93
67
  if (isLinkOnSamePage) {
94
- const pageHeading = findHeading(`#${urlHash}`, pageContext)
95
- sectionTitle = pageHeading.title
96
- } else if ('sectionTitles' in heading && heading.sectionTitles) {
97
- heading.sectionTitles.forEach((title) => {
98
- if (determineSectionUrlHash(title) === urlHash) {
68
+ const linkDataPageSection = findLinkData(`#${hrefHash}`, pageContext)
69
+ sectionTitle = linkDataPageSection.title
70
+ } else if ('sectionTitles' in linkData && linkData.sectionTitles) {
71
+ linkData.sectionTitles.forEach((title) => {
72
+ if (determineSectionUrlHash(title) === hrefHash) {
99
73
  sectionTitle = parseTitle(title)
100
74
  }
101
75
  })
@@ -131,14 +105,53 @@ function getLinkText({
131
105
  )
132
106
  }
133
107
 
134
- function findHeading(href: string, pageContext: PageContextResolved): Heading | HeadingDetached {
108
+ type LinkData = {
109
+ url?: null | string
110
+ title: JSX.Element
111
+ linkBreadcrumb: null | JSX.Element[]
112
+ sectionTitles?: string[]
113
+ }
114
+
115
+ function findLinkData(href: string, pageContext: PageContextResolved): LinkData {
135
116
  assert(href.startsWith('/') || href.startsWith('#'))
136
- const { headingsAll } = pageContext
137
- const heading = headingsAll.find(({ url }) => href === url)
117
+ const { linksAll } = pageContext
118
+ const linkData = linksAll.find(({ url }) => href === url)
138
119
  if (href.startsWith('#')) {
139
- assertUsage(heading, `Couldn't find ${href} in ${pageContext.urlPathname}, does it exist?`)
120
+ assertUsage(linkData, `Couldn't find ${href} in ${pageContext.urlPathname}, does it exist?`)
140
121
  } else {
141
- assertUsage(heading, `Couldn't find heading for ${href}, did you define the heading for ${href}?`)
122
+ assertUsage(
123
+ linkData,
124
+ [
125
+ `Couldn't find page with URL ${pc.bold(href)}`,
126
+ `— did you define it in`,
127
+ [
128
+ pc.cyan('docpress.config.js'),
129
+ pc.dim('#{'),
130
+ pc.cyan('headings'),
131
+ pc.dim(','),
132
+ pc.cyan('headingsDetached'),
133
+ pc.dim('}'),
134
+ '?',
135
+ ].join(''),
136
+ ].join(' '),
137
+ )
138
+ }
139
+ return linkData
140
+ }
141
+
142
+ function parseHref(href: string) {
143
+ let hrefHash: string | null = null
144
+ let hrefPathname: string | null = null
145
+ if (!href.includes('#')) {
146
+ hrefPathname = href
147
+ } else {
148
+ const [partsFirst, ...partsRest] = href.split('#')
149
+ if (partsFirst) {
150
+ hrefPathname = partsFirst
151
+ }
152
+ hrefHash = partsRest.join('#')
142
153
  }
143
- return heading
154
+ assert(hrefPathname !== null || hrefHash !== null)
155
+ assert(hrefPathname || hrefHash)
156
+ return { hrefPathname, hrefHash }
144
157
  }
@@ -0,0 +1,291 @@
1
+ export { resolveHeadingsData }
2
+
3
+ import { assert, jsxToTextContent } from '../utils/server'
4
+ import type {
5
+ HeadingDefinition,
6
+ HeadingDetachedDefinition,
7
+ HeadingResolved,
8
+ HeadingDetachedResolved,
9
+ } from '../types/Heading'
10
+ import type { Config } from '../types/Config'
11
+ import { getConfig } from './getConfig'
12
+ import { parseTitle, withEmoji } from '../parseTitle'
13
+ import { NavigationData, NavItem } from '../navigation/Navigation'
14
+ import type { LinkData } from '../components'
15
+ import type { Exports, PageContextOriginal } from './resolvePageContext'
16
+ import pc from '@brillout/picocolors'
17
+
18
+ type PageSectionResolved = {
19
+ url: string | null
20
+ title: JSX.Element
21
+ titleInNav: JSX.Element
22
+ linkBreadcrumb: JSX.Element[]
23
+ pageSectionLevel: number
24
+ }
25
+
26
+ function resolveHeadingsData(pageContext: PageContextOriginal) {
27
+ const config = getConfig()
28
+
29
+ {
30
+ const { headings, headingsDetached } = config
31
+ assertHeadingsDefinition([...headings, ...headingsDetached])
32
+ }
33
+
34
+ const resolved = getHeadingsResolved(config)
35
+ const { headingsDetachedResolved } = resolved
36
+ let { headingsResolved } = resolved
37
+
38
+ const { activeHeading, isDetachedPage } = getActiveHeading(headingsResolved, headingsDetachedResolved, pageContext)
39
+
40
+ const { documentTitle, isLandingPage, pageTitle } = getTitles(activeHeading, pageContext, config)
41
+
42
+ const pageSectionsResolved = getPageSectionsResolved(pageContext, activeHeading)
43
+
44
+ const linksAll: LinkData[] = [
45
+ ...pageSectionsResolved.map(pageSectionToLinkData),
46
+ ...headingsResolved.map(headingToLinkData),
47
+ ...headingsDetachedResolved.map(headingToLinkData),
48
+ ]
49
+
50
+ let navigationData: NavigationData
51
+ {
52
+ const currentUrl: string = pageContext.urlPathname
53
+ const navItemsPageSections = pageSectionsResolved
54
+ .filter((pageSection) => pageSection.pageSectionLevel === 2)
55
+ .map(pageSectionToNavItem)
56
+ if (isDetachedPage) {
57
+ const navItemsAll: NavItem[] = headingsResolved
58
+ const navItems: NavItem[] = [headingToNavItem(activeHeading), ...navItemsPageSections]
59
+ navigationData = {
60
+ isDetachedPage: true,
61
+ navItems,
62
+ navItemsAll,
63
+ currentUrl,
64
+ }
65
+ } else {
66
+ const navItemsAll: NavItem[] = headingsResolved.map(headingToNavItem)
67
+ const activeHeadingIndex = navItemsAll.findIndex((navItem) => navItem.url === currentUrl)
68
+ assert(activeHeadingIndex >= 0)
69
+ navItemsPageSections.forEach((navItem, i) => {
70
+ navItemsAll.splice(activeHeadingIndex + 1 + i, 0, navItem)
71
+ })
72
+
73
+ navigationData = {
74
+ isDetachedPage: false,
75
+ navItems: navItemsAll,
76
+ navItemsAll,
77
+ currentUrl,
78
+ }
79
+ }
80
+ }
81
+
82
+ const pageContextAddendum = {
83
+ navigationData,
84
+ linksAll,
85
+ isLandingPage,
86
+ pageTitle,
87
+ documentTitle,
88
+ }
89
+ return pageContextAddendum
90
+ }
91
+
92
+ function headingToNavItem(heading: HeadingResolved | HeadingDetachedResolved): NavItem {
93
+ return {
94
+ level: heading.level,
95
+ url: heading.url,
96
+ title: heading.title,
97
+ titleInNav: heading.titleInNav,
98
+ }
99
+ }
100
+ function headingToLinkData(heading: HeadingResolved | HeadingDetachedResolved): LinkData {
101
+ return {
102
+ url: heading.url,
103
+ title: heading.title,
104
+ linkBreadcrumb: heading.linkBreadcrumb,
105
+ sectionTitles: heading.sectionTitles,
106
+ }
107
+ }
108
+ function pageSectionToNavItem(pageSection: PageSectionResolved): NavItem {
109
+ return {
110
+ level: pageSection.pageSectionLevel + 1,
111
+ url: pageSection.url,
112
+ title: pageSection.title,
113
+ titleInNav: pageSection.titleInNav,
114
+ }
115
+ }
116
+ function pageSectionToLinkData(pageSection: PageSectionResolved): LinkData {
117
+ return {
118
+ url: pageSection.url,
119
+ title: pageSection.title,
120
+ linkBreadcrumb: pageSection.linkBreadcrumb,
121
+ }
122
+ }
123
+
124
+ function getTitles(
125
+ activeHeading: HeadingResolved | HeadingDetachedResolved,
126
+ pageContext: { urlOriginal: string },
127
+ config: Config,
128
+ ) {
129
+ const url = pageContext.urlOriginal
130
+ const isLandingPage = url === '/'
131
+
132
+ const { title } = activeHeading
133
+ let pageTitle = isLandingPage ? null : title
134
+ let documentTitle = activeHeading.titleDocument || jsxToTextContent(title)
135
+
136
+ if (!isLandingPage) {
137
+ documentTitle += ' | ' + config.projectInfo.projectName
138
+ }
139
+
140
+ if (isLandingPage) {
141
+ pageTitle = null
142
+ }
143
+
144
+ return { documentTitle, isLandingPage, pageTitle }
145
+ }
146
+
147
+ function getActiveHeading(
148
+ headingsResolved: HeadingResolved[],
149
+ headingsDetachedResolved: HeadingDetachedResolved[],
150
+ pageContext: { urlOriginal: string; exports: Exports },
151
+ ) {
152
+ let activeHeading: HeadingResolved | HeadingDetachedResolved | null = null
153
+ const { urlOriginal } = pageContext
154
+ assert(urlOriginal)
155
+ headingsResolved.forEach((heading) => {
156
+ if (heading.url === urlOriginal) {
157
+ activeHeading = heading
158
+ assert(heading.level === 2, { pageUrl: urlOriginal, heading })
159
+ }
160
+ })
161
+ const isDetachedPage = !activeHeading
162
+ if (!activeHeading) {
163
+ activeHeading = headingsDetachedResolved.find(({ url }) => urlOriginal === url) ?? null
164
+ }
165
+ if (!activeHeading) {
166
+ throw new Error(
167
+ [
168
+ `URL ${pc.bold(urlOriginal)} not found in following URLs:`,
169
+ ...headingsResolved
170
+ .map((h) => ` ${h.url}`)
171
+ .filter(Boolean)
172
+ .sort(),
173
+ ].join('\n'),
174
+ )
175
+ }
176
+ return { activeHeading, isDetachedPage }
177
+ }
178
+
179
+ function getPageSectionsResolved(
180
+ pageContext: { exports: Exports; urlOriginal: string },
181
+ activeHeading: HeadingResolved | HeadingDetachedResolved,
182
+ ): PageSectionResolved[] {
183
+ const pageSections = pageContext.exports.pageSectionsExport ?? []
184
+
185
+ const pageSectionsResolved = pageSections.map((pageSection) => {
186
+ const pageSectionTitleJsx = parseTitle(pageSection.pageSectionTitle)
187
+ const url: null | string = pageSection.pageSectionId === null ? null : '#' + pageSection.pageSectionId
188
+ const pageSectionResolved: PageSectionResolved = {
189
+ url,
190
+ title: pageSectionTitleJsx,
191
+ linkBreadcrumb: [activeHeading.title, ...(activeHeading.linkBreadcrumb ?? [])],
192
+ titleInNav: pageSectionTitleJsx,
193
+ pageSectionLevel: pageSection.pageSectionLevel,
194
+ }
195
+ return pageSectionResolved
196
+ })
197
+
198
+ if (activeHeading?.sectionTitles) {
199
+ activeHeading.sectionTitles.forEach((sectionTitle) => {
200
+ const pageSectionTitles = pageSections.map((h) => h.pageSectionTitle)
201
+ assert(pageSectionTitles.includes(sectionTitle), { pageHeadingTitles: pageSectionTitles, sectionTitle })
202
+ })
203
+ }
204
+
205
+ return pageSectionsResolved
206
+ }
207
+
208
+ /**
209
+ * - Parse title (from `string` to `JSX.Element`)
210
+ * - Determine navigation breadcrumbs
211
+ */
212
+ function getHeadingsResolved(config: {
213
+ headings: HeadingDefinition[]
214
+ headingsDetached: HeadingDetachedDefinition[]
215
+ }): {
216
+ headingsResolved: HeadingResolved[]
217
+ headingsDetachedResolved: HeadingDetachedResolved[]
218
+ } {
219
+ const headingsWithoutBreadcrumb: Omit<HeadingResolved, 'linkBreadcrumb'>[] = config.headings.map(
220
+ (heading: HeadingDefinition) => {
221
+ const titleParsed: JSX.Element = parseTitle(heading.title)
222
+
223
+ const titleInNav = heading.titleInNav || heading.title
224
+ let titleInNavParsed: JSX.Element
225
+ titleInNavParsed = parseTitle(titleInNav)
226
+ if ('titleEmoji' in heading) {
227
+ assert(heading.titleEmoji)
228
+ titleInNavParsed = withEmoji(heading.titleEmoji, titleInNavParsed)
229
+ }
230
+
231
+ const headingResolved: Omit<HeadingResolved, 'linkBreadcrumb'> = {
232
+ ...heading,
233
+ title: titleParsed,
234
+ titleInNav: titleInNavParsed,
235
+ }
236
+ return headingResolved
237
+ },
238
+ )
239
+
240
+ const headingsResolved: HeadingResolved[] = []
241
+ headingsWithoutBreadcrumb.forEach((heading) => {
242
+ const linkBreadcrumb = getHeadingsBreadcrumb(heading, headingsResolved)
243
+ headingsResolved.push({
244
+ ...heading,
245
+ linkBreadcrumb,
246
+ })
247
+ })
248
+
249
+ const headingsDetachedResolved = config.headingsDetached.map((headingsDetached) => {
250
+ const { url, title } = headingsDetached
251
+ assert(
252
+ headingsResolved.find((heading) => heading.url === url) === undefined,
253
+ `remove ${headingsDetached.url} from headingsDetached`,
254
+ )
255
+ const titleParsed = typeof title === 'string' ? parseTitle(title) : title
256
+ return {
257
+ ...headingsDetached,
258
+ level: 2 as const,
259
+ title: titleParsed,
260
+ titleInNav: titleParsed,
261
+ linkBreadcrumb: null,
262
+ }
263
+ })
264
+
265
+ return { headingsResolved, headingsDetachedResolved }
266
+ }
267
+
268
+ function getHeadingsBreadcrumb(heading: Omit<HeadingResolved, 'linkBreadcrumb'>, headings: HeadingResolved[]) {
269
+ const linkBreadcrumb: JSX.Element[] = []
270
+ let levelCurrent = heading.level
271
+ headings
272
+ .slice()
273
+ .reverse()
274
+ .forEach((parentCandidate) => {
275
+ const isParent = parentCandidate.level < levelCurrent
276
+ if (isParent) {
277
+ levelCurrent = parentCandidate.level
278
+ linkBreadcrumb.push(parentCandidate.title)
279
+ }
280
+ })
281
+ return linkBreadcrumb
282
+ }
283
+
284
+ function assertHeadingsDefinition(headings: { url?: null | string }[]) {
285
+ headings.forEach((heading) => {
286
+ if (heading.url) {
287
+ const { url } = heading
288
+ assert(url.startsWith('/'))
289
+ }
290
+ })
291
+ }
@@ -1,56 +1,34 @@
1
- import { assert, jsxToTextContent, objectAssign } from '../utils/server'
2
- import type { Heading, HeadingDetached } from '../types/Heading'
3
- import type { PageContextBuiltInServer } from 'vike/types'
4
- import type { MarkdownHeading } from '../markdownHeadingsVitePlugin'
5
- import type { Config } from '../types/Config'
6
- import { getConfig } from './getConfig'
7
- import { getHeadingsWithProcessedTitle, parseTitle } from '../parseTitle'
8
-
9
1
  export { resolvePageContext }
10
2
  export type { PageContextOriginal }
11
3
  export type { PageContextResolved }
12
- export type { Heading }
4
+ export type { Exports }
5
+
6
+ import { objectAssign } from '../utils/server'
7
+ import type { PageContextServer } from 'vike/types'
8
+ import type { PageSection } from '../parsePageSections'
9
+ import { getConfig } from './getConfig'
10
+ import { resolveHeadingsData } from './resolveHeadingsData'
13
11
 
14
12
  type ReactComponent = () => JSX.Element
15
13
  type Exports = {
16
- headings?: MarkdownHeading[]
14
+ pageSectionsExport?: PageSection[]
17
15
  }
18
- type PageContextOriginal = PageContextBuiltInServer & {
16
+ type PageContextOriginal = PageContextServer & {
19
17
  Page: ReactComponent
20
18
  exports: Exports
21
19
  }
22
20
  type PageContextResolved = ReturnType<typeof resolvePageContext>
23
21
 
24
22
  function resolvePageContext(pageContext: PageContextOriginal) {
23
+ const pageContextResolved = {}
24
+
25
+ objectAssign(pageContextResolved, resolveHeadingsData(pageContext))
26
+
25
27
  const config = getConfig()
26
- const processed = getHeadingsWithProcessedTitle(config)
27
- const { headingsDetachedProcessed } = processed
28
- let { headingsProcessed } = processed
29
- const { activeHeading, activeNavigationHeading } = findHeading(
30
- headingsProcessed,
31
- headingsDetachedProcessed,
32
- pageContext,
33
- )
34
- let headingsOfDetachedPage: null | (Heading | HeadingDetached)[] = null
35
- let headingsAll = [...headingsProcessed, ...headingsDetachedProcessed]
36
- headingsAll = getHeadingsAll(headingsAll, pageContext, activeHeading)
37
- if (activeNavigationHeading) {
38
- headingsProcessed = getHeadingsAll(headingsProcessed, pageContext, activeNavigationHeading)
39
- } else {
40
- headingsOfDetachedPage = [activeHeading, ...getHeadingsOfTheCurrentPage(pageContext, activeHeading)]
41
- }
42
- const { title, isLandingPage, pageTitle } = getMetaData(
43
- headingsDetachedProcessed,
44
- activeNavigationHeading,
45
- pageContext,
46
- config,
47
- )
48
28
  const { faviconUrl, algolia, tagline, twitterHandle, bannerUrl, websiteUrl } = config
49
- const pageContextResolved = {}
50
29
  objectAssign(pageContextResolved, {
51
30
  ...pageContext,
52
31
  meta: {
53
- title,
54
32
  faviconUrl,
55
33
  twitterHandle,
56
34
  bannerUrl,
@@ -58,129 +36,8 @@ function resolvePageContext(pageContext: PageContextOriginal) {
58
36
  tagline,
59
37
  algolia,
60
38
  },
61
- activeHeading,
62
- headingsAll,
63
- headingsProcessed,
64
- headingsDetachedProcessed,
65
- headingsOfDetachedPage,
66
- isLandingPage,
67
- pageTitle,
68
39
  config,
69
40
  })
70
- return pageContextResolved
71
- }
72
-
73
- function getMetaData(
74
- headingsDetachedProcessed: HeadingDetached[],
75
- activeNavigationHeading: Heading | null,
76
- pageContext: { urlOriginal: string; exports: Exports },
77
- config: Config,
78
- ) {
79
- const url = pageContext.urlOriginal
80
-
81
- let title: string
82
- let pageTitle: string | JSX.Element | null
83
- if (activeNavigationHeading) {
84
- title = activeNavigationHeading.titleDocument || jsxToTextContent(activeNavigationHeading.title)
85
- pageTitle = activeNavigationHeading.title
86
- } else {
87
- pageTitle = headingsDetachedProcessed.find((h) => h.url === url)!.title
88
- title = jsxToTextContent(pageTitle)
89
- }
90
-
91
- const isLandingPage = url === '/'
92
- if (!isLandingPage) {
93
- title += ' | ' + config.projectInfo.projectName
94
- }
95
-
96
- if (isLandingPage) {
97
- pageTitle = null
98
- }
99
-
100
- return { title, isLandingPage, pageTitle }
101
- }
102
-
103
- function findHeading(
104
- headingsProcessed: Heading[],
105
- headingsDetachedProcessed: HeadingDetached[],
106
- pageContext: { urlOriginal: string; exports: Exports },
107
- ): { activeHeading: Heading | HeadingDetached; activeNavigationHeading: Heading | null } {
108
- let activeNavigationHeading: Heading | null = null
109
- let activeHeading: Heading | HeadingDetached | null = null
110
- assert(pageContext.urlOriginal)
111
- const pageUrl = pageContext.urlOriginal
112
- headingsProcessed.forEach((heading) => {
113
- if (heading.url === pageUrl) {
114
- activeNavigationHeading = heading
115
- activeHeading = heading
116
- assert(heading.level === 2, { pageUrl, heading })
117
- }
118
- })
119
- if (!activeHeading) {
120
- activeHeading = headingsDetachedProcessed.find(({ url }) => pageUrl === url) ?? null
121
- }
122
- if (!activeHeading) {
123
- throw new Error(
124
- [
125
- `Heading not found for URL '${pageUrl}'`,
126
- 'Heading is defined for following URLs:',
127
- ...headingsProcessed
128
- .map((h) => ` ${h.url}`)
129
- .filter(Boolean)
130
- .sort(),
131
- ].join('\n'),
132
- )
133
- }
134
- return { activeHeading, activeNavigationHeading }
135
- }
136
-
137
- function getHeadingsAll<T extends Heading | HeadingDetached>(
138
- headingsProcessed: T[],
139
- pageContext: { exports: Exports; urlOriginal: string },
140
- activeHeading: T,
141
- ): T[] {
142
- const headingsAll = headingsProcessed.slice()
143
-
144
- const headingsOfTheCurrentPage = getHeadingsOfTheCurrentPage(pageContext, activeHeading)
145
-
146
- const activeHeadingIdx = headingsAll.indexOf(activeHeading)
147
- assert(activeHeadingIdx >= 0)
148
- headingsOfTheCurrentPage.forEach((pageHeading, i) => {
149
- headingsAll.splice(activeHeadingIdx + 1 + i, 0, pageHeading as T)
150
- })
151
41
 
152
- return headingsAll
153
- }
154
-
155
- function getHeadingsOfTheCurrentPage(
156
- pageContext: { exports: Exports; urlOriginal: string },
157
- currentHeading: Heading | HeadingDetached,
158
- ) {
159
- const headingsOfCurrentPage: Heading[] = []
160
-
161
- const headingsExport = pageContext.exports.headings ?? []
162
-
163
- headingsExport.forEach((markdownHeading) => {
164
- const title = parseTitle(markdownHeading.title)
165
- const url: null | string = markdownHeading.headingId && '#' + markdownHeading.headingId
166
- if (markdownHeading.headingLevel === 2) {
167
- const heading: Heading = {
168
- url,
169
- title,
170
- headingsBreadcrumb: [currentHeading, ...(currentHeading.headingsBreadcrumb ?? [])],
171
- titleInNav: title,
172
- level: 3,
173
- }
174
- headingsOfCurrentPage.push(heading)
175
- }
176
- })
177
-
178
- if (currentHeading?.sectionTitles) {
179
- currentHeading.sectionTitles.forEach((sectionTitle) => {
180
- const pageHeadingTitles = headingsExport.map((h) => h.title)
181
- assert(pageHeadingTitles.includes(sectionTitle), { pageHeadingTitles, sectionTitle })
182
- })
183
- }
184
-
185
- return headingsOfCurrentPage
42
+ return pageContextResolved
186
43
  }
package/dist/+config.js CHANGED
@@ -5,11 +5,11 @@ export default {
5
5
  client: 'import:@brillout/docpress/renderer/client:doesNotExist',
6
6
  meta: {
7
7
  Page: {
8
- env: { client: false, server: true }
8
+ env: { client: false, server: true },
9
9
  },
10
10
  // Vike already defines the setting 'name', but we redundantly define it here for older Vike versions (otherwise older Vike versions will complain that 'name` is an unknown config).
11
11
  name: {
12
- env: { config: true }
13
- }
14
- }
12
+ env: { config: true },
13
+ },
14
+ },
15
15
  };
@@ -0,0 +1,12 @@
1
+ export { parsePageSections };
2
+ export type { PageSection };
3
+ type PageSection = {
4
+ pageSectionTitle: string;
5
+ pageSectionId: string | null;
6
+ pageSectionLevel: number;
7
+ };
8
+ declare function parsePageSections(): {
9
+ name: string;
10
+ enforce: string;
11
+ transform: (code: string, id: string) => Promise<string | undefined>;
12
+ };