@loomcli/core 0.7.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/dist/application.d.ts +11 -6
- package/dist/application.js +11 -5
- 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 +136 -170
- package/dist/errors.d.ts +22 -21
- package/dist/errors.js +38 -18
- package/dist/globals.d.ts +48 -22
- package/dist/globals.js +63 -22
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/input-rules.d.ts +16 -2
- package/dist/input-rules.js +40 -5
- package/dist/inspect.d.ts +35 -11
- package/dist/inspect.js +69 -66
- package/dist/locate.js +52 -60
- package/dist/options.d.ts +94 -83
- package/dist/options.js +298 -260
- package/dist/parse.d.ts +162 -0
- package/dist/parse.js +601 -0
- package/dist/plugin-rules.d.ts +2 -4
- package/dist/plugin-rules.js +3 -8
- package/dist/plugin.d.ts +26 -21
- package/dist/plugin.js +10 -45
- package/dist/sources.d.ts +18 -9
- package/dist/sources.js +46 -16
- package/dist/types.d.ts +68 -28
- package/dist/validation.d.ts +84 -31
- package/dist/validation.js +222 -110
- package/package.json +1 -1
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/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,7 +3,8 @@ 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';
|
|
6
|
+
import { checkOptionName, compileOptions, spellingMark, tableEntries } from './options.js';
|
|
7
|
+
import { snapshot } from './plain.js';
|
|
7
8
|
import { captureInputConfig, configUnread } from './validation.js';
|
|
8
9
|
const globalSubject = 'the global options';
|
|
9
10
|
/**
|
|
@@ -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) {
|
|
@@ -101,6 +102,20 @@ function pluginSites(identity, inputs) {
|
|
|
101
102
|
const options = Object.fromEntries(inputs.map(({ config, name }) => [name, config]));
|
|
102
103
|
return (input) => pluginOptionSite({ identity, options }, input.name, `Plugin ${quoted(identity)} option ${quoted(input.name)}`);
|
|
103
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
|
+
}
|
|
118
|
+
}
|
|
104
119
|
/**
|
|
105
120
|
* One global option's own facts, binding, and extension values, checked at its `globalOption()`
|
|
106
121
|
* call against the Application's descriptors. The rules that pair it with another option belong to
|
|
@@ -115,14 +130,7 @@ function declareGlobalOption(state, declared, descriptors) {
|
|
|
115
130
|
};
|
|
116
131
|
const site = globalSite(input);
|
|
117
132
|
const sentence = site.subject;
|
|
118
|
-
|
|
119
|
-
if (rejected !== undefined) {
|
|
120
|
-
throw factFault(globalPresenceRule, site, {
|
|
121
|
-
correction: `Remove ${rejected}, and check for the value in each Command that needs it.`,
|
|
122
|
-
fact: rejected,
|
|
123
|
-
sentence: `${sentence} declares ${rejected}.`,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
133
|
+
checkNoPresenceRule(site, input.config);
|
|
126
134
|
checkDescription(site, input.config.description);
|
|
127
135
|
checkHidden(site, input.config.hidden);
|
|
128
136
|
checkDeprecated(site, input.config.deprecated);
|
|
@@ -138,16 +146,15 @@ function declareGlobalOption(state, declared, descriptors) {
|
|
|
138
146
|
return {
|
|
139
147
|
input,
|
|
140
148
|
state: {
|
|
141
|
-
bind: (values) => ({ ...state.bind(values), ...values.option(input) }),
|
|
142
149
|
inputs: [...state.inputs, input],
|
|
143
150
|
records: new Map([...state.records, [input, record]]),
|
|
144
151
|
},
|
|
145
152
|
};
|
|
146
153
|
}
|
|
147
154
|
/**
|
|
148
|
-
* The
|
|
149
|
-
*
|
|
150
|
-
*
|
|
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.
|
|
151
158
|
*/
|
|
152
159
|
function globalTable(inputs, plugins) {
|
|
153
160
|
const names = new Map();
|
|
@@ -177,13 +184,47 @@ function globalTable(inputs, plugins) {
|
|
|
177
184
|
]);
|
|
178
185
|
return { names, options, variables };
|
|
179
186
|
}
|
|
180
|
-
/**
|
|
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
|
+
*/
|
|
181
206
|
function buildGlobals(node, plugins) {
|
|
182
|
-
|
|
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
|
+
};
|
|
183
224
|
}
|
|
184
225
|
/**
|
|
185
226
|
* One Command's own options against the globals table, compiled for dispatch. The table holds the
|
|
186
|
-
* application's
|
|
227
|
+
* application's global options and every plugin's, so a local collision reads the same sentence
|
|
187
228
|
* whichever scope on the other side claimed the name, the spelling, or the variable. One
|
|
188
229
|
* invocation's scope is this Command's own options and the table, so a variable binds one option
|
|
189
230
|
* there, while a sibling Command may bind it again. `scope` names the Command and places each of
|
|
@@ -204,7 +245,7 @@ function checkLocalOptions(declarations, table, scope) {
|
|
|
204
245
|
const claimed = global && table.names.get(global.name);
|
|
205
246
|
const declaration = declarations.find((input) => input.name === option.name);
|
|
206
247
|
if (global && claimed && declaration) {
|
|
207
|
-
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) });
|
|
208
249
|
}
|
|
209
250
|
}
|
|
210
251
|
claimVariables(boundOptions(declarations, (input) => ({
|
|
@@ -236,9 +277,9 @@ function join(owner, inputs, table) {
|
|
|
236
277
|
const claimed = existing && names.get(existing.name);
|
|
237
278
|
const own = names.get(option.name);
|
|
238
279
|
if (existing && claimed && own) {
|
|
239
|
-
throw spellingCollision(spelling, { ...own, name: option.name,
|
|
280
|
+
throw spellingCollision(spelling, { ...own, name: option.name, origin: option }, { ...claimed, name: existing.name, origin: existing });
|
|
240
281
|
}
|
|
241
282
|
options.set(spelling, option);
|
|
242
283
|
}
|
|
243
284
|
}
|
|
244
|
-
export { boundOptions, buildGlobals, checkLocalOptions, declareGlobalOption, emptyGlobals, globalSite, globalTable, pluginSites, };
|
|
285
|
+
export { boundOptions, buildGlobals, checkLocalOptions, checkNoPresenceRule, declareGlobalOption, emptyGlobals, frozenValues, globalSite, globalTable, optionValues, pluginSites, };
|
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';
|
|
@@ -19,6 +19,7 @@ export { glyph } from './glyphs.generated.js';
|
|
|
19
19
|
export type { RenderingPolicy } from './rendering.js';
|
|
20
20
|
export type { ViewContext } from './types.js';
|
|
21
21
|
export { pad, style } from './style.js';
|
|
22
|
+
export { reportedSpelling } from './inspect.js';
|
|
22
23
|
export { issuePath } from './validation.js';
|
|
23
24
|
export type { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec';
|
|
24
25
|
export type { ApplicationMethod, ApplicationOptions, Packet } from './application.js';
|
|
@@ -34,6 +35,6 @@ export type { WordPosition } from './locate.js';
|
|
|
34
35
|
export type { AnyDeclaredView, AnyOverrideKey, DeclaredRowView, DeclaredView, DeclaredViewBrand, FailureClass, FailureView, FailureViewContext, ReplacementView, ViewContribution, ViewOverride, } from './view.js';
|
|
35
36
|
export type { ArgumentNode, CommandGraph, CommandNode, OptionNode, ResultNode } from './inspect.js';
|
|
36
37
|
export type { Middleware, OptionsOf, Plugin, PluginDefinition, PluginOptions, PluginOptionSpellings, PluginOptionValues, SourceAnswer, SourceContext, SourceResolver, } from './plugin.js';
|
|
37
|
-
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';
|
|
38
39
|
export type { ApplicationEnvironment, EnvironmentOf, Register, RegisteredEnvironment, } from './environment.js';
|
|
39
40
|
export type { Ansi16Color, Ansi256Fallbacks, ColorFallbacks, ConcreteStyle, ContextualStyle, Style, ThemeMapping, ThemeConstraint, } from './style.js';
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ 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';
|
|
@@ -15,4 +15,5 @@ export { checkShortSetting } from './plugin-settings.js';
|
|
|
15
15
|
export { translate } from './translators.js';
|
|
16
16
|
export { glyph } from './glyphs.generated.js';
|
|
17
17
|
export { pad, style } from './style.js';
|
|
18
|
+
export { reportedSpelling } from './inspect.js';
|
|
18
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,6 +61,8 @@ 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. */
|
|
@@ -63,6 +75,8 @@ declare const defaultLevels = 10;
|
|
|
63
75
|
declare const defaultDepth: import("./diagnostic-text.js").DiagnosticRule;
|
|
64
76
|
/** A declared default its validator rejected. */
|
|
65
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;
|
|
66
80
|
/** A validator's JSON Schema converter that threw or returned a value that is not a plain object. */
|
|
67
81
|
declare const schemaConverterFailed: import("./diagnostic-text.js").DiagnosticRule;
|
|
68
|
-
export { booleanOptionMultiple, booleanOptionValueRule, defaultDepth, defaultLevels, 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, };
|
package/dist/input-rules.js
CHANGED
|
@@ -4,9 +4,9 @@ import { registerRule } from './diagnostic-text.js';
|
|
|
4
4
|
* options, and environment bindings. Each is declared once here and shared by every site that
|
|
5
5
|
* raises it, as a plugin's rules are.
|
|
6
6
|
*/
|
|
7
|
-
/** An option declared with a type other than string or
|
|
7
|
+
/** An option declared with a type other than string, Boolean, or count. */
|
|
8
8
|
const optionType = registerRule('@loomcli/core/option-type', {
|
|
9
|
-
explanation: 'The type decides how the parser reads an option: a string option consumes a value, and a
|
|
9
|
+
explanation: 'The type decides how the parser reads an option: a string option consumes a value, a Boolean option consumes none, and a counted option consumes none and counts its occurrences. Core reads no other kind.',
|
|
10
10
|
headline: 'Invalid option type',
|
|
11
11
|
});
|
|
12
12
|
/** A short alias that is not one ASCII letter. */
|
|
@@ -27,11 +27,36 @@ const shortOnlyWithoutShort = registerRule('@loomcli/core/short-only-without-sho
|
|
|
27
27
|
explanation: 'shortOnly removes every long spelling of an option, so an option with no short alias would leave an operator no spelling to type.',
|
|
28
28
|
headline: 'Short only with no short alias',
|
|
29
29
|
});
|
|
30
|
+
/** `aliases` on an option that declares `shortOnly`. */
|
|
31
|
+
const shortOnlyWithAliases = registerRule('@loomcli/core/short-only-with-aliases', {
|
|
32
|
+
explanation: 'shortOnly removes every long spelling of an option, and each alias adds a long spelling, so an option cannot declare both.',
|
|
33
|
+
headline: 'Short only with aliases',
|
|
34
|
+
});
|
|
30
35
|
/** `multiple` on a Boolean option. */
|
|
31
36
|
const booleanOptionMultiple = registerRule('@loomcli/core/boolean-option-multiple', {
|
|
32
37
|
explanation: 'A Boolean option reports whether its spelling was supplied, so a repeat has no second value to collect. multiple collects each occurrence of a string option into an array.',
|
|
33
38
|
headline: 'Boolean option takes one value',
|
|
34
39
|
});
|
|
40
|
+
/** `multiple` on a counted option. */
|
|
41
|
+
const countOptionMultiple = registerRule('@loomcli/core/count-option-multiple', {
|
|
42
|
+
explanation: 'A counted option already counts every occurrence of every spelling, so it has no values to collect. multiple collects each occurrence of a string option into an array.',
|
|
43
|
+
headline: 'Counted option takes no values',
|
|
44
|
+
});
|
|
45
|
+
/** `polarity` on a counted option. */
|
|
46
|
+
const polarityOnCount = registerRule('@loomcli/core/polarity-on-count', {
|
|
47
|
+
explanation: 'Polarity chooses which long forms a Boolean option accepts and what its absence means. A counted option reads how many times it was supplied, so it has no negative form and no polarity.',
|
|
48
|
+
headline: 'Polarity on a counted option',
|
|
49
|
+
});
|
|
50
|
+
/** `implied` on a Boolean or counted option. */
|
|
51
|
+
const impliedOnBooleanOrCount = registerRule('@loomcli/core/implied-on-boolean-or-count', {
|
|
52
|
+
explanation: 'An implied value is the value a bare spelling of a string option supplies. A Boolean option and a counted option take no value, so a bare spelling already says everything they read.',
|
|
53
|
+
headline: 'Implied on a valueless option',
|
|
54
|
+
});
|
|
55
|
+
/** An `implied` value that is not a string. */
|
|
56
|
+
const impliedNotAString = registerRule('@loomcli/core/implied-not-a-string', {
|
|
57
|
+
explanation: "An implied value stands in for the string an operator would otherwise attach to the spelling, so it is a string, in the validator's input type.",
|
|
58
|
+
headline: 'Implied value not a string',
|
|
59
|
+
});
|
|
35
60
|
/** `polarity` on a string option. */
|
|
36
61
|
const polarityOnString = registerRule('@loomcli/core/polarity-on-string', {
|
|
37
62
|
explanation: 'Polarity chooses which long forms a Boolean option accepts and what its absence means. A string option takes its value from the operator, so it has no polarity.',
|
|
@@ -52,7 +77,7 @@ const shortOnlyBothPolarities = registerRule('@loomcli/core/short-only-both-pola
|
|
|
52
77
|
* plugin's hook declared.
|
|
53
78
|
*/
|
|
54
79
|
const spellingTaken = registerRule('@loomcli/core/spelling-taken', {
|
|
55
|
-
explanation: "The parser reads each spelling as one option, and a Command's own options share one invocation with the global options and every installed plugin's options. A spelling two options claim, a short alias or a generated negative form included, would reach only one of them.",
|
|
80
|
+
explanation: "The parser reads each spelling as one option, and a Command's own options share one invocation with the global options and every installed plugin's options. A spelling two options claim, a short alias, an alias, or a generated negative form included, would reach only one of them.",
|
|
56
81
|
headline: 'Spelling used twice',
|
|
57
82
|
});
|
|
58
83
|
/**
|
|
@@ -60,7 +85,7 @@ const spellingTaken = registerRule('@loomcli/core/spelling-taken', {
|
|
|
60
85
|
* plugin's hook declared.
|
|
61
86
|
*/
|
|
62
87
|
const optionDeclaredTwice = registerRule('@loomcli/core/option-declared-twice', {
|
|
63
|
-
explanation: "An action reads the global options and its Command's own options from one options object, each under its declared name, and the
|
|
88
|
+
explanation: "An action reads the global options and its Command's own options from one options object, each under its declared name, and the global options, the application's and every installed plugin's, share every Command's one table of spellings. Two options with one name in either leave one of them unreadable.",
|
|
64
89
|
headline: 'Option declared twice',
|
|
65
90
|
});
|
|
66
91
|
/**
|
|
@@ -117,6 +142,11 @@ const booleanOptionValueRule = registerRule('@loomcli/core/boolean-option-value-
|
|
|
117
142
|
explanation: 'A Boolean option consumes no value, so there is nothing to validate, and its polarity decides the value an absent option reads. validate, default, required, and validateOmitted belong to inputs that take a value.',
|
|
118
143
|
headline: 'Value rule on a Boolean option',
|
|
119
144
|
});
|
|
145
|
+
/** `validate`, `default`, `required`, or `validateOmitted` on a counted option. */
|
|
146
|
+
const countOptionValueRule = registerRule('@loomcli/core/count-option-value-rule', {
|
|
147
|
+
explanation: 'A counted option consumes no value and reads how many times it was supplied, 0 when nothing supplied it, so there is nothing to validate and no absence to decide. validate, default, required, and validateOmitted belong to inputs that take a value.',
|
|
148
|
+
headline: 'Value rule on a counted option',
|
|
149
|
+
});
|
|
120
150
|
/** A required input that also declares a default. */
|
|
121
151
|
const requiredWithDefault = registerRule('@loomcli/core/required-with-default', {
|
|
122
152
|
explanation: 'A default fills an omitted value, and a required input fails when it is omitted, so a required input never reads its default.',
|
|
@@ -144,9 +174,14 @@ const invalidDefault = registerRule('@loomcli/core/invalid-default', {
|
|
|
144
174
|
explanation: 'Each run passes every declared default through its validator before it reads a token, because a default reaches the action as a validated value. A default the validator rejects would reach no action, whatever the operator supplies.',
|
|
145
175
|
headline: 'Default rejected',
|
|
146
176
|
});
|
|
177
|
+
/** A declared implied value its validator rejected. */
|
|
178
|
+
const invalidImplied = registerRule('@loomcli/core/invalid-implied', {
|
|
179
|
+
explanation: 'Each run passes every implied value through its validator before it reads a token, because a bare spelling supplies it to the action as a validated value. An implied value the validator rejects is the declaration at fault, whatever the operator supplies, so the run reports it whether or not a bare spelling was typed.',
|
|
180
|
+
headline: 'Implied value rejected',
|
|
181
|
+
});
|
|
147
182
|
/** A validator's JSON Schema converter that threw or returned a value that is not a plain object. */
|
|
148
183
|
const schemaConverterFailed = registerRule('@loomcli/core/schema-converter-failed', {
|
|
149
184
|
explanation: "Help, the manifest, and completion read what an input accepts from the JSON Schema its validator publishes. A converter that throws or returns anything but a plain object publishes no shape, so a distributed build reads the input's schema as null.",
|
|
150
185
|
headline: 'Schema converter failed',
|
|
151
186
|
});
|
|
152
|
-
export { booleanOptionMultiple, booleanOptionValueRule, defaultDepth, defaultLevels, defaultShape, envName, envOnArgument, envOnMultiple, flagNotBoolean, globalOptionAfterCommand, globalPresenceRule, invalidDefault, notAValidator, omissionAlreadyDecided, nameSharedAcrossKinds, omissionWithoutValidator, optionDeclaredTwice, optionPolarity, optionType, polarityOnString, requiredWithDefault, schemaConverterFailed, shortAlias, shortOnlyBothPolarities, shortOnlyWithoutShort, spellingTaken, variableBoundTwice, };
|
|
187
|
+
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, };
|