@heroiclands/package-build 22.0.3 → 22.1.1
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 +66 -0
- package/CONTENT.md +56 -18
- package/README.md +1 -1
- package/bin/content-build.mjs +67 -23
- package/bin/package-build.mjs +3 -2
- package/config.mjs +52 -1
- package/content-config.mjs +264 -8
- package/docs/api.md +33 -30
- package/docs/commands.md +68 -32
- package/docs/configuration.md +276 -62
- package/docs/getting-started.md +15 -4
- package/docs/project-setup.md +45 -20
- package/engine/content-links.mjs +19 -9
- package/engine/helpers.mjs +2 -1
- package/engine/metadata-index.mjs +32 -0
- package/engine/note-vocabulary.mjs +20 -0
- package/engine/pack-config.mjs +21 -2
- package/engine/site-build.mjs +29 -55
- package/engine/site-config.mjs +503 -0
- package/engine/site-index.mjs +10 -1
- package/engine/web-wikilinks.mjs +16 -6
- package/engine/wikilink-syntax.mjs +10 -0
- package/engine/wikilinks.mjs +34 -7
- package/manifest.mjs +12 -7
- package/package.json +2 -1
- package/stage.mjs +4 -3
- package/types/config.d.mts +24 -0
- package/types/content-config.d.mts +64 -0
- package/types/engine/content-links.d.mts +3 -2
- package/types/engine/metadata-index.d.mts +22 -0
- package/types/engine/note-vocabulary.d.mts +12 -0
- package/types/engine/site-build.d.mts +8 -26
- package/types/engine/site-config.d.mts +236 -0
- package/types/engine/site-index.d.mts +6 -1
- package/types/engine/web-wikilinks.d.mts +9 -5
- package/types/engine/wikilink-syntax.d.mts +3 -0
- package/types/engine/wikilinks.d.mts +16 -5
- package/types/manifest.d.mts +4 -4
- package/types/stage.d.mts +4 -3
package/engine/content-links.mjs
CHANGED
|
@@ -86,7 +86,7 @@ import {
|
|
|
86
86
|
PACKAGE_BASE,
|
|
87
87
|
readCanonicalKey,
|
|
88
88
|
} from "./content-address.mjs";
|
|
89
|
-
import { loadForeignIndexes } from "./metadata-index.mjs";
|
|
89
|
+
import { loadForeignIndexes, noContentIndexPackages } from "./metadata-index.mjs";
|
|
90
90
|
import { frontmatterWikilinks, slugify } from "./web-wikilinks.mjs";
|
|
91
91
|
import { homepageAddresses, isHomepage } from "./homepage.mjs";
|
|
92
92
|
import { RETIRED_TYPES } from "./ids.mjs";
|
|
@@ -268,6 +268,10 @@ export function buildLinkIndex(
|
|
|
268
268
|
for (const v of foreign.index.values()) if (v.type) types.add(v.type);
|
|
269
269
|
|
|
270
270
|
const packages = new Set([...(byKey.size ? [pkg] : []), ...foreign.packages]);
|
|
271
|
+
// Packages declared `contentIndex: false` — a Foundry dependency only, with
|
|
272
|
+
// no fetched index. A link naming one is refused with a diagnostic that
|
|
273
|
+
// names the key, rather than reading as an undeclared package or a typo.
|
|
274
|
+
const noIndexPackages = config ? noContentIndexPackages(config) : new Set();
|
|
271
275
|
|
|
272
276
|
// The address space an `![[…]]` embed resolves against, shaped as every
|
|
273
277
|
// other asset resolver reads one so the checker cannot answer an authored
|
|
@@ -464,7 +468,7 @@ export function buildLinkIndex(
|
|
|
464
468
|
* @returns {object|undefined} The note it addresses.
|
|
465
469
|
*/
|
|
466
470
|
function resolveAddress(target, keyPath) {
|
|
467
|
-
const qualified = readQualifier(target, types, packages);
|
|
471
|
+
const qualified = readQualifier(target, types, packages, noIndexPackages);
|
|
468
472
|
if (!qualified || qualified.reason) return undefined;
|
|
469
473
|
// Every omitted segment defaults from where the link is written,
|
|
470
474
|
// so the target expands to exactly one canonical address and this is a
|
|
@@ -501,7 +505,7 @@ export function buildLinkIndex(
|
|
|
501
505
|
* @returns {object[]} The foreign entries, each carrying its `package`.
|
|
502
506
|
*/
|
|
503
507
|
function foreignHits(target, keyPath) {
|
|
504
|
-
const q = readQualifier(target, types, packages);
|
|
508
|
+
const q = readQualifier(target, types, packages, noIndexPackages);
|
|
505
509
|
if (!q || q.reason) return [];
|
|
506
510
|
// An omitted package means *this* package, so a short form
|
|
507
511
|
// addresses nothing foreign and never reaches a dependency's index.
|
|
@@ -552,7 +556,7 @@ export function buildLinkIndex(
|
|
|
552
556
|
* @returns {object|null} The note, asset record or foreign entry declaring it.
|
|
553
557
|
*/
|
|
554
558
|
function referenceHit(target) {
|
|
555
|
-
const q = readQualifier(target, types, packages);
|
|
559
|
+
const q = readQualifier(target, types, packages, noIndexPackages);
|
|
556
560
|
if (!q || q.reason) return null;
|
|
557
561
|
const local = matchAddress([...byKey, ...byAssetKey], q);
|
|
558
562
|
if (local.length) return local[0][1];
|
|
@@ -566,6 +570,8 @@ export function buildLinkIndex(
|
|
|
566
570
|
anchors,
|
|
567
571
|
types,
|
|
568
572
|
packages,
|
|
573
|
+
/** Packages declared `contentIndex: false`, a Foundry dependency only. */
|
|
574
|
+
noIndexPackages,
|
|
569
575
|
/**
|
|
570
576
|
* The files this package ships, by canonical address. Separate from the
|
|
571
577
|
* notes because the two record shapes are read differently, and exposed
|
|
@@ -597,7 +603,7 @@ export function buildLinkIndex(
|
|
|
597
603
|
foreignHits,
|
|
598
604
|
referenceHit,
|
|
599
605
|
/** Whether a target reads as a qualified address at all. */
|
|
600
|
-
isAddress: (target) => Boolean(readQualifier(target, types, packages)),
|
|
606
|
+
isAddress: (target) => Boolean(readQualifier(target, types, packages, noIndexPackages)),
|
|
601
607
|
};
|
|
602
608
|
}
|
|
603
609
|
|
|
@@ -922,8 +928,9 @@ export function auditHomepageLinks(index) {
|
|
|
922
928
|
* which addresses a foreign manifest answered. Each `deadAddresses` entry
|
|
923
929
|
* carries a `reason` from {@link LINK_FINDING_REASONS} —
|
|
924
930
|
* `"not-an-address"`, `"unknown-type"`, `"ambiguous"` (with the claiming
|
|
925
|
-
* `packages`), or `"unresolved"` — and every one of
|
|
926
|
-
* the three resolvers agree on severity for every
|
|
931
|
+
* `packages`), `"no-content-index"`, or `"unresolved"` — and every one of
|
|
932
|
+
* them is an **error**: the three resolvers agree on severity for every
|
|
933
|
+
* class.
|
|
927
934
|
*/
|
|
928
935
|
export function auditLinks(index) {
|
|
929
936
|
const { notes, anchors, linksOf, embedsOf, resolve, manifestHit, isAddress } = index;
|
|
@@ -1004,10 +1011,13 @@ export function auditLinks(index) {
|
|
|
1004
1011
|
});
|
|
1005
1012
|
continue;
|
|
1006
1013
|
}
|
|
1007
|
-
const read = readQualifier(target, index.types, index.packages);
|
|
1014
|
+
const read = readQualifier(target, index.types, index.packages, index.noIndexPackages);
|
|
1008
1015
|
deadAddresses.push({
|
|
1009
1016
|
...at,
|
|
1010
|
-
reason:
|
|
1017
|
+
reason:
|
|
1018
|
+
read?.reason === "unknown-type" ? "unknown-type"
|
|
1019
|
+
: read?.reason === "no-content-index" ? "no-content-index"
|
|
1020
|
+
: "unresolved",
|
|
1011
1021
|
});
|
|
1012
1022
|
}
|
|
1013
1023
|
}
|
package/engine/helpers.mjs
CHANGED
|
@@ -43,7 +43,7 @@ import { contentPackage, foundryPackageId } from "./content-package.mjs";
|
|
|
43
43
|
import { searchableFrontmatter } from "./note-package.mjs";
|
|
44
44
|
import { PACKAGE_BASE } from "./content-address.mjs";
|
|
45
45
|
import { resolveNoteId } from "./note-ids.mjs";
|
|
46
|
-
import { loadForeignIndexes } from "./metadata-index.mjs";
|
|
46
|
+
import { loadForeignIndexes, noContentIndexPackages } from "./metadata-index.mjs";
|
|
47
47
|
// The record accessors only — deriving records reaches the pack router and the
|
|
48
48
|
// manifest emitter, which reach the compilers, which load this module. Reading
|
|
49
49
|
// a record needs none of that.
|
|
@@ -787,6 +787,7 @@ export function buildContentLinkIndex(
|
|
|
787
787
|
);
|
|
788
788
|
return buildWikilinkIndex(docs, resolved.foundryPackage, foreign, resolved.contentPackage, {
|
|
789
789
|
assets,
|
|
790
|
+
noIndexPackages: noContentIndexPackages(resolved),
|
|
790
791
|
});
|
|
791
792
|
}
|
|
792
793
|
|
|
@@ -84,6 +84,11 @@ export const METADATA_RELATIONSHIP_KINDS = Object.freeze(["systems", "requires"]
|
|
|
84
84
|
* and needing no items is the mirror of it. Gating the index on the catalogue
|
|
85
85
|
* flag would serve neither.
|
|
86
86
|
*
|
|
87
|
+
* **Excludes a relationship declaring `contentIndex: false`.** That opts a
|
|
88
|
+
* dependency out of both edges at once: it is a Foundry dependency only, cited
|
|
89
|
+
* by neither a wikilink nor an item reference, so there is nothing here for
|
|
90
|
+
* `deps fetch` to fill and no cache this build will ever read.
|
|
91
|
+
*
|
|
87
92
|
* The declaration is the one already in the emitted `system.json` /
|
|
88
93
|
* `module.json`, so it cannot drift from what Foundry itself installs, and
|
|
89
94
|
* there is no new configuration key to keep in step. Each entry carries the
|
|
@@ -98,6 +103,7 @@ export function metadataRelationships(config) {
|
|
|
98
103
|
const out = [];
|
|
99
104
|
for (const kind of METADATA_RELATIONSHIP_KINDS) {
|
|
100
105
|
for (const rel of config?.relationships?.[kind] ?? []) {
|
|
106
|
+
if (rel.contentIndex === false) continue;
|
|
101
107
|
out.push({
|
|
102
108
|
id: rel.id,
|
|
103
109
|
manifest: rel.manifest,
|
|
@@ -109,6 +115,32 @@ export function metadataRelationships(config) {
|
|
|
109
115
|
return out;
|
|
110
116
|
}
|
|
111
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Every package a relationship declares `contentIndex: false` on, keyed by
|
|
120
|
+
* the content package name a link into it would use.
|
|
121
|
+
*
|
|
122
|
+
* A separate set from {@link metadataRelationships}, which answers "what does
|
|
123
|
+
* `deps fetch` fill" — this answers "what does the link resolver recognise as
|
|
124
|
+
* a package with no fetched index", which a wikilink checker or pack compiler
|
|
125
|
+
* needs to tell that case apart from a package nobody declared at all.
|
|
126
|
+
*
|
|
127
|
+
* Walked across every relationship kind, not only the citable ones: the
|
|
128
|
+
* config validation refuses the flag nowhere by kind, so a resolver reading it
|
|
129
|
+
* back should not assume one either.
|
|
130
|
+
*
|
|
131
|
+
* @param {object} config - The resolved build configuration.
|
|
132
|
+
* @returns {ReadonlySet<string>} The content package names.
|
|
133
|
+
*/
|
|
134
|
+
export function noContentIndexPackages(config) {
|
|
135
|
+
const out = new Set();
|
|
136
|
+
for (const entries of Object.values(config?.relationships ?? {})) {
|
|
137
|
+
for (const rel of entries ?? []) {
|
|
138
|
+
if (rel.contentIndex === false) out.add(rel.contentPackage ?? rel.id);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return Object.freeze(out);
|
|
142
|
+
}
|
|
143
|
+
|
|
112
144
|
/**
|
|
113
145
|
* The cache directory for one dependency's index at one version.
|
|
114
146
|
*
|
|
@@ -496,6 +496,26 @@ export function hasTag(fm, tag) {
|
|
|
496
496
|
return false;
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
+
/**
|
|
500
|
+
* Whether a note carries any `tags:` at all, however authored.
|
|
501
|
+
*
|
|
502
|
+
* The one question the site build asks of tags in aggregate — whether the
|
|
503
|
+
* tree publishes taxonomy pages — rather than about a particular tag. Reads
|
|
504
|
+
* `tags` and `tag` exactly as {@link hasTag} does, and treats an empty list
|
|
505
|
+
* or a blank string as carrying none.
|
|
506
|
+
*
|
|
507
|
+
* @param {object|null|undefined} fm - Parsed frontmatter.
|
|
508
|
+
* @returns {boolean} Whether the note carries at least one tag.
|
|
509
|
+
*/
|
|
510
|
+
export function hasAnyTag(fm) {
|
|
511
|
+
const raw = fm?.tags ?? fm?.tag;
|
|
512
|
+
if (raw == null) return false;
|
|
513
|
+
for (const entry of Array.isArray(raw) ? raw : [raw]) {
|
|
514
|
+
if (typeof entry === "string" && entry.trim() !== "") return true;
|
|
515
|
+
}
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
|
|
499
519
|
/**
|
|
500
520
|
* Whether a note is tagged as an unfinished **draft**.
|
|
501
521
|
*
|
package/engine/pack-config.mjs
CHANGED
|
@@ -267,8 +267,8 @@ function readPackageJson(rootDir) {
|
|
|
267
267
|
} catch (err) {
|
|
268
268
|
throw new Error(
|
|
269
269
|
`package-build: ${manifestPath} could not be read, and the ` +
|
|
270
|
-
`configuration derives
|
|
271
|
-
`
|
|
270
|
+
`configuration derives its Foundry package id, system version, ` +
|
|
271
|
+
`homepage and author from it.`,
|
|
272
272
|
{ cause: err },
|
|
273
273
|
);
|
|
274
274
|
}
|
|
@@ -555,6 +555,25 @@ export function configFromData(data, configPath) {
|
|
|
555
555
|
input.foundryPackage = foundryPackageId(rootDir);
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
+
// `homepage` and `author` are the same kind of fact, transcribed the same
|
|
559
|
+
// way, and read regardless of `packageKind`: a documentation package
|
|
560
|
+
// publishes a site too, and needs both for it.
|
|
561
|
+
for (const [field, label] of [
|
|
562
|
+
["homepage", "`homepage`"],
|
|
563
|
+
["author", "`author`"],
|
|
564
|
+
]) {
|
|
565
|
+
if (input[field] !== undefined) {
|
|
566
|
+
throw new Error(
|
|
567
|
+
`package-build: ${configPath} declares \`${field}\`, which a data ` +
|
|
568
|
+
`configuration may not: it is \`package.json\`'s own ${label}. ` +
|
|
569
|
+
`Remove the key.`,
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
const { pkg } = readPackageJson(rootDir);
|
|
574
|
+
if (pkg.homepage !== undefined) input.homepage = pkg.homepage;
|
|
575
|
+
if (pkg.author !== undefined) input.author = pkg.author;
|
|
576
|
+
|
|
558
577
|
if (input.itemBuilders !== undefined) {
|
|
559
578
|
const declared = input.itemBuilders;
|
|
560
579
|
const known = Object.keys(ITEM_BUILDER_REGISTRIES).join(", ");
|
package/engine/site-build.mjs
CHANGED
|
@@ -57,13 +57,14 @@ import { renderImageFigures } from "./content-images.mjs";
|
|
|
57
57
|
import { pathnameProblem, resolvePathname } from "./pathnames.mjs";
|
|
58
58
|
import { buildSiteIndex, resolveInfoboxRef, wikiContext } from "./site-index.mjs";
|
|
59
59
|
import { frontmatterWikilinks, resolveWebWikilinks } from "./web-wikilinks.mjs";
|
|
60
|
-
import { loadForeignIndexes } from "./metadata-index.mjs";
|
|
60
|
+
import { loadForeignIndexes, noContentIndexPackages } from "./metadata-index.mjs";
|
|
61
61
|
import { noteInfoboxes } from "./infobox-registry.mjs";
|
|
62
62
|
import { formatUnaddressableFinding, unaddressableForeignPackages } from "./metadata-index.mjs";
|
|
63
63
|
import { deriveBeingInfo, isBeing } from "../sohl/being-info.mjs";
|
|
64
64
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
65
65
|
import { routerFor } from "./pack-router.mjs";
|
|
66
66
|
import { searchableFrontmatter } from "./note-package.mjs";
|
|
67
|
+
import { hasAnyTag } from "./note-vocabulary.mjs";
|
|
67
68
|
// The corpus, from the one pass that derives it.
|
|
68
69
|
import { indexRecordsFor } from "./content-index.mjs";
|
|
69
70
|
import { isNoteRecord, noteFile } from "./index-records.mjs";
|
|
@@ -76,6 +77,7 @@ import {
|
|
|
76
77
|
isHomepage,
|
|
77
78
|
} from "./homepage.mjs";
|
|
78
79
|
import { publishesContentPages } from "../content-config.mjs";
|
|
80
|
+
import { HUGO_CONTENT } from "./site-config.mjs";
|
|
79
81
|
|
|
80
82
|
const require = createRequire(import.meta.url);
|
|
81
83
|
|
|
@@ -397,8 +399,8 @@ export function collectHomepages(contentBase, ctx) {
|
|
|
397
399
|
* has. Nothing is written at `/<package>/` itself: that becomes a redirect the
|
|
398
400
|
* package's own repository authors, which is a routing fact rather than a page.
|
|
399
401
|
*
|
|
400
|
-
* @param {string} outRoot - The package's site root — the
|
|
401
|
-
* one level above the
|
|
402
|
+
* @param {string} outRoot - The package's site root — the content mount's
|
|
403
|
+
* root, `build/hugo/content`, one level above the mount itself.
|
|
402
404
|
* @param {readonly object[]} pages - From {@link collectHomepages}.
|
|
403
405
|
* @param {object} config - The resolved configuration, for the package name and
|
|
404
406
|
* the default title.
|
|
@@ -475,7 +477,10 @@ export function siteGates(pages, findings, { config }) {
|
|
|
475
477
|
out.unaddressable = unaddressableForeignPackages(foreign.index);
|
|
476
478
|
if (out.unaddressable.length) return out;
|
|
477
479
|
|
|
478
|
-
const index = buildSiteIndex(pages, {
|
|
480
|
+
const index = buildSiteIndex(pages, {
|
|
481
|
+
foreignIndex: foreign.index,
|
|
482
|
+
noIndexPackages: noContentIndexPackages(config),
|
|
483
|
+
});
|
|
479
484
|
out.conflicts = index.conflicts;
|
|
480
485
|
if (out.conflicts.length) return out;
|
|
481
486
|
|
|
@@ -1025,46 +1030,6 @@ export function resolveSitePass(name, options) {
|
|
|
1025
1030
|
return factory()(options);
|
|
1026
1031
|
}
|
|
1027
1032
|
|
|
1028
|
-
/**
|
|
1029
|
-
* The output root, having established that it is safe to delete.
|
|
1030
|
-
*
|
|
1031
|
-
* The whole tree is a build artifact and is wiped on every run, so this
|
|
1032
|
-
* resolution is the difference between clearing a build directory and clearing
|
|
1033
|
-
* the repository. An unset `site.out` resolves to `rootDir` itself, and the
|
|
1034
|
-
* wipe then deletes the working tree — which is not a hypothetical: it happened
|
|
1035
|
-
* while this module was being written, on a configuration that simply had no
|
|
1036
|
-
* `site` section yet.
|
|
1037
|
-
*
|
|
1038
|
-
* So the path is refused unless it is **strictly inside** the repository root.
|
|
1039
|
-
* Both failing shapes are ordinary rather than exotic — an absent setting, and a
|
|
1040
|
-
* `..` that climbs out — and neither should be recoverable by being careful.
|
|
1041
|
-
*
|
|
1042
|
-
* @param {string} rootDir - The repository root.
|
|
1043
|
-
* @param {string} out - The configured `site.out`.
|
|
1044
|
-
* @returns {string} The absolute output root.
|
|
1045
|
-
* @throws {Error} When it is unset, or is not below `rootDir`.
|
|
1046
|
-
*/
|
|
1047
|
-
export function resolveOutputRoot(rootDir, out) {
|
|
1048
|
-
if (!out) {
|
|
1049
|
-
throw new Error(
|
|
1050
|
-
"site.out is not set, so there is nowhere to write the site. " +
|
|
1051
|
-
"Refusing to continue: the output directory is wiped on every " +
|
|
1052
|
-
"run, and an unset one resolves to the repository root.",
|
|
1053
|
-
);
|
|
1054
|
-
}
|
|
1055
|
-
const root = path.resolve(rootDir);
|
|
1056
|
-
const resolved = path.resolve(root, out);
|
|
1057
|
-
const inside = resolved !== root && resolved.startsWith(root + path.sep);
|
|
1058
|
-
if (!inside) {
|
|
1059
|
-
throw new Error(
|
|
1060
|
-
`site.out (${JSON.stringify(out)}) resolves to ${resolved}, which ` +
|
|
1061
|
-
`is not inside ${root}. Refusing to continue: that directory ` +
|
|
1062
|
-
`is wiped on every run.`,
|
|
1063
|
-
);
|
|
1064
|
-
}
|
|
1065
|
-
return resolved;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
1033
|
/**
|
|
1069
1034
|
* Builds a Hugo content tree from a content tree, and reports what it found.
|
|
1070
1035
|
*
|
|
@@ -1076,16 +1041,18 @@ export function resolveOutputRoot(rootDir, out) {
|
|
|
1076
1041
|
* @param {object} [options] - Options.
|
|
1077
1042
|
* @param {object} [options.config] - A resolved configuration; loaded when
|
|
1078
1043
|
* omitted.
|
|
1079
|
-
* @param {string} [options.outRoot] - Override the configured output mount.
|
|
1080
1044
|
* @param {Map<string, object[]>} [options.sqlTables] - Prepared `sql` results,
|
|
1081
1045
|
* keyed by the note's absolute file, from
|
|
1082
1046
|
* {@link module:engine/sql-tables.prepareSqlTables}. A page authoring an
|
|
1083
1047
|
* `sql` directive with none prepared is a table error: nothing here runs a
|
|
1084
1048
|
* query.
|
|
1085
1049
|
* @returns {{gates: object, stats: object|null, tableErrors: object[],
|
|
1086
|
-
* wikiErrors: object[], imageErrors: object[], manifests: object|null
|
|
1050
|
+
* wikiErrors: object[], imageErrors: object[], manifests: object|null,
|
|
1051
|
+
* hasTags: boolean}} `hasTags` is whether any note the walk read carries
|
|
1052
|
+
* `tags:` — what {@link module:engine/site-config.hugoConfig} reads to
|
|
1053
|
+
* decide whether the site emits taxonomy pages.
|
|
1087
1054
|
*/
|
|
1088
|
-
export function buildSite({ config,
|
|
1055
|
+
export function buildSite({ config, sqlTables } = {}) {
|
|
1089
1056
|
const resolved = config ?? loadPackConfig();
|
|
1090
1057
|
const site = resolved.site;
|
|
1091
1058
|
const scheme = resolved.publish.address;
|
|
@@ -1104,16 +1071,19 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
1104
1071
|
|
|
1105
1072
|
// The Hugo content tree mirrors that mount: a page written to
|
|
1106
1073
|
// `<out>/<prefix>/<section>/` publishes at `<base><prefix><section>/`.
|
|
1107
|
-
//
|
|
1108
|
-
//
|
|
1109
|
-
//
|
|
1110
|
-
|
|
1074
|
+
// The root is fixed — `build/hugo/content`, beside the generated
|
|
1075
|
+
// `hugo.toml` — and resolved against the repository root for the same
|
|
1076
|
+
// reason every configured path is, so the build reads and writes the same
|
|
1077
|
+
// places whatever directory it was launched from. It is wiped on every
|
|
1078
|
+
// run, which is safe precisely because it is not configurable: nothing an
|
|
1079
|
+
// author writes can point it at the working tree.
|
|
1080
|
+
const outBase = path.join(resolved.rootDir, HUGO_CONTENT);
|
|
1111
1081
|
const out =
|
|
1112
|
-
|
|
1113
|
-
|
|
1082
|
+
publishesContent ?
|
|
1083
|
+
path.join(outBase, scheme.prefix.replace(/\/$/, ""))
|
|
1114
1084
|
// Homepage-only has no content mount, so the package's root *is*
|
|
1115
|
-
// the output root
|
|
1116
|
-
:
|
|
1085
|
+
// the output root.
|
|
1086
|
+
: outBase;
|
|
1117
1087
|
// The homepage publishes at `/<contentPackage>/<type>-<shortcode>/`, so its
|
|
1118
1088
|
// file goes at the package's own root — one level above the content mount,
|
|
1119
1089
|
// and the same directory in homepage-only mode.
|
|
@@ -1179,6 +1149,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
1179
1149
|
wikiErrors: [],
|
|
1180
1150
|
imageErrors: [],
|
|
1181
1151
|
stats: null,
|
|
1152
|
+
hasTags: homepages.some((p) => hasAnyTag(p.fm)),
|
|
1182
1153
|
};
|
|
1183
1154
|
}
|
|
1184
1155
|
|
|
@@ -1201,6 +1172,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
1201
1172
|
landings: 0,
|
|
1202
1173
|
out: homeRoot,
|
|
1203
1174
|
},
|
|
1175
|
+
hasTags: homepages.some((p) => hasAnyTag(p.fm)),
|
|
1204
1176
|
};
|
|
1205
1177
|
}
|
|
1206
1178
|
|
|
@@ -1248,6 +1220,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
1248
1220
|
tableErrors: [],
|
|
1249
1221
|
wikiErrors: [],
|
|
1250
1222
|
imageErrors: [],
|
|
1223
|
+
hasTags: [...pages, ...homepageEntries].some((p) => hasAnyTag(p.fm)),
|
|
1251
1224
|
};
|
|
1252
1225
|
}
|
|
1253
1226
|
|
|
@@ -1306,6 +1279,7 @@ export function buildSite({ config, outRoot, sqlTables } = {}) {
|
|
|
1306
1279
|
landings,
|
|
1307
1280
|
out,
|
|
1308
1281
|
},
|
|
1282
|
+
hasTags: [...pages, ...homepageEntries].some((p) => hasAnyTag(p.fm)),
|
|
1309
1283
|
};
|
|
1310
1284
|
}
|
|
1311
1285
|
|