@heroiclands/package-build 0.5.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 +86 -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 +12 -0
- package/package.json +34 -12
- 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 -0
- package/types/templates.d.mts +74 -0
package/types/e2e.d.mts
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash a password the way Foundry's `core/auth.mjs` does: pbkdf2, 1000 rounds,
|
|
3
|
+
* 64 bytes, sha512. A different shape logs nobody in.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} password - The plaintext password.
|
|
6
|
+
* @param {string} salt - The hex salt.
|
|
7
|
+
* @returns {string} The hex hash.
|
|
8
|
+
*/
|
|
9
|
+
export function hashPassword(password: string, salt: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* The disposable world's identity and credentials.
|
|
12
|
+
*
|
|
13
|
+
* @typedef {object} E2EWorld
|
|
14
|
+
* @property {string} worldId
|
|
15
|
+
* @property {string} worldTitle
|
|
16
|
+
* @property {string} worldDescription
|
|
17
|
+
* @property {string} gmId
|
|
18
|
+
* @property {string} gmName
|
|
19
|
+
* @property {string} gmPassword
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the seeded world from configuration and the environment.
|
|
23
|
+
*
|
|
24
|
+
* Everything derives from the package id unless the repository says otherwise,
|
|
25
|
+
* and the environment overrides under the repository's own variable prefix —
|
|
26
|
+
* the same prefix the deploy already uses — so a contributor can point a run at
|
|
27
|
+
* a scratch world without touching committed configuration.
|
|
28
|
+
*
|
|
29
|
+
* @param {object} config - The resolved package-build configuration.
|
|
30
|
+
* @param {NodeJS.ProcessEnv} [env] - Environment to read.
|
|
31
|
+
* @returns {E2EWorld} The resolved world.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveE2EWorld(config: object, env?: NodeJS.ProcessEnv): E2EWorld;
|
|
34
|
+
/**
|
|
35
|
+
* The `world.json` a seeded world carries.
|
|
36
|
+
*
|
|
37
|
+
* Foundry re-stamps `coreVersion`, `systemVersion` and `compatibility` from the
|
|
38
|
+
* running core on launch, so a generation-level range is enough here — the
|
|
39
|
+
* point is a world that launches without a migration prompt or a setup step.
|
|
40
|
+
*
|
|
41
|
+
* @param {object} opts
|
|
42
|
+
* @param {string} opts.worldId
|
|
43
|
+
* @param {string} opts.worldTitle
|
|
44
|
+
* @param {string} opts.worldDescription
|
|
45
|
+
* @param {string} opts.systemId - The system the world runs.
|
|
46
|
+
* @param {string} opts.systemVersion - That system's version.
|
|
47
|
+
* @param {string} opts.coreVersion - The Foundry generation.
|
|
48
|
+
* @returns {object} The world manifest.
|
|
49
|
+
*/
|
|
50
|
+
export function worldManifest({ worldId, worldTitle, worldDescription, systemId, systemVersion, coreVersion, }: {
|
|
51
|
+
worldId: string;
|
|
52
|
+
worldTitle: string;
|
|
53
|
+
worldDescription: string;
|
|
54
|
+
systemId: string;
|
|
55
|
+
systemVersion: string;
|
|
56
|
+
coreVersion: string;
|
|
57
|
+
}): object;
|
|
58
|
+
/**
|
|
59
|
+
* The seeded world's single Gamemaster.
|
|
60
|
+
*
|
|
61
|
+
* @param {object} opts
|
|
62
|
+
* @param {string} opts.id - The fixed document id.
|
|
63
|
+
* @param {string} opts.name - The user name.
|
|
64
|
+
* @param {string} opts.password - The plaintext password.
|
|
65
|
+
* @param {string} opts.salt - The hex salt to hash it with.
|
|
66
|
+
* @returns {object} The user document.
|
|
67
|
+
*/
|
|
68
|
+
export function gmDocument({ id, name, password, salt }: {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
password: string;
|
|
72
|
+
salt: string;
|
|
73
|
+
}): object;
|
|
74
|
+
/**
|
|
75
|
+
* The one pre-activated scene every seeded world carries.
|
|
76
|
+
*
|
|
77
|
+
* @returns {object} The scene document.
|
|
78
|
+
*/
|
|
79
|
+
export function defaultSceneDocument(): object;
|
|
80
|
+
/**
|
|
81
|
+
* The world setting that switches a module package on.
|
|
82
|
+
*
|
|
83
|
+
* A **system** is the world's own — `world.json` names it and Foundry loads it.
|
|
84
|
+
* A **module** is not: it has to be activated, and the switch is
|
|
85
|
+
* `core.moduleConfiguration`, a world setting holding a JSON map of package id
|
|
86
|
+
* to enabled. Without it a module repository would stand its suite up against a
|
|
87
|
+
* world that never loaded the very thing under test.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} packageId - The module to enable.
|
|
90
|
+
* @returns {object} The setting document.
|
|
91
|
+
*/
|
|
92
|
+
export function moduleConfigurationDocument(packageId: string): object;
|
|
93
|
+
/**
|
|
94
|
+
* Whether a `/join` response shows a world that is actually **active**.
|
|
95
|
+
*
|
|
96
|
+
* Foundry answers on the port long before a world is serving; a suite started
|
|
97
|
+
* at that moment fails every spec for no visible reason. The join screen
|
|
98
|
+
* renders the form only once a world is active, so that is what is waited for.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} body - The response body.
|
|
101
|
+
* @returns {boolean} Whether the world is active.
|
|
102
|
+
*/
|
|
103
|
+
export function isWorldActive(body: string): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* The build a sweep was asked to run against.
|
|
106
|
+
*
|
|
107
|
+
* There is no default, deliberately. The product of a sweep is a citable result
|
|
108
|
+
* — "the full suite passed on 14.367" — and a hard-coded "newest release" would
|
|
109
|
+
* rot on Foundry's next release day, quietly turning the sweep into a second
|
|
110
|
+
* pinned build. A bare major or a tag is refused for the same reason: the image
|
|
111
|
+
* passes it through verbatim, so the run would name no particular Foundry.
|
|
112
|
+
*
|
|
113
|
+
* @param {string[]} argv - Arguments after the action.
|
|
114
|
+
* @returns {string} The exact build, trimmed.
|
|
115
|
+
* @throws {Error} When none was given, or it is not an exact build.
|
|
116
|
+
*/
|
|
117
|
+
export function resolveSweepVersion(argv: string[]): string;
|
|
118
|
+
/**
|
|
119
|
+
* What a fast loop was asked to do.
|
|
120
|
+
*
|
|
121
|
+
* @typedef {object} FastArgs
|
|
122
|
+
* @property {string[]} targets Build targets, in declared order.
|
|
123
|
+
* @property {boolean} recreate Whether the container must be recreated.
|
|
124
|
+
* @property {boolean} runSuite Whether to run the suite at all.
|
|
125
|
+
* @property {string[]} suiteArgs Arguments handed to the suite verbatim.
|
|
126
|
+
*/
|
|
127
|
+
/**
|
|
128
|
+
* Parse the fast loop's arguments against the repository's build table.
|
|
129
|
+
*
|
|
130
|
+
* Build order is **declaration order**, not the order they were asked for: a
|
|
131
|
+
* bundler that empties the stage has to run before the passes that copy into
|
|
132
|
+
* it, and the repository already stated that by writing them down in order.
|
|
133
|
+
*
|
|
134
|
+
* @param {string[]} argv - Arguments after the action.
|
|
135
|
+
* @param {Record<string, {script: string, recreate: boolean}>} build - The
|
|
136
|
+
* declared build table.
|
|
137
|
+
* @returns {FastArgs} What to do.
|
|
138
|
+
* @throws {Error} On an unknown build target.
|
|
139
|
+
*/
|
|
140
|
+
export function parseFastArgs(argv: string[], build: Record<string, {
|
|
141
|
+
script: string;
|
|
142
|
+
recreate: boolean;
|
|
143
|
+
}>): FastArgs;
|
|
144
|
+
/**
|
|
145
|
+
* Seed the disposable world into the end-to-end stage's data root.
|
|
146
|
+
*
|
|
147
|
+
* Writes `world.json` and compiles each LevelDB collection from JSON, so the
|
|
148
|
+
* result is a genuine world Foundry launches without migration or setup. The
|
|
149
|
+
* world directory is wiped and rewritten each time, which is what makes a run
|
|
150
|
+
* repeatable.
|
|
151
|
+
*
|
|
152
|
+
* @param {object} opts
|
|
153
|
+
* @param {object} opts.config - The resolved package-build configuration.
|
|
154
|
+
* @param {object} opts.packageJson - The repository's `package.json`.
|
|
155
|
+
* @param {NodeJS.ProcessEnv} [opts.env] - Environment to read.
|
|
156
|
+
* @param {(message: string) => void} [opts.log] - Progress reporting.
|
|
157
|
+
* @returns {Promise<{worldDir: string, world: E2EWorld}>} Where it landed.
|
|
158
|
+
*/
|
|
159
|
+
export function seedTestWorld({ config, packageJson, env, log, }: {
|
|
160
|
+
config: object;
|
|
161
|
+
packageJson: object;
|
|
162
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
163
|
+
log?: ((message: string) => void) | undefined;
|
|
164
|
+
}): Promise<{
|
|
165
|
+
worldDir: string;
|
|
166
|
+
world: E2EWorld;
|
|
167
|
+
}>;
|
|
168
|
+
/**
|
|
169
|
+
* Poll until the world is active, or fail with a diagnosis.
|
|
170
|
+
*
|
|
171
|
+
* A licence failure never recovers, so it is detected from the container's own
|
|
172
|
+
* log and reported at once rather than after a three-minute timeout that says
|
|
173
|
+
* nothing about why.
|
|
174
|
+
*
|
|
175
|
+
* @param {object} opts
|
|
176
|
+
* @param {string} opts.url - The container's base URL.
|
|
177
|
+
* @param {string} opts.container - The container name, for its log.
|
|
178
|
+
* @param {string} opts.stage - The stage, named in a licence failure.
|
|
179
|
+
* @param {number} [opts.timeoutMs] - How long to wait.
|
|
180
|
+
* @param {(message: string) => void} [opts.log] - Progress reporting.
|
|
181
|
+
* @returns {Promise<void>} Resolves once the world is active.
|
|
182
|
+
* @throws {Error} On a licence failure or a timeout.
|
|
183
|
+
*/
|
|
184
|
+
export function waitForWorld({ url, container, stage, timeoutMs, log, }: {
|
|
185
|
+
url: string;
|
|
186
|
+
container: string;
|
|
187
|
+
stage: string;
|
|
188
|
+
timeoutMs?: number | undefined;
|
|
189
|
+
log?: ((message: string) => void) | undefined;
|
|
190
|
+
}): Promise<void>;
|
|
191
|
+
/**
|
|
192
|
+
* Run the repository's suite.
|
|
193
|
+
*
|
|
194
|
+
* `ELECTRON_RUN_AS_NODE` is stripped from the child environment. Editor
|
|
195
|
+
* terminals and most agent shells export it, and with it set an Electron-based
|
|
196
|
+
* runner launches as plain Node, rejects its own flags, and dies with a
|
|
197
|
+
* `MODULE_NOT_FOUND` naming nothing relevant.
|
|
198
|
+
*
|
|
199
|
+
* @param {object} opts
|
|
200
|
+
* @param {string[]} opts.command - The program and its arguments.
|
|
201
|
+
* @param {string[]} [opts.args] - Extra arguments, appended verbatim.
|
|
202
|
+
* @param {string} opts.cwd - The repository root.
|
|
203
|
+
* @param {NodeJS.ProcessEnv} [opts.env] - Environment for the child.
|
|
204
|
+
* @param {(message: string) => void} [opts.log] - Progress reporting.
|
|
205
|
+
* @returns {number} The suite's exit status.
|
|
206
|
+
*/
|
|
207
|
+
export function runSuite({ command, args, cwd, env, log, }: {
|
|
208
|
+
command: string[];
|
|
209
|
+
args?: string[] | undefined;
|
|
210
|
+
cwd: string;
|
|
211
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
212
|
+
log?: ((message: string) => void) | undefined;
|
|
213
|
+
}): number;
|
|
214
|
+
/**
|
|
215
|
+
* The suite command for a mode, or a clear failure when none is declared.
|
|
216
|
+
*
|
|
217
|
+
* @param {object} config - The resolved package-build configuration.
|
|
218
|
+
* @param {"run"|"open"} mode - Which command to take.
|
|
219
|
+
* @returns {string[]} The program and its arguments.
|
|
220
|
+
* @throws {Error} When the repository declares no such command.
|
|
221
|
+
*/
|
|
222
|
+
export function suiteCommand(config: object, mode: "run" | "open"): string[];
|
|
223
|
+
/**
|
|
224
|
+
* A full, from-scratch end-to-end run.
|
|
225
|
+
*
|
|
226
|
+
* Deploy the staged package, reseed the world, recreate the container onto it,
|
|
227
|
+
* wait for it to activate, run the suite, and tear the container down again —
|
|
228
|
+
* except in interactive mode, where it is left serving.
|
|
229
|
+
*
|
|
230
|
+
* This is the only path that may change Foundry build: the seeded world is
|
|
231
|
+
* stamped with the build that created it, and Foundry refuses to auto-launch a
|
|
232
|
+
* world stamped by another.
|
|
233
|
+
*
|
|
234
|
+
* @param {object} opts
|
|
235
|
+
* @param {object} opts.config - The resolved package-build configuration.
|
|
236
|
+
* @param {object} opts.packageJson - The repository's `package.json`.
|
|
237
|
+
* @param {"run"|"open"} [opts.mode] - Headless or interactive.
|
|
238
|
+
* @param {string[]} [opts.suiteArgs] - Extra arguments for the suite.
|
|
239
|
+
* @param {NodeJS.ProcessEnv} [opts.env] - Environment to read.
|
|
240
|
+
* @param {(message: string) => void} [opts.log] - Progress reporting.
|
|
241
|
+
* @returns {Promise<number>} The suite's exit status.
|
|
242
|
+
*/
|
|
243
|
+
export function e2eRun({ config, packageJson, mode, suiteArgs, env, log, }: {
|
|
244
|
+
config: object;
|
|
245
|
+
packageJson: object;
|
|
246
|
+
mode?: "open" | "run" | undefined;
|
|
247
|
+
suiteArgs?: string[] | undefined;
|
|
248
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
249
|
+
log?: ((message: string) => void) | undefined;
|
|
250
|
+
}): Promise<number>;
|
|
251
|
+
/**
|
|
252
|
+
* The iteration loop: rebuild what changed, redeploy, cycle, re-run.
|
|
253
|
+
*
|
|
254
|
+
* Each step has a quiet failure mode, and hand-rolling the sequence means
|
|
255
|
+
* meeting them one at a time. The bundler empties the stage, so build order is
|
|
256
|
+
* the declared one; the deploy is a destructive mirror, so it runs on a
|
|
257
|
+
* complete stage; a running Foundry holds its packs open, so the world is
|
|
258
|
+
* always cycled; the container answers on its port long before the world is
|
|
259
|
+
* serving, so the loop waits for the world.
|
|
260
|
+
*
|
|
261
|
+
* @param {object} opts
|
|
262
|
+
* @param {object} opts.config - The resolved package-build configuration.
|
|
263
|
+
* @param {string[]} [opts.argv] - Arguments after the action.
|
|
264
|
+
* @param {NodeJS.ProcessEnv} [opts.env] - Environment to read.
|
|
265
|
+
* @param {(message: string) => void} [opts.log] - Progress reporting.
|
|
266
|
+
* @returns {Promise<number>} The suite's exit status.
|
|
267
|
+
*/
|
|
268
|
+
export function e2eFast({ config, argv, env, log, }: {
|
|
269
|
+
config: object;
|
|
270
|
+
argv?: string[] | undefined;
|
|
271
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
272
|
+
log?: ((message: string) => void) | undefined;
|
|
273
|
+
}): Promise<number>;
|
|
274
|
+
/**
|
|
275
|
+
* The forward sweep: the full suite against a build the repository does not
|
|
276
|
+
* pin.
|
|
277
|
+
*
|
|
278
|
+
* Routine runs go against the pinned build, which is the manifest's
|
|
279
|
+
* `compatibility.minimum` — the claim the suite exists to defend. That leaves
|
|
280
|
+
* the other direction untested: a new Foundry release can break the package and
|
|
281
|
+
* nothing would notice until a user did.
|
|
282
|
+
*
|
|
283
|
+
* A green sweep is what licenses moving `compatibility.verified` to that build.
|
|
284
|
+
* A red one is the early warning it exists to produce.
|
|
285
|
+
*
|
|
286
|
+
* @param {object} opts
|
|
287
|
+
* @param {object} opts.config - The resolved package-build configuration.
|
|
288
|
+
* @param {object} opts.packageJson - The repository's `package.json`.
|
|
289
|
+
* @param {string[]} [opts.argv] - Arguments after the action.
|
|
290
|
+
* @param {NodeJS.ProcessEnv} [opts.env] - Environment to read.
|
|
291
|
+
* @param {(message: string) => void} [opts.log] - Progress reporting.
|
|
292
|
+
* @returns {Promise<number>} The suite's exit status.
|
|
293
|
+
*/
|
|
294
|
+
export function e2eSweep({ config, packageJson, argv, env, log, }: {
|
|
295
|
+
config: object;
|
|
296
|
+
packageJson: object;
|
|
297
|
+
argv?: string[] | undefined;
|
|
298
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
299
|
+
log?: ((message: string) => void) | undefined;
|
|
300
|
+
}): Promise<number>;
|
|
301
|
+
/**
|
|
302
|
+
* The Gamemaster's document id in every seeded world.
|
|
303
|
+
*
|
|
304
|
+
* Fixed, and deliberately not derived from the package: a spec has to know it
|
|
305
|
+
* without a handoff from the seed, and the world is disposable, so there is
|
|
306
|
+
* nothing for a per-package id to disambiguate. Sixteen alphanumeric
|
|
307
|
+
* characters, which is the shape Foundry document ids take.
|
|
308
|
+
*/
|
|
309
|
+
export const E2E_GM_ID: "heroiclandsE2EGM";
|
|
310
|
+
/**
|
|
311
|
+
* The seeded, pre-activated default scene's id.
|
|
312
|
+
*
|
|
313
|
+
* A non-empty world means Foundry's New User Experience does not auto-start the
|
|
314
|
+
* welcome tour, whose callout overlays sheets; an *active* scene at load means
|
|
315
|
+
* the canvas is ready, which several read paths depend on.
|
|
316
|
+
*/
|
|
317
|
+
export const E2E_SCENE_ID: "heroiclandsScene";
|
|
318
|
+
/** How the suite may be run. */
|
|
319
|
+
export const E2E_MODES: readonly string[];
|
|
320
|
+
/**
|
|
321
|
+
* The disposable world's identity and credentials.
|
|
322
|
+
*/
|
|
323
|
+
export type E2EWorld = {
|
|
324
|
+
worldId: string;
|
|
325
|
+
worldTitle: string;
|
|
326
|
+
worldDescription: string;
|
|
327
|
+
gmId: string;
|
|
328
|
+
gmName: string;
|
|
329
|
+
gmPassword: string;
|
|
330
|
+
};
|
|
331
|
+
/**
|
|
332
|
+
* What a fast loop was asked to do.
|
|
333
|
+
*/
|
|
334
|
+
export type FastArgs = {
|
|
335
|
+
/**
|
|
336
|
+
* Build targets, in declared order.
|
|
337
|
+
*/
|
|
338
|
+
targets: string[];
|
|
339
|
+
/**
|
|
340
|
+
* Whether the container must be recreated.
|
|
341
|
+
*/
|
|
342
|
+
recreate: boolean;
|
|
343
|
+
/**
|
|
344
|
+
* Whether to run the suite at all.
|
|
345
|
+
*/
|
|
346
|
+
runSuite: boolean;
|
|
347
|
+
/**
|
|
348
|
+
* Arguments handed to the suite verbatim.
|
|
349
|
+
*/
|
|
350
|
+
suiteArgs: string[];
|
|
351
|
+
};
|
package/types/index.d.mts
CHANGED
|
@@ -3,4 +3,8 @@ export * as bundle from "./bundle.mjs";
|
|
|
3
3
|
export * as stage from "./stage.mjs";
|
|
4
4
|
export * as release from "./release.mjs";
|
|
5
5
|
export * as deploy from "./deploy.mjs";
|
|
6
|
+
export * as container from "./container.mjs";
|
|
7
|
+
export * as e2e from "./e2e.mjs";
|
|
6
8
|
export * as lang from "./lang.mjs";
|
|
9
|
+
export * as coverage from "./coverage.mjs";
|
|
10
|
+
export * as templates from "./templates.mjs";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every user-visible literal a template leaves untranslated.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} source - The template source.
|
|
5
|
+
* @param {object} [options]
|
|
6
|
+
* @param {Iterable<string>} [options.allow] - Literals that are deliberately
|
|
7
|
+
* not localization keys — a code sample shown as a placeholder, say. This is
|
|
8
|
+
* the escape hatch, not the rule: anything that is ordinary UI prose belongs
|
|
9
|
+
* in the localization file, and a repository states each entry with the
|
|
10
|
+
* reason it cannot be one.
|
|
11
|
+
* @returns {TemplateFinding[]} The findings, in the order they appear.
|
|
12
|
+
*/
|
|
13
|
+
export function findHardcodedText(source: string, { allow }?: {
|
|
14
|
+
allow?: Iterable<string> | undefined;
|
|
15
|
+
}): TemplateFinding[];
|
|
16
|
+
/**
|
|
17
|
+
* Whether the template compiles at all.
|
|
18
|
+
*
|
|
19
|
+
* Precompiling rather than compiling: the question is whether Handlebars can
|
|
20
|
+
* *parse* the source, and precompilation answers it without needing any of the
|
|
21
|
+
* helpers the template calls to exist.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} source - The template source.
|
|
24
|
+
* @returns {TemplateFinding[]} One finding when it does not parse, else none.
|
|
25
|
+
*/
|
|
26
|
+
export function findTemplateSyntaxErrors(source: string): TemplateFinding[];
|
|
27
|
+
/**
|
|
28
|
+
* A single finding, in the fields the shared diagnostic format takes.
|
|
29
|
+
*
|
|
30
|
+
* `file` is absent for the same reason it is absent from a localization
|
|
31
|
+
* finding: these functions are handed source text, not a path.
|
|
32
|
+
*
|
|
33
|
+
* @typedef {object} TemplateFinding
|
|
34
|
+
* @property {number} [line] - 1-based line, omitted when it cannot be
|
|
35
|
+
* established honestly.
|
|
36
|
+
* @property {number} [column] - 1-based column, omitted likewise.
|
|
37
|
+
* @property {"error"|"warning"} severity - How the finding should be treated.
|
|
38
|
+
* @property {string} message - What is wrong, in one sentence.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Attributes whose value the user reads.
|
|
42
|
+
*
|
|
43
|
+
* Every one of these renders as prose somewhere — a tooltip, a placeholder, a
|
|
44
|
+
* screen reader's announcement — so English in one is as untranslated as
|
|
45
|
+
* English in a heading, and far easier to miss.
|
|
46
|
+
*
|
|
47
|
+
* @type {readonly string[]}
|
|
48
|
+
*/
|
|
49
|
+
export const VISIBLE_ATTRIBUTES: readonly string[];
|
|
50
|
+
/**
|
|
51
|
+
* A single finding, in the fields the shared diagnostic format takes.
|
|
52
|
+
*
|
|
53
|
+
* `file` is absent for the same reason it is absent from a localization
|
|
54
|
+
* finding: these functions are handed source text, not a path.
|
|
55
|
+
*/
|
|
56
|
+
export type TemplateFinding = {
|
|
57
|
+
/**
|
|
58
|
+
* - 1-based line, omitted when it cannot be
|
|
59
|
+
* established honestly.
|
|
60
|
+
*/
|
|
61
|
+
line?: number | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* - 1-based column, omitted likewise.
|
|
64
|
+
*/
|
|
65
|
+
column?: number | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* - How the finding should be treated.
|
|
68
|
+
*/
|
|
69
|
+
severity: "error" | "warning";
|
|
70
|
+
/**
|
|
71
|
+
* - What is wrong, in one sentence.
|
|
72
|
+
*/
|
|
73
|
+
message: string;
|
|
74
|
+
};
|