@rohal12/twee-ts 1.1.1
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 +86 -0
- package/dist/bin/twee-ts.js +2247 -0
- package/dist/bin/twee-ts.js.map +1 -0
- package/dist/chunk-QPA7RLK5.js +1783 -0
- package/dist/chunk-QPA7RLK5.js.map +1 -0
- package/dist/index.cjs +2212 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +352 -0
- package/dist/index.d.ts +352 -0
- package/dist/index.js +2160 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/rollup.d.ts +23 -0
- package/dist/plugins/rollup.js +34 -0
- package/dist/plugins/rollup.js.map +1 -0
- package/dist/plugins/vite.d.ts +44 -0
- package/dist/plugins/vite.js +46 -0
- package/dist/plugins/vite.js.map +1 -0
- package/dist/types-B3qtPxs8.d.ts +43 -0
- package/package.json +70 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All public interfaces for twee-ts.
|
|
3
|
+
*/
|
|
4
|
+
type OutputMode = 'html' | 'twee3' | 'twee1' | 'twine2-archive' | 'twine1-archive' | 'json';
|
|
5
|
+
type InlineSource = {
|
|
6
|
+
filename: string;
|
|
7
|
+
content: string | Buffer;
|
|
8
|
+
};
|
|
9
|
+
type SourceInput = string | InlineSource;
|
|
10
|
+
interface CompileOptions {
|
|
11
|
+
/** Files, directories, or inline sources to compile. */
|
|
12
|
+
sources: SourceInput[];
|
|
13
|
+
/** Output mode. Default: 'html'. */
|
|
14
|
+
outputMode?: OutputMode;
|
|
15
|
+
/** Story format directory ID (e.g. 'sugarcube-2'). */
|
|
16
|
+
formatId?: string;
|
|
17
|
+
/** Name of the starting passage. Default: 'Start'. */
|
|
18
|
+
startPassage?: string;
|
|
19
|
+
/** Extra directories to search for story formats. */
|
|
20
|
+
formatPaths?: string[];
|
|
21
|
+
/** Also search TWEEGO_PATH env for formats. Default: true. */
|
|
22
|
+
useTweegoPath?: boolean;
|
|
23
|
+
/** Module files to inject into <head>. */
|
|
24
|
+
modules?: string[];
|
|
25
|
+
/** Raw HTML file to append to <head>. */
|
|
26
|
+
headFile?: string;
|
|
27
|
+
/** Trim passage whitespace. Default: true. */
|
|
28
|
+
trim?: boolean;
|
|
29
|
+
/** Twee2 compatibility mode. Default: false. */
|
|
30
|
+
twee2Compat?: boolean;
|
|
31
|
+
/** Enable debug/test mode option. Default: false. */
|
|
32
|
+
testMode?: boolean;
|
|
33
|
+
/** URLs to SFA-compatible index.json files for remote format lookup. */
|
|
34
|
+
formatIndices?: string[];
|
|
35
|
+
/** Direct URLs to format.js files. */
|
|
36
|
+
formatUrls?: string[];
|
|
37
|
+
/** Disable remote format fetching. Default: false. */
|
|
38
|
+
noRemote?: boolean;
|
|
39
|
+
/** Map alias tags to canonical special tags (e.g. { library: 'script' }). */
|
|
40
|
+
tagAliases?: Record<string, string>;
|
|
41
|
+
}
|
|
42
|
+
interface CompileToFileOptions extends CompileOptions {
|
|
43
|
+
/** Output file path. */
|
|
44
|
+
outFile: string;
|
|
45
|
+
}
|
|
46
|
+
interface WatchOptions extends CompileToFileOptions {
|
|
47
|
+
/** Called after each successful rebuild. */
|
|
48
|
+
onBuild?: (result: CompileResult) => void;
|
|
49
|
+
/** Called on build errors. */
|
|
50
|
+
onError?: (error: Error) => void;
|
|
51
|
+
}
|
|
52
|
+
interface Diagnostic {
|
|
53
|
+
level: 'warning' | 'error';
|
|
54
|
+
message: string;
|
|
55
|
+
file?: string;
|
|
56
|
+
line?: number;
|
|
57
|
+
}
|
|
58
|
+
interface CompileResult {
|
|
59
|
+
/** The compiled output string (HTML, Twee, JSON, etc.). */
|
|
60
|
+
output: string;
|
|
61
|
+
/** The parsed story model. */
|
|
62
|
+
story: Story;
|
|
63
|
+
/** The format used for compilation (undefined for non-HTML modes). */
|
|
64
|
+
format?: StoryFormatInfo;
|
|
65
|
+
/** Collected diagnostics. */
|
|
66
|
+
diagnostics: Diagnostic[];
|
|
67
|
+
/** Compilation statistics. */
|
|
68
|
+
stats: CompileStats;
|
|
69
|
+
}
|
|
70
|
+
interface CompileStats {
|
|
71
|
+
passages: number;
|
|
72
|
+
storyPassages: number;
|
|
73
|
+
words: number;
|
|
74
|
+
files: string[];
|
|
75
|
+
}
|
|
76
|
+
interface PassageMetadata {
|
|
77
|
+
position?: string;
|
|
78
|
+
size?: string;
|
|
79
|
+
}
|
|
80
|
+
interface Passage {
|
|
81
|
+
name: string;
|
|
82
|
+
tags: string[];
|
|
83
|
+
text: string;
|
|
84
|
+
metadata?: PassageMetadata;
|
|
85
|
+
}
|
|
86
|
+
interface Twine1Metadata {
|
|
87
|
+
settings: Map<string, string>;
|
|
88
|
+
}
|
|
89
|
+
interface Twine2Metadata {
|
|
90
|
+
format: string;
|
|
91
|
+
formatVersion: string;
|
|
92
|
+
options: Map<string, boolean>;
|
|
93
|
+
start: string;
|
|
94
|
+
tagColors: Map<string, string>;
|
|
95
|
+
zoom: number;
|
|
96
|
+
}
|
|
97
|
+
interface Story {
|
|
98
|
+
name: string;
|
|
99
|
+
ifid: string;
|
|
100
|
+
passages: Passage[];
|
|
101
|
+
legacyIFID: string;
|
|
102
|
+
twine1: Twine1Metadata;
|
|
103
|
+
twine2: Twine2Metadata;
|
|
104
|
+
}
|
|
105
|
+
interface StoryFormatInfo {
|
|
106
|
+
id: string;
|
|
107
|
+
filename: string;
|
|
108
|
+
isTwine2: boolean;
|
|
109
|
+
name: string;
|
|
110
|
+
version: string;
|
|
111
|
+
proofing: boolean;
|
|
112
|
+
}
|
|
113
|
+
interface Twine2FormatJSON {
|
|
114
|
+
name: string;
|
|
115
|
+
version: string;
|
|
116
|
+
proofing?: boolean;
|
|
117
|
+
source: string;
|
|
118
|
+
}
|
|
119
|
+
declare const enum ItemType {
|
|
120
|
+
Error = 0,
|
|
121
|
+
EOF = 1,
|
|
122
|
+
Header = 2,
|
|
123
|
+
Name = 3,
|
|
124
|
+
Tags = 4,
|
|
125
|
+
Metadata = 5,
|
|
126
|
+
Content = 6
|
|
127
|
+
}
|
|
128
|
+
interface LexerItem {
|
|
129
|
+
type: ItemType;
|
|
130
|
+
line: number;
|
|
131
|
+
pos: number;
|
|
132
|
+
val: string;
|
|
133
|
+
}
|
|
134
|
+
interface SFAIndexEntry {
|
|
135
|
+
name: string;
|
|
136
|
+
version: string;
|
|
137
|
+
proofing: boolean;
|
|
138
|
+
files: string[];
|
|
139
|
+
checksums: Record<string, string>;
|
|
140
|
+
}
|
|
141
|
+
interface SFAIndex {
|
|
142
|
+
twine1: SFAIndexEntry[];
|
|
143
|
+
twine2: SFAIndexEntry[];
|
|
144
|
+
}
|
|
145
|
+
interface TweeTsConfig {
|
|
146
|
+
sources?: string[];
|
|
147
|
+
output?: string;
|
|
148
|
+
outputMode?: OutputMode;
|
|
149
|
+
formatId?: string;
|
|
150
|
+
startPassage?: string;
|
|
151
|
+
formatPaths?: string[];
|
|
152
|
+
formatIndices?: string[];
|
|
153
|
+
formatUrls?: string[];
|
|
154
|
+
useTweegoPath?: boolean;
|
|
155
|
+
modules?: string[];
|
|
156
|
+
headFile?: string;
|
|
157
|
+
trim?: boolean;
|
|
158
|
+
twee2Compat?: boolean;
|
|
159
|
+
testMode?: boolean;
|
|
160
|
+
noRemote?: boolean;
|
|
161
|
+
tagAliases?: Record<string, string>;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
declare class TweeTsError extends Error {
|
|
165
|
+
diagnostics: Diagnostic[];
|
|
166
|
+
constructor(message: string, diagnostics?: Diagnostic[]);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Compile Twee sources to HTML, Twee, or JSON.
|
|
170
|
+
*/
|
|
171
|
+
declare function compile(options: CompileOptions): Promise<CompileResult>;
|
|
172
|
+
/**
|
|
173
|
+
* Compile and write to a file.
|
|
174
|
+
*/
|
|
175
|
+
declare function compileToFile(options: CompileToFileOptions): Promise<CompileResult>;
|
|
176
|
+
/**
|
|
177
|
+
* Watch for file changes and recompile.
|
|
178
|
+
*/
|
|
179
|
+
declare function watch(options: WatchOptions): Promise<AbortController>;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Story inspection utilities for unit testing.
|
|
183
|
+
*
|
|
184
|
+
* Extracts structural information from a compiled story:
|
|
185
|
+
* passage names, tags, link graph, broken links, dead ends, orphans.
|
|
186
|
+
*
|
|
187
|
+
* Usage:
|
|
188
|
+
* const result = await compile({ sources: ['./story'] });
|
|
189
|
+
* const map = storyInspect(result.story);
|
|
190
|
+
* expect(map.passages).toContain('Start');
|
|
191
|
+
* expect(map.brokenLinks).toHaveLength(0);
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
interface StoryMap {
|
|
195
|
+
/** All passage names, in source order. */
|
|
196
|
+
passages: string[];
|
|
197
|
+
/** Only story passages (excludes StoryData, StoryTitle, scripts, stylesheets, etc.). */
|
|
198
|
+
storyPassages: string[];
|
|
199
|
+
/** Only info/special passages. */
|
|
200
|
+
infoPassages: string[];
|
|
201
|
+
/** All unique tags used across the story, sorted alphabetically. */
|
|
202
|
+
tags: string[];
|
|
203
|
+
/** Map of tag → passage names that carry that tag. */
|
|
204
|
+
passagesByTag: Map<string, string[]>;
|
|
205
|
+
/** Map of passage name → tags on that passage. */
|
|
206
|
+
tagsByPassage: Map<string, string[]>;
|
|
207
|
+
/**
|
|
208
|
+
* Map of passage name → passage names it links to.
|
|
209
|
+
* Parses `[[target]]`, `[[display->target]]`, `[[display|target]]`,
|
|
210
|
+
* and SugarCube's `<<goto "target">>` / `<<link "display" "target">>`.
|
|
211
|
+
*/
|
|
212
|
+
links: Map<string, string[]>;
|
|
213
|
+
/** Broken links: `{ from, to }` pairs where `to` doesn't exist as a passage. */
|
|
214
|
+
brokenLinks: BrokenLink[];
|
|
215
|
+
/** Story passages with no outgoing links (potential dead ends). */
|
|
216
|
+
deadEnds: string[];
|
|
217
|
+
/** Story passages that no other passage links to (excluding the start passage). */
|
|
218
|
+
orphans: string[];
|
|
219
|
+
/** The configured start passage name, if any. */
|
|
220
|
+
start: string;
|
|
221
|
+
}
|
|
222
|
+
interface BrokenLink {
|
|
223
|
+
from: string;
|
|
224
|
+
to: string;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Inspect a story and return its full structural map.
|
|
228
|
+
*
|
|
229
|
+
* Designed for story authors to use in unit tests:
|
|
230
|
+
* ```ts
|
|
231
|
+
* const result = await compile({ sources: ['./story'] });
|
|
232
|
+
* const map = storyInspect(result.story);
|
|
233
|
+
*
|
|
234
|
+
* // Assert structure
|
|
235
|
+
* expect(map.passages).toContain('Start');
|
|
236
|
+
* expect(map.tags).toContain('location');
|
|
237
|
+
* expect(map.passagesByTag.get('location')).toContain('Kitchen');
|
|
238
|
+
* expect(map.brokenLinks).toEqual([]);
|
|
239
|
+
* expect(map.deadEnds).not.toContain('Start');
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
declare function storyInspect(story: Story): StoryMap;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Passage helpers and output methods.
|
|
246
|
+
* Ported from passage.go / passagedata.go.
|
|
247
|
+
*/
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Apply tag aliases: for each passage carrying an alias tag, add the canonical
|
|
251
|
+
* tag if not already present. Idempotent — safe to call multiple times.
|
|
252
|
+
*/
|
|
253
|
+
declare function applyTagAliases(passages: Passage[], aliases: Record<string, string>): void;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Twee 3 state-machine lexer implemented as a synchronous generator.
|
|
257
|
+
* Ported from tweelexer.go — Go channels become `yield`.
|
|
258
|
+
*/
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Twee 3 lexer — yields LexerItem tokens from Twee source text.
|
|
262
|
+
*
|
|
263
|
+
* In the Go reference, this uses goroutines and channels.
|
|
264
|
+
* Here we use a synchronous generator that accumulates items per state step.
|
|
265
|
+
*/
|
|
266
|
+
declare function tweeLexer(input: string): Generator<LexerItem, void, undefined>;
|
|
267
|
+
/** Convenience class wrapper for the lexer generator. */
|
|
268
|
+
declare class TweeLexer {
|
|
269
|
+
private gen;
|
|
270
|
+
constructor(input: string);
|
|
271
|
+
nextItem(): {
|
|
272
|
+
item: LexerItem;
|
|
273
|
+
done: boolean;
|
|
274
|
+
};
|
|
275
|
+
[Symbol.iterator](): Generator<LexerItem, void, undefined>;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Parse lexer items into Passage[].
|
|
280
|
+
* Ported from storyload.go:loadTwee().
|
|
281
|
+
*/
|
|
282
|
+
|
|
283
|
+
interface ParseOptions {
|
|
284
|
+
/** Filename for diagnostics. */
|
|
285
|
+
filename?: string;
|
|
286
|
+
/** Trim whitespace from passage content. Default: true. */
|
|
287
|
+
trim?: boolean;
|
|
288
|
+
/** Enable Twee2 compatibility. Default: false. */
|
|
289
|
+
twee2Compat?: boolean;
|
|
290
|
+
}
|
|
291
|
+
interface ParseResult {
|
|
292
|
+
passages: Passage[];
|
|
293
|
+
diagnostics: Diagnostic[];
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Parse Twee source text into passages.
|
|
297
|
+
*/
|
|
298
|
+
declare function parseTwee(source: string, options?: ParseOptions): ParseResult;
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Parse the Twine 2 format.js JSON chunk.
|
|
302
|
+
* Handles Harlowe's malformed JSON by stripping the "setup" function property.
|
|
303
|
+
*/
|
|
304
|
+
declare function parseFormatJSON(source: string, formatId: string): Twine2FormatJSON | null;
|
|
305
|
+
/**
|
|
306
|
+
* Discover all story formats in the given search directories.
|
|
307
|
+
*/
|
|
308
|
+
declare function discoverFormats(searchDirs: string[]): Map<string, StoryFormatInfo>;
|
|
309
|
+
/**
|
|
310
|
+
* Get the default format search directories.
|
|
311
|
+
*/
|
|
312
|
+
declare function getFormatSearchDirs(extraPaths?: string[], useTweegoPath?: boolean): string[];
|
|
313
|
+
/** Parse a simple semver string into [major, minor, patch]. */
|
|
314
|
+
declare function parseSemver(v: string): [number, number, number] | null;
|
|
315
|
+
declare function semverCompare(a: [number, number, number], b: [number, number, number]): number;
|
|
316
|
+
|
|
317
|
+
/** Generate a new IFID (UUID v4, uppercase). */
|
|
318
|
+
declare function generateIFID(): string;
|
|
319
|
+
/**
|
|
320
|
+
* Validate an IFID string.
|
|
321
|
+
* Accepts both bare UUIDs and UUID://...// wrapped format.
|
|
322
|
+
* Returns null if valid, or an error message string.
|
|
323
|
+
*/
|
|
324
|
+
declare function validateIFID(ifid: string): string | null;
|
|
325
|
+
|
|
326
|
+
declare const CONFIG_FILENAME = "twee-ts.config.json";
|
|
327
|
+
/** Load a config file from the given directory (default: cwd). Returns null if not found. */
|
|
328
|
+
declare function loadConfig(dir?: string): TweeTsConfig | null;
|
|
329
|
+
/** Load a config from a specific file path. */
|
|
330
|
+
declare function loadConfigFile(filePath: string): TweeTsConfig;
|
|
331
|
+
/** Validate a config object. Returns an array of error strings (empty = valid). */
|
|
332
|
+
declare function validateConfig(data: unknown): string[];
|
|
333
|
+
/** Return a default config JSON string for --init scaffolding. */
|
|
334
|
+
declare function scaffoldConfig(): string;
|
|
335
|
+
|
|
336
|
+
/** Get the cache directory for downloaded story formats. */
|
|
337
|
+
declare function getCacheDir(): string;
|
|
338
|
+
/** Download a format, verify its checksum, write to cache, and return StoryFormatInfo. */
|
|
339
|
+
declare function fetchAndCacheFormat(entry: SFAIndexEntry, downloadUrl: string): Promise<StoryFormatInfo>;
|
|
340
|
+
/** Download a direct format.js URL, parse its JSON, cache it, and return StoryFormatInfo. */
|
|
341
|
+
declare function fetchDirectFormat(url: string): Promise<StoryFormatInfo>;
|
|
342
|
+
/**
|
|
343
|
+
* Try to resolve a remote story format by name and version.
|
|
344
|
+
* 1. Try direct format URLs
|
|
345
|
+
* 2. Try custom index URLs
|
|
346
|
+
* 3. Try default SFA indices
|
|
347
|
+
*/
|
|
348
|
+
declare function resolveRemoteFormat(name: string, version: string, indices?: string[], urls?: string[]): Promise<StoryFormatInfo | undefined>;
|
|
349
|
+
/** Discover all cached remote formats. Returns a Map like discoverFormats(). */
|
|
350
|
+
declare function discoverCachedFormats(): Map<string, StoryFormatInfo>;
|
|
351
|
+
|
|
352
|
+
export { type BrokenLink, CONFIG_FILENAME, type CompileOptions, type CompileResult, type CompileStats, type CompileToFileOptions, type Diagnostic, type InlineSource, ItemType, type LexerItem, type OutputMode, type Passage, type PassageMetadata, type SFAIndex, type SFAIndexEntry, type SourceInput, type Story, type StoryFormatInfo, type StoryMap, TweeLexer, type TweeTsConfig, TweeTsError, type WatchOptions, applyTagAliases, compile, compileToFile, discoverCachedFormats, discoverFormats, fetchAndCacheFormat, fetchDirectFormat, generateIFID, getCacheDir, getFormatSearchDirs, loadConfig, loadConfigFile, parseFormatJSON, parseSemver, parseTwee, resolveRemoteFormat, scaffoldConfig, semverCompare, storyInspect, tweeLexer, validateConfig, validateIFID, watch };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All public interfaces for twee-ts.
|
|
3
|
+
*/
|
|
4
|
+
type OutputMode = 'html' | 'twee3' | 'twee1' | 'twine2-archive' | 'twine1-archive' | 'json';
|
|
5
|
+
type InlineSource = {
|
|
6
|
+
filename: string;
|
|
7
|
+
content: string | Buffer;
|
|
8
|
+
};
|
|
9
|
+
type SourceInput = string | InlineSource;
|
|
10
|
+
interface CompileOptions {
|
|
11
|
+
/** Files, directories, or inline sources to compile. */
|
|
12
|
+
sources: SourceInput[];
|
|
13
|
+
/** Output mode. Default: 'html'. */
|
|
14
|
+
outputMode?: OutputMode;
|
|
15
|
+
/** Story format directory ID (e.g. 'sugarcube-2'). */
|
|
16
|
+
formatId?: string;
|
|
17
|
+
/** Name of the starting passage. Default: 'Start'. */
|
|
18
|
+
startPassage?: string;
|
|
19
|
+
/** Extra directories to search for story formats. */
|
|
20
|
+
formatPaths?: string[];
|
|
21
|
+
/** Also search TWEEGO_PATH env for formats. Default: true. */
|
|
22
|
+
useTweegoPath?: boolean;
|
|
23
|
+
/** Module files to inject into <head>. */
|
|
24
|
+
modules?: string[];
|
|
25
|
+
/** Raw HTML file to append to <head>. */
|
|
26
|
+
headFile?: string;
|
|
27
|
+
/** Trim passage whitespace. Default: true. */
|
|
28
|
+
trim?: boolean;
|
|
29
|
+
/** Twee2 compatibility mode. Default: false. */
|
|
30
|
+
twee2Compat?: boolean;
|
|
31
|
+
/** Enable debug/test mode option. Default: false. */
|
|
32
|
+
testMode?: boolean;
|
|
33
|
+
/** URLs to SFA-compatible index.json files for remote format lookup. */
|
|
34
|
+
formatIndices?: string[];
|
|
35
|
+
/** Direct URLs to format.js files. */
|
|
36
|
+
formatUrls?: string[];
|
|
37
|
+
/** Disable remote format fetching. Default: false. */
|
|
38
|
+
noRemote?: boolean;
|
|
39
|
+
/** Map alias tags to canonical special tags (e.g. { library: 'script' }). */
|
|
40
|
+
tagAliases?: Record<string, string>;
|
|
41
|
+
}
|
|
42
|
+
interface CompileToFileOptions extends CompileOptions {
|
|
43
|
+
/** Output file path. */
|
|
44
|
+
outFile: string;
|
|
45
|
+
}
|
|
46
|
+
interface WatchOptions extends CompileToFileOptions {
|
|
47
|
+
/** Called after each successful rebuild. */
|
|
48
|
+
onBuild?: (result: CompileResult) => void;
|
|
49
|
+
/** Called on build errors. */
|
|
50
|
+
onError?: (error: Error) => void;
|
|
51
|
+
}
|
|
52
|
+
interface Diagnostic {
|
|
53
|
+
level: 'warning' | 'error';
|
|
54
|
+
message: string;
|
|
55
|
+
file?: string;
|
|
56
|
+
line?: number;
|
|
57
|
+
}
|
|
58
|
+
interface CompileResult {
|
|
59
|
+
/** The compiled output string (HTML, Twee, JSON, etc.). */
|
|
60
|
+
output: string;
|
|
61
|
+
/** The parsed story model. */
|
|
62
|
+
story: Story;
|
|
63
|
+
/** The format used for compilation (undefined for non-HTML modes). */
|
|
64
|
+
format?: StoryFormatInfo;
|
|
65
|
+
/** Collected diagnostics. */
|
|
66
|
+
diagnostics: Diagnostic[];
|
|
67
|
+
/** Compilation statistics. */
|
|
68
|
+
stats: CompileStats;
|
|
69
|
+
}
|
|
70
|
+
interface CompileStats {
|
|
71
|
+
passages: number;
|
|
72
|
+
storyPassages: number;
|
|
73
|
+
words: number;
|
|
74
|
+
files: string[];
|
|
75
|
+
}
|
|
76
|
+
interface PassageMetadata {
|
|
77
|
+
position?: string;
|
|
78
|
+
size?: string;
|
|
79
|
+
}
|
|
80
|
+
interface Passage {
|
|
81
|
+
name: string;
|
|
82
|
+
tags: string[];
|
|
83
|
+
text: string;
|
|
84
|
+
metadata?: PassageMetadata;
|
|
85
|
+
}
|
|
86
|
+
interface Twine1Metadata {
|
|
87
|
+
settings: Map<string, string>;
|
|
88
|
+
}
|
|
89
|
+
interface Twine2Metadata {
|
|
90
|
+
format: string;
|
|
91
|
+
formatVersion: string;
|
|
92
|
+
options: Map<string, boolean>;
|
|
93
|
+
start: string;
|
|
94
|
+
tagColors: Map<string, string>;
|
|
95
|
+
zoom: number;
|
|
96
|
+
}
|
|
97
|
+
interface Story {
|
|
98
|
+
name: string;
|
|
99
|
+
ifid: string;
|
|
100
|
+
passages: Passage[];
|
|
101
|
+
legacyIFID: string;
|
|
102
|
+
twine1: Twine1Metadata;
|
|
103
|
+
twine2: Twine2Metadata;
|
|
104
|
+
}
|
|
105
|
+
interface StoryFormatInfo {
|
|
106
|
+
id: string;
|
|
107
|
+
filename: string;
|
|
108
|
+
isTwine2: boolean;
|
|
109
|
+
name: string;
|
|
110
|
+
version: string;
|
|
111
|
+
proofing: boolean;
|
|
112
|
+
}
|
|
113
|
+
interface Twine2FormatJSON {
|
|
114
|
+
name: string;
|
|
115
|
+
version: string;
|
|
116
|
+
proofing?: boolean;
|
|
117
|
+
source: string;
|
|
118
|
+
}
|
|
119
|
+
declare const enum ItemType {
|
|
120
|
+
Error = 0,
|
|
121
|
+
EOF = 1,
|
|
122
|
+
Header = 2,
|
|
123
|
+
Name = 3,
|
|
124
|
+
Tags = 4,
|
|
125
|
+
Metadata = 5,
|
|
126
|
+
Content = 6
|
|
127
|
+
}
|
|
128
|
+
interface LexerItem {
|
|
129
|
+
type: ItemType;
|
|
130
|
+
line: number;
|
|
131
|
+
pos: number;
|
|
132
|
+
val: string;
|
|
133
|
+
}
|
|
134
|
+
interface SFAIndexEntry {
|
|
135
|
+
name: string;
|
|
136
|
+
version: string;
|
|
137
|
+
proofing: boolean;
|
|
138
|
+
files: string[];
|
|
139
|
+
checksums: Record<string, string>;
|
|
140
|
+
}
|
|
141
|
+
interface SFAIndex {
|
|
142
|
+
twine1: SFAIndexEntry[];
|
|
143
|
+
twine2: SFAIndexEntry[];
|
|
144
|
+
}
|
|
145
|
+
interface TweeTsConfig {
|
|
146
|
+
sources?: string[];
|
|
147
|
+
output?: string;
|
|
148
|
+
outputMode?: OutputMode;
|
|
149
|
+
formatId?: string;
|
|
150
|
+
startPassage?: string;
|
|
151
|
+
formatPaths?: string[];
|
|
152
|
+
formatIndices?: string[];
|
|
153
|
+
formatUrls?: string[];
|
|
154
|
+
useTweegoPath?: boolean;
|
|
155
|
+
modules?: string[];
|
|
156
|
+
headFile?: string;
|
|
157
|
+
trim?: boolean;
|
|
158
|
+
twee2Compat?: boolean;
|
|
159
|
+
testMode?: boolean;
|
|
160
|
+
noRemote?: boolean;
|
|
161
|
+
tagAliases?: Record<string, string>;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
declare class TweeTsError extends Error {
|
|
165
|
+
diagnostics: Diagnostic[];
|
|
166
|
+
constructor(message: string, diagnostics?: Diagnostic[]);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Compile Twee sources to HTML, Twee, or JSON.
|
|
170
|
+
*/
|
|
171
|
+
declare function compile(options: CompileOptions): Promise<CompileResult>;
|
|
172
|
+
/**
|
|
173
|
+
* Compile and write to a file.
|
|
174
|
+
*/
|
|
175
|
+
declare function compileToFile(options: CompileToFileOptions): Promise<CompileResult>;
|
|
176
|
+
/**
|
|
177
|
+
* Watch for file changes and recompile.
|
|
178
|
+
*/
|
|
179
|
+
declare function watch(options: WatchOptions): Promise<AbortController>;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Story inspection utilities for unit testing.
|
|
183
|
+
*
|
|
184
|
+
* Extracts structural information from a compiled story:
|
|
185
|
+
* passage names, tags, link graph, broken links, dead ends, orphans.
|
|
186
|
+
*
|
|
187
|
+
* Usage:
|
|
188
|
+
* const result = await compile({ sources: ['./story'] });
|
|
189
|
+
* const map = storyInspect(result.story);
|
|
190
|
+
* expect(map.passages).toContain('Start');
|
|
191
|
+
* expect(map.brokenLinks).toHaveLength(0);
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
interface StoryMap {
|
|
195
|
+
/** All passage names, in source order. */
|
|
196
|
+
passages: string[];
|
|
197
|
+
/** Only story passages (excludes StoryData, StoryTitle, scripts, stylesheets, etc.). */
|
|
198
|
+
storyPassages: string[];
|
|
199
|
+
/** Only info/special passages. */
|
|
200
|
+
infoPassages: string[];
|
|
201
|
+
/** All unique tags used across the story, sorted alphabetically. */
|
|
202
|
+
tags: string[];
|
|
203
|
+
/** Map of tag → passage names that carry that tag. */
|
|
204
|
+
passagesByTag: Map<string, string[]>;
|
|
205
|
+
/** Map of passage name → tags on that passage. */
|
|
206
|
+
tagsByPassage: Map<string, string[]>;
|
|
207
|
+
/**
|
|
208
|
+
* Map of passage name → passage names it links to.
|
|
209
|
+
* Parses `[[target]]`, `[[display->target]]`, `[[display|target]]`,
|
|
210
|
+
* and SugarCube's `<<goto "target">>` / `<<link "display" "target">>`.
|
|
211
|
+
*/
|
|
212
|
+
links: Map<string, string[]>;
|
|
213
|
+
/** Broken links: `{ from, to }` pairs where `to` doesn't exist as a passage. */
|
|
214
|
+
brokenLinks: BrokenLink[];
|
|
215
|
+
/** Story passages with no outgoing links (potential dead ends). */
|
|
216
|
+
deadEnds: string[];
|
|
217
|
+
/** Story passages that no other passage links to (excluding the start passage). */
|
|
218
|
+
orphans: string[];
|
|
219
|
+
/** The configured start passage name, if any. */
|
|
220
|
+
start: string;
|
|
221
|
+
}
|
|
222
|
+
interface BrokenLink {
|
|
223
|
+
from: string;
|
|
224
|
+
to: string;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Inspect a story and return its full structural map.
|
|
228
|
+
*
|
|
229
|
+
* Designed for story authors to use in unit tests:
|
|
230
|
+
* ```ts
|
|
231
|
+
* const result = await compile({ sources: ['./story'] });
|
|
232
|
+
* const map = storyInspect(result.story);
|
|
233
|
+
*
|
|
234
|
+
* // Assert structure
|
|
235
|
+
* expect(map.passages).toContain('Start');
|
|
236
|
+
* expect(map.tags).toContain('location');
|
|
237
|
+
* expect(map.passagesByTag.get('location')).toContain('Kitchen');
|
|
238
|
+
* expect(map.brokenLinks).toEqual([]);
|
|
239
|
+
* expect(map.deadEnds).not.toContain('Start');
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
declare function storyInspect(story: Story): StoryMap;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Passage helpers and output methods.
|
|
246
|
+
* Ported from passage.go / passagedata.go.
|
|
247
|
+
*/
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Apply tag aliases: for each passage carrying an alias tag, add the canonical
|
|
251
|
+
* tag if not already present. Idempotent — safe to call multiple times.
|
|
252
|
+
*/
|
|
253
|
+
declare function applyTagAliases(passages: Passage[], aliases: Record<string, string>): void;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Twee 3 state-machine lexer implemented as a synchronous generator.
|
|
257
|
+
* Ported from tweelexer.go — Go channels become `yield`.
|
|
258
|
+
*/
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Twee 3 lexer — yields LexerItem tokens from Twee source text.
|
|
262
|
+
*
|
|
263
|
+
* In the Go reference, this uses goroutines and channels.
|
|
264
|
+
* Here we use a synchronous generator that accumulates items per state step.
|
|
265
|
+
*/
|
|
266
|
+
declare function tweeLexer(input: string): Generator<LexerItem, void, undefined>;
|
|
267
|
+
/** Convenience class wrapper for the lexer generator. */
|
|
268
|
+
declare class TweeLexer {
|
|
269
|
+
private gen;
|
|
270
|
+
constructor(input: string);
|
|
271
|
+
nextItem(): {
|
|
272
|
+
item: LexerItem;
|
|
273
|
+
done: boolean;
|
|
274
|
+
};
|
|
275
|
+
[Symbol.iterator](): Generator<LexerItem, void, undefined>;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Parse lexer items into Passage[].
|
|
280
|
+
* Ported from storyload.go:loadTwee().
|
|
281
|
+
*/
|
|
282
|
+
|
|
283
|
+
interface ParseOptions {
|
|
284
|
+
/** Filename for diagnostics. */
|
|
285
|
+
filename?: string;
|
|
286
|
+
/** Trim whitespace from passage content. Default: true. */
|
|
287
|
+
trim?: boolean;
|
|
288
|
+
/** Enable Twee2 compatibility. Default: false. */
|
|
289
|
+
twee2Compat?: boolean;
|
|
290
|
+
}
|
|
291
|
+
interface ParseResult {
|
|
292
|
+
passages: Passage[];
|
|
293
|
+
diagnostics: Diagnostic[];
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Parse Twee source text into passages.
|
|
297
|
+
*/
|
|
298
|
+
declare function parseTwee(source: string, options?: ParseOptions): ParseResult;
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Parse the Twine 2 format.js JSON chunk.
|
|
302
|
+
* Handles Harlowe's malformed JSON by stripping the "setup" function property.
|
|
303
|
+
*/
|
|
304
|
+
declare function parseFormatJSON(source: string, formatId: string): Twine2FormatJSON | null;
|
|
305
|
+
/**
|
|
306
|
+
* Discover all story formats in the given search directories.
|
|
307
|
+
*/
|
|
308
|
+
declare function discoverFormats(searchDirs: string[]): Map<string, StoryFormatInfo>;
|
|
309
|
+
/**
|
|
310
|
+
* Get the default format search directories.
|
|
311
|
+
*/
|
|
312
|
+
declare function getFormatSearchDirs(extraPaths?: string[], useTweegoPath?: boolean): string[];
|
|
313
|
+
/** Parse a simple semver string into [major, minor, patch]. */
|
|
314
|
+
declare function parseSemver(v: string): [number, number, number] | null;
|
|
315
|
+
declare function semverCompare(a: [number, number, number], b: [number, number, number]): number;
|
|
316
|
+
|
|
317
|
+
/** Generate a new IFID (UUID v4, uppercase). */
|
|
318
|
+
declare function generateIFID(): string;
|
|
319
|
+
/**
|
|
320
|
+
* Validate an IFID string.
|
|
321
|
+
* Accepts both bare UUIDs and UUID://...// wrapped format.
|
|
322
|
+
* Returns null if valid, or an error message string.
|
|
323
|
+
*/
|
|
324
|
+
declare function validateIFID(ifid: string): string | null;
|
|
325
|
+
|
|
326
|
+
declare const CONFIG_FILENAME = "twee-ts.config.json";
|
|
327
|
+
/** Load a config file from the given directory (default: cwd). Returns null if not found. */
|
|
328
|
+
declare function loadConfig(dir?: string): TweeTsConfig | null;
|
|
329
|
+
/** Load a config from a specific file path. */
|
|
330
|
+
declare function loadConfigFile(filePath: string): TweeTsConfig;
|
|
331
|
+
/** Validate a config object. Returns an array of error strings (empty = valid). */
|
|
332
|
+
declare function validateConfig(data: unknown): string[];
|
|
333
|
+
/** Return a default config JSON string for --init scaffolding. */
|
|
334
|
+
declare function scaffoldConfig(): string;
|
|
335
|
+
|
|
336
|
+
/** Get the cache directory for downloaded story formats. */
|
|
337
|
+
declare function getCacheDir(): string;
|
|
338
|
+
/** Download a format, verify its checksum, write to cache, and return StoryFormatInfo. */
|
|
339
|
+
declare function fetchAndCacheFormat(entry: SFAIndexEntry, downloadUrl: string): Promise<StoryFormatInfo>;
|
|
340
|
+
/** Download a direct format.js URL, parse its JSON, cache it, and return StoryFormatInfo. */
|
|
341
|
+
declare function fetchDirectFormat(url: string): Promise<StoryFormatInfo>;
|
|
342
|
+
/**
|
|
343
|
+
* Try to resolve a remote story format by name and version.
|
|
344
|
+
* 1. Try direct format URLs
|
|
345
|
+
* 2. Try custom index URLs
|
|
346
|
+
* 3. Try default SFA indices
|
|
347
|
+
*/
|
|
348
|
+
declare function resolveRemoteFormat(name: string, version: string, indices?: string[], urls?: string[]): Promise<StoryFormatInfo | undefined>;
|
|
349
|
+
/** Discover all cached remote formats. Returns a Map like discoverFormats(). */
|
|
350
|
+
declare function discoverCachedFormats(): Map<string, StoryFormatInfo>;
|
|
351
|
+
|
|
352
|
+
export { type BrokenLink, CONFIG_FILENAME, type CompileOptions, type CompileResult, type CompileStats, type CompileToFileOptions, type Diagnostic, type InlineSource, ItemType, type LexerItem, type OutputMode, type Passage, type PassageMetadata, type SFAIndex, type SFAIndexEntry, type SourceInput, type Story, type StoryFormatInfo, type StoryMap, TweeLexer, type TweeTsConfig, TweeTsError, type WatchOptions, applyTagAliases, compile, compileToFile, discoverCachedFormats, discoverFormats, fetchAndCacheFormat, fetchDirectFormat, generateIFID, getCacheDir, getFormatSearchDirs, loadConfig, loadConfigFile, parseFormatJSON, parseSemver, parseTwee, resolveRemoteFormat, scaffoldConfig, semverCompare, storyInspect, tweeLexer, validateConfig, validateIFID, watch };
|