@heroiclands/package-build 22.0.2 → 22.1.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 +79 -0
- package/CONTENT.md +34 -18
- package/README.md +1 -1
- package/bin/content-build.mjs +40 -22
- package/bin/package-build.mjs +34 -0
- package/config.mjs +88 -3
- package/content-config.mjs +246 -12
- package/docs/api.md +10 -7
- package/docs/commands.md +122 -32
- package/docs/configuration.md +257 -62
- package/docs/getting-started.md +15 -4
- package/docs/project-setup.md +45 -20
- package/engine/content-index.mjs +25 -6
- package/engine/pack-config.mjs +21 -2
- package/engine/site-build.mjs +15 -52
- package/engine/site-config.mjs +462 -0
- package/engine/site-root.mjs +165 -0
- package/engine/svg-theme.mjs +140 -0
- package/manifest.mjs +12 -7
- package/package.json +2 -1
- package/stage.mjs +4 -3
- package/types/config.d.mts +80 -33
- package/types/content-config.d.mts +53 -0
- package/types/engine/site-build.d.mts +3 -25
- package/types/engine/site-config.d.mts +223 -0
- package/types/engine/site-root.d.mts +87 -0
- package/types/engine/svg-theme.d.mts +22 -0
- package/types/manifest.d.mts +4 -4
- package/types/stage.d.mts +4 -3
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check a navigation's shape, and return it.
|
|
3
|
+
*
|
|
4
|
+
* `[{name, url, children?: [{name, url}]}]`, every `url` absolute. Checked
|
|
5
|
+
* on fetch and again on read: a file that is not a navigation would otherwise
|
|
6
|
+
* reach the generated menu as `undefined` labels and links.
|
|
7
|
+
*
|
|
8
|
+
* @param {unknown} value - The parsed document.
|
|
9
|
+
* @param {string} where - What is being checked, for the error.
|
|
10
|
+
* @returns {NavigationEntry[]} The navigation.
|
|
11
|
+
* @throws {TypeError} When the shape is not a navigation.
|
|
12
|
+
*/
|
|
13
|
+
export function checkNavigation(value: unknown, where?: string): NavigationEntry[];
|
|
14
|
+
/**
|
|
15
|
+
* The `[[menu.main]]` entries a navigation renders as.
|
|
16
|
+
*
|
|
17
|
+
* Entry for entry, in order; a dropdown is an entry with an `identifier` and
|
|
18
|
+
* its children are entries naming it as `parent`, which is how the theme's
|
|
19
|
+
* header partial draws one. Weights count from one within each level, so the
|
|
20
|
+
* order is the navigation's and not Hugo's alphabetical fallback.
|
|
21
|
+
*
|
|
22
|
+
* @param {readonly NavigationEntry[]} navigation - The navigation.
|
|
23
|
+
* @returns {MenuEntry[]} The menu entries.
|
|
24
|
+
*/
|
|
25
|
+
export function menuEntries(navigation: readonly NavigationEntry[]): MenuEntry[];
|
|
26
|
+
/**
|
|
27
|
+
* Where the fetched navigation sits.
|
|
28
|
+
*
|
|
29
|
+
* @param {object} config - The resolved build configuration.
|
|
30
|
+
* @returns {string} The cache directory.
|
|
31
|
+
*/
|
|
32
|
+
export function navigationCacheDir(config: object): string;
|
|
33
|
+
/**
|
|
34
|
+
* The cached navigation.
|
|
35
|
+
*
|
|
36
|
+
* **Reads the cache only.** A cold cache is an error naming the command that
|
|
37
|
+
* fills it, rather than a download nobody asked for: a site build that reaches
|
|
38
|
+
* the network is not reproducible and fails strangely offline. That is the
|
|
39
|
+
* content index's rule, and it holds here for the same reason. A half-finished
|
|
40
|
+
* fetch counts as cold.
|
|
41
|
+
*
|
|
42
|
+
* @param {object} config - The resolved build configuration.
|
|
43
|
+
* @returns {NavigationEntry[]} The navigation.
|
|
44
|
+
* @throws {Error} When it has not been fetched, or is not a navigation.
|
|
45
|
+
*/
|
|
46
|
+
export function readCachedNavigation(config: object): NavigationEntry[];
|
|
47
|
+
/**
|
|
48
|
+
* Fetch the navigation into the cache, and stamp it complete.
|
|
49
|
+
*
|
|
50
|
+
* Rebuilt from empty on every call rather than kept when present: the
|
|
51
|
+
* navigation carries no version to key a cache on, and a package added to the
|
|
52
|
+
* roster reaches a site on its next `deps fetch`.
|
|
53
|
+
*
|
|
54
|
+
* @param {object} config - The resolved build configuration.
|
|
55
|
+
* @param {object} [options] - Options.
|
|
56
|
+
* @param {string} [options.url] - Where to fetch from. Defaults to
|
|
57
|
+
* {@link NAVIGATION_URL}.
|
|
58
|
+
* @param {typeof globalThis.fetch} [options.fetch] - The fetch to use.
|
|
59
|
+
* @returns {Promise<string>} The cached file.
|
|
60
|
+
* @throws {Error} When the download fails, or the document is not a navigation.
|
|
61
|
+
*/
|
|
62
|
+
export function fetchNavigation(config: object, { url, fetch }?: {
|
|
63
|
+
url?: string | undefined;
|
|
64
|
+
fetch?: typeof globalThis.fetch | undefined;
|
|
65
|
+
}): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* The `themesDir` for a repository, as the path from `build/hugo/` to the
|
|
68
|
+
* directory holding the installed theme.
|
|
69
|
+
*
|
|
70
|
+
* Resolved the way Node resolves a package — `node_modules/` in the
|
|
71
|
+
* repository root, then in each parent — and written as a path rather than
|
|
72
|
+
* assumed, so a worktree that resolves its parent's install says so in the
|
|
73
|
+
* generated file.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} rootDir - The repository root.
|
|
76
|
+
* @returns {string} The relative path, POSIX-separated.
|
|
77
|
+
* @throws {Error} When the theme is installed nowhere above the root.
|
|
78
|
+
*/
|
|
79
|
+
export function resolveThemesDir(rootDir: string): string;
|
|
80
|
+
/**
|
|
81
|
+
* The Hugo configuration, as an object.
|
|
82
|
+
*
|
|
83
|
+
* Pure: every input is handed in, so a test can describe the generated shape
|
|
84
|
+
* without a repository on disk. {@link generateHugoConfig} is the same function
|
|
85
|
+
* with the reading put back.
|
|
86
|
+
*
|
|
87
|
+
* `checkHomepage` runs first, before any value is composed — a missing or
|
|
88
|
+
* mismatched `package.json` `homepage` is a finding on every site build.
|
|
89
|
+
*
|
|
90
|
+
* @param {object} options - The sources.
|
|
91
|
+
* @param {object} options.config - The resolved build configuration.
|
|
92
|
+
* @param {string} [options.description] - `package.json`'s `description`.
|
|
93
|
+
* @param {readonly NavigationEntry[]} options.navigation - The navigation.
|
|
94
|
+
* @param {string} options.themesDir - From {@link resolveThemesDir}.
|
|
95
|
+
* @returns {Record<string, any>} The configuration Hugo reads.
|
|
96
|
+
* @throws {TypeError} When `homepage` fails `checkHomepage`, or the
|
|
97
|
+
* configuration declares no `packageBuild.manifest.title`.
|
|
98
|
+
*/
|
|
99
|
+
export function hugoConfig({ config, description, navigation, themesDir }: {
|
|
100
|
+
config: object;
|
|
101
|
+
description?: string | undefined;
|
|
102
|
+
navigation: readonly NavigationEntry[];
|
|
103
|
+
themesDir: string;
|
|
104
|
+
}): Record<string, any>;
|
|
105
|
+
/**
|
|
106
|
+
* The configuration as the TOML Hugo reads.
|
|
107
|
+
*
|
|
108
|
+
* @param {Record<string, unknown>} generated - From {@link hugoConfig}.
|
|
109
|
+
* @returns {string} The file's contents.
|
|
110
|
+
*/
|
|
111
|
+
export function hugoToml(generated: Record<string, unknown>): string;
|
|
112
|
+
/**
|
|
113
|
+
* The Hugo configuration, every source read from the repository.
|
|
114
|
+
*
|
|
115
|
+
* Reads `package.json`, the cached navigation and the installed theme's
|
|
116
|
+
* location, and composes them with {@link hugoConfig}. Nothing is written, so
|
|
117
|
+
* a caller can run this before touching the output tree and fail with it
|
|
118
|
+
* intact.
|
|
119
|
+
*
|
|
120
|
+
* @param {object} config - The resolved build configuration.
|
|
121
|
+
* @returns {Record<string, any>} The configuration Hugo reads.
|
|
122
|
+
* @throws {Error} When any source is missing or wrong.
|
|
123
|
+
*/
|
|
124
|
+
export function generateHugoConfig(config: object): Record<string, any>;
|
|
125
|
+
/**
|
|
126
|
+
* Write `build/hugo/hugo.toml`.
|
|
127
|
+
*
|
|
128
|
+
* @param {object} config - The resolved build configuration.
|
|
129
|
+
* @param {Record<string, unknown>} [generated] - The configuration to write,
|
|
130
|
+
* when the caller already generated it. Generated here otherwise.
|
|
131
|
+
* @returns {{file: string}} The file written.
|
|
132
|
+
*/
|
|
133
|
+
export function writeHugoConfig(config: object, generated?: Record<string, unknown>): {
|
|
134
|
+
file: string;
|
|
135
|
+
};
|
|
136
|
+
/** The Hugo source directory, relative to the repository root. */
|
|
137
|
+
export const HUGO_SOURCE: "build/hugo";
|
|
138
|
+
/** The content mount `content-build site` writes, relative to the repository root. */
|
|
139
|
+
export const HUGO_CONTENT: "build/hugo/content";
|
|
140
|
+
/**
|
|
141
|
+
* The directory that is deployed, relative to the repository root.
|
|
142
|
+
*
|
|
143
|
+
* Hugo renders into `<DEPLOY_ROOT>/<contentPackage>/`; `package-build
|
|
144
|
+
* site-root` writes `_headers` and `_redirects` beside it.
|
|
145
|
+
*/
|
|
146
|
+
export const DEPLOY_ROOT: "build/site";
|
|
147
|
+
/** The npm package the shared theme arrives as. */
|
|
148
|
+
export const THEME_PACKAGE: "@heroiclands/hugo-theme";
|
|
149
|
+
/** The theme's name under `themesDir`, which is the package's unscoped name. */
|
|
150
|
+
export const THEME: "hugo-theme";
|
|
151
|
+
/** The locale every site renders in. */
|
|
152
|
+
export const LOCALE: "en-us";
|
|
153
|
+
/**
|
|
154
|
+
* The brand chrome's own links, the same on every site.
|
|
155
|
+
*
|
|
156
|
+
* `logo` is resolved through the theme's `cdn-url.html`, so it is a path on
|
|
157
|
+
* the asset host rather than an address.
|
|
158
|
+
*/
|
|
159
|
+
export const BRAND: Readonly<{
|
|
160
|
+
logo: "images/brand/sohl-icon-white.webp";
|
|
161
|
+
licenseURL: "https://www.heroiclands.org/license/";
|
|
162
|
+
discordURL: "https://discord.gg/EwMfkNd3az";
|
|
163
|
+
}>;
|
|
164
|
+
/**
|
|
165
|
+
* The kinds no site renders.
|
|
166
|
+
*
|
|
167
|
+
* A section exists only where `site.sections` declares one, so a tree holding
|
|
168
|
+
* only the homepage emits nothing beyond it; taxonomies and feeds would be
|
|
169
|
+
* empty shells on every site.
|
|
170
|
+
*/
|
|
171
|
+
export const DISABLE_KINDS: readonly string[];
|
|
172
|
+
/**
|
|
173
|
+
* The markup settings the toolchain's own output requires.
|
|
174
|
+
*
|
|
175
|
+
* Pages are written with raw HTML in them — a `<figure>` for every image, a
|
|
176
|
+
* `<span>` marking an unresolved link — and Goldmark drops raw HTML unless
|
|
177
|
+
* told otherwise. A theme cannot supply this: Hugo does not merge a theme's
|
|
178
|
+
* `markup` block.
|
|
179
|
+
*/
|
|
180
|
+
export const MARKUP: Readonly<{
|
|
181
|
+
goldmark: Readonly<{
|
|
182
|
+
renderer: Readonly<{
|
|
183
|
+
unsafe: true;
|
|
184
|
+
}>;
|
|
185
|
+
}>;
|
|
186
|
+
}>;
|
|
187
|
+
/** Where the navigation is published. */
|
|
188
|
+
export const NAVIGATION_URL: "https://www.heroiclands.org/nav.json";
|
|
189
|
+
/** The cached navigation's file name. */
|
|
190
|
+
export const NAVIGATION_FILE: "nav.json";
|
|
191
|
+
/**
|
|
192
|
+
* A navigation entry, as `nav.json` states one.
|
|
193
|
+
*/
|
|
194
|
+
export type NavigationEntry = {
|
|
195
|
+
/**
|
|
196
|
+
* - The entry's label.
|
|
197
|
+
*/
|
|
198
|
+
name: string;
|
|
199
|
+
/**
|
|
200
|
+
* - Where it links, absolute.
|
|
201
|
+
*/
|
|
202
|
+
url: string;
|
|
203
|
+
/**
|
|
204
|
+
* - A dropdown's entries.
|
|
205
|
+
*/
|
|
206
|
+
children?: NavigationEntry[] | undefined;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* A Hugo menu entry, as `[[menu.main]]` states one.
|
|
210
|
+
*/
|
|
211
|
+
export type MenuEntry = {
|
|
212
|
+
name: string;
|
|
213
|
+
url: string;
|
|
214
|
+
weight: number;
|
|
215
|
+
/**
|
|
216
|
+
* - Set on an entry that has children.
|
|
217
|
+
*/
|
|
218
|
+
identifier?: string | undefined;
|
|
219
|
+
/**
|
|
220
|
+
* - Set on a child, naming its parent's identifier.
|
|
221
|
+
*/
|
|
222
|
+
parent?: string | undefined;
|
|
223
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a package's landing is served, now that it is an addressed page.
|
|
3
|
+
*
|
|
4
|
+
* The site build emits the homepage at its own address rather than as the
|
|
5
|
+
* site root's `_index.md`, so the prefix root is a redirect to it.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} pkg - The content package name.
|
|
8
|
+
* @returns {string} The landing's path.
|
|
9
|
+
*/
|
|
10
|
+
export function landingPath(pkg: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Suppress indexing of every address a deployment answers on but nobody
|
|
13
|
+
* advertises.
|
|
14
|
+
*
|
|
15
|
+
* Cloudflare Pages assigns three: the project's own `pages.dev`, a per-
|
|
16
|
+
* deployment `pages.dev`, and the custom domain the project carries so the
|
|
17
|
+
* routing layer has an origin to fetch. None is advertised, all answer with the
|
|
18
|
+
* same pages, and left alone they are indexed and compete with the canonical
|
|
19
|
+
* URL in search results.
|
|
20
|
+
*
|
|
21
|
+
* The third matters most: it is the address the routing layer fetches, so it is
|
|
22
|
+
* the host-assigned address a reader is most plausibly handed. The rules are
|
|
23
|
+
* **scoped to those hostnames**, which keeps this correct for anyone deploying
|
|
24
|
+
* the site under a domain of their own — there it is indexable, and only the
|
|
25
|
+
* host-assigned addresses are not.
|
|
26
|
+
*
|
|
27
|
+
* @returns {string[]} The header block's lines.
|
|
28
|
+
*/
|
|
29
|
+
export function noindexHeaders(): string[];
|
|
30
|
+
/**
|
|
31
|
+
* The lifetime pinned on the prefix-root redirect, and why it is pinned.
|
|
32
|
+
*
|
|
33
|
+
* Cloudflare Pages sets no `Cache-Control` on a redirect it generates — those
|
|
34
|
+
* responses carry `location` and nothing else — and a 301 with no lifetime is
|
|
35
|
+
* cached by a browser indefinitely, on the most-linked URL there is. An hour
|
|
36
|
+
* keeps the 301's canonical signal without the permanence.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} pkg - The content package name.
|
|
39
|
+
* @returns {string[]} The header block's lines.
|
|
40
|
+
*/
|
|
41
|
+
export function cacheHeaders(pkg: string): string[];
|
|
42
|
+
/**
|
|
43
|
+
* Both forms of the prefix root, because Pages matches the raw path.
|
|
44
|
+
*
|
|
45
|
+
* Redirect matching runs before any trailing-slash or `index.html` handling, so
|
|
46
|
+
* `/<pkg>` and `/<pkg>/` are distinct keys and a rule on one does not catch the
|
|
47
|
+
* other.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} pkg - The content package name.
|
|
50
|
+
* @returns {string} The `_redirects` file's contents.
|
|
51
|
+
*/
|
|
52
|
+
export function redirects(pkg: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* The `_headers` file's contents.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} pkg - The content package name.
|
|
57
|
+
* @returns {string} The file's contents.
|
|
58
|
+
*/
|
|
59
|
+
export function headers(pkg: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* Write `_headers` and `_redirects` beside the rendered site.
|
|
62
|
+
*
|
|
63
|
+
* @param {object} options - Options.
|
|
64
|
+
* @param {string} options.pkg - The content package name, which is also the
|
|
65
|
+
* directory Hugo rendered into.
|
|
66
|
+
* @param {string} options.out - The directory that is deployed.
|
|
67
|
+
* @returns {{files: string[]}} The files written.
|
|
68
|
+
* @throws {Error} When no rendered site is there, which means the site build
|
|
69
|
+
* has not run and writing root files would publish a deployment with nothing
|
|
70
|
+
* under the prefix.
|
|
71
|
+
*/
|
|
72
|
+
export function writeSiteRoot({ pkg, out }: {
|
|
73
|
+
pkg: string;
|
|
74
|
+
out: string;
|
|
75
|
+
}): {
|
|
76
|
+
files: string[];
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* The namespace the routing layer derives a package's origin in.
|
|
80
|
+
*
|
|
81
|
+
* `/<package>/` on the public host is proxied to
|
|
82
|
+
* `https://<package>.<suffix>/<package>/`, and {@link noindexHeaders} depends on
|
|
83
|
+
* that being a dedicated namespace.
|
|
84
|
+
*
|
|
85
|
+
* @type {string}
|
|
86
|
+
*/
|
|
87
|
+
export const ORIGIN_SUFFIX: string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SVG with a scheme-aware fill rule, or unchanged where one cannot apply.
|
|
3
|
+
*
|
|
4
|
+
* Three files are returned as they are. One already carrying a
|
|
5
|
+
* `prefers-color-scheme` rule is themed — by this pass on an earlier run, or by
|
|
6
|
+
* its author — and re-theming it would stack rules. One with an inline `fill`
|
|
7
|
+
* cannot be themed at all, because an inline declaration beats a `<style>` rule
|
|
8
|
+
* and the result would be a half-recoloured icon, which is worse than an
|
|
9
|
+
* unthemed one. One with no `<svg>` element is not an SVG.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} svg - The SVG source.
|
|
12
|
+
* @returns {string} The themed source, or the input unchanged.
|
|
13
|
+
*/
|
|
14
|
+
export function injectAdaptiveFill(svg: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* The staging hook: theme an SVG, and pass everything else through untouched.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} sourcePath - The file being staged.
|
|
19
|
+
* @returns {string|null} The themed source, or `null` to stage the file as it
|
|
20
|
+
* is — which is what every non-SVG asset gets.
|
|
21
|
+
*/
|
|
22
|
+
export function transform(sourcePath: string): string | null;
|
package/types/manifest.d.mts
CHANGED
|
@@ -188,10 +188,10 @@ export function publishedRelationships(relationships: Record<string, unknown>):
|
|
|
188
188
|
*
|
|
189
189
|
* - **Declared** — everything in `packageBuild.manifest`, emitted unchanged, so
|
|
190
190
|
* a key Foundry adds later needs no release of this package.
|
|
191
|
-
* - **Derived** — the identity, the
|
|
192
|
-
* and system compatibility ranges, and the pack list.
|
|
193
|
-
* also declared: an authored copy would be overwritten
|
|
194
|
-
* disagree with nothing to say so.
|
|
191
|
+
* - **Derived** — the identity, the description, the release addresses, the
|
|
192
|
+
* version, the Foundry and system compatibility ranges, and the pack list.
|
|
193
|
+
* These are refused if also declared: an authored copy would be overwritten
|
|
194
|
+
* and the two would disagree with nothing to say so.
|
|
195
195
|
* - **Computed** — namespaced `flags` a repository works out for itself, merged
|
|
196
196
|
* over any it declared.
|
|
197
197
|
*
|
package/types/stage.d.mts
CHANGED
|
@@ -77,8 +77,9 @@ export function cleanBuildArtifacts(root: string, { extra, includeNodeModules }?
|
|
|
77
77
|
/**
|
|
78
78
|
* Directories every HeroicLands repository regenerates and none commits.
|
|
79
79
|
*
|
|
80
|
-
* A repository adds its own
|
|
81
|
-
*
|
|
82
|
-
*
|
|
80
|
+
* A repository adds its own through `packageBuild.clean.extra`, but these four
|
|
81
|
+
* are common to all of them because they come from the shared toolchain rather
|
|
82
|
+
* than from any one package's layout. Everything the site build writes — the
|
|
83
|
+
* Hugo source tree, Hugo's cache and the rendered site — is under `build/`.
|
|
83
84
|
*/
|
|
84
85
|
export const BUILD_ARTIFACT_DIRS: readonly string[];
|