@loomcli/core 0.6.0 → 0.8.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/NOTICE +34 -0
- package/dist/application.d.ts +11 -6
- package/dist/application.js +57 -19
- package/dist/capture.d.ts +65 -0
- package/dist/capture.js +99 -0
- package/dist/chain.d.ts +28 -17
- package/dist/chain.js +11 -10
- package/dist/command-rules.d.ts +1 -1
- package/dist/command-rules.js +2 -2
- package/dist/command.d.ts +31 -47
- package/dist/command.js +204 -209
- package/dist/errors.d.ts +22 -21
- package/dist/errors.js +38 -18
- package/dist/facts.d.ts +5 -0
- package/dist/facts.js +8 -1
- package/dist/globals.d.ts +48 -22
- package/dist/globals.js +72 -29
- package/dist/glyphs.generated.js +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -1
- package/dist/input-rules.d.ts +20 -2
- package/dist/input-rules.js +47 -5
- package/dist/inspect.d.ts +33 -17
- package/dist/inspect.js +74 -87
- package/dist/locate.js +52 -60
- package/dist/options.d.ts +101 -83
- package/dist/options.js +311 -267
- package/dist/parse.d.ts +162 -0
- package/dist/parse.js +601 -0
- package/dist/plain.d.ts +52 -2
- package/dist/plain.js +228 -2
- package/dist/plugin-rules.d.ts +8 -4
- package/dist/plugin-rules.js +13 -9
- package/dist/plugin-settings.d.ts +18 -0
- package/dist/plugin-settings.js +38 -0
- package/dist/plugin.d.ts +32 -27
- package/dist/plugin.js +107 -89
- package/dist/sources.d.ts +18 -9
- package/dist/sources.js +50 -20
- package/dist/style-layout.js +2 -2
- package/dist/style-width.d.ts +13 -0
- package/dist/style-width.js +170 -0
- package/dist/types.d.ts +70 -30
- package/dist/unicode.generated.d.ts +27 -0
- package/dist/unicode.generated.js +1036 -0
- package/dist/validation.d.ts +108 -34
- package/dist/validation.js +282 -125
- package/dist/view.d.ts +16 -11
- package/dist/view.js +13 -4
- package/licenses/unicode-LICENSE.txt +41 -0
- package/licenses/uucode-LICENSE.md +35 -0
- package/package.json +9 -5
package/dist/errors.d.ts
CHANGED
|
@@ -10,20 +10,6 @@ import type { InputIdentity } from './types.js';
|
|
|
10
10
|
* keeps the raw value.
|
|
11
11
|
*/
|
|
12
12
|
export declare function quoted(value: unknown): string;
|
|
13
|
-
/**
|
|
14
|
-
* The two short-group faults. A value option that is not last in its group names that option's
|
|
15
|
-
* spelling; a group that mixes scopes names only the two letters that disagree, because the rest
|
|
16
|
-
* of the group may hold an inline value. `token` keeps the whole group as the reported fact.
|
|
17
|
-
*/
|
|
18
|
-
type ShortGroupFault = {
|
|
19
|
-
reason: 'value-position';
|
|
20
|
-
token: string;
|
|
21
|
-
} | {
|
|
22
|
-
reason: 'mixed-scope';
|
|
23
|
-
token: string;
|
|
24
|
-
global: string;
|
|
25
|
-
other: string;
|
|
26
|
-
};
|
|
27
13
|
/**
|
|
28
14
|
* The one message a distributed build shows an operator for a defect or a declaration fault: the
|
|
29
15
|
* application name and a fixed phrase, with no reason, class name, code, or path.
|
|
@@ -123,24 +109,39 @@ export declare class UnknownOptionError extends UsageError {
|
|
|
123
109
|
readonly spelling: string;
|
|
124
110
|
constructor(spelling: string);
|
|
125
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Where a missing value should have been: the words ran out, or the next word was an option word or
|
|
114
|
+
* the bare `--`, which is never a separate value, so the sentence names the attached form too.
|
|
115
|
+
*/
|
|
116
|
+
type MissingValueForm = 'attached' | 'separate';
|
|
126
117
|
export declare class MissingValueError extends UsageError {
|
|
127
118
|
readonly spelling: string;
|
|
128
|
-
constructor(spelling: string);
|
|
119
|
+
constructor(spelling: string, form?: MissingValueForm);
|
|
129
120
|
}
|
|
130
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* A Boolean or counted spelling takes no value, so the token carried one the declaration cannot
|
|
123
|
+
* accept. A counted option's sentence tells the operator to repeat the spelling instead, and `kind`
|
|
124
|
+
* chooses the sentence alone.
|
|
125
|
+
*/
|
|
131
126
|
export declare class UnexpectedValueError extends UsageError {
|
|
132
127
|
readonly spelling: string;
|
|
133
128
|
readonly value: string;
|
|
134
|
-
constructor(spelling: string, value: string);
|
|
129
|
+
constructor(spelling: string, value: string, kind?: 'boolean' | 'count');
|
|
135
130
|
}
|
|
136
131
|
export declare class RepeatedOptionError extends UsageError {
|
|
137
132
|
readonly spelling: string;
|
|
138
133
|
constructor(spelling: string);
|
|
139
134
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
135
|
+
/**
|
|
136
|
+
* An option word the Command routing reached does not declare, while a visible Command below it
|
|
137
|
+
* does, such as a Command's own option typed before its name, or a parent's own option the routed
|
|
138
|
+
* Command declares with another value class. `commands` holds each such Command's path from the
|
|
139
|
+
* root, in authoring order.
|
|
140
|
+
*/
|
|
141
|
+
export declare class MisplacedOptionError extends UsageError {
|
|
142
|
+
readonly spelling: string;
|
|
143
|
+
readonly commands: readonly (readonly string[])[];
|
|
144
|
+
constructor(spelling: string, commands: readonly (readonly string[])[]);
|
|
144
145
|
}
|
|
145
146
|
/**
|
|
146
147
|
* Exit 1: the declaration is wrong, so the author reads its Developer Diagnostic. A rule and the
|
package/dist/errors.js
CHANGED
|
@@ -50,10 +50,16 @@ function nonCallableMessage(command, candidates) {
|
|
|
50
50
|
? `A command is required.${offering(candidates, 'command')}`
|
|
51
51
|
: `${routedSentence(command)} requires a subcommand.${offering(candidates, 'subcommand')}`;
|
|
52
52
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
/**
|
|
54
|
+
* The sentence for an option word the routed Command's table does not hold, while visible Commands
|
|
55
|
+
* below it declare it. Each Command reads as its path from the root.
|
|
56
|
+
*/
|
|
57
|
+
function misplacedMessage(spelling, commands) {
|
|
58
|
+
const names = commands.map((path) => path.join(' '));
|
|
59
|
+
const [only] = names;
|
|
60
|
+
return names.length === 1 && only !== undefined
|
|
61
|
+
? `Option ${quoted(spelling)} belongs to command ${quoted(only)}. Supply it after ${quoted(only)}.`
|
|
62
|
+
: `Option ${quoted(spelling)} belongs to commands ${names.join(', ')}. Supply it after the command name.`;
|
|
57
63
|
}
|
|
58
64
|
/**
|
|
59
65
|
* The sentence for a class whose declared code no failure may exit with. The class is named by its
|
|
@@ -248,18 +254,26 @@ export class UnknownOptionError extends UsageError {
|
|
|
248
254
|
}
|
|
249
255
|
export class MissingValueError extends UsageError {
|
|
250
256
|
spelling;
|
|
251
|
-
constructor(spelling) {
|
|
252
|
-
super(
|
|
257
|
+
constructor(spelling, form = 'separate') {
|
|
258
|
+
super(form === 'attached'
|
|
259
|
+
? `Option ${quoted(spelling)} requires a value. Supply a value after ${quoted(spelling)}, or attach one that starts with a hyphen as ${quoted(`${spelling}=<value>`)}.`
|
|
260
|
+
: `Option ${quoted(spelling)} requires a value. Supply a value after ${quoted(spelling)}.`);
|
|
253
261
|
this.name = 'MissingValueError';
|
|
254
262
|
this.spelling = spelling;
|
|
255
263
|
}
|
|
256
264
|
}
|
|
257
|
-
/**
|
|
265
|
+
/**
|
|
266
|
+
* A Boolean or counted spelling takes no value, so the token carried one the declaration cannot
|
|
267
|
+
* accept. A counted option's sentence tells the operator to repeat the spelling instead, and `kind`
|
|
268
|
+
* chooses the sentence alone.
|
|
269
|
+
*/
|
|
258
270
|
export class UnexpectedValueError extends UsageError {
|
|
259
271
|
spelling;
|
|
260
272
|
value;
|
|
261
|
-
constructor(spelling, value) {
|
|
262
|
-
super(
|
|
273
|
+
constructor(spelling, value, kind = 'boolean') {
|
|
274
|
+
super(kind === 'count'
|
|
275
|
+
? `Counted option ${quoted(spelling)} does not accept a value. Repeat ${quoted(spelling)} to raise its count.`
|
|
276
|
+
: `Boolean option ${quoted(spelling)} does not accept a value. Supply the flag alone.`);
|
|
263
277
|
this.name = 'UnexpectedValueError';
|
|
264
278
|
this.spelling = spelling;
|
|
265
279
|
this.value = value;
|
|
@@ -273,14 +287,20 @@ export class RepeatedOptionError extends UsageError {
|
|
|
273
287
|
this.spelling = spelling;
|
|
274
288
|
}
|
|
275
289
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
290
|
+
/**
|
|
291
|
+
* An option word the Command routing reached does not declare, while a visible Command below it
|
|
292
|
+
* does, such as a Command's own option typed before its name, or a parent's own option the routed
|
|
293
|
+
* Command declares with another value class. `commands` holds each such Command's path from the
|
|
294
|
+
* root, in authoring order.
|
|
295
|
+
*/
|
|
296
|
+
export class MisplacedOptionError extends UsageError {
|
|
297
|
+
spelling;
|
|
298
|
+
commands;
|
|
299
|
+
constructor(spelling, commands) {
|
|
300
|
+
super(misplacedMessage(spelling, commands));
|
|
301
|
+
this.commands = commands;
|
|
302
|
+
this.name = 'MisplacedOptionError';
|
|
303
|
+
this.spelling = spelling;
|
|
284
304
|
}
|
|
285
305
|
}
|
|
286
306
|
/** The platform's error options one value carries, read without trusting its shape. */
|
|
@@ -498,7 +518,7 @@ for (const Class of [
|
|
|
498
518
|
MissingValueError,
|
|
499
519
|
UnexpectedValueError,
|
|
500
520
|
RepeatedOptionError,
|
|
501
|
-
|
|
521
|
+
MisplacedOptionError,
|
|
502
522
|
DeclarationError,
|
|
503
523
|
FatalError,
|
|
504
524
|
InternalError,
|
package/dist/facts.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export interface FactSite {
|
|
|
9
9
|
readonly declaration: Omit<Finding, 'mark' | 'note'>;
|
|
10
10
|
readonly at: string;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* The note a finding carries for what a plugin declared on the author's behalf, such as an input
|
|
14
|
+
* its hook declares or the settings its factory takes, so the diagnostic names the declarer.
|
|
15
|
+
*/
|
|
16
|
+
export declare function declarerNote(identity: string): string;
|
|
12
17
|
/** The finding for the call one site holds, marking one part of it, with a note when given. */
|
|
13
18
|
export declare function siteFinding(site: FactSite, mark: string, note?: string): Finding;
|
|
14
19
|
/**
|
package/dist/facts.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { misplacedListingFact, notOneLine } from './command-rules.js';
|
|
2
|
-
import { DeclarationError } from './errors.js';
|
|
2
|
+
import { DeclarationError, quoted } from './errors.js';
|
|
3
3
|
import { flagNotBoolean } from './input-rules.js';
|
|
4
4
|
/**
|
|
5
5
|
* One character outside Unicode `White_Space`, so a fact holds prose and not only spacing.
|
|
@@ -12,6 +12,13 @@ const prose = /\P{White_Space}/u;
|
|
|
12
12
|
* The seven are LF, VT, FF, CR, NEL, LS, and PS, each of them `White_Space` too.
|
|
13
13
|
*/
|
|
14
14
|
const lineTerminator = /[\n\v\f\r\u0085\u2028\u2029]/u;
|
|
15
|
+
/**
|
|
16
|
+
* The note a finding carries for what a plugin declared on the author's behalf, such as an input
|
|
17
|
+
* its hook declares or the settings its factory takes, so the diagnostic names the declarer.
|
|
18
|
+
*/
|
|
19
|
+
export function declarerNote(identity) {
|
|
20
|
+
return `declared by plugin ${quoted(identity)}`;
|
|
21
|
+
}
|
|
15
22
|
/** The finding for the call one site holds, marking one part of it, with a note when given. */
|
|
16
23
|
export function siteFinding(site, mark, note) {
|
|
17
24
|
const finding = { ...site.declaration, mark };
|
package/dist/globals.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { BoundOption } from './bindings.js';
|
|
2
2
|
import type { DescriptorRegistry } from './extension.js';
|
|
3
|
-
import type { InputSite } from './facts.js';
|
|
3
|
+
import type { FactSite, InputSite } from './facts.js';
|
|
4
4
|
import { compileOptions } from './options.js';
|
|
5
|
-
import type { CompileScope } from './options.js';
|
|
5
|
+
import type { CompileScope, SpellingTable } from './options.js';
|
|
6
6
|
import type { BuiltPlugin } from './plugin.js';
|
|
7
|
-
import type { OptionConfig
|
|
7
|
+
import type { OptionConfig } from './types.js';
|
|
8
8
|
import type { InputDeclaration, OptionInput, ValidatedInputs } from './validation.js';
|
|
9
9
|
/**
|
|
10
10
|
* Who declared one option that shares the globals table, or the Command that declares a local
|
|
@@ -27,10 +27,10 @@ interface TableEntry {
|
|
|
27
27
|
site: InputSite;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
* The globals table
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
30
|
+
* The globals table, with every rule that pairs two of its options settled: one spelling map, the
|
|
31
|
+
* scope that owns each key and where it was declared, and the variable each option binds. It holds
|
|
32
|
+
* the application's global options and every installed plugin's options, because both are global
|
|
33
|
+
* options that routing reads and every Command's table holds.
|
|
34
34
|
*/
|
|
35
35
|
interface GlobalTable {
|
|
36
36
|
names: ReadonlyMap<string, TableEntry>;
|
|
@@ -39,26 +39,33 @@ interface GlobalTable {
|
|
|
39
39
|
variables: ReadonlyMap<string, BoundOption>;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* The compiled table one graph build shares. `inputs` holds the application's
|
|
43
|
-
*
|
|
42
|
+
* The compiled table one graph build shares. `inputs` holds every global option, the application's
|
|
43
|
+
* in authoring order and then each installed plugin's in installation order, which is the order
|
|
44
|
+
* the input-source stage fills them, validation checks them, and `inspect()` lists them. `sites`
|
|
45
|
+
* holds the call that declared each of them, which a fault about one rebuilds. `bind` reads every
|
|
46
|
+
* global option's validated value, keyed by declared name, as every action receives it. `table`
|
|
47
|
+
* holds the global options alone as parser entries, which routing reads at a Command without its
|
|
48
|
+
* own options to offer and every Command's table joins.
|
|
44
49
|
*/
|
|
45
50
|
interface BuiltGlobals extends GlobalTable {
|
|
46
51
|
bind: (values: ValidatedInputs) => unknown;
|
|
47
52
|
inputs: readonly OptionInput[];
|
|
48
53
|
plugins: readonly BuiltPlugin[];
|
|
54
|
+
sites: ReadonlyMap<InputDeclaration, InputSite>;
|
|
55
|
+
table: SpellingTable;
|
|
49
56
|
}
|
|
50
57
|
/** The validated extension record of each declaration, keyed by the declaration itself. */
|
|
51
58
|
type InputRecords = ReadonlyMap<InputDeclaration, Readonly<Record<string, unknown>>>;
|
|
52
59
|
/**
|
|
53
|
-
* The Application's private global declarations
|
|
54
|
-
*
|
|
60
|
+
* The Application's private global declarations and the extension record each declaration's own
|
|
61
|
+
* call validated. The global options' value types live on the Application's type, and the action
|
|
62
|
+
* reads every global option's value through the built table's binder.
|
|
55
63
|
*/
|
|
56
|
-
interface GlobalsState
|
|
57
|
-
bind: (values: ValidatedInputs) => Globals;
|
|
64
|
+
interface GlobalsState {
|
|
58
65
|
inputs: readonly OptionInput[];
|
|
59
66
|
records: InputRecords;
|
|
60
67
|
}
|
|
61
|
-
declare function emptyGlobals(): GlobalsState
|
|
68
|
+
declare function emptyGlobals(): GlobalsState;
|
|
62
69
|
/** Where one global option was declared: its `globalOption()` call on the Application. */
|
|
63
70
|
declare function globalSite(input: OptionInput): InputSite;
|
|
64
71
|
/**
|
|
@@ -66,26 +73,45 @@ declare function globalSite(input: OptionInput): InputSite;
|
|
|
66
73
|
* plugin's `plugin()` call, which is rebuilt from every option the plugin holds.
|
|
67
74
|
*/
|
|
68
75
|
declare function pluginSites(identity: string, inputs: readonly OptionInput[]): (input: OptionInput) => InputSite;
|
|
76
|
+
/**
|
|
77
|
+
* A global option declares no presence rule, whether the application or a plugin declares it, and
|
|
78
|
+
* whatever the key's value. The fault marks the key on the call that declared the option.
|
|
79
|
+
*/
|
|
80
|
+
declare function checkNoPresenceRule(site: FactSite, config: OptionConfig): void;
|
|
69
81
|
/**
|
|
70
82
|
* One global option's own facts, binding, and extension values, checked at its `globalOption()`
|
|
71
83
|
* call against the Application's descriptors. The rules that pair it with another option belong to
|
|
72
84
|
* the table, which the caller rebuilds with it.
|
|
73
85
|
*/
|
|
74
|
-
declare function declareGlobalOption<
|
|
86
|
+
declare function declareGlobalOption<Name extends string, Config extends OptionConfig>(state: GlobalsState, declared: OptionInput<Name, Config>, descriptors: DescriptorRegistry): {
|
|
75
87
|
readonly input: OptionInput<Name, Config>;
|
|
76
|
-
readonly state: GlobalsState
|
|
88
|
+
readonly state: GlobalsState;
|
|
77
89
|
};
|
|
78
90
|
/**
|
|
79
|
-
* The
|
|
80
|
-
*
|
|
81
|
-
*
|
|
91
|
+
* The globals table: the application's global options in authoring order, then each installed
|
|
92
|
+
* plugin's options in installation order. Every collision between the two scopes, by key or by
|
|
93
|
+
* spelling, is reported here, so the parser meets a table with one owner per name.
|
|
82
94
|
*/
|
|
83
95
|
declare function globalTable(inputs: readonly OptionInput[], plugins: readonly BuiltPlugin[]): GlobalTable;
|
|
84
|
-
/**
|
|
96
|
+
/**
|
|
97
|
+
* The validated value of each listed option, keyed by declared name. Entries become own keys even
|
|
98
|
+
* for a name such as `__proto__`, which assignment would not.
|
|
99
|
+
*/
|
|
100
|
+
declare function optionValues(inputs: readonly OptionInput[], values: ValidatedInputs): Record<string, unknown>;
|
|
101
|
+
/**
|
|
102
|
+
* The validated value of each listed option, keyed by declared name, as a plugin reads it: each
|
|
103
|
+
* value a plain-data copy frozen to every depth, as the request's values are, so a plugin that
|
|
104
|
+
* reaches into one contributes nothing to what another plugin or the action receives.
|
|
105
|
+
*/
|
|
106
|
+
declare function frozenValues(inputs: readonly OptionInput[], values: ValidatedInputs): Readonly<Record<string, unknown>>;
|
|
107
|
+
/**
|
|
108
|
+
* The table one graph build shares, which every earlier call already proved free of collisions.
|
|
109
|
+
* A plugin's options are global options, so they join the application's in every reading.
|
|
110
|
+
*/
|
|
85
111
|
declare function buildGlobals(node: GlobalsState, plugins: readonly BuiltPlugin[]): BuiltGlobals;
|
|
86
112
|
/**
|
|
87
113
|
* One Command's own options against the globals table, compiled for dispatch. The table holds the
|
|
88
|
-
* application's
|
|
114
|
+
* application's global options and every plugin's, so a local collision reads the same sentence
|
|
89
115
|
* whichever scope on the other side claimed the name, the spelling, or the variable. One
|
|
90
116
|
* invocation's scope is this Command's own options and the table, so a variable binds one option
|
|
91
117
|
* there, while a sibling Command may bind it again. `scope` names the Command and places each of
|
|
@@ -95,4 +121,4 @@ declare function checkLocalOptions(declarations: readonly OptionInput[], table:
|
|
|
95
121
|
/** The options in one list that bind a variable, each named and placed by its scope. */
|
|
96
122
|
declare function boundOptions(inputs: readonly OptionInput[], describe: (input: OptionInput) => Omit<BoundOption, 'variable'>): BoundOption[];
|
|
97
123
|
export type { BuiltGlobals, GlobalsState, GlobalTable, InputRecords, OptionOwner, TableEntry };
|
|
98
|
-
export { boundOptions, buildGlobals, checkLocalOptions, declareGlobalOption, emptyGlobals, globalSite, globalTable, pluginSites, };
|
|
124
|
+
export { boundOptions, buildGlobals, checkLocalOptions, checkNoPresenceRule, declareGlobalOption, emptyGlobals, frozenValues, globalSite, globalTable, optionValues, pluginSites, };
|
package/dist/globals.js
CHANGED
|
@@ -3,8 +3,9 @@ import { DeclarationError, quoted } from './errors.js';
|
|
|
3
3
|
import { buildExtensions } from './extension.js';
|
|
4
4
|
import { callSite, checkDeprecated, checkDescription, checkHidden, factFault, pluginOptionSite, siteFinding, } from './facts.js';
|
|
5
5
|
import { globalPresenceRule, optionDeclaredTwice, spellingTaken } from './input-rules.js';
|
|
6
|
-
import { checkOptionName, compileOptions, spellingMark } from './options.js';
|
|
7
|
-
import {
|
|
6
|
+
import { checkOptionName, compileOptions, spellingMark, tableEntries } from './options.js';
|
|
7
|
+
import { snapshot } from './plain.js';
|
|
8
|
+
import { captureInputConfig, configUnread } from './validation.js';
|
|
8
9
|
const globalSubject = 'the global options';
|
|
9
10
|
/**
|
|
10
11
|
* The presence keys a global option may not declare, in the order its diagnostic names them. A
|
|
@@ -59,7 +60,7 @@ function correction(first, second) {
|
|
|
59
60
|
const sideNotes = {
|
|
60
61
|
application: 'the global option',
|
|
61
62
|
local: 'the local option',
|
|
62
|
-
plugin:
|
|
63
|
+
plugin: "the plugin's global option",
|
|
63
64
|
};
|
|
64
65
|
/** One key claimed twice, whichever two scopes claimed it. */
|
|
65
66
|
function keyCollision(first, second) {
|
|
@@ -77,12 +78,12 @@ function spellingCollision(spelling, first, second) {
|
|
|
77
78
|
const [leading, trailing] = pair;
|
|
78
79
|
return new DeclarationError(spellingTaken, {
|
|
79
80
|
correction: 'Change one declaration.',
|
|
80
|
-
findings: pair.map(({
|
|
81
|
+
findings: pair.map(({ origin, owner, site }) => siteFinding(site, spellingMark(site, origin), sideNotes[owner.kind])),
|
|
81
82
|
sentence: `Option spelling ${quoted(spelling)} is used by ${usedBy(leading)} and ${usedBy(trailing)}.`,
|
|
82
83
|
});
|
|
83
84
|
}
|
|
84
85
|
function emptyGlobals() {
|
|
85
|
-
return {
|
|
86
|
+
return { inputs: [], records: new Map() };
|
|
86
87
|
}
|
|
87
88
|
/** Where one global option was declared: its `globalOption()` call on the Application. */
|
|
88
89
|
function globalSite(input) {
|
|
@@ -99,7 +100,21 @@ function globalSite(input) {
|
|
|
99
100
|
*/
|
|
100
101
|
function pluginSites(identity, inputs) {
|
|
101
102
|
const options = Object.fromEntries(inputs.map(({ config, name }) => [name, config]));
|
|
102
|
-
return (input) => pluginOptionSite({ identity, options }, input.name, `Plugin ${quoted(identity)} option
|
|
103
|
+
return (input) => pluginOptionSite({ identity, options }, input.name, `Plugin ${quoted(identity)} option ${quoted(input.name)}`);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* A global option declares no presence rule, whether the application or a plugin declares it, and
|
|
107
|
+
* whatever the key's value. The fault marks the key on the call that declared the option.
|
|
108
|
+
*/
|
|
109
|
+
function checkNoPresenceRule(site, config) {
|
|
110
|
+
const rejected = omissionRules.find((key) => key in config);
|
|
111
|
+
if (rejected !== undefined) {
|
|
112
|
+
throw factFault(globalPresenceRule, site, {
|
|
113
|
+
correction: `Remove ${rejected}, and check for the value in each Command that needs it.`,
|
|
114
|
+
fact: rejected,
|
|
115
|
+
sentence: `${site.subject} declares ${rejected}.`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
103
118
|
}
|
|
104
119
|
/**
|
|
105
120
|
* One global option's own facts, binding, and extension values, checked at its `globalOption()`
|
|
@@ -108,19 +123,14 @@ function pluginSites(identity, inputs) {
|
|
|
108
123
|
*/
|
|
109
124
|
function declareGlobalOption(state, declared, descriptors) {
|
|
110
125
|
// The name is judged before the config, as every other declaration judges its own name first.
|
|
111
|
-
checkOptionName(declared.name, globalSite(declared));
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
checkOptionName(declared.name, configUnread(globalSite(declared)));
|
|
127
|
+
const input = {
|
|
128
|
+
...declared,
|
|
129
|
+
config: captureInputConfig(declared, { call: 'globalOption', path: [] }),
|
|
130
|
+
};
|
|
114
131
|
const site = globalSite(input);
|
|
115
132
|
const sentence = site.subject;
|
|
116
|
-
|
|
117
|
-
if (rejected !== undefined) {
|
|
118
|
-
throw factFault(globalPresenceRule, site, {
|
|
119
|
-
correction: `Remove ${rejected}, and check for the value in each Command that needs it.`,
|
|
120
|
-
fact: rejected,
|
|
121
|
-
sentence: `${sentence} declares ${rejected}.`,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
133
|
+
checkNoPresenceRule(site, input.config);
|
|
124
134
|
checkDescription(site, input.config.description);
|
|
125
135
|
checkHidden(site, input.config.hidden);
|
|
126
136
|
checkDeprecated(site, input.config.deprecated);
|
|
@@ -136,16 +146,15 @@ function declareGlobalOption(state, declared, descriptors) {
|
|
|
136
146
|
return {
|
|
137
147
|
input,
|
|
138
148
|
state: {
|
|
139
|
-
bind: (values) => ({ ...state.bind(values), ...values.option(input) }),
|
|
140
149
|
inputs: [...state.inputs, input],
|
|
141
150
|
records: new Map([...state.records, [input, record]]),
|
|
142
151
|
},
|
|
143
152
|
};
|
|
144
153
|
}
|
|
145
154
|
/**
|
|
146
|
-
* The
|
|
147
|
-
*
|
|
148
|
-
*
|
|
155
|
+
* The globals table: the application's global options in authoring order, then each installed
|
|
156
|
+
* plugin's options in installation order. Every collision between the two scopes, by key or by
|
|
157
|
+
* spelling, is reported here, so the parser meets a table with one owner per name.
|
|
149
158
|
*/
|
|
150
159
|
function globalTable(inputs, plugins) {
|
|
151
160
|
const names = new Map();
|
|
@@ -168,20 +177,54 @@ function globalTable(inputs, plugins) {
|
|
|
168
177
|
...plugins.flatMap((installed) => {
|
|
169
178
|
const siteOf = pluginSites(installed.identity, installed.inputs);
|
|
170
179
|
return boundOptions(installed.inputs, (input) => ({
|
|
171
|
-
phrase: `plugin ${quoted(installed.identity)} option
|
|
180
|
+
phrase: `plugin ${quoted(installed.identity)} option ${quoted(input.name)}`,
|
|
172
181
|
site: siteOf(input),
|
|
173
182
|
}));
|
|
174
183
|
}),
|
|
175
184
|
]);
|
|
176
185
|
return { names, options, variables };
|
|
177
186
|
}
|
|
178
|
-
/**
|
|
187
|
+
/**
|
|
188
|
+
* The validated value of each listed option, keyed by declared name. Entries become own keys even
|
|
189
|
+
* for a name such as `__proto__`, which assignment would not.
|
|
190
|
+
*/
|
|
191
|
+
function optionValues(inputs, values) {
|
|
192
|
+
return Object.fromEntries(inputs.map((input) => [input.name, values.read(input)]));
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The validated value of each listed option, keyed by declared name, as a plugin reads it: each
|
|
196
|
+
* value a plain-data copy frozen to every depth, as the request's values are, so a plugin that
|
|
197
|
+
* reaches into one contributes nothing to what another plugin or the action receives.
|
|
198
|
+
*/
|
|
199
|
+
function frozenValues(inputs, values) {
|
|
200
|
+
return Object.freeze(Object.fromEntries(inputs.map((input) => [input.name, snapshot(values.read(input))])));
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* The table one graph build shares, which every earlier call already proved free of collisions.
|
|
204
|
+
* A plugin's options are global options, so they join the application's in every reading.
|
|
205
|
+
*/
|
|
179
206
|
function buildGlobals(node, plugins) {
|
|
180
|
-
|
|
207
|
+
const inputs = [...node.inputs, ...plugins.flatMap((installed) => installed.inputs)];
|
|
208
|
+
const sites = new Map(node.inputs.map((input) => [input, globalSite(input)]));
|
|
209
|
+
for (const installed of plugins) {
|
|
210
|
+
const siteOf = pluginSites(installed.identity, installed.inputs);
|
|
211
|
+
for (const input of installed.inputs) {
|
|
212
|
+
sites.set(input, siteOf(input));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const table = globalTable(node.inputs, plugins);
|
|
216
|
+
return {
|
|
217
|
+
...table,
|
|
218
|
+
bind: (values) => optionValues(inputs, values),
|
|
219
|
+
inputs,
|
|
220
|
+
plugins,
|
|
221
|
+
sites,
|
|
222
|
+
table: new Map(tableEntries(table.options, true)),
|
|
223
|
+
};
|
|
181
224
|
}
|
|
182
225
|
/**
|
|
183
226
|
* One Command's own options against the globals table, compiled for dispatch. The table holds the
|
|
184
|
-
* application's
|
|
227
|
+
* application's global options and every plugin's, so a local collision reads the same sentence
|
|
185
228
|
* whichever scope on the other side claimed the name, the spelling, or the variable. One
|
|
186
229
|
* invocation's scope is this Command's own options and the table, so a variable binds one option
|
|
187
230
|
* there, while a sibling Command may bind it again. `scope` names the Command and places each of
|
|
@@ -202,11 +245,11 @@ function checkLocalOptions(declarations, table, scope) {
|
|
|
202
245
|
const claimed = global && table.names.get(global.name);
|
|
203
246
|
const declaration = declarations.find((input) => input.name === option.name);
|
|
204
247
|
if (global && claimed && declaration) {
|
|
205
|
-
throw spellingCollision(spelling, { ...claimed, name: global.name,
|
|
248
|
+
throw spellingCollision(spelling, { ...claimed, name: global.name, origin: global }, { name: option.name, origin: option, owner: local, site: siteOf(declaration) });
|
|
206
249
|
}
|
|
207
250
|
}
|
|
208
251
|
claimVariables(boundOptions(declarations, (input) => ({
|
|
209
|
-
phrase: `${subject} option
|
|
252
|
+
phrase: `${subject} option ${quoted(input.name)}`,
|
|
210
253
|
site: siteOf(input),
|
|
211
254
|
})), table.variables);
|
|
212
255
|
return options;
|
|
@@ -234,9 +277,9 @@ function join(owner, inputs, table) {
|
|
|
234
277
|
const claimed = existing && names.get(existing.name);
|
|
235
278
|
const own = names.get(option.name);
|
|
236
279
|
if (existing && claimed && own) {
|
|
237
|
-
throw spellingCollision(spelling, { ...own, name: option.name,
|
|
280
|
+
throw spellingCollision(spelling, { ...own, name: option.name, origin: option }, { ...claimed, name: existing.name, origin: existing });
|
|
238
281
|
}
|
|
239
282
|
options.set(spelling, option);
|
|
240
283
|
}
|
|
241
284
|
}
|
|
242
|
-
export { boundOptions, buildGlobals, checkLocalOptions, declareGlobalOption, emptyGlobals, globalSite, globalTable, pluginSites, };
|
|
285
|
+
export { boundOptions, buildGlobals, checkLocalOptions, checkNoPresenceRule, declareGlobalOption, emptyGlobals, frozenValues, globalSite, globalTable, optionValues, pluginSites, };
|
package/dist/glyphs.generated.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { validationContext, validationContextKey } from './context.js';
|
|
|
5
5
|
export { diagnosticRule } from './diagnostic.js';
|
|
6
6
|
export { isRuleIdentity } from './identity.js';
|
|
7
7
|
export type { DiagnosticParts, DiagnosticRule, Finding } from './diagnostic-text.js';
|
|
8
|
-
export { DeclarationError, FatalError, InputError, InternalError, LoomError, MissingValueError, NonCallableCommandError, RepeatedOptionError, ResultError,
|
|
8
|
+
export { DeclarationError, FatalError, InputError, InternalError, LoomError, MisplacedOptionError, MissingValueError, NonCallableCommandError, RepeatedOptionError, ResultError, UnexpectedArgumentError, UnexpectedValueError, UnknownCommandError, UnknownOptionError, UsageError, } from './errors.js';
|
|
9
9
|
export { EX_CANTCREAT, EX_CONFIG, EX_DATAERR, EX_IOERR, EX_NOHOST, EX_NOINPUT, EX_NOPERM, EX_NOUSER, EX_OSERR, EX_OSFILE, EX_PROTOCOL, EX_SOFTWARE, EX_TEMPFAIL, EX_UNAVAILABLE, EX_USAGE, } from './exit-codes.js';
|
|
10
10
|
export type { FailureExitCode } from './exit-codes.js';
|
|
11
11
|
export { extension, readExtension } from './extension.js';
|
|
@@ -13,11 +13,13 @@ export { locate } from './locate.js';
|
|
|
13
13
|
export { incompleteResult, lanes } from './lanes.js';
|
|
14
14
|
export { override, view } from './view.js';
|
|
15
15
|
export { plugin } from './plugin.js';
|
|
16
|
+
export { checkShortSetting } from './plugin-settings.js';
|
|
16
17
|
export { translate } from './translators.js';
|
|
17
18
|
export { glyph } from './glyphs.generated.js';
|
|
18
19
|
export type { RenderingPolicy } from './rendering.js';
|
|
19
20
|
export type { ViewContext } from './types.js';
|
|
20
21
|
export { pad, style } from './style.js';
|
|
22
|
+
export { reportedSpelling } from './inspect.js';
|
|
21
23
|
export { issuePath } from './validation.js';
|
|
22
24
|
export type { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec';
|
|
23
25
|
export type { ApplicationMethod, ApplicationOptions, Packet } from './application.js';
|
|
@@ -30,9 +32,9 @@ export type { CancellationReason } from './signals.js';
|
|
|
30
32
|
export type { ErrorClass, Translation, Translator } from './translators.js';
|
|
31
33
|
export type { IncompleteResult } from './lanes.js';
|
|
32
34
|
export type { WordPosition } from './locate.js';
|
|
33
|
-
export type { AnyDeclaredView, DeclaredRowView, DeclaredView, DeclaredViewBrand, FailureClass, FailureView, FailureViewContext, ViewContribution, ViewOverride, } from './view.js';
|
|
35
|
+
export type { AnyDeclaredView, AnyOverrideKey, DeclaredRowView, DeclaredView, DeclaredViewBrand, FailureClass, FailureView, FailureViewContext, ReplacementView, ViewContribution, ViewOverride, } from './view.js';
|
|
34
36
|
export type { ArgumentNode, CommandGraph, CommandNode, OptionNode, ResultNode } from './inspect.js';
|
|
35
37
|
export type { Middleware, OptionsOf, Plugin, PluginDefinition, PluginOptions, PluginOptionSpellings, PluginOptionValues, SourceAnswer, SourceContext, SourceResolver, } from './plugin.js';
|
|
36
|
-
export type { Action, ActionArgs, ActionContext, ActionHandler, ActionOptions, ArgumentConfig, AttachedCommand, BooleanOption, CommandAttachHook, ExitCode, Host, InputIdentity, InputTerminal, Out, OptionConfig, OutputTerminal, Request, ResultInput, ResultView, ResultViews, RunOptions, ScalarArgument, StringOption, SuppliedInputs, RowView, RowViews, ValidationContext, VariadicArgument, View, } from './types.js';
|
|
38
|
+
export type { Action, ActionArgs, ActionContext, ActionHandler, ActionOptions, ArgumentConfig, AttachedCommand, BooleanOption, CommandAttachHook, CountOption, ExitCode, Host, InputIdentity, InputTerminal, Out, OptionConfig, OutputTerminal, Request, ResultInput, ResultView, ResultViews, RunOptions, ScalarArgument, StringOption, SuppliedInputs, RowView, RowViews, ValidationContext, VariadicArgument, View, } from './types.js';
|
|
37
39
|
export type { ApplicationEnvironment, EnvironmentOf, Register, RegisteredEnvironment, } from './environment.js';
|
|
38
40
|
export type { Ansi16Color, Ansi256Fallbacks, ColorFallbacks, ConcreteStyle, ContextualStyle, Style, ThemeMapping, ThemeConstraint, } from './style.js';
|
package/dist/index.js
CHANGED
|
@@ -4,14 +4,16 @@ export { escapeControlCharacters } from './controls.js';
|
|
|
4
4
|
export { validationContext, validationContextKey } from './context.js';
|
|
5
5
|
export { diagnosticRule } from './diagnostic.js';
|
|
6
6
|
export { isRuleIdentity } from './identity.js';
|
|
7
|
-
export { DeclarationError, FatalError, InputError, InternalError, LoomError, MissingValueError, NonCallableCommandError, RepeatedOptionError, ResultError,
|
|
7
|
+
export { DeclarationError, FatalError, InputError, InternalError, LoomError, MisplacedOptionError, MissingValueError, NonCallableCommandError, RepeatedOptionError, ResultError, UnexpectedArgumentError, UnexpectedValueError, UnknownCommandError, UnknownOptionError, UsageError, } from './errors.js';
|
|
8
8
|
export { EX_CANTCREAT, EX_CONFIG, EX_DATAERR, EX_IOERR, EX_NOHOST, EX_NOINPUT, EX_NOPERM, EX_NOUSER, EX_OSERR, EX_OSFILE, EX_PROTOCOL, EX_SOFTWARE, EX_TEMPFAIL, EX_UNAVAILABLE, EX_USAGE, } from './exit-codes.js';
|
|
9
9
|
export { extension, readExtension } from './extension.js';
|
|
10
10
|
export { locate } from './locate.js';
|
|
11
11
|
export { incompleteResult, lanes } from './lanes.js';
|
|
12
12
|
export { override, view } from './view.js';
|
|
13
13
|
export { plugin } from './plugin.js';
|
|
14
|
+
export { checkShortSetting } from './plugin-settings.js';
|
|
14
15
|
export { translate } from './translators.js';
|
|
15
16
|
export { glyph } from './glyphs.generated.js';
|
|
16
17
|
export { pad, style } from './style.js';
|
|
18
|
+
export { reportedSpelling } from './inspect.js';
|
|
17
19
|
export { issuePath } from './validation.js';
|
package/dist/input-rules.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** An option declared with a type other than string or
|
|
1
|
+
/** An option declared with a type other than string, Boolean, or count. */
|
|
2
2
|
declare const optionType: import("./diagnostic-text.js").DiagnosticRule;
|
|
3
3
|
/** A short alias that is not one ASCII letter. */
|
|
4
4
|
declare const shortAlias: import("./diagnostic-text.js").DiagnosticRule;
|
|
@@ -9,8 +9,18 @@ declare const shortAlias: import("./diagnostic-text.js").DiagnosticRule;
|
|
|
9
9
|
declare const flagNotBoolean: import("./diagnostic-text.js").DiagnosticRule;
|
|
10
10
|
/** `shortOnly` on an option that declares no short alias. */
|
|
11
11
|
declare const shortOnlyWithoutShort: import("./diagnostic-text.js").DiagnosticRule;
|
|
12
|
+
/** `aliases` on an option that declares `shortOnly`. */
|
|
13
|
+
declare const shortOnlyWithAliases: import("./diagnostic-text.js").DiagnosticRule;
|
|
12
14
|
/** `multiple` on a Boolean option. */
|
|
13
15
|
declare const booleanOptionMultiple: import("./diagnostic-text.js").DiagnosticRule;
|
|
16
|
+
/** `multiple` on a counted option. */
|
|
17
|
+
declare const countOptionMultiple: import("./diagnostic-text.js").DiagnosticRule;
|
|
18
|
+
/** `polarity` on a counted option. */
|
|
19
|
+
declare const polarityOnCount: import("./diagnostic-text.js").DiagnosticRule;
|
|
20
|
+
/** `implied` on a Boolean or counted option. */
|
|
21
|
+
declare const impliedOnBooleanOrCount: import("./diagnostic-text.js").DiagnosticRule;
|
|
22
|
+
/** An `implied` value that is not a string. */
|
|
23
|
+
declare const impliedNotAString: import("./diagnostic-text.js").DiagnosticRule;
|
|
14
24
|
/** `polarity` on a string option. */
|
|
15
25
|
declare const polarityOnString: import("./diagnostic-text.js").DiagnosticRule;
|
|
16
26
|
/** A polarity outside the three settings. */
|
|
@@ -51,14 +61,22 @@ declare const omissionAlreadyDecided: import("./diagnostic-text.js").DiagnosticR
|
|
|
51
61
|
declare const omissionWithoutValidator: import("./diagnostic-text.js").DiagnosticRule;
|
|
52
62
|
/** `validate`, `default`, `required`, or `validateOmitted` on a Boolean option. */
|
|
53
63
|
declare const booleanOptionValueRule: import("./diagnostic-text.js").DiagnosticRule;
|
|
64
|
+
/** `validate`, `default`, `required`, or `validateOmitted` on a counted option. */
|
|
65
|
+
declare const countOptionValueRule: import("./diagnostic-text.js").DiagnosticRule;
|
|
54
66
|
/** A required input that also declares a default. */
|
|
55
67
|
declare const requiredWithDefault: import("./diagnostic-text.js").DiagnosticRule;
|
|
56
68
|
/** A `validate` value that is not a Standard Schema v1 object. */
|
|
57
69
|
declare const notAValidator: import("./diagnostic-text.js").DiagnosticRule;
|
|
58
70
|
/** A default of the wrong raw shape for its declaration. */
|
|
59
71
|
declare const defaultShape: import("./diagnostic-text.js").DiagnosticRule;
|
|
72
|
+
/** The most arrays and plain objects any path through a declared default may hold. */
|
|
73
|
+
declare const defaultLevels = 10;
|
|
74
|
+
/** A declared default nested deeper than `defaultLevels`. */
|
|
75
|
+
declare const defaultDepth: import("./diagnostic-text.js").DiagnosticRule;
|
|
60
76
|
/** A declared default its validator rejected. */
|
|
61
77
|
declare const invalidDefault: import("./diagnostic-text.js").DiagnosticRule;
|
|
78
|
+
/** A declared implied value its validator rejected. */
|
|
79
|
+
declare const invalidImplied: import("./diagnostic-text.js").DiagnosticRule;
|
|
62
80
|
/** A validator's JSON Schema converter that threw or returned a value that is not a plain object. */
|
|
63
81
|
declare const schemaConverterFailed: import("./diagnostic-text.js").DiagnosticRule;
|
|
64
|
-
export { booleanOptionMultiple, booleanOptionValueRule, defaultShape, envName, envOnArgument, envOnMultiple, flagNotBoolean, globalOptionAfterCommand, globalPresenceRule, invalidDefault, notAValidator, omissionAlreadyDecided, nameSharedAcrossKinds, omissionWithoutValidator, optionDeclaredTwice, optionPolarity, optionType, polarityOnString, requiredWithDefault, schemaConverterFailed, shortAlias, shortOnlyBothPolarities, shortOnlyWithoutShort, spellingTaken, variableBoundTwice, };
|
|
82
|
+
export { booleanOptionMultiple, booleanOptionValueRule, countOptionMultiple, countOptionValueRule, defaultDepth, defaultLevels, defaultShape, envName, envOnArgument, envOnMultiple, flagNotBoolean, globalOptionAfterCommand, globalPresenceRule, impliedNotAString, impliedOnBooleanOrCount, invalidDefault, invalidImplied, notAValidator, omissionAlreadyDecided, nameSharedAcrossKinds, omissionWithoutValidator, optionDeclaredTwice, optionPolarity, optionType, polarityOnCount, polarityOnString, requiredWithDefault, schemaConverterFailed, shortAlias, shortOnlyBothPolarities, shortOnlyWithAliases, shortOnlyWithoutShort, spellingTaken, variableBoundTwice, };
|