markupress 0.1.0 → 0.2.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/README.md +113 -77
- package/dist/content.d.ts +33 -8
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +126 -250
- package/dist/content.js.map +1 -1
- package/dist/controllers/shell.d.ts +2 -3
- package/dist/controllers/shell.d.ts.map +1 -1
- package/dist/controllers/shell.js +4 -12
- package/dist/controllers/shell.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/markdown.d.ts +1 -1
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +1 -9
- package/dist/markdown.js.map +1 -1
- package/dist/navigation.d.ts +17 -31
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +43 -62
- package/dist/navigation.js.map +1 -1
- package/dist/site.d.ts +9 -25
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +62 -40
- package/dist/site.js.map +1 -1
- package/dist/versions.d.ts +4 -1
- package/dist/versions.d.ts.map +1 -1
- package/dist/versions.js +21 -41
- package/dist/versions.js.map +1 -1
- package/package.json +8 -7
- package/styles/prose.css +24 -0
- package/theme/shell.html +13 -13
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/controllers/shell.ts"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/controllers/shell.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,IAAmB;IAC/C,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;QACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAY,CAAC;QACrC,+DAA+D;QAC/D,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,KAAK,MAAM,CAAC;QACpE,MAAM,KAAK,GAAG,CAAC,KAAY,EAAE,EAAE;YAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YAC5B,IAAI,CAAC,CAAC,MAAM,YAAY,MAAM,CAAC,OAAO,CAAC;gBAAE,OAAO;YAChD,IAAI,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,EAAE,CAAC;gBAC5C,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;gBACjD,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;gBAC/C,IAAI,CAAC;oBAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,wCAAwC,CAAC,CAAC;YACpH,CAAC;YACD,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,aAAa,CAAc,wCAAwC,CAAC,EAAE,WAAW,EAAE,CAAC;QAC/H,CAAC,CAAC;QACF,4DAA4D;QAC5D,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC3C,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { compileMarkdown } from './content.js';
|
|
|
2
2
|
export type { CompiledMarkdown, FrontMatter, MarkdownOptions, OutlineItem } from './content.js';
|
|
3
3
|
export { markdown } from './markdown.js';
|
|
4
4
|
export type { MarkdownPluginOptions } from './markdown.js';
|
|
5
|
-
export type { Link, NavItem,
|
|
5
|
+
export type { Link, NavItem, SidebarNode } from './navigation.js';
|
|
6
6
|
export { buildSite, devSite, markupress, previewSite, siteOptions } from './site.js';
|
|
7
7
|
export type { DocumentationPage, DocumentationVersion, MarkupressOptions } from './site.js';
|
|
8
8
|
export { snapshotVersion, versionLinks } from './versions.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3D,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3D,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACrF,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/markdown.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface MarkdownPluginOptions {
|
|
|
12
12
|
readonly styles?: readonly string[];
|
|
13
13
|
/** Number the lines of every highlighted code block unless its fence says `:no-line-numbers`. */
|
|
14
14
|
readonly lineNumbers?: boolean;
|
|
15
|
-
/** Front matter for a page that its own fields override, such as a
|
|
15
|
+
/** Front matter for a page that its own fields override, such as a site-wide default. */
|
|
16
16
|
readonly frontmatter?: (file: string) => FrontMatter | undefined;
|
|
17
17
|
}
|
|
18
18
|
/**
|
package/dist/markdown.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAmB,KAAK,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAExF,MAAM,WAAW,qBAAqB;IACpC,qHAAqH;IACrH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1D,oFAAoF;IACpF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;IAClH,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,iGAAiG;IACjG,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAmB,KAAK,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAExF,MAAM,WAAW,qBAAqB;IACpC,qHAAqH;IACrH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1D,oFAAoF;IACpF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;IAClH,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,iGAAiG;IACjG,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,yFAAyF;IACzF,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,WAAW,GAAG,SAAS,CAAC;CAClE;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,qBAA0B,GAAG,aAAa,CAW3E"}
|
package/dist/markdown.js
CHANGED
|
@@ -11,17 +11,9 @@ export function markdown(options = {}) {
|
|
|
11
11
|
const href = page.href(page.file);
|
|
12
12
|
// The URL, not the machine's path, names the page, so builds are reproducible.
|
|
13
13
|
const pageName = 'page-md-' + createHash('sha256').update(href ?? page.file).digest('hex').slice(0, 20);
|
|
14
|
-
// compileMarkdown also passes through links it already resolved; return those unchanged.
|
|
15
|
-
const resolved = new Set();
|
|
16
14
|
const frontmatter = options.frontmatter?.(page.file);
|
|
17
15
|
const compiled = await compileMarkdown(source, { file: page.file, pageName, ...(options.styles ? { styles: options.styles } : {}),
|
|
18
|
-
...(options.lineNumbers === undefined ? {} : { lineNumbers: options.lineNumbers }), ...(frontmatter ? { frontmatter } : {}), resolveLink(target, kind)
|
|
19
|
-
if (resolved.has(target))
|
|
20
|
-
return target;
|
|
21
|
-
const url = link(target, kind, page, options);
|
|
22
|
-
resolved.add(url);
|
|
23
|
-
return url;
|
|
24
|
-
} });
|
|
16
|
+
...(options.lineNumbers === undefined ? {} : { lineNumbers: options.lineNumbers }), ...(frontmatter ? { frontmatter } : {}), resolveLink: (target, kind) => link(target, kind, page, options) });
|
|
25
17
|
options.onPage?.({ ...compiled, file: page.file, href });
|
|
26
18
|
return compiled.resource;
|
|
27
19
|
} } };
|
package/dist/markdown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,eAAe,EAA2C,MAAM,cAAc,CAAC;AAexF;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAO,GAA0B,EAAE;IAC1D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI;gBACjF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAClC,+EAA+E;gBAC/E,MAAM,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACxG,
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"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"}
|
package/dist/navigation.d.ts
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { FrontMatter } from './content.js';
|
|
1
|
+
import type { NavigationNode } from '@nextwebwg/htmlkit';
|
|
3
2
|
/** A top navigation or footer link. A path starting with `/` is a page of the selected version. */
|
|
4
3
|
export interface Link {
|
|
5
4
|
readonly label: string;
|
|
6
5
|
readonly href: string;
|
|
7
6
|
}
|
|
8
|
-
/** A top navigation
|
|
9
|
-
export type NavItem = Link
|
|
10
|
-
readonly
|
|
11
|
-
readonly items: readonly Link[];
|
|
12
|
-
};
|
|
13
|
-
/** An explicit sidebar entry: a page by its path in the version (`/guide/install/`), a link, or a group. */
|
|
14
|
-
export type SidebarItem = string | Link | {
|
|
15
|
-
readonly label: string;
|
|
16
|
-
readonly items: readonly SidebarItem[];
|
|
17
|
-
readonly collapsed?: boolean;
|
|
7
|
+
/** A top navigation link after the sections, shown as its `icon` image (a path starting with `/` is in `public/`) or its label. */
|
|
8
|
+
export type NavItem = Link & {
|
|
9
|
+
readonly icon?: string;
|
|
18
10
|
};
|
|
19
11
|
/** A sidebar entry as `<hk-nav>` renders it: a page or link with `href`, and a folder or group with `children`. */
|
|
20
12
|
export interface SidebarNode {
|
|
@@ -25,27 +17,21 @@ export interface SidebarNode {
|
|
|
25
17
|
readonly depth: number;
|
|
26
18
|
readonly children: readonly SidebarNode[];
|
|
27
19
|
}
|
|
28
|
-
/** Whether a Markdown file is its folder's index page. */
|
|
29
|
-
export declare const isIndex: (file: string) => boolean;
|
|
30
|
-
interface Category {
|
|
31
|
-
readonly label?: string;
|
|
32
|
-
readonly position?: number;
|
|
33
|
-
readonly collapsed?: boolean;
|
|
34
|
-
}
|
|
35
|
-
/** A folder's group metadata. YAML also reads Docusaurus's JSON. */
|
|
36
|
-
export declare function category(directory: string): Category;
|
|
37
20
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
21
|
+
* HTMLKit's navigation tree with Markupress's group policy: a folder stays open unless its index page's front
|
|
22
|
+
* matter says `navigation: collapsed`, and one holding the current page is always open. `collapsed` tells whether
|
|
23
|
+
* a folder's index page URL starts closed.
|
|
41
24
|
*/
|
|
42
|
-
export declare function
|
|
25
|
+
export declare function generatedSidebar(tree: readonly NavigationNode[], collapsed: (href: string) => boolean): SidebarNode[];
|
|
43
26
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
27
|
+
* A version's footer, from the `_footer.md` in its content directory (HTMLKit routes no `_` file): each heading
|
|
28
|
+
* starts a column, its list items are links, and a paragraph after them is the copyright line.
|
|
46
29
|
*/
|
|
47
|
-
export declare function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
30
|
+
export declare function footer(file: string): {
|
|
31
|
+
columns: {
|
|
32
|
+
title: string;
|
|
33
|
+
items: Link[];
|
|
34
|
+
}[];
|
|
35
|
+
copyright: string;
|
|
36
|
+
};
|
|
51
37
|
//# sourceMappingURL=navigation.d.ts.map
|
package/dist/navigation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIzD,mGAAmG;AACnG,MAAM,WAAW,IAAI;IAAG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AACvE,mIAAmI;AACnI,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,mHAAmH;AACnH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,EAAE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,WAAW,EAAE,CAGrH;AAID;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,IAAI,EAAE,CAAA;KAAE,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CA0BvG"}
|
package/dist/navigation.js
CHANGED
|
@@ -1,71 +1,52 @@
|
|
|
1
|
-
import { existsSync,
|
|
2
|
-
import {
|
|
3
|
-
import { parse } from 'yaml';
|
|
4
|
-
const index = /^(?:\d+\.)?index\.md$/;
|
|
5
|
-
/** Whether a Markdown file is its folder's index page. */
|
|
6
|
-
export const isIndex = (file) => index.test(basename(file));
|
|
7
|
-
// Markupress's own name, and Docusaurus's, so a migrated folder keeps its metadata.
|
|
8
|
-
const categoryFiles = ['_category.yml', '_category_.yml', '_category_.json'];
|
|
9
|
-
/** A folder's group metadata. YAML also reads Docusaurus's JSON. */
|
|
10
|
-
export function category(directory) {
|
|
11
|
-
const file = categoryFiles.map(name => join(directory, name)).find(existsSync);
|
|
12
|
-
if (file === undefined)
|
|
13
|
-
return {};
|
|
14
|
-
const value = parse(readFileSync(file, 'utf8'));
|
|
15
|
-
const fields = (value ?? {});
|
|
16
|
-
if (typeof fields !== 'object' || Array.isArray(fields))
|
|
17
|
-
throw new Error(`${file}: must be a mapping.`);
|
|
18
|
-
for (const [name, type] of [['label', 'string'], ['position', 'number'], ['collapsed', 'boolean']]) {
|
|
19
|
-
if (fields[name] !== undefined && typeof fields[name] !== type)
|
|
20
|
-
throw new Error(`${file}: ${name} must be a ${type}.`);
|
|
21
|
-
}
|
|
22
|
-
// HTMLKit's hk:order is a whole number, as a file's number prefix is.
|
|
23
|
-
if (fields.position !== undefined && !(Number.isSafeInteger(fields.position) && fields.position >= 0))
|
|
24
|
-
throw new Error(`${file}: position must be a whole number.`);
|
|
25
|
-
return fields;
|
|
26
|
-
}
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { createLoomaMarkdownIt } from '@threadlabs/looma/markdown/markdown-it';
|
|
27
3
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*/
|
|
32
|
-
export function categoryFrontMatter(file, top) {
|
|
33
|
-
// HTMLKit labels and orders a folder only through its index page, so a _category without one could not apply.
|
|
34
|
-
for (let directory = dirname(file); directory.length > top.length; directory = dirname(directory)) {
|
|
35
|
-
const metadata = categoryFiles.map(name => join(directory, name)).find(existsSync);
|
|
36
|
-
if (metadata !== undefined && !readdirSync(directory).some(name => index.test(name))) {
|
|
37
|
-
throw new Error(`${metadata}: a folder's label, position and collapsed state apply through its index page; add an index.md to ${directory}.`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
if (!isIndex(file) || dirname(file) === top)
|
|
41
|
-
return undefined;
|
|
42
|
-
const { label, position } = category(dirname(file));
|
|
43
|
-
return { ...(label === undefined ? {} : { sidebarLabel: label }), ...(position === undefined ? {} : { sidebarPosition: position }) };
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* HTMLKit's navigation tree with Markupress's group policy: a folder stays open unless its `_category` collapses
|
|
47
|
-
* it, and one holding the current page is always open. `collapsed` tells whether a folder's index page URL starts closed.
|
|
4
|
+
* HTMLKit's navigation tree with Markupress's group policy: a folder stays open unless its index page's front
|
|
5
|
+
* matter says `navigation: collapsed`, and one holding the current page is always open. `collapsed` tells whether
|
|
6
|
+
* a folder's index page URL starts closed.
|
|
48
7
|
*/
|
|
49
8
|
export function generatedSidebar(tree, collapsed) {
|
|
50
9
|
return tree.map(node => ({ ...node, open: node.open || (node.children.length > 0 && !(node.href !== undefined && collapsed(node.href))),
|
|
51
10
|
children: generatedSidebar(node.children, collapsed) }));
|
|
52
11
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
12
|
+
const markdown = createLoomaMarkdownIt();
|
|
13
|
+
/**
|
|
14
|
+
* A version's footer, from the `_footer.md` in its content directory (HTMLKit routes no `_` file): each heading
|
|
15
|
+
* starts a column, its list items are links, and a paragraph after them is the copyright line.
|
|
16
|
+
*/
|
|
17
|
+
export function footer(file) {
|
|
18
|
+
const result = { columns: [], copyright: '' };
|
|
19
|
+
if (!existsSync(file))
|
|
20
|
+
return result;
|
|
21
|
+
const fail = (message) => { throw new Error(`${file}: ${message}`); };
|
|
22
|
+
// A list item's first link, labelled by its text.
|
|
23
|
+
const link = (inline) => {
|
|
24
|
+
const children = inline.children ?? [], open = children.findIndex(child => child.type === 'link_open');
|
|
25
|
+
if (open < 0)
|
|
26
|
+
return undefined;
|
|
27
|
+
const close = children.findIndex((child, index) => index > open && child.type === 'link_close');
|
|
28
|
+
return { label: children.slice(open + 1, close).map(child => child.content).join(''), href: children[open].attrGet('href') ?? '' };
|
|
29
|
+
};
|
|
30
|
+
const tokens = markdown.parse(readFileSync(file, 'utf8'), {});
|
|
31
|
+
for (const [index, token] of tokens.entries()) {
|
|
32
|
+
if (token.level !== 0 || token.nesting === -1)
|
|
33
|
+
continue;
|
|
34
|
+
if (token.type === 'heading_open')
|
|
35
|
+
result.columns.push({ title: tokens[index + 1].content, items: [] });
|
|
36
|
+
else if (token.type === 'bullet_list_open' || token.type === 'ordered_list_open') {
|
|
37
|
+
const column = result.columns.at(-1) ?? fail('a footer list follows a heading, which titles its column.');
|
|
38
|
+
for (let item = index + 1; tokens[item].level > 0; item++) {
|
|
39
|
+
if (tokens[item].type !== 'list_item_open' || tokens[item].level !== 1)
|
|
40
|
+
continue;
|
|
41
|
+
const inline = tokens.slice(item).find(next => next.type === 'inline');
|
|
42
|
+
column.items.push(link(inline) ?? fail(`the footer list item "${inline.content}" is a link.`));
|
|
43
|
+
}
|
|
65
44
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
45
|
+
else if (token.type === 'paragraph_open')
|
|
46
|
+
result.copyright = tokens[index + 1].content;
|
|
47
|
+
else
|
|
48
|
+
fail(`a footer holds headings, lists of links, and a copyright paragraph, not ${token.type.replace(/_open$/, '')}.`);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
69
51
|
}
|
|
70
|
-
const holdsCurrent = (node) => node.current === 'page' || node.children.some(holdsCurrent);
|
|
71
52
|
//# sourceMappingURL=navigation.js.map
|
package/dist/navigation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"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"}
|
package/dist/site.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ApplicationOptions, type ApplicationServer, type BuildResult, type HtmlKitPlugin, type ServerOptions } from '@nextwebwg/htmlkit';
|
|
2
|
-
import { type
|
|
2
|
+
import { type NavItem } from './navigation.js';
|
|
3
3
|
export interface DocumentationVersion {
|
|
4
4
|
readonly id: string;
|
|
5
5
|
readonly label?: string;
|
|
@@ -19,36 +19,15 @@ export interface MarkupressOptions {
|
|
|
19
19
|
readonly port?: number;
|
|
20
20
|
/** Number the lines of every highlighted code block unless its fence says `:no-line-numbers`. */
|
|
21
21
|
readonly lineNumbers?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
* An HTML Next layout component, relative to the root, that replaces the Markupress shell. It receives the
|
|
24
|
-
* loader data its `<prop>` declarations name: see "Layout data" in the README.
|
|
25
|
-
*/
|
|
26
|
-
readonly layout?: string;
|
|
27
|
-
/** The logo before the title: one image, or one per theme. Paths starting with `/` are files in `public/`. */
|
|
22
|
+
/** The logo before the title, in place of `public/logo.svg` and `public/logo-dark.svg`: one image, or one per theme. */
|
|
28
23
|
readonly logo?: string | {
|
|
29
24
|
readonly light: string;
|
|
30
25
|
readonly dark?: string;
|
|
31
26
|
};
|
|
32
|
-
/** The site's icon,
|
|
27
|
+
/** The site's icon, in place of `public/favicon.svg`. */
|
|
33
28
|
readonly favicon?: string;
|
|
34
|
-
/**
|
|
29
|
+
/** Links after the sections in the top navigation, each shown as its `icon` image or its label. */
|
|
35
30
|
readonly nav?: readonly NavItem[];
|
|
36
|
-
/** Links at the end of the top navigation, each shown as its `icon` image (from `public/`) or its label. */
|
|
37
|
-
readonly social?: readonly (Link & {
|
|
38
|
-
readonly icon?: string;
|
|
39
|
-
})[];
|
|
40
|
-
/** Text of the badge beside the title. Without it, a site with several versions shows the selected version's label. */
|
|
41
|
-
readonly versionBadge?: string;
|
|
42
|
-
/** Explicit sidebars by section path (`/`, `/editor/`). Every other section's sidebar is generated from its folder. */
|
|
43
|
-
readonly sidebar?: Readonly<Record<string, readonly SidebarItem[]>>;
|
|
44
|
-
/** Footer link columns and a copyright line. */
|
|
45
|
-
readonly footer?: {
|
|
46
|
-
readonly columns?: readonly {
|
|
47
|
-
readonly title: string;
|
|
48
|
-
readonly items: readonly Link[];
|
|
49
|
-
}[];
|
|
50
|
-
readonly copyright?: string;
|
|
51
|
-
};
|
|
52
31
|
}
|
|
53
32
|
export interface DocumentationPage {
|
|
54
33
|
readonly id: string;
|
|
@@ -57,6 +36,11 @@ export interface DocumentationPage {
|
|
|
57
36
|
/** Application-relative URL in its version, such as /v/current/guide/install/. */
|
|
58
37
|
readonly pathname: string;
|
|
59
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* A snapshot recorded in `versions/<id>/`, which keeps the project's layout: its content is `versions/<id>/<contentDir>`
|
|
41
|
+
* and its public files `versions/<id>/public`. Snapshots follow the current version, highest number first.
|
|
42
|
+
*/
|
|
43
|
+
export declare function snapshotOf(root: string, id: string, contentDir: string): DocumentationVersion;
|
|
60
44
|
/**
|
|
61
45
|
* Versioned documentation as an HTMLKit plugin. Markdown pages come from the markdown plugin, and
|
|
62
46
|
* HTMLKit routes them: each version's directory serves /v/<id>/, and the default version's also
|
package/dist/site.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAIA,OAAO,EAAoE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,
|
|
1
|
+
{"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAIA,OAAO,EAAoE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAClG,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAG7G,OAAO,EAAuC,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAIpF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wFAAwF;IACxF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AACD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACpD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,iGAAiG;IACjG,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,wHAAwH;IACxH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5E,yDAAyD;IACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,mGAAmG;IACnG,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAKD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,oBAAoB,CAE7F;AAkBD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,aAAa,CA8GzE;AAaD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,iBAAsB,EAAE,OAAO,GAAE,kBAAuB,GAAG,aAAa,CAQ5G;AAGD,wBAAsB,SAAS,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAwD;AAC9I,wBAAsB,WAAW,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAA0D;AACxJ,oFAAoF;AACpF,wBAAsB,OAAO,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAsD"}
|
package/dist/site.js
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { readdir } from 'node:fs/promises';
|
|
3
|
+
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { buildApplication, devApplication, loadConfig, previewApplication } from '@nextwebwg/htmlkit';
|
|
6
|
+
import { isIndex } from './content.js';
|
|
6
7
|
import { markdown } from './markdown.js';
|
|
7
|
-
import {
|
|
8
|
+
import { footer, generatedSidebar } from './navigation.js';
|
|
8
9
|
import { themeScript } from './theme.js';
|
|
9
10
|
import { versionLinks } from './versions.js';
|
|
11
|
+
const optionNames = ['root', 'contentDir', 'base', 'outDir', 'title', 'versions', 'defaultVersion', 'host', 'port', 'lineNumbers', 'logo', 'favicon', 'nav'];
|
|
10
12
|
function inside(root, path) {
|
|
11
13
|
const part = relative(root, path);
|
|
12
14
|
return part === '' || (!isAbsolute(part) && part !== '..' && !part.startsWith('../') && !part.startsWith('..\\'));
|
|
13
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* A snapshot recorded in `versions/<id>/`, which keeps the project's layout: its content is `versions/<id>/<contentDir>`
|
|
18
|
+
* and its public files `versions/<id>/public`. Snapshots follow the current version, highest number first.
|
|
19
|
+
*/
|
|
20
|
+
export function snapshotOf(root, id, contentDir) {
|
|
21
|
+
return { id, directory: `versions/${id}/${contentDir}`, ...(existsSync(join(root, 'versions', id, 'public')) ? { publicDir: `versions/${id}/public` } : {}) };
|
|
22
|
+
}
|
|
14
23
|
async function versions(options, root) {
|
|
15
24
|
if (options.versions)
|
|
16
25
|
return options.versions;
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
const content = options.contentDir ?? 'docs';
|
|
27
|
+
// A folder whose name starts with "." is a snapshot in progress, or its lock.
|
|
28
|
+
const ids = existsSync(join(root, 'versions')) ? (await readdir(join(root, 'versions'), { withFileTypes: true }))
|
|
29
|
+
.filter(entry => entry.isDirectory() && !entry.name.startsWith('.')).map(entry => entry.name).sort((a, b) => b.localeCompare(a, 'en', { numeric: true })) : [];
|
|
30
|
+
return [{ id: 'current', label: 'Current', directory: content }, ...ids.map(id => snapshotOf(root, id, content))];
|
|
20
31
|
}
|
|
21
32
|
const shell = fileURLToPath(new URL('../theme/shell.html', import.meta.url));
|
|
22
33
|
const own = (path) => fileURLToPath(new URL(`../${path}`, import.meta.url));
|
|
@@ -44,7 +55,6 @@ export function markupress(options = {}) {
|
|
|
44
55
|
styles: [own('styles/prose.css')],
|
|
45
56
|
...(options.lineNumbers === undefined ? {} : { lineNumbers: options.lineNumbers }),
|
|
46
57
|
publicDir: file => resolve(root, editionOf(file)?.publicDir ?? 'public'),
|
|
47
|
-
frontmatter(file) { const edition = editionOf(file); return edition && categoryFrontMatter(file, resolve(root, edition.directory)); },
|
|
48
58
|
onPage(page) {
|
|
49
59
|
const owners = editions.filter(edition => inside(resolve(root, edition.directory), page.file));
|
|
50
60
|
if (owners.length === 0 || page.href === undefined)
|
|
@@ -58,12 +68,13 @@ export function markupress(options = {}) {
|
|
|
58
68
|
if (twin)
|
|
59
69
|
throw new Error(`Documentation identity collision: ${twin.source} and ${page.file} (${id}).`);
|
|
60
70
|
documents.set(`${edition.id}\0${page.file}`, { id, version: edition.id, source: page.file, pathname: `/v/${edition.id}/${path}`,
|
|
61
|
-
aliases: page.aliases.map(alias => `/v/${edition.id}${alias}`), outline: page.outline,
|
|
62
|
-
page: { ...page.frontmatter, id, title: page.title
|
|
71
|
+
aliases: page.aliases.map(alias => `/v/${edition.id}${alias}`), outline: page.outline, generated: page.generated,
|
|
72
|
+
page: { ...page.frontmatter, id, title: page.title } });
|
|
63
73
|
}
|
|
64
74
|
},
|
|
65
75
|
});
|
|
66
76
|
let layoutFile = shell;
|
|
77
|
+
let logo, favicon;
|
|
67
78
|
async function load({ navigation, navigationTree, breadcrumbs, pager, url, base, propNames }) {
|
|
68
79
|
const path = '/' + url.pathname.slice(base.length);
|
|
69
80
|
const selected = /^\/v\/([^/]+)\//.exec(path)?.[1] ?? defaultVersion;
|
|
@@ -76,12 +87,13 @@ export function markupress(options = {}) {
|
|
|
76
87
|
const data = { title: options.title ?? 'Documentation', home: base, navigation: items,
|
|
77
88
|
...await chrome(items, navigationTree, catalog.filter(doc => doc.version === selected), selected, within.current, base),
|
|
78
89
|
versions: versionLinks(catalog, editions, page?.id ?? 'index', selected, base),
|
|
79
|
-
crumbs: await breadcrumbs(within), ...await pager(within), outline: page?.outline ?? [], page: page?.page ?? {}
|
|
90
|
+
crumbs: await breadcrumbs(within), ...await pager(within), outline: page?.outline ?? [], page: page?.page ?? {},
|
|
91
|
+
contents: page?.generated ? contents(await navigationTree({ ...within, from: page.pathname }), catalog, base, within.current) : [] };
|
|
80
92
|
// HTMLKit rejects props a layout does not declare, so a layout receives the data its contract names, and
|
|
81
93
|
// nested folder layouts' loaders read all of it from parent.markupress.
|
|
82
94
|
return { props: Object.fromEntries(propNames.filter(name => name in data).map(name => [name, data[name]])), data: { markupress: data } };
|
|
83
95
|
}
|
|
84
|
-
/** The shell around the page: top navigation, logo, badge, the section's sidebar and the footer. */
|
|
96
|
+
/** The shell around the page: the sections and links of the top navigation, logo, badge, the section's sidebar and the footer. */
|
|
85
97
|
async function chrome(items, navigationTree, pages, selected, current, base) {
|
|
86
98
|
const home = `${base}v/${selected}/`;
|
|
87
99
|
const href = (to) => /^(?:[A-Za-z][\w+.-]*:|\/\/|#)/.test(to) || !to.startsWith('/') ? to : home + to.slice(1);
|
|
@@ -91,35 +103,38 @@ export function markupress(options = {}) {
|
|
|
91
103
|
const target = href(to), path = target.split('#')[0];
|
|
92
104
|
return { label, href: target, current: target === current ? 'page' : path !== home && path.endsWith('/') && current.startsWith(path) ? 'true' : 'false' };
|
|
93
105
|
};
|
|
94
|
-
// The section is the
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
const collapsed = (page) => { const source = sources.get(page); return source !== undefined && isIndex(source) && category(dirname(source)).collapsed === true; };
|
|
103
|
-
const logo = typeof options.logo === 'string' ? { light: options.logo, dark: options.logo } : options.logo;
|
|
106
|
+
// The section is the page's top-level folder, else the root.
|
|
107
|
+
const top = /^([^/]+)\//.exec(current.slice(home.length))?.[1];
|
|
108
|
+
const section = top !== undefined && items.some(item => item.href.startsWith(`${home}${top}/`) && item.href !== `${home}${top}/`) ? `/${top}/` : '/';
|
|
109
|
+
// Each top-level folder with an index page is a section, linked from the top navigation by its index page.
|
|
110
|
+
const sections = (await navigationTree({ from: `/v/${selected}/`, current })).filter(node => node.href !== undefined && node.children.length > 0);
|
|
111
|
+
// Pages recompile after each edit, so a folder's collapsed state follows its index page in development.
|
|
112
|
+
const folded = new Set(pages.filter(doc => doc.page.navigation === 'collapsed').map(doc => base + doc.pathname.slice(1)));
|
|
113
|
+
const collapsed = (page) => folded.has(page);
|
|
104
114
|
const versions = editions.length > 1 ? editions.find(edition => edition.id === selected) : undefined;
|
|
115
|
+
const edition = editions.find(entry => entry.id === selected);
|
|
116
|
+
const { columns, copyright } = footer(resolve(root, edition.directory, '_footer.md'));
|
|
105
117
|
return {
|
|
106
|
-
sidebar:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (!('items' in item))
|
|
110
|
-
return { ...link(item), items: [] };
|
|
111
|
-
const links = item.items.map(link);
|
|
112
|
-
return { label: item.label, href: '', current: links.some(entry => entry.current !== 'false') ? 'true' : 'false', items: links };
|
|
113
|
-
}),
|
|
118
|
+
sidebar: generatedSidebar(await navigationTree({ from: `/v/${selected}${section}`, current }), collapsed),
|
|
119
|
+
nav: [...sections.map(node => ({ ...link({ label: node.label, href: '/' + node.href.slice(home.length) }), icon: '' })),
|
|
120
|
+
...(options.nav ?? []).map(entry => ({ ...link(entry), icon: entry.icon === undefined ? '' : file(entry.icon) }))],
|
|
114
121
|
logo: logo === undefined ? null : { light: file(logo.light), dark: file(logo.dark ?? logo.light) },
|
|
115
|
-
favicon:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
footer: { columns: (options.footer?.columns ?? []).map(column => ({ title: column.title, items: column.items.map(link) })), copyright: options.footer?.copyright ?? '' },
|
|
122
|
+
favicon: favicon === undefined ? null : file(favicon),
|
|
123
|
+
badge: versions ? versions.label ?? versions.id : '',
|
|
124
|
+
footer: { columns: columns.map(column => ({ title: column.title, items: column.items.map(link) })), copyright },
|
|
119
125
|
};
|
|
120
126
|
}
|
|
127
|
+
// Config is plain data, so an option from another tool, or a removed one, fails rather than doing nothing.
|
|
128
|
+
for (const name of Object.keys(options))
|
|
129
|
+
if (!optionNames.includes(name))
|
|
130
|
+
throw new Error(`Unknown Markupress option ${name}. Options are ${optionNames.join(', ')}.`);
|
|
121
131
|
return { ...pages, name: 'markupress', async config(htmlkit) {
|
|
122
132
|
root = resolve(htmlkit.root ?? root);
|
|
133
|
+
// Conventions first: public/logo.svg, public/logo-dark.svg and public/favicon.svg, which the options override.
|
|
134
|
+
const own = (name) => existsSync(join(root, 'public', name)) ? `/${name}` : undefined;
|
|
135
|
+
const light = own('logo.svg');
|
|
136
|
+
logo = typeof options.logo === 'string' ? { light: options.logo } : options.logo ?? (light === undefined ? undefined : { light, dark: own('logo-dark.svg') ?? light });
|
|
137
|
+
favicon = options.favicon ?? own('favicon.svg');
|
|
123
138
|
editions = await versions(options, root);
|
|
124
139
|
const ids = new Set();
|
|
125
140
|
for (const edition of editions) {
|
|
@@ -129,23 +144,30 @@ export function markupress(options = {}) {
|
|
|
129
144
|
const content = resolve(root, edition.directory);
|
|
130
145
|
if (!inside(root, content))
|
|
131
146
|
throw new Error(`Version ${edition.id}: directory must be inside the project.`);
|
|
132
|
-
if (!(await readdir(content)).some(
|
|
147
|
+
if (!(await readdir(content)).some(isIndex))
|
|
133
148
|
throw new Error(`Version ${edition.id}: a root index.md landing page is required.`);
|
|
134
149
|
}
|
|
135
150
|
const home = editions.find(edition => edition.id === defaultVersion);
|
|
136
151
|
if (home === undefined)
|
|
137
152
|
throw new Error(`Unknown defaultVersion: ${defaultVersion}.`);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (options.layout !== undefined)
|
|
143
|
-
layoutFile = resolve(root, options.layout);
|
|
153
|
+
// The shell is the layout of Markupress's own folders only, so a site's other pages keep theirs. A site's
|
|
154
|
+
// app/layouts/default.html, which HTMLKit gives its other pages, replaces the shell and receives its loader data.
|
|
155
|
+
const replacement = join(root, 'app/layouts/default.html');
|
|
156
|
+
layoutFile = existsSync(replacement) ? replacement : shell;
|
|
144
157
|
const layout = { component: layoutFile, server: { load } };
|
|
145
158
|
return { headScript: themeScript, css: pageStyles,
|
|
146
159
|
pages: [...editions.map(edition => ({ dir: edition.directory, prefix: `/v/${edition.id}/`, layout })), { dir: home.directory, layout }] };
|
|
147
160
|
} };
|
|
148
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* A generated index's entries: the pages and folders directly in its folder, from HTMLKit's navigation tree below
|
|
164
|
+
* the index page, each with its page's description. A folder without an index page (a route group) adds its pages.
|
|
165
|
+
*/
|
|
166
|
+
function contents(tree, catalog, base, self) {
|
|
167
|
+
const descriptions = new Map(catalog.map(doc => [base + doc.pathname.slice(1), doc.page.description ?? '']));
|
|
168
|
+
const entries = (nodes) => nodes.flatMap(node => node.href === undefined ? entries(node.children) : node.href === self ? [] : [{ label: node.label, href: node.href, description: descriptions.get(node.href) ?? '' }]);
|
|
169
|
+
return entries(tree);
|
|
170
|
+
}
|
|
149
171
|
/**
|
|
150
172
|
* HTMLKit options for a Markupress site, as the CLI runs it: the site's own HTMLKit options (its `htmlkit.config.ts`)
|
|
151
173
|
* with `markupress()` added after their plugins. Markupress's own `base` and `outDir`, when set, win over HTMLKit's.
|