@heroiclands/package-build 0.4.0 → 0.6.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 +115 -0
- package/README.md +238 -2
- package/bin/package-build.mjs +482 -49
- package/bin/report.mjs +13 -4
- package/config.mjs +380 -1
- package/container.mjs +699 -0
- package/coverage.mjs +626 -0
- package/e2e.mjs +876 -0
- package/index.mjs +11 -2
- package/lang.mjs +2 -2
- package/package.json +34 -17
- package/templates.mjs +224 -0
- package/types/config.d.mts +129 -0
- package/types/container.d.mts +359 -0
- package/types/coverage.d.mts +182 -0
- package/types/e2e.d.mts +351 -0
- package/types/index.d.mts +4 -1
- package/types/templates.d.mts +74 -0
- package/text.mjs +0 -74
- package/types/text.d.mts +0 -51
package/index.mjs
CHANGED
|
@@ -54,8 +54,17 @@ export * as release from "./release.mjs";
|
|
|
54
54
|
/** Deploying a staged package into a Foundry data directory. */
|
|
55
55
|
export * as deploy from "./deploy.mjs";
|
|
56
56
|
|
|
57
|
+
/** Running a built package inside a Foundry VTT container. */
|
|
58
|
+
export * as container from "./container.mjs";
|
|
59
|
+
|
|
60
|
+
/** The end-to-end harness: a disposable world, served, with a suite driven at it. */
|
|
61
|
+
export * as e2e from "./e2e.mjs";
|
|
62
|
+
|
|
57
63
|
/** Localization files: what a shippable `lang/*.json` must satisfy. */
|
|
58
64
|
export * as lang from "./lang.mjs";
|
|
59
65
|
|
|
60
|
-
/**
|
|
61
|
-
export * as
|
|
66
|
+
/** Localization coverage: the keys a package references against the keys it declares. */
|
|
67
|
+
export * as coverage from "./coverage.mjs";
|
|
68
|
+
|
|
69
|
+
/** Templates: whether their user-visible text is localized, and whether they compile. */
|
|
70
|
+
export * as templates from "./templates.mjs";
|
package/lang.mjs
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
* @module
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
|
-
import {
|
|
48
|
+
import { positionOfLiteral } from "@heroiclands/content-build/engine/diagnostics";
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* A single finding, in the fields the shared diagnostic format takes.
|
|
@@ -140,7 +140,7 @@ export function validateLangSource(raw) {
|
|
|
140
140
|
* @param {string} key - The localization key.
|
|
141
141
|
* @returns {{line?: number, column?: number}} Spreadable position fields.
|
|
142
142
|
*/
|
|
143
|
-
const at = (key) =>
|
|
143
|
+
const at = (key) => positionOfLiteral(raw, `"${key}"`);
|
|
144
144
|
|
|
145
145
|
for (const [key, value] of Object.entries(json)) {
|
|
146
146
|
if (typeof value !== "string") continue;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./index.mjs",
|
|
@@ -15,10 +15,26 @@
|
|
|
15
15
|
"types": "./types/bundle.d.mts",
|
|
16
16
|
"import": "./bundle.mjs"
|
|
17
17
|
},
|
|
18
|
+
"./config": {
|
|
19
|
+
"types": "./types/config.d.mts",
|
|
20
|
+
"import": "./config.mjs"
|
|
21
|
+
},
|
|
22
|
+
"./container": {
|
|
23
|
+
"types": "./types/container.d.mts",
|
|
24
|
+
"import": "./container.mjs"
|
|
25
|
+
},
|
|
26
|
+
"./coverage": {
|
|
27
|
+
"types": "./types/coverage.d.mts",
|
|
28
|
+
"import": "./coverage.mjs"
|
|
29
|
+
},
|
|
18
30
|
"./deploy": {
|
|
19
31
|
"types": "./types/deploy.d.mts",
|
|
20
32
|
"import": "./deploy.mjs"
|
|
21
33
|
},
|
|
34
|
+
"./e2e": {
|
|
35
|
+
"types": "./types/e2e.d.mts",
|
|
36
|
+
"import": "./e2e.mjs"
|
|
37
|
+
},
|
|
22
38
|
"./lang": {
|
|
23
39
|
"types": "./types/lang.d.mts",
|
|
24
40
|
"import": "./lang.mjs"
|
|
@@ -27,6 +43,7 @@
|
|
|
27
43
|
"types": "./types/manifest.d.mts",
|
|
28
44
|
"import": "./manifest.mjs"
|
|
29
45
|
},
|
|
46
|
+
"./package.json": "./package.json",
|
|
30
47
|
"./release": {
|
|
31
48
|
"types": "./types/release.d.mts",
|
|
32
49
|
"import": "./release.mjs"
|
|
@@ -35,30 +52,28 @@
|
|
|
35
52
|
"types": "./types/stage.d.mts",
|
|
36
53
|
"import": "./stage.mjs"
|
|
37
54
|
},
|
|
38
|
-
"./
|
|
39
|
-
"types": "./types/
|
|
40
|
-
"import": "./
|
|
41
|
-
},
|
|
42
|
-
"./package.json": "./package.json",
|
|
43
|
-
"./config": {
|
|
44
|
-
"types": "./types/config.d.mts",
|
|
45
|
-
"import": "./config.mjs"
|
|
55
|
+
"./templates": {
|
|
56
|
+
"types": "./types/templates.d.mts",
|
|
57
|
+
"import": "./templates.mjs"
|
|
46
58
|
}
|
|
47
59
|
},
|
|
48
60
|
"files": [
|
|
61
|
+
"CHANGELOG.md",
|
|
62
|
+
"README.md",
|
|
63
|
+
"bin",
|
|
49
64
|
"bundle.mjs",
|
|
65
|
+
"config.mjs",
|
|
66
|
+
"container.mjs",
|
|
67
|
+
"coverage.mjs",
|
|
50
68
|
"deploy.mjs",
|
|
69
|
+
"e2e.mjs",
|
|
51
70
|
"index.mjs",
|
|
52
71
|
"lang.mjs",
|
|
53
72
|
"manifest.mjs",
|
|
54
73
|
"release.mjs",
|
|
55
74
|
"stage.mjs",
|
|
56
|
-
"
|
|
57
|
-
"types"
|
|
58
|
-
"CHANGELOG.md",
|
|
59
|
-
"README.md",
|
|
60
|
-
"bin",
|
|
61
|
-
"config.mjs"
|
|
75
|
+
"templates.mjs",
|
|
76
|
+
"types"
|
|
62
77
|
],
|
|
63
78
|
"scripts": {
|
|
64
79
|
"test": "vitest run",
|
|
@@ -72,19 +87,21 @@
|
|
|
72
87
|
"prepare": "git config core.hooksPath .githooks || true"
|
|
73
88
|
},
|
|
74
89
|
"dependencies": {
|
|
90
|
+
"@foundryvtt/foundryvtt-cli": "^3.0.4",
|
|
75
91
|
"@heroiclands/content-build": "^1.0.0",
|
|
76
92
|
"acorn": "^8.18.0",
|
|
77
93
|
"archiver": "^8.0.0",
|
|
78
94
|
"dotenv": "^17.2.3",
|
|
79
95
|
"glob": "^11.0.3",
|
|
96
|
+
"handlebars": "^4.7.9",
|
|
80
97
|
"ssh2-sftp-client": "^12.1.1",
|
|
98
|
+
"typescript": "^6.0.3",
|
|
81
99
|
"yargs": "^18.1.0"
|
|
82
100
|
},
|
|
83
101
|
"devDependencies": {
|
|
84
102
|
"@changesets/cli": "^3.0.0",
|
|
85
103
|
"@types/node": "^26.2.0",
|
|
86
104
|
"prettier": "^3.9.6",
|
|
87
|
-
"typescript": "^6.0.3",
|
|
88
105
|
"vitest": "^4.1.10"
|
|
89
106
|
},
|
|
90
107
|
"engines": {
|
package/templates.mjs
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
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
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Whether a template's user-visible text goes through localization at all.
|
|
16
|
+
*
|
|
17
|
+
* This is the **reverse** of {@link module:coverage}, and the two are
|
|
18
|
+
* deliberate opposites. Coverage walks *key → file*: it can tell you that every
|
|
19
|
+
* key the code names exists, and it is completely blind to a template that
|
|
20
|
+
* names no key whatsoever. This walks *text → key*: every user-visible literal
|
|
21
|
+
* in the markup must be a `{{localize}}` call rather than English sitting in
|
|
22
|
+
* the file.
|
|
23
|
+
*
|
|
24
|
+
* The Song of Heroic Lands repository is the argument for having both. Before
|
|
25
|
+
* the work that prompted this guard there were **516 hardcoded English literals
|
|
26
|
+
* across 61 templates**, and translating every key in `en.json` would have left
|
|
27
|
+
* every one of them in English — a fully "translated" package that renders half
|
|
28
|
+
* in the translator's language and half in the author's.
|
|
29
|
+
*
|
|
30
|
+
* It also **compiles** every template, because the usual way to break one while
|
|
31
|
+
* localizing it is to nest `{{localize …}}` inside another mustache. That is
|
|
32
|
+
* legal in an HTML attribute and a parse error inside a helper's hash, where a
|
|
33
|
+
* `(localize …)` subexpression is required — so the mistake ships from a
|
|
34
|
+
* template that looks exactly like its working neighbour.
|
|
35
|
+
*
|
|
36
|
+
* Both functions are pure: source text in, findings out. Discovery, I/O and
|
|
37
|
+
* reporting stay with the caller.
|
|
38
|
+
*
|
|
39
|
+
* @module
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import Handlebars from "handlebars";
|
|
43
|
+
import { positionOfLiteral } from "@heroiclands/content-build/engine/diagnostics";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A single finding, in the fields the shared diagnostic format takes.
|
|
47
|
+
*
|
|
48
|
+
* `file` is absent for the same reason it is absent from a localization
|
|
49
|
+
* finding: these functions are handed source text, not a path.
|
|
50
|
+
*
|
|
51
|
+
* @typedef {object} TemplateFinding
|
|
52
|
+
* @property {number} [line] - 1-based line, omitted when it cannot be
|
|
53
|
+
* established honestly.
|
|
54
|
+
* @property {number} [column] - 1-based column, omitted likewise.
|
|
55
|
+
* @property {"error"|"warning"} severity - How the finding should be treated.
|
|
56
|
+
* @property {string} message - What is wrong, in one sentence.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Attributes whose value the user reads.
|
|
61
|
+
*
|
|
62
|
+
* Every one of these renders as prose somewhere — a tooltip, a placeholder, a
|
|
63
|
+
* screen reader's announcement — so English in one is as untranslated as
|
|
64
|
+
* English in a heading, and far easier to miss.
|
|
65
|
+
*
|
|
66
|
+
* @type {readonly string[]}
|
|
67
|
+
*/
|
|
68
|
+
export const VISIBLE_ATTRIBUTES = Object.freeze([
|
|
69
|
+
"title",
|
|
70
|
+
"placeholder",
|
|
71
|
+
"aria-label",
|
|
72
|
+
"alt",
|
|
73
|
+
"data-tooltip",
|
|
74
|
+
"data-title",
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Remove everything that is not user-visible prose.
|
|
79
|
+
*
|
|
80
|
+
* Handlebars expressions go first (their contents are code), then `<style>` and
|
|
81
|
+
* `<script>` bodies (theirs are too). What is left is what a player reads.
|
|
82
|
+
*
|
|
83
|
+
* Substitutions are single spaces rather than removals so that nothing new is
|
|
84
|
+
* glued together — `>{{a}}<` must not become `><`, which would read as an empty
|
|
85
|
+
* text node rather than as no text node at all.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} source - The template source.
|
|
88
|
+
* @returns {string} The source with every non-prose region blanked.
|
|
89
|
+
*/
|
|
90
|
+
function stripNonProse(source) {
|
|
91
|
+
return source
|
|
92
|
+
.replace(/\{\{![\s\S]*?\}\}/g, " ")
|
|
93
|
+
.replace(/\{\{[^}]*\}\}/g, " ")
|
|
94
|
+
.replace(/<style[\s\S]*?<\/style>/g, " ")
|
|
95
|
+
.replace(/<script[\s\S]*?<\/script>/g, " ");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Whether a run of text is prose a player would expect in their own language.
|
|
100
|
+
*
|
|
101
|
+
* An HTML entity (`∞`, `·`) is a symbol, not prose — its letters
|
|
102
|
+
* are markup — so entities are dropped before looking for words. Two letters is
|
|
103
|
+
* the threshold: it admits "OK" and excludes every unit, separator and numeral.
|
|
104
|
+
*
|
|
105
|
+
* @param {string} text - The candidate, whitespace already normalized.
|
|
106
|
+
* @param {Set<string>} allowed - Literals the repository has justified.
|
|
107
|
+
* @returns {boolean} Whether it should have been localized.
|
|
108
|
+
*/
|
|
109
|
+
function isProse(text, allowed) {
|
|
110
|
+
return (
|
|
111
|
+
/[A-Za-z]{2}/.test(text.replace(/&[a-zA-Z]+;|&#\d+;/g, " ")) &&
|
|
112
|
+
!allowed.has(text)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Every user-visible literal a template leaves untranslated.
|
|
118
|
+
*
|
|
119
|
+
* @param {string} source - The template source.
|
|
120
|
+
* @param {object} [options]
|
|
121
|
+
* @param {Iterable<string>} [options.allow] - Literals that are deliberately
|
|
122
|
+
* not localization keys — a code sample shown as a placeholder, say. This is
|
|
123
|
+
* the escape hatch, not the rule: anything that is ordinary UI prose belongs
|
|
124
|
+
* in the localization file, and a repository states each entry with the
|
|
125
|
+
* reason it cannot be one.
|
|
126
|
+
* @returns {TemplateFinding[]} The findings, in the order they appear.
|
|
127
|
+
*/
|
|
128
|
+
export function findHardcodedText(source, { allow = [] } = {}) {
|
|
129
|
+
const allowed = new Set(allow);
|
|
130
|
+
const stripped = stripNonProse(source);
|
|
131
|
+
const found = [];
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Locate one literal in the **source**, not in the stripped text.
|
|
135
|
+
*
|
|
136
|
+
* The stripped copy has had substitutions of a different length, so a match
|
|
137
|
+
* index taken there cannot be carried across — the finding would name a
|
|
138
|
+
* position that drifts further from the truth the more Handlebars a file
|
|
139
|
+
* contains. Searching the source for the literal itself is exact, and
|
|
140
|
+
* counting occurrences keeps a literal that appears twice from reporting
|
|
141
|
+
* the same line twice.
|
|
142
|
+
*
|
|
143
|
+
* @param {string} needle - The literal exactly as the source spells it.
|
|
144
|
+
* @returns {{line?: number, column?: number}} Spreadable position fields.
|
|
145
|
+
*/
|
|
146
|
+
const seen = new Map();
|
|
147
|
+
const at = (needle) => {
|
|
148
|
+
const occurrence = (seen.get(needle) ?? 0) + 1;
|
|
149
|
+
seen.set(needle, occurrence);
|
|
150
|
+
return positionOfLiteral(source, needle, occurrence);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
for (const match of stripped.matchAll(/>([^<>]+)</g)) {
|
|
154
|
+
const text = match[1].replace(/\s+/g, " ").trim();
|
|
155
|
+
if (!isProse(text, allowed)) continue;
|
|
156
|
+
found.push({
|
|
157
|
+
...at(match[1]),
|
|
158
|
+
severity: "error",
|
|
159
|
+
message: `hardcoded user-visible string: ${text}`,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (const attr of VISIBLE_ATTRIBUTES) {
|
|
164
|
+
// `\b` alone would match `title` inside `data-title`, reporting one
|
|
165
|
+
// literal twice under two names; a hyphen is a word boundary to a
|
|
166
|
+
// regular expression and part of the attribute name to HTML.
|
|
167
|
+
const pattern = new RegExp(`(?<![\\w-])${attr}="([^"]*)"`, "g");
|
|
168
|
+
for (const match of stripped.matchAll(pattern)) {
|
|
169
|
+
const text = match[1].replace(/\s+/g, " ").trim();
|
|
170
|
+
if (!isProse(text, allowed)) continue;
|
|
171
|
+
found.push({
|
|
172
|
+
...at(match[0]),
|
|
173
|
+
severity: "error",
|
|
174
|
+
message: `hardcoded user-visible string: ${attr}="${text}"`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return found;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Whether the template compiles at all.
|
|
184
|
+
*
|
|
185
|
+
* Precompiling rather than compiling: the question is whether Handlebars can
|
|
186
|
+
* *parse* the source, and precompilation answers it without needing any of the
|
|
187
|
+
* helpers the template calls to exist.
|
|
188
|
+
*
|
|
189
|
+
* @param {string} source - The template source.
|
|
190
|
+
* @returns {TemplateFinding[]} One finding when it does not parse, else none.
|
|
191
|
+
*/
|
|
192
|
+
export function findTemplateSyntaxErrors(source) {
|
|
193
|
+
try {
|
|
194
|
+
Handlebars.precompile(source);
|
|
195
|
+
return [];
|
|
196
|
+
} catch (err) {
|
|
197
|
+
// Handlebars states a position three different ways depending on which
|
|
198
|
+
// stage rejected the template, and populates `hash.loc` for almost
|
|
199
|
+
// none of them — so the line printed in its own message is the one that
|
|
200
|
+
// is usually there. Reading it back is not guesswork: it is Handlebars'
|
|
201
|
+
// answer, in the only place this error carries it.
|
|
202
|
+
const loc = err?.hash?.loc;
|
|
203
|
+
const stated = /Parse error on line (\d+)/.exec(String(err?.message));
|
|
204
|
+
const line =
|
|
205
|
+
loc?.first_line ??
|
|
206
|
+
err?.lineNumber ??
|
|
207
|
+
(stated ? Number(stated[1]) : undefined);
|
|
208
|
+
return [
|
|
209
|
+
{
|
|
210
|
+
...(typeof line === "number" ? { line } : {}),
|
|
211
|
+
...((
|
|
212
|
+
typeof line === "number" &&
|
|
213
|
+
typeof loc?.first_column === "number"
|
|
214
|
+
) ?
|
|
215
|
+
{ column: loc.first_column + 1 }
|
|
216
|
+
: {}),
|
|
217
|
+
severity: "error",
|
|
218
|
+
message: `template does not compile: ${
|
|
219
|
+
String(err).split("\n")[0]
|
|
220
|
+
}`,
|
|
221
|
+
},
|
|
222
|
+
];
|
|
223
|
+
}
|
|
224
|
+
}
|
package/types/config.d.mts
CHANGED
|
@@ -21,9 +21,43 @@
|
|
|
21
21
|
* conventional build artifacts.
|
|
22
22
|
* @property {string} langSources Glob for the localization files to check.
|
|
23
23
|
* @property {string|null} langHelp Extra guidance printed after a failure.
|
|
24
|
+
* @property {string} langPrimary The localization file coverage is measured
|
|
25
|
+
* against — the one the package authors.
|
|
26
|
+
* @property {readonly string[]} langScripts Globs for the sources scanned
|
|
27
|
+
* for key references.
|
|
28
|
+
* @property {readonly string[]} langTemplates Globs for the templates scanned
|
|
29
|
+
* for references and for hardcoded text.
|
|
30
|
+
* @property {readonly string[]|null} langKeyRoots The key roots, when the
|
|
31
|
+
* package references one its file does not
|
|
32
|
+
* yet declare. `null` derives them.
|
|
33
|
+
* @property {string|null} langReferences Module to load a `references`
|
|
34
|
+
* function from, contributing the keys only
|
|
35
|
+
* this repository's conventions can find.
|
|
36
|
+
* @property {readonly string[]} langRetained Key prefixes exempt from the
|
|
37
|
+
* unreferenced advisory.
|
|
38
|
+
* @property {readonly string[]} langAllow Template literals that are
|
|
39
|
+
* deliberately not localization keys.
|
|
24
40
|
* @property {string} envPrefix Prefix of the deploy environment variables.
|
|
25
41
|
* @property {string} bundleEntry The bundle file Foundry loads, as the
|
|
26
42
|
* manifest spells it. Derived from the package id.
|
|
43
|
+
* @property {string|null} compatibilityMinimum The Foundry floor the package
|
|
44
|
+
* claims, read from the shared configuration's top level.
|
|
45
|
+
* @property {string} systemId The system a world runs — the package
|
|
46
|
+
* itself for a system, its target for a module.
|
|
47
|
+
* @property {string|null} systemVersion That system's version, when the shared
|
|
48
|
+
* configuration stamps one.
|
|
49
|
+
* @property {string|null} containerImage Image override for every stage.
|
|
50
|
+
* @property {Readonly<Record<string, Readonly<{port: number|null, world: string|null, version: string|null}>>>} containerStages
|
|
51
|
+
* Container stages beyond the conventional four.
|
|
52
|
+
* @property {string} e2eStage Which stage the suite runs against.
|
|
53
|
+
* @property {Readonly<{run: readonly string[], open: readonly string[]|null}>|null} e2eSuite
|
|
54
|
+
* What to run against the served world; `null` when the repository has none.
|
|
55
|
+
* @property {Readonly<Record<string, Readonly<{script: string, recreate: boolean}>>>} e2eBuild
|
|
56
|
+
* Build targets the fast loop can produce, in declaration order.
|
|
57
|
+
* @property {Readonly<Record<string, string>>} e2eWorld Declared world identity.
|
|
58
|
+
* @property {Readonly<Record<string, string>>} e2eGm Declared GM credentials.
|
|
59
|
+
* @property {Readonly<Record<string, string>>} e2eDocuments Extra world
|
|
60
|
+
* collections, as collection → source directory.
|
|
27
61
|
*/
|
|
28
62
|
/**
|
|
29
63
|
* Resolve a package-build configuration from an already-loaded shared one.
|
|
@@ -133,6 +167,43 @@ export type PackageBuildConfig = {
|
|
|
133
167
|
* Extra guidance printed after a failure.
|
|
134
168
|
*/
|
|
135
169
|
langHelp: string | null;
|
|
170
|
+
/**
|
|
171
|
+
* The localization file coverage is measured
|
|
172
|
+
* against — the one the package authors.
|
|
173
|
+
*/
|
|
174
|
+
langPrimary: string;
|
|
175
|
+
/**
|
|
176
|
+
* Globs for the sources scanned
|
|
177
|
+
* for key references.
|
|
178
|
+
*/
|
|
179
|
+
langScripts: readonly string[];
|
|
180
|
+
/**
|
|
181
|
+
* Globs for the templates scanned
|
|
182
|
+
* for references and for hardcoded text.
|
|
183
|
+
*/
|
|
184
|
+
langTemplates: readonly string[];
|
|
185
|
+
/**
|
|
186
|
+
* The key roots, when the
|
|
187
|
+
* package references one its file does not
|
|
188
|
+
* yet declare. `null` derives them.
|
|
189
|
+
*/
|
|
190
|
+
langKeyRoots: readonly string[] | null;
|
|
191
|
+
/**
|
|
192
|
+
* Module to load a `references`
|
|
193
|
+
* function from, contributing the keys only
|
|
194
|
+
* this repository's conventions can find.
|
|
195
|
+
*/
|
|
196
|
+
langReferences: string | null;
|
|
197
|
+
/**
|
|
198
|
+
* Key prefixes exempt from the
|
|
199
|
+
* unreferenced advisory.
|
|
200
|
+
*/
|
|
201
|
+
langRetained: readonly string[];
|
|
202
|
+
/**
|
|
203
|
+
* Template literals that are
|
|
204
|
+
* deliberately not localization keys.
|
|
205
|
+
*/
|
|
206
|
+
langAllow: readonly string[];
|
|
136
207
|
/**
|
|
137
208
|
* Prefix of the deploy environment variables.
|
|
138
209
|
*/
|
|
@@ -142,4 +213,62 @@ export type PackageBuildConfig = {
|
|
|
142
213
|
* manifest spells it. Derived from the package id.
|
|
143
214
|
*/
|
|
144
215
|
bundleEntry: string;
|
|
216
|
+
/**
|
|
217
|
+
* The Foundry floor the package
|
|
218
|
+
* claims, read from the shared configuration's top level.
|
|
219
|
+
*/
|
|
220
|
+
compatibilityMinimum: string | null;
|
|
221
|
+
/**
|
|
222
|
+
* The system a world runs — the package
|
|
223
|
+
* itself for a system, its target for a module.
|
|
224
|
+
*/
|
|
225
|
+
systemId: string;
|
|
226
|
+
/**
|
|
227
|
+
* That system's version, when the shared
|
|
228
|
+
* configuration stamps one.
|
|
229
|
+
*/
|
|
230
|
+
systemVersion: string | null;
|
|
231
|
+
/**
|
|
232
|
+
* Image override for every stage.
|
|
233
|
+
*/
|
|
234
|
+
containerImage: string | null;
|
|
235
|
+
/**
|
|
236
|
+
* Container stages beyond the conventional four.
|
|
237
|
+
*/
|
|
238
|
+
containerStages: Readonly<Record<string, Readonly<{
|
|
239
|
+
port: number | null;
|
|
240
|
+
world: string | null;
|
|
241
|
+
version: string | null;
|
|
242
|
+
}>>>;
|
|
243
|
+
/**
|
|
244
|
+
* Which stage the suite runs against.
|
|
245
|
+
*/
|
|
246
|
+
e2eStage: string;
|
|
247
|
+
/**
|
|
248
|
+
* What to run against the served world; `null` when the repository has none.
|
|
249
|
+
*/
|
|
250
|
+
e2eSuite: Readonly<{
|
|
251
|
+
run: readonly string[];
|
|
252
|
+
open: readonly string[] | null;
|
|
253
|
+
}> | null;
|
|
254
|
+
/**
|
|
255
|
+
* Build targets the fast loop can produce, in declaration order.
|
|
256
|
+
*/
|
|
257
|
+
e2eBuild: Readonly<Record<string, Readonly<{
|
|
258
|
+
script: string;
|
|
259
|
+
recreate: boolean;
|
|
260
|
+
}>>>;
|
|
261
|
+
/**
|
|
262
|
+
* Declared world identity.
|
|
263
|
+
*/
|
|
264
|
+
e2eWorld: Readonly<Record<string, string>>;
|
|
265
|
+
/**
|
|
266
|
+
* Declared GM credentials.
|
|
267
|
+
*/
|
|
268
|
+
e2eGm: Readonly<Record<string, string>>;
|
|
269
|
+
/**
|
|
270
|
+
* Extra world
|
|
271
|
+
* collections, as collection → source directory.
|
|
272
|
+
*/
|
|
273
|
+
e2eDocuments: Readonly<Record<string, string>>;
|
|
145
274
|
};
|