@loomcli/core 0.2.0 → 0.4.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 +59 -34
- package/dist/application.js +162 -59
- package/dist/chain.d.ts +25 -6
- package/dist/chain.js +99 -15
- package/dist/command.d.ts +146 -42
- package/dist/command.js +642 -78
- package/dist/environment.d.ts +22 -0
- package/dist/environment.js +1 -0
- package/dist/errors.d.ts +31 -59
- package/dist/errors.js +49 -106
- package/dist/extension.d.ts +80 -20
- package/dist/extension.js +115 -30
- package/dist/globals.d.ts +14 -25
- package/dist/globals.js +10 -61
- package/dist/glyphs.generated.d.ts +464 -0
- package/dist/glyphs.generated.js +491 -0
- package/dist/host.js +2 -1
- package/dist/index.d.ts +15 -6
- package/dist/index.js +5 -2
- package/dist/inspect.d.ts +38 -4
- package/dist/inspect.js +69 -6
- package/dist/lanes.d.ts +26 -0
- package/dist/lanes.js +45 -0
- package/dist/output.d.ts +93 -15
- package/dist/output.js +307 -34
- package/dist/plugin.d.ts +32 -16
- package/dist/plugin.js +46 -18
- package/dist/rendering.d.ts +21 -0
- package/dist/rendering.js +72 -0
- package/dist/sequence.d.ts +41 -0
- package/dist/sequence.js +225 -0
- package/dist/style-ansi.d.ts +13 -0
- package/dist/style-ansi.js +306 -0
- package/dist/style-layout.d.ts +29 -0
- package/dist/style-layout.js +228 -0
- package/dist/style-resolve.d.ts +6 -0
- package/dist/style-resolve.js +26 -0
- package/dist/style-state.d.ts +14 -0
- package/dist/style-state.js +179 -0
- package/dist/style-wire.d.ts +31 -0
- package/dist/style-wire.js +201 -0
- package/dist/style.d.ts +86 -0
- package/dist/style.js +201 -0
- package/dist/theme.d.ts +3 -0
- package/dist/theme.js +22 -0
- package/dist/types.d.ts +174 -21
- package/dist/validation.d.ts +8 -1
- package/dist/validation.js +17 -2
- package/dist/view.d.ts +180 -0
- package/dist/view.js +307 -0
- package/package.json +2 -1
package/dist/extension.js
CHANGED
|
@@ -19,16 +19,15 @@ class ExtensionCarrier {
|
|
|
19
19
|
Object.freeze(this);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
* One typed fact a plugin defines for one target. The descriptor is compared by reference wherever
|
|
24
|
-
* it appears, so one identity means one descriptor and a duplicated package copy is visible.
|
|
25
|
-
*/
|
|
26
22
|
function extension(identity, config) {
|
|
27
23
|
// `Object.assign` returns the same function object, so the value carries the descriptor itself.
|
|
28
24
|
function create(input) {
|
|
29
25
|
return new ExtensionCarrier({ descriptor, input });
|
|
30
26
|
}
|
|
27
|
+
// An omitted or undefined `collect` publishes as false, and any other value as given.
|
|
28
|
+
// Build then rejects a JavaScript author's value that is neither Boolean.
|
|
31
29
|
const descriptor = Object.assign(create, {
|
|
30
|
+
collect: config.collect === undefined ? false : config.collect,
|
|
32
31
|
identity,
|
|
33
32
|
schema: config.schema,
|
|
34
33
|
target: config.target,
|
|
@@ -36,26 +35,31 @@ function extension(identity, config) {
|
|
|
36
35
|
Object.freeze(descriptor);
|
|
37
36
|
return descriptor;
|
|
38
37
|
}
|
|
38
|
+
/** The read of a collecting extension a declaration carries no value of, shared and frozen. */
|
|
39
|
+
const noValues = Object.freeze([]);
|
|
39
40
|
/**
|
|
40
41
|
* The typed read of one extension value. It takes the node kind the descriptor targets, returns the
|
|
41
42
|
* stored output or `undefined`, compares the descriptor by reference with the one that produced the
|
|
42
|
-
* value, and runs no schema.
|
|
43
|
+
* value, and runs no schema. Through a collecting descriptor it returns every collected output in
|
|
44
|
+
* collection order, and an empty list where the node carries none.
|
|
43
45
|
*/
|
|
44
46
|
function readExtension(node, descriptor) {
|
|
45
47
|
const record = node.extensions;
|
|
46
48
|
const owner = owners.get(record)?.get(descriptor.identity);
|
|
47
|
-
if (owner
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
if (owner !== descriptor) {
|
|
49
|
+
if (owner !== undefined && owner !== descriptor) {
|
|
51
50
|
throw new DeclarationError(`Extension "${descriptor.identity}" was read through a descriptor that did not define the stored value. Install one copy of the package that defines it.`);
|
|
52
51
|
}
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
//
|
|
52
|
+
// A collecting extension a declaration carries no value of reads as an empty list.
|
|
53
|
+
const absent = descriptor.collect ? noValues : undefined;
|
|
54
|
+
const stored = owner === undefined ? absent : record[descriptor.identity];
|
|
55
|
+
// Last resort: no typed path exists.
|
|
56
|
+
// The graph stores every output under a string identity, so the record reads back as `unknown`.
|
|
57
|
+
// No key relates one entry to a descriptor's schema or to its runtime `collect` flag.
|
|
58
|
+
// It holds because the reference test above proves this descriptor produced this entry.
|
|
59
|
+
// Build stored exactly what its schema returned, a list when the descriptor collects.
|
|
60
|
+
// `Collect` is the literal `extension()` published as `collect`, which registration enforces.
|
|
57
61
|
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
58
|
-
return
|
|
62
|
+
return stored;
|
|
59
63
|
}
|
|
60
64
|
/** How a diagnostic names the declarations one target covers. */
|
|
61
65
|
const applies = {
|
|
@@ -172,7 +176,10 @@ function plainData(value) {
|
|
|
172
176
|
}
|
|
173
177
|
}
|
|
174
178
|
}
|
|
175
|
-
/**
|
|
179
|
+
/**
|
|
180
|
+
* Whether a value has a descriptor's shape: a callable object carrying an identity and a declared
|
|
181
|
+
* target. Its `collect` flag is checked where a build registers it.
|
|
182
|
+
*/
|
|
176
183
|
function isDescriptor(value) {
|
|
177
184
|
return (value !== null &&
|
|
178
185
|
(typeof value === 'object' || typeof value === 'function') &&
|
|
@@ -181,16 +188,37 @@ function isDescriptor(value) {
|
|
|
181
188
|
'target' in value &&
|
|
182
189
|
(value.target === 'argument' || value.target === 'command' || value.target === 'option'));
|
|
183
190
|
}
|
|
184
|
-
/**
|
|
185
|
-
function
|
|
191
|
+
/** Whether a descriptor carries the `collect` flag every descriptor publishes, as a Boolean. */
|
|
192
|
+
function hasCollectFlag(descriptor) {
|
|
193
|
+
return 'collect' in descriptor && typeof descriptor.collect === 'boolean';
|
|
194
|
+
}
|
|
195
|
+
/** Whether one registered descriptor collects, so its values accumulate instead of replacing. */
|
|
196
|
+
function collects(descriptor) {
|
|
197
|
+
return descriptor.collect;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The descriptor a registry holds for one identity once this one is admitted. One identity means
|
|
201
|
+
* one descriptor, wherever on the graph that descriptor appears, and a descriptor is checked when
|
|
202
|
+
* a build first meets it: its `collect` flag is `true` or `false`, which the factory always
|
|
203
|
+
* publishes and a hand-built descriptor may not. It reads the registry and changes nothing.
|
|
204
|
+
*/
|
|
205
|
+
function admitDescriptor(descriptors, descriptor) {
|
|
186
206
|
const known = descriptors.get(descriptor.identity);
|
|
187
207
|
if (known === undefined) {
|
|
188
|
-
|
|
189
|
-
|
|
208
|
+
if (!hasCollectFlag(descriptor)) {
|
|
209
|
+
throw new DeclarationError(`Extension "${descriptor.identity}" declares collect that is not a Boolean. Supply true or false, or build the descriptor with extension(identity, config).`);
|
|
210
|
+
}
|
|
211
|
+
return descriptor;
|
|
190
212
|
}
|
|
191
213
|
if (known !== descriptor) {
|
|
192
214
|
throw new DeclarationError(`Extension "${descriptor.identity}" is defined twice. Install one copy of the package that defines it.`);
|
|
193
215
|
}
|
|
216
|
+
return known;
|
|
217
|
+
}
|
|
218
|
+
/** Admits one descriptor and records it, so a later descriptor of its identity is compared to it. */
|
|
219
|
+
function registerDescriptor(descriptors, descriptor) {
|
|
220
|
+
const admitted = admitDescriptor(descriptors, descriptor);
|
|
221
|
+
descriptors.set(admitted.identity, admitted);
|
|
194
222
|
}
|
|
195
223
|
/** Whether a value answers the Standard Schema v1 contract this build calls synchronously. */
|
|
196
224
|
function isSchema(value) {
|
|
@@ -286,28 +314,85 @@ function carriedValue(subject, target, entry) {
|
|
|
286
314
|
return carried;
|
|
287
315
|
}
|
|
288
316
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* the record, so a typed read compares by reference without the graph carrying a reference.
|
|
317
|
+
* One layer's values, each validated once, synchronously, in authoring order. A layer holds at
|
|
318
|
+
* most one value of an extension, collecting or not, so a second one is a declaration fault.
|
|
292
319
|
*/
|
|
293
|
-
function
|
|
320
|
+
function validateLayer(slot) {
|
|
294
321
|
const { subject } = slot;
|
|
295
|
-
const
|
|
296
|
-
const
|
|
322
|
+
const layer = [];
|
|
323
|
+
const seen = new Set();
|
|
324
|
+
// The layer's descriptors register together once every value is valid.
|
|
325
|
+
// A rejected layer, such as a hook's `extend()` call the hook catches, leaves the registry as it was.
|
|
326
|
+
const staged = new Map(slot.descriptors);
|
|
297
327
|
for (const entry of readList(subject, slot.declared)) {
|
|
298
328
|
const carried = carriedValue(subject, slot.target, entry);
|
|
299
329
|
const { descriptor } = carried;
|
|
300
|
-
registerDescriptor(
|
|
301
|
-
if (
|
|
330
|
+
registerDescriptor(staged, descriptor);
|
|
331
|
+
if (seen.has(descriptor.identity)) {
|
|
302
332
|
throw new DeclarationError(`${subject.sentence} holds extension "${descriptor.identity}" twice. Supply one value.`);
|
|
303
333
|
}
|
|
304
|
-
|
|
305
|
-
|
|
334
|
+
seen.add(descriptor.identity);
|
|
335
|
+
layer.push({ descriptor, output: validateValue(subject, carried) });
|
|
336
|
+
}
|
|
337
|
+
for (const [identity, descriptor] of staged) {
|
|
338
|
+
slot.descriptors.set(identity, descriptor);
|
|
339
|
+
}
|
|
340
|
+
return layer;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* The record each store published, so a store a hook left unchanged publishes the same record on
|
|
344
|
+
* every read and build does no second walk of it.
|
|
345
|
+
*/
|
|
346
|
+
const published = new WeakMap();
|
|
347
|
+
/** The store of a declaration that carries no value yet. */
|
|
348
|
+
const emptyStore = { entries: new Map() };
|
|
349
|
+
/**
|
|
350
|
+
* The store with one more validated layer. An ordinary value replaces the earlier one in place, so
|
|
351
|
+
* a key keeps its first position, and a collecting value joins the values before it.
|
|
352
|
+
*/
|
|
353
|
+
function extendStore(store, layer) {
|
|
354
|
+
const entries = new Map(store.entries);
|
|
355
|
+
for (const { descriptor, output } of layer) {
|
|
356
|
+
const earlier = entries.get(descriptor.identity)?.outputs ?? [];
|
|
357
|
+
const outputs = collects(descriptor) ? [...earlier, output] : [output];
|
|
358
|
+
entries.set(descriptor.identity, { descriptor, outputs });
|
|
359
|
+
}
|
|
360
|
+
return { entries };
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* The frozen record a store publishes: each ordinary extension's output, and each collecting
|
|
364
|
+
* extension's frozen list of outputs, under its identity. The descriptors that produced them are
|
|
365
|
+
* recorded beside the record, so a typed read compares by reference without the graph carrying a
|
|
366
|
+
* reference.
|
|
367
|
+
*/
|
|
368
|
+
function publishStore(store) {
|
|
369
|
+
const cached = published.get(store);
|
|
370
|
+
if (cached) {
|
|
371
|
+
return cached;
|
|
372
|
+
}
|
|
373
|
+
const stored = [];
|
|
374
|
+
const defined = new Map();
|
|
375
|
+
for (const [identity, { descriptor, outputs }] of store.entries) {
|
|
376
|
+
stored.push([identity, collects(descriptor) ? Object.freeze([...outputs]) : outputs[0]]);
|
|
377
|
+
defined.set(identity, descriptor);
|
|
306
378
|
}
|
|
307
379
|
// `Object.fromEntries` defines each identity as an own data property, so an identity of
|
|
308
380
|
// `__proto__` is a key of the record and the record keeps `Object.prototype`.
|
|
309
381
|
const frozen = Object.freeze(Object.fromEntries(stored));
|
|
310
382
|
owners.set(frozen, defined);
|
|
383
|
+
published.set(store, frozen);
|
|
311
384
|
return frozen;
|
|
312
385
|
}
|
|
313
|
-
|
|
386
|
+
/** The frozen record of one `extensions` slot, which is one layer. */
|
|
387
|
+
function buildExtensions(slot) {
|
|
388
|
+
return publishStore(extendStore(emptyStore, validateLayer(slot)));
|
|
389
|
+
}
|
|
390
|
+
/** A Command's author layers, validated in authoring order into the store its hooks extend. */
|
|
391
|
+
function storeCommandLayers(slot) {
|
|
392
|
+
let store = emptyStore;
|
|
393
|
+
for (const declared of slot.layers) {
|
|
394
|
+
store = extendStore(store, validateLayer({ ...slot, declared, target: 'command' }));
|
|
395
|
+
}
|
|
396
|
+
return store;
|
|
397
|
+
}
|
|
398
|
+
export { buildExtensions, extendStore, extension, isDescriptor, publishStore, readExtension, registerDescriptor, storeCommandLayers, validateLayer, };
|
package/dist/globals.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { DeclarationError } from './errors.js';
|
|
2
2
|
import { compileOptions } from './options.js';
|
|
3
3
|
import type { BuiltPlugin, PluginBuild } from './plugin.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { OptionConfig, OptionValue } from './types.js';
|
|
5
5
|
import type { InputDeclaration, OptionInput, ValidatedInputs } from './validation.js';
|
|
6
|
-
/** Phantom key. It keeps the declared option types exact and holds no runtime value. */
|
|
7
|
-
declare const declaredTypes: unique symbol;
|
|
8
6
|
/**
|
|
9
7
|
* Who declared one option that shares the globals table, or the Command that declares a local
|
|
10
8
|
* option colliding with it. Every collision sentence is derived from a pair of these, so the
|
|
@@ -36,33 +34,24 @@ declare function spellingCollision(spelling: string, first: OptionSite, second:
|
|
|
36
34
|
* validated and never reaches an action.
|
|
37
35
|
*/
|
|
38
36
|
interface BuiltGlobals {
|
|
37
|
+
bind: (values: ValidatedInputs) => unknown;
|
|
39
38
|
inputs: readonly InputDeclaration[];
|
|
40
39
|
names: ReadonlyMap<string, OptionOwner>;
|
|
41
40
|
options: ReturnType<typeof compileOptions>;
|
|
42
41
|
plugins: readonly BuiltPlugin[];
|
|
43
|
-
source: unknown;
|
|
44
42
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
option<const Name extends string, const Config extends OptionConfig>(name: Name, config: Config & NameConstraint<Name> & NoInfer<DefaultConstraint<Config>> & NoInfer<MultipleConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): GlobalOptions<Options & Record<Name, OptionValue<Config>>>;
|
|
43
|
+
/** The Application's private global declarations and their schema-derived value binder. */
|
|
44
|
+
interface GlobalsState<Globals = unknown> {
|
|
45
|
+
bind: (values: ValidatedInputs) => Globals;
|
|
46
|
+
inputs: readonly OptionInput[];
|
|
50
47
|
}
|
|
48
|
+
declare function emptyGlobals(): GlobalsState<{}>;
|
|
49
|
+
declare function declareGlobalOption<Globals, Name extends string, Config extends OptionConfig>(state: GlobalsState<Globals>, input: OptionInput<Name, Config>): GlobalsState<Globals & Record<Name, OptionValue<Config>>>;
|
|
51
50
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
51
|
+
* The one table the pre-scan reads: the application's global options in authoring order, then each
|
|
52
|
+
* installed plugin's options in installation order. Every collision between the two scopes, by key
|
|
53
|
+
* or by spelling, is reported here, so the pre-scan meets a table with one owner per name.
|
|
55
54
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
* registry is the test, because `option()` returns a new declaration of its own and the exported
|
|
60
|
-
* constructor is only the first of them.
|
|
61
|
-
*/
|
|
62
|
-
declare function isGlobalOptions(value: unknown): boolean;
|
|
63
|
-
/** Absent globals compile to an empty table whose source is `undefined`, like the declarations. */
|
|
64
|
-
declare function buildGlobals(globals: object | undefined, plugins: readonly BuiltPlugin[], build: PluginBuild): BuiltGlobals;
|
|
65
|
-
declare function bindGlobals<Options>(globals: GlobalOptions<Options> | undefined, values: ValidatedInputs): Options;
|
|
66
|
-
declare const GlobalOptions: new () => GlobalOptions;
|
|
67
|
-
export type { BuiltGlobals, OptionOwner, OptionSite };
|
|
68
|
-
export { bindGlobals, buildGlobals, GlobalOptions, isGlobalOptions, keyCollision, spellingCollision, };
|
|
55
|
+
declare function buildGlobals(node: GlobalsState, plugins: readonly BuiltPlugin[], build: PluginBuild): BuiltGlobals;
|
|
56
|
+
export type { BuiltGlobals, GlobalsState, OptionOwner, OptionSite };
|
|
57
|
+
export { buildGlobals, declareGlobalOption, emptyGlobals, keyCollision, spellingCollision };
|
package/dist/globals.js
CHANGED
|
@@ -2,7 +2,6 @@ import { DeclarationError } from './errors.js';
|
|
|
2
2
|
import { buildExtensions } from './extension.js';
|
|
3
3
|
import { checkDeprecated, checkDescription, checkHidden } from './facts.js';
|
|
4
4
|
import { compileOptions } from './options.js';
|
|
5
|
-
import { captureConfig } from './validation.js';
|
|
6
5
|
const globalSubject = 'the global options';
|
|
7
6
|
/** A collision sentence names a plugin first, then the application's globals, then a local. */
|
|
8
7
|
const ranks = {
|
|
@@ -57,51 +56,21 @@ function spellingCollision(spelling, first, second) {
|
|
|
57
56
|
const [leading, trailing] = ordered(first, second);
|
|
58
57
|
return new DeclarationError(`Option spelling "${spelling}" is used by ${usedBy(leading)} and ${usedBy(trailing)}. Change one declaration.`);
|
|
59
58
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class GlobalOptionsBuilder {
|
|
63
|
-
#bind;
|
|
64
|
-
#inputs;
|
|
65
|
-
constructor(inputs, bind) {
|
|
66
|
-
this.#bind = bind;
|
|
67
|
-
this.#inputs = inputs;
|
|
68
|
-
nodes.set(this, { bind, inputs, source: this });
|
|
69
|
-
}
|
|
70
|
-
option(name, config) {
|
|
71
|
-
const input = {
|
|
72
|
-
config: captureConfig(config),
|
|
73
|
-
kind: 'option',
|
|
74
|
-
name,
|
|
75
|
-
};
|
|
76
|
-
const previous = this.#bind;
|
|
77
|
-
return new GlobalOptionsBuilder([...this.#inputs, input], (values) => ({
|
|
78
|
-
...previous(values),
|
|
79
|
-
...values.option(input),
|
|
80
|
-
}));
|
|
81
|
-
}
|
|
59
|
+
function emptyGlobals() {
|
|
60
|
+
return { bind: () => ({}), inputs: [] };
|
|
82
61
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
return node;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Whether a value is an authored GlobalOptions declaration, whichever call produced it. The
|
|
93
|
-
* registry is the test, because `option()` returns a new declaration of its own and the exported
|
|
94
|
-
* constructor is only the first of them.
|
|
95
|
-
*/
|
|
96
|
-
function isGlobalOptions(value) {
|
|
97
|
-
return typeof value === 'object' && value !== null && nodes.has(value);
|
|
62
|
+
function declareGlobalOption(state, input) {
|
|
63
|
+
return {
|
|
64
|
+
bind: (values) => ({ ...state.bind(values), ...values.option(input) }),
|
|
65
|
+
inputs: [...state.inputs, input],
|
|
66
|
+
};
|
|
98
67
|
}
|
|
99
68
|
/**
|
|
100
69
|
* The one table the pre-scan reads: the application's global options in authoring order, then each
|
|
101
70
|
* installed plugin's options in installation order. Every collision between the two scopes, by key
|
|
102
71
|
* or by spelling, is reported here, so the pre-scan meets a table with one owner per name.
|
|
103
72
|
*/
|
|
104
|
-
function
|
|
73
|
+
function buildGlobals(node, plugins, build) {
|
|
105
74
|
const names = new Map();
|
|
106
75
|
const application = { kind: 'application' };
|
|
107
76
|
// A global option belongs to the application, not to one Command, so its facts read that way.
|
|
@@ -125,7 +94,7 @@ function compileTable(node, plugins, build) {
|
|
|
125
94
|
options,
|
|
126
95
|
});
|
|
127
96
|
});
|
|
128
|
-
return { inputs: node.inputs, names, options, plugins
|
|
97
|
+
return { bind: node.bind, inputs: node.inputs, names, options, plugins };
|
|
129
98
|
}
|
|
130
99
|
/** One plugin's options joining the table the application's globals already hold. */
|
|
131
100
|
function join(owner, inputs, table) {
|
|
@@ -145,24 +114,4 @@ function join(owner, inputs, table) {
|
|
|
145
114
|
options.set(spelling, option);
|
|
146
115
|
}
|
|
147
116
|
}
|
|
148
|
-
|
|
149
|
-
function buildGlobals(globals, plugins, build) {
|
|
150
|
-
const node = globals === undefined ? { bind: () => ({}), inputs: [], source: undefined } : nodeOf(globals);
|
|
151
|
-
return compileTable(node, plugins, build);
|
|
152
|
-
}
|
|
153
|
-
function bindGlobals(globals, values) {
|
|
154
|
-
const bound = globals === undefined ? {} : nodeOf(globals).bind(values);
|
|
155
|
-
// Last resort: no typed path exists. The public GlobalOptions type hides its declarations.
|
|
156
|
-
// The registry is the only bridge from a value to its binder, and a WeakMap cannot carry the
|
|
157
|
-
// Options type of its key. It holds because the registered binder belongs to this value alone.
|
|
158
|
-
// Its record composes exactly the declarations that its Options type records.
|
|
159
|
-
// A declaration without globals publishes `Options = {}`, and the empty record is exactly that.
|
|
160
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
161
|
-
return bound;
|
|
162
|
-
}
|
|
163
|
-
const GlobalOptions = class extends GlobalOptionsBuilder {
|
|
164
|
-
constructor() {
|
|
165
|
-
super([], () => ({}));
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
export { bindGlobals, buildGlobals, GlobalOptions, isGlobalOptions, keyCollision, spellingCollision, };
|
|
117
|
+
export { buildGlobals, declareGlobalOption, emptyGlobals, keyCollision, spellingCollision };
|