@rohal12/spindle 0.56.0 → 0.58.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/pkg/format.js +1 -1
- package/dist/pkg/headless.js +638 -414
- package/dist/pkg/macro-registry.json +2 -1
- package/dist/pkg/story-variables.js +611 -157
- package/dist/pkg/tooling.js +39 -2
- package/package.json +1 -1
- package/src/code-check.ts +129 -21
- package/src/components/macros/Include.tsx +2 -6
- package/src/components/macros/MacroLink.tsx +33 -7
- package/src/components/macros/arg-utils.ts +37 -0
- package/src/components/macros/include-args.ts +23 -0
- package/src/components/macros/macro-args.ts +61 -5
- package/src/components/macros/option-utils.ts +17 -6
- package/src/define-macro.ts +22 -2
- package/src/expression.ts +4 -54
- package/src/markup/grammar-shared.ts +8 -0
- package/src/markup/parse.ts +89 -2
- package/src/markup/spindle.d.peggy.ts +5 -2
- package/src/markup/spindle.peggy +6 -7
- package/src/markup/validate.ts +16 -3
- package/src/registry.ts +7 -0
- package/src/story-variables.ts +48 -10
- package/src/tooling.ts +59 -0
- package/src/transform.ts +65 -0
- package/types/tooling.d.ts +311 -0
package/types/tooling.d.ts
CHANGED
|
@@ -160,3 +160,314 @@ export interface ValidateMarkupOptions {
|
|
|
160
160
|
* `Passage "Start", line 3, column 5 (story.twee:12): Unclosed {if}: …`.
|
|
161
161
|
*/
|
|
162
162
|
export declare function formatDiagnostic(diagnostic: MarkupDiagnostic): string;
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// Parsing rules
|
|
166
|
+
//
|
|
167
|
+
// The leaf rules Spindle parses passages with, so that editor tooling can use
|
|
168
|
+
// them instead of mirroring them (see docs/tooling.md). They are pure
|
|
169
|
+
// string-to-data functions that do not load the runtime.
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
/** The sigil of a variable reference: story, temporary, local, transient. */
|
|
173
|
+
export type Sigil = '$' | '_' | '@' | '%';
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* What a piece of code is: one expression (a macro argument) or a list of
|
|
177
|
+
* statements (a `{do}` body).
|
|
178
|
+
*/
|
|
179
|
+
export type JsGoal = 'expression' | 'statements';
|
|
180
|
+
|
|
181
|
+
/** What `lexJs` reports, in source order, every character exactly once. */
|
|
182
|
+
export interface JsLexHandlers {
|
|
183
|
+
/**
|
|
184
|
+
* One character of code, outside literals and comments. `nesting` is the
|
|
185
|
+
* number of template-literal `${…}` interpolations around it.
|
|
186
|
+
*/
|
|
187
|
+
code?(ch: string, index: number, nesting: number): void;
|
|
188
|
+
/**
|
|
189
|
+
* Literal text passed through verbatim: a string or regex literal, a
|
|
190
|
+
* comment, or a piece of a template literal.
|
|
191
|
+
*/
|
|
192
|
+
literal?(text: string, index: number, nesting: number): void;
|
|
193
|
+
/** A sigil variable reference (`$name`), covering the sigil and name. */
|
|
194
|
+
variable?(sigil: Sigil, name: string, index: number, nesting: number): void;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Walk `src` as JavaScript, reporting code characters, literal text and
|
|
199
|
+
* variable references to `handlers`. Text that is no JavaScript is read
|
|
200
|
+
* leniently: an unterminated literal runs to the end. Returns `src.length`.
|
|
201
|
+
*/
|
|
202
|
+
export declare function lexJs(
|
|
203
|
+
src: string,
|
|
204
|
+
handlers: JsLexHandlers,
|
|
205
|
+
goal?: JsGoal,
|
|
206
|
+
): number;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Lex the template literal opening at `start` (a backtick) as `lexJs` does,
|
|
210
|
+
* its interpolations one nesting level deeper. Returns the index just past
|
|
211
|
+
* its closing backtick, or `src.length` if it is unterminated.
|
|
212
|
+
*/
|
|
213
|
+
export declare function lexTemplate(
|
|
214
|
+
src: string,
|
|
215
|
+
start: number,
|
|
216
|
+
handlers?: JsLexHandlers,
|
|
217
|
+
nesting?: number,
|
|
218
|
+
): number;
|
|
219
|
+
|
|
220
|
+
export interface FindCodeEndOptions {
|
|
221
|
+
/** What the code is (default `expression`). */
|
|
222
|
+
goal?: JsGoal;
|
|
223
|
+
/** End the code at a `{` in code, at any depth, for which this holds. */
|
|
224
|
+
stop?: (index: number) => boolean;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Where the code starting at `start` ends: the index of the `}` that closes
|
|
229
|
+
* the `{…}` around it (or, with `stop`, of the first `{` for which it holds).
|
|
230
|
+
* Braces and quotes in literals and comments don't count. `-1` when there is
|
|
231
|
+
* no such end, or the code before it can't be JavaScript.
|
|
232
|
+
*/
|
|
233
|
+
export declare function findCodeEnd(
|
|
234
|
+
src: string,
|
|
235
|
+
start: number,
|
|
236
|
+
options?: FindCodeEndOptions,
|
|
237
|
+
): number;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Scan the `"…"` or `'…'` string literal opening at `start`: `end` is just
|
|
241
|
+
* past its closing quote, or `src.length` when unterminated (`closed` false).
|
|
242
|
+
*/
|
|
243
|
+
export declare function scanStringLiteral(
|
|
244
|
+
src: string,
|
|
245
|
+
start: number,
|
|
246
|
+
): { end: number; closed: boolean };
|
|
247
|
+
|
|
248
|
+
/** The namespace a variable reference reads. */
|
|
249
|
+
export type VariableScope = 'variable' | 'temporary' | 'local' | 'transient';
|
|
250
|
+
|
|
251
|
+
/** The scope each variable sigil names. */
|
|
252
|
+
export declare const SIGIL_SCOPES: Readonly<Record<Sigil, VariableScope>>;
|
|
253
|
+
|
|
254
|
+
/** Whether `c` is a variable sigil. */
|
|
255
|
+
export declare function isSigil(c: string | undefined): c is Sigil;
|
|
256
|
+
|
|
257
|
+
/** The `.class#id` selectors written before a link, variable or macro. */
|
|
258
|
+
export interface Selectors {
|
|
259
|
+
className?: string;
|
|
260
|
+
id?: string;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* The selectors that start `source` at `at`, and `end`, the index just past
|
|
265
|
+
* them and the one space that may follow (`at` if there are none). A name
|
|
266
|
+
* may hold `{$name}` interpolations.
|
|
267
|
+
*/
|
|
268
|
+
export declare function parseSelectors(
|
|
269
|
+
source: string,
|
|
270
|
+
at?: number,
|
|
271
|
+
): Selectors & { end: number };
|
|
272
|
+
|
|
273
|
+
/** Where a token is in the input: from `start` up to `end`. */
|
|
274
|
+
interface TokenSpan {
|
|
275
|
+
start: number;
|
|
276
|
+
end: number;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export interface TextToken extends TokenSpan {
|
|
280
|
+
type: 'text';
|
|
281
|
+
value: string;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export interface LinkToken extends TokenSpan, Selectors {
|
|
285
|
+
type: 'link';
|
|
286
|
+
display: string;
|
|
287
|
+
target: string;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export interface MacroToken extends TokenSpan, Selectors {
|
|
291
|
+
type: 'macro';
|
|
292
|
+
name: string;
|
|
293
|
+
rawArgs: string;
|
|
294
|
+
isClose: boolean;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export interface VariableToken extends TokenSpan, Selectors {
|
|
298
|
+
type: 'variable';
|
|
299
|
+
name: string;
|
|
300
|
+
scope: VariableScope;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export interface ExpressionToken extends TokenSpan, Selectors {
|
|
304
|
+
type: 'expression';
|
|
305
|
+
expression: string;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export interface HtmlToken extends TokenSpan {
|
|
309
|
+
type: 'html';
|
|
310
|
+
tag: string;
|
|
311
|
+
attributes: Record<string, string>;
|
|
312
|
+
isClose: boolean;
|
|
313
|
+
isSelfClose: boolean;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** A flat markup token, with its offsets (UTF-16 code units) in the input. */
|
|
317
|
+
export type Token =
|
|
318
|
+
| TextToken
|
|
319
|
+
| LinkToken
|
|
320
|
+
| MacroToken
|
|
321
|
+
| VariableToken
|
|
322
|
+
| ExpressionToken
|
|
323
|
+
| HtmlToken;
|
|
324
|
+
|
|
325
|
+
/** Malformed markup, with where it starts (0-based offset, 1-based line and column). */
|
|
326
|
+
export declare class MarkupError extends Error {
|
|
327
|
+
reason: string;
|
|
328
|
+
offset: number;
|
|
329
|
+
line: number;
|
|
330
|
+
column: number;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export interface ParseMarkupOptions {
|
|
334
|
+
/**
|
|
335
|
+
* Text mode, for markup that becomes a string (HTML attribute values,
|
|
336
|
+
* macro labels): only `{…}` markup and brace escapes are recognized.
|
|
337
|
+
*/
|
|
338
|
+
text?: boolean;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* The flat tokens of markup, without nesting, so unclosed or mismatched
|
|
343
|
+
* macros and elements are no error here. Throws a `MarkupError` for a
|
|
344
|
+
* malformed tag, such as an unclosed `{`, `[[` or attribute value.
|
|
345
|
+
*/
|
|
346
|
+
export declare function tokenizeMarkup(
|
|
347
|
+
source: string,
|
|
348
|
+
options?: ParseMarkupOptions,
|
|
349
|
+
): Token[];
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Split macro arguments at top-level commas (outside strings, templates and
|
|
353
|
+
* brackets); with no comma, adjacent standalone values separated by
|
|
354
|
+
* whitespace (`"Label" "target"`).
|
|
355
|
+
*/
|
|
356
|
+
export declare function splitArgs(raw: string): string[];
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Split `src` at every top-level character for which `isSeparator` holds
|
|
360
|
+
* (code outside literals, comments and brackets). Segments are untrimmed and
|
|
361
|
+
* empty ones are kept.
|
|
362
|
+
*/
|
|
363
|
+
export declare function splitTopLevel(
|
|
364
|
+
src: string,
|
|
365
|
+
isSeparator: (ch: string) => boolean,
|
|
366
|
+
): string[];
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Read the `"…"` or `'…'` string whose opening quote is at `start`: its
|
|
370
|
+
* unescaped value (`\"`, `\'` and `\\` only) and the index just past it, or
|
|
371
|
+
* `null` if there is none or it is unterminated.
|
|
372
|
+
*/
|
|
373
|
+
export declare function readQuoted(
|
|
374
|
+
src: string,
|
|
375
|
+
start: number,
|
|
376
|
+
): { value: string; end: number } | null;
|
|
377
|
+
|
|
378
|
+
/** Undo `\"`, `\'` and `\\` in the body of a quoted macro argument. */
|
|
379
|
+
export declare function unescapeQuoted(body: string): string;
|
|
380
|
+
|
|
381
|
+
/** Strip an optional quote from each end of a loosely quoted argument. */
|
|
382
|
+
export declare function stripLooseQuotes(src: string): string;
|
|
383
|
+
|
|
384
|
+
/** Whether code ends with an operator that still needs an operand. */
|
|
385
|
+
export declare function endsWithOperator(src: string): boolean;
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* The arguments of `{include}`: whether it has the standalone `inline` flag
|
|
389
|
+
* (first or last word, outside quotes and brackets), and the passage
|
|
390
|
+
* expression that is left.
|
|
391
|
+
*/
|
|
392
|
+
export declare function splitIncludeFlag(rawArgs: string): {
|
|
393
|
+
inline: boolean;
|
|
394
|
+
passage: string | undefined;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
/** The tokens of markup that may be malformed, and its errors. */
|
|
398
|
+
export interface TolerantTokens {
|
|
399
|
+
tokens: Token[];
|
|
400
|
+
/** One for each malformed tag, in source order, with offsets in the source. */
|
|
401
|
+
errors: MarkupError[];
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* `tokenizeMarkup` for half-typed markup: the tokens it can read, and every
|
|
406
|
+
* error, instead of throwing at the first. At a malformed tag the tokens
|
|
407
|
+
* before it are kept, its first character is read as text, and tokenizing
|
|
408
|
+
* resumes after it. Offsets are in `source`. For well-formed markup the
|
|
409
|
+
* tokens are those of `tokenizeMarkup` and there are no errors.
|
|
410
|
+
*/
|
|
411
|
+
export declare function tokenizeMarkupTolerant(
|
|
412
|
+
source: string,
|
|
413
|
+
options?: ParseMarkupOptions,
|
|
414
|
+
): TolerantTokens;
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* The code Spindle runs for `expr`: its variable references (`$var`, `_var`,
|
|
418
|
+
* `@var`, `%var`) turned into namespace lookups (`variables["var"]`).
|
|
419
|
+
* String, template and regex literals and comments are untouched. `goal` is
|
|
420
|
+
* `'statements'` for a `{do}` body. Throws a `SyntaxError` for code that is
|
|
421
|
+
* not well-formed, and for a reference to a variable named `__proto__`.
|
|
422
|
+
*/
|
|
423
|
+
export declare function transform(expr: string, goal?: JsGoal): string;
|
|
424
|
+
|
|
425
|
+
/** What a `passage` argument is: a quoted name, or an expression. */
|
|
426
|
+
export type PassageTarget =
|
|
427
|
+
| { kind: 'name'; name: string }
|
|
428
|
+
| { kind: 'expression'; expression: string };
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Read a `passage` argument (`{goto "Hall"}`, `{include $room}`) as written:
|
|
432
|
+
* a quoted string is the name its JavaScript literal has (`"Hall"` is
|
|
433
|
+
* `Hall`), anything else an expression whose value, as a string, is the name
|
|
434
|
+
* when it runs.
|
|
435
|
+
*/
|
|
436
|
+
export declare function passageTarget(arg: string): PassageTarget;
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* The passage a `passage` argument names, as `{goto}`, `{include}` and
|
|
440
|
+
* `{link}` find it: `String(evaluate(expr))`. Throws what `evaluate` throws,
|
|
441
|
+
* and an error naming the current passage if the story has no passage of
|
|
442
|
+
* that name.
|
|
443
|
+
*/
|
|
444
|
+
export declare function evaluatePassageName(
|
|
445
|
+
expr: string | undefined,
|
|
446
|
+
evaluate: (expr: string) => unknown,
|
|
447
|
+
state: {
|
|
448
|
+
storyData: { passages: { has(name: string): boolean } } | null;
|
|
449
|
+
currentPassage: string;
|
|
450
|
+
},
|
|
451
|
+
): string;
|
|
452
|
+
|
|
453
|
+
/** A passage that markup names, and where. */
|
|
454
|
+
export interface PassageReference {
|
|
455
|
+
/** The macro it is the argument of; `link` for `[[…]]` links. */
|
|
456
|
+
macro: string;
|
|
457
|
+
/** The name written out, or the expression whose value is the name. */
|
|
458
|
+
target: PassageTarget;
|
|
459
|
+
/** Where it is written, as written (quotes included): offsets in `source`. */
|
|
460
|
+
start: number;
|
|
461
|
+
end: number;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* The passages the markup of `source` names, in source order: `[[…]]` links,
|
|
466
|
+
* the passage of `{goto}`, `{include}` and `{link}` (and of macros that
|
|
467
|
+
* declare a `passage` argument), the `goto` and `dialog` actions of
|
|
468
|
+
* `{watch}` and the body of `{dialog}`. Malformed tags are skipped, so
|
|
469
|
+
* half-typed markup reads.
|
|
470
|
+
*/
|
|
471
|
+
export declare function collectPassageReferences(
|
|
472
|
+
source: string,
|
|
473
|
+
): PassageReference[];
|