@heroiclands/package-build 25.0.0 → 25.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 +26 -0
- package/README.md +1 -0
- package/bin/content-commands.mjs +30 -96
- package/bin/encoding-commands.mjs +146 -0
- package/bin/package-build.mjs +35 -24
- package/config.mjs +25 -0
- package/convert.mjs +182 -0
- package/docs/api.md +144 -6
- package/docs/authoring/links-and-markup.md +260 -481
- package/docs/commands.md +110 -119
- package/docs/configuration.md +31 -13
- package/docs/content-format.md +11 -4
- package/docs/diagnostics.md +43 -16
- package/docs/encoding-core.md +193 -0
- package/docs/guides/book.md +2 -2
- package/docs/guides/module-project.md +23 -1
- package/docs/guides/site.md +2 -2
- package/docs/guides/writing-in-invented-scripts.md +458 -575
- package/docs/reference/format-details.md +292 -404
- package/docs/reference/note-types.md +83 -63
- package/docs/source-preview.md +8 -0
- package/e2e.mjs +1 -1
- package/engine/base-compiler.mjs +27 -9
- package/engine/content-charset.mjs +13 -1
- package/engine/content-format.yaml +6 -2
- package/engine/content-index.mjs +6 -2
- package/engine/content-links.mjs +1 -0
- package/engine/content-scripts.mjs +614 -459
- package/engine/content-scroll.mjs +4 -4
- package/engine/content-spans.mjs +40 -9
- package/engine/content-verse.mjs +1 -1
- package/engine/converter-data.mjs +70 -0
- package/engine/dictionary-rows.mjs +2 -34
- package/engine/encoding-integrity.mjs +94 -0
- package/engine/encoding-language.mjs +506 -0
- package/engine/encoding-lexicon.mjs +701 -0
- package/engine/encoding-notes.mjs +186 -0
- package/engine/encoding-standard.mjs +42 -0
- package/engine/encoding-template.mjs +370 -0
- package/engine/encoding-terms.mjs +178 -0
- package/engine/encoding.mjs +952 -0
- package/engine/extension-attributes.mjs +2 -0
- package/engine/helpers.mjs +1 -1
- package/engine/index.mjs +7 -0
- package/engine/language-terms.mjs +17 -16
- package/engine/{logographic.mjs → language-writings.mjs} +29 -38
- package/engine/markup-validation.mjs +1 -1
- package/engine/note-vocabulary.mjs +4 -158
- package/engine/pdf-render.mjs +31 -3
- package/engine/roman-characters.mjs +135 -16
- package/engine/roman-run.mjs +149 -0
- package/engine/roman-verse.mjs +95 -0
- package/engine/run-rows.mjs +29 -31
- package/engine/run-words.mjs +2 -2
- package/engine/script-emphasis-terms.mjs +3 -0
- package/engine/script-emphasis.mjs +3 -10
- package/engine/script-font-delivery.mjs +9 -12
- package/engine/script-fonts.mjs +2 -1
- package/engine/site-build.mjs +14 -5
- package/engine/site-preview.mjs +12 -2
- package/engine/sql-tables.mjs +36 -31
- package/engine/verse-metre.mjs +1 -1
- package/engine/word-forms.mjs +1 -1
- package/engine/word-inflections.mjs +48 -0
- package/engine/word-rows.mjs +1 -1
- package/engine/word-terms.mjs +16 -17
- package/package.json +11 -5
- package/types/config.d.mts +5 -0
- package/types/convert.d.mts +44 -0
- package/types/e2e.d.mts +1 -1
- package/types/engine/base-compiler.d.mts +4 -2
- package/types/engine/content-index.d.mts +4 -1
- package/types/engine/content-scripts.d.mts +103 -42
- package/types/engine/converter-data.d.mts +11 -0
- package/types/engine/dictionary-rows.d.mts +1 -25
- package/types/engine/encoding-integrity.d.mts +14 -0
- package/types/engine/encoding-language.d.mts +36 -0
- package/types/engine/encoding-lexicon.d.mts +259 -0
- package/types/engine/encoding-notes.d.mts +39 -0
- package/types/engine/encoding-standard.d.mts +11 -0
- package/types/engine/encoding-template.d.mts +44 -0
- package/types/engine/encoding-terms.d.mts +143 -0
- package/types/engine/encoding.d.mts +155 -0
- package/types/engine/helpers.d.mts +1 -1
- package/types/engine/index.d.mts +4 -0
- package/types/engine/language-terms.d.mts +1 -1
- package/types/engine/{logographic.d.mts → language-writings.d.mts} +6 -6
- package/types/engine/markup-validation.d.mts +1 -1
- package/types/engine/pdf-render.d.mts +1 -1
- package/types/engine/roman-characters.d.mts +2 -2
- package/types/engine/roman-run.d.mts +101 -0
- package/types/engine/roman-verse.d.mts +23 -0
- package/types/engine/run-rows.d.mts +13 -20
- package/types/engine/script-emphasis-terms.d.mts +2 -0
- package/types/engine/script-emphasis.d.mts +1 -1
- package/types/engine/script-font-delivery.d.mts +2 -3
- package/types/engine/script-fonts.d.mts +5 -0
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/word-inflections.d.mts +25 -0
- package/engine/map-lexicon.mjs +0 -1045
- package/engine/text-maps.mjs +0 -654
- package/engine/written-in.mjs +0 -204
- package/types/engine/map-lexicon.d.mts +0 -330
- package/types/engine/text-maps.d.mts +0 -189
- package/types/engine/written-in.d.mts +0 -76
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 25.1.1
|
|
4
|
+
|
|
5
|
+
**Language conversion**
|
|
6
|
+
|
|
7
|
+
- Scripts can opt into conventional spellings for unwritten vowels and shared signs without hidden recovery marks; exact conversion remains the default.
|
|
8
|
+
- Signs can vary before consonants or at word and line boundaries, and explicit word dividers can become spaces.
|
|
9
|
+
- Accent settings can explicitly allow or forbid diacritics.
|
|
10
|
+
|
|
11
|
+
**Spelling checks**
|
|
12
|
+
|
|
13
|
+
- English translations in name registers no longer trigger spelling errors for the invented language.
|
|
14
|
+
|
|
15
|
+
## 25.1.0
|
|
16
|
+
|
|
17
|
+
**Writing and conversion**
|
|
18
|
+
|
|
19
|
+
- Native writing now requires an encoding note that chooses its language and font, while unencoded text keeps its Roman spelling.
|
|
20
|
+
- Language runs share Roman punctuation and literal glyph codes, preserving typed spelling and case while locating invalid characters.
|
|
21
|
+
- The `encode` and `decode` commands replace `transliterate`; browser conversion can use vocabulary to recover words and names while preserving stress.
|
|
22
|
+
|
|
23
|
+
**Rendering and preview**
|
|
24
|
+
|
|
25
|
+
- Nested emphasis follows the surrounding script, using its red ink or overline convention.
|
|
26
|
+
- Indexed previews show source positions for text they cannot render while keeping the readable preview visible.
|
|
27
|
+
- Ch'alish P'ilam Common text no longer triggers a false character warning.
|
|
28
|
+
|
|
3
29
|
## 25.0.0
|
|
4
30
|
|
|
5
31
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -78,6 +78,7 @@ The documentation lives in the Markdown files in this repository. Start with [th
|
|
|
78
78
|
| Foundry integration testing | [End-to-end testing](docs/guides/e2e.md), including an optional S3 server |
|
|
79
79
|
| Release and deployment | [Release and deploy](docs/guides/release-and-deploy.md) |
|
|
80
80
|
| Invented languages and scripts | [Write in invented scripts](docs/guides/writing-in-invented-scripts.md) |
|
|
81
|
+
| Encoding rules and the conversion core | [Encoding core](docs/encoding-core.md) |
|
|
81
82
|
| Buffer-only preview and Gitea rendering | [Source preview](docs/source-preview.md) |
|
|
82
83
|
| Every CLI command | [Commands](docs/commands.md) |
|
|
83
84
|
| Every build setting | [Configuration](docs/configuration.md) |
|
package/bin/content-commands.mjs
CHANGED
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
* npx package-build site [--out <dir>]
|
|
31
31
|
* npx package-build map [--tree] [--from <shortcode>] [--chart <shortcode>] [--travel] [--out <dir>]
|
|
32
32
|
* npx package-build reachability <dir> [file] [--index <shortcode>]
|
|
33
|
-
* npx package-build transliterate <map> [--lang <language>] [--font <slug>] [--hex] [text..]
|
|
34
33
|
* npx package-build lexicon <language> [--map <map>] [--font <slug>] [--derived] [--json]
|
|
35
34
|
* npx package-build addresses diff --from <zip|dir> [--strict]
|
|
36
35
|
* npx package-build npc new (--spec <json> | --specfile <path>) [--seed <text>] [--out <path>]
|
|
@@ -110,11 +109,10 @@ import { coreReport, coreReportText } from "../engine/core-report.mjs";
|
|
|
110
109
|
import { checkScriptNoteFonts } from "../engine/script-emphasis.mjs";
|
|
111
110
|
import {
|
|
112
111
|
checkLexiconNatives,
|
|
113
|
-
|
|
112
|
+
checkEncodingLexicon,
|
|
114
113
|
languageNoteFindings,
|
|
115
114
|
lexiconEntries,
|
|
116
|
-
|
|
117
|
-
} from "../engine/map-lexicon.mjs";
|
|
115
|
+
} from "../engine/encoding-lexicon.mjs";
|
|
118
116
|
import { checkFormatting, checkPrettierConventions, lintMarkdown } from "../engine/prose-lint.mjs";
|
|
119
117
|
import { lintProse, scoreProseTree } from "../engine/readability-lint.mjs";
|
|
120
118
|
import { loadPackageBuildConfig } from "../config.mjs";
|
|
@@ -321,7 +319,6 @@ export function registerContentCommands(cli) {
|
|
|
321
319
|
)
|
|
322
320
|
.command(withIndexPreflight(mapCommand()))
|
|
323
321
|
.command(withIndexPreflight(reachabilityCommand()))
|
|
324
|
-
.command(transliterateCommand())
|
|
325
322
|
.command(lexiconCommand())
|
|
326
323
|
.command(coreCommand())
|
|
327
324
|
.command(addressesCommand())
|
|
@@ -1285,15 +1282,22 @@ function lintCommand() {
|
|
|
1285
1282
|
// the languages this package declares, the faces it installed
|
|
1286
1283
|
// and the maps its notes declare.
|
|
1287
1284
|
const runs = runContext(config, records);
|
|
1285
|
+
const loaderLocations = new Set(
|
|
1286
|
+
(runs.encodingFindings ?? []).map((one) =>
|
|
1287
|
+
JSON.stringify([one.file, one.line, one.column, one.message]),
|
|
1288
|
+
),
|
|
1289
|
+
);
|
|
1290
|
+
const reportedLoaders = new Set();
|
|
1288
1291
|
const findings = [
|
|
1289
1292
|
...index.notes.flatMap((note) =>
|
|
1290
1293
|
markupFindings(note.raw, note.file, session.syntax, runs),
|
|
1291
1294
|
),
|
|
1292
1295
|
// Each mapping note's faces, each language's native forms
|
|
1293
1296
|
// and each language's `note`, against the faces and maps.
|
|
1294
|
-
...index.notes.flatMap((note) => checkMapFonts(note, runs)),
|
|
1295
1297
|
...index.notes.flatMap((note) => checkScriptNoteFonts(note, runs)),
|
|
1296
1298
|
...index.notes.flatMap((note) => checkLexiconNatives(note, runs)),
|
|
1299
|
+
...index.notes.flatMap((note) => checkEncodingLexicon(note, runs)),
|
|
1300
|
+
...(runs.encodingFindings ?? []),
|
|
1297
1301
|
...languageNoteFindings({ ...config, configFile: packConfigPath() }, records),
|
|
1298
1302
|
...index.notes.flatMap(firstUseChecker),
|
|
1299
1303
|
...(names ? [names] : []),
|
|
@@ -1309,7 +1313,13 @@ function lintCommand() {
|
|
|
1309
1313
|
...states.findings,
|
|
1310
1314
|
...generatedArt,
|
|
1311
1315
|
...artSizes,
|
|
1312
|
-
]
|
|
1316
|
+
].filter((one) => {
|
|
1317
|
+
const key = JSON.stringify([one.file, one.line, one.column, one.message]);
|
|
1318
|
+
if (!loaderLocations.has(key)) return true;
|
|
1319
|
+
if (reportedLoaders.has(key)) return false;
|
|
1320
|
+
reportedLoaders.add(key);
|
|
1321
|
+
return true;
|
|
1322
|
+
});
|
|
1313
1323
|
// Only an **error** fails the run. `reportFindings` draws that
|
|
1314
1324
|
// line, so the rule is the shared one rather than a second
|
|
1315
1325
|
// copy here. Each finding names its own file.
|
|
@@ -2566,7 +2576,7 @@ function mapCommand() {
|
|
|
2566
2576
|
* `undefined` when the content tree could not be read whole, which is
|
|
2567
2577
|
* reported.
|
|
2568
2578
|
*/
|
|
2569
|
-
function
|
|
2579
|
+
function encodingContext() {
|
|
2570
2580
|
const config = loadPackConfig();
|
|
2571
2581
|
const problems = [];
|
|
2572
2582
|
const records = indexRecordsFor({
|
|
@@ -2590,7 +2600,7 @@ function mappingContext() {
|
|
|
2590
2600
|
* @param {{problem: string, about: string}} found - What is wrong.
|
|
2591
2601
|
* @param {string} [lang] - The language asked for.
|
|
2592
2602
|
*/
|
|
2593
|
-
function
|
|
2603
|
+
function reportEncoding(config, found) {
|
|
2594
2604
|
if (found.about === "lang") {
|
|
2595
2605
|
const file = packConfigPath();
|
|
2596
2606
|
let text;
|
|
@@ -2613,84 +2623,10 @@ function reportMapping(config, found) {
|
|
|
2613
2623
|
}
|
|
2614
2624
|
|
|
2615
2625
|
/**
|
|
2616
|
-
* `package-build
|
|
2617
|
-
*
|
|
2618
|
-
*
|
|
2619
|
-
* @returns {object} The yargs command module.
|
|
2620
|
-
*/
|
|
2621
|
-
// eslint-disable-next-line
|
|
2622
|
-
function transliterateCommand() {
|
|
2623
|
-
return {
|
|
2624
|
-
command: "transliterate <map> [text..]",
|
|
2625
|
-
describe: "Map a passage into a script exactly as the build maps a marked run",
|
|
2626
|
-
builder: (yargs) =>
|
|
2627
|
-
yargs
|
|
2628
|
-
.positional("map", {
|
|
2629
|
-
type: "string",
|
|
2630
|
-
describe: "The `data.map.name` of a mapping note",
|
|
2631
|
-
})
|
|
2632
|
-
.positional("text", {
|
|
2633
|
-
type: "string",
|
|
2634
|
-
array: true,
|
|
2635
|
-
describe: "The passage; `-` or nothing reads it from stdin",
|
|
2636
|
-
})
|
|
2637
|
-
.option("lang", {
|
|
2638
|
-
type: "string",
|
|
2639
|
-
describe: "A declared language, whose lexicon is read first",
|
|
2640
|
-
})
|
|
2641
|
-
.option("font", {
|
|
2642
|
-
type: "string",
|
|
2643
|
-
describe:
|
|
2644
|
-
"The face; the language's `data.writtenIn` face where the map is drawn in it, and required when neither it nor the map gives one",
|
|
2645
|
-
})
|
|
2646
|
-
.option("hex", {
|
|
2647
|
-
type: "boolean",
|
|
2648
|
-
default: false,
|
|
2649
|
-
describe: "Print the code points, as `U+XXXX` separated by spaces",
|
|
2650
|
-
}),
|
|
2651
|
-
handler: async (argv) => {
|
|
2652
|
-
try {
|
|
2653
|
-
const read = mappingContext();
|
|
2654
|
-
if (!read) {
|
|
2655
|
-
process.exitCode = 1;
|
|
2656
|
-
return;
|
|
2657
|
-
}
|
|
2658
|
-
const words = (argv.text ?? []).map(String);
|
|
2659
|
-
const text =
|
|
2660
|
-
!words.length || (words.length === 1 && words[0] === "-") ?
|
|
2661
|
-
(await readStdin()).replace(/\r?\n$/, "")
|
|
2662
|
-
: words.join(" ");
|
|
2663
|
-
const mapped = transliterate(
|
|
2664
|
-
read.ctx,
|
|
2665
|
-
{ map: String(argv.map), lang: argv.lang, font: argv.font },
|
|
2666
|
-
text,
|
|
2667
|
-
);
|
|
2668
|
-
if ("problem" in mapped) {
|
|
2669
|
-
reportMapping(read.config, mapped);
|
|
2670
|
-
return;
|
|
2671
|
-
}
|
|
2672
|
-
console.log(
|
|
2673
|
-
argv.hex ?
|
|
2674
|
-
Array.from(
|
|
2675
|
-
mapped.text,
|
|
2676
|
-
(char) =>
|
|
2677
|
-
`U+${char.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")}`,
|
|
2678
|
-
).join(" ")
|
|
2679
|
-
: mapped.text,
|
|
2680
|
-
);
|
|
2681
|
-
} catch (err) {
|
|
2682
|
-
reportFailure(err);
|
|
2683
|
-
process.exitCode = 1;
|
|
2684
|
-
}
|
|
2685
|
-
},
|
|
2686
|
-
};
|
|
2687
|
-
}
|
|
2688
|
-
|
|
2689
|
-
/**
|
|
2690
|
-
* `package-build lexicon <language> [--map <map>]` — every word of a language's
|
|
2691
|
-
* lexicon with its native form under a map, as the build writes it; with
|
|
2626
|
+
* `package-build lexicon <language> [--enc <enc>]` — every word of a language's
|
|
2627
|
+
* lexicon with its native form under an encoding, as the build writes it; with
|
|
2692
2628
|
* `--derived`, the forms its rules derive follow, each naming its word in a
|
|
2693
|
-
* sixth column. Under a logographic
|
|
2629
|
+
* sixth column. Under a logographic enc the third column says where a
|
|
2694
2630
|
* word's writing comes from, `own` or `parts`, or marks it `none` or
|
|
2695
2631
|
* `ambiguous`.
|
|
2696
2632
|
*
|
|
@@ -2700,22 +2636,20 @@ function transliterateCommand() {
|
|
|
2700
2636
|
function lexiconCommand() {
|
|
2701
2637
|
return {
|
|
2702
2638
|
command: "lexicon <language>",
|
|
2703
|
-
describe: "List a language's words with their native forms under
|
|
2639
|
+
describe: "List a language's words with their native forms under an encoding",
|
|
2704
2640
|
builder: (yargs) =>
|
|
2705
2641
|
yargs
|
|
2706
2642
|
.positional("language", {
|
|
2707
2643
|
type: "string",
|
|
2708
2644
|
describe: "A declared language whose `note` names its language note",
|
|
2709
2645
|
})
|
|
2710
|
-
.option("
|
|
2646
|
+
.option("enc", {
|
|
2711
2647
|
type: "string",
|
|
2712
|
-
describe:
|
|
2713
|
-
"The `data.map.name` of a mapping note; the language's `data.writtenIn` map where left out",
|
|
2648
|
+
describe: "The encoding document Address, with doc as default type",
|
|
2714
2649
|
})
|
|
2715
2650
|
.option("font", {
|
|
2716
2651
|
type: "string",
|
|
2717
|
-
describe:
|
|
2718
|
-
"The face; the language's `data.writtenIn` face where the map is drawn in it, and required when neither it nor the map gives one",
|
|
2652
|
+
describe: "The encoding document's font; an explicit value must agree",
|
|
2719
2653
|
})
|
|
2720
2654
|
.option("derived", {
|
|
2721
2655
|
type: "boolean",
|
|
@@ -2730,19 +2664,19 @@ function lexiconCommand() {
|
|
|
2730
2664
|
}),
|
|
2731
2665
|
handler: (argv) => {
|
|
2732
2666
|
try {
|
|
2733
|
-
const read =
|
|
2667
|
+
const read = encodingContext();
|
|
2734
2668
|
if (!read) {
|
|
2735
2669
|
process.exitCode = 1;
|
|
2736
2670
|
return;
|
|
2737
2671
|
}
|
|
2738
2672
|
const listed = lexiconEntries(read.ctx, {
|
|
2739
2673
|
lang: String(argv.language),
|
|
2740
|
-
|
|
2674
|
+
enc: argv.enc === undefined ? undefined : String(argv.enc),
|
|
2741
2675
|
font: argv.font,
|
|
2742
2676
|
derived: Boolean(argv.derived),
|
|
2743
2677
|
});
|
|
2744
2678
|
if ("problem" in listed) {
|
|
2745
|
-
|
|
2679
|
+
reportEncoding(read.config, listed);
|
|
2746
2680
|
return;
|
|
2747
2681
|
}
|
|
2748
2682
|
if (argv.json) console.log(JSON.stringify(listed.entries, null, 2));
|
|
@@ -3339,7 +3273,7 @@ function readNpcSpec(argv) {
|
|
|
3339
3273
|
*
|
|
3340
3274
|
* @param {{file?: string, raw?: string}} note - Where the note is.
|
|
3341
3275
|
* @param {Array<string|number>} keyPath - The frontmatter key at fault, or the
|
|
3342
|
-
*
|
|
3276
|
+
* enc a missing key belongs in.
|
|
3343
3277
|
* @param {string} message - What is wrong.
|
|
3344
3278
|
* @returns {Error} The error, for `reportFailure` to print as a diagnostic.
|
|
3345
3279
|
*/
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
+
*
|
|
5
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
+
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
+
*/
|
|
13
|
+
/** Bare encoding commands: a published definition, UTF-8 input and exact output. */
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import { encodeText, decodeText } from "../engine/encoding.mjs";
|
|
16
|
+
import { encodingAddress } from "../engine/encoding-notes.mjs";
|
|
17
|
+
import { readStdin } from "../engine/read-stdin.mjs";
|
|
18
|
+
|
|
19
|
+
/** Apply one of the bare commands; all conversion is shared with the build. */
|
|
20
|
+
async function execute(direction, argv) {
|
|
21
|
+
const strings = argv.text ?? [];
|
|
22
|
+
if (
|
|
23
|
+
!argv.enc ||
|
|
24
|
+
(argv.in !== undefined && strings.length) ||
|
|
25
|
+
(argv.in === undefined && strings.length !== 1)
|
|
26
|
+
) {
|
|
27
|
+
console.error(
|
|
28
|
+
`package-build ${direction}: --enc is required; give one string or --in <file|->`,
|
|
29
|
+
);
|
|
30
|
+
process.exitCode = 2;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
let source = "<text>";
|
|
34
|
+
try {
|
|
35
|
+
let encoding;
|
|
36
|
+
if (argv.enc !== "default") {
|
|
37
|
+
const [{ loadPackConfig }, { indexRecordsFor }, { runContext }] = await Promise.all([
|
|
38
|
+
import("../engine/pack-config.mjs"),
|
|
39
|
+
import("../engine/content-index.mjs"),
|
|
40
|
+
import("../engine/content-scripts.mjs"),
|
|
41
|
+
]);
|
|
42
|
+
const config = loadPackConfig(),
|
|
43
|
+
problems = [];
|
|
44
|
+
const records = indexRecordsFor({ config, problems });
|
|
45
|
+
if (problems.length) throw new Error(problems.map((one) => one.message).join("\n"));
|
|
46
|
+
const context = runContext(config, records);
|
|
47
|
+
if (context.encodingFindings?.length)
|
|
48
|
+
throw Object.assign(
|
|
49
|
+
new Error(context.encodingFindings[0].message),
|
|
50
|
+
context.encodingFindings[0],
|
|
51
|
+
);
|
|
52
|
+
encoding =
|
|
53
|
+
context.encodings.get(String(argv.enc)) ??
|
|
54
|
+
context.encodings.get(encodingAddress(String(argv.enc), config.contentPackage));
|
|
55
|
+
if (!encoding) throw new Error(`Encoding document \`${argv.enc}\` does not resolve`);
|
|
56
|
+
}
|
|
57
|
+
source =
|
|
58
|
+
argv.in === "-" ? "<stdin>"
|
|
59
|
+
: argv.in === undefined ? "<text>"
|
|
60
|
+
: String(argv.in);
|
|
61
|
+
const input =
|
|
62
|
+
argv.in === undefined ? String(strings[0])
|
|
63
|
+
: argv.in === "-" ? await readStdin()
|
|
64
|
+
: fs.readFileSync(String(argv.in), "utf8");
|
|
65
|
+
let output;
|
|
66
|
+
try {
|
|
67
|
+
output =
|
|
68
|
+
!encoding ? input
|
|
69
|
+
: direction === "encode" ? encodeText(encoding, input)
|
|
70
|
+
: decodeText(encoding, input);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
error.file = source;
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
if (argv.out !== undefined) fs.writeFileSync(String(argv.out), output, "utf8");
|
|
76
|
+
else process.stdout.write(output + (argv.in === undefined ? "\n" : ""));
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const file = error.file ?? source;
|
|
79
|
+
const position = error.line !== undefined ? `:${error.line}:${error.column ?? 1}` : "";
|
|
80
|
+
console.error(`${file}${position}: error: ${error.message}`);
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Encode roman text through one explicitly selected encoding. */
|
|
86
|
+
function encodeCommand() {
|
|
87
|
+
return {
|
|
88
|
+
command: "encode [text..]",
|
|
89
|
+
describe: "Encode roman text using an encoding document",
|
|
90
|
+
builder: (yargs) =>
|
|
91
|
+
yargs
|
|
92
|
+
.positional("text", { type: "string", array: true, describe: "One input string" })
|
|
93
|
+
.option("enc", {
|
|
94
|
+
type: "string",
|
|
95
|
+
requiresArg: true,
|
|
96
|
+
describe: "Required encoding document Address, or default",
|
|
97
|
+
})
|
|
98
|
+
.option("in", {
|
|
99
|
+
alias: "i",
|
|
100
|
+
type: "string",
|
|
101
|
+
requiresArg: true,
|
|
102
|
+
describe: "Read UTF-8 input from a file or - for stdin",
|
|
103
|
+
})
|
|
104
|
+
.option("out", {
|
|
105
|
+
alias: "o",
|
|
106
|
+
type: "string",
|
|
107
|
+
requiresArg: true,
|
|
108
|
+
describe: "Write the exact result to a file",
|
|
109
|
+
}),
|
|
110
|
+
handler: (argv) => execute("encode", argv),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Decode code points through one explicitly selected encoding. */
|
|
115
|
+
function decodeCommand() {
|
|
116
|
+
return {
|
|
117
|
+
command: "decode [text..]",
|
|
118
|
+
describe: "Decode code points using an encoding document",
|
|
119
|
+
builder: (yargs) =>
|
|
120
|
+
yargs
|
|
121
|
+
.positional("text", { type: "string", array: true, describe: "One input string" })
|
|
122
|
+
.option("enc", {
|
|
123
|
+
type: "string",
|
|
124
|
+
requiresArg: true,
|
|
125
|
+
describe: "Required encoding document Address, or default",
|
|
126
|
+
})
|
|
127
|
+
.option("in", {
|
|
128
|
+
alias: "i",
|
|
129
|
+
type: "string",
|
|
130
|
+
requiresArg: true,
|
|
131
|
+
describe: "Read UTF-8 input from a file or - for stdin",
|
|
132
|
+
})
|
|
133
|
+
.option("out", {
|
|
134
|
+
alias: "o",
|
|
135
|
+
type: "string",
|
|
136
|
+
requiresArg: true,
|
|
137
|
+
describe: "Write the exact result to a file",
|
|
138
|
+
}),
|
|
139
|
+
handler: (argv) => execute("decode", argv),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Register the two bare conversion commands on the shared CLI. */
|
|
144
|
+
export function registerEncodingCommands(yargs) {
|
|
145
|
+
return yargs.command(encodeCommand()).command(decodeCommand());
|
|
146
|
+
}
|
package/bin/package-build.mjs
CHANGED
|
@@ -145,6 +145,7 @@ import {
|
|
|
145
145
|
INIT_KINDS,
|
|
146
146
|
INIT_LICENSES,
|
|
147
147
|
} from "../engine/project-init.mjs";
|
|
148
|
+
import { registerEncodingCommands } from "./encoding-commands.mjs";
|
|
148
149
|
import { registerContentCommands } from "./content-commands.mjs";
|
|
149
150
|
|
|
150
151
|
/**
|
|
@@ -711,6 +712,10 @@ function manifestCommand() {
|
|
|
711
712
|
// So a `packFolders` finding names the line it is about.
|
|
712
713
|
configFile: packConfigPath(),
|
|
713
714
|
});
|
|
715
|
+
if (config.converterData) {
|
|
716
|
+
const { writeConverterData } = await import("../engine/converter-data.mjs");
|
|
717
|
+
writeConverterData({ config: shared, packageBuild: config });
|
|
718
|
+
}
|
|
714
719
|
console.log(
|
|
715
720
|
`✅ Wrote ${path.relative(config.rootDir, written)} ` +
|
|
716
721
|
`(${Object.keys(manifest).length} keys, ` +
|
|
@@ -1746,30 +1751,32 @@ function bumpCommand() {
|
|
|
1746
1751
|
}
|
|
1747
1752
|
|
|
1748
1753
|
registerContentCommands(
|
|
1749
|
-
|
|
1750
|
-
.
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1754
|
+
registerEncodingCommands(
|
|
1755
|
+
yargs(hideBin(process.argv))
|
|
1756
|
+
.scriptName("package-build")
|
|
1757
|
+
.command(initCommand())
|
|
1758
|
+
.command(ciCommand())
|
|
1759
|
+
.command(cleanCommand())
|
|
1760
|
+
.command(assetsCommand())
|
|
1761
|
+
.command(stageCommand())
|
|
1762
|
+
.command(calendarsCommand())
|
|
1763
|
+
.command(datefromCommand())
|
|
1764
|
+
.command(datetoCommand())
|
|
1765
|
+
.command(manifestCommand())
|
|
1766
|
+
.command(siteRootCommand())
|
|
1767
|
+
.command(typesCommand())
|
|
1768
|
+
.command(schemaCommand())
|
|
1769
|
+
.command(langCommand())
|
|
1770
|
+
.command(bumpCommand())
|
|
1771
|
+
.command(yamlCommand())
|
|
1772
|
+
.command(changelogCommand())
|
|
1773
|
+
.command(bundleCommand())
|
|
1774
|
+
.command(releaseCommand())
|
|
1775
|
+
.command(deployCommand())
|
|
1776
|
+
.command(cdnCommand())
|
|
1777
|
+
.command(containerCommand())
|
|
1778
|
+
.command(e2eCommand()),
|
|
1779
|
+
),
|
|
1773
1780
|
)
|
|
1774
1781
|
.demandCommand(1, "Name a command.")
|
|
1775
1782
|
.strict()
|
|
@@ -1779,5 +1786,9 @@ registerContentCommands(
|
|
|
1779
1786
|
.epilog("Run package-build <command> --help for that command's actions and options.")
|
|
1780
1787
|
.fail((message, err, cli) => {
|
|
1781
1788
|
if (!err) cli.showHelp("error");
|
|
1789
|
+
if (["encode", "decode"].includes(process.argv[2])) {
|
|
1790
|
+
console.error(`package-build: ${err?.message ?? message}`);
|
|
1791
|
+
process.exit(2);
|
|
1792
|
+
}
|
|
1782
1793
|
die(err ?? message);
|
|
1783
1794
|
}).argv;
|
package/config.mjs
CHANGED
|
@@ -70,6 +70,7 @@ const SECTION_KEYS = [
|
|
|
70
70
|
"baseStyles",
|
|
71
71
|
"manifest",
|
|
72
72
|
"manifestFlags",
|
|
73
|
+
"converterData",
|
|
73
74
|
"schema",
|
|
74
75
|
"clean",
|
|
75
76
|
"lang",
|
|
@@ -338,6 +339,28 @@ function normalizeManifest(value) {
|
|
|
338
339
|
return Object.freeze(structuredClone(input));
|
|
339
340
|
}
|
|
340
341
|
|
|
342
|
+
/** The optional converter artifact is a JSON file beneath the package stage. */
|
|
343
|
+
function normalizeConverterData(value) {
|
|
344
|
+
if (value === undefined) return null;
|
|
345
|
+
const declared = requireNonEmptyString(value, "packageBuild.converterData");
|
|
346
|
+
const pieces = declared.split(/[\\/]/);
|
|
347
|
+
if (
|
|
348
|
+
path.posix.isAbsolute(declared) ||
|
|
349
|
+
path.win32.isAbsolute(declared) ||
|
|
350
|
+
pieces.includes("..") ||
|
|
351
|
+
pieces.includes("") ||
|
|
352
|
+
!declared.endsWith(".json") ||
|
|
353
|
+
declared.includes("\0") ||
|
|
354
|
+
["module.json", "system.json", "package.json"].includes(declared)
|
|
355
|
+
) {
|
|
356
|
+
fail(
|
|
357
|
+
"packageBuild.converterData",
|
|
358
|
+
"must name a JSON artifact within the build stage, such as data/converter.json",
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
return pieces.join("/");
|
|
362
|
+
}
|
|
363
|
+
|
|
341
364
|
/**
|
|
342
365
|
* Normalize a setting that is one glob or several.
|
|
343
366
|
*
|
|
@@ -565,6 +588,7 @@ function normalizeExceptions(value, field, where) {
|
|
|
565
588
|
* named first in the manifest's `styles`.
|
|
566
589
|
* @property {Readonly<Record<string, unknown>>} manifest The manifest
|
|
567
590
|
* specification, emitted as declared.
|
|
591
|
+
* @property {string|null} converterData Optional JSON path relative to the build stage.
|
|
568
592
|
* @property {string|null} manifestFlags Module to load a `flags` function
|
|
569
593
|
* from, for namespaced flags a repository has
|
|
570
594
|
* to compute. `null` when it declares none.
|
|
@@ -938,6 +962,7 @@ export function resolvePackageBuildConfig(shared) {
|
|
|
938
962
|
shared.rootDir,
|
|
939
963
|
),
|
|
940
964
|
manifest: normalizeManifest(section.manifest),
|
|
965
|
+
converterData: normalizeConverterData(section.converterData),
|
|
941
966
|
manifestFlags:
|
|
942
967
|
section.manifestFlags === undefined ?
|
|
943
968
|
null
|