@heroiclands/package-build 11.0.0 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +279 -0
- package/CONTENT.md +96 -49
- package/MIGRATING.md +65 -0
- package/content-config.mjs +51 -22
- package/docs/content-format.md +51 -27
- package/engine/base-compiler.mjs +6 -1
- package/engine/content-address.mjs +24 -73
- package/engine/frontmatter-lint.mjs +92 -1
- package/engine/manifest-emit.mjs +16 -18
- package/engine/note-vocabulary.mjs +159 -1
- package/engine/retired-fields.mjs +76 -3
- package/engine/site-build.mjs +70 -78
- package/engine/site-index.mjs +20 -8
- package/engine/web-wikilinks.mjs +1 -1
- package/engine/wikilinks.mjs +1 -1
- package/package.json +1 -1
- package/types/content-config.d.mts +29 -17
- package/types/engine/content-address.d.mts +22 -34
- package/types/engine/note-vocabulary.d.mts +76 -0
- package/types/engine/retired-fields.d.mts +47 -0
- package/types/engine/site-build.d.mts +56 -31
- package/types/engine/site-index.d.mts +9 -4
|
@@ -56,28 +56,36 @@ export namespace DEFAULT_PATHS {
|
|
|
56
56
|
*/
|
|
57
57
|
export const PACK_DOCUMENT_TYPES: readonly ["Actor", "Adventure", "Item", "JournalEntry", "Macro", "Scene"];
|
|
58
58
|
/**
|
|
59
|
-
* The landing-page rules a repository may route by.
|
|
59
|
+
* The landing-page rules a repository may route by. **Inert since #204.**
|
|
60
60
|
*
|
|
61
|
-
* A *landing page*
|
|
62
|
-
* within one, so it
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* published:
|
|
61
|
+
* A *landing page* was a note that addressed a whole section rather than a page
|
|
62
|
+
* within one, so it had no slug of its own. There are no sections in the note
|
|
63
|
+
* format any more — a section is a Hugo content directory, and a page's address
|
|
64
|
+
* names no directory — so there are no landings and this selects nothing.
|
|
66
65
|
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* The two are not disjoint and cannot simply both apply: each tree holds notes
|
|
76
|
-
* the other rule would move.
|
|
66
|
+
* The key survives its own mechanism on purpose. Both publishing consumers
|
|
67
|
+
* declare `landing: readme`, which stated something true when they wrote it;
|
|
68
|
+
* refusing it now would break them over a correct statement, and silently
|
|
69
|
+
* ignoring an unknown value would be worse. So `readme` stays accepted, the
|
|
70
|
+
* retired `collection` stays refused by name (below), and the key is deleted
|
|
71
|
+
* once no configuration writes it — `content-config.mjs` has no warning channel
|
|
72
|
+
* with which to say "accepted, and does nothing" in between.
|
|
77
73
|
*
|
|
78
74
|
* @type {readonly string[]}
|
|
79
75
|
*/
|
|
80
76
|
export const LANDING_RULES: readonly string[];
|
|
77
|
+
/**
|
|
78
|
+
* What a configuration naming the retired `collection` landing rule is told.
|
|
79
|
+
*
|
|
80
|
+
* A retired *value* is refused the way a retired *field* is (see
|
|
81
|
+
* `engine/retired-fields.mjs`): left merely unrecognized it would be reported
|
|
82
|
+
* as a bad value, which names something to correct and leaves the author to
|
|
83
|
+
* work out for themselves that the mechanism is gone. The message says the rule
|
|
84
|
+
* is retired, what lands a section instead, and what to do with the key.
|
|
85
|
+
*
|
|
86
|
+
* @type {Readonly<Record<string, string>>}
|
|
87
|
+
*/
|
|
88
|
+
export const RETIRED_LANDING_RULES: Readonly<Record<string, string>>;
|
|
81
89
|
/**
|
|
82
90
|
* A repository's address scheme, with the defaults an unconfigured one gets.
|
|
83
91
|
*
|
|
@@ -87,6 +95,8 @@ export const LANDING_RULES: readonly string[];
|
|
|
87
95
|
* own mount point: where the package itself is served is the consuming build's
|
|
88
96
|
* knowledge, held in `PACKAGE_BASE` (`engine/kb-manifest.mjs`) and prefixed at
|
|
89
97
|
* resolve time, so it is never recorded here (#1465).
|
|
98
|
+
*
|
|
99
|
+
* `landing` is inert — see {@link LANDING_RULES}.
|
|
90
100
|
*/
|
|
91
101
|
export const DEFAULT_ADDRESS_SCHEME: Readonly<{
|
|
92
102
|
prefix: "";
|
|
@@ -506,7 +516,9 @@ export type AddressSchemeInput = {
|
|
|
506
516
|
*/
|
|
507
517
|
prefix?: string | undefined;
|
|
508
518
|
/**
|
|
509
|
-
* Which note
|
|
519
|
+
* Which note addressed a whole section. Inert
|
|
520
|
+
* since #204 retired sections from the note format — see
|
|
521
|
+
* {@link LANDING_RULES}.
|
|
510
522
|
*/
|
|
511
523
|
landing?: string | undefined;
|
|
512
524
|
};
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The URL section a note routes to.
|
|
3
|
-
*
|
|
4
|
-
* A `doc` is narrative content whose only identity is its subtype label, so it
|
|
5
|
-
* routes by `subType`; every other type names its own section.
|
|
6
|
-
*
|
|
7
|
-
* @param {object} fm - Parsed frontmatter.
|
|
8
|
-
* @returns {string|undefined} The section, or `undefined` when the note has
|
|
9
|
-
* none — a `doc` with no subtype has no address and is not published.
|
|
10
|
-
*/
|
|
11
|
-
export function sectionOf(fm: object): string | undefined;
|
|
12
1
|
/**
|
|
13
2
|
* The single path segment a note is addressed by: `type-shortcode`.
|
|
14
3
|
*
|
|
@@ -34,15 +23,16 @@ export function addressSlug(fm: object): string;
|
|
|
34
23
|
/**
|
|
35
24
|
* A note's address below the knowledgebase mount, e.g. `affliction-aconite/`.
|
|
36
25
|
*
|
|
37
|
-
* A `README.md`
|
|
38
|
-
*
|
|
26
|
+
* Every note, without exception. A `README.md` used to be its section's landing
|
|
27
|
+
* page and to address the section instead of itself; a section is a Hugo
|
|
28
|
+
* directory concept the note format no longer carries (#204), so a file's name
|
|
29
|
+
* decides nothing about where it publishes.
|
|
39
30
|
*
|
|
40
31
|
* @param {object} fm - Parsed frontmatter.
|
|
41
|
-
* @param {boolean} isReadme - Whether the file is a `README.md`.
|
|
42
32
|
* @returns {string} The mount-relative address, with a trailing slash.
|
|
43
33
|
* @throws {Error} When the note has no address.
|
|
44
34
|
*/
|
|
45
|
-
export function contentAddress(fm: object
|
|
35
|
+
export function contentAddress(fm: object): string;
|
|
46
36
|
/**
|
|
47
37
|
* A note's address relative to its **package**, e.g. `affliction-aconite/`.
|
|
48
38
|
*
|
|
@@ -51,36 +41,34 @@ export function contentAddress(fm: object, isReadme: boolean): string;
|
|
|
51
41
|
* address the site does not publish resolves at build time and 404s for the
|
|
52
42
|
* reader, which is the failure this module exists to prevent.
|
|
53
43
|
*
|
|
44
|
+
* **It is a pure function of the frontmatter.** Nothing about the file the note
|
|
45
|
+
* was read from reaches it: the `README.md` convention that made one note
|
|
46
|
+
* address a whole section is retired with the section itself (#204), so there
|
|
47
|
+
* is one rule and no branch.
|
|
48
|
+
*
|
|
54
49
|
* **The prefix does not apply to a page's own address.** `prefix` says where the
|
|
55
|
-
* content tree *mounts inside the package* —
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* `/sohl/kb/affliction/`. The `type-` half is what keeps that flat namespace
|
|
61
|
-
* clear of the package's fixed mounts — `/<package>/` for the landing,
|
|
50
|
+
* content tree *mounts inside the package* — the Hugo directory its pages are
|
|
51
|
+
* written under — and an address is `(type, shortcode)`, a package-wide identity
|
|
52
|
+
* that takes no mount: `sohl` publishes `/sohl/affliction-aconite/` from a file
|
|
53
|
+
* written under `kb/`. The `type-` half is what keeps that flat namespace clear
|
|
54
|
+
* of the package's fixed mounts — `/<package>/` for the landing,
|
|
62
55
|
* `/<package>/api/` for generated API docs, neither of which contains a hyphen
|
|
63
56
|
* or names a type.
|
|
64
57
|
*
|
|
65
|
-
* **The section still decides where the *file* is written**, which is why a
|
|
66
|
-
* note without one still has no address: Hugo derives a section from a page's
|
|
67
|
-
* directory rather than from its URL, so a page with nowhere to be filed is a
|
|
68
|
-
* page with no section landing, no `.CurrentSection` and no per-section layout.
|
|
69
|
-
*
|
|
70
58
|
* @param {object} fm - Parsed frontmatter.
|
|
71
59
|
* @param {object} [options] - Options.
|
|
72
|
-
* @param {boolean} [options.isReadme] - Whether the file is a `README.md`.
|
|
73
60
|
* @param {{prefix?: string, landing?: string}} [options.scheme] - The
|
|
74
61
|
* repository's address scheme; defaults to {@link DEFAULT_ADDRESS_SCHEME}.
|
|
62
|
+
* `landing` is validated against {@link LANDING_RULES} and selects nothing —
|
|
63
|
+
* it is accepted so a configuration declaring the still-true `landing: readme`
|
|
64
|
+
* keeps loading, and is removed once none does.
|
|
75
65
|
* @returns {string} The package-relative address, with a trailing slash and no
|
|
76
66
|
* leading one.
|
|
77
|
-
* @throws {Error} When the note has no
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* in the manifest.
|
|
67
|
+
* @throws {Error} When the note has no type or no shortcode to be addressed by.
|
|
68
|
+
* Such a note is not published, and inventing an address for one would put a
|
|
69
|
+
* dead entry in the manifest.
|
|
81
70
|
*/
|
|
82
|
-
export function packageAddress(fm: object, {
|
|
83
|
-
isReadme?: boolean | undefined;
|
|
71
|
+
export function packageAddress(fm: object, { scheme }?: {
|
|
84
72
|
scheme?: {
|
|
85
73
|
prefix?: string;
|
|
86
74
|
landing?: string;
|
|
@@ -35,6 +35,57 @@ export function hasTag(fm: object | null | undefined, tag: string): boolean;
|
|
|
35
35
|
* @returns {boolean} Whether the note carries the `draft` tag.
|
|
36
36
|
*/
|
|
37
37
|
export function isDraftNote(fm: object | null | undefined): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* What to write in place of a retired subType value, if it is one.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} type - The note's `type`.
|
|
42
|
+
* @param {string} value - The authored `subType`.
|
|
43
|
+
* @param {Readonly<Record<string, Readonly<Record<string, string>>>>} [retired]
|
|
44
|
+
* The map to read, defaulting to {@link RETIRED_SUBTYPES}.
|
|
45
|
+
* @returns {string|undefined} The current spelling, or `undefined` when the
|
|
46
|
+
* value is not a retired one — which is not the same as it being valid.
|
|
47
|
+
*/
|
|
48
|
+
export function retiredSubType(type: string, value: string, retired?: Readonly<Record<string, Readonly<Record<string, string>>>>): string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* What a note carrying a retired subType is told.
|
|
51
|
+
*
|
|
52
|
+
* One message, so the lint and any later refusal cannot describe the same
|
|
53
|
+
* retirement differently.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} type - The note's `type`.
|
|
56
|
+
* @param {string} value - The retired spelling the note carries.
|
|
57
|
+
* @param {string} replacement - What to write instead.
|
|
58
|
+
* @returns {string} The message.
|
|
59
|
+
*/
|
|
60
|
+
export function retiredSubTypeMessage(type: string, value: string, replacement: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* What a note carrying a subType outside the address charset is told.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} value - The authored `subType`.
|
|
65
|
+
* @returns {string} The message.
|
|
66
|
+
*/
|
|
67
|
+
export function subTypeCharsetMessage(value: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* What a note carrying a type outside the address charset is told.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} type - The authored `type`.
|
|
72
|
+
* @returns {string} The message.
|
|
73
|
+
*/
|
|
74
|
+
export function typeCharsetMessage(type: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Refuse a vocabulary that declares a type or subType outside the charset.
|
|
77
|
+
*
|
|
78
|
+
* Run over {@link NOTE_VOCABULARY} as this module loads, so a declaration that
|
|
79
|
+
* breaks the rule cannot be imported. That is stricter than a lint on purpose:
|
|
80
|
+
* a note's bad value is one author's mistake and belongs in a report, while a
|
|
81
|
+
* bad *declaration* would tell every author to write something unaddressable.
|
|
82
|
+
*
|
|
83
|
+
* @param {Readonly<Record<string, TypeVocabulary>>} vocabulary - The registry.
|
|
84
|
+
* @param {string} [where] - What declares it, for the message.
|
|
85
|
+
* @throws {Error} Naming every offending type and subType at once, rather than
|
|
86
|
+
* stopping at the first — a reader fixing a list wants the whole list.
|
|
87
|
+
*/
|
|
88
|
+
export function assertVocabularyCharset(vocabulary: Readonly<Record<string, TypeVocabulary>>, where?: string): void;
|
|
38
89
|
/**
|
|
39
90
|
* The `data:` keys a note type may carry.
|
|
40
91
|
*
|
|
@@ -237,6 +288,31 @@ export const NOTE_VOCABULARY: Readonly<{
|
|
|
237
288
|
})[];
|
|
238
289
|
}>;
|
|
239
290
|
}>;
|
|
291
|
+
/**
|
|
292
|
+
* The retired spelling of a subType a type declares → what to write now (#206).
|
|
293
|
+
*
|
|
294
|
+
* Keyed by type, because a retirement is a statement about *that type's*
|
|
295
|
+
* vocabulary: `user-guide` on a `doc` is the old spelling of `userguide`, while
|
|
296
|
+
* the same string on any other type is nothing but a charset violation, and
|
|
297
|
+
* saying "did you mean userguide" there would be a guess dressed as a fact.
|
|
298
|
+
*
|
|
299
|
+
* **Recorded here rather than left in `subTypes`** so the declared list stays
|
|
300
|
+
* the list of values a note *should* write. A retired value is accepted, not
|
|
301
|
+
* declared — the difference is exactly what makes the finding possible.
|
|
302
|
+
*
|
|
303
|
+
* **Deliberately not the shape of a type rename** ({@link
|
|
304
|
+
* import("./ids.mjs").RETIRED_TYPES}), which is an error: a retired type routes
|
|
305
|
+
* a note to the wrong pack, whereas a retired subType still compiles to the
|
|
306
|
+
* correct page. The sweep is the consumer's, and the ordering is the reverse of
|
|
307
|
+
* the usual — the acceptance ships *first*, because declaring only the new
|
|
308
|
+
* spelling while 43 `sohl` notes still author the old one would invalidate all
|
|
309
|
+
* 43 with a release they had no chance to sweep ahead of. A later change
|
|
310
|
+
* removes this map, and the old spelling then falls through to the ordinary
|
|
311
|
+
* undeclared-value error with no code left to remove.
|
|
312
|
+
*
|
|
313
|
+
* @type {Readonly<Record<string, Readonly<Record<string, string>>>>}
|
|
314
|
+
*/
|
|
315
|
+
export const RETIRED_SUBTYPES: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
240
316
|
/**
|
|
241
317
|
* One `data:` key a note type may carry.
|
|
242
318
|
*
|
|
@@ -106,6 +106,53 @@ export function assertNoAliasesField(fm: object | null | undefined, { file, absP
|
|
|
106
106
|
* @returns {boolean} Whether the retired field is declared.
|
|
107
107
|
*/
|
|
108
108
|
export function declaresRetiredAliasesField(fm: object | null | undefined): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* What a note declaring `section:` is told, in one place.
|
|
111
|
+
*
|
|
112
|
+
* Shared by the compile-time refusal and the frontmatter lint, because an
|
|
113
|
+
* author meets whichever of the two runs first and they should read the same.
|
|
114
|
+
* It names what lands a section now rather than a value to correct: no value
|
|
115
|
+
* makes declaring the field right.
|
|
116
|
+
*
|
|
117
|
+
* **What it did (#202).** It named the section a `collection` note headed,
|
|
118
|
+
* under the `collection` landing rule — the only reader it ever had, in the
|
|
119
|
+
* second branch of `landingOf` (`engine/content-address.mjs`). That rule went
|
|
120
|
+
* first, and the whole mechanism went with it (#204): a section is a Hugo
|
|
121
|
+
* directory the note format does not carry, so no note lands one and a page
|
|
122
|
+
* that introduces a type is an ordinary note addressed `doc-<type>`. Nothing
|
|
123
|
+
* else read the field, and no schema or vocabulary declared it, so left in
|
|
124
|
+
* place it would be ignored in silence — the note saying one thing and the
|
|
125
|
+
* build doing another.
|
|
126
|
+
*
|
|
127
|
+
* @param {string} [file] - The note's path, named in the message. Omit it where
|
|
128
|
+
* the caller emits through a diagnostic, whose locator already starts the
|
|
129
|
+
* line — repeating it prints the path twice.
|
|
130
|
+
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
131
|
+
*/
|
|
132
|
+
export function sectionRetiredMessage(file?: string): string;
|
|
133
|
+
/**
|
|
134
|
+
* Refuse a note that declares `section:` at all.
|
|
135
|
+
*
|
|
136
|
+
* Presence is the whole test, as it is for `draft:` and `aliases:`: an empty
|
|
137
|
+
* value reads as "this note heads a section and names none", a statement about
|
|
138
|
+
* a rule that no longer exists.
|
|
139
|
+
*
|
|
140
|
+
* @param {object|null|undefined} fm - Parsed frontmatter, or nothing when it
|
|
141
|
+
* could not be parsed.
|
|
142
|
+
* @param {object} [options] - Options.
|
|
143
|
+
* @param {string} [options.file] - The note's path, named in the message. Omit
|
|
144
|
+
* it where the caller emits through a diagnostic, which puts the locator at
|
|
145
|
+
* the start of the line already — repeating it prints the path twice.
|
|
146
|
+
* @param {string} [options.absPath] - The note's file on disk, read only on the
|
|
147
|
+
* failing path to locate the offending line and column. The position rides on
|
|
148
|
+
* the thrown error as `position`, for a caller that emits a diagnostic.
|
|
149
|
+
* @returns {void}
|
|
150
|
+
* @throws {Error} When the note declares the field.
|
|
151
|
+
*/
|
|
152
|
+
export function assertNoSectionField(fm: object | null | undefined, { file, absPath }?: {
|
|
153
|
+
file?: string | undefined;
|
|
154
|
+
absPath?: string | undefined;
|
|
155
|
+
}): void;
|
|
109
156
|
/**
|
|
110
157
|
* A frontmatter key's position in a note's file, or nothing.
|
|
111
158
|
*
|
|
@@ -196,12 +196,10 @@ export function sectionFrontmatter(meta: object): object;
|
|
|
196
196
|
* redirects of its own.
|
|
197
197
|
*
|
|
198
198
|
* A content page states its own **`url`**, which is its address rather than its
|
|
199
|
-
* path (#181).
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* lookup. So the directory stays and the address is stated, and the two are
|
|
204
|
-
* free to differ.
|
|
199
|
+
* path (#181). It is written flat under the content mount (#204), so Hugo would
|
|
200
|
+
* otherwise publish it at `<mount><type>-<shortcode>/` rather than at the
|
|
201
|
+
* package-wide address the link manifest records — the same address, one
|
|
202
|
+
* segment too deep. So the address is stated and the mount does not reach it.
|
|
205
203
|
*
|
|
206
204
|
* A content page carries the package the build **derived** (#65). No note
|
|
207
205
|
* declares one — `package:` is retired (#56) — so the note's frontmatter alone
|
|
@@ -213,24 +211,34 @@ export function sectionFrontmatter(meta: object): object;
|
|
|
213
211
|
* self-describing and makes sweeping the field out of a content tree
|
|
214
212
|
* output-preserving for a site as it already is for the packs.
|
|
215
213
|
*
|
|
214
|
+
* A **tree** page is the one that still reads `readmeSections`: a `trees` entry
|
|
215
|
+
* keeps its source layout below a named section, so its own `README` is that
|
|
216
|
+
* section's landing and takes the title and hero the section declares.
|
|
217
|
+
*
|
|
216
218
|
* @param {object} page - The page.
|
|
217
|
-
* @param {object} options - `{
|
|
219
|
+
* @param {object} options - `{ readmeSections, decorate }`.
|
|
218
220
|
* @returns {object} The frontmatter to write.
|
|
219
221
|
*/
|
|
220
222
|
export function pageFrontmatter(page: object, { readmeSections, decorate }: object): object;
|
|
221
223
|
/**
|
|
222
224
|
* Where a page is written, relative to the output root.
|
|
223
225
|
*
|
|
224
|
-
* **
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
* Hugo
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* The
|
|
232
|
-
* two
|
|
233
|
-
*
|
|
226
|
+
* **Flat, under the mount, named by its address** (#204). A content page's URL
|
|
227
|
+
* is its address — `/<package>/<type>-<shortcode>/` — and the file is now named
|
|
228
|
+
* the same way, so the two agree. It used to be filed into `<section>/` so that
|
|
229
|
+
* Hugo would read a section off its path; a section appears in no address, and
|
|
230
|
+
* a directory chosen only to satisfy a rendering engine's idea of what a
|
|
231
|
+
* section is has no business in the note format.
|
|
232
|
+
*
|
|
233
|
+
* The name is the *whole* address rather than a section-relative half of it, so
|
|
234
|
+
* two types cannot fight over one file: a `doc` note's `subType` may be spelled
|
|
235
|
+
* the same as another note's `type`, and `doc-gear.md` and `weapongear-gear.md`
|
|
236
|
+
* are distinct whatever the sections used to be.
|
|
237
|
+
*
|
|
238
|
+
* **A `trees` entry is the exception, and always was.** Those pages preserve
|
|
239
|
+
* their source layout below a named section — they are a book with chapters,
|
|
240
|
+
* addressed by their path — so a `README` there is still its directory's
|
|
241
|
+
* `_index.md`.
|
|
234
242
|
*/
|
|
235
243
|
export function pageDestination(page: any): string;
|
|
236
244
|
/**
|
|
@@ -259,26 +267,43 @@ export function renderPages(pages: object[], options: object): {
|
|
|
259
267
|
wikiErrors: object[];
|
|
260
268
|
};
|
|
261
269
|
/**
|
|
262
|
-
* Writes the
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
* is
|
|
266
|
-
*
|
|
270
|
+
* Writes the Hugo sections a published tree declares.
|
|
271
|
+
*
|
|
272
|
+
* **This is where a section lives now, and the only place** (#204). A content
|
|
273
|
+
* note carries none: it is addressed by `(type, shortcode)` and emitted flat
|
|
274
|
+
* under the mount, so nothing a page does creates a directory. A site that wants
|
|
275
|
+
* `/<package>/<prefix><section>/` to answer — with a title, a hero, and whatever
|
|
276
|
+
* listing its layout builds — says so here, in configuration, and this writes
|
|
277
|
+
* the `_index.md` that makes Hugo agree it is a section.
|
|
278
|
+
*
|
|
279
|
+
* Three jobs, all of them Hugo's directory semantics rather than the note
|
|
280
|
+
* format's:
|
|
281
|
+
*
|
|
282
|
+
* - **The mount's own landing**, so `/<package>/<prefix>` is a page rather than
|
|
283
|
+
* a directory listing. It carries a `type` of its own: Hugo's template lookup
|
|
284
|
+
* walks up a page's path, so an untyped landing template at the mount would
|
|
285
|
+
* also serve every section below it that has none.
|
|
267
286
|
* - **Declared sections** get a titled `_index.md` with their hero, so a landing
|
|
268
287
|
* matches the card that links to it instead of showing Hugo's auto-humanised
|
|
269
|
-
* directory name. The body is empty, which lets the theme
|
|
270
|
-
*
|
|
271
|
-
* - **Every other section directly under the mount** gets a bare `_index.md`,
|
|
288
|
+
* directory name. The body is empty, which lets the theme decide what to list.
|
|
289
|
+
* - **Every other directory directly under the mount** gets a bare `_index.md`,
|
|
272
290
|
* or its own address publishes nothing. Hugo generates a section page
|
|
273
291
|
* automatically only for a *top-level* content directory; below that, a
|
|
274
|
-
* directory without an `_index.md` is not a section, so its URL 404s while
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
292
|
+
* directory without an `_index.md` is not a section, so its URL 404s while its
|
|
293
|
+
* children publish normally. With content pages flat, what that reaches is a
|
|
294
|
+
* `trees` entry's directory — the one thing left below the mount that a note
|
|
295
|
+
* creates.
|
|
296
|
+
*
|
|
297
|
+
* **A section listing is not a page listing any more.** A layout that reads
|
|
298
|
+
* `.Pages` off a section it declares here will find nothing, because no file is
|
|
299
|
+
* filed into it; one that queries `site.RegularPages` by `Params.type` — which
|
|
300
|
+
* is how `sohl`'s eleven catalog layouts already work — is unaffected. That is a
|
|
301
|
+
* consumer's layout to choose, and it is stated here because the choice is no
|
|
302
|
+
* longer free.
|
|
278
303
|
*
|
|
279
304
|
* Scoped to one level on purpose. A directory further down was not a section
|
|
280
|
-
* before
|
|
281
|
-
*
|
|
305
|
+
* before either, and giving it one here would silently re-scope the prev/next
|
|
306
|
+
* navigation of every page inside it.
|
|
282
307
|
*
|
|
283
308
|
* @param {string} outRoot - The mount directory.
|
|
284
309
|
* @param {object} options - `{ sections, landing, sectionTitle }`.
|
|
@@ -73,9 +73,13 @@ export type SiteEntry = {
|
|
|
73
73
|
*/
|
|
74
74
|
slug: string;
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
76
|
+
* The Hugo section a **tree** page is filed under, and
|
|
77
|
+
* the first segment of the `<sec>/<slug>` address it
|
|
78
|
+
* is reachable by. A content page has none: it is
|
|
79
|
+
* addressed by `(type, shortcode)` and emitted flat
|
|
80
|
+
* (#204).
|
|
77
81
|
*/
|
|
78
|
-
sec
|
|
82
|
+
sec?: string | undefined;
|
|
79
83
|
/**
|
|
80
84
|
* Source file's basename, e.g. `Climbing.md`.
|
|
81
85
|
*/
|
|
@@ -85,9 +89,10 @@ export type SiteEntry = {
|
|
|
85
89
|
*/
|
|
86
90
|
url: string;
|
|
87
91
|
/**
|
|
88
|
-
* Whether
|
|
92
|
+
* Whether a tree page is its directory's
|
|
93
|
+
* landing.
|
|
89
94
|
*/
|
|
90
|
-
isReadme
|
|
95
|
+
isReadme?: boolean | undefined;
|
|
91
96
|
};
|
|
92
97
|
/**
|
|
93
98
|
* The resolved index and everything a wikilink resolver reads beside it.
|