@rohal12/spindle 0.53.0 → 0.55.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/README.md +3 -1
- package/dist/pkg/format.js +1 -1
- package/dist/pkg/headless.js +10058 -2075
- package/dist/pkg/macro-registry.json +4 -4
- package/dist/pkg/story-variables.js +10197 -1925
- package/dist/pkg/tooling.js +15 -1
- package/package.json +3 -1
- package/src/code-check.ts +314 -0
- package/src/components/Passage.tsx +3 -3
- package/src/components/PassageDialog.tsx +2 -4
- package/src/components/StoryInterface.tsx +2 -4
- package/src/components/macros/Do.tsx +1 -1
- package/src/components/macros/Goto.tsx +19 -8
- package/src/components/macros/Include.tsx +23 -11
- package/src/components/macros/Set.tsx +1 -1
- package/src/components/macros/VarDisplay.tsx +1 -1
- package/src/components/macros/Widget.tsx +5 -33
- package/src/components/macros/macro-args.ts +16 -7
- package/src/define-macro.ts +2 -0
- package/src/expression.ts +25 -20
- package/src/index.tsx +63 -37
- package/src/interpolation.ts +4 -5
- package/src/js-lexer.ts +981 -1342
- package/src/markup/ast.ts +11 -226
- package/src/markup/code-attributes.ts +3 -3
- package/src/markup/code-end.ts +121 -0
- package/src/markup/parse.ts +118 -0
- package/src/markup/render.tsx +1 -1
- package/src/markup/spindle.d.peggy.ts +24 -0
- package/src/markup/spindle.peggy +463 -0
- package/src/markup/tokens.ts +95 -0
- package/src/markup/validate.ts +281 -0
- package/src/parser.ts +19 -4
- package/src/registry.ts +49 -14
- package/src/runtime-errors.ts +10 -0
- package/src/store.ts +8 -2
- package/src/story-init.ts +3 -5
- package/src/story-variables.ts +49 -8
- package/src/tooling.ts +58 -0
- package/src/types-drift-check.ts +28 -1
- package/src/widgets/widget-def.ts +60 -0
- package/types/index.d.ts +7 -3
- package/types/tooling.d.ts +53 -3
- package/src/markup/tokenizer.ts +0 -1112
package/src/types-drift-check.ts
CHANGED
|
@@ -21,7 +21,19 @@ import type {
|
|
|
21
21
|
MacroProps as PublishedMacroProps,
|
|
22
22
|
ASTNode as PublishedASTNode,
|
|
23
23
|
} from '../types/index';
|
|
24
|
-
import type {
|
|
24
|
+
import type {
|
|
25
|
+
parseStoryVariables as PublishedParse,
|
|
26
|
+
formatDiagnostic as PublishedFormatDiagnostic,
|
|
27
|
+
MarkupDiagnostic as PublishedMarkupDiagnostic,
|
|
28
|
+
MarkupPassage as PublishedMarkupPassage,
|
|
29
|
+
validateMarkup as PublishedValidateMarkup,
|
|
30
|
+
} from '../types/tooling';
|
|
31
|
+
import type {
|
|
32
|
+
formatDiagnostic as SourceFormatDiagnostic,
|
|
33
|
+
MarkupDiagnostic as SourceMarkupDiagnostic,
|
|
34
|
+
MarkupPassage as SourceMarkupPassage,
|
|
35
|
+
validateStoryMarkup as SourceValidateStoryMarkup,
|
|
36
|
+
} from './tooling';
|
|
25
37
|
import type { bootStory as PublishedBootStory } from '../types/headless';
|
|
26
38
|
|
|
27
39
|
/** What the source declares, by published name. */
|
|
@@ -30,6 +42,11 @@ interface Source {
|
|
|
30
42
|
// Tooling entry point (`@rohal12/spindle/tooling`): the parser that
|
|
31
43
|
// dist/pkg/tooling.js re-exports.
|
|
32
44
|
parseStoryVariables: typeof SourceParse;
|
|
45
|
+
// Its markup validator (pkg/tooling.js binds validateStoryMarkup to the
|
|
46
|
+
// macro metadata, so validateMarkup itself takes only the passages).
|
|
47
|
+
MarkupPassage: SourceMarkupPassage;
|
|
48
|
+
MarkupDiagnostic: SourceMarkupDiagnostic;
|
|
49
|
+
formatDiagnostic: typeof SourceFormatDiagnostic;
|
|
33
50
|
// Custom macro API: the MacroContext/MacroDefinition defineMacro()
|
|
34
51
|
// actually passes and accepts.
|
|
35
52
|
MacroContext: SourceMacroContext;
|
|
@@ -44,6 +61,9 @@ interface Source {
|
|
|
44
61
|
interface Published {
|
|
45
62
|
StoryAPI: PublishedAPI;
|
|
46
63
|
parseStoryVariables: typeof PublishedParse;
|
|
64
|
+
MarkupPassage: PublishedMarkupPassage;
|
|
65
|
+
MarkupDiagnostic: PublishedMarkupDiagnostic;
|
|
66
|
+
formatDiagnostic: typeof PublishedFormatDiagnostic;
|
|
47
67
|
MacroContext: PublishedMacroContext;
|
|
48
68
|
MacroDefinition: PublishedMacroDefinition;
|
|
49
69
|
MacroProps: PublishedMacroProps;
|
|
@@ -90,3 +110,10 @@ const _exampleMacro: PublishedMacroDefinition = {
|
|
|
90
110
|
);
|
|
91
111
|
},
|
|
92
112
|
};
|
|
113
|
+
|
|
114
|
+
// pkg/tooling.js's validateMarkup is validateStoryMarkup bound to the macro
|
|
115
|
+
// metadata.
|
|
116
|
+
declare const validateStoryMarkup: typeof SourceValidateStoryMarkup;
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
118
|
+
const _validateMarkup: typeof PublishedValidateMarkup = (passages) =>
|
|
119
|
+
validateStoryMarkup(passages, []);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading widget definitions without rendering anything: the `{widget}`
|
|
3
|
+
* macro, startup and markup validation (also in tooling) share these.
|
|
4
|
+
*/
|
|
5
|
+
import { parseMacroArgs } from '../components/macros/macro-args';
|
|
6
|
+
import type { MacroArgs } from '../registry';
|
|
7
|
+
|
|
8
|
+
/** A {widget} definition's name, then its `@` parameters. */
|
|
9
|
+
export const WIDGET_PARAMETERS = [
|
|
10
|
+
{ name: 'name', type: 'text', required: true },
|
|
11
|
+
{ name: 'parameters', type: 'text' },
|
|
12
|
+
] as const;
|
|
13
|
+
|
|
14
|
+
export interface WidgetDef {
|
|
15
|
+
name: string;
|
|
16
|
+
params: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The widget a {widget} definition's arguments declare: its name and its
|
|
21
|
+
* parameters, the words after it that start with `@` (docs/widgets.md).
|
|
22
|
+
* Other words, such as `$name`, are not parameters.
|
|
23
|
+
*/
|
|
24
|
+
export function widgetDef({
|
|
25
|
+
name = '',
|
|
26
|
+
parameters = '',
|
|
27
|
+
}: MacroArgs<typeof WIDGET_PARAMETERS>): WidgetDef {
|
|
28
|
+
return {
|
|
29
|
+
name,
|
|
30
|
+
params: parameters.split(/\s+/).filter((word) => word.startsWith('@')),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Read the arguments of a {widget} definition (see widgetDef). */
|
|
35
|
+
export function parseWidgetDef(rawArgs: string): WidgetDef {
|
|
36
|
+
return widgetDef(parseMacroArgs(rawArgs, WIDGET_PARAMETERS));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const BLOCK_WIDGET = /\{widget\s+["']?(\w+)["']?[^}]*\}([\s\S]*?)\{\/widget\}/g;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The names of the block widgets (those whose body renders `{@children}`)
|
|
43
|
+
* that StoryInit and the passages tagged `widget` define. They must be
|
|
44
|
+
* known as block macros before any passage is parsed, so that passages
|
|
45
|
+
* invoking them nest their content whatever the passage order.
|
|
46
|
+
*/
|
|
47
|
+
export function blockWidgetNames(
|
|
48
|
+
passages: Iterable<{ name: string; tags?: string[]; content: string }>,
|
|
49
|
+
): string[] {
|
|
50
|
+
const names: string[] = [];
|
|
51
|
+
for (const passage of passages) {
|
|
52
|
+
if (passage.name !== 'StoryInit' && !passage.tags?.includes('widget')) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
for (const match of passage.content.matchAll(BLOCK_WIDGET)) {
|
|
56
|
+
if (/\{@children\}/.test(match[2]!)) names.push(match[1]!);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return names;
|
|
60
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -242,7 +242,9 @@ export interface StorageQuota {
|
|
|
242
242
|
/**
|
|
243
243
|
* How a macro argument is read into `ctx.args`. Quoted strings accept `\"`,
|
|
244
244
|
* `\'` and `\\` escapes.
|
|
245
|
-
* - `expression`: code, as written
|
|
245
|
+
* - `expression`: code, as written.
|
|
246
|
+
* - `statements`: code run as statements (`{set}`), as written.
|
|
247
|
+
* - `passage`: a passage name: a quoted string or an expression, as written.
|
|
246
248
|
* - `variable`: a variable reference such as `$name` or `"$name"`, as written.
|
|
247
249
|
* - `string`: one quoted string; anything else leaves the argument unset.
|
|
248
250
|
* - `text`: one quoted string, or text with any loose quotes stripped.
|
|
@@ -258,6 +260,8 @@ export interface StorageQuota {
|
|
|
258
260
|
*/
|
|
259
261
|
export type ParameterType =
|
|
260
262
|
| 'expression'
|
|
263
|
+
| 'statements'
|
|
264
|
+
| 'passage'
|
|
261
265
|
| 'variable'
|
|
262
266
|
| 'string'
|
|
263
267
|
| 'text'
|
|
@@ -276,8 +280,8 @@ export interface ParameterDef {
|
|
|
276
280
|
name: string;
|
|
277
281
|
required?: boolean;
|
|
278
282
|
description?: string;
|
|
279
|
-
/** How the argument is read
|
|
280
|
-
type
|
|
283
|
+
/** How the argument is read: required, there is no default. */
|
|
284
|
+
type: ParameterType;
|
|
281
285
|
/** The options of an `options` parameter. */
|
|
282
286
|
parameters?: readonly ParameterDef[];
|
|
283
287
|
}
|
package/types/tooling.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* How a macro argument is read into `ctx.args`. Quoted strings accept `\"`,
|
|
3
3
|
* `\'` and `\\` escapes.
|
|
4
|
-
* - `expression`: code, as written
|
|
4
|
+
* - `expression`: code, as written.
|
|
5
|
+
* - `statements`: code run as statements (`{set}`), as written.
|
|
6
|
+
* - `passage`: a passage name: a quoted string or an expression, as written.
|
|
5
7
|
* - `variable`: a variable reference such as `$name` or `"$name"`, as written.
|
|
6
8
|
* - `string`: one quoted string; anything else leaves the argument unset.
|
|
7
9
|
* - `text`: one quoted string, or text with any loose quotes stripped.
|
|
@@ -16,6 +18,8 @@
|
|
|
16
18
|
*/
|
|
17
19
|
export type ParameterType =
|
|
18
20
|
| 'expression'
|
|
21
|
+
| 'statements'
|
|
22
|
+
| 'passage'
|
|
19
23
|
| 'variable'
|
|
20
24
|
| 'string'
|
|
21
25
|
| 'text'
|
|
@@ -30,8 +34,8 @@ export interface ParameterDef {
|
|
|
30
34
|
name: string;
|
|
31
35
|
required?: boolean;
|
|
32
36
|
description?: string;
|
|
33
|
-
/** How the argument is read
|
|
34
|
-
type
|
|
37
|
+
/** How the argument is read: required, there is no default. */
|
|
38
|
+
type: ParameterType;
|
|
35
39
|
/** The options of an `options` parameter. */
|
|
36
40
|
parameters?: readonly ParameterDef[];
|
|
37
41
|
}
|
|
@@ -99,3 +103,49 @@ export declare function parseStoryVariables(
|
|
|
99
103
|
content: string,
|
|
100
104
|
sigil?: '$' | '%',
|
|
101
105
|
): Map<string, VariableSchema>;
|
|
106
|
+
|
|
107
|
+
/** A passage to validate. */
|
|
108
|
+
export interface MarkupPassage {
|
|
109
|
+
name: string;
|
|
110
|
+
content: string;
|
|
111
|
+
tags?: string[];
|
|
112
|
+
/**
|
|
113
|
+
* Passage attributes; `data-source-file` and `data-source-line` (the
|
|
114
|
+
* line of its `::` header) place errors in the source file.
|
|
115
|
+
*/
|
|
116
|
+
metadata?: Record<string, string>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** An error in a passage's markup. */
|
|
120
|
+
export interface MarkupDiagnostic {
|
|
121
|
+
passage: string;
|
|
122
|
+
/** 1-based line within the passage's content. */
|
|
123
|
+
line: number;
|
|
124
|
+
/** 1-based column (UTF-16 code units). */
|
|
125
|
+
column: number;
|
|
126
|
+
message: string;
|
|
127
|
+
/** The source file and line, when the passage says where it came from. */
|
|
128
|
+
file?: string;
|
|
129
|
+
fileLine?: number;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Validate the markup of a story's passages as Spindle does when the story
|
|
134
|
+
* starts: malformed markup (unclosed or mismatched macros, tags, links,
|
|
135
|
+
* braces and attribute values), unknown macros and syntax errors in the
|
|
136
|
+
* code passages run (`{$…}` expressions, `{do}` bodies, conditions and the
|
|
137
|
+
* `expression`/`statements`/`passage` arguments of macros), checked against
|
|
138
|
+
* the built-in macros, those registered with `defineMacro` and the widgets
|
|
139
|
+
* the passages define, and passage names that name none of `passages`
|
|
140
|
+
* (links, quoted `passage` arguments, `{link}`, `{dialog}`, `{watch}`).
|
|
141
|
+
* Spindle refuses to start a story with any of these.
|
|
142
|
+
*/
|
|
143
|
+
export declare function validateMarkup(
|
|
144
|
+
passages: Iterable<MarkupPassage>,
|
|
145
|
+
): MarkupDiagnostic[];
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* A diagnostic as one line of text, as Spindle shows it:
|
|
149
|
+
* `Passage "Start", line 3, column 5 (story.twee:12): Unclosed {if}: …`.
|
|
150
|
+
*/
|
|
151
|
+
export declare function formatDiagnostic(diagnostic: MarkupDiagnostic): string;
|