@rohal12/spindle 0.52.4 → 0.52.6
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/pkg/format.js +1 -1
- package/dist/pkg/headless.js +1502 -1521
- package/dist/pkg/macro-registry.json +255 -0
- package/package.json +1 -1
- package/src/class-registry.ts +6 -182
- package/src/components/macros/Button.tsx +2 -4
- package/src/components/macros/Checkbox.tsx +6 -13
- package/src/components/macros/Computed.tsx +38 -55
- package/src/components/macros/Cycle.tsx +1 -0
- package/src/components/macros/Dialog.tsx +7 -8
- package/src/components/macros/Do.tsx +2 -1
- package/src/components/macros/For.tsx +27 -78
- package/src/components/macros/Goto.tsx +6 -9
- package/src/components/macros/Include.tsx +10 -57
- package/src/components/macros/Listbox.tsx +1 -0
- package/src/components/macros/MacroError.tsx +37 -0
- package/src/components/macros/MacroLink.tsx +9 -24
- package/src/components/macros/Meter.tsx +19 -37
- package/src/components/macros/Numberbox.tsx +5 -32
- package/src/components/macros/PassageDisplay.tsx +37 -47
- package/src/components/macros/Print.tsx +1 -0
- package/src/components/macros/Radiobutton.tsx +8 -38
- package/src/components/macros/Repeat.tsx +3 -3
- package/src/components/macros/Set.tsx +3 -29
- package/src/components/macros/Switch.tsx +36 -37
- package/src/components/macros/Textarea.tsx +2 -28
- package/src/components/macros/Textbox.tsx +2 -29
- package/src/components/macros/Type.tsx +3 -3
- package/src/components/macros/Unset.tsx +17 -40
- package/src/components/macros/Watch.tsx +23 -91
- package/src/components/macros/WidgetInvocation.tsx +9 -71
- package/src/components/macros/detached-body.tsx +2 -2
- package/src/components/macros/input-macro.ts +48 -0
- package/src/components/macros/locals-scope.tsx +60 -0
- package/src/components/macros/macro-args.ts +280 -0
- package/src/components/macros/option-utils.ts +7 -12
- package/src/define-macro.ts +47 -30
- package/src/execute-mutation.ts +17 -97
- package/src/hooks/use-render-options.ts +26 -0
- package/src/markup/render.tsx +2 -4
- package/src/registry.ts +48 -0
- package/src/saves/save-manager.ts +190 -259
- package/src/saves/storage.ts +229 -402
- package/src/store.ts +244 -310
- package/src/structural.ts +388 -0
- package/types/index.d.ts +69 -6
- package/types/tooling.d.ts +33 -1
package/src/define-macro.ts
CHANGED
|
@@ -11,12 +11,9 @@ import {
|
|
|
11
11
|
} from 'preact/hooks';
|
|
12
12
|
import { useInterpolate } from './hooks/use-interpolate';
|
|
13
13
|
import { useMergedLocals } from './hooks/use-merged-locals';
|
|
14
|
+
import { useRenderOptions } from './hooks/use-render-options';
|
|
14
15
|
import {
|
|
15
16
|
LocalsUpdateContext,
|
|
16
|
-
LocalsValuesContext,
|
|
17
|
-
NobrContext,
|
|
18
|
-
InlineContext,
|
|
19
|
-
RawTextContext,
|
|
20
17
|
renderNodes as _renderNodes,
|
|
21
18
|
renderInlineNodes,
|
|
22
19
|
} from './markup/render';
|
|
@@ -31,23 +28,34 @@ import type { UseActionOptions } from './hooks/use-action';
|
|
|
31
28
|
import { collectText } from './utils/extract-text';
|
|
32
29
|
import { currentSourceLocation } from './utils/source-location';
|
|
33
30
|
import { parseVarArgs, extractOptions } from './components/macros/option-utils';
|
|
31
|
+
import {
|
|
32
|
+
parseMacroArgs,
|
|
33
|
+
readBoundVariable,
|
|
34
|
+
} from './components/macros/macro-args';
|
|
34
35
|
import {
|
|
35
36
|
registerMacro,
|
|
36
37
|
registerMacroText,
|
|
37
38
|
registerSubMacro,
|
|
38
39
|
registerMacroMetadata,
|
|
39
40
|
} from './registry';
|
|
40
|
-
import type {
|
|
41
|
+
import type {
|
|
42
|
+
MacroArgs,
|
|
43
|
+
MacroProps,
|
|
44
|
+
MacroTextContext,
|
|
45
|
+
ParameterDef,
|
|
46
|
+
} from './registry';
|
|
41
47
|
import { registerBlockMacro } from './markup/ast';
|
|
42
48
|
|
|
43
|
-
export type { MacroTextContext };
|
|
49
|
+
export type { MacroArgs, MacroTextContext, ParameterDef };
|
|
44
50
|
|
|
45
51
|
export function macroClass(type: string, className?: string): string {
|
|
46
52
|
const base = `macro-${type}`;
|
|
47
53
|
return className ? `${base} ${className}` : base;
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
export interface MacroContext {
|
|
56
|
+
export interface MacroContext<A = MacroArgs> {
|
|
57
|
+
/** The arguments, read into the declared parameters (see ParameterDef). */
|
|
58
|
+
args: A;
|
|
51
59
|
className?: string;
|
|
52
60
|
id?: string;
|
|
53
61
|
resolve?: (s: string | undefined) => string | undefined;
|
|
@@ -86,7 +94,9 @@ export interface MacroContext {
|
|
|
86
94
|
};
|
|
87
95
|
}
|
|
88
96
|
|
|
89
|
-
export interface MacroDefinition
|
|
97
|
+
export interface MacroDefinition<
|
|
98
|
+
P extends readonly ParameterDef[] = ParameterDef[],
|
|
99
|
+
> {
|
|
90
100
|
name: string;
|
|
91
101
|
subMacros?: string[];
|
|
92
102
|
block?: boolean;
|
|
@@ -94,14 +104,20 @@ export interface MacroDefinition {
|
|
|
94
104
|
merged?: boolean;
|
|
95
105
|
storeVar?: boolean;
|
|
96
106
|
description?: string;
|
|
97
|
-
parameters?:
|
|
98
|
-
render: (
|
|
107
|
+
parameters?: P;
|
|
108
|
+
render: (
|
|
109
|
+
props: MacroProps,
|
|
110
|
+
ctx: MacroContext<MacroArgs<P>>,
|
|
111
|
+
) => ComponentChildren;
|
|
99
112
|
/**
|
|
100
113
|
* The macro's text form, used where markup becomes a string: HTML
|
|
101
114
|
* attribute values, image alt text and link titles, macro labels. Without
|
|
102
115
|
* one, the macro can't be used there and is reported as an error.
|
|
103
116
|
*/
|
|
104
|
-
text?: (
|
|
117
|
+
text?: (
|
|
118
|
+
props: MacroProps,
|
|
119
|
+
ctx: MacroTextContext & { args: MacroArgs<P> },
|
|
120
|
+
) => string;
|
|
105
121
|
}
|
|
106
122
|
|
|
107
123
|
const sharedHooks = {
|
|
@@ -114,10 +130,12 @@ const sharedHooks = {
|
|
|
114
130
|
useContext,
|
|
115
131
|
};
|
|
116
132
|
|
|
117
|
-
export function defineMacro(
|
|
118
|
-
config: MacroDefinition
|
|
133
|
+
export function defineMacro<const P extends readonly ParameterDef[] = []>(
|
|
134
|
+
config: MacroDefinition<P>,
|
|
119
135
|
source: 'builtin' | 'user' = 'builtin',
|
|
120
136
|
): void {
|
|
137
|
+
const parameters: readonly ParameterDef[] = config.parameters ?? [];
|
|
138
|
+
|
|
121
139
|
function Wrapper(props: MacroProps) {
|
|
122
140
|
// className/id resolved first (interpolate may transform them)
|
|
123
141
|
let className = props.className;
|
|
@@ -131,10 +149,7 @@ export function defineMacro(
|
|
|
131
149
|
|
|
132
150
|
// Always-on: cssClass + mutation
|
|
133
151
|
const { update, getValues } = useContext(LocalsUpdateContext);
|
|
134
|
-
const
|
|
135
|
-
const inline = useContext(InlineContext);
|
|
136
|
-
const raw = useContext(RawTextContext);
|
|
137
|
-
const localsValues = useContext(LocalsValuesContext);
|
|
152
|
+
const renderOptions = useRenderOptions();
|
|
138
153
|
const renderNodes = (
|
|
139
154
|
nodes: ASTNode[],
|
|
140
155
|
options?: {
|
|
@@ -143,15 +158,9 @@ export function defineMacro(
|
|
|
143
158
|
inline?: boolean;
|
|
144
159
|
raw?: boolean;
|
|
145
160
|
},
|
|
146
|
-
) =>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
inline,
|
|
150
|
-
raw,
|
|
151
|
-
locals: localsValues,
|
|
152
|
-
...options,
|
|
153
|
-
});
|
|
154
|
-
const ctx: MacroContext = {
|
|
161
|
+
) => _renderNodes(nodes, { ...renderOptions, ...options });
|
|
162
|
+
const ctx: MacroContext<MacroArgs<P>> = {
|
|
163
|
+
args: parseMacroArgs(props.rawArgs, parameters) as MacroArgs<P>,
|
|
155
164
|
collectText,
|
|
156
165
|
sourceLocation: currentSourceLocation,
|
|
157
166
|
parseVarArgs,
|
|
@@ -183,8 +192,7 @@ export function defineMacro(
|
|
|
183
192
|
}
|
|
184
193
|
|
|
185
194
|
if (config.storeVar) {
|
|
186
|
-
const firstToken =
|
|
187
|
-
props.rawArgs.trim().split(/\s+/)[0]?.replace(/["']/g, '') ?? '';
|
|
195
|
+
const firstToken = readBoundVariable(props.rawArgs).replace(/["']/g, '');
|
|
188
196
|
|
|
189
197
|
if (firstToken.startsWith('%')) {
|
|
190
198
|
return h(
|
|
@@ -220,7 +228,16 @@ export function defineMacro(
|
|
|
220
228
|
}
|
|
221
229
|
|
|
222
230
|
registerMacro(config.name, Wrapper);
|
|
223
|
-
|
|
231
|
+
const text = config.text;
|
|
232
|
+
registerMacroText(
|
|
233
|
+
config.name,
|
|
234
|
+
text &&
|
|
235
|
+
((props, ctx) =>
|
|
236
|
+
text(props, {
|
|
237
|
+
...ctx,
|
|
238
|
+
args: parseMacroArgs(props.rawArgs, parameters) as MacroArgs<P>,
|
|
239
|
+
})),
|
|
240
|
+
);
|
|
224
241
|
|
|
225
242
|
// Store metadata for tooling API
|
|
226
243
|
const isBlock =
|
|
@@ -234,7 +251,7 @@ export function defineMacro(
|
|
|
234
251
|
interpolate: config.interpolate,
|
|
235
252
|
merged: config.merged,
|
|
236
253
|
description: config.description,
|
|
237
|
-
parameters: config.parameters,
|
|
254
|
+
parameters: config.parameters && [...config.parameters],
|
|
238
255
|
source,
|
|
239
256
|
});
|
|
240
257
|
|
package/src/execute-mutation.ts
CHANGED
|
@@ -9,9 +9,18 @@ import {
|
|
|
9
9
|
import { useStoryStore } from './store';
|
|
10
10
|
import type { StoryState, VariableNamespaces } from './store';
|
|
11
11
|
import { execute } from './expression';
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
import {
|
|
13
|
+
applyChange,
|
|
14
|
+
deepClone,
|
|
15
|
+
deepEqual,
|
|
16
|
+
diffPaths,
|
|
17
|
+
isApplied,
|
|
18
|
+
isMergeable,
|
|
19
|
+
mergeKeys,
|
|
20
|
+
type PathChange,
|
|
21
|
+
} from './structural';
|
|
22
|
+
import { getByPath } from './utils/object-path';
|
|
23
|
+
import { asNamespace, hasOwn } from './utils/namespace';
|
|
15
24
|
|
|
16
25
|
type NamespaceName = keyof VariableNamespaces;
|
|
17
26
|
|
|
@@ -21,11 +30,6 @@ const NAMESPACES: readonly NamespaceName[] = [
|
|
|
21
30
|
'transient',
|
|
22
31
|
];
|
|
23
32
|
|
|
24
|
-
/** A write to one property path of a namespace, or its deletion. */
|
|
25
|
-
type PathChange =
|
|
26
|
-
| { path: string[]; deleted: false; value: unknown }
|
|
27
|
-
| { path: string[]; deleted: true };
|
|
28
|
-
|
|
29
33
|
/**
|
|
30
34
|
* The state of one executing mutation. The code reads and writes `work`.
|
|
31
35
|
* `base` is what `work` started as, with every write made elsewhere during
|
|
@@ -76,80 +80,6 @@ export function frozenCopy<T>(value: T): T {
|
|
|
76
80
|
return freeze(cloneValue(value), true);
|
|
77
81
|
}
|
|
78
82
|
|
|
79
|
-
/**
|
|
80
|
-
* Objects merged property by property: plain objects and (registered)
|
|
81
|
-
* class instances. Arrays, Map, Set, Date and RegExp are values that are
|
|
82
|
-
* replaced as a whole, since their elements have no stable identity to
|
|
83
|
-
* merge by (a shift moves every index).
|
|
84
|
-
*/
|
|
85
|
-
export function isMergeable(value: unknown): value is Record<string, unknown> {
|
|
86
|
-
return (
|
|
87
|
-
typeof value === 'object' &&
|
|
88
|
-
value !== null &&
|
|
89
|
-
!Array.isArray(value) &&
|
|
90
|
-
!(value instanceof Map) &&
|
|
91
|
-
!(value instanceof Set) &&
|
|
92
|
-
!(value instanceof Date) &&
|
|
93
|
-
!(value instanceof RegExp)
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const hasOwn = (obj: object, key: string): boolean =>
|
|
98
|
-
Object.prototype.hasOwnProperty.call(obj, key);
|
|
99
|
-
|
|
100
|
-
/** Collect the property paths where `work` differs from `base`. */
|
|
101
|
-
function diff(
|
|
102
|
-
base: Record<string, unknown>,
|
|
103
|
-
work: Record<string, unknown>,
|
|
104
|
-
path: string[],
|
|
105
|
-
changes: PathChange[],
|
|
106
|
-
ancestors: Set<object>,
|
|
107
|
-
): void {
|
|
108
|
-
// Stop at cycles; shared (non-cyclic) references are visited per path.
|
|
109
|
-
if (ancestors.has(work)) return;
|
|
110
|
-
ancestors.add(work);
|
|
111
|
-
for (const key of Object.keys(work)) {
|
|
112
|
-
const b = base[key];
|
|
113
|
-
const w = work[key];
|
|
114
|
-
if (!hasOwn(base, key)) {
|
|
115
|
-
changes.push({ path: [...path, key], deleted: false, value: w });
|
|
116
|
-
} else if (Object.is(b, w)) {
|
|
117
|
-
// The same value (an untouched subtree of an Immer update)
|
|
118
|
-
continue;
|
|
119
|
-
} else if (
|
|
120
|
-
isMergeable(b) &&
|
|
121
|
-
isMergeable(w) &&
|
|
122
|
-
Object.getPrototypeOf(b) === Object.getPrototypeOf(w)
|
|
123
|
-
) {
|
|
124
|
-
diff(b, w, [...path, key], changes, ancestors);
|
|
125
|
-
} else if (!deepEqual(b, w)) {
|
|
126
|
-
changes.push({ path: [...path, key], deleted: false, value: w });
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
for (const key of Object.keys(base)) {
|
|
130
|
-
if (!hasOwn(work, key))
|
|
131
|
-
changes.push({ path: [...path, key], deleted: true });
|
|
132
|
-
}
|
|
133
|
-
ancestors.delete(work);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/** Whether `ns` already holds what `change` would write. */
|
|
137
|
-
function isApplied(ns: Record<string, unknown>, change: PathChange): boolean {
|
|
138
|
-
const parent = getByPath(ns, change.path.slice(0, -1));
|
|
139
|
-
if (parent === null || typeof parent !== 'object') return change.deleted;
|
|
140
|
-
const key = change.path[change.path.length - 1]!;
|
|
141
|
-
if (!hasOwn(parent, key)) return change.deleted;
|
|
142
|
-
return (
|
|
143
|
-
!change.deleted &&
|
|
144
|
-
deepEqual((parent as Record<string, unknown>)[key], change.value)
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function applyChange(ns: Record<string, unknown>, change: PathChange): void {
|
|
149
|
-
if (change.deleted) deleteByPath(ns, change.path);
|
|
150
|
-
else setByPath(ns, change.path, change.value);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
83
|
/**
|
|
154
84
|
* Apply a write that reached the store while mutations execute to their
|
|
155
85
|
* `work` and `base` copies (see MutationScope), so the code sees it and
|
|
@@ -381,11 +311,9 @@ function commitScopes(commits: readonly Commit[]): void {
|
|
|
381
311
|
const all = commits.map(({ scope, keepRunning }) => ({
|
|
382
312
|
scope,
|
|
383
313
|
keepRunning,
|
|
384
|
-
changes: NAMESPACES.map(
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
return [ns, list] as const;
|
|
388
|
-
}),
|
|
314
|
+
changes: NAMESPACES.map(
|
|
315
|
+
(ns) => [ns, diffPaths(scope.base[ns], scope.work[ns])] as const,
|
|
316
|
+
),
|
|
389
317
|
}));
|
|
390
318
|
const changed = all.filter(({ changes }) =>
|
|
391
319
|
changes.some(([, list]) => list.length > 0),
|
|
@@ -450,16 +378,8 @@ const running = (): Commit[] =>
|
|
|
450
378
|
function resync(scope: MutationScope): void {
|
|
451
379
|
const state = useStoryStore.getState();
|
|
452
380
|
for (const ns of NAMESPACES) {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
for (const key of Object.keys(work)) {
|
|
456
|
-
if (!hasOwn(stored, key)) delete work[key];
|
|
457
|
-
}
|
|
458
|
-
for (const key of Object.keys(stored)) {
|
|
459
|
-
if (!hasOwn(work, key) || !deepEqual(work[key], stored[key])) {
|
|
460
|
-
work[key] = deepClone(stored[key]);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
381
|
+
// Root by root: a root that differs is replaced with a copy of the store's
|
|
382
|
+
mergeKeys(scope.work[ns], scope.work[ns], state[ns]);
|
|
463
383
|
}
|
|
464
384
|
scope.base = cloneNamespaces(state);
|
|
465
385
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useContext } from 'preact/hooks';
|
|
2
|
+
import {
|
|
3
|
+
InlineContext,
|
|
4
|
+
LocalsValuesContext,
|
|
5
|
+
NobrContext,
|
|
6
|
+
RawTextContext,
|
|
7
|
+
} from '../markup/render';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The renderNodes() options of the enclosing content: nobr, inline and raw
|
|
11
|
+
* rendering and the locals in scope. A macro or widget body rendered with
|
|
12
|
+
* them renders as the content around it does.
|
|
13
|
+
*/
|
|
14
|
+
export function useRenderOptions(): {
|
|
15
|
+
nobr: boolean;
|
|
16
|
+
inline: boolean;
|
|
17
|
+
raw: boolean;
|
|
18
|
+
locals: Record<string, unknown>;
|
|
19
|
+
} {
|
|
20
|
+
return {
|
|
21
|
+
nobr: useContext(NobrContext),
|
|
22
|
+
inline: useContext(InlineContext),
|
|
23
|
+
raw: useContext(RawTextContext),
|
|
24
|
+
locals: useContext(LocalsValuesContext),
|
|
25
|
+
};
|
|
26
|
+
}
|
package/src/markup/render.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import { markdownToHtml } from './markdown';
|
|
|
9
9
|
import { h } from 'preact';
|
|
10
10
|
import type { ASTNode, HtmlNode, MacroNode } from './ast';
|
|
11
11
|
import { useTextScope } from '../hooks/use-interpolate';
|
|
12
|
+
import { useRenderOptions } from '../hooks/use-render-options';
|
|
12
13
|
import {
|
|
13
14
|
hasInterpolation,
|
|
14
15
|
interpolateCode,
|
|
@@ -524,11 +525,8 @@ function resolveAttributeValue(
|
|
|
524
525
|
|
|
525
526
|
function HtmlNodeRenderer({ node }: { node: HtmlNode }) {
|
|
526
527
|
const scope = useTextScope();
|
|
527
|
-
const nobr =
|
|
528
|
-
const locals = useContext(LocalsValuesContext);
|
|
529
|
-
const inRaw = useContext(RawTextContext);
|
|
528
|
+
const { nobr, locals, raw: inRaw, inline: parentInline } = useRenderOptions();
|
|
530
529
|
const inSvg = useContext(SvgContext);
|
|
531
|
-
const parentInline = useContext(InlineContext);
|
|
532
530
|
const tag = node.tag.toLowerCase();
|
|
533
531
|
const isSvgRoot = tag === 'svg';
|
|
534
532
|
const isRawRoot = !inRaw && (isSvgRoot || PREFORMATTED_ELEMENTS.has(tag));
|
package/src/registry.ts
CHANGED
|
@@ -67,12 +67,60 @@ export function isSubMacro(name: string): boolean {
|
|
|
67
67
|
return subMacros.has(name.toLowerCase());
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* How a macro argument is read (see components/macros/macro-args.ts).
|
|
72
|
+
* Quoted strings accept `\"`, `\'` and `\\` escapes.
|
|
73
|
+
* - `expression`: code, as written (the default).
|
|
74
|
+
* - `variable`: a variable reference such as `$name` or `"$name"`, as written.
|
|
75
|
+
* - `string`: one quoted string; anything else leaves the argument unset.
|
|
76
|
+
* - `text`: one quoted string, or text with any loose quotes stripped.
|
|
77
|
+
* - `names`: a comma-separated list of names (`@item, @i`).
|
|
78
|
+
* - `delay`: a duration (`2s`, `500ms`, `300`) in milliseconds.
|
|
79
|
+
* - `number`: a number.
|
|
80
|
+
* - `flag`: a keyword, the parameter's name, at the start or end; a boolean.
|
|
81
|
+
* - `separator`: a word (`of`) or `=` separating the parameters before it
|
|
82
|
+
* from those after it; a boolean.
|
|
83
|
+
* - `options`: keywords, the names of its `parameters`, each followed by a
|
|
84
|
+
* quoted string or a number unless it is a flag.
|
|
85
|
+
*/
|
|
86
|
+
export type ParameterType =
|
|
87
|
+
| 'expression'
|
|
88
|
+
| 'variable'
|
|
89
|
+
| 'string'
|
|
90
|
+
| 'text'
|
|
91
|
+
| 'names'
|
|
92
|
+
| 'delay'
|
|
93
|
+
| 'number'
|
|
94
|
+
| 'flag'
|
|
95
|
+
| 'separator'
|
|
96
|
+
| 'options';
|
|
97
|
+
|
|
70
98
|
export interface ParameterDef {
|
|
71
99
|
name: string;
|
|
72
100
|
required?: boolean;
|
|
73
101
|
description?: string;
|
|
102
|
+
type?: ParameterType;
|
|
103
|
+
/** The options of an `options` parameter. */
|
|
104
|
+
parameters?: readonly ParameterDef[];
|
|
74
105
|
}
|
|
75
106
|
|
|
107
|
+
type ArgValue<T, D> = T extends 'flag' | 'separator'
|
|
108
|
+
? boolean
|
|
109
|
+
: T extends 'names'
|
|
110
|
+
? string[] | undefined
|
|
111
|
+
: T extends 'delay' | 'number'
|
|
112
|
+
? number | undefined
|
|
113
|
+
: T extends 'options'
|
|
114
|
+
? D extends { parameters: infer Q extends readonly ParameterDef[] }
|
|
115
|
+
? Partial<MacroArgs<Q>>
|
|
116
|
+
: Partial<MacroArgs>
|
|
117
|
+
: string | undefined;
|
|
118
|
+
|
|
119
|
+
/** The arguments of a macro declaring `parameters`, by parameter name. */
|
|
120
|
+
export type MacroArgs<P extends readonly ParameterDef[] = ParameterDef[]> = {
|
|
121
|
+
[D in P[number] as D['name']]: ArgValue<D['type'], D>;
|
|
122
|
+
};
|
|
123
|
+
|
|
76
124
|
export interface MacroMetadata {
|
|
77
125
|
name: string;
|
|
78
126
|
block: boolean;
|