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.
@@ -1 +1 @@
1
- {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/controllers/shell.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;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,MAAgB,EAAE,EAAE;YACjC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,sBAAsB,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC;oBAAE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC1I,CAAC,CAAC;QACF,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,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,aAAa,CAAc,wCAAwC,CAAC,EAAE,WAAW,EAAE,CAAC;QAC1G,CAAC,CAAC;QACF,8FAA8F;QAC9F,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"}
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, SidebarItem, SidebarNode } from './navigation.js';
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';
@@ -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,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/E,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"}
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"}
@@ -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 folder's label and position on its index page. */
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
  /**
@@ -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,qHAAqH;IACrH,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,CAkB3E"}
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
  } } };
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"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"}
@@ -1,20 +1,12 @@
1
- import type { NavigationItem, NavigationNode } from '@nextwebwg/htmlkit';
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 entry: a link, or a dropdown of links. */
9
- export type NavItem = Link | {
10
- readonly label: string;
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
- * The front matter a folder's `_category` gives its index page: the label and position that HTMLKit's navigation
39
- * tree reads from the index page as the folder's own. `top` is the version's content directory, whose index page
40
- * is the version home and has no folder to order.
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 categoryFrontMatter(file: string, top: string): FrontMatter | undefined;
25
+ export declare function generatedSidebar(tree: readonly NavigationNode[], collapsed: (href: string) => boolean): SidebarNode[];
43
26
  /**
44
- * HTMLKit's navigation tree with Markupress's group policy: a folder stays open unless its `_category` collapses
45
- * it, and one holding the current page is always open. `collapsed` tells whether a folder's index page URL starts closed.
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 generatedSidebar(tree: readonly NavigationNode[], collapsed: (href: string) => boolean): SidebarNode[];
48
- /** A configured sidebar, resolved against the version's pages so entries get their labels and current page. */
49
- export declare function configuredSidebar(entries: readonly SidebarItem[], items: readonly NavigationItem[], href: (path: string) => string, current: string, depth?: number): SidebarNode[];
50
- export {};
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
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,mGAAmG;AACnG,MAAM,WAAW,IAAI;IAAG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AACvE,8DAA8D;AAC9D,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAA;CAAE,CAAC;AACzF,4GAA4G;AAC5G,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,IAAI,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE3I,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;AAGD,0DAA0D;AAC1D,eAAO,MAAM,OAAO,SAAU,MAAM,YAA+B,CAAC;AAIpE,UAAU,QAAQ;IAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE;AACxG,oEAAoE;AACpE,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAYpD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAWtF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,EAAE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,WAAW,EAAE,CAGrH;AAED,+GAA+G;AAC/G,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,EAAE,KAAK,EAAE,SAAS,cAAc,EAAE,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,WAAW,EAAE,CAc9K"}
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"}
@@ -1,71 +1,52 @@
1
- import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
- import { basename, dirname, join } from 'node:path';
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
- * The front matter a folder's `_category` gives its index page: the label and position that HTMLKit's navigation
29
- * tree reads from the index page as the folder's own. `top` is the version's content directory, whose index page
30
- * is the version home and has no folder to order.
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
- /** A configured sidebar, resolved against the version's pages so entries get their labels and current page. */
54
- export function configuredSidebar(entries, items, href, current, depth = 0) {
55
- return entries.map(entry => {
56
- if (typeof entry === 'string') {
57
- const page = items.find(item => item.href === href(entry));
58
- if (page === undefined)
59
- throw new Error(`sidebar: no page at ${entry}.`);
60
- return { label: page.label, href: page.href, current: page.current, open: false, depth, children: [] };
61
- }
62
- if ('items' in entry) {
63
- const children = configuredSidebar(entry.items, items, href, current, depth + 1);
64
- return { label: entry.label, current: 'false', open: entry.collapsed !== true || children.some(holdsCurrent), depth, children };
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
- const target = href(entry.href);
67
- return { label: entry.label, href: target, current: target === current ? 'page' : 'false', open: false, depth, children: [] };
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
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAkB/E;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAA+B,EAAE,SAAoC;IACpG,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACrI,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,EAAwC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IACpF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IACrC,MAAM,IAAI,GAAG,CAAC,OAAe,EAAS,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,kDAAkD;IAClD,MAAM,IAAI,GAAG,CAAC,MAAa,EAAoB,EAAE;QAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;QACvG,IAAI,IAAI,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;QAChG,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;IACtI,CAAC,CAAC;IACF,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9D,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9C,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC;YAAE,SAAS;QACxD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;YAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,CAAC,CAAE,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;aACpG,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;YACjF,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,2DAA2D,CAAC,CAAC;YAC1G,KAAK,IAAI,IAAI,GAAG,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAE,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;gBAC3D,IAAI,MAAM,CAAC,IAAI,CAAE,CAAC,IAAI,KAAK,gBAAgB,IAAI,MAAM,CAAC,IAAI,CAAE,CAAC,KAAK,KAAK,CAAC;oBAAE,SAAS;gBACnF,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAE,CAAC;gBACxE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,yBAAyB,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC;YACjG,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;YAAE,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAE,CAAC,OAAO,CAAC;;YACrF,IAAI,CAAC,2EAA2E,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IAC5H,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
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 Link, type NavItem, type SidebarItem } from './navigation.js';
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, such as `/favicon.svg` from `public/`. */
27
+ /** The site's icon, in place of `public/favicon.svg`. */
33
28
  readonly favicon?: string;
34
- /** Top navigation: links, and dropdowns of links. A path starting with `/` is a page of the version being read. */
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
@@ -1 +1 @@
1
- {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"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"}
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 { readFile, readdir } from 'node:fs/promises';
3
- import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
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 { category, categoryFrontMatter, configuredSidebar, generatedSidebar, isIndex } from './navigation.js';
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 manifest = join(root, 'versioned_docs/versions.json');
18
- const snapshots = existsSync(manifest) ? JSON.parse(await readFile(manifest, 'utf8')) : [];
19
- return [{ id: 'current', label: 'Current', directory: options.contentDir ?? 'docs' }, ...snapshots];
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, label: page.label } });
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 longest configured sidebar path holding the page, else its top-level folder, else the root.
95
- const path = '/' + current.slice(home.length);
96
- const top = /^\/([^/]+)\//.exec(path)?.[1];
97
- const section = Object.keys(options.sidebar ?? {}).filter(key => key !== '/' && path.startsWith(key)).sort((a, b) => b.length - a.length)[0]
98
- ?? (top !== undefined && items.some(item => item.href.startsWith(`${home}${top}/`) && item.href !== `${home}${top}/`) ? `/${top}/` : '/');
99
- const explicit = options.sidebar?.[section];
100
- // A _category's collapsed state is read on each render, so editing it applies in development.
101
- const sources = new Map(pages.map(doc => [base + doc.pathname.slice(1), doc.source]));
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: explicit ? configuredSidebar(explicit, items, href, current)
107
- : generatedSidebar(await navigationTree({ from: `/v/${selected}${section}`, current }), collapsed),
108
- nav: (options.nav ?? []).map(item => {
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: options.favicon === undefined ? null : file(options.favicon),
116
- social: (options.social ?? []).map(entry => ({ label: entry.label, href: href(entry.href), icon: entry.icon === undefined ? '' : file(entry.icon) })),
117
- badge: options.versionBadge ?? (versions ? versions.label ?? versions.id : ''),
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(name => /^(?:\d+\.)?index\.md$/.test(name)))
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
- for (const key of Object.keys(options.sidebar ?? {}))
139
- if (!/^\/(?:[^/]+\/)*$/.test(key))
140
- throw new Error(`sidebar: ${key} must be a path such as / or /guide/.`);
141
- // The shell is the layout of Markupress's own folders only, so a site's other pages keep theirs.
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.