@heroiclands/package-build 22.15.1 → 22.16.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 +95 -0
- package/README.md +39 -678
- package/bin/{content-build.mjs → content-commands.mjs} +197 -118
- package/bin/package-build.mjs +303 -42
- package/bin/report.mjs +1 -1
- package/ci/ci-docker.mjs +6 -3
- package/ci/ci-steps.mjs +6 -4
- package/ci/content-format-reference.mjs +106 -0
- package/config.mjs +22 -2
- package/content-config.mjs +74 -134
- package/docs/api.md +294 -234
- package/docs/authoring/assets.md +31 -0
- package/docs/authoring/dates-and-calendars.md +33 -0
- package/docs/authoring/first-note.md +48 -0
- package/docs/authoring/frontmatter.md +40 -0
- package/docs/authoring/links-and-markup.md +137 -0
- package/docs/commands.md +631 -162
- package/docs/configuration.md +176 -140
- package/docs/content-format.md +50 -4229
- package/docs/design.md +32 -0
- package/docs/diagnostics.md +29 -22
- package/docs/getting-started.md +21 -877
- package/docs/guides/book.md +153 -0
- package/docs/guides/e2e.md +143 -0
- package/docs/guides/icons.md +126 -0
- package/docs/guides/localization.md +120 -0
- package/docs/guides/module-project.md +915 -0
- package/docs/guides/packs.md +167 -0
- package/docs/guides/reference-pages.md +148 -0
- package/docs/guides/release-and-deploy.md +114 -0
- package/docs/guides/site.md +152 -0
- package/docs/guides/system-project.md +45 -0
- package/docs/index.md +16 -0
- package/docs/project-setup.md +54 -35
- package/docs/reference/format-details.md +4420 -0
- package/docs/reference/note-types.md +531 -0
- package/engine/address-render.mjs +26 -0
- package/engine/address.mjs +3 -20
- package/engine/anchors.mjs +15 -6
- package/engine/asset-bindings.mjs +6 -2
- package/engine/base-compiler.mjs +47 -11
- package/engine/being-measurements.mjs +70 -0
- package/engine/calendar-artifacts.mjs +1 -1
- package/engine/calendar-choice.mjs +32 -0
- package/engine/calendar-human.mjs +71 -0
- package/engine/calendar-notes.mjs +41 -3
- package/engine/calendars.mjs +8 -1
- package/engine/changelog-lint.mjs +2 -2
- package/engine/code-fences.mjs +23 -97
- package/engine/compile-corpus.mjs +6 -7
- package/engine/content-admonitions.mjs +114 -0
- package/engine/content-captions.mjs +181 -0
- package/engine/content-embeds.mjs +2 -2
- package/engine/content-footnotes.mjs +34 -0
- package/engine/content-format-check.mjs +2 -2
- package/engine/content-format.mjs +127 -73
- package/engine/content-format.yaml +745 -0
- package/engine/content-html.mjs +1 -1
- package/engine/content-icons.mjs +45 -92
- package/engine/content-images.mjs +22 -74
- package/engine/content-index.mjs +12 -3
- package/engine/content-links.mjs +21 -26
- package/engine/content-lint.mjs +1 -5
- package/engine/content-secrets.mjs +3 -1
- package/engine/content-tables.mjs +47 -1257
- package/engine/culture-choice.mjs +32 -0
- package/engine/date-conversion.mjs +47 -0
- package/engine/declaration-check.mjs +87 -0
- package/engine/docs-checks.mjs +140 -0
- package/engine/documentation-links.mjs +31 -0
- package/engine/exported-scene.mjs +1 -1
- package/engine/extension-attributes.mjs +74 -0
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +4 -0
- package/engine/foreign-catalog.mjs +5 -5
- package/engine/format-generated.mjs +15 -0
- package/engine/foundry-entries.mjs +3 -1
- package/engine/frontmatter-lint.mjs +5 -1
- package/engine/frontmatter.mjs +10 -0
- package/engine/generate.mjs +2 -2
- package/engine/helpers.mjs +71 -117
- package/engine/homepage.mjs +2 -3
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +18 -3
- package/engine/infobox-registry.mjs +3 -1
- package/engine/infobox.mjs +38 -20
- package/engine/item-compiler.mjs +10 -8
- package/engine/item-docs.mjs +7 -6
- package/engine/journals.mjs +51 -10
- package/engine/macros.mjs +1 -1
- package/engine/map-build.mjs +1 -1
- package/engine/map-dot.mjs +1 -1
- package/engine/markdown-expressions.mjs +117 -0
- package/engine/metadata-index.mjs +7 -3
- package/engine/note-addresses.mjs +1 -0
- package/engine/note-dates.mjs +283 -61
- package/engine/note-package.mjs +5 -25
- package/engine/note-state.mjs +6 -2
- package/engine/note-vocabulary.mjs +74 -19
- package/engine/number-words.mjs +75 -0
- package/engine/office-holders.mjs +188 -0
- package/engine/pack-config.mjs +2 -15
- package/engine/pack-router.mjs +24 -5
- package/engine/packages.mjs +3 -2
- package/engine/pathnames.mjs +21 -24
- package/engine/pdf-build.mjs +217 -55
- package/engine/pdf-fonts.mjs +2 -2
- package/engine/pdf-images.mjs +82 -0
- package/engine/pdf-render.mjs +174 -22
- package/engine/populations.mjs +3 -2
- package/engine/project-init.mjs +499 -0
- package/engine/prose-config.mjs +1 -1
- package/engine/prose-lint.mjs +7 -6
- package/engine/readability-lint.mjs +86 -0
- package/engine/reckoning-markers.mjs +205 -93
- package/engine/scenes.mjs +5 -2
- package/engine/schema-check.mjs +3 -3
- package/engine/site-build.mjs +87 -37
- package/engine/site-config.mjs +8 -8
- package/engine/site-index.mjs +4 -0
- package/engine/site-maps.mjs +1 -1
- package/engine/site-preview.mjs +19 -8
- package/engine/social-ties.mjs +5 -1
- package/engine/sql-tables.mjs +92 -22
- package/engine/web-wikilinks.mjs +15 -31
- package/engine/wikilink-syntax.mjs +4 -1
- package/engine/wikilinks.mjs +12 -14
- package/hm3/item-fields.mjs +1 -1
- package/hm3/items.mjs +12 -45
- package/index.mjs +2 -14
- package/manifest.mjs +31 -4
- package/markdownlint-config.mjs +1 -1
- package/package.json +32 -11
- package/prettier-config.mjs +1 -1
- package/sohl/being-info.mjs +3 -145
- package/sohl/index.mjs +1 -1
- package/sohl/infobox.mjs +2 -1
- package/sohl/item-builders.mjs +3 -27
- package/sohl/item-fields.mjs +61 -22
- package/sohl/kb-passes.mjs +2 -2
- package/sohl/note-schemas.mjs +0 -8
- package/stage.mjs +19 -0
- package/types/config.d.mts +12 -3
- package/types/content-config.d.mts +15 -81
- package/types/engine/address-render.d.mts +13 -0
- package/types/engine/address.d.mts +2 -11
- package/types/engine/anchors.d.mts +3 -4
- package/types/engine/asset-bindings.d.mts +9 -2
- package/types/engine/base-compiler.d.mts +1 -2
- package/types/engine/being-measurements.d.mts +16 -0
- package/types/engine/calendar-choice.d.mts +14 -0
- package/types/engine/calendar-human.d.mts +4 -0
- package/types/engine/calendar-notes.d.mts +2 -0
- package/types/engine/calendars.d.mts +5 -3
- package/types/engine/code-fences.d.mts +6 -72
- package/types/engine/compile-corpus.d.mts +1 -3
- package/types/engine/content-admonitions.d.mts +24 -0
- package/types/engine/content-captions.d.mts +45 -0
- package/types/engine/content-footnotes.d.mts +10 -0
- package/types/engine/content-format-check.d.mts +17 -4
- package/types/engine/content-format.d.mts +24 -11
- package/types/engine/content-icons.d.mts +14 -23
- package/types/engine/content-images.d.mts +2 -2
- package/types/engine/content-index.d.mts +9 -2
- package/types/engine/content-links.d.mts +3 -5
- package/types/engine/content-tables.d.mts +11 -140
- package/types/engine/culture-choice.d.mts +14 -0
- package/types/engine/date-conversion.d.mts +4 -0
- package/types/engine/declaration-check.d.mts +24 -0
- package/types/engine/docs-checks.d.mts +28 -0
- package/types/engine/documentation-links.d.mts +7 -0
- package/types/engine/exported-scene.d.mts +6 -2
- package/types/engine/extension-attributes.d.mts +14 -0
- package/types/engine/field-spec.d.mts +9 -0
- package/types/engine/format-generated.d.mts +7 -0
- package/types/engine/frontmatter.d.mts +14 -4
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +37 -92
- package/types/engine/homepage.d.mts +2 -3
- package/types/engine/index.d.mts +5 -0
- package/types/engine/infobox-registry.d.mts +3 -1
- package/types/engine/infobox.d.mts +17 -6
- package/types/engine/item-compiler.d.mts +4 -4
- package/types/engine/item-docs.d.mts +4 -3
- package/types/engine/journals.d.mts +10 -7
- package/types/engine/map-graphviz.d.mts +1 -1
- package/types/engine/markdown-expressions.d.mts +17 -0
- package/types/engine/note-dates.d.mts +47 -1
- package/types/engine/note-package.d.mts +3 -19
- package/types/engine/note-state.d.mts +2 -1
- package/types/engine/note-vocabulary.d.mts +9 -0
- package/types/engine/number-words.d.mts +4 -0
- package/types/engine/office-holders.d.mts +14 -0
- package/types/engine/pack-config.d.mts +1 -1
- package/types/engine/packages.d.mts +3 -2
- package/types/engine/pdf-images.d.mts +38 -0
- package/types/engine/populations.d.mts +2 -2
- package/types/engine/project-init.d.mts +17 -0
- package/types/engine/prose-config.d.mts +1 -1
- package/types/engine/prose-lint.d.mts +3 -2
- package/types/engine/readability-lint.d.mts +22 -0
- package/types/engine/reckoning-markers.d.mts +4 -2
- package/types/engine/schema-check.d.mts +3 -3
- package/types/engine/site-build.d.mts +38 -28
- package/types/engine/site-config.d.mts +2 -2
- package/types/engine/site-index.d.mts +4 -0
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/social-ties.d.mts +8 -2
- package/types/engine/sql-tables.d.mts +13 -2
- package/types/engine/web-wikilinks.d.mts +2 -1
- package/types/engine/wikilinks.d.mts +7 -1
- package/types/hm3/items.d.mts +5 -6
- package/types/labels.d.mts +40 -0
- package/types/manifest.d.mts +3 -3
- package/types/sohl/actors.d.mts +2 -2
- package/types/sohl/being-info.d.mts +3 -43
- package/types/sohl/index.d.mts +1 -1
- package/types/sohl/infobox.d.mts +5 -2
- package/types/sohl/item-fields.d.mts +4 -13
- package/types/stage.d.mts +9 -0
- package/CONTENT.md +0 -3239
package/bin/package-build.mjs
CHANGED
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
* npx package-build clean [--distclean]
|
|
51
51
|
* npx package-build assets
|
|
52
52
|
* npx package-build calendars --calendaria-version <version>
|
|
53
|
+
* npx package-build datefrom <calendar> <date>
|
|
54
|
+
* npx package-build dateto <calendar> <canonical-date>
|
|
53
55
|
* npx package-build manifest
|
|
54
56
|
* npx package-build lang check
|
|
55
57
|
* npx package-build lang coverage [--unused]
|
|
@@ -76,6 +78,9 @@
|
|
|
76
78
|
import fs from "node:fs";
|
|
77
79
|
import path from "node:path";
|
|
78
80
|
import process from "node:process";
|
|
81
|
+
import { spawnSync } from "node:child_process";
|
|
82
|
+
import { createInterface } from "node:readline/promises";
|
|
83
|
+
import { fileURLToPath } from "node:url";
|
|
79
84
|
import { globSync } from "glob";
|
|
80
85
|
import yargs from "yargs";
|
|
81
86
|
import { hideBin } from "yargs/helpers";
|
|
@@ -87,9 +92,12 @@ import { writeSiteRoot } from "../engine/site-root.mjs";
|
|
|
87
92
|
import { DEPLOY_ROOT } from "../engine/site-config.mjs";
|
|
88
93
|
import { compilesFoundryDocuments } from "../content-config.mjs";
|
|
89
94
|
import { loadPackConfig, packConfigPath, resolveConfigFile } from "../engine/pack-config.mjs";
|
|
90
|
-
import { cleanBuildArtifacts, stageAssets } from "../stage.mjs";
|
|
95
|
+
import { cleanBuildArtifacts, resetStage, stageAssets } from "../stage.mjs";
|
|
91
96
|
import { buildSchemaArtifact } from "../engine/schema-extract.mjs";
|
|
92
97
|
import { emitCalendarArtifacts } from "../engine/calendar-artifacts.mjs";
|
|
98
|
+
import { dateFromCalendar, dateToCalendar } from "../engine/date-conversion.mjs";
|
|
99
|
+
import { walkMarkdownTree } from "../engine/helpers.mjs";
|
|
100
|
+
import { reckoningContext } from "../engine/reckoning-markers.mjs";
|
|
93
101
|
import { SCHEMA_ARTIFACT_FILE } from "../engine/foreign-catalog.mjs";
|
|
94
102
|
import { validateLangSource } from "../lang.mjs";
|
|
95
103
|
import { checkLabelRegistry } from "../labels.mjs";
|
|
@@ -112,6 +120,14 @@ import { deployStage } from "../deploy.mjs";
|
|
|
112
120
|
import { CONTAINER_ACTIONS, containerAction } from "../container.mjs";
|
|
113
121
|
import { E2E_MODES, e2eFast, e2eRun, e2eSweep, seedTestWorld } from "../e2e.mjs";
|
|
114
122
|
import { reportFindings } from "./report.mjs";
|
|
123
|
+
import { formatGenerated } from "../engine/format-generated.mjs";
|
|
124
|
+
import {
|
|
125
|
+
checkProject,
|
|
126
|
+
initializeProject,
|
|
127
|
+
INIT_KINDS,
|
|
128
|
+
INIT_LICENSES,
|
|
129
|
+
} from "../engine/project-init.mjs";
|
|
130
|
+
import { registerContentCommands } from "./content-commands.mjs";
|
|
115
131
|
|
|
116
132
|
/**
|
|
117
133
|
* How many unreferenced keys a run prints before it summarizes the rest.
|
|
@@ -121,6 +137,143 @@ import { reportFindings } from "./report.mjs";
|
|
|
121
137
|
*/
|
|
122
138
|
const ADVISORY_PREVIEW = 20;
|
|
123
139
|
|
|
140
|
+
/** Replay pull-request workflow commands in Docker or on the host. */
|
|
141
|
+
function ciCommand() {
|
|
142
|
+
return {
|
|
143
|
+
command: "ci",
|
|
144
|
+
describe: "Replay pull-request workflow commands",
|
|
145
|
+
builder: (y) =>
|
|
146
|
+
y.option("native", {
|
|
147
|
+
type: "boolean",
|
|
148
|
+
default: false,
|
|
149
|
+
describe: "Run in the current working tree without Docker",
|
|
150
|
+
}),
|
|
151
|
+
handler: handler(async (args) => {
|
|
152
|
+
const script = fileURLToPath(
|
|
153
|
+
new URL(
|
|
154
|
+
args.native ? "../ci/ci-steps.mjs" : "../ci/ci-docker.mjs",
|
|
155
|
+
import.meta.url,
|
|
156
|
+
),
|
|
157
|
+
);
|
|
158
|
+
const top = spawnSync("git", ["rev-parse", "--show-toplevel"], {
|
|
159
|
+
encoding: "utf8",
|
|
160
|
+
});
|
|
161
|
+
const root = top.status === 0 ? top.stdout.trim() : process.cwd();
|
|
162
|
+
const result = spawnSync(process.execPath, [script], {
|
|
163
|
+
cwd: root,
|
|
164
|
+
stdio: "inherit",
|
|
165
|
+
});
|
|
166
|
+
if (result.error) throw result.error;
|
|
167
|
+
if (result.status !== 0) process.exitCode = 1;
|
|
168
|
+
}),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Initialize a repository from anywhere, including outside a Git checkout. */
|
|
173
|
+
function initCommand() {
|
|
174
|
+
return {
|
|
175
|
+
command: "init [directory]",
|
|
176
|
+
describe: "Create or check a content package with site and book targets",
|
|
177
|
+
builder: (y) =>
|
|
178
|
+
y
|
|
179
|
+
.positional("directory", {
|
|
180
|
+
type: "string",
|
|
181
|
+
describe: "Directory to initialize or check (default: current directory)",
|
|
182
|
+
})
|
|
183
|
+
.option("check", {
|
|
184
|
+
type: "boolean",
|
|
185
|
+
default: false,
|
|
186
|
+
describe: "Check an existing package without writing files",
|
|
187
|
+
})
|
|
188
|
+
.option("kind", {
|
|
189
|
+
type: "string",
|
|
190
|
+
choices: INIT_KINDS,
|
|
191
|
+
describe: "Package kind",
|
|
192
|
+
})
|
|
193
|
+
.option("name", { type: "string", describe: "npm and Foundry package name" })
|
|
194
|
+
.option("title", { type: "string", describe: "Human-readable package title" })
|
|
195
|
+
.option("description", { type: "string", describe: "Package and site description" })
|
|
196
|
+
.option("author", { type: "string", describe: "Author credited in the package" })
|
|
197
|
+
.option("license", {
|
|
198
|
+
type: "string",
|
|
199
|
+
choices: INIT_LICENSES,
|
|
200
|
+
describe: "Original or unofficial fan-material license template",
|
|
201
|
+
})
|
|
202
|
+
.option("core-minimum", {
|
|
203
|
+
type: "string",
|
|
204
|
+
default: "14.359",
|
|
205
|
+
describe: "Minimum Foundry core version for a Foundry package",
|
|
206
|
+
})
|
|
207
|
+
.option("core-verified", {
|
|
208
|
+
type: "string",
|
|
209
|
+
default: "14.364",
|
|
210
|
+
describe: "Verified Foundry core version for a Foundry package",
|
|
211
|
+
}),
|
|
212
|
+
handler: handler(async (args) => {
|
|
213
|
+
const root = path.resolve(args.directory ?? process.cwd());
|
|
214
|
+
if (args.check) {
|
|
215
|
+
const findings = checkProject(root);
|
|
216
|
+
for (const { file, message } of findings) {
|
|
217
|
+
console.error(`${path.join(root, file)}: error: ${message}`);
|
|
218
|
+
}
|
|
219
|
+
if (findings.length) process.exitCode = 1;
|
|
220
|
+
else console.log(`package-build: ${root} is ready to build.`);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const defaults = {
|
|
224
|
+
name: path.basename(root),
|
|
225
|
+
title: path
|
|
226
|
+
.basename(root)
|
|
227
|
+
.replaceAll("-", " ")
|
|
228
|
+
.split(" ")
|
|
229
|
+
.map((word) => word[0]?.toUpperCase() + word.slice(1))
|
|
230
|
+
.join(" "),
|
|
231
|
+
kind: "modules",
|
|
232
|
+
license: "original",
|
|
233
|
+
};
|
|
234
|
+
const answers = {
|
|
235
|
+
name: args.name,
|
|
236
|
+
title: args.title,
|
|
237
|
+
description: args.description,
|
|
238
|
+
author: args.author,
|
|
239
|
+
kind: args.kind,
|
|
240
|
+
license: args.license,
|
|
241
|
+
};
|
|
242
|
+
const questions = ["name", "title", "description", "author", "kind", "license"];
|
|
243
|
+
if (questions.some((key) => !answers[key])) {
|
|
244
|
+
if (!process.stdin.isTTY) {
|
|
245
|
+
const missing = questions.filter((key) => !answers[key]);
|
|
246
|
+
throw new Error(
|
|
247
|
+
`init: supply ${missing.map((key) => `--${key}`).join(", ")} when input is not interactive`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const input = createInterface({ input: process.stdin, output: process.stdout });
|
|
251
|
+
try {
|
|
252
|
+
for (const key of questions) {
|
|
253
|
+
if (answers[key]) continue;
|
|
254
|
+
const fallback = defaults[key];
|
|
255
|
+
const value = await input.question(
|
|
256
|
+
`${key}${fallback ? ` [${fallback}]` : ""}: `,
|
|
257
|
+
);
|
|
258
|
+
answers[key] = value.trim() || fallback;
|
|
259
|
+
}
|
|
260
|
+
} finally {
|
|
261
|
+
input.close();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const files = await initializeProject(root, {
|
|
265
|
+
...answers,
|
|
266
|
+
version: ownVersion(),
|
|
267
|
+
coreMinimum: args.coreMinimum,
|
|
268
|
+
coreVerified: args.coreVerified,
|
|
269
|
+
});
|
|
270
|
+
console.log(`package-build: created ${files.length} files in ${root}.`);
|
|
271
|
+
console.log("Run `npm install`, then `npm run lint`.");
|
|
272
|
+
console.log("Use `npm run build:site`, `npm run serve:site`, or `npm run build:book`.");
|
|
273
|
+
}),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
124
277
|
/**
|
|
125
278
|
* This package's own version, for `--version`.
|
|
126
279
|
*
|
|
@@ -318,6 +471,24 @@ function assetsCommand() {
|
|
|
318
471
|
};
|
|
319
472
|
}
|
|
320
473
|
|
|
474
|
+
/** Clear the package stage before a complete build writes to it. */
|
|
475
|
+
function stageCommand() {
|
|
476
|
+
return {
|
|
477
|
+
command: "stage <action>",
|
|
478
|
+
describe: "Manage the assembled Foundry package stage",
|
|
479
|
+
builder: (y) =>
|
|
480
|
+
y.positional("action", {
|
|
481
|
+
choices: ["reset"],
|
|
482
|
+
describe: "Remove the configured stage directory",
|
|
483
|
+
}),
|
|
484
|
+
handler: handler(() => {
|
|
485
|
+
const config = loadPackageBuildConfig();
|
|
486
|
+
const removed = resetStage(config.rootDir, config.stageDir);
|
|
487
|
+
console.log(removed ? `Removed ${config.stageDir}` : "Stage is already empty.");
|
|
488
|
+
}),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
321
492
|
/** Emit the package's calendar definitions from its content index. */
|
|
322
493
|
function calendarsCommand() {
|
|
323
494
|
return {
|
|
@@ -340,32 +511,110 @@ function calendarsCommand() {
|
|
|
340
511
|
};
|
|
341
512
|
}
|
|
342
513
|
|
|
343
|
-
/**
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
514
|
+
/** Read the current content tree so conversion uses its authored calendars. */
|
|
515
|
+
function calendarConversionContext() {
|
|
516
|
+
const config = loadPackConfig();
|
|
517
|
+
if (!fs.existsSync(config.paths.content))
|
|
518
|
+
throw new Error(`no content tree at ${config.paths.content}`);
|
|
519
|
+
const notes = [
|
|
520
|
+
...walkMarkdownTree(config.paths.content, {
|
|
521
|
+
skipDirectories: config.skipDirectories,
|
|
522
|
+
}),
|
|
523
|
+
].map(({ frontmatter, absPath }) => ({ fm: frontmatter, file: absPath }));
|
|
524
|
+
const context = reckoningContext({ notes });
|
|
525
|
+
const error = context.findings.find((finding) => finding.severity === "error");
|
|
526
|
+
if (error) throw Object.assign(new Error(formatDiagnostic(error)), { located: true });
|
|
527
|
+
if (!Number.isSafeInteger(context.daysPerYear) || context.daysPerYear < 1)
|
|
528
|
+
throw new Error("the content tree needs a world with data.year.days");
|
|
529
|
+
return context;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/** Convert a calendar day to the world's canonical year and day. */
|
|
533
|
+
function datefromCommand() {
|
|
534
|
+
return {
|
|
535
|
+
command: "datefrom <calendar> <date>",
|
|
536
|
+
describe: "Convert a calendar date to its canonical value",
|
|
537
|
+
builder: (y) =>
|
|
538
|
+
y
|
|
539
|
+
.positional("calendar", {
|
|
540
|
+
type: "string",
|
|
541
|
+
describe: "Calendar Address or shortcode",
|
|
542
|
+
})
|
|
543
|
+
.positional("date", { type: "string", describe: "Day in that calendar" }),
|
|
544
|
+
handler: handler((args) => {
|
|
545
|
+
console.log(dateFromCalendar(args.calendar, args.date, calendarConversionContext()));
|
|
546
|
+
}),
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/** Express a canonical day in the covering era of a calendar. */
|
|
551
|
+
function datetoCommand() {
|
|
552
|
+
return {
|
|
553
|
+
command: "dateto <calendar> <canonical-date>",
|
|
554
|
+
describe: "Convert a canonical day to a calendar date",
|
|
555
|
+
builder: (y) =>
|
|
556
|
+
y
|
|
557
|
+
.positional("calendar", {
|
|
558
|
+
type: "string",
|
|
559
|
+
describe: "Calendar Address or shortcode",
|
|
560
|
+
})
|
|
561
|
+
.positional("canonical-date", {
|
|
562
|
+
type: "string",
|
|
563
|
+
describe: "Canonical <year>.<day>[:HHMMSS]",
|
|
564
|
+
}),
|
|
565
|
+
handler: handler((args) => {
|
|
566
|
+
console.log(
|
|
567
|
+
dateToCalendar(args.calendar, args.canonicalDate, calendarConversionContext()),
|
|
568
|
+
);
|
|
569
|
+
}),
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/** Check a project's declarations with library checks enabled. */
|
|
574
|
+
function typesCheckCommand() {
|
|
575
|
+
return {
|
|
576
|
+
command: "check",
|
|
577
|
+
describe: "Check project declaration files with TypeScript library checks enabled",
|
|
578
|
+
builder: (yargs) =>
|
|
579
|
+
yargs
|
|
580
|
+
.option("project", {
|
|
581
|
+
describe: "TypeScript project file.",
|
|
582
|
+
type: "string",
|
|
583
|
+
default: "tsconfig.json",
|
|
584
|
+
})
|
|
585
|
+
.option("exports", {
|
|
586
|
+
describe: "Include every declaration entry point in package.json exports.",
|
|
587
|
+
type: "boolean",
|
|
588
|
+
default: false,
|
|
589
|
+
}),
|
|
590
|
+
handler: handler(async (args) => {
|
|
591
|
+
const { checkDeclarations } = await import("../engine/declaration-check.mjs");
|
|
592
|
+
const findings = checkDeclarations(args.project, { exports: args.exports }).map(
|
|
593
|
+
(finding) => ({
|
|
594
|
+
...finding,
|
|
595
|
+
file: path.relative(process.cwd(), finding.file),
|
|
596
|
+
}),
|
|
597
|
+
);
|
|
598
|
+
if (reportFindings(findings, {}) > 0) process.exitCode = 1;
|
|
599
|
+
else console.log("Package declarations are valid.");
|
|
600
|
+
}),
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/** Check project declaration files. */
|
|
605
|
+
function typesCommand() {
|
|
606
|
+
return {
|
|
607
|
+
command: "types <action>",
|
|
608
|
+
describe: "Check TypeScript declarations",
|
|
609
|
+
builder: (yargs) => yargs.command(typesCheckCommand()).demandCommand(1).strict(),
|
|
610
|
+
handler: () => {},
|
|
611
|
+
};
|
|
363
612
|
}
|
|
364
613
|
|
|
365
614
|
/**
|
|
366
615
|
* `schema` — publish this package's DataModel field sets as `schema.json`.
|
|
367
616
|
*
|
|
368
|
-
* The producing half of the check `
|
|
617
|
+
* The producing half of the check `package-build lint` runs: Foundry discards
|
|
369
618
|
* an unknown `system` key at construction and says nothing, so a content build
|
|
370
619
|
* needs to know what a document will actually receive. It cannot ask a
|
|
371
620
|
* running Foundry, and it cannot read `defineSchema()` from a sibling checkout,
|
|
@@ -447,7 +696,7 @@ function manifestCommand() {
|
|
|
447
696
|
die(
|
|
448
697
|
`\`packageKind: ${shared.packageKind}\` ships no Foundry ` +
|
|
449
698
|
`package, so there is no manifest to generate. The site ` +
|
|
450
|
-
`and the book are built by \`
|
|
699
|
+
`and the book are built by \`package-build\`.`,
|
|
451
700
|
);
|
|
452
701
|
}
|
|
453
702
|
const packageJson = readPackageJson(config);
|
|
@@ -1406,27 +1655,39 @@ function bumpCommand() {
|
|
|
1406
1655
|
};
|
|
1407
1656
|
}
|
|
1408
1657
|
|
|
1409
|
-
|
|
1410
|
-
.
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1658
|
+
registerContentCommands(
|
|
1659
|
+
yargs(hideBin(process.argv))
|
|
1660
|
+
.scriptName("package-build")
|
|
1661
|
+
.command(initCommand())
|
|
1662
|
+
.command(ciCommand())
|
|
1663
|
+
.command(cleanCommand())
|
|
1664
|
+
.command(assetsCommand())
|
|
1665
|
+
.command(stageCommand())
|
|
1666
|
+
.command(calendarsCommand())
|
|
1667
|
+
.command(datefromCommand())
|
|
1668
|
+
.command(datetoCommand())
|
|
1669
|
+
.command(manifestCommand())
|
|
1670
|
+
.command(siteRootCommand())
|
|
1671
|
+
.command(typesCommand())
|
|
1672
|
+
.command(schemaCommand())
|
|
1673
|
+
.command(langCommand())
|
|
1674
|
+
.command(labelsCommand())
|
|
1675
|
+
.command(bumpCommand())
|
|
1676
|
+
.command(yamlCommand())
|
|
1677
|
+
.command(changelogCommand())
|
|
1678
|
+
.command(bundleCommand())
|
|
1679
|
+
.command(releaseCommand())
|
|
1680
|
+
.command(deployCommand())
|
|
1681
|
+
.command(containerCommand())
|
|
1682
|
+
.command(e2eCommand()),
|
|
1683
|
+
)
|
|
1427
1684
|
.demandCommand(1, "Name a command.")
|
|
1428
1685
|
.strict()
|
|
1429
1686
|
.version(ownVersion())
|
|
1430
1687
|
.help()
|
|
1431
1688
|
.alias("help", "h")
|
|
1432
|
-
.
|
|
1689
|
+
.epilog("Run package-build <command> --help for that command's actions and options.")
|
|
1690
|
+
.fail((message, err, cli) => {
|
|
1691
|
+
if (!err) cli.showHelp("error");
|
|
1692
|
+
die(err ?? message);
|
|
1693
|
+
}).argv;
|
package/bin/report.mjs
CHANGED
|
@@ -104,7 +104,7 @@ export function toDiagnostics(findings, { file }) {
|
|
|
104
104
|
* Optional only when every finding carries its own.
|
|
105
105
|
* @param {(d: object) => void} [opts.emit] - How to emit one diagnostic.
|
|
106
106
|
* Injectable so a test can capture the emitted shape without reaching for
|
|
107
|
-
* the console; defaults to
|
|
107
|
+
* the console; defaults to package-build's `emitDiagnostic`, which writes
|
|
108
108
|
* both severities to stderr.
|
|
109
109
|
* @returns {number} How many diagnostics were errors — the count a command
|
|
110
110
|
* turns into its exit code.
|
package/ci/ci-docker.mjs
CHANGED
|
@@ -194,7 +194,7 @@ function main() {
|
|
|
194
194
|
"--rm",
|
|
195
195
|
...(native ? [] : ["--platform", "linux/amd64"]),
|
|
196
196
|
"-v",
|
|
197
|
-
`${dir}:/
|
|
197
|
+
`${dir}:/source:ro`,
|
|
198
198
|
// The step runner comes from this package, not from the export: the
|
|
199
199
|
// repository under test does not carry it, and should not have to.
|
|
200
200
|
"-v",
|
|
@@ -202,8 +202,11 @@ function main() {
|
|
|
202
202
|
"-w",
|
|
203
203
|
"/work",
|
|
204
204
|
image,
|
|
205
|
-
"
|
|
206
|
-
"
|
|
205
|
+
"sh",
|
|
206
|
+
"-c",
|
|
207
|
+
// Run from the container's own filesystem. Some generators create
|
|
208
|
+
// files that Docker Desktop cannot read back through a macOS mount.
|
|
209
|
+
"mkdir -p /work && cp -a /source/. /work && exec node /ci/ci-steps.mjs",
|
|
207
210
|
]);
|
|
208
211
|
|
|
209
212
|
fs.rmSync(dir, { recursive: true, force: true });
|
package/ci/ci-steps.mjs
CHANGED
|
@@ -69,8 +69,8 @@ const WORKFLOW_DIR = path.join(ROOT, ".github/workflows");
|
|
|
69
69
|
/**
|
|
70
70
|
* The workflow's steps, split into what can be run here and what cannot.
|
|
71
71
|
*
|
|
72
|
-
* @returns {{run: Array<{name: string, run: string}>, skipped: string[]}}
|
|
73
|
-
* `run:` steps in
|
|
72
|
+
* @returns {{run: Array<{name: string, run: string}>, skipped: string[], files: string[]}}
|
|
73
|
+
* Workflow files, their `run:` steps in order, and the `uses:` step names.
|
|
74
74
|
* @throws {Error} When the workflow cannot be read or declares no `run:` step.
|
|
75
75
|
*/
|
|
76
76
|
export function ciSteps() {
|
|
@@ -213,8 +213,10 @@ function main() {
|
|
|
213
213
|
return 1;
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
const { run, skipped } = steps;
|
|
217
|
-
console.log(
|
|
216
|
+
const { run, skipped, files } = steps;
|
|
217
|
+
console.log(
|
|
218
|
+
`ci-steps: running ${run.length} step(s) from ${files.map((file) => path.relative(ROOT, file)).join(", ")}`,
|
|
219
|
+
);
|
|
218
220
|
if (skipped.length) {
|
|
219
221
|
console.log(`ci-steps: not runnable here (published actions): ${skipped.join(", ")}`);
|
|
220
222
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { formatGenerated } from "../engine/format-generated.mjs";
|
|
9
|
+
import { loadContentFormat } from "../engine/content-format.mjs";
|
|
10
|
+
import { NOTE_VOCABULARY, SHARED_DATA_FIELDS } from "../engine/note-vocabulary.mjs";
|
|
11
|
+
|
|
12
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
|
+
const output = path.join(root, "docs/reference/note-types.md");
|
|
14
|
+
const contract = loadContentFormat();
|
|
15
|
+
const cell = (value) =>
|
|
16
|
+
String(value ?? "")
|
|
17
|
+
.replaceAll("|", "\\|")
|
|
18
|
+
.replaceAll("\n", " ");
|
|
19
|
+
const fields = (list) =>
|
|
20
|
+
list.map(
|
|
21
|
+
(field) =>
|
|
22
|
+
`| \`data.${cell(field.name)}\` | ${cell(field.shape ?? field.kind ?? "value")} | ${cell(field.describe)} |`,
|
|
23
|
+
);
|
|
24
|
+
const rows = [
|
|
25
|
+
"---",
|
|
26
|
+
"shortcode: referencenotetypes",
|
|
27
|
+
'name: { full: "Note types and fields" }',
|
|
28
|
+
"type: doc",
|
|
29
|
+
"subType: reference",
|
|
30
|
+
"---",
|
|
31
|
+
"",
|
|
32
|
+
"# Note types and fields",
|
|
33
|
+
"",
|
|
34
|
+
"Choose a note's `type` for the subject it describes. Use `subType` when its type declares a more specific genre. Field rules are declared in `engine/note-vocabulary.mjs`; mapping claims and closed vocabularies are recorded in `engine/content-format.yaml`. This page is checked against both declarations.",
|
|
35
|
+
"",
|
|
36
|
+
"The [first-note guide](../authoring/first-note.md) teaches the shape of a note. [Frontmatter](../authoring/frontmatter.md) explains shared and system-specific values. The [detailed reference](format-details.md) gives the full behavior and examples.",
|
|
37
|
+
"",
|
|
38
|
+
"## Shared `data` fields",
|
|
39
|
+
"",
|
|
40
|
+
"These fields are accepted by every note type. A field's value can still be irrelevant to a particular output; the build reports that where it can.",
|
|
41
|
+
"",
|
|
42
|
+
"| Field | Shape | Meaning |",
|
|
43
|
+
"| --- | --- | --- |",
|
|
44
|
+
...fields(SHARED_DATA_FIELDS),
|
|
45
|
+
"",
|
|
46
|
+
"## Type-specific fields",
|
|
47
|
+
"",
|
|
48
|
+
];
|
|
49
|
+
for (const [type, spec] of contract.types) {
|
|
50
|
+
const declared = NOTE_VOCABULARY[type];
|
|
51
|
+
if (!declared) throw new Error(`No vocabulary for ${type}`);
|
|
52
|
+
rows.push(`### ${type}`, "");
|
|
53
|
+
rows.push(
|
|
54
|
+
`**Subtypes:** ${
|
|
55
|
+
declared.subTypes === null ? "Open vocabulary"
|
|
56
|
+
: spec.subTypes.length ? spec.subTypes.map((value) => `\`${value}\``).join(", ")
|
|
57
|
+
: "None"
|
|
58
|
+
}.`,
|
|
59
|
+
"",
|
|
60
|
+
);
|
|
61
|
+
if (type === "lore") {
|
|
62
|
+
rows.push(
|
|
63
|
+
"Lore records in-world knowledge. A `culture` describes a people; a `custom` describes how they practice a rite, observance, or usage. A `material` describes a physical constituent and its qualities, which may vary by region. See the [lore subtype definitions](format-details.md#type-lore) for the complete vocabulary.",
|
|
64
|
+
"",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
rows.push("| Field | Shape | Meaning |", "| --- | --- | --- |");
|
|
68
|
+
rows.push(
|
|
69
|
+
...(declared.data?.length ?
|
|
70
|
+
fields(declared.data)
|
|
71
|
+
: ["| — | — | No type-specific `data` fields. |"]),
|
|
72
|
+
);
|
|
73
|
+
rows.push("");
|
|
74
|
+
const claims = contract.claims.filter((claim) => !claim.shared && claim.noteType === type);
|
|
75
|
+
if (claims.length) {
|
|
76
|
+
rows.push("**System mappings**", "", "| Source | System | Target |", "| --- | --- | --- |");
|
|
77
|
+
rows.push(
|
|
78
|
+
...claims.map(
|
|
79
|
+
(claim) =>
|
|
80
|
+
`| \`${cell(claim.source)}\` | ${cell(claim.system)} | \`${cell(claim.target)}\` |`,
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
rows.push("");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
rows.push("## Shared system mappings", "", "| Source | System | Target |", "| --- | --- | --- |");
|
|
87
|
+
rows.push(
|
|
88
|
+
...contract.claims
|
|
89
|
+
.filter((claim) => claim.shared)
|
|
90
|
+
.map(
|
|
91
|
+
(claim) =>
|
|
92
|
+
`| \`${cell(claim.source)}\` | ${cell(claim.system)} | \`${cell(claim.target)}\` |`,
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
rows.push("");
|
|
96
|
+
const result = await formatGenerated(`${rows.join("\n")}\n`, output);
|
|
97
|
+
if (process.argv.includes("--check")) {
|
|
98
|
+
if (!fs.existsSync(output) || fs.readFileSync(output, "utf8") !== result) {
|
|
99
|
+
console.error(
|
|
100
|
+
`${path.relative(root, output)}: error: run node ci/content-format-reference.mjs to refresh the note-type reference`,
|
|
101
|
+
);
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
process.stdout.write(result);
|
|
106
|
+
}
|
package/config.mjs
CHANGED
|
@@ -75,6 +75,7 @@ const SECTION_KEYS = [
|
|
|
75
75
|
"bundle",
|
|
76
76
|
"container",
|
|
77
77
|
"e2e",
|
|
78
|
+
"proseLint",
|
|
78
79
|
];
|
|
79
80
|
|
|
80
81
|
/**
|
|
@@ -526,7 +527,8 @@ function normalizeExceptions(value, field, where) {
|
|
|
526
527
|
* The resolved `packageBuild` section, every optional half filled in.
|
|
527
528
|
*
|
|
528
529
|
* @typedef {object} PackageBuildConfig
|
|
529
|
-
* @property {string} rootDir The repository root, from
|
|
530
|
+
* @property {string} rootDir The repository root, from package-build.
|
|
531
|
+
* @property {{age: number, threshold: number, minWords: number}} proseLint Optional prose analysis settings.
|
|
530
532
|
* @property {string} packageKind `systems` or `modules`.
|
|
531
533
|
* @property {string} packageId The Foundry package id.
|
|
532
534
|
* @property {string} artifact Derived: `system` or `module`.
|
|
@@ -689,6 +691,23 @@ export function resolvePackageBuildConfig(shared) {
|
|
|
689
691
|
const section = /** @type {Record<string, unknown>} */ (shared.packageBuild ?? {});
|
|
690
692
|
rejectUnknownKeys(section, SECTION_KEYS, "packageBuild.");
|
|
691
693
|
|
|
694
|
+
const proseLint = section.proseLint ?? {};
|
|
695
|
+
if (!isMapping(proseLint)) fail("packageBuild.proseLint", "must be a mapping");
|
|
696
|
+
rejectUnknownKeys(proseLint, ["age", "threshold", "minWords"], "packageBuild.proseLint.");
|
|
697
|
+
const proseOptions = { age: 21, threshold: 5, minWords: 8 };
|
|
698
|
+
for (const [key, value] of Object.entries(proseLint)) {
|
|
699
|
+
const upper = key === "threshold" ? 7 : Infinity;
|
|
700
|
+
if (!Number.isInteger(value) || value < 1 || value > upper) {
|
|
701
|
+
fail(
|
|
702
|
+
`packageBuild.proseLint.${key}`,
|
|
703
|
+
upper === Infinity ?
|
|
704
|
+
"must be a positive integer"
|
|
705
|
+
: "must be an integer from 1 to 7",
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
proseOptions[key] = value;
|
|
709
|
+
}
|
|
710
|
+
|
|
692
711
|
if (section.assets !== undefined && !Array.isArray(section.assets)) {
|
|
693
712
|
fail("packageBuild.assets", "must be a list");
|
|
694
713
|
}
|
|
@@ -789,6 +808,7 @@ export function resolvePackageBuildConfig(shared) {
|
|
|
789
808
|
|
|
790
809
|
return Object.freeze({
|
|
791
810
|
rootDir: shared.rootDir,
|
|
811
|
+
proseLint: Object.freeze(proseOptions),
|
|
792
812
|
// Where the package is assembled before it is zipped or deployed. Every
|
|
793
813
|
// asset destination is relative to it, so a repository's table says
|
|
794
814
|
// `lang`, not `build/stage/lang` — the latter is what each consumer's
|
|
@@ -921,7 +941,7 @@ export function resolvePackageBuildConfig(shared) {
|
|
|
921
941
|
/**
|
|
922
942
|
* The repository's resolved package-build configuration.
|
|
923
943
|
*
|
|
924
|
-
* Read on call rather than at import, exactly as
|
|
944
|
+
* Read on call rather than at import, exactly as package-build resolves its
|
|
925
945
|
* own: importing a module of this package must not require a configuration to
|
|
926
946
|
* exist anywhere above it.
|
|
927
947
|
*
|