@rohal12/twee-ts 1.15.0 → 1.15.2

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.
@@ -1,4809 +0,0 @@
1
- // src/media-types.ts
2
- import { extname } from "path";
3
- var MEDIA_TYPE_MAP = {
4
- // Audio
5
- aac: "audio/aac",
6
- flac: "audio/flac",
7
- ogg: "audio/ogg",
8
- wav: "audio/wav",
9
- mp3: "audio/mpeg",
10
- m4a: "audio/mp4",
11
- oga: "audio/ogg",
12
- opus: "audio/ogg",
13
- wave: "audio/wav",
14
- weba: "audio/webm",
15
- // Fonts
16
- otf: "font/otf",
17
- ttf: "font/ttf",
18
- woff: "font/woff",
19
- woff2: "font/woff2",
20
- // Images
21
- gif: "image/gif",
22
- jpeg: "image/jpeg",
23
- jpg: "image/jpeg",
24
- png: "image/png",
25
- svg: "image/svg+xml",
26
- tif: "image/tiff",
27
- tiff: "image/tiff",
28
- webp: "image/webp",
29
- // Metadata
30
- vtt: "text/vtt",
31
- // Video
32
- mp4: "video/mp4",
33
- ogv: "video/ogg",
34
- webm: "video/webm"
35
- };
36
- function mediaTypeFromExt(ext) {
37
- return MEDIA_TYPE_MAP[ext] ?? "application/octet-stream";
38
- }
39
- function mediaTypeFromFilename(filename) {
40
- return mediaTypeFromExt(normalizedFileExt(filename));
41
- }
42
- function normalizedFileExt(filename) {
43
- const ext = extname(filename);
44
- if (!ext) return "";
45
- return ext.slice(1).toLowerCase();
46
- }
47
- function slugify(original) {
48
- return original.replace(/[\x00-\x20!-/:-@[-^`{-\x9f]+/g, "_");
49
- }
50
- function fontFormatHint(ext) {
51
- switch (ext) {
52
- case "ttf":
53
- return "truetype";
54
- case "otf":
55
- return "opentype";
56
- default:
57
- return ext;
58
- }
59
- }
60
-
61
- // src/compiler.ts
62
- import { writeFileSync as writeFileSync2 } from "fs";
63
-
64
- // src/ifid.ts
65
- import { randomUUID } from "crypto";
66
- function generateIFID() {
67
- return randomUUID().toUpperCase();
68
- }
69
- function validateIFID(ifid) {
70
- let uuid = ifid;
71
- switch (ifid.length) {
72
- case 36:
73
- break;
74
- case 45: {
75
- if (ifid.slice(0, 7).toUpperCase() !== "UUID://" || ifid.slice(43) !== "//") {
76
- return "invalid IFID UUID://...// format";
77
- }
78
- uuid = ifid.slice(7, 43);
79
- break;
80
- }
81
- default:
82
- return `invalid IFID length: ${ifid.length}`;
83
- }
84
- for (let i = 0; i < uuid.length; i++) {
85
- const ch = uuid[i];
86
- switch (i) {
87
- case 8:
88
- case 13:
89
- case 18:
90
- case 23:
91
- if (ch !== "-") {
92
- return `invalid IFID character '${ch}' at position ${i + 1}`;
93
- }
94
- break;
95
- case 14:
96
- if (ch < "1" || ch > "5") {
97
- return `invalid version '${ch}' at position ${i + 1}`;
98
- }
99
- break;
100
- case 19:
101
- if (!["8", "9", "a", "A", "b", "B"].includes(ch)) {
102
- return `invalid variant '${ch}' at position ${i + 1}`;
103
- }
104
- break;
105
- default:
106
- if (!/^[0-9a-fA-F]$/.test(ch)) {
107
- return `invalid IFID hex value '${ch}' at position ${i + 1}`;
108
- }
109
- break;
110
- }
111
- }
112
- return null;
113
- }
114
-
115
- // src/escape.ts
116
- function attrEscape(s) {
117
- if (s.length === 0) return s;
118
- return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
119
- }
120
- function fullAttrEscape(s) {
121
- if (s.length === 0) return s;
122
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
123
- }
124
- function htmlEscape(s) {
125
- if (s.length === 0) return s;
126
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
127
- }
128
- function tiddlerEscape(s) {
129
- if (s.length === 0) return s;
130
- return s.replace(/\\/g, "\\s").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/\t/g, "\\t").replace(/\n/g, "\\n");
131
- }
132
- function tiddlerUnescape(s) {
133
- if (s.length === 0) return s;
134
- return s.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\s/g, "\\");
135
- }
136
- function tweeEscape(s) {
137
- if (s.length === 0) return s;
138
- return s.replace(/[\\[\]{}]/g, (ch) => "\\" + ch);
139
- }
140
- function jsStringEscape(s) {
141
- if (s.length === 0) return s;
142
- return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
143
- }
144
- function commentSanitize(s) {
145
- return s.replace(/\*\//g, "* /");
146
- }
147
- function htmlCommentSanitize(s) {
148
- return s.replace(/-->/g, "-- >");
149
- }
150
- function rot13(s) {
151
- if (s.length === 0) return s;
152
- return s.replace(/[A-Za-z]/g, (ch) => {
153
- const base = ch <= "Z" ? 65 : 97;
154
- return String.fromCharCode((ch.charCodeAt(0) - base + 13) % 26 + base);
155
- });
156
- }
157
- function tweeUnescape(s) {
158
- if (s.length === 0) return s;
159
- let result = "";
160
- for (let i = 0; i < s.length; i++) {
161
- if (s[i] === "\\") {
162
- i++;
163
- if (i >= s.length) {
164
- result += "\\";
165
- break;
166
- }
167
- }
168
- result += s[i];
169
- }
170
- return result;
171
- }
172
-
173
- // src/passage.ts
174
- var INFO_PASSAGE_NAMES = /* @__PURE__ */ new Set([
175
- "StoryAuthor",
176
- "StoryInit",
177
- "StoryMenu",
178
- "StorySubtitle",
179
- "StoryTitle",
180
- "PassageReady",
181
- "PassageDone",
182
- "PassageHeader",
183
- "PassageFooter",
184
- "StoryBanner",
185
- "StoryCaption",
186
- "MenuOptions",
187
- "MenuShare",
188
- "MenuStory",
189
- "StoryInterface",
190
- "StoryShare",
191
- "StorySettings",
192
- "StoryData",
193
- "StoryIncludes"
194
- ]);
195
- var INFO_TAGS = ["annotation", "script", "stylesheet", "widget"];
196
- function hasTag(p, tag) {
197
- return p.tags.includes(tag);
198
- }
199
- function hasAnyTag(p, ...tags) {
200
- return p.tags.some((t) => tags.includes(t));
201
- }
202
- function hasTagStartingWith(p, prefix) {
203
- return p.tags.some((t) => t.startsWith(prefix));
204
- }
205
- function hasInfoTags(p) {
206
- return hasAnyTag(p, ...INFO_TAGS) || hasTagStartingWith(p, "Twine.");
207
- }
208
- function hasInfoName(p) {
209
- return INFO_PASSAGE_NAMES.has(p.name);
210
- }
211
- function isStoryPassage(p) {
212
- return !hasInfoName(p) && !hasInfoTags(p);
213
- }
214
- function hasMetadataPosition(p) {
215
- return p.metadata != null && p.metadata.position != null && p.metadata.position !== "";
216
- }
217
- function hasMetadataSize(p) {
218
- return p.metadata != null && p.metadata.size != null && p.metadata.size !== "";
219
- }
220
- function hasAnyMetadata(p) {
221
- if (!p.metadata) return false;
222
- return Object.values(p.metadata).some((v) => v != null && v !== "");
223
- }
224
- function marshalMetadata(meta) {
225
- const obj = {};
226
- for (const [key, value] of Object.entries(meta)) {
227
- if (typeof value === "string" && value) obj[key] = value;
228
- }
229
- return JSON.stringify(obj);
230
- }
231
- function passageToTwee(p, outMode) {
232
- let output;
233
- if (outMode === "twee3") {
234
- output = ":: " + tweeEscape(p.name);
235
- if (p.tags.length > 0) {
236
- output += " [" + tweeEscape(p.tags.join(" ")) + "]";
237
- }
238
- if (hasAnyMetadata(p) && p.metadata) {
239
- output += " " + marshalMetadata(p.metadata);
240
- }
241
- } else {
242
- output = ":: " + p.name;
243
- if (p.tags.length > 0) {
244
- output += " [" + p.tags.join(" ") + "]";
245
- }
246
- }
247
- output += "\n";
248
- if (p.text.length > 0) {
249
- output += p.text + "\n";
250
- }
251
- output += "\n\n";
252
- return output;
253
- }
254
- function passageToPassagedata(p, pid, options) {
255
- let position;
256
- let size;
257
- if (hasMetadataPosition(p)) {
258
- position = p.metadata?.position ?? "";
259
- } else {
260
- const x = pid % 10;
261
- const y = Math.floor(pid / 10);
262
- const xp = x === 0 ? 10 : x;
263
- const yp = x === 0 ? y : y + 1;
264
- position = `${xp * 125 - 25},${yp * 125 - 25}`;
265
- }
266
- if (hasMetadataSize(p)) {
267
- size = p.metadata?.size ?? "";
268
- } else {
269
- size = "100,100";
270
- }
271
- let attrs = `<tw-passagedata pid="${pid}" name=${quote(fullAttrEscape(p.name))} tags=${quote(attrEscape(p.tags.join(" ")))} position=${quote(attrEscape(position))} size=${quote(attrEscape(size))}`;
272
- if (options?.sourceInfo && p.source) {
273
- attrs += ` data-source-file=${quote(attrEscape(p.source.file))} data-source-line="${p.source.line}"`;
274
- }
275
- return `${attrs}>${htmlEscape(p.text)}</tw-passagedata>`;
276
- }
277
- function passageToTiddler(p, pid, obfuscateRot13 = false) {
278
- let position;
279
- if (hasMetadataPosition(p)) {
280
- position = p.metadata?.position ?? "";
281
- } else {
282
- const x = pid % 10;
283
- const y = Math.floor(pid / 10);
284
- const xp = x === 0 ? 10 : x;
285
- const yp = x === 0 ? y : y + 1;
286
- position = `${xp * 140 - 130},${yp * 140 - 130}`;
287
- }
288
- const created = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 12);
289
- const content = obfuscateRot13 && p.name !== "StorySettings" ? tiddlerEscape(rot13(p.text)) : tiddlerEscape(p.text);
290
- const nameComment = obfuscateRot13 ? `<!-- ${commentSanitize(p.name)} -->` : "";
291
- return `<div tiddler=${quote(attrEscape(p.name))} tags=${quote(attrEscape(p.tags.join(" ")))} created=${quote(created)} modifier=${quote("twee")} twine-position=${quote(attrEscape(position))}>${nameComment}${content}</div>`;
292
- }
293
- function countWords(p, method = "tweego") {
294
- switch (method) {
295
- case "tweego": {
296
- let text = p.text;
297
- text = text.replace(/\n/g, "");
298
- text = text.replace(/(?:\/%.+?%\/|\/\*.+?\*\/|<!--.+?-->)/gs, "");
299
- const normalized = text.normalize("NFKD");
300
- const count = [...normalized].length;
301
- if (count === 0) return 0;
302
- const words = Math.floor(count / 5);
303
- return count % 5 > 0 ? words + 1 : words;
304
- }
305
- case "whitespace": {
306
- let text = p.text;
307
- text = text.replace(/(?:\/%.+?%\/|\/\*.+?\*\/|<!--.+?-->)/gs, "");
308
- text = text.replace(/<<[^>]*>>/g, "");
309
- text = text.replace(/\[\[([^\]|]*?)(?:\|[^\]]*?)?\]\]/g, "$1");
310
- text = text.replace(/<[^>]+>/g, "");
311
- const tokens = text.split(/\s+/).filter((t) => t.length > 0);
312
- return tokens.length;
313
- }
314
- default: {
315
- const _exhaustive = method;
316
- throw new Error(`Unhandled word count method: ${_exhaustive}`);
317
- }
318
- }
319
- }
320
- function applyTagAliases(passages, aliases) {
321
- const entries = Object.entries(aliases);
322
- if (entries.length === 0) return [...passages];
323
- return passages.map((p) => {
324
- const original = p.tags;
325
- const added = [];
326
- for (const [alias, canonical] of entries) {
327
- if (original.includes(alias) && !original.includes(canonical) && !added.includes(canonical)) {
328
- added.push(canonical);
329
- }
330
- }
331
- return added.length > 0 ? { ...p, tags: [...original, ...added] } : p;
332
- });
333
- }
334
- function quote(s) {
335
- return `"${s}"`;
336
- }
337
-
338
- // src/story.ts
339
- var passageIndex = /* @__PURE__ */ new WeakMap();
340
- function getIndex(story) {
341
- let index = passageIndex.get(story);
342
- if (!index) {
343
- index = /* @__PURE__ */ new Map();
344
- for (let i = 0; i < story.passages.length; i++) {
345
- const passage = story.passages[i];
346
- if (passage) index.set(passage.name, i);
347
- }
348
- passageIndex.set(story, index);
349
- }
350
- return index;
351
- }
352
- function createStory() {
353
- const story = {
354
- name: "",
355
- ifid: "",
356
- passages: [],
357
- legacyIFID: "",
358
- twine1: { settings: /* @__PURE__ */ new Map() },
359
- twine2: {
360
- format: "",
361
- formatVersion: "",
362
- options: /* @__PURE__ */ new Map(),
363
- start: "",
364
- tags: "",
365
- tagColors: /* @__PURE__ */ new Map(),
366
- zoom: 1
367
- }
368
- };
369
- passageIndex.set(story, /* @__PURE__ */ new Map());
370
- return story;
371
- }
372
- function storyHas(story, name) {
373
- return getIndex(story).has(name);
374
- }
375
- function storyAppend(story, p, diagnostics) {
376
- const index = getIndex(story);
377
- const i = index.get(p.name) ?? -1;
378
- if (i === -1) {
379
- index.set(p.name, story.passages.length);
380
- story.passages.push(p);
381
- } else {
382
- diagnostics.push({
383
- level: "warning",
384
- message: `Replacing existing passage "${p.name}" with duplicate.`
385
- });
386
- story.passages[i] = p;
387
- }
388
- }
389
- function storyPrepend(story, p, diagnostics) {
390
- const index = getIndex(story);
391
- const i = index.get(p.name) ?? -1;
392
- if (i === -1) {
393
- story.passages.unshift(p);
394
- index.clear();
395
- for (let j = 0; j < story.passages.length; j++) {
396
- const passage = story.passages[j];
397
- if (passage) index.set(passage.name, j);
398
- }
399
- } else {
400
- diagnostics.push({
401
- level: "warning",
402
- message: `Replacing existing passage "${p.name}" with duplicate.`
403
- });
404
- story.passages[i] = p;
405
- }
406
- }
407
- function marshalStoryData(story) {
408
- const data = {};
409
- if (story.ifid) data.ifid = story.ifid;
410
- if (story.twine2.format) data.format = story.twine2.format;
411
- if (story.twine2.formatVersion) data["format-version"] = story.twine2.formatVersion;
412
- const options = [];
413
- for (const [opt, val] of story.twine2.options) {
414
- if (val) options.push(opt);
415
- }
416
- if (options.length > 0) data.options = options;
417
- if (story.twine2.start) data.start = story.twine2.start;
418
- if (story.twine2.tags) data.tags = story.twine2.tags;
419
- if (story.twine2.tagColors.size > 0) {
420
- data["tag-colors"] = Object.fromEntries(story.twine2.tagColors);
421
- }
422
- if (story.twine2.zoom !== 1) data.zoom = story.twine2.zoom;
423
- return JSON.stringify(data, null, " ");
424
- }
425
- function unmarshalStoryData(story, json) {
426
- let raw;
427
- try {
428
- raw = JSON.parse(json);
429
- } catch (e) {
430
- return `Cannot unmarshal "StoryData"; ${e instanceof Error ? e.message : String(e)}`;
431
- }
432
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
433
- return 'Cannot unmarshal "StoryData"; expected a JSON object';
434
- }
435
- const data = raw;
436
- if (typeof data.ifid === "string" && data.ifid) story.ifid = data.ifid.toUpperCase();
437
- if (typeof data.format === "string" && data.format) story.twine2.format = data.format;
438
- if (typeof data["format-version"] === "string" && data["format-version"])
439
- story.twine2.formatVersion = data["format-version"];
440
- if (Array.isArray(data.options)) {
441
- for (const opt of data.options) {
442
- if (typeof opt === "string") story.twine2.options.set(opt, true);
443
- }
444
- }
445
- if (typeof data.start === "string" && data.start) story.twine2.start = data.start;
446
- if (typeof data.tags === "string") story.twine2.tags = data.tags;
447
- if (typeof data["tag-colors"] === "object" && data["tag-colors"] !== null && !Array.isArray(data["tag-colors"])) {
448
- for (const [tag, color] of Object.entries(data["tag-colors"])) {
449
- if (typeof color === "string") story.twine2.tagColors.set(tag, color);
450
- }
451
- }
452
- if (typeof data.zoom === "number" && data.zoom !== 0) story.twine2.zoom = data.zoom;
453
- return null;
454
- }
455
- function unmarshalStorySettings(story, text, diagnostics) {
456
- const obsolete = [];
457
- for (const rawLine of text.split("\n")) {
458
- const line = rawLine.trim();
459
- if (line.length === 0) continue;
460
- const colonIdx = line.indexOf(":");
461
- if (colonIdx === -1) {
462
- diagnostics.push({
463
- level: "warning",
464
- message: `Malformed "StorySettings" entry; skipping "${line}".`
465
- });
466
- continue;
467
- }
468
- const key = line.slice(0, colonIdx).trim().toLowerCase();
469
- const val = line.slice(colonIdx + 1).trim().toLowerCase();
470
- switch (key) {
471
- case "ifid": {
472
- const err = validateIFID(val);
473
- if (err === null) {
474
- story.legacyIFID = val.toUpperCase();
475
- }
476
- obsolete.push('"ifid"');
477
- continue;
478
- }
479
- case "zoom":
480
- obsolete.push('"zoom"');
481
- continue;
482
- }
483
- story.twine1.settings.set(key, val);
484
- }
485
- if (obsolete.length > 0) {
486
- const entries = obsolete.length === 1 ? "entry" : "entries";
487
- const pronoun = obsolete.length === 1 ? "it" : "them";
488
- diagnostics.push({
489
- level: "warning",
490
- message: `Detected obsolete "StorySettings" ${entries}: ${obsolete.join(", ")}. Please remove ${pronoun} from the "StorySettings" special passage.`
491
- });
492
- }
493
- }
494
- function storyAdd(story, p, diagnostics) {
495
- let processed = p;
496
- switch (p.name) {
497
- case "StoryIncludes":
498
- diagnostics.push({
499
- level: "warning",
500
- message: 'Ignoring "StoryIncludes" compiler special passage; twee-ts allows you to specify project files and/or directories to recursively search.'
501
- });
502
- break;
503
- case "StoryData": {
504
- const err = unmarshalStoryData(story, p.text);
505
- if (err === null) {
506
- if (story.ifid.length > 0) {
507
- const vErr = validateIFID(story.ifid);
508
- if (vErr !== null) {
509
- diagnostics.push({
510
- level: "error",
511
- message: `Cannot validate IFID; ${vErr}.`
512
- });
513
- }
514
- }
515
- processed = { ...p, text: marshalStoryData(story) };
516
- } else {
517
- diagnostics.push({
518
- level: "warning",
519
- message: `Cannot unmarshal "StoryData" compiler special passage; ${err}.`
520
- });
521
- }
522
- break;
523
- }
524
- case "StorySettings":
525
- unmarshalStorySettings(story, p.text, diagnostics);
526
- break;
527
- case "StoryTitle": {
528
- const trimmed = p.text.trim();
529
- processed = { ...p, text: trimmed };
530
- story.name = trimmed;
531
- break;
532
- }
533
- }
534
- storyAppend(story, processed, diagnostics);
535
- }
536
- function getStoryStats(story, wordCountMethod = "tweego") {
537
- let storyPassages = 0;
538
- let words = 0;
539
- for (const p of story.passages) {
540
- if (isStoryPassage(p)) {
541
- storyPassages++;
542
- words += countWords(p, wordCountMethod);
543
- }
544
- }
545
- return { passages: story.passages.length, storyPassages, words };
546
- }
547
-
548
- // src/filesystem.ts
549
- import { readdirSync, statSync, watch as fsWatch } from "fs";
550
- import { resolve, relative, join } from "path";
551
- function getFilenames(pathnames, outFilename) {
552
- const filenames = [];
553
- const absOutFile = outFilename ? resolve(outFilename) : "";
554
- function walk(pathname) {
555
- let stat;
556
- try {
557
- stat = statSync(pathname);
558
- } catch {
559
- return;
560
- }
561
- if (stat.isFile()) {
562
- const abs = resolve(pathname);
563
- if (abs === absOutFile) return;
564
- const rel = relative(process.cwd(), abs);
565
- filenames.push(rel || abs);
566
- } else if (stat.isDirectory()) {
567
- let entries;
568
- try {
569
- entries = readdirSync(pathname);
570
- } catch {
571
- return;
572
- }
573
- for (const entry of entries) {
574
- walk(join(pathname, entry));
575
- }
576
- }
577
- }
578
- for (const pathname of pathnames) {
579
- walk(pathname);
580
- }
581
- return filenames;
582
- }
583
-
584
- // src/formats.ts
585
- import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
586
- import { join as join2 } from "path";
587
- import { homedir } from "os";
588
-
589
- // src/util.ts
590
- import { readFileSync } from "fs";
591
- import { parse as parsePath } from "path";
592
- function readUTF8(filename) {
593
- let content = readFileSync(filename, "utf-8");
594
- if (content.charCodeAt(0) === 65279) content = content.slice(1);
595
- content = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
596
- return content;
597
- }
598
- function readBase64(filename) {
599
- return readFileSync(filename).toString("base64");
600
- }
601
- function baseNameWithoutExt(filename) {
602
- const { name, ext } = parsePath(filename);
603
- return name || ext;
604
- }
605
-
606
- // src/formats.ts
607
- function relaxedJSONParse(json) {
608
- try {
609
- return JSON.parse(json);
610
- } catch {
611
- }
612
- let fixed = json;
613
- fixed = fixed.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
614
- fixed = fixed.replace(/(?<=^|[{,]\s*)([A-Za-z_$][A-Za-z0-9_$]*)\s*:/gm, '"$1":');
615
- fixed = fixed.replace(/,\s*([}\]])/g, "$1");
616
- return JSON.parse(fixed);
617
- }
618
- function parseFormatJSON(source, formatId) {
619
- const first = source.indexOf("{");
620
- const last = source.lastIndexOf("}");
621
- if (first === -1 || last === -1) return null;
622
- let chunk = source.slice(first, last + 1);
623
- const parse = (json) => {
624
- const raw = relaxedJSONParse(json);
625
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
626
- const obj = raw;
627
- if (typeof obj.version !== "string" || typeof obj.source !== "string") return null;
628
- if (!parseSemver(obj.version)) return null;
629
- const name = typeof obj.name === "string" ? obj.name : "Untitled Story Format";
630
- const result = {
631
- name,
632
- version: obj.version,
633
- source: obj.source,
634
- proofing: obj.proofing === true
635
- };
636
- if (typeof obj.author === "string") result.author = obj.author;
637
- if (typeof obj.description === "string") result.description = obj.description;
638
- if (typeof obj.image === "string") result.image = obj.image;
639
- if (typeof obj.url === "string") result.url = obj.url;
640
- if (typeof obj.license === "string") result.license = obj.license;
641
- return result;
642
- };
643
- try {
644
- return parse(chunk);
645
- } catch {
646
- if (formatId.toLowerCase().startsWith("harlowe")) {
647
- const setupIdx = chunk.lastIndexOf(',"setup": function');
648
- if (setupIdx !== -1) {
649
- chunk = chunk.slice(0, setupIdx) + "}";
650
- try {
651
- return parse(chunk);
652
- } catch {
653
- }
654
- }
655
- }
656
- return null;
657
- }
658
- }
659
- function discoverFormats(searchDirs) {
660
- const formats = /* @__PURE__ */ new Map();
661
- const baseFilenames = ["format.js", "header.html"];
662
- for (const searchDir of searchDirs) {
663
- try {
664
- const stat = statSync2(searchDir);
665
- if (!stat.isDirectory()) continue;
666
- } catch {
667
- continue;
668
- }
669
- let entries;
670
- try {
671
- entries = readdirSync2(searchDir);
672
- } catch {
673
- continue;
674
- }
675
- for (const entry of entries) {
676
- const formatDir = join2(searchDir, entry);
677
- try {
678
- if (!statSync2(formatDir).isDirectory()) continue;
679
- } catch {
680
- continue;
681
- }
682
- for (const baseFilename of baseFilenames) {
683
- const formatFilename = join2(formatDir, baseFilename);
684
- try {
685
- const stat = statSync2(formatFilename);
686
- if (!stat.isFile()) continue;
687
- } catch {
688
- continue;
689
- }
690
- const isTwine2 = baseFilename === "format.js";
691
- const format = {
692
- id: entry,
693
- filename: formatFilename,
694
- isTwine2,
695
- name: "",
696
- version: "",
697
- proofing: false
698
- };
699
- if (isTwine2) {
700
- try {
701
- const source = readUTF8(formatFilename);
702
- const data = parseFormatJSON(source, entry);
703
- if (!data) continue;
704
- format.name = data.name;
705
- format.version = data.version;
706
- format.proofing = data.proofing ?? false;
707
- if (data.author) format.author = data.author;
708
- if (data.description) format.description = data.description;
709
- if (data.image) format.image = data.image;
710
- if (data.url) format.url = data.url;
711
- if (data.license) format.license = data.license;
712
- } catch {
713
- continue;
714
- }
715
- } else {
716
- format.name = entry;
717
- }
718
- formats.set(entry, format);
719
- break;
720
- }
721
- }
722
- }
723
- pruneFormats(formats);
724
- return formats;
725
- }
726
- function pruneFormats(formats) {
727
- const bestByNameMajor = /* @__PURE__ */ new Map();
728
- const seenNameVersion = /* @__PURE__ */ new Set();
729
- for (const [id, f] of formats) {
730
- if (!f.isTwine2 || !f.name || !f.version) continue;
731
- const v = parseSemver(f.version);
732
- if (!v) continue;
733
- const nameVersionKey = `${f.name}@${f.version}`;
734
- if (seenNameVersion.has(nameVersionKey)) {
735
- formats.delete(id);
736
- continue;
737
- }
738
- seenNameVersion.add(nameVersionKey);
739
- const groupKey = `${f.name}@${v[0]}`;
740
- const existing = bestByNameMajor.get(groupKey);
741
- if (!existing || semverCompare(v, existing.version) > 0) {
742
- bestByNameMajor.set(groupKey, { id, version: v });
743
- }
744
- }
745
- for (const [id, f] of [...formats]) {
746
- if (!f.isTwine2 || !f.name || !f.version) continue;
747
- const v = parseSemver(f.version);
748
- if (!v) continue;
749
- const groupKey = `${f.name}@${v[0]}`;
750
- const best = bestByNameMajor.get(groupKey);
751
- if (best && best.id !== id) {
752
- formats.delete(id);
753
- }
754
- }
755
- }
756
- function getFormatSearchDirs(extraPaths = [], useTweegoPath = true) {
757
- const subdirNames = ["storyformats", ".storyformats", "story-formats", "storyFormats", "targets"];
758
- const basePaths = /* @__PURE__ */ new Set();
759
- try {
760
- basePaths.add(homedir());
761
- } catch {
762
- }
763
- basePaths.add(process.cwd());
764
- const dirs = [];
765
- for (const base of basePaths) {
766
- for (const sub of subdirNames) {
767
- const dir = join2(base, sub);
768
- try {
769
- if (statSync2(dir).isDirectory()) {
770
- dirs.push(dir);
771
- }
772
- } catch {
773
- }
774
- }
775
- }
776
- dirs.push(...extraPaths);
777
- if (useTweegoPath) {
778
- const tweegoPath = process.env.TWEEGO_PATH;
779
- if (tweegoPath) {
780
- dirs.push(...tweegoPath.split(process.platform === "win32" ? ";" : ":"));
781
- }
782
- }
783
- return dirs;
784
- }
785
- function parseSemver(v) {
786
- const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
787
- if (!m) return null;
788
- return [Number(m[1]), Number(m[2]), Number(m[3])];
789
- }
790
- function semverCompare(a, b) {
791
- const [aMajor, aMinor, aPatch] = a;
792
- const [bMajor, bMinor, bPatch] = b;
793
- if (aMajor !== bMajor) return aMajor - bMajor;
794
- if (aMinor !== bMinor) return aMinor - bMinor;
795
- if (aPatch !== bPatch) return aPatch - bPatch;
796
- return 0;
797
- }
798
- function getFormatIdByNameAndVersion(formats, name, version) {
799
- const wanted = parseSemver(version);
800
- let bestVersion = null;
801
- let bestId;
802
- for (const [id, f] of formats) {
803
- if (!f.isTwine2 || f.name !== name) continue;
804
- const have = parseSemver(f.version);
805
- if (!have) continue;
806
- if (wanted === null || have[0] === wanted[0] && semverCompare(have, wanted) >= 0) {
807
- if (!bestVersion || semverCompare(have, bestVersion) > 0) {
808
- bestVersion = have;
809
- bestId = id;
810
- }
811
- }
812
- }
813
- return bestId;
814
- }
815
- function readFormatSource(format) {
816
- const source = readUTF8(format.filename);
817
- if (format.isTwine2) {
818
- const data = parseFormatJSON(source, format.id);
819
- if (!data) throw new Error(`Cannot parse format ${format.id} JSON`);
820
- return data.source;
821
- }
822
- return source;
823
- }
824
-
825
- // src/loader.ts
826
- import { basename } from "path";
827
- import { statSync as statSync3 } from "fs";
828
-
829
- // src/types.ts
830
- var ItemType = {
831
- Error: 0,
832
- EOF: 1,
833
- Header: 2,
834
- Name: 3,
835
- Tags: 4,
836
- Metadata: 5,
837
- Content: 6
838
- };
839
-
840
- // src/lexer.ts
841
- var EOF = -1;
842
- var HEADER_DELIM = "::";
843
- var NEWLINE_HEADER_DELIM = "\n::";
844
- var LexerContext = class {
845
- input;
846
- line = 1;
847
- start = 0;
848
- pos = 0;
849
- items = [];
850
- constructor(input) {
851
- this.input = input;
852
- }
853
- next() {
854
- if (this.pos >= this.input.length) return EOF;
855
- const ch = this.input.charCodeAt(this.pos);
856
- this.pos++;
857
- if (ch === 10) this.line++;
858
- return ch;
859
- }
860
- peek() {
861
- if (this.pos >= this.input.length) return EOF;
862
- return this.input.charCodeAt(this.pos);
863
- }
864
- backup() {
865
- if (this.pos > this.start) {
866
- this.pos--;
867
- if (this.input.charCodeAt(this.pos) === 10) this.line--;
868
- } else {
869
- throw new Error("backup would leave pos < start");
870
- }
871
- }
872
- // Line-counting invariant: next() increments this.line for each newline during
873
- // character-by-character scanning. However, Content items are produced by jumping
874
- // this.pos directly (e.g. via indexOf) without calling next(), so those newlines
875
- // haven't been counted yet. emit() re-counts newlines in [start, pos) for Content
876
- // items to compensate. This is safe because this.start is reset to this.pos after
877
- // every emit(), so no range is ever counted twice.
878
- emit(type) {
879
- const val = this.input.slice(this.start, this.pos);
880
- this.items.push({ type, line: this.line, pos: this.start, val });
881
- if (type === ItemType.Content) {
882
- for (let i = this.start; i < this.pos; i++) {
883
- if (this.input.charCodeAt(i) === 10) this.line++;
884
- }
885
- }
886
- this.start = this.pos;
887
- }
888
- ignore() {
889
- for (let i = this.start; i < this.pos; i++) {
890
- if (this.input.charCodeAt(i) === 10) this.line++;
891
- }
892
- this.start = this.pos;
893
- }
894
- acceptRun(valid) {
895
- let ch;
896
- for (ch = this.next(); ch !== EOF && valid.includes(String.fromCharCode(ch)); ch = this.next()) {
897
- }
898
- if (ch !== EOF) this.backup();
899
- }
900
- errorf(message) {
901
- this.items.push({
902
- type: ItemType.Error,
903
- line: this.line,
904
- pos: this.start,
905
- val: message
906
- });
907
- return null;
908
- }
909
- /** Drain all pending items and return them. */
910
- drainItems() {
911
- const result = this.items;
912
- this.items = [];
913
- return result;
914
- }
915
- };
916
- function acceptQuoted(ctx, quote2) {
917
- for (; ; ) {
918
- const ch = ctx.next();
919
- switch (ch) {
920
- case 92: {
921
- const r = ctx.next();
922
- if (r !== 10 && r !== EOF) break;
923
- }
924
- // falls through
925
- case 10:
926
- case EOF:
927
- return "unterminated quoted string";
928
- default:
929
- if (ch === quote2) return null;
930
- break;
931
- }
932
- }
933
- }
934
- function lexProlog(ctx) {
935
- if (ctx.input.startsWith(HEADER_DELIM, ctx.pos)) {
936
- return lexHeaderDelim;
937
- }
938
- const i = ctx.input.indexOf(NEWLINE_HEADER_DELIM, ctx.pos);
939
- if (i > -1) {
940
- ctx.pos = i + 1;
941
- ctx.ignore();
942
- return lexHeaderDelim;
943
- }
944
- ctx.emit(ItemType.EOF);
945
- return null;
946
- }
947
- function lexContent(ctx) {
948
- if (ctx.input.startsWith(HEADER_DELIM, ctx.pos)) {
949
- return lexHeaderDelim;
950
- }
951
- const i = ctx.input.indexOf(NEWLINE_HEADER_DELIM, ctx.pos);
952
- if (i > -1) {
953
- ctx.pos = i + 1;
954
- ctx.emit(ItemType.Content);
955
- return lexHeaderDelim;
956
- }
957
- ctx.pos = ctx.input.length;
958
- if (ctx.pos > ctx.start) {
959
- ctx.emit(ItemType.Content);
960
- }
961
- ctx.emit(ItemType.EOF);
962
- return null;
963
- }
964
- function lexHeaderDelim(ctx) {
965
- ctx.pos += HEADER_DELIM.length;
966
- ctx.emit(ItemType.Header);
967
- return lexName;
968
- }
969
- function lexName(ctx) {
970
- let r;
971
- outer: for (; ; ) {
972
- r = ctx.next();
973
- switch (r) {
974
- case 92: {
975
- const next = ctx.next();
976
- if (next !== 10 && next !== EOF) break;
977
- r = next;
978
- }
979
- // falls through
980
- case 91:
981
- // [
982
- case 93:
983
- // ]
984
- case 123:
985
- // {
986
- case 125:
987
- // }
988
- case 10:
989
- // \n
990
- case EOF:
991
- if (r !== EOF) ctx.backup();
992
- break outer;
993
- }
994
- }
995
- ctx.emit(ItemType.Name);
996
- switch (r) {
997
- case 91:
998
- return lexTags;
999
- // [
1000
- case 93:
1001
- return ctx.errorf(`unexpected right square bracket ']'`);
1002
- case 123:
1003
- return lexMetadata;
1004
- // {
1005
- case 125:
1006
- return ctx.errorf(`unexpected right curly brace '}'`);
1007
- case 10:
1008
- ctx.pos++;
1009
- ctx.ignore();
1010
- return lexContent;
1011
- }
1012
- ctx.emit(ItemType.EOF);
1013
- return null;
1014
- }
1015
- function lexNextOptionalBlock(ctx) {
1016
- ctx.acceptRun(" ");
1017
- ctx.ignore();
1018
- const r = ctx.peek();
1019
- switch (r) {
1020
- case 91:
1021
- return lexTags;
1022
- // [
1023
- case 93:
1024
- return ctx.errorf(`unexpected right square bracket ']'`);
1025
- case 123:
1026
- return lexMetadata;
1027
- // {
1028
- case 125:
1029
- return ctx.errorf(`unexpected right curly brace '}'`);
1030
- case 10:
1031
- ctx.pos++;
1032
- ctx.ignore();
1033
- return lexContent;
1034
- case EOF:
1035
- ctx.emit(ItemType.EOF);
1036
- return null;
1037
- }
1038
- return ctx.errorf(`illegal character '${String.fromCharCode(r)}' amid the optional blocks`);
1039
- }
1040
- function lexTags(ctx) {
1041
- ctx.pos++;
1042
- for (; ; ) {
1043
- const r = ctx.next();
1044
- switch (r) {
1045
- case 92: {
1046
- const next = ctx.next();
1047
- if (next !== 10 && next !== EOF) break;
1048
- }
1049
- // falls through
1050
- case 10:
1051
- case EOF:
1052
- if (r === 10) ctx.backup();
1053
- return ctx.errorf("unterminated tag block");
1054
- case 93:
1055
- if (ctx.pos > ctx.start) ctx.emit(ItemType.Tags);
1056
- return lexNextOptionalBlock;
1057
- case 91:
1058
- return ctx.errorf(`unexpected left square bracket '['`);
1059
- case 123:
1060
- return ctx.errorf(`unexpected left curly brace '{'`);
1061
- case 125:
1062
- return ctx.errorf(`unexpected right curly brace '}'`);
1063
- }
1064
- }
1065
- }
1066
- function lexMetadata(ctx) {
1067
- ctx.pos++;
1068
- let depth = 1;
1069
- for (; ; ) {
1070
- const r = ctx.next();
1071
- switch (r) {
1072
- case 34: {
1073
- const err = acceptQuoted(ctx, 34);
1074
- if (err) return ctx.errorf(err);
1075
- break;
1076
- }
1077
- case 10:
1078
- ctx.backup();
1079
- // falls through
1080
- case EOF:
1081
- return ctx.errorf("unterminated metadata block");
1082
- case 123:
1083
- depth++;
1084
- break;
1085
- case 125:
1086
- depth--;
1087
- if (depth === 0) {
1088
- if (ctx.pos > ctx.start) ctx.emit(ItemType.Metadata);
1089
- return lexNextOptionalBlock;
1090
- }
1091
- break;
1092
- }
1093
- }
1094
- }
1095
- function* tweeLexer(input) {
1096
- const ctx = new LexerContext(input);
1097
- let state = lexProlog;
1098
- while (state !== null) {
1099
- state = state(ctx);
1100
- const items = ctx.drainItems();
1101
- for (const item of items) {
1102
- yield item;
1103
- }
1104
- }
1105
- const remaining = ctx.drainItems();
1106
- for (const item of remaining) {
1107
- yield item;
1108
- }
1109
- }
1110
-
1111
- // src/twee2-compat.ts
1112
- var twee2DetectRe = /^:: *[^[]*?(?: *\[.*?\])? *<(.*?)> *$/m;
1113
- var twee2HeaderRe = /^(:: *[^[]*?)( *\[.*?\])?(?: *<(.*?)>)? *$/gm;
1114
- var twee2BadPosRe = /^(::.*?) *\{"position":" *"\}$/gm;
1115
- function hasTwee2Syntax(s) {
1116
- return twee2DetectRe.test(s);
1117
- }
1118
- function twee2ToV3(s) {
1119
- if (!hasTwee2Syntax(s)) return s;
1120
- let result = s.replace(twee2HeaderRe, (_, p1, p2, p3) => {
1121
- const tags = p2 ?? "";
1122
- const pos = p3 ?? "";
1123
- return `${p1}${tags} {"position":"${pos}"}`;
1124
- });
1125
- result = result.replace(twee2BadPosRe, "$1");
1126
- return result;
1127
- }
1128
-
1129
- // src/parser.ts
1130
- function parseTwee(source, options = {}) {
1131
- const { filename = "<inline>", trim = true, twee2Compat = false } = options;
1132
- const diagnostics = [];
1133
- if (twee2Compat) {
1134
- source = twee2ToV3(source);
1135
- }
1136
- const passages = [];
1137
- let current = null;
1138
- let pCount = 0;
1139
- let lastType = ItemType.EOF;
1140
- for (const item of tweeLexer(source)) {
1141
- switch (item.type) {
1142
- case ItemType.Error:
1143
- diagnostics.push({
1144
- level: "error",
1145
- message: `line ${item.line}: Malformed twee source; ${item.val}.`,
1146
- file: filename,
1147
- line: item.line
1148
- });
1149
- return { passages, diagnostics };
1150
- case ItemType.EOF:
1151
- if (pCount > 0 && current) {
1152
- passages.push(current);
1153
- }
1154
- return { passages, diagnostics };
1155
- case ItemType.Header:
1156
- pCount++;
1157
- if (pCount > 1 && current) {
1158
- passages.push(current);
1159
- }
1160
- current = { name: "", tags: [], text: "", source: { file: filename, line: item.line } };
1161
- break;
1162
- case ItemType.Name: {
1163
- if (!current) break;
1164
- const name = tweeUnescape(item.val).trim();
1165
- if (name.length === 0) {
1166
- diagnostics.push({
1167
- level: "error",
1168
- message: `line ${item.line}: Malformed twee source; passage with no name.`,
1169
- file: filename,
1170
- line: item.line
1171
- });
1172
- return { passages, diagnostics };
1173
- }
1174
- current.name = name;
1175
- break;
1176
- }
1177
- case ItemType.Tags: {
1178
- if (!current) break;
1179
- if (lastType !== ItemType.Name) {
1180
- diagnostics.push({
1181
- level: "error",
1182
- message: `line ${item.line}: Malformed twee source; optional tags block must immediately follow the passage name.`,
1183
- file: filename,
1184
- line: item.line
1185
- });
1186
- return { passages, diagnostics };
1187
- }
1188
- const inner = item.val.slice(1, -1);
1189
- current.tags = tweeUnescape(inner).split(/\s+/).filter(Boolean);
1190
- break;
1191
- }
1192
- case ItemType.Metadata: {
1193
- if (!current) break;
1194
- if (lastType !== ItemType.Name && lastType !== ItemType.Tags) {
1195
- diagnostics.push({
1196
- level: "error",
1197
- message: `line ${item.line}: Malformed twee source; optional metadata block must immediately follow the passage name or tags block.`,
1198
- file: filename,
1199
- line: item.line
1200
- });
1201
- return { passages, diagnostics };
1202
- }
1203
- try {
1204
- const raw = JSON.parse(item.val);
1205
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
1206
- throw new Error("expected a JSON object");
1207
- }
1208
- const parsed = raw;
1209
- const meta = {};
1210
- for (const [key, value] of Object.entries(parsed)) {
1211
- if (typeof value === "string") {
1212
- meta[key] = value;
1213
- }
1214
- }
1215
- current.metadata = meta;
1216
- } catch (e) {
1217
- diagnostics.push({
1218
- level: "warning",
1219
- message: `load ${filename}: line ${item.line}: Malformed twee source; could not decode metadata (reason: ${e instanceof Error ? e.message : String(e)}).`,
1220
- file: filename,
1221
- line: item.line
1222
- });
1223
- }
1224
- break;
1225
- }
1226
- case ItemType.Content: {
1227
- if (!current) break;
1228
- if (trim) {
1229
- current.text = item.val.trim();
1230
- } else {
1231
- current.text = item.val.replace(/\n\s*$/, "");
1232
- }
1233
- break;
1234
- }
1235
- default: {
1236
- const _exhaustive = item.type;
1237
- diagnostics.push({
1238
- level: "error",
1239
- message: `line ${item.line}: Unhandled lexer item type: ${_exhaustive}.`,
1240
- file: filename,
1241
- line: item.line
1242
- });
1243
- return { passages, diagnostics };
1244
- }
1245
- }
1246
- lastType = item.type;
1247
- }
1248
- if (pCount > 0 && current) {
1249
- passages.push(current);
1250
- }
1251
- return { passages, diagnostics };
1252
- }
1253
-
1254
- // node_modules/.pnpm/entities@8.0.0/node_modules/entities/dist/decode-codepoint.js
1255
- var decodeMap = /* @__PURE__ */ new Map([
1256
- [0, 65533],
1257
- // C1 Unicode control character reference replacements
1258
- [128, 8364],
1259
- [130, 8218],
1260
- [131, 402],
1261
- [132, 8222],
1262
- [133, 8230],
1263
- [134, 8224],
1264
- [135, 8225],
1265
- [136, 710],
1266
- [137, 8240],
1267
- [138, 352],
1268
- [139, 8249],
1269
- [140, 338],
1270
- [142, 381],
1271
- [145, 8216],
1272
- [146, 8217],
1273
- [147, 8220],
1274
- [148, 8221],
1275
- [149, 8226],
1276
- [150, 8211],
1277
- [151, 8212],
1278
- [152, 732],
1279
- [153, 8482],
1280
- [154, 353],
1281
- [155, 8250],
1282
- [156, 339],
1283
- [158, 382],
1284
- [159, 376]
1285
- ]);
1286
- function replaceCodePoint(codePoint) {
1287
- if (codePoint >= 55296 && codePoint <= 57343 || codePoint > 1114111) {
1288
- return 65533;
1289
- }
1290
- return decodeMap.get(codePoint) ?? codePoint;
1291
- }
1292
-
1293
- // node_modules/.pnpm/entities@8.0.0/node_modules/entities/dist/internal/decode-shared.js
1294
- function decodeBase64(input) {
1295
- const binary = atob(input);
1296
- const evenLength = binary.length & ~1;
1297
- const out = new Uint16Array(evenLength / 2);
1298
- for (let index = 0, outIndex = 0; index < evenLength; index += 2) {
1299
- const lo = binary.charCodeAt(index);
1300
- const hi = binary.charCodeAt(index + 1);
1301
- out[outIndex++] = lo | hi << 8;
1302
- }
1303
- return out;
1304
- }
1305
-
1306
- // node_modules/.pnpm/entities@8.0.0/node_modules/entities/dist/generated/decode-data-html.js
1307
- var htmlDecodeTree = /* @__PURE__ */ decodeBase64("QR08ALkAAgH6AYsDNQR2BO0EPgXZBQEGLAbdBxMISQrvCmQLfQurDKQNLw4fD4YPpA+6D/IPAAAAAAAAAAAAAAAAKhBMEY8TmxUWF2EYLBkxGuAa3RsJHDscWR8YIC8jSCSIJcMl6ie3Ku8rEC0CLjoupS7kLgAIRU1hYmNmZ2xtbm9wcnN0dVQAWgBeAGUAaQBzAHcAfgCBAIQAhwCSAJoAoACsALMAbABpAGcAO4DGAMZAUAA7gCYAJkBjAHUAdABlADuAwQDBQHIiZXZlAAJhAAFpeW0AcgByAGMAO4DCAMJAEGRyAADgNdgE3XIAYQB2AGUAO4DAAMBA8CFoYZFj4SFjcgBhZAAAoFMqAAFncIsAjgBvAG4ABGFmAADgNdg43fAlbHlGdW5jdGlvbgCgYSBpAG4AZwA7gMUAxUAAAWNzpACoAHIAAOA12Jzc6SFnbgCgVCJpAGwAZABlADuAwwDDQG0AbAA7gMQAxEAABGFjZWZvcnN1xQDYANoA7QDxAPYA+QD8AAABY3LJAM8AayNzbGFzaAAAoBYidgHTANUAAKDnKmUAZAAAoAYjeQARZIABY3J0AOAA5QDrAGEidXNlAACgNSLuI291bGxpcwCgLCFhAJJjcgAA4DXYBd1wAGYAAOA12Dnd5SF2ZdhiYwDyAOoAbSJwZXEAAKBOIgAHSE9hY2RlZmhpbG9yc3UXARoBHwE6AVIBVQFiAWQBZgGCAakB6QHtAfIBYwB5ACdkUABZADuAqQCpQIABY3B5ACUBKAE1AfUhdGUGYWmg0iJ0KGFsRGlmZmVyZW50aWFsRAAAoEUhbCJleXMAAKAtIQACYWVpb0EBRAFKAU0B8iFvbgxhZABpAGwAO4DHAMdAcgBjAAhhbiJpbnQAAKAwIm8AdAAKYQABZG5ZAV0BaSJsbGEAuGB0I2VyRG90ALdg8gA5AWkAp2NyImNsZQAAAkRNUFRwAXQBeQF9AW8AdAAAoJkiaSJudXMAAKCWIuwhdXMAoJUiaSJtZXMAAKCXIm8AAAFjc4cBlAFrKndpc2VDb250b3VySW50ZWdyYWwAAKAyImUjQ3VybHkAAAFEUZwBpAFvJXVibGVRdW90ZQAAoB0gdSJvdGUAAKAZIAACbG5wdbABtgHNAdgBbwBuAGWgNyIAoHQqgAFnaXQAvAHBAcUB8iJ1ZW50AKBhIm4AdAAAoC8i7yV1ckludGVncmFsAKAuIgABZnLRAdMBAKACIe8iZHVjdACgECJuLnRlckNsb2Nrd2lzZUNvbnRvdXJJbnRlZ3JhbAAAoDMi7yFzcwCgLypjAHIAAOA12J7ccABDoNMiYQBwAACgTSKABURKU1phY2VmaW9zAAsCEgIVAhgCGwIsAjQCOQI9AnMCfwNvoEUh9CJyYWhkAKARKWMAeQACZGMAeQAFZGMAeQAPZIABZ3JzACECJQIoAuchZXIAoCEgcgAAoKEhaAB2AACg5CoAAWF5MAIzAvIhb24OYRRkbAB0oAciYQCUY3IAAOA12AfdAAFhZkECawIAAWNtRQJnAvIjaXRpY2FsAAJBREdUUAJUAl8CYwJjInV0ZQC0YG8AdAFZAloC2WJiJGxlQWN1dGUA3WJyImF2ZQBgYGkibGRlANxi7yFuZACgxCJmJWVyZW50aWFsRAAAoEYhcAR9AgAAAAAAAIECjgIAABoDZgAA4DXYO91EoagAhQKJAm8AdAAAoNwgcSJ1YWwAAKBQIuIhbGUAA0NETFJVVpkCqAK1Au8C/wIRA28AbgB0AG8AdQByAEkAbgB0AGUAZwByAGEA7ADEAW8AdAKvAgAAAACwAqhgbiNBcnJvdwAAoNMhAAFlb7kC0AJmAHQAgAFBUlQAwQLGAs0CciJyb3cAAKDQIekkZ2h0QXJyb3cAoNQhZQDlACsCbgBnAAABTFLWAugC5SFmdAABQVLcAuECciJyb3cAAKD4J+kkZ2h0QXJyb3cAoPon6SRnaHRBcnJvdwCg+SdpImdodAAAAUFU9gL7AnIicm93AACg0iFlAGUAAKCoInAAQQIGAwAAAAALA3Iicm93AACg0SFvJHduQXJyb3cAAKDVIWUlcnRpY2FsQmFyAACgJSJuAAADQUJMUlRhJAM2AzoDWgNxA3oDciJyb3cAAKGTIUJVLAMwA2EAcgAAoBMpcCNBcnJvdwAAoPUhciJldmUAEWPlIWZ00gJDAwAASwMAAFIDaSVnaHRWZWN0b3IAAKBQKWUkZVZlY3RvcgAAoF4p5SJjdG9yQqC9IWEAcgAAoFYpaSJnaHQA1AFiAwAAaQNlJGVWZWN0b3IAAKBfKeUiY3RvckKgwSFhAHIAAKBXKWUAZQBBoKQiciJyb3cAAKCnIXIAcgBvAPcAtAIAAWN0gwOHA3IA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YwAAoN8i4SFycnCgtiEAoD0pgKIqImJjZG9zACsVMBU6FT4VQRVyImNhcAAAoEgqAAFhdTQVNxVwAACgRipwAACgSipvAHQAAKCNInIAAKBFKgDgKiIA/gACYWxydksVURVuFXMVcgByAG2gtyEAoDwpeQCAAWV2dwBYFWUVaRVxAHACXxUAAAAAYxVyAGUA4wAXFXUA4wAZFWUAZQAAoM4iZSJkZ2UAAKDPImUAbgA7gKQApEBlI2Fycm93AAABbHJ7FX8V5SFmdACgtiFpImdodAAAoLchZQDkAG0VAAFjaYsVkRVvAG4AaQBuAPQAkwFuAHQAAKAxImwiY3R5AACgLSOACUFIYWJjZGVmaGlqbG9yc3R1d3oAuBW7Fb8V1RXgFegV+RUKFhUWHxZUFlcWZRbFFtsW7xb7FgUXChdyAPIAtAJhAHIAAKBlKQACZ2xyc8YVyhXOFdAV5yFlcgCgICDlIXRoAKA4IfIA9QxoAHagECAAoKMiawHZFd4VYSJyb3cAAKAPKWEA4wBfAgABYXnkFecV8iFvbg9hNGQAoUYhYW/tFfQVAAFnciEC8RVyAACgyiF0InNlcQAAoHcqgAFnbG0A/xUCFgUWO4CwALBAdABhALRjcCJ0eXYAAKCxKQABaXIOFhIW8yFodACgfykA4DXYId1hAHIAAAFschsWHRYAoMMhAKDCIYACYWVnc3YAKBauAjYWOhY+Fm0AAKHEIm9zLhY0Fm4AZABzoMQi9SFpdACgZiZhIm1tYQDdY2kAbgAAoPIiAKH3AGlvQxZRFmQAZQAAgfcAO29KFksW90BuI3RpbWVzAACgxyJuAPgAUBZjAHkAUmRjAG8CXhYAAAAAYhZyAG4AAKAeI28AcAAAoA0jgAJscHR1dwBuFnEWdRaSFp4W7CFhciRgZgAA4DXYVd0AotkCZW1wc30WhBaJFo0WcQBkoFAibwB0AACgUSJpIm51cwAAoDgi7CF1cwCgFCLxInVhcmUAoKEiYgBsAGUAYgBhAHIAdwBlAGQAZwDlANcAbgCAAWFkaAClFqoWtBZyAHIAbwD3APUMbwB3AG4AYQByAHIAbwB3APMA8xVhI3Jwb29uAAABbHK8FsAWZQBmAPQAHBZpAGcAaAD0AB4WYgHJFs8WawBhAHIAbwD3AJILbwLUFgAAAADYFnIAbgAAoB8jbwBwAACgDCOAAWNvdADhFukW7BYAAXJ55RboFgDgNdi53FVkbAAAoPYp8iFvaxFhAAFkcvMW9xZvAHQAAKDxImkA5qC/JVsSAAFhaP8WAhdyAPIANQNhAPIA1wvhIm5nbGUAoKYpAAFjaQ4XEBd5AF9k5yJyYXJyAKD/JwAJRGFjZGVmZ2xtbm9wcXJzdHV4MRc4F0YXWxcyBF4XaRd5F40XrBe0F78X2RcVGCEYLRg1GEAYAAFEbzUXgRZvAPQA+BUAAWNzPBdCF3UAdABlADuA6QDpQPQhZXIAoG4qAAJhaW95TRdQF1YXWhfyIW9uG2FyAGOgViI7gOoA6kDsIW9uAKBVIk1kbwB0ABdhAAFEcmIXZhdvAHQAAKBSIgDgNdgi3XKhmipuF3QXYQB2AGUAO4DoAOhAZKCWKm8AdAAAoJgqgKGZKmlscwCAF4UXhxfuInRlcnMAoOcjAKATIWSglSpvAHQAAKCXKoABYXBzAJMXlheiF2MAcgATYXQAeQBzogUinxcAAAAAoRdlAHQAAKAFInAAMaADIDMBqRerFwCgBCAAoAUgAAFnc7AXsRdLYXAAAKACIAABZ3C4F7sXbwBuABlhZgAA4DXYVt2AAWFscwDFF8sXzxdyAHOg1SJsAACg4yl1AHMAAKBxKmkAAKG1A2x21RfYF28AbgC1Y/VjAAJjc3V24BfoF/0XEBgAAWlv5BdWF3IAYwAAoFYiaQLuFwAAAADwF+0ADQThIW50AAFnbPUX+Rd0AHIAAKCWKuUhc3MAoJUqgAFhZWkAAxgGGAoYbABzAD1gcwB0AACgXyJ2AESgYSJEAACgeCrwImFyc2wAoOUpAAFEYRkYHRhvAHQAAKBTInIAcgAAoHEpgAFjZGkAJxgqGO0XcgAAoC8hbwD0AIwCAAFhaDEYMhi3YzuA8ADwQAABbXI5GD0YbAA7gOsA60BvAACgrCCAAWNpcABGGEgYSxhsACFgcwD0ACwEAAFlb08YVxhjAHQAYQB0AGkAbwDuABoEbgBlAG4AdABpAGEAbADlADME4Ql1GAAAgRgAAIMYiBgAAAAAoRilGAAAqhgAALsYvhjRGAAA1xgnGWwAbABpAG4AZwBkAG8AdABzAGUA8QBlF3kARGRtImFsZQAAoEAmgAFpbHIAjRiRGJ0Y7CFpZwCgA/tpApcYAAAAAJoYZwAAoAD7aQBnAACgBPsA4DXYI93sIWlnAKAB++whaWcA4GYAagCAAWFsdACvGLIYthh0AACgbSZpAGcAAKAC+24AcwAAoLElbwBmAJJh8AHCGAAAxhhmAADgNdhX3QABYWvJGMwYbADsAGsEdqDUIgCg2SphI3J0aW50AACgDSoAAWFv2hgiGQABY3PeGB8ZsQPnGP0YBRkSGRUZAAAdGbID7xjyGPQY9xj5GAAA+xg7gL0AvUAAoFMhO4C8ALxAAKBVIQCgWSEAoFshswEBGQAAAxkAoFQhAKBWIbQCCxkOGQAAAAAQGTuAvgC+QACgVyEAoFwhNQAAoFghtgEZGQAAGxkAoFohAKBdITgAAKBeIWwAAKBEIHcAbgAAoCIjYwByAADgNdi73IAIRWFiY2RlZmdpamxub3JzdHYARhlKGVoZXhlmGWkZkhmWGZkZnRmgGa0ZxhnLGc8Z4BkjGmygZyIAoIwqgAFjbXAAUBlTGVgZ9SF0ZfVhbQBhAOSgswM6FgCghipyImV2ZQAfYQABaXliGWUZcgBjAB1hM2RvAHQAIWGAoWUibHFzAMYEcBl6GfGhZSLOBAAAdhlsAGEAbgD0AN8EgKF+KmNkbACBGYQZjBljAACgqSpvAHQAb6CAKmyggioAoIQqZeDbIgD+cwAAoJQqcgAA4DXYJN3noGsirATtIWVsAKA3IWMAeQBTZIChdyJFYWoApxmpGasZAKCSKgCgpSoAoKQqAAJFYWVztBm2Gb0ZwhkAoGkicABwoIoq8iFveACgiipxoIgq8aCIKrUZaQBtAACg5yJwAGYAAOA12FjdYQB2AOUAYwIAAWNp0xnWGXIAAKAKIW0AAKFzImVs3BneGQCgjioAoJAqAIM+ADtjZGxxco0E6xn0GfgZ/BkBGgABY2nvGfEZAKCnKnIAAKB6Km8AdAAAoNci0CFhcgCglSl1ImVzdAAAoHwqgAJhZGVscwAKGvQZFhrVBCAa8AEPGgAAFBpwAHIAbwD4AFkZcgAAoHgpcQAAAWxxxAQbGmwAZQBzAPMASRlpAO0A5AQAAWVuJxouGnIjdG5lcXEAAOBpIgD+xQAsGgAFQWFiY2Vma29zeUAaQxpmGmoabRqDGocalhrCGtMacgDyAMwCAAJpbG1yShpOGlAaVBpyAHMA8ABxD2YAvWBpAGwA9AASBQABZHJYGlsaYwB5AEpkAKGUIWN3YBpkGmkAcgAAoEgpAKCtIWEAcgAAoA8h6SFyYyVhgAFhbHIAcxp7Gn8a8iF0c3WgZSZpAHQAAKBlJuwhaXAAoCYg4yFvbgCguSJyAADgNdgl3XMAAAFld4wakRphInJvdwAAoCUpYSJyb3cAAKAmKYACYW1vcHIAnxqjGqcauhq+GnIAcgAAoP8h9CFodACgOyJrAAABbHKsGrMaZSRmdGFycm93AACgqSHpJGdodGFycm93AKCqIWYAAOA12Fnd4iFhcgCgFSCAAWNsdADIGswa0BpyAADgNdi93GEAcwDoAGka8iFvaydhAAFicNca2xr1IWxsAKBDIOghZW4AoBAg4Qr2GgAA/RoAAAgbExsaGwAAIRs7GwAAAAA+G2IbmRuVG6sbAACyG80b0htjAHUAdABlADuA7QDtQAChYyBpeQEbBhtyAGMAO4DuAO5AOGQAAWN4CxsNG3kANWRjAGwAO4ChAKFAAAFmcssCFhsA4DXYJt1yAGEAdgBlADuA7ADsQIChSCFpbm8AJxsyGzYbAAFpbisbLxtuAHQAAKAMKnQAAKAtIuYhaW4AoNwpdABhAACgKSHsIWlnM2GAAWFvcABDG1sbXhuAAWNndABJG0sbWRtyACthgAFlbHAAcQVRG1UbaQBuAOUAyAVhAHIA9AByBWgAMWFmAACgtyJlAGQAtWEAoggiY2ZvdGkbbRt1G3kb4SFyZQCgBSFpAG4AdKAeImkAZQAAoN0pZABvAPQAWxsAoisiY2VscIEbhRuPG5QbYQBsAACguiIAAWdyiRuNG2UAcgDzACMQ4wCCG2EicmhrAACgFyryIW9kAKA8KgACY2dwdJ8boRukG6gbeQBRZG8AbgAvYWYAAOA12FrdYQC5Y3UAZQBzAHQAO4C/AL9AAAFjabUbuRtyAADgNdi+3G4AAKIIIkVkc3bCG8QbyBvQAwCg+SJvAHQAAKD1Inag9CIAoPMiaaBiIOwhZGUpYesB1hsAANkbYwB5AFZkbAA7gO8A70AAA2NmbW9zdeYb7hvyG/Ub+hsFHAABaXnqG+0bcgBjADVhOWRyAADgNdgn3eEhdGg3YnAAZgAA4DXYW93jAf8bAAADHHIAAOA12L/c8iFjeVhk6yFjeVRkAARhY2ZnaGpvcxUcGhwiHCYcKhwtHDAcNRzwIXBhdqC6A/BjAAFleR4cIRzkIWls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ZmdobG1vcHJzdHV3CiwNLBEsHSwnLDEsQCxLLFIsYix6LIQsjyzLLOgs7Sz/LAotcgDyAAkDYQByAACgYykAAWNyFSwbLHUAdABlADuA+gD6QPIACQ1yAOMBIywAACUseQBeZHYAZQBtYQABaXkrLDAscgBjADuA+wD7QENkgAFhYmgANyw6LD0scgDyANEO7CFhY3FhYQDyAOAOAAFpckQsSCzzIWh0AKB+KQDgNdgy3XIAYQB2AGUAO4D5APlAYQFWLF8scgAAAWxyWixcLACgvyEAoL4hbABrAACggCUAAWN0Zix2LG8CbCwAAAAAcyxyAG4AZaAcI3IAAKAcI28AcAAAoA8jcgBpAACg+CUAAWFsfiyBLGMAcgBrYTuAqACoQAABZ3CILIssbwBuAHNhZgAA4DXYZt0AA2FkaGxzdZksniynLLgsuyzFLHIAcgBvAPcACQ1vAHcAbgBhAHIAcgBvAPcA2A5hI3Jwb29uAAABbHKvLLMsZQBmAPQAWyxpAGcAaAD0AF0sdQDzAKYOaQAAocUDaGzBLMIs0mNvAG4AxWPwI2Fycm93cwCgyCGAAWNpdADRLOEs5CxvAtcsAAAAAN4scgBuAGWgHSNyAACgHSNvAHAAAKAOI24AZwBvYXIAaQAAoPklYwByAADgNdjK3IABZGlyAPMs9yz6LG8AdAAAoPAi7CFkZWlhaQBmoLUlAKC0JQABYW0DLQYtcgDyAMosbAA7gPwA/EDhIm5nbGUAoKcpgAdBQkRhY2RlZmxub3Byc3oAJy0qLTAtNC2bLZ0toS2/LcMtxy3TLdgt3C3gLfwtcgDyABADYQByAHag6CoAoOkqYQBzAOgA/gIAAW5yOC08LechcnQAoJwpgANla25wcnN0AJkpSC1NLVQtXi1iLYItYQBwAHAA4QAaHG8AdABoAGkAbgDnAKEXgAFoaXIAoSmzJFotbwBwAPQAdCVooJUh7wD4JgABaXVmLWotZwBtAOEAuygAAWJwbi14LXMjZXRuZXEAceCKIgD+AODLKgD+cyNldG5lcQBx4IsiAP4A4MwqAP4AAWhyhi2KLWUAdADhABIraSNhbmdsZQAAAWxyki2WLeUhZnQAoLIiaSJnaHQAAKCzInkAMmThIXNoAKCiIoABZWxyAKcttC24LWKiKCKuLQAAAACyLWEAcgAAoLsicQAAoFoi7CFpcACg7iIAAWJ0vC1eD2EA8gBfD3IAAOA12DPddAByAOkAlS1zAHUAAAFicM0t0C0A4IIi0iAA4IMi0iBwAGYAAOA12GfdcgBvAPAAWQt0AHIA6QCaLQABY3XkLegtcgAA4DXYy9wAAWJw7C30LW4AAAFFZXUt8S0A4IoiAP5uAAABRWV/LfktAOCLIgD+6SJnemFnAKCaKYADY2Vmb3BycwANLhAuJS4pLiMuLi40LukhcmN1YQABZGkULiEuAAFiZxguHC5hAHIAAKBfKmUAcaAnIgCgWSLlIXJwAKAYIXIAAOA12DTdcABmAADgNdho3WWgQCJhAHQA6ABqD2MAcgAA4DXYzNzjCuQRUC4AAFQuAABYLmIuAAAAAGMubS5wLnQuAAAAAIguki4AAJouJxIqEnQAcgDpAB0ScgAA4DXYNd0AAUFhWy5eLnIA8gDnAnIA8gCTB75jAAFBYWYuaS5yAPIA4AJyAPIAjAdhAPAAeh5pAHMAAKD7IoABZHB0APgReS6DLgABZmx9LoAuAOA12GnddQDzAP8RaQBtAOUABBIAAUFhiy6OLnIA8gDuAnIA8gCaBwABY3GVLgoScgAA4DXYzdwAAXB0nS6hLmwAdQDzACUScgDpACASAARhY2VmaW9zdbEuvC7ELsguzC7PLtQu2S5jAAABdXm2LrsudABlADuA/QD9QE9kAAFpecAuwy5yAGMAd2FLZG4AO4ClAKVAcgAA4DXYNt1jAHkAV2RwAGYAAOA12GrdYwByAADgNdjO3AABY23dLt8ueQBOZGwAO4D/AP9AAAVhY2RlZmhpb3N38y73Lv8uAi8MLxAvEy8YLx0vIi9jInV0ZQB6YQABYXn7Lv4u8iFvbn5hN2RvAHQAfGEAAWV0Bi8KL3QAcgDmAB8QYQC2Y3IAAOA12DfdYwB5ADZk5yJyYXJyAKDdIXAAZgAA4DXYa91jAHIAAOA12M/cAAFqbiYvKC8AoA0gagAAoAwg");
1308
-
1309
- // node_modules/.pnpm/entities@8.0.0/node_modules/entities/dist/generated/decode-data-xml.js
1310
- var xmlDecodeTree = /* @__PURE__ */ decodeBase64("AAJhZ2xxBwARABMAFQBtAg0AAAAAAA8AcAAmYG8AcwAnYHQAPmB0ADxg9SFvdCJg");
1311
-
1312
- // node_modules/.pnpm/entities@8.0.0/node_modules/entities/dist/internal/bin-trie-flags.js
1313
- var BinTrieFlags;
1314
- (function(BinTrieFlags2) {
1315
- BinTrieFlags2[BinTrieFlags2["VALUE_LENGTH"] = 49152] = "VALUE_LENGTH";
1316
- BinTrieFlags2[BinTrieFlags2["FLAG13"] = 8192] = "FLAG13";
1317
- BinTrieFlags2[BinTrieFlags2["BRANCH_LENGTH"] = 8064] = "BRANCH_LENGTH";
1318
- BinTrieFlags2[BinTrieFlags2["JUMP_TABLE"] = 127] = "JUMP_TABLE";
1319
- })(BinTrieFlags || (BinTrieFlags = {}));
1320
-
1321
- // node_modules/.pnpm/entities@8.0.0/node_modules/entities/dist/decode.js
1322
- var CharCodes;
1323
- (function(CharCodes3) {
1324
- CharCodes3[CharCodes3["NUM"] = 35] = "NUM";
1325
- CharCodes3[CharCodes3["SEMI"] = 59] = "SEMI";
1326
- CharCodes3[CharCodes3["EQUALS"] = 61] = "EQUALS";
1327
- CharCodes3[CharCodes3["ZERO"] = 48] = "ZERO";
1328
- CharCodes3[CharCodes3["NINE"] = 57] = "NINE";
1329
- CharCodes3[CharCodes3["LOWER_A"] = 97] = "LOWER_A";
1330
- CharCodes3[CharCodes3["LOWER_F"] = 102] = "LOWER_F";
1331
- CharCodes3[CharCodes3["LOWER_X"] = 120] = "LOWER_X";
1332
- CharCodes3[CharCodes3["LOWER_Z"] = 122] = "LOWER_Z";
1333
- CharCodes3[CharCodes3["UPPER_A"] = 65] = "UPPER_A";
1334
- CharCodes3[CharCodes3["UPPER_F"] = 70] = "UPPER_F";
1335
- CharCodes3[CharCodes3["UPPER_Z"] = 90] = "UPPER_Z";
1336
- })(CharCodes || (CharCodes = {}));
1337
- var TO_LOWER_BIT = 32;
1338
- function isNumber(code) {
1339
- return code >= CharCodes.ZERO && code <= CharCodes.NINE;
1340
- }
1341
- function isHexadecimalCharacter(code) {
1342
- return code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_F || code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_F;
1343
- }
1344
- function isAsciiAlphaNumeric(code) {
1345
- return code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z || code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z || isNumber(code);
1346
- }
1347
- function isEntityInAttributeInvalidEnd(code) {
1348
- return code === CharCodes.EQUALS || isAsciiAlphaNumeric(code);
1349
- }
1350
- var EntityDecoderState;
1351
- (function(EntityDecoderState2) {
1352
- EntityDecoderState2[EntityDecoderState2["EntityStart"] = 0] = "EntityStart";
1353
- EntityDecoderState2[EntityDecoderState2["NumericStart"] = 1] = "NumericStart";
1354
- EntityDecoderState2[EntityDecoderState2["NumericDecimal"] = 2] = "NumericDecimal";
1355
- EntityDecoderState2[EntityDecoderState2["NumericHex"] = 3] = "NumericHex";
1356
- EntityDecoderState2[EntityDecoderState2["NamedEntity"] = 4] = "NamedEntity";
1357
- })(EntityDecoderState || (EntityDecoderState = {}));
1358
- var DecodingMode;
1359
- (function(DecodingMode2) {
1360
- DecodingMode2[DecodingMode2["Legacy"] = 0] = "Legacy";
1361
- DecodingMode2[DecodingMode2["Strict"] = 1] = "Strict";
1362
- DecodingMode2[DecodingMode2["Attribute"] = 2] = "Attribute";
1363
- })(DecodingMode || (DecodingMode = {}));
1364
- var EntityDecoder = class {
1365
- decodeTree;
1366
- emitCodePoint;
1367
- errors;
1368
- constructor(decodeTree, emitCodePoint, errors) {
1369
- this.decodeTree = decodeTree;
1370
- this.emitCodePoint = emitCodePoint;
1371
- this.errors = errors;
1372
- }
1373
- /** The current state of the decoder. */
1374
- state = EntityDecoderState.EntityStart;
1375
- /** Characters that were consumed while parsing an entity. */
1376
- consumed = 1;
1377
- /**
1378
- * The result of the entity.
1379
- *
1380
- * Either the result index of a numeric entity, or the codepoint of a
1381
- * numeric entity.
1382
- */
1383
- result = 0;
1384
- /** The current index in the decode tree. */
1385
- treeIndex = 0;
1386
- /** The number of characters that were consumed in excess. */
1387
- excess = 1;
1388
- /** The mode in which the decoder is operating. */
1389
- decodeMode = DecodingMode.Strict;
1390
- /** The number of characters that have been consumed in the current run. */
1391
- runConsumed = 0;
1392
- /**
1393
- * Resets the instance to make it reusable.
1394
- * @param decodeMode Entity decoding mode to use.
1395
- */
1396
- startEntity(decodeMode) {
1397
- this.decodeMode = decodeMode;
1398
- this.state = EntityDecoderState.EntityStart;
1399
- this.result = 0;
1400
- this.treeIndex = 0;
1401
- this.excess = 1;
1402
- this.consumed = 1;
1403
- this.runConsumed = 0;
1404
- }
1405
- /**
1406
- * Write an entity to the decoder. This can be called multiple times with partial entities.
1407
- * If the entity is incomplete, the decoder will return -1.
1408
- *
1409
- * Mirrors the implementation of `getDecoder`, but with the ability to stop decoding if the
1410
- * entity is incomplete, and resume when the next string is written.
1411
- * @param input The string containing the entity (or a continuation of the entity).
1412
- * @param offset The offset at which the entity begins. Should be 0 if this is not the first call.
1413
- * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
1414
- */
1415
- write(input, offset) {
1416
- switch (this.state) {
1417
- case EntityDecoderState.EntityStart: {
1418
- if (input.charCodeAt(offset) === CharCodes.NUM) {
1419
- this.state = EntityDecoderState.NumericStart;
1420
- this.consumed += 1;
1421
- return this.stateNumericStart(input, offset + 1);
1422
- }
1423
- this.state = EntityDecoderState.NamedEntity;
1424
- return this.stateNamedEntity(input, offset);
1425
- }
1426
- case EntityDecoderState.NumericStart: {
1427
- return this.stateNumericStart(input, offset);
1428
- }
1429
- case EntityDecoderState.NumericDecimal: {
1430
- return this.stateNumericDecimal(input, offset);
1431
- }
1432
- case EntityDecoderState.NumericHex: {
1433
- return this.stateNumericHex(input, offset);
1434
- }
1435
- case EntityDecoderState.NamedEntity: {
1436
- return this.stateNamedEntity(input, offset);
1437
- }
1438
- }
1439
- }
1440
- /**
1441
- * Switches between the numeric decimal and hexadecimal states.
1442
- *
1443
- * Equivalent to the `Numeric character reference state` in the HTML spec.
1444
- * @param input The string containing the entity (or a continuation of the entity).
1445
- * @param offset The current offset.
1446
- * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
1447
- */
1448
- stateNumericStart(input, offset) {
1449
- if (offset >= input.length) {
1450
- return -1;
1451
- }
1452
- if ((input.charCodeAt(offset) | TO_LOWER_BIT) === CharCodes.LOWER_X) {
1453
- this.state = EntityDecoderState.NumericHex;
1454
- this.consumed += 1;
1455
- return this.stateNumericHex(input, offset + 1);
1456
- }
1457
- this.state = EntityDecoderState.NumericDecimal;
1458
- return this.stateNumericDecimal(input, offset);
1459
- }
1460
- /**
1461
- * Parses a hexadecimal numeric entity.
1462
- *
1463
- * Equivalent to the `Hexademical character reference state` in the HTML spec.
1464
- * @param input The string containing the entity (or a continuation of the entity).
1465
- * @param offset The current offset.
1466
- * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
1467
- */
1468
- stateNumericHex(input, offset) {
1469
- while (offset < input.length) {
1470
- const char = input.charCodeAt(offset);
1471
- if (isNumber(char) || isHexadecimalCharacter(char)) {
1472
- const digit = char <= CharCodes.NINE ? char - CharCodes.ZERO : (char | TO_LOWER_BIT) - CharCodes.LOWER_A + 10;
1473
- this.result = this.result * 16 + digit;
1474
- this.consumed++;
1475
- offset++;
1476
- } else {
1477
- return this.emitNumericEntity(char, 3);
1478
- }
1479
- }
1480
- return -1;
1481
- }
1482
- /**
1483
- * Parses a decimal numeric entity.
1484
- *
1485
- * Equivalent to the `Decimal character reference state` in the HTML spec.
1486
- * @param input The string containing the entity (or a continuation of the entity).
1487
- * @param offset The current offset.
1488
- * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
1489
- */
1490
- stateNumericDecimal(input, offset) {
1491
- while (offset < input.length) {
1492
- const char = input.charCodeAt(offset);
1493
- if (isNumber(char)) {
1494
- this.result = this.result * 10 + (char - CharCodes.ZERO);
1495
- this.consumed++;
1496
- offset++;
1497
- } else {
1498
- return this.emitNumericEntity(char, 2);
1499
- }
1500
- }
1501
- return -1;
1502
- }
1503
- /**
1504
- * Validate and emit a numeric entity.
1505
- *
1506
- * Implements the logic from the `Hexademical character reference start
1507
- * state` and `Numeric character reference end state` in the HTML spec.
1508
- * @param lastCp The last code point of the entity. Used to see if the
1509
- * entity was terminated with a semicolon.
1510
- * @param expectedLength The minimum number of characters that should be
1511
- * consumed. Used to validate that at least one digit
1512
- * was consumed.
1513
- * @returns The number of characters that were consumed.
1514
- */
1515
- emitNumericEntity(lastCp, expectedLength) {
1516
- if (this.consumed <= expectedLength) {
1517
- this.errors?.absenceOfDigitsInNumericCharacterReference(this.consumed);
1518
- return 0;
1519
- }
1520
- if (lastCp === CharCodes.SEMI) {
1521
- this.consumed += 1;
1522
- } else if (this.decodeMode === DecodingMode.Strict) {
1523
- return 0;
1524
- }
1525
- this.emitCodePoint(replaceCodePoint(this.result), this.consumed);
1526
- if (this.errors) {
1527
- if (lastCp !== CharCodes.SEMI) {
1528
- this.errors.missingSemicolonAfterCharacterReference();
1529
- }
1530
- this.errors.validateNumericCharacterReference(this.result);
1531
- }
1532
- return this.consumed;
1533
- }
1534
- /**
1535
- * Parses a named entity.
1536
- *
1537
- * Equivalent to the `Named character reference state` in the HTML spec.
1538
- * @param input The string containing the entity (or a continuation of the entity).
1539
- * @param offset The current offset.
1540
- * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
1541
- */
1542
- stateNamedEntity(input, offset) {
1543
- const { decodeTree } = this;
1544
- let current = decodeTree[this.treeIndex];
1545
- let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
1546
- while (offset < input.length) {
1547
- if (valueLength === 0 && (current & BinTrieFlags.FLAG13) !== 0) {
1548
- const runLength = (current & BinTrieFlags.BRANCH_LENGTH) >> 7;
1549
- if (this.runConsumed === 0) {
1550
- const firstChar = current & BinTrieFlags.JUMP_TABLE;
1551
- if (input.charCodeAt(offset) !== firstChar) {
1552
- return this.result === 0 ? 0 : this.emitNotTerminatedNamedEntity();
1553
- }
1554
- offset++;
1555
- this.excess++;
1556
- this.runConsumed++;
1557
- }
1558
- while (this.runConsumed < runLength) {
1559
- if (offset >= input.length) {
1560
- return -1;
1561
- }
1562
- const charIndexInPacked = this.runConsumed - 1;
1563
- const packedWord = decodeTree[this.treeIndex + 1 + (charIndexInPacked >> 1)];
1564
- const expectedChar = charIndexInPacked % 2 === 0 ? packedWord & 255 : packedWord >> 8 & 255;
1565
- if (input.charCodeAt(offset) !== expectedChar) {
1566
- this.runConsumed = 0;
1567
- return this.result === 0 ? 0 : this.emitNotTerminatedNamedEntity();
1568
- }
1569
- offset++;
1570
- this.excess++;
1571
- this.runConsumed++;
1572
- }
1573
- this.runConsumed = 0;
1574
- this.treeIndex += 1 + (runLength >> 1);
1575
- current = decodeTree[this.treeIndex];
1576
- valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
1577
- }
1578
- if (offset >= input.length)
1579
- break;
1580
- const char = input.charCodeAt(offset);
1581
- if (char === CharCodes.SEMI && valueLength !== 0 && (current & BinTrieFlags.FLAG13) !== 0) {
1582
- return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
1583
- }
1584
- this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char);
1585
- if (this.treeIndex < 0) {
1586
- return this.result === 0 || // If we are parsing an attribute
1587
- this.decodeMode === DecodingMode.Attribute && // We shouldn't have consumed any characters after the entity,
1588
- (valueLength === 0 || // And there should be no invalid characters.
1589
- isEntityInAttributeInvalidEnd(char)) ? 0 : this.emitNotTerminatedNamedEntity();
1590
- }
1591
- current = decodeTree[this.treeIndex];
1592
- valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
1593
- if (valueLength !== 0) {
1594
- if (char === CharCodes.SEMI) {
1595
- return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
1596
- }
1597
- if (this.decodeMode !== DecodingMode.Strict && (current & BinTrieFlags.FLAG13) === 0) {
1598
- this.result = this.treeIndex;
1599
- this.consumed += this.excess;
1600
- this.excess = 0;
1601
- }
1602
- }
1603
- offset++;
1604
- this.excess++;
1605
- }
1606
- return -1;
1607
- }
1608
- /**
1609
- * Emit a named entity that was not terminated with a semicolon.
1610
- * @returns The number of characters consumed.
1611
- */
1612
- emitNotTerminatedNamedEntity() {
1613
- const { result, decodeTree } = this;
1614
- const valueLength = (decodeTree[result] & BinTrieFlags.VALUE_LENGTH) >> 14;
1615
- this.emitNamedEntityData(result, valueLength, this.consumed);
1616
- this.errors?.missingSemicolonAfterCharacterReference();
1617
- return this.consumed;
1618
- }
1619
- /**
1620
- * Emit a named entity.
1621
- * @param result The index of the entity in the decode tree.
1622
- * @param valueLength The number of bytes in the entity.
1623
- * @param consumed The number of characters consumed.
1624
- * @returns The number of characters consumed.
1625
- */
1626
- emitNamedEntityData(result, valueLength, consumed) {
1627
- const { decodeTree } = this;
1628
- this.emitCodePoint(valueLength === 1 ? decodeTree[result] & ~(BinTrieFlags.VALUE_LENGTH | BinTrieFlags.FLAG13) : decodeTree[result + 1], consumed);
1629
- if (valueLength === 3) {
1630
- this.emitCodePoint(decodeTree[result + 2], consumed);
1631
- }
1632
- return consumed;
1633
- }
1634
- /**
1635
- * Signal to the parser that the end of the input was reached.
1636
- *
1637
- * Remaining data will be emitted and relevant errors will be produced.
1638
- * @returns The number of characters consumed.
1639
- */
1640
- end() {
1641
- switch (this.state) {
1642
- case EntityDecoderState.NamedEntity: {
1643
- return this.result !== 0 && (this.decodeMode !== DecodingMode.Attribute || this.result === this.treeIndex) ? this.emitNotTerminatedNamedEntity() : 0;
1644
- }
1645
- // Otherwise, emit a numeric entity if we have one.
1646
- case EntityDecoderState.NumericDecimal: {
1647
- return this.emitNumericEntity(0, 2);
1648
- }
1649
- case EntityDecoderState.NumericHex: {
1650
- return this.emitNumericEntity(0, 3);
1651
- }
1652
- case EntityDecoderState.NumericStart: {
1653
- this.errors?.absenceOfDigitsInNumericCharacterReference(this.consumed);
1654
- return 0;
1655
- }
1656
- case EntityDecoderState.EntityStart: {
1657
- return 0;
1658
- }
1659
- }
1660
- }
1661
- };
1662
- function determineBranch(decodeTree, current, nodeIndex, char) {
1663
- const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7;
1664
- const jumpOffset = current & BinTrieFlags.JUMP_TABLE;
1665
- if (branchCount === 0) {
1666
- return jumpOffset !== 0 && char === jumpOffset ? nodeIndex : -1;
1667
- }
1668
- if (jumpOffset) {
1669
- const value = char - jumpOffset;
1670
- return value < 0 || value >= branchCount ? -1 : decodeTree[nodeIndex + value] - 1;
1671
- }
1672
- const packedKeySlots = branchCount + 1 >> 1;
1673
- let lo = 0;
1674
- let hi = branchCount - 1;
1675
- while (lo <= hi) {
1676
- const mid = lo + hi >>> 1;
1677
- const slot = mid >> 1;
1678
- const packed = decodeTree[nodeIndex + slot];
1679
- const midKey = packed >> (mid & 1) * 8 & 255;
1680
- if (midKey < char) {
1681
- lo = mid + 1;
1682
- } else if (midKey > char) {
1683
- hi = mid - 1;
1684
- } else {
1685
- return decodeTree[nodeIndex + packedKeySlots + mid];
1686
- }
1687
- }
1688
- return -1;
1689
- }
1690
-
1691
- // node_modules/.pnpm/htmlparser2@12.0.0/node_modules/htmlparser2/dist/Tokenizer.js
1692
- var CharCodes2;
1693
- (function(CharCodes3) {
1694
- CharCodes3[CharCodes3["Tab"] = 9] = "Tab";
1695
- CharCodes3[CharCodes3["NewLine"] = 10] = "NewLine";
1696
- CharCodes3[CharCodes3["FormFeed"] = 12] = "FormFeed";
1697
- CharCodes3[CharCodes3["CarriageReturn"] = 13] = "CarriageReturn";
1698
- CharCodes3[CharCodes3["Space"] = 32] = "Space";
1699
- CharCodes3[CharCodes3["ExclamationMark"] = 33] = "ExclamationMark";
1700
- CharCodes3[CharCodes3["Number"] = 35] = "Number";
1701
- CharCodes3[CharCodes3["Amp"] = 38] = "Amp";
1702
- CharCodes3[CharCodes3["SingleQuote"] = 39] = "SingleQuote";
1703
- CharCodes3[CharCodes3["DoubleQuote"] = 34] = "DoubleQuote";
1704
- CharCodes3[CharCodes3["Dash"] = 45] = "Dash";
1705
- CharCodes3[CharCodes3["Slash"] = 47] = "Slash";
1706
- CharCodes3[CharCodes3["Zero"] = 48] = "Zero";
1707
- CharCodes3[CharCodes3["Nine"] = 57] = "Nine";
1708
- CharCodes3[CharCodes3["Semi"] = 59] = "Semi";
1709
- CharCodes3[CharCodes3["Lt"] = 60] = "Lt";
1710
- CharCodes3[CharCodes3["Eq"] = 61] = "Eq";
1711
- CharCodes3[CharCodes3["Gt"] = 62] = "Gt";
1712
- CharCodes3[CharCodes3["Questionmark"] = 63] = "Questionmark";
1713
- CharCodes3[CharCodes3["UpperA"] = 65] = "UpperA";
1714
- CharCodes3[CharCodes3["LowerA"] = 97] = "LowerA";
1715
- CharCodes3[CharCodes3["UpperF"] = 70] = "UpperF";
1716
- CharCodes3[CharCodes3["LowerF"] = 102] = "LowerF";
1717
- CharCodes3[CharCodes3["UpperZ"] = 90] = "UpperZ";
1718
- CharCodes3[CharCodes3["LowerZ"] = 122] = "LowerZ";
1719
- CharCodes3[CharCodes3["LowerX"] = 120] = "LowerX";
1720
- CharCodes3[CharCodes3["OpeningSquareBracket"] = 91] = "OpeningSquareBracket";
1721
- })(CharCodes2 || (CharCodes2 = {}));
1722
- var State;
1723
- (function(State2) {
1724
- State2[State2["Text"] = 1] = "Text";
1725
- State2[State2["BeforeTagName"] = 2] = "BeforeTagName";
1726
- State2[State2["InTagName"] = 3] = "InTagName";
1727
- State2[State2["InSelfClosingTag"] = 4] = "InSelfClosingTag";
1728
- State2[State2["BeforeClosingTagName"] = 5] = "BeforeClosingTagName";
1729
- State2[State2["InClosingTagName"] = 6] = "InClosingTagName";
1730
- State2[State2["AfterClosingTagName"] = 7] = "AfterClosingTagName";
1731
- State2[State2["BeforeAttributeName"] = 8] = "BeforeAttributeName";
1732
- State2[State2["InAttributeName"] = 9] = "InAttributeName";
1733
- State2[State2["AfterAttributeName"] = 10] = "AfterAttributeName";
1734
- State2[State2["BeforeAttributeValue"] = 11] = "BeforeAttributeValue";
1735
- State2[State2["InAttributeValueDq"] = 12] = "InAttributeValueDq";
1736
- State2[State2["InAttributeValueSq"] = 13] = "InAttributeValueSq";
1737
- State2[State2["InAttributeValueNq"] = 14] = "InAttributeValueNq";
1738
- State2[State2["BeforeDeclaration"] = 15] = "BeforeDeclaration";
1739
- State2[State2["InDeclaration"] = 16] = "InDeclaration";
1740
- State2[State2["InProcessingInstruction"] = 17] = "InProcessingInstruction";
1741
- State2[State2["BeforeComment"] = 18] = "BeforeComment";
1742
- State2[State2["CDATASequence"] = 19] = "CDATASequence";
1743
- State2[State2["DeclarationSequence"] = 20] = "DeclarationSequence";
1744
- State2[State2["InSpecialComment"] = 21] = "InSpecialComment";
1745
- State2[State2["InCommentLike"] = 22] = "InCommentLike";
1746
- State2[State2["SpecialStartSequence"] = 23] = "SpecialStartSequence";
1747
- State2[State2["InSpecialTag"] = 24] = "InSpecialTag";
1748
- State2[State2["InPlainText"] = 25] = "InPlainText";
1749
- State2[State2["InEntity"] = 26] = "InEntity";
1750
- })(State || (State = {}));
1751
- function isWhitespace(c) {
1752
- return c === CharCodes2.Space || c === CharCodes2.NewLine || c === CharCodes2.Tab || c === CharCodes2.FormFeed || c === CharCodes2.CarriageReturn;
1753
- }
1754
- function isEndOfTagSection(c) {
1755
- return c === CharCodes2.Slash || c === CharCodes2.Gt || isWhitespace(c);
1756
- }
1757
- function isASCIIAlpha(c) {
1758
- return c >= CharCodes2.LowerA && c <= CharCodes2.LowerZ || c >= CharCodes2.UpperA && c <= CharCodes2.UpperZ;
1759
- }
1760
- var QuoteType;
1761
- (function(QuoteType2) {
1762
- QuoteType2[QuoteType2["NoValue"] = 0] = "NoValue";
1763
- QuoteType2[QuoteType2["Unquoted"] = 1] = "Unquoted";
1764
- QuoteType2[QuoteType2["Single"] = 2] = "Single";
1765
- QuoteType2[QuoteType2["Double"] = 3] = "Double";
1766
- })(QuoteType || (QuoteType = {}));
1767
- var Sequences = {
1768
- Empty: new Uint8Array(0),
1769
- Cdata: new Uint8Array([67, 68, 65, 84, 65, 91]),
1770
- // CDATA[
1771
- CdataEnd: new Uint8Array([93, 93, 62]),
1772
- // ]]>
1773
- CommentEnd: new Uint8Array([45, 45, 33, 62]),
1774
- // `--!>`
1775
- Doctype: new Uint8Array([100, 111, 99, 116, 121, 112, 101]),
1776
- // `doctype`
1777
- IframeEnd: new Uint8Array([60, 47, 105, 102, 114, 97, 109, 101]),
1778
- // `</iframe`
1779
- NoembedEnd: new Uint8Array([
1780
- 60,
1781
- 47,
1782
- 110,
1783
- 111,
1784
- 101,
1785
- 109,
1786
- 98,
1787
- 101,
1788
- 100
1789
- ]),
1790
- // `</noembed`
1791
- NoframesEnd: new Uint8Array([
1792
- 60,
1793
- 47,
1794
- 110,
1795
- 111,
1796
- 102,
1797
- 114,
1798
- 97,
1799
- 109,
1800
- 101,
1801
- 115
1802
- ]),
1803
- // `</noframes`
1804
- Plaintext: new Uint8Array([
1805
- 60,
1806
- 47,
1807
- 112,
1808
- 108,
1809
- 97,
1810
- 105,
1811
- 110,
1812
- 116,
1813
- 101,
1814
- 120,
1815
- 116
1816
- ]),
1817
- // `</plaintext`
1818
- ScriptEnd: new Uint8Array([60, 47, 115, 99, 114, 105, 112, 116]),
1819
- // `</script`
1820
- StyleEnd: new Uint8Array([60, 47, 115, 116, 121, 108, 101]),
1821
- // `</style`
1822
- TitleEnd: new Uint8Array([60, 47, 116, 105, 116, 108, 101]),
1823
- // `</title`
1824
- TextareaEnd: new Uint8Array([
1825
- 60,
1826
- 47,
1827
- 116,
1828
- 101,
1829
- 120,
1830
- 116,
1831
- 97,
1832
- 114,
1833
- 101,
1834
- 97
1835
- ]),
1836
- // `</textarea`
1837
- XmpEnd: new Uint8Array([60, 47, 120, 109, 112])
1838
- // `</xmp`
1839
- };
1840
- var specialStartSequences = /* @__PURE__ */ new Map([
1841
- [Sequences.IframeEnd[2], Sequences.IframeEnd],
1842
- [Sequences.NoembedEnd[2], Sequences.NoembedEnd],
1843
- [Sequences.Plaintext[2], Sequences.Plaintext],
1844
- [Sequences.ScriptEnd[2], Sequences.ScriptEnd],
1845
- [Sequences.TitleEnd[2], Sequences.TitleEnd],
1846
- [Sequences.XmpEnd[2], Sequences.XmpEnd]
1847
- ]);
1848
- var Tokenizer = class {
1849
- cbs;
1850
- /** The current state the tokenizer is in. */
1851
- state = State.Text;
1852
- /** The read buffer. */
1853
- buffer = "";
1854
- /** The beginning of the section that is currently being read. */
1855
- sectionStart = 0;
1856
- /** The index within the buffer that we are currently looking at. */
1857
- index = 0;
1858
- /** The start of the last entity. */
1859
- entityStart = 0;
1860
- /** Some behavior, eg. when decoding entities, is done while we are in another state. This keeps track of the other state type. */
1861
- baseState = State.Text;
1862
- /** For special parsing behavior inside of script and style tags. */
1863
- isSpecial = false;
1864
- /** Indicates whether the tokenizer has been paused. */
1865
- running = true;
1866
- /** The offset of the current buffer. */
1867
- offset = 0;
1868
- xmlMode;
1869
- decodeEntities;
1870
- recognizeSelfClosing;
1871
- entityDecoder;
1872
- constructor({ xmlMode = false, decodeEntities = true, recognizeSelfClosing = xmlMode }, cbs) {
1873
- this.cbs = cbs;
1874
- this.xmlMode = xmlMode;
1875
- this.decodeEntities = decodeEntities;
1876
- this.recognizeSelfClosing = recognizeSelfClosing;
1877
- this.entityDecoder = new EntityDecoder(xmlMode ? xmlDecodeTree : htmlDecodeTree, (cp, consumed) => this.emitCodePoint(cp, consumed));
1878
- }
1879
- reset() {
1880
- this.state = State.Text;
1881
- this.buffer = "";
1882
- this.sectionStart = 0;
1883
- this.index = 0;
1884
- this.baseState = State.Text;
1885
- this.isSpecial = false;
1886
- this.currentSequence = Sequences.Empty;
1887
- this.sequenceIndex = 0;
1888
- this.running = true;
1889
- this.offset = 0;
1890
- }
1891
- write(chunk) {
1892
- this.offset += this.buffer.length;
1893
- this.buffer = chunk;
1894
- this.parse();
1895
- }
1896
- end() {
1897
- if (this.running)
1898
- this.finish();
1899
- }
1900
- pause() {
1901
- this.running = false;
1902
- }
1903
- resume() {
1904
- this.running = true;
1905
- if (this.index < this.buffer.length + this.offset) {
1906
- this.parse();
1907
- }
1908
- }
1909
- stateText(c) {
1910
- if (c === CharCodes2.Lt || !this.decodeEntities && this.fastForwardTo(CharCodes2.Lt)) {
1911
- if (this.index > this.sectionStart) {
1912
- this.cbs.ontext(this.sectionStart, this.index);
1913
- }
1914
- this.state = State.BeforeTagName;
1915
- this.sectionStart = this.index;
1916
- } else if (this.decodeEntities && c === CharCodes2.Amp) {
1917
- this.startEntity();
1918
- }
1919
- }
1920
- currentSequence = Sequences.Empty;
1921
- sequenceIndex = 0;
1922
- enterTagBody() {
1923
- if (this.currentSequence === Sequences.Plaintext) {
1924
- this.currentSequence = Sequences.Empty;
1925
- this.state = State.InPlainText;
1926
- } else if (this.isSpecial) {
1927
- this.state = State.InSpecialTag;
1928
- this.sequenceIndex = 0;
1929
- } else {
1930
- this.state = State.Text;
1931
- }
1932
- }
1933
- /**
1934
- * Match the opening tag name against an HTML text-only tag sequence.
1935
- *
1936
- * Some tags share an initial prefix (`script`/`style`, `title`/`textarea`,
1937
- * `noembed`/`noframes`), so we may switch to an alternate sequence at the
1938
- * first distinguishing byte. On a successful full match we fall back to
1939
- * the normal tag-name state; a later `>` will enter raw-text, RCDATA, or
1940
- * plaintext mode based on `currentSequence` / `isSpecial`.
1941
- * @param c Current character code point.
1942
- */
1943
- stateSpecialStartSequence(c) {
1944
- const lower = c | 32;
1945
- if (this.sequenceIndex < this.currentSequence.length) {
1946
- if (lower === this.currentSequence[this.sequenceIndex]) {
1947
- this.sequenceIndex++;
1948
- return;
1949
- }
1950
- if (this.sequenceIndex === 3) {
1951
- if (this.currentSequence === Sequences.ScriptEnd && lower === Sequences.StyleEnd[3]) {
1952
- this.currentSequence = Sequences.StyleEnd;
1953
- this.sequenceIndex = 4;
1954
- return;
1955
- }
1956
- if (this.currentSequence === Sequences.TitleEnd && lower === Sequences.TextareaEnd[3]) {
1957
- this.currentSequence = Sequences.TextareaEnd;
1958
- this.sequenceIndex = 4;
1959
- return;
1960
- }
1961
- } else if (this.sequenceIndex === 4 && this.currentSequence === Sequences.NoembedEnd && lower === Sequences.NoframesEnd[4]) {
1962
- this.currentSequence = Sequences.NoframesEnd;
1963
- this.sequenceIndex = 5;
1964
- return;
1965
- }
1966
- } else if (isEndOfTagSection(c)) {
1967
- this.sequenceIndex = 0;
1968
- this.state = State.InTagName;
1969
- this.stateInTagName(c);
1970
- return;
1971
- }
1972
- this.isSpecial = false;
1973
- this.currentSequence = Sequences.Empty;
1974
- this.sequenceIndex = 0;
1975
- this.state = State.InTagName;
1976
- this.stateInTagName(c);
1977
- }
1978
- stateCDATASequence(c) {
1979
- if (c === Sequences.Cdata[this.sequenceIndex]) {
1980
- if (++this.sequenceIndex === Sequences.Cdata.length) {
1981
- this.state = State.InCommentLike;
1982
- this.currentSequence = Sequences.CdataEnd;
1983
- this.sequenceIndex = 0;
1984
- this.sectionStart = this.index + 1;
1985
- }
1986
- } else {
1987
- this.sequenceIndex = 0;
1988
- if (this.xmlMode) {
1989
- this.state = State.InDeclaration;
1990
- this.stateInDeclaration(c);
1991
- } else {
1992
- this.state = State.InSpecialComment;
1993
- this.stateInSpecialComment(c);
1994
- }
1995
- }
1996
- }
1997
- /**
1998
- * When we wait for one specific character, we can speed things up
1999
- * by skipping through the buffer until we find it.
2000
- * @param c Current character code point.
2001
- * @returns Whether the character was found.
2002
- */
2003
- fastForwardTo(c) {
2004
- while (++this.index < this.buffer.length + this.offset) {
2005
- if (this.buffer.charCodeAt(this.index - this.offset) === c) {
2006
- return true;
2007
- }
2008
- }
2009
- this.index = this.buffer.length + this.offset - 1;
2010
- return false;
2011
- }
2012
- /**
2013
- * Emit a comment token and return to the text state.
2014
- * @param offset Number of characters in the end sequence that have already been matched.
2015
- */
2016
- emitComment(offset) {
2017
- this.cbs.oncomment(this.sectionStart, this.index, offset);
2018
- this.sequenceIndex = 0;
2019
- this.sectionStart = this.index + 1;
2020
- this.state = State.Text;
2021
- }
2022
- /**
2023
- * Comments and CDATA end with `-->` and `]]>`.
2024
- *
2025
- * Their common qualities are:
2026
- * - Their end sequences have a distinct character they start with.
2027
- * - That character is then repeated, so we have to check multiple repeats.
2028
- * - All characters but the start character of the sequence can be skipped.
2029
- * @param c Current character code point.
2030
- */
2031
- stateInCommentLike(c) {
2032
- if (!this.xmlMode && this.currentSequence === Sequences.CommentEnd && this.sequenceIndex <= 1 && /*
2033
- * We're still at the very start of the comment: the only
2034
- * characters consumed since `<!--` are the dashes that
2035
- * advanced sequenceIndex (0 for `<!-->`, 1 for `<!--->`).
2036
- */
2037
- this.index === this.sectionStart + this.sequenceIndex && c === CharCodes2.Gt) {
2038
- this.emitComment(this.sequenceIndex);
2039
- } else if (this.currentSequence === Sequences.CommentEnd && this.sequenceIndex === 2 && c === CharCodes2.Gt) {
2040
- this.emitComment(2);
2041
- } else if (this.currentSequence === Sequences.CommentEnd && this.sequenceIndex === this.currentSequence.length - 1 && c !== CharCodes2.Gt) {
2042
- this.sequenceIndex = Number(c === CharCodes2.Dash);
2043
- } else if (c === this.currentSequence[this.sequenceIndex]) {
2044
- if (++this.sequenceIndex === this.currentSequence.length) {
2045
- if (this.currentSequence === Sequences.CdataEnd) {
2046
- this.cbs.oncdata(this.sectionStart, this.index, 2);
2047
- } else {
2048
- this.cbs.oncomment(this.sectionStart, this.index, 3);
2049
- }
2050
- this.sequenceIndex = 0;
2051
- this.sectionStart = this.index + 1;
2052
- this.state = State.Text;
2053
- }
2054
- } else if (this.sequenceIndex === 0) {
2055
- if (this.fastForwardTo(this.currentSequence[0])) {
2056
- this.sequenceIndex = 1;
2057
- }
2058
- } else if (c !== this.currentSequence[this.sequenceIndex - 1]) {
2059
- this.sequenceIndex = 0;
2060
- }
2061
- }
2062
- /**
2063
- * HTML only allows ASCII alpha characters (a-z and A-Z) at the beginning of a tag name.
2064
- *
2065
- * XML allows a lot more characters here (@see https://www.w3.org/TR/REC-xml/#NT-NameStartChar).
2066
- * We allow anything that wouldn't end the tag.
2067
- * @param c Current character code point.
2068
- */
2069
- isTagStartChar(c) {
2070
- return this.xmlMode ? !isEndOfTagSection(c) : isASCIIAlpha(c);
2071
- }
2072
- /**
2073
- * Scan raw-text / RCDATA content for the matching end tag.
2074
- *
2075
- * For RCDATA tags (`<title>`, `<textarea>`) entities are decoded inline.
2076
- * For raw-text tags (`<script>`, `<style>`, etc.) we fast-forward to `<`.
2077
- * @param c Current character code point.
2078
- */
2079
- stateInSpecialTag(c) {
2080
- if (this.sequenceIndex === this.currentSequence.length) {
2081
- if (isEndOfTagSection(c)) {
2082
- const endOfText = this.index - this.currentSequence.length;
2083
- if (this.sectionStart < endOfText) {
2084
- const actualIndex = this.index;
2085
- this.index = endOfText;
2086
- this.cbs.ontext(this.sectionStart, endOfText);
2087
- this.index = actualIndex;
2088
- }
2089
- this.isSpecial = false;
2090
- this.sectionStart = endOfText + 2;
2091
- this.stateInClosingTagName(c);
2092
- return;
2093
- }
2094
- this.sequenceIndex = 0;
2095
- }
2096
- if ((c | 32) === this.currentSequence[this.sequenceIndex]) {
2097
- this.sequenceIndex += 1;
2098
- } else if (this.sequenceIndex === 0) {
2099
- if (this.currentSequence === Sequences.TitleEnd || this.currentSequence === Sequences.TextareaEnd) {
2100
- if (this.decodeEntities && c === CharCodes2.Amp) {
2101
- this.startEntity();
2102
- }
2103
- } else if (this.fastForwardTo(CharCodes2.Lt)) {
2104
- this.sequenceIndex = 1;
2105
- }
2106
- } else {
2107
- this.sequenceIndex = Number(c === CharCodes2.Lt);
2108
- }
2109
- }
2110
- stateBeforeTagName(c) {
2111
- if (c === CharCodes2.ExclamationMark) {
2112
- this.state = State.BeforeDeclaration;
2113
- this.sectionStart = this.index + 1;
2114
- } else if (c === CharCodes2.Questionmark) {
2115
- if (this.xmlMode) {
2116
- this.state = State.InProcessingInstruction;
2117
- this.sequenceIndex = 0;
2118
- this.sectionStart = this.index + 1;
2119
- } else {
2120
- this.state = State.InSpecialComment;
2121
- this.sectionStart = this.index;
2122
- }
2123
- } else if (this.isTagStartChar(c)) {
2124
- this.sectionStart = this.index;
2125
- const special = this.xmlMode || this.cbs.isInForeignContext?.() ? void 0 : specialStartSequences.get(c | 32);
2126
- if (special === void 0) {
2127
- this.state = State.InTagName;
2128
- } else {
2129
- this.isSpecial = true;
2130
- this.currentSequence = special;
2131
- this.sequenceIndex = 3;
2132
- this.state = State.SpecialStartSequence;
2133
- }
2134
- } else if (c === CharCodes2.Slash) {
2135
- this.state = State.BeforeClosingTagName;
2136
- } else {
2137
- this.state = State.Text;
2138
- this.stateText(c);
2139
- }
2140
- }
2141
- stateInTagName(c) {
2142
- if (isEndOfTagSection(c)) {
2143
- this.cbs.onopentagname(this.sectionStart, this.index);
2144
- this.sectionStart = -1;
2145
- this.state = State.BeforeAttributeName;
2146
- this.stateBeforeAttributeName(c);
2147
- }
2148
- }
2149
- stateBeforeClosingTagName(c) {
2150
- if (isWhitespace(c)) {
2151
- if (this.xmlMode) {
2152
- } else {
2153
- this.state = State.InSpecialComment;
2154
- this.sectionStart = this.index;
2155
- }
2156
- } else if (c === CharCodes2.Gt) {
2157
- this.state = State.Text;
2158
- if (!this.xmlMode) {
2159
- this.sectionStart = this.index + 1;
2160
- }
2161
- } else {
2162
- this.state = this.isTagStartChar(c) ? State.InClosingTagName : State.InSpecialComment;
2163
- this.sectionStart = this.index;
2164
- }
2165
- }
2166
- stateInClosingTagName(c) {
2167
- if (isEndOfTagSection(c)) {
2168
- this.cbs.onclosetag(this.sectionStart, this.index);
2169
- this.sectionStart = -1;
2170
- this.state = State.AfterClosingTagName;
2171
- this.stateAfterClosingTagName(c);
2172
- }
2173
- }
2174
- stateAfterClosingTagName(c) {
2175
- if (c === CharCodes2.Gt || this.fastForwardTo(CharCodes2.Gt)) {
2176
- this.state = State.Text;
2177
- this.sectionStart = this.index + 1;
2178
- }
2179
- }
2180
- stateBeforeAttributeName(c) {
2181
- if (c === CharCodes2.Gt) {
2182
- this.cbs.onopentagend(this.index);
2183
- this.enterTagBody();
2184
- this.sectionStart = this.index + 1;
2185
- } else if (c === CharCodes2.Slash) {
2186
- this.state = State.InSelfClosingTag;
2187
- } else if (!isWhitespace(c)) {
2188
- this.state = State.InAttributeName;
2189
- this.sectionStart = this.index;
2190
- }
2191
- }
2192
- /**
2193
- * Handle `/` before `>` in an opening tag.
2194
- *
2195
- * In HTML mode, text-only tags ignore the self-closing flag and still enter
2196
- * their raw-text/RCDATA/plaintext state unless self-closing tags are being
2197
- * recognized. In XML mode, or for ordinary tags, the tokenizer returns to
2198
- * regular text parsing after emitting the self-closing callback.
2199
- * @param c Current character code point.
2200
- */
2201
- stateInSelfClosingTag(c) {
2202
- if (c === CharCodes2.Gt) {
2203
- this.cbs.onselfclosingtag(this.index);
2204
- this.sectionStart = this.index + 1;
2205
- if (!this.recognizeSelfClosing) {
2206
- this.enterTagBody();
2207
- return;
2208
- }
2209
- this.state = State.Text;
2210
- this.isSpecial = false;
2211
- this.currentSequence = Sequences.Empty;
2212
- } else if (!isWhitespace(c)) {
2213
- this.state = State.BeforeAttributeName;
2214
- this.stateBeforeAttributeName(c);
2215
- }
2216
- }
2217
- stateInAttributeName(c) {
2218
- if (c === CharCodes2.Eq || isEndOfTagSection(c)) {
2219
- this.cbs.onattribname(this.sectionStart, this.index);
2220
- this.sectionStart = this.index;
2221
- this.state = State.AfterAttributeName;
2222
- this.stateAfterAttributeName(c);
2223
- }
2224
- }
2225
- stateAfterAttributeName(c) {
2226
- if (c === CharCodes2.Eq) {
2227
- this.state = State.BeforeAttributeValue;
2228
- } else if (c === CharCodes2.Slash || c === CharCodes2.Gt) {
2229
- this.cbs.onattribend(QuoteType.NoValue, this.sectionStart);
2230
- this.sectionStart = -1;
2231
- this.state = State.BeforeAttributeName;
2232
- this.stateBeforeAttributeName(c);
2233
- } else if (!isWhitespace(c)) {
2234
- this.cbs.onattribend(QuoteType.NoValue, this.sectionStart);
2235
- this.state = State.InAttributeName;
2236
- this.sectionStart = this.index;
2237
- }
2238
- }
2239
- stateBeforeAttributeValue(c) {
2240
- if (c === CharCodes2.DoubleQuote) {
2241
- this.state = State.InAttributeValueDq;
2242
- this.sectionStart = this.index + 1;
2243
- } else if (c === CharCodes2.SingleQuote) {
2244
- this.state = State.InAttributeValueSq;
2245
- this.sectionStart = this.index + 1;
2246
- } else if (!isWhitespace(c)) {
2247
- this.sectionStart = this.index;
2248
- this.state = State.InAttributeValueNq;
2249
- this.stateInAttributeValueNoQuotes(c);
2250
- }
2251
- }
2252
- handleInAttributeValue(c, quote2) {
2253
- if (c === quote2 || !this.decodeEntities && this.fastForwardTo(quote2)) {
2254
- this.cbs.onattribdata(this.sectionStart, this.index);
2255
- this.sectionStart = -1;
2256
- this.cbs.onattribend(quote2 === CharCodes2.DoubleQuote ? QuoteType.Double : QuoteType.Single, this.index + 1);
2257
- this.state = State.BeforeAttributeName;
2258
- } else if (this.decodeEntities && c === CharCodes2.Amp) {
2259
- this.startEntity();
2260
- }
2261
- }
2262
- stateInAttributeValueDoubleQuotes(c) {
2263
- this.handleInAttributeValue(c, CharCodes2.DoubleQuote);
2264
- }
2265
- stateInAttributeValueSingleQuotes(c) {
2266
- this.handleInAttributeValue(c, CharCodes2.SingleQuote);
2267
- }
2268
- stateInAttributeValueNoQuotes(c) {
2269
- if (isWhitespace(c) || c === CharCodes2.Gt) {
2270
- this.cbs.onattribdata(this.sectionStart, this.index);
2271
- this.sectionStart = -1;
2272
- this.cbs.onattribend(QuoteType.Unquoted, this.index);
2273
- this.state = State.BeforeAttributeName;
2274
- this.stateBeforeAttributeName(c);
2275
- } else if (this.decodeEntities && c === CharCodes2.Amp) {
2276
- this.startEntity();
2277
- }
2278
- }
2279
- /**
2280
- * Distinguish between CDATA, declarations, HTML comments, and HTML bogus
2281
- * comments after `<!`.
2282
- *
2283
- * In HTML mode, only real comments and doctypes stay on declaration paths;
2284
- * everything else becomes a bogus comment terminated by the next `>`.
2285
- * @param c Current character code point.
2286
- */
2287
- stateBeforeDeclaration(c) {
2288
- if (c === CharCodes2.OpeningSquareBracket) {
2289
- this.state = State.CDATASequence;
2290
- this.sequenceIndex = 0;
2291
- } else if (this.xmlMode) {
2292
- this.state = c === CharCodes2.Dash ? State.BeforeComment : State.InDeclaration;
2293
- } else if ((c | 32) === Sequences.Doctype[0]) {
2294
- this.state = State.DeclarationSequence;
2295
- this.currentSequence = Sequences.Doctype;
2296
- this.sequenceIndex = 1;
2297
- } else if (c === CharCodes2.Gt) {
2298
- this.cbs.oncomment(this.sectionStart, this.index, 0);
2299
- this.state = State.Text;
2300
- this.sectionStart = this.index + 1;
2301
- } else if (c === CharCodes2.Dash) {
2302
- this.state = State.BeforeComment;
2303
- } else {
2304
- this.state = State.InSpecialComment;
2305
- }
2306
- }
2307
- /**
2308
- * Continue matching `doctype` after `<!d`.
2309
- *
2310
- * A full `doctype` match stays on the declaration path; any other name falls
2311
- * back to an HTML bogus comment, which matches browser behavior for
2312
- * non-doctype `<!...>` constructs.
2313
- * @param c Current character code point.
2314
- */
2315
- stateDeclarationSequence(c) {
2316
- if (this.sequenceIndex === this.currentSequence.length) {
2317
- this.state = State.InDeclaration;
2318
- this.stateInDeclaration(c);
2319
- } else if ((c | 32) === this.currentSequence[this.sequenceIndex]) {
2320
- this.sequenceIndex += 1;
2321
- } else if (c === CharCodes2.Gt) {
2322
- this.cbs.oncomment(this.sectionStart, this.index, 0);
2323
- this.state = State.Text;
2324
- this.sectionStart = this.index + 1;
2325
- } else {
2326
- this.state = State.InSpecialComment;
2327
- }
2328
- }
2329
- stateInDeclaration(c) {
2330
- if (c === CharCodes2.Gt || this.fastForwardTo(CharCodes2.Gt)) {
2331
- this.cbs.ondeclaration(this.sectionStart, this.index);
2332
- this.state = State.Text;
2333
- this.sectionStart = this.index + 1;
2334
- }
2335
- }
2336
- /**
2337
- * XML processing instructions (`<?...?>`).
2338
- *
2339
- * In HTML mode `<?` is routed to `InSpecialComment` instead, so this
2340
- * state is only reachable in XML mode.
2341
- * @param c Current character code point.
2342
- */
2343
- stateInProcessingInstruction(c) {
2344
- if (c === CharCodes2.Questionmark) {
2345
- this.sequenceIndex = 1;
2346
- } else if (c === CharCodes2.Gt && this.sequenceIndex === 1) {
2347
- this.cbs.onprocessinginstruction(this.sectionStart, this.index - 1);
2348
- this.sequenceIndex = 0;
2349
- this.state = State.Text;
2350
- this.sectionStart = this.index + 1;
2351
- } else {
2352
- this.sequenceIndex = Number(this.fastForwardTo(CharCodes2.Questionmark));
2353
- }
2354
- }
2355
- stateBeforeComment(c) {
2356
- if (c === CharCodes2.Dash) {
2357
- this.state = State.InCommentLike;
2358
- this.currentSequence = Sequences.CommentEnd;
2359
- this.sequenceIndex = 0;
2360
- this.sectionStart = this.index + 1;
2361
- } else if (this.xmlMode) {
2362
- this.state = State.InDeclaration;
2363
- } else if (c === CharCodes2.Gt) {
2364
- this.cbs.oncomment(this.sectionStart, this.index, 0);
2365
- this.state = State.Text;
2366
- this.sectionStart = this.index + 1;
2367
- } else {
2368
- this.state = State.InSpecialComment;
2369
- }
2370
- }
2371
- stateInSpecialComment(c) {
2372
- if (c === CharCodes2.Gt || this.fastForwardTo(CharCodes2.Gt)) {
2373
- this.cbs.oncomment(this.sectionStart, this.index, 0);
2374
- this.state = State.Text;
2375
- this.sectionStart = this.index + 1;
2376
- }
2377
- }
2378
- startEntity() {
2379
- this.baseState = this.state;
2380
- this.state = State.InEntity;
2381
- this.entityStart = this.index;
2382
- this.entityDecoder.startEntity(this.xmlMode ? DecodingMode.Strict : this.baseState === State.Text || this.baseState === State.InSpecialTag ? DecodingMode.Legacy : DecodingMode.Attribute);
2383
- }
2384
- stateInEntity() {
2385
- const indexInBuffer = this.index - this.offset;
2386
- const length = this.entityDecoder.write(this.buffer, indexInBuffer);
2387
- if (length >= 0) {
2388
- this.state = this.baseState;
2389
- if (length === 0) {
2390
- this.index -= 1;
2391
- }
2392
- } else {
2393
- if (indexInBuffer < this.buffer.length && this.buffer.charCodeAt(indexInBuffer) === CharCodes2.Amp) {
2394
- this.state = this.baseState;
2395
- this.index -= 1;
2396
- return;
2397
- }
2398
- this.index = this.offset + this.buffer.length - 1;
2399
- }
2400
- }
2401
- /**
2402
- * Remove data that has already been consumed from the buffer.
2403
- */
2404
- cleanup() {
2405
- if (this.running && this.sectionStart !== this.index) {
2406
- if (this.state === State.Text || this.state === State.InPlainText || this.state === State.InSpecialTag && this.sequenceIndex === 0) {
2407
- this.cbs.ontext(this.sectionStart, this.index);
2408
- this.sectionStart = this.index;
2409
- } else if (this.state === State.InAttributeValueDq || this.state === State.InAttributeValueSq || this.state === State.InAttributeValueNq) {
2410
- this.cbs.onattribdata(this.sectionStart, this.index);
2411
- this.sectionStart = this.index;
2412
- }
2413
- }
2414
- }
2415
- shouldContinue() {
2416
- return this.index < this.buffer.length + this.offset && this.running;
2417
- }
2418
- /**
2419
- * Iterates through the buffer, calling the function corresponding to the current state.
2420
- *
2421
- * States that are more likely to be hit are higher up, as a performance improvement.
2422
- */
2423
- parse() {
2424
- while (this.shouldContinue()) {
2425
- const c = this.buffer.charCodeAt(this.index - this.offset);
2426
- switch (this.state) {
2427
- case State.Text: {
2428
- this.stateText(c);
2429
- break;
2430
- }
2431
- case State.InPlainText: {
2432
- this.index = this.buffer.length + this.offset - 1;
2433
- break;
2434
- }
2435
- case State.SpecialStartSequence: {
2436
- this.stateSpecialStartSequence(c);
2437
- break;
2438
- }
2439
- case State.InSpecialTag: {
2440
- this.stateInSpecialTag(c);
2441
- break;
2442
- }
2443
- case State.CDATASequence: {
2444
- this.stateCDATASequence(c);
2445
- break;
2446
- }
2447
- case State.DeclarationSequence: {
2448
- this.stateDeclarationSequence(c);
2449
- break;
2450
- }
2451
- case State.InAttributeValueDq: {
2452
- this.stateInAttributeValueDoubleQuotes(c);
2453
- break;
2454
- }
2455
- case State.InAttributeName: {
2456
- this.stateInAttributeName(c);
2457
- break;
2458
- }
2459
- case State.InCommentLike: {
2460
- this.stateInCommentLike(c);
2461
- break;
2462
- }
2463
- case State.InSpecialComment: {
2464
- this.stateInSpecialComment(c);
2465
- break;
2466
- }
2467
- case State.BeforeAttributeName: {
2468
- this.stateBeforeAttributeName(c);
2469
- break;
2470
- }
2471
- case State.InTagName: {
2472
- this.stateInTagName(c);
2473
- break;
2474
- }
2475
- case State.InClosingTagName: {
2476
- this.stateInClosingTagName(c);
2477
- break;
2478
- }
2479
- case State.BeforeTagName: {
2480
- this.stateBeforeTagName(c);
2481
- break;
2482
- }
2483
- case State.AfterAttributeName: {
2484
- this.stateAfterAttributeName(c);
2485
- break;
2486
- }
2487
- case State.InAttributeValueSq: {
2488
- this.stateInAttributeValueSingleQuotes(c);
2489
- break;
2490
- }
2491
- case State.BeforeAttributeValue: {
2492
- this.stateBeforeAttributeValue(c);
2493
- break;
2494
- }
2495
- case State.BeforeClosingTagName: {
2496
- this.stateBeforeClosingTagName(c);
2497
- break;
2498
- }
2499
- case State.AfterClosingTagName: {
2500
- this.stateAfterClosingTagName(c);
2501
- break;
2502
- }
2503
- case State.InAttributeValueNq: {
2504
- this.stateInAttributeValueNoQuotes(c);
2505
- break;
2506
- }
2507
- case State.InSelfClosingTag: {
2508
- this.stateInSelfClosingTag(c);
2509
- break;
2510
- }
2511
- case State.InDeclaration: {
2512
- this.stateInDeclaration(c);
2513
- break;
2514
- }
2515
- case State.BeforeDeclaration: {
2516
- this.stateBeforeDeclaration(c);
2517
- break;
2518
- }
2519
- case State.BeforeComment: {
2520
- this.stateBeforeComment(c);
2521
- break;
2522
- }
2523
- case State.InProcessingInstruction: {
2524
- this.stateInProcessingInstruction(c);
2525
- break;
2526
- }
2527
- case State.InEntity: {
2528
- this.stateInEntity();
2529
- break;
2530
- }
2531
- }
2532
- this.index++;
2533
- }
2534
- this.cleanup();
2535
- }
2536
- finish() {
2537
- if (this.state === State.InEntity) {
2538
- this.entityDecoder.end();
2539
- this.state = this.baseState;
2540
- }
2541
- this.handleTrailingData();
2542
- this.cbs.onend();
2543
- }
2544
- handleTrailingCommentLikeData(endIndex) {
2545
- if (this.state !== State.InCommentLike) {
2546
- return false;
2547
- }
2548
- if (this.currentSequence === Sequences.CdataEnd) {
2549
- if (this.xmlMode) {
2550
- if (this.sectionStart < endIndex) {
2551
- this.cbs.oncdata(this.sectionStart, endIndex, 0);
2552
- }
2553
- } else {
2554
- const cdataStart = this.sectionStart - Sequences.Cdata.length - 1;
2555
- this.cbs.oncomment(cdataStart, endIndex, 0);
2556
- }
2557
- } else {
2558
- const offset = this.xmlMode ? 0 : Math.min(this.sequenceIndex, Sequences.CommentEnd.length - 1);
2559
- this.cbs.oncomment(this.sectionStart, endIndex, offset);
2560
- }
2561
- return true;
2562
- }
2563
- handleTrailingMarkupDeclaration(endIndex) {
2564
- if (this.xmlMode) {
2565
- switch (this.state) {
2566
- case State.InSpecialComment:
2567
- case State.BeforeComment:
2568
- case State.CDATASequence:
2569
- case State.DeclarationSequence:
2570
- case State.InDeclaration: {
2571
- this.cbs.ontext(this.sectionStart, endIndex);
2572
- return true;
2573
- }
2574
- default: {
2575
- return false;
2576
- }
2577
- }
2578
- }
2579
- switch (this.state) {
2580
- case State.BeforeDeclaration:
2581
- case State.InSpecialComment:
2582
- case State.BeforeComment:
2583
- case State.CDATASequence: {
2584
- this.cbs.oncomment(this.sectionStart, endIndex, 0);
2585
- return true;
2586
- }
2587
- case State.DeclarationSequence: {
2588
- if (this.sequenceIndex !== Sequences.Doctype.length) {
2589
- this.cbs.oncomment(this.sectionStart, endIndex, 0);
2590
- }
2591
- return true;
2592
- }
2593
- case State.InDeclaration: {
2594
- return true;
2595
- }
2596
- default: {
2597
- return false;
2598
- }
2599
- }
2600
- }
2601
- /** Handle any trailing data. */
2602
- handleTrailingData() {
2603
- const endIndex = this.buffer.length + this.offset;
2604
- if (this.handleTrailingCommentLikeData(endIndex) || this.handleTrailingMarkupDeclaration(endIndex)) {
2605
- return;
2606
- }
2607
- if (this.sectionStart >= endIndex) {
2608
- return;
2609
- }
2610
- switch (this.state) {
2611
- case State.InTagName:
2612
- case State.BeforeAttributeName:
2613
- case State.BeforeAttributeValue:
2614
- case State.AfterAttributeName:
2615
- case State.InAttributeName:
2616
- case State.InAttributeValueSq:
2617
- case State.InAttributeValueDq:
2618
- case State.InAttributeValueNq:
2619
- case State.InClosingTagName: {
2620
- break;
2621
- }
2622
- default: {
2623
- this.cbs.ontext(this.sectionStart, endIndex);
2624
- }
2625
- }
2626
- }
2627
- emitCodePoint(cp, consumed) {
2628
- if (this.baseState !== State.Text && this.baseState !== State.InSpecialTag) {
2629
- if (this.sectionStart < this.entityStart) {
2630
- this.cbs.onattribdata(this.sectionStart, this.entityStart);
2631
- }
2632
- this.sectionStart = this.entityStart + consumed;
2633
- this.index = this.sectionStart - 1;
2634
- this.cbs.onattribentity(cp);
2635
- } else {
2636
- if (this.sectionStart < this.entityStart) {
2637
- this.cbs.ontext(this.sectionStart, this.entityStart);
2638
- }
2639
- this.sectionStart = this.entityStart + consumed;
2640
- this.index = this.sectionStart - 1;
2641
- this.cbs.ontextentity(cp, this.sectionStart);
2642
- }
2643
- }
2644
- };
2645
-
2646
- // node_modules/.pnpm/htmlparser2@12.0.0/node_modules/htmlparser2/dist/Parser.js
2647
- var { fromCodePoint } = String;
2648
- var formTags = /* @__PURE__ */ new Set([
2649
- "input",
2650
- "option",
2651
- "optgroup",
2652
- "select",
2653
- "button",
2654
- "datalist",
2655
- "textarea"
2656
- ]);
2657
- var pTag = /* @__PURE__ */ new Set(["p"]);
2658
- var headingTags = /* @__PURE__ */ new Set(["h1", "h2", "h3", "h4", "h5", "h6", "p"]);
2659
- var tableSectionTags = /* @__PURE__ */ new Set(["thead", "tbody"]);
2660
- var ddtTags = /* @__PURE__ */ new Set(["dd", "dt"]);
2661
- var rtpTags = /* @__PURE__ */ new Set(["rt", "rp"]);
2662
- var openImpliesClose = /* @__PURE__ */ new Map([
2663
- ["tr", /* @__PURE__ */ new Set(["tr", "th", "td"])],
2664
- ["th", /* @__PURE__ */ new Set(["th"])],
2665
- ["td", /* @__PURE__ */ new Set(["thead", "th", "td"])],
2666
- ["body", /* @__PURE__ */ new Set(["head", "link", "script"])],
2667
- ["a", /* @__PURE__ */ new Set(["a"])],
2668
- ["li", /* @__PURE__ */ new Set(["li"])],
2669
- ["p", pTag],
2670
- ["h1", headingTags],
2671
- ["h2", headingTags],
2672
- ["h3", headingTags],
2673
- ["h4", headingTags],
2674
- ["h5", headingTags],
2675
- ["h6", headingTags],
2676
- ["select", formTags],
2677
- ["input", formTags],
2678
- ["output", formTags],
2679
- ["button", formTags],
2680
- ["datalist", formTags],
2681
- ["textarea", formTags],
2682
- ["option", /* @__PURE__ */ new Set(["option"])],
2683
- ["optgroup", /* @__PURE__ */ new Set(["optgroup", "option"])],
2684
- ["dd", ddtTags],
2685
- ["dt", ddtTags],
2686
- ["address", pTag],
2687
- ["article", pTag],
2688
- ["aside", pTag],
2689
- ["blockquote", pTag],
2690
- ["details", pTag],
2691
- ["div", pTag],
2692
- ["dl", pTag],
2693
- ["fieldset", pTag],
2694
- ["figcaption", pTag],
2695
- ["figure", pTag],
2696
- ["footer", pTag],
2697
- ["form", pTag],
2698
- ["header", pTag],
2699
- ["hr", pTag],
2700
- ["main", pTag],
2701
- ["nav", pTag],
2702
- ["ol", pTag],
2703
- ["pre", pTag],
2704
- ["section", pTag],
2705
- ["table", pTag],
2706
- ["ul", pTag],
2707
- ["rt", rtpTags],
2708
- ["rp", rtpTags],
2709
- ["tbody", tableSectionTags],
2710
- ["tfoot", tableSectionTags]
2711
- ]);
2712
- var DOCUMENT_TYPE = "doctype";
2713
- var voidElements = /* @__PURE__ */ new Set([
2714
- "area",
2715
- "base",
2716
- "basefont",
2717
- "br",
2718
- "col",
2719
- "command",
2720
- "embed",
2721
- "frame",
2722
- "hr",
2723
- "img",
2724
- "input",
2725
- "isindex",
2726
- "keygen",
2727
- "link",
2728
- "meta",
2729
- "param",
2730
- "source",
2731
- "track",
2732
- "wbr"
2733
- ]);
2734
- var foreignContextElements = /* @__PURE__ */ new Set(["math", "svg"]);
2735
- var htmlIntegrationElements = /* @__PURE__ */ new Set([
2736
- "mi",
2737
- "mo",
2738
- "mn",
2739
- "ms",
2740
- "mtext",
2741
- "annotation-xml",
2742
- "foreignObject",
2743
- "desc",
2744
- "title"
2745
- ]);
2746
- var svgTagNameAdjustments = /* @__PURE__ */ new Map([
2747
- ["altglyph", "altGlyph"],
2748
- ["altglyphdef", "altGlyphDef"],
2749
- ["altglyphitem", "altGlyphItem"],
2750
- ["animatecolor", "animateColor"],
2751
- ["animatemotion", "animateMotion"],
2752
- ["animatetransform", "animateTransform"],
2753
- ["clippath", "clipPath"],
2754
- ["feblend", "feBlend"],
2755
- ["fecolormatrix", "feColorMatrix"],
2756
- ["fecomponenttransfer", "feComponentTransfer"],
2757
- ["fecomposite", "feComposite"],
2758
- ["feconvolvematrix", "feConvolveMatrix"],
2759
- ["fediffuselighting", "feDiffuseLighting"],
2760
- ["fedisplacementmap", "feDisplacementMap"],
2761
- ["fedistantlight", "feDistantLight"],
2762
- ["fedropshadow", "feDropShadow"],
2763
- ["feflood", "feFlood"],
2764
- ["fefunca", "feFuncA"],
2765
- ["fefuncb", "feFuncB"],
2766
- ["fefuncg", "feFuncG"],
2767
- ["fefuncr", "feFuncR"],
2768
- ["fegaussianblur", "feGaussianBlur"],
2769
- ["feimage", "feImage"],
2770
- ["femerge", "feMerge"],
2771
- ["femergenode", "feMergeNode"],
2772
- ["femorphology", "feMorphology"],
2773
- ["feoffset", "feOffset"],
2774
- ["fepointlight", "fePointLight"],
2775
- ["fespecularlighting", "feSpecularLighting"],
2776
- ["fespotlight", "feSpotLight"],
2777
- ["fetile", "feTile"],
2778
- ["feturbulence", "feTurbulence"],
2779
- ["foreignobject", "foreignObject"],
2780
- ["glyphref", "glyphRef"],
2781
- ["lineargradient", "linearGradient"],
2782
- ["radialgradient", "radialGradient"],
2783
- ["textpath", "textPath"]
2784
- ]);
2785
- var ForeignContext;
2786
- (function(ForeignContext2) {
2787
- ForeignContext2[ForeignContext2["None"] = 0] = "None";
2788
- ForeignContext2[ForeignContext2["Svg"] = 1] = "Svg";
2789
- ForeignContext2[ForeignContext2["MathML"] = 2] = "MathML";
2790
- })(ForeignContext || (ForeignContext = {}));
2791
- var reNameEnd = /\s|\//;
2792
- var Parser = class {
2793
- options;
2794
- /** The start index of the last event. */
2795
- startIndex = 0;
2796
- /** The end index of the last event. */
2797
- endIndex = 0;
2798
- /**
2799
- * Store the start index of the current open tag,
2800
- * so we can update the start index for attributes.
2801
- */
2802
- openTagStart = 0;
2803
- tagname = "";
2804
- attribname = "";
2805
- attribvalue = "";
2806
- attribs = null;
2807
- stack = [];
2808
- foreignContext;
2809
- cbs;
2810
- lowerCaseTagNames;
2811
- lowerCaseAttributeNames;
2812
- recognizeSelfClosing;
2813
- /** We are parsing HTML. Inverse of the `xmlMode` option. */
2814
- htmlMode;
2815
- tokenizer;
2816
- buffers = [];
2817
- bufferOffset = 0;
2818
- /** The index of the last written buffer. Used when resuming after a `pause()`. */
2819
- writeIndex = 0;
2820
- /** Indicates whether the parser has finished running / `.end` has been called. */
2821
- ended = false;
2822
- constructor(cbs, options = {}) {
2823
- this.options = options;
2824
- this.cbs = cbs ?? {};
2825
- this.htmlMode = !this.options.xmlMode;
2826
- this.lowerCaseTagNames = options.lowerCaseTags ?? this.htmlMode;
2827
- this.lowerCaseAttributeNames = options.lowerCaseAttributeNames ?? this.htmlMode;
2828
- this.recognizeSelfClosing = options.recognizeSelfClosing ?? !this.htmlMode;
2829
- this.tokenizer = new (options.Tokenizer ?? Tokenizer)(this.options, this);
2830
- this.foreignContext = [ForeignContext.None];
2831
- this.cbs.onparserinit?.(this);
2832
- }
2833
- // Tokenizer event handlers
2834
- /**
2835
- * @param start Start index for the current parser event.
2836
- * @param endIndex End index for the current parser event.
2837
- * @internal
2838
- */
2839
- ontext(start, endIndex) {
2840
- const data = this.getSlice(start, endIndex);
2841
- this.endIndex = endIndex - 1;
2842
- this.cbs.ontext?.(data);
2843
- this.startIndex = endIndex;
2844
- }
2845
- /**
2846
- * @param cp Current Unicode code point.
2847
- * @param endIndex End index for the current parser event.
2848
- * @internal
2849
- */
2850
- ontextentity(cp, endIndex) {
2851
- this.endIndex = endIndex - 1;
2852
- this.cbs.ontext?.(fromCodePoint(cp));
2853
- this.startIndex = endIndex;
2854
- }
2855
- /** @internal */
2856
- isInForeignContext() {
2857
- return this.foreignContext[0] !== ForeignContext.None;
2858
- }
2859
- /**
2860
- * Checks if the current tag is a void element. Override this if you want
2861
- * to specify your own additional void elements.
2862
- * @param name Name of the pseudo selector.
2863
- */
2864
- isVoidElement(name) {
2865
- return this.htmlMode && voidElements.has(name);
2866
- }
2867
- /**
2868
- * Read a tag name from the buffer.
2869
- *
2870
- * When `lowerCaseTagNames` is enabled (the default in HTML mode), the name
2871
- * is lowercased and may be adjusted for SVG casing or the `image` → `img`
2872
- * alias.
2873
- * @param start Start index of the tag name in the buffer.
2874
- * @param endIndex End index of the tag name in the buffer.
2875
- */
2876
- readTagName(start, endIndex) {
2877
- const name = this.lowerCaseTagNames ? this.getSlice(start, endIndex).toLowerCase() : this.getSlice(start, endIndex);
2878
- if (!(this.lowerCaseTagNames && this.htmlMode)) {
2879
- return name;
2880
- }
2881
- if (this.foreignContext[0] === ForeignContext.Svg) {
2882
- return svgTagNameAdjustments.get(name) ?? name;
2883
- }
2884
- if (this.foreignContext.length > 1) {
2885
- const adjusted = svgTagNameAdjustments.get(name);
2886
- if (adjusted !== void 0 && this.stack.includes(adjusted)) {
2887
- return adjusted;
2888
- }
2889
- }
2890
- if (!this.isInForeignContext()) {
2891
- return name === "image" ? "img" : name;
2892
- }
2893
- return name;
2894
- }
2895
- /**
2896
- * @param start Start index for the current parser event.
2897
- * @param endIndex End index for the current parser event.
2898
- * @internal
2899
- */
2900
- onopentagname(start, endIndex) {
2901
- this.endIndex = endIndex;
2902
- this.emitOpenTag(this.readTagName(start, endIndex));
2903
- }
2904
- emitOpenTag(name) {
2905
- this.openTagStart = this.startIndex;
2906
- this.tagname = name;
2907
- if (this.htmlMode && name === "form" && this.stack.includes("form")) {
2908
- this.tagname = "";
2909
- return;
2910
- }
2911
- const impliesClose = this.htmlMode && openImpliesClose.get(name);
2912
- if (impliesClose) {
2913
- while (this.stack.length > 0 && impliesClose.has(this.stack[0])) {
2914
- this.popElement(true);
2915
- }
2916
- }
2917
- if (!this.isVoidElement(name)) {
2918
- this.stack.unshift(name);
2919
- if (this.htmlMode) {
2920
- if (name === "svg") {
2921
- this.foreignContext.unshift(ForeignContext.Svg);
2922
- } else if (name === "math") {
2923
- this.foreignContext.unshift(ForeignContext.MathML);
2924
- } else if (htmlIntegrationElements.has(name)) {
2925
- this.foreignContext.unshift(ForeignContext.None);
2926
- }
2927
- }
2928
- }
2929
- this.cbs.onopentagname?.(name);
2930
- if (this.cbs.onopentag)
2931
- this.attribs = {};
2932
- }
2933
- endOpenTag(isImplied) {
2934
- this.startIndex = this.openTagStart;
2935
- if (this.attribs) {
2936
- this.cbs.onopentag?.(this.tagname, this.attribs, isImplied);
2937
- this.attribs = null;
2938
- }
2939
- if (this.cbs.onclosetag && this.isVoidElement(this.tagname)) {
2940
- this.cbs.onclosetag(this.tagname, true);
2941
- }
2942
- this.tagname = "";
2943
- }
2944
- /**
2945
- * @param endIndex End index for the current parser event.
2946
- * @internal
2947
- */
2948
- onopentagend(endIndex) {
2949
- this.endIndex = endIndex;
2950
- this.endOpenTag(false);
2951
- this.startIndex = endIndex + 1;
2952
- }
2953
- /**
2954
- * @param start Start index for the current parser event.
2955
- * @param endIndex End index for the current parser event.
2956
- * @internal
2957
- */
2958
- onclosetag(start, endIndex) {
2959
- this.endIndex = endIndex;
2960
- const name = this.readTagName(start, endIndex);
2961
- if (!this.isVoidElement(name)) {
2962
- const pos = this.stack.indexOf(name);
2963
- if (pos !== -1) {
2964
- for (let index = 0; index < pos; index++) {
2965
- this.popElement(true);
2966
- }
2967
- this.popElement(false);
2968
- } else if (this.htmlMode && name === "p") {
2969
- this.emitOpenTag("p");
2970
- this.closeCurrentTag(true);
2971
- }
2972
- } else if (this.htmlMode && name === "br") {
2973
- this.cbs.onopentagname?.("br");
2974
- this.cbs.onopentag?.("br", {}, true);
2975
- this.cbs.onclosetag?.("br", false);
2976
- }
2977
- this.startIndex = endIndex + 1;
2978
- }
2979
- /**
2980
- * @param endIndex End index for the current parser event.
2981
- * @internal
2982
- */
2983
- onselfclosingtag(endIndex) {
2984
- this.endIndex = endIndex;
2985
- if (this.recognizeSelfClosing || this.isInForeignContext()) {
2986
- this.closeCurrentTag(false);
2987
- this.startIndex = endIndex + 1;
2988
- } else {
2989
- this.onopentagend(endIndex);
2990
- }
2991
- }
2992
- /**
2993
- * Pop the top element off the stack, emit a close event, and maintain
2994
- * the foreign context stack.
2995
- * @param implied Whether this close is implied (not from an explicit end tag).
2996
- */
2997
- popElement(implied) {
2998
- const element = this.stack.shift();
2999
- if (this.htmlMode && (foreignContextElements.has(element) || htmlIntegrationElements.has(element))) {
3000
- this.foreignContext.shift();
3001
- }
3002
- this.cbs.onclosetag?.(element, implied);
3003
- }
3004
- closeCurrentTag(isOpenImplied) {
3005
- const name = this.tagname;
3006
- this.endOpenTag(isOpenImplied);
3007
- if (this.stack[0] === name) {
3008
- this.popElement(!isOpenImplied);
3009
- }
3010
- }
3011
- /**
3012
- * @param start Start index for the current parser event.
3013
- * @param endIndex End index for the current parser event.
3014
- * @internal
3015
- */
3016
- onattribname(start, endIndex) {
3017
- this.startIndex = start;
3018
- const name = this.getSlice(start, endIndex);
3019
- this.attribname = this.lowerCaseAttributeNames ? name.toLowerCase() : name;
3020
- }
3021
- /**
3022
- * @param start Start index for the current parser event.
3023
- * @param endIndex End index for the current parser event.
3024
- * @internal
3025
- */
3026
- onattribdata(start, endIndex) {
3027
- this.attribvalue += this.getSlice(start, endIndex);
3028
- }
3029
- /**
3030
- * @param cp Current Unicode code point.
3031
- * @internal
3032
- */
3033
- onattribentity(cp) {
3034
- this.attribvalue += fromCodePoint(cp);
3035
- }
3036
- /**
3037
- * @param quote Quote type used for the current attribute.
3038
- * @param endIndex End index for the current parser event.
3039
- * @internal
3040
- */
3041
- onattribend(quote2, endIndex) {
3042
- this.endIndex = endIndex;
3043
- this.cbs.onattribute?.(this.attribname, this.attribvalue, quote2 === QuoteType.Double ? '"' : quote2 === QuoteType.Single ? "'" : quote2 === QuoteType.NoValue ? void 0 : null);
3044
- if (this.attribs && !Object.hasOwn(this.attribs, this.attribname)) {
3045
- this.attribs[this.attribname] = this.attribvalue;
3046
- }
3047
- this.attribvalue = "";
3048
- }
3049
- getInstructionName(value) {
3050
- const index = value.search(reNameEnd);
3051
- let name = index < 0 ? value : value.substr(0, index);
3052
- if (this.lowerCaseTagNames) {
3053
- name = name.toLowerCase();
3054
- }
3055
- return name;
3056
- }
3057
- /**
3058
- * @param start Start index for the current parser event.
3059
- * @param endIndex End index for the current parser event.
3060
- * @internal
3061
- */
3062
- ondeclaration(start, endIndex) {
3063
- this.endIndex = endIndex;
3064
- const value = this.getSlice(start, endIndex);
3065
- if (this.cbs.onprocessinginstruction) {
3066
- const name = this.htmlMode ? this.lowerCaseTagNames ? DOCUMENT_TYPE : value.slice(0, DOCUMENT_TYPE.length) : this.getInstructionName(value);
3067
- this.cbs.onprocessinginstruction(`!${name}`, `!${value}`);
3068
- }
3069
- this.startIndex = endIndex + 1;
3070
- }
3071
- /**
3072
- * @param start Start index for the current parser event.
3073
- * @param endIndex End index for the current parser event.
3074
- * @internal
3075
- */
3076
- onprocessinginstruction(start, endIndex) {
3077
- this.endIndex = endIndex;
3078
- const value = this.getSlice(start, endIndex);
3079
- if (this.cbs.onprocessinginstruction) {
3080
- const name = this.getInstructionName(value);
3081
- this.cbs.onprocessinginstruction(`?${name}`, `?${value}`);
3082
- }
3083
- this.startIndex = endIndex + 1;
3084
- }
3085
- /**
3086
- * @param start Start index for the current parser event.
3087
- * @param endIndex End index for the current parser event.
3088
- * @param offset Offset applied when computing parser indices.
3089
- * @internal
3090
- */
3091
- oncomment(start, endIndex, offset) {
3092
- this.endIndex = endIndex;
3093
- this.cbs.oncomment?.(this.getSlice(start, endIndex - offset));
3094
- this.cbs.oncommentend?.();
3095
- this.startIndex = endIndex + 1;
3096
- }
3097
- /**
3098
- * @param start Start index for the current parser event.
3099
- * @param endIndex End index for the current parser event.
3100
- * @param offset Offset applied when computing parser indices.
3101
- * @internal
3102
- */
3103
- oncdata(start, endIndex, offset) {
3104
- this.endIndex = endIndex;
3105
- const value = this.getSlice(start, endIndex - offset);
3106
- if (!this.htmlMode || this.options.recognizeCDATA) {
3107
- this.cbs.oncdatastart?.();
3108
- this.cbs.ontext?.(value);
3109
- this.cbs.oncdataend?.();
3110
- } else if (this.isInForeignContext()) {
3111
- this.cbs.ontext?.(value);
3112
- } else {
3113
- this.cbs.oncomment?.(`[CDATA[${value}]]`);
3114
- this.cbs.oncommentend?.();
3115
- }
3116
- this.startIndex = endIndex + 1;
3117
- }
3118
- /** @internal */
3119
- onend() {
3120
- if (this.cbs.onclosetag) {
3121
- this.endIndex = this.startIndex;
3122
- for (let index = 0; index < this.stack.length; index++) {
3123
- this.cbs.onclosetag(this.stack[index], true);
3124
- }
3125
- }
3126
- this.cbs.onend?.();
3127
- }
3128
- /**
3129
- * Resets the parser to a blank state, ready to parse a new HTML document
3130
- */
3131
- reset() {
3132
- this.cbs.onreset?.();
3133
- this.tokenizer.reset();
3134
- this.tagname = "";
3135
- this.attribname = "";
3136
- this.attribvalue = "";
3137
- this.attribs = null;
3138
- this.stack.length = 0;
3139
- this.startIndex = 0;
3140
- this.endIndex = 0;
3141
- this.cbs.onparserinit?.(this);
3142
- this.buffers.length = 0;
3143
- this.foreignContext.length = 0;
3144
- this.foreignContext.unshift(ForeignContext.None);
3145
- this.bufferOffset = 0;
3146
- this.writeIndex = 0;
3147
- this.ended = false;
3148
- }
3149
- /**
3150
- * Resets the parser, then parses a complete document and
3151
- * pushes it to the handler.
3152
- * @param data Document to parse.
3153
- */
3154
- parseComplete(data) {
3155
- this.reset();
3156
- this.end(data);
3157
- }
3158
- getSlice(start, end) {
3159
- if (start === end) {
3160
- return "";
3161
- }
3162
- while (start - this.bufferOffset >= this.buffers[0].length) {
3163
- this.shiftBuffer();
3164
- }
3165
- let slice = this.buffers[0].slice(start - this.bufferOffset, end - this.bufferOffset);
3166
- while (end - this.bufferOffset > this.buffers[0].length) {
3167
- this.shiftBuffer();
3168
- slice += this.buffers[0].slice(0, end - this.bufferOffset);
3169
- }
3170
- return slice;
3171
- }
3172
- shiftBuffer() {
3173
- this.bufferOffset += this.buffers[0].length;
3174
- this.writeIndex--;
3175
- this.buffers.shift();
3176
- }
3177
- /**
3178
- * Parses a chunk of data and calls the corresponding callbacks.
3179
- * @param chunk Chunk to parse.
3180
- */
3181
- write(chunk) {
3182
- if (this.ended) {
3183
- this.cbs.onerror?.(new Error(".write() after done!"));
3184
- return;
3185
- }
3186
- this.buffers.push(chunk);
3187
- if (this.tokenizer.running) {
3188
- this.tokenizer.write(chunk);
3189
- this.writeIndex++;
3190
- }
3191
- }
3192
- /**
3193
- * Parses the end of the buffer and clears the stack, calls onend.
3194
- * @param chunk Optional final chunk to parse.
3195
- */
3196
- end(chunk) {
3197
- if (this.ended) {
3198
- this.cbs.onerror?.(new Error(".end() after done!"));
3199
- return;
3200
- }
3201
- if (chunk)
3202
- this.write(chunk);
3203
- this.ended = true;
3204
- this.tokenizer.end();
3205
- }
3206
- /**
3207
- * Pauses parsing. The parser won't emit events until `resume` is called.
3208
- */
3209
- pause() {
3210
- this.tokenizer.pause();
3211
- }
3212
- /**
3213
- * Resumes parsing after `pause` was called.
3214
- */
3215
- resume() {
3216
- this.tokenizer.resume();
3217
- while (this.tokenizer.running && this.writeIndex < this.buffers.length) {
3218
- this.tokenizer.write(this.buffers[this.writeIndex++]);
3219
- }
3220
- if (this.ended)
3221
- this.tokenizer.end();
3222
- }
3223
- };
3224
-
3225
- // node_modules/.pnpm/domelementtype@3.0.0/node_modules/domelementtype/dist/index.js
3226
- var ElementType;
3227
- (function(ElementType2) {
3228
- ElementType2["Root"] = "root";
3229
- ElementType2["Text"] = "text";
3230
- ElementType2["Directive"] = "directive";
3231
- ElementType2["Comment"] = "comment";
3232
- ElementType2["Script"] = "script";
3233
- ElementType2["Style"] = "style";
3234
- ElementType2["Tag"] = "tag";
3235
- ElementType2["CDATA"] = "cdata";
3236
- ElementType2["Doctype"] = "doctype";
3237
- })(ElementType || (ElementType = {}));
3238
- function isTag(element) {
3239
- return element.type === ElementType.Tag || element.type === ElementType.Script || element.type === ElementType.Style;
3240
- }
3241
- var Root = ElementType.Root;
3242
- var Text = ElementType.Text;
3243
- var Directive = ElementType.Directive;
3244
- var Comment = ElementType.Comment;
3245
- var Script = ElementType.Script;
3246
- var Style = ElementType.Style;
3247
- var Tag = ElementType.Tag;
3248
- var CDATA = ElementType.CDATA;
3249
- var Doctype = ElementType.Doctype;
3250
-
3251
- // node_modules/.pnpm/domhandler@6.0.1/node_modules/domhandler/dist/node.js
3252
- var Node = class {
3253
- /** Parent of the node */
3254
- parent = null;
3255
- /** Previous sibling */
3256
- prev = null;
3257
- /** Next sibling */
3258
- next = null;
3259
- /** The start index of the node. Requires `withStartIndices` on the handler to be `true. */
3260
- startIndex = null;
3261
- /** The end index of the node. Requires `withEndIndices` on the handler to be `true. */
3262
- endIndex = null;
3263
- // Read-write aliases for properties
3264
- /**
3265
- * Same as {@link parent}.
3266
- * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
3267
- */
3268
- get parentNode() {
3269
- return this.parent;
3270
- }
3271
- set parentNode(parent) {
3272
- this.parent = parent;
3273
- }
3274
- /**
3275
- * Same as {@link prev}.
3276
- * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
3277
- */
3278
- get previousSibling() {
3279
- return this.prev;
3280
- }
3281
- set previousSibling(previous) {
3282
- this.prev = previous;
3283
- }
3284
- /**
3285
- * Same as {@link next}.
3286
- * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
3287
- */
3288
- get nextSibling() {
3289
- return this.next;
3290
- }
3291
- set nextSibling(next) {
3292
- this.next = next;
3293
- }
3294
- /**
3295
- * Clone this node, and optionally its children.
3296
- * @param recursive Clone child nodes as well.
3297
- * @returns A clone of the node.
3298
- */
3299
- cloneNode(recursive = false) {
3300
- return cloneNode(this, recursive);
3301
- }
3302
- };
3303
- var DataNode = class extends Node {
3304
- data;
3305
- /**
3306
- * @param data The content of the data node
3307
- */
3308
- constructor(data) {
3309
- super();
3310
- this.data = data;
3311
- }
3312
- /**
3313
- * Same as {@link data}.
3314
- * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
3315
- */
3316
- get nodeValue() {
3317
- return this.data;
3318
- }
3319
- set nodeValue(data) {
3320
- this.data = data;
3321
- }
3322
- };
3323
- var Text2 = class extends DataNode {
3324
- type = ElementType.Text;
3325
- get nodeType() {
3326
- return 3;
3327
- }
3328
- };
3329
- var Comment2 = class extends DataNode {
3330
- type = ElementType.Comment;
3331
- get nodeType() {
3332
- return 8;
3333
- }
3334
- };
3335
- var ProcessingInstruction = class extends DataNode {
3336
- type = ElementType.Directive;
3337
- name;
3338
- constructor(name, data) {
3339
- super(data);
3340
- this.name = name;
3341
- }
3342
- get nodeType() {
3343
- return 1;
3344
- }
3345
- /** If this is a doctype, the document type name (parse5 only). */
3346
- "x-name";
3347
- /** If this is a doctype, the document type public identifier (parse5 only). */
3348
- "x-publicId";
3349
- /** If this is a doctype, the document type system identifier (parse5 only). */
3350
- "x-systemId";
3351
- };
3352
- var NodeWithChildren = class extends Node {
3353
- children;
3354
- /**
3355
- * @param children Children of the node. Only certain node types can have children.
3356
- */
3357
- constructor(children) {
3358
- super();
3359
- this.children = children;
3360
- }
3361
- // Aliases
3362
- /** First child of the node. */
3363
- get firstChild() {
3364
- return this.children[0] ?? null;
3365
- }
3366
- /** Last child of the node. */
3367
- get lastChild() {
3368
- return this.children.length > 0 ? this.children[this.children.length - 1] : null;
3369
- }
3370
- /**
3371
- * Same as {@link children}.
3372
- * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
3373
- */
3374
- get childNodes() {
3375
- return this.children;
3376
- }
3377
- set childNodes(children) {
3378
- this.children = children;
3379
- }
3380
- };
3381
- var CDATA2 = class extends NodeWithChildren {
3382
- type = ElementType.CDATA;
3383
- get nodeType() {
3384
- return 4;
3385
- }
3386
- };
3387
- var Document = class extends NodeWithChildren {
3388
- type = ElementType.Root;
3389
- get nodeType() {
3390
- return 9;
3391
- }
3392
- };
3393
- var Element = class extends NodeWithChildren {
3394
- name;
3395
- attribs;
3396
- type;
3397
- /**
3398
- * @param name Name of the tag, eg. `div`, `span`.
3399
- * @param attribs Object mapping attribute names to attribute values.
3400
- * @param children Children of the node.
3401
- * @param type Node type used for the new node instance.
3402
- */
3403
- constructor(name, attribs, children = [], type = name === "script" ? ElementType.Script : name === "style" ? ElementType.Style : ElementType.Tag) {
3404
- super(children);
3405
- this.name = name;
3406
- this.attribs = attribs;
3407
- this.type = type;
3408
- }
3409
- get nodeType() {
3410
- return 1;
3411
- }
3412
- // DOM Level 1 aliases
3413
- /**
3414
- * Same as {@link name}.
3415
- * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
3416
- */
3417
- get tagName() {
3418
- return this.name;
3419
- }
3420
- set tagName(name) {
3421
- this.name = name;
3422
- }
3423
- get attributes() {
3424
- return Object.keys(this.attribs).map((name) => ({
3425
- name,
3426
- value: this.attribs[name],
3427
- namespace: this["x-attribsNamespace"]?.[name],
3428
- prefix: this["x-attribsPrefix"]?.[name]
3429
- }));
3430
- }
3431
- /** Element namespace (parse5 only). */
3432
- namespace;
3433
- /** Element attribute namespaces (parse5 only). */
3434
- "x-attribsNamespace";
3435
- /** Element attribute namespace-related prefixes (parse5 only). */
3436
- "x-attribsPrefix";
3437
- };
3438
- function isTag2(node) {
3439
- return isTag(node);
3440
- }
3441
- function isCDATA(node) {
3442
- return node.type === ElementType.CDATA;
3443
- }
3444
- function isText(node) {
3445
- return node.type === ElementType.Text;
3446
- }
3447
- function isComment(node) {
3448
- return node.type === ElementType.Comment;
3449
- }
3450
- function isDirective(node) {
3451
- return node.type === ElementType.Directive;
3452
- }
3453
- function isDocument(node) {
3454
- return node.type === ElementType.Root;
3455
- }
3456
- function cloneNode(node, recursive = false) {
3457
- let result;
3458
- if (isText(node)) {
3459
- result = new Text2(node.data);
3460
- } else if (isComment(node)) {
3461
- result = new Comment2(node.data);
3462
- } else if (isTag2(node)) {
3463
- const children = recursive ? cloneChildren(node.children) : [];
3464
- const clone = new Element(node.name, { ...node.attribs }, children);
3465
- for (const child of children) {
3466
- child.parent = clone;
3467
- }
3468
- if (node.namespace != null) {
3469
- clone.namespace = node.namespace;
3470
- }
3471
- if (node["x-attribsNamespace"]) {
3472
- clone["x-attribsNamespace"] = { ...node["x-attribsNamespace"] };
3473
- }
3474
- if (node["x-attribsPrefix"]) {
3475
- clone["x-attribsPrefix"] = { ...node["x-attribsPrefix"] };
3476
- }
3477
- result = clone;
3478
- } else if (isCDATA(node)) {
3479
- const children = recursive ? cloneChildren(node.children) : [];
3480
- const clone = new CDATA2(children);
3481
- for (const child of children) {
3482
- child.parent = clone;
3483
- }
3484
- result = clone;
3485
- } else if (isDocument(node)) {
3486
- const children = recursive ? cloneChildren(node.children) : [];
3487
- const clone = new Document(children);
3488
- for (const child of children) {
3489
- child.parent = clone;
3490
- }
3491
- if (node["x-mode"]) {
3492
- clone["x-mode"] = node["x-mode"];
3493
- }
3494
- result = clone;
3495
- } else if (isDirective(node)) {
3496
- const instruction = new ProcessingInstruction(node.name, node.data);
3497
- if (node["x-name"] != null) {
3498
- instruction["x-name"] = node["x-name"];
3499
- instruction["x-publicId"] = node["x-publicId"];
3500
- instruction["x-systemId"] = node["x-systemId"];
3501
- }
3502
- result = instruction;
3503
- } else {
3504
- throw new Error(`Not implemented yet: ${node.type}`);
3505
- }
3506
- result.startIndex = node.startIndex;
3507
- result.endIndex = node.endIndex;
3508
- if (node.sourceCodeLocation != null) {
3509
- result.sourceCodeLocation = node.sourceCodeLocation;
3510
- }
3511
- return result;
3512
- }
3513
- function cloneChildren(childs) {
3514
- const children = childs.map((child) => cloneNode(child, true));
3515
- for (let index = 1; index < children.length; index++) {
3516
- children[index].prev = children[index - 1];
3517
- children[index - 1].next = children[index];
3518
- }
3519
- return children;
3520
- }
3521
-
3522
- // node_modules/.pnpm/domhandler@6.0.1/node_modules/domhandler/dist/index.js
3523
- var defaultOptions = {
3524
- withStartIndices: false,
3525
- withEndIndices: false,
3526
- xmlMode: false
3527
- };
3528
- var DomHandler = class {
3529
- /** The elements of the DOM */
3530
- dom = [];
3531
- /** The root element for the DOM */
3532
- root = new Document(this.dom);
3533
- /** Called once parsing has completed. */
3534
- callback;
3535
- /** Settings for the handler. */
3536
- options;
3537
- /** Callback whenever a tag is closed. */
3538
- elementCB;
3539
- /** Indicated whether parsing has been completed. */
3540
- done = false;
3541
- /** Stack of open tags. */
3542
- tagStack = [this.root];
3543
- /** A data node that is still being written to. */
3544
- lastNode = null;
3545
- /** Reference to the parser instance. Used for location information. */
3546
- parser = null;
3547
- /**
3548
- * @param callback Called once parsing has completed.
3549
- * @param options Settings for the handler.
3550
- * @param elementCB Callback whenever a tag is closed.
3551
- */
3552
- constructor(callback, options, elementCB) {
3553
- if (typeof options === "function") {
3554
- elementCB = options;
3555
- options = defaultOptions;
3556
- }
3557
- if (typeof callback === "object") {
3558
- options = callback;
3559
- callback = void 0;
3560
- }
3561
- this.callback = callback ?? null;
3562
- this.options = options ?? defaultOptions;
3563
- this.elementCB = elementCB ?? null;
3564
- }
3565
- onparserinit(parser) {
3566
- this.parser = parser;
3567
- }
3568
- // Resets the handler back to starting state
3569
- onreset() {
3570
- this.dom = [];
3571
- this.root = new Document(this.dom);
3572
- this.done = false;
3573
- this.tagStack = [this.root];
3574
- this.lastNode = null;
3575
- this.parser = null;
3576
- }
3577
- // Signals the handler that parsing is done
3578
- onend() {
3579
- if (this.done)
3580
- return;
3581
- this.done = true;
3582
- this.parser = null;
3583
- this.handleCallback(null);
3584
- }
3585
- onerror(error) {
3586
- this.handleCallback(error);
3587
- }
3588
- onclosetag() {
3589
- this.lastNode = null;
3590
- const element = this.tagStack.pop();
3591
- if (this.options.withEndIndices && this.parser) {
3592
- element.endIndex = this.parser.endIndex;
3593
- }
3594
- if (this.elementCB)
3595
- this.elementCB(element);
3596
- }
3597
- onopentag(name, attribs) {
3598
- const type = this.options.xmlMode ? ElementType.Tag : void 0;
3599
- const element = new Element(name, attribs, void 0, type);
3600
- this.addNode(element);
3601
- this.tagStack.push(element);
3602
- }
3603
- ontext(data) {
3604
- const { lastNode } = this;
3605
- if (lastNode && lastNode.type === ElementType.Text) {
3606
- lastNode.data += data;
3607
- if (this.options.withEndIndices && this.parser) {
3608
- lastNode.endIndex = this.parser.endIndex;
3609
- }
3610
- } else {
3611
- const node = new Text2(data);
3612
- this.addNode(node);
3613
- this.lastNode = node;
3614
- }
3615
- }
3616
- oncomment(data) {
3617
- if (this.lastNode && this.lastNode.type === ElementType.Comment) {
3618
- this.lastNode.data += data;
3619
- return;
3620
- }
3621
- const node = new Comment2(data);
3622
- this.addNode(node);
3623
- this.lastNode = node;
3624
- }
3625
- oncommentend() {
3626
- this.lastNode = null;
3627
- }
3628
- oncdatastart() {
3629
- const text = new Text2("");
3630
- const node = new CDATA2([text]);
3631
- this.addNode(node);
3632
- text.parent = node;
3633
- this.lastNode = text;
3634
- }
3635
- oncdataend() {
3636
- this.lastNode = null;
3637
- }
3638
- onprocessinginstruction(name, data) {
3639
- const node = new ProcessingInstruction(name, data);
3640
- this.addNode(node);
3641
- }
3642
- handleCallback(error) {
3643
- if (typeof this.callback === "function") {
3644
- this.callback(error, this.dom);
3645
- } else if (error) {
3646
- throw error;
3647
- }
3648
- }
3649
- addNode(node) {
3650
- const parent = this.tagStack[this.tagStack.length - 1];
3651
- const previousSibling = parent.children[parent.children.length - 1];
3652
- if (this.options.withStartIndices && this.parser) {
3653
- node.startIndex = this.parser.startIndex;
3654
- }
3655
- if (this.options.withEndIndices && this.parser) {
3656
- node.endIndex = this.parser.endIndex;
3657
- }
3658
- parent.children.push(node);
3659
- if (previousSibling) {
3660
- node.prev = previousSibling;
3661
- previousSibling.next = node;
3662
- }
3663
- node.parent = parent;
3664
- this.lastNode = null;
3665
- }
3666
- };
3667
-
3668
- // node_modules/.pnpm/htmlparser2@12.0.0/node_modules/htmlparser2/dist/index.js
3669
- function parseDocument(data, options) {
3670
- const handler = new DomHandler(void 0, options);
3671
- new Parser(handler, options).end(data);
3672
- return handler.root;
3673
- }
3674
-
3675
- // src/html-parser.ts
3676
- function decompileHTML(html) {
3677
- const diagnostics = [];
3678
- const story = createStory();
3679
- const doc = parseDocument(html);
3680
- const twine2Data = findElement(doc, "tw-storydata");
3681
- if (twine2Data) {
3682
- decompileTwine2(twine2Data, story, diagnostics);
3683
- return { story, diagnostics };
3684
- }
3685
- const twine1Data = findElementByIdPattern(doc, /^store(?:-a|A)rea$/);
3686
- if (twine1Data) {
3687
- decompileTwine1(twine1Data, story, diagnostics);
3688
- return { story, diagnostics };
3689
- }
3690
- diagnostics.push({ level: "error", message: "Malformed HTML source; story data not found." });
3691
- return { story, diagnostics };
3692
- }
3693
- function decompileTwine2(storyData, story, diagnostics) {
3694
- let startnode = 0;
3695
- const attrs = storyData.attribs ?? {};
3696
- if (attrs["name"]) story.name = attrs["name"];
3697
- if (attrs["startnode"]) {
3698
- const parsed = parseInt(attrs["startnode"], 10);
3699
- if (Number.isNaN(parsed)) {
3700
- diagnostics.push({
3701
- level: "warning",
3702
- message: `Cannot parse "tw-storydata" content attribute "startnode" as an integer; value "${attrs["startnode"]}".`
3703
- });
3704
- } else {
3705
- startnode = parsed;
3706
- }
3707
- }
3708
- if (attrs["ifid"]) story.ifid = attrs["ifid"].toUpperCase();
3709
- if (attrs["zoom"]) {
3710
- const parsed = parseFloat(attrs["zoom"]);
3711
- if (Number.isNaN(parsed)) {
3712
- diagnostics.push({
3713
- level: "warning",
3714
- message: `Cannot parse "tw-storydata" content attribute "zoom" as a float; value "${attrs["zoom"]}".`
3715
- });
3716
- } else {
3717
- story.twine2.zoom = parsed;
3718
- }
3719
- }
3720
- if (attrs["format"]) story.twine2.format = attrs["format"];
3721
- if (attrs["format-version"]) story.twine2.formatVersion = attrs["format-version"];
3722
- if (attrs["options"]) {
3723
- for (const opt of attrs["options"].split(/\s+/).filter((s) => s.length > 0)) {
3724
- story.twine2.options.set(opt, true);
3725
- }
3726
- }
3727
- for (const node of storyData.children ?? []) {
3728
- if (node.type !== "tag" && node.type !== "style" && node.type !== "script") continue;
3729
- switch (node.name) {
3730
- case "style":
3731
- case "script": {
3732
- const text = getTextContent(node).trim();
3733
- if (text.length === 0) continue;
3734
- const name = node.name === "style" ? "Story Stylesheet" : "Story JavaScript";
3735
- const tags = node.name === "style" ? ["stylesheet"] : ["script"];
3736
- storyAdd(story, { name, tags, text }, diagnostics);
3737
- break;
3738
- }
3739
- case "tw-tag": {
3740
- const tagAttrs = node.attribs ?? {};
3741
- const tagName = tagAttrs["name"] ?? "";
3742
- const tagColor = tagAttrs["color"] ?? "";
3743
- if (tagName) story.twine2.tagColors.set(tagName, tagColor);
3744
- break;
3745
- }
3746
- case "tw-passagedata": {
3747
- let pid = 0;
3748
- const pAttrs = node.attribs ?? {};
3749
- const name = pAttrs["name"] ?? "";
3750
- const tags = pAttrs["tags"] ? pAttrs["tags"].split(/\s+/).filter((s) => s.length > 0) : [];
3751
- const metadata = {};
3752
- if (pAttrs["pid"]) {
3753
- const parsed = parseInt(pAttrs["pid"], 10);
3754
- if (Number.isNaN(parsed)) {
3755
- diagnostics.push({
3756
- level: "warning",
3757
- message: `Cannot parse "tw-passagedata" content attribute "pid" as an integer; value "${pAttrs["pid"]}".`
3758
- });
3759
- } else {
3760
- pid = parsed;
3761
- }
3762
- }
3763
- if (pAttrs["position"]) metadata.position = pAttrs["position"];
3764
- if (pAttrs["size"]) metadata.size = pAttrs["size"];
3765
- if (pid === startnode && pid !== 0) {
3766
- story.twine2.start = name;
3767
- }
3768
- const text = getTextContent(node).trim();
3769
- const passage = { name, tags, text };
3770
- if (metadata.position || metadata.size) {
3771
- passage.metadata = metadata;
3772
- }
3773
- storyAdd(story, passage, diagnostics);
3774
- break;
3775
- }
3776
- }
3777
- }
3778
- storyPrepend(story, { name: "StoryData", tags: [], text: marshalStoryData(story) }, diagnostics);
3779
- }
3780
- function decompileTwine1(storeArea, story, diagnostics) {
3781
- for (const node of storeArea.children ?? []) {
3782
- if (node.type !== "tag" || node.name !== "div") continue;
3783
- const nodeAttrs = node.attribs ?? {};
3784
- if (!("tiddler" in nodeAttrs)) continue;
3785
- const name = nodeAttrs["tiddler"] ?? "";
3786
- const tags = nodeAttrs["tags"] ? nodeAttrs["tags"].split(/\s+/).filter((s) => s.length > 0) : [];
3787
- const metadata = {};
3788
- if (nodeAttrs["twine-position"]) metadata.position = nodeAttrs["twine-position"];
3789
- const rawContent = getTextContent(node);
3790
- const text = tiddlerUnescape(rawContent).trim();
3791
- const passage = { name, tags, text };
3792
- if (metadata.position) {
3793
- passage.metadata = metadata;
3794
- }
3795
- storyAdd(story, passage, diagnostics);
3796
- }
3797
- }
3798
- function findElement(node, tagName) {
3799
- if (node.type === "tag" && node.name === tagName) return node;
3800
- for (const child of node.children ?? []) {
3801
- const found = findElement(child, tagName);
3802
- if (found) return found;
3803
- }
3804
- return void 0;
3805
- }
3806
- function findElementByIdPattern(node, idPattern) {
3807
- if (node.type === "tag" && node.attribs?.["id"] && idPattern.test(node.attribs["id"])) return node;
3808
- for (const child of node.children ?? []) {
3809
- const found = findElementByIdPattern(child, idPattern);
3810
- if (found) return found;
3811
- }
3812
- return void 0;
3813
- }
3814
- function getTextContent(el) {
3815
- let text = "";
3816
- for (const child of el.children ?? []) {
3817
- if (child.type === "text" && child.data) {
3818
- text += child.data;
3819
- }
3820
- }
3821
- return text;
3822
- }
3823
-
3824
- // src/loader.ts
3825
- function loadSources(story, filenames, opts, diagnostics, processedFiles) {
3826
- for (const filename of filenames) {
3827
- if (processedFiles.has(filename)) {
3828
- diagnostics.push({ level: "warning", message: `load ${filename}: Skipping duplicate.` });
3829
- continue;
3830
- }
3831
- const ext = normalizedFileExt(filename);
3832
- try {
3833
- switch (ext) {
3834
- case "tw":
3835
- case "twee":
3836
- loadTwee(story, filename, opts, diagnostics);
3837
- break;
3838
- case "tw2":
3839
- case "twee2":
3840
- loadTwee(story, filename, { ...opts, twee2Compat: true }, diagnostics);
3841
- break;
3842
- case "htm":
3843
- case "html":
3844
- loadHTML(story, filename, diagnostics);
3845
- break;
3846
- case "css":
3847
- loadTagged(story, "stylesheet", filename, diagnostics);
3848
- break;
3849
- case "js":
3850
- loadTagged(story, "script", filename, diagnostics);
3851
- break;
3852
- case "otf":
3853
- case "ttf":
3854
- case "woff":
3855
- case "woff2":
3856
- loadFont(story, filename, diagnostics);
3857
- break;
3858
- case "gif":
3859
- case "jpeg":
3860
- case "jpg":
3861
- case "png":
3862
- case "svg":
3863
- case "tif":
3864
- case "tiff":
3865
- case "webp":
3866
- loadMedia(story, "Twine.image", filename, diagnostics);
3867
- break;
3868
- case "aac":
3869
- case "flac":
3870
- case "m4a":
3871
- case "mp3":
3872
- case "oga":
3873
- case "ogg":
3874
- case "opus":
3875
- case "wav":
3876
- case "wave":
3877
- case "weba":
3878
- loadMedia(story, "Twine.audio", filename, diagnostics);
3879
- break;
3880
- case "mp4":
3881
- case "ogv":
3882
- case "webm":
3883
- loadMedia(story, "Twine.video", filename, diagnostics);
3884
- break;
3885
- case "vtt":
3886
- loadMedia(story, "Twine.vtt", filename, diagnostics);
3887
- break;
3888
- default:
3889
- continue;
3890
- }
3891
- } catch (e) {
3892
- diagnostics.push({
3893
- level: "error",
3894
- message: `load ${filename}: ${e instanceof Error ? e.message : String(e)}`,
3895
- file: filename
3896
- });
3897
- continue;
3898
- }
3899
- processedFiles.add(filename);
3900
- }
3901
- if (story.name !== "" && !story.passages.some((p) => p.name === "StoryTitle")) {
3902
- storyPrepend(story, { name: "StoryTitle", tags: [], text: story.name }, diagnostics);
3903
- }
3904
- }
3905
- function loadInlineSources(story, sources, opts, diagnostics) {
3906
- for (const source of sources) {
3907
- if (typeof source === "string") {
3908
- continue;
3909
- }
3910
- const content = typeof source.content === "string" ? source.content : source.content.toString("utf-8");
3911
- const ext = normalizedFileExt(source.filename);
3912
- switch (ext) {
3913
- case "":
3914
- case "tw":
3915
- case "twee":
3916
- case "tw2":
3917
- case "twee2": {
3918
- const twee2 = ext === "tw2" || ext === "twee2" || opts.twee2Compat;
3919
- const result = parseTwee(content, {
3920
- filename: source.filename,
3921
- trim: opts.trim ?? true,
3922
- twee2Compat: twee2
3923
- });
3924
- diagnostics.push(...result.diagnostics);
3925
- for (const p of result.passages) {
3926
- storyAdd(story, p, diagnostics);
3927
- }
3928
- break;
3929
- }
3930
- case "css":
3931
- storyAdd(story, { name: basename(source.filename), tags: ["stylesheet"], text: content }, diagnostics);
3932
- break;
3933
- case "js":
3934
- storyAdd(story, { name: basename(source.filename), tags: ["script"], text: content }, diagnostics);
3935
- break;
3936
- default:
3937
- diagnostics.push({
3938
- level: "warning",
3939
- message: `load ${source.filename}: in-memory sources of type .${ext} are not supported; skipped.`
3940
- });
3941
- }
3942
- }
3943
- }
3944
- function parseTweeFile(filename, opts) {
3945
- const source = readUTF8(filename);
3946
- const result = parseTwee(source, {
3947
- filename,
3948
- trim: opts.trim ?? true,
3949
- twee2Compat: opts.twee2Compat ?? false
3950
- });
3951
- return { passages: result.passages, diagnostics: [...result.diagnostics] };
3952
- }
3953
- function parseTaggedFile(tag, filename) {
3954
- const source = readUTF8(filename);
3955
- return { passages: [{ name: basename(filename), tags: [tag], text: source }], diagnostics: [] };
3956
- }
3957
- function parseMediaFile(tag, filename) {
3958
- const source = readBase64(filename);
3959
- const name = baseNameWithoutExt(filename);
3960
- return {
3961
- passages: [{ name, tags: [tag], text: `data:${mediaTypeFromFilename(filename)};base64,${source}` }],
3962
- diagnostics: []
3963
- };
3964
- }
3965
- function parseFontFile(filename) {
3966
- const source = readBase64(filename);
3967
- const name = basename(filename);
3968
- const family = baseNameWithoutExt(filename);
3969
- const ext = normalizedFileExt(filename);
3970
- const mediaType = mediaTypeFromExt(ext);
3971
- const hint = fontFormatHint(ext);
3972
- return {
3973
- passages: [
3974
- {
3975
- name,
3976
- tags: ["stylesheet"],
3977
- text: `@font-face {
3978
- font-family: "${family}";
3979
- src: url("data:${mediaType};base64,${source}") format("${hint}");
3980
- }`
3981
- }
3982
- ],
3983
- diagnostics: []
3984
- };
3985
- }
3986
- function parseHTMLFile(filename) {
3987
- const source = readUTF8(filename);
3988
- const result = decompileHTML(source);
3989
- return { passages: result.story.passages, diagnostics: [...result.diagnostics] };
3990
- }
3991
- function parseFile(filename, opts) {
3992
- const ext = normalizedFileExt(filename);
3993
- switch (ext) {
3994
- case "tw":
3995
- case "twee":
3996
- return parseTweeFile(filename, opts);
3997
- case "tw2":
3998
- case "twee2":
3999
- return parseTweeFile(filename, { ...opts, twee2Compat: true });
4000
- case "htm":
4001
- case "html":
4002
- return parseHTMLFile(filename);
4003
- case "css":
4004
- return parseTaggedFile("stylesheet", filename);
4005
- case "js":
4006
- return parseTaggedFile("script", filename);
4007
- case "otf":
4008
- case "ttf":
4009
- case "woff":
4010
- case "woff2":
4011
- return parseFontFile(filename);
4012
- case "gif":
4013
- case "jpeg":
4014
- case "jpg":
4015
- case "png":
4016
- case "svg":
4017
- case "tif":
4018
- case "tiff":
4019
- case "webp":
4020
- return parseMediaFile("Twine.image", filename);
4021
- case "aac":
4022
- case "flac":
4023
- case "m4a":
4024
- case "mp3":
4025
- case "oga":
4026
- case "ogg":
4027
- case "opus":
4028
- case "wav":
4029
- case "wave":
4030
- case "weba":
4031
- return parseMediaFile("Twine.audio", filename);
4032
- case "mp4":
4033
- case "ogv":
4034
- case "webm":
4035
- return parseMediaFile("Twine.video", filename);
4036
- case "vtt":
4037
- return parseMediaFile("Twine.vtt", filename);
4038
- default:
4039
- return void 0;
4040
- }
4041
- }
4042
- function loadHTML(story, filename, diagnostics) {
4043
- const result = parseHTMLFile(filename);
4044
- diagnostics.push(...result.diagnostics);
4045
- for (const p of result.passages) {
4046
- storyAdd(story, p, diagnostics);
4047
- }
4048
- }
4049
- function loadTwee(story, filename, opts, diagnostics) {
4050
- const result = parseTweeFile(filename, opts);
4051
- diagnostics.push(...result.diagnostics);
4052
- for (const p of result.passages) {
4053
- storyAdd(story, p, diagnostics);
4054
- }
4055
- }
4056
- function loadTagged(story, tag, filename, diagnostics) {
4057
- const result = parseTaggedFile(tag, filename);
4058
- for (const p of result.passages) {
4059
- storyAdd(story, p, diagnostics);
4060
- }
4061
- }
4062
- function loadMedia(story, tag, filename, diagnostics) {
4063
- const result = parseMediaFile(tag, filename);
4064
- for (const p of result.passages) {
4065
- storyAdd(story, p, diagnostics);
4066
- }
4067
- }
4068
- function loadFont(story, filename, diagnostics) {
4069
- const result = parseFontFile(filename);
4070
- for (const p of result.passages) {
4071
- storyAdd(story, p, diagnostics);
4072
- }
4073
- }
4074
- function loadSourcesCached(story, filenames, opts, diagnostics, processedFiles, cache, changedFiles) {
4075
- const currentFiles = /* @__PURE__ */ new Set();
4076
- for (const filename of filenames) {
4077
- if (processedFiles.has(filename)) {
4078
- diagnostics.push({ level: "warning", message: `load ${filename}: Skipping duplicate.` });
4079
- continue;
4080
- }
4081
- currentFiles.add(filename);
4082
- const cached = cache.get(filename);
4083
- if (changedFiles && cached && !changedFiles.has(filename)) {
4084
- diagnostics.push(...cached.diagnostics);
4085
- for (const p of cached.passages) {
4086
- storyAdd(story, p, diagnostics);
4087
- }
4088
- processedFiles.add(filename);
4089
- continue;
4090
- }
4091
- let mtimeMs;
4092
- try {
4093
- mtimeMs = statSync3(filename).mtimeMs;
4094
- } catch {
4095
- continue;
4096
- }
4097
- if (cached && cached.mtimeMs === mtimeMs) {
4098
- diagnostics.push(...cached.diagnostics);
4099
- for (const p of cached.passages) {
4100
- storyAdd(story, p, diagnostics);
4101
- }
4102
- processedFiles.add(filename);
4103
- continue;
4104
- }
4105
- try {
4106
- const result = parseFile(filename, opts);
4107
- if (!result) continue;
4108
- cache.set(filename, {
4109
- mtimeMs,
4110
- passages: result.passages,
4111
- diagnostics: result.diagnostics
4112
- });
4113
- diagnostics.push(...result.diagnostics);
4114
- for (const p of result.passages) {
4115
- storyAdd(story, p, diagnostics);
4116
- }
4117
- } catch (e) {
4118
- diagnostics.push({
4119
- level: "error",
4120
- message: `load ${filename}: ${e instanceof Error ? e.message : String(e)}`,
4121
- file: filename
4122
- });
4123
- continue;
4124
- }
4125
- processedFiles.add(filename);
4126
- }
4127
- for (const key of cache.keys()) {
4128
- if (!currentFiles.has(key)) {
4129
- cache.delete(key);
4130
- }
4131
- }
4132
- if (story.name !== "" && !story.passages.some((p) => p.name === "StoryTitle")) {
4133
- storyPrepend(story, { name: "StoryTitle", tags: [], text: story.name }, diagnostics);
4134
- }
4135
- }
4136
-
4137
- // src/version.ts
4138
- import { readFileSync as readFileSync2 } from "fs";
4139
- import { dirname, join as join3 } from "path";
4140
- import { fileURLToPath } from "url";
4141
- var PACKAGE_NAME = "@rohal12/twee-ts";
4142
- function findPackageVersion(startDir) {
4143
- let dir = startDir;
4144
- for (; ; ) {
4145
- try {
4146
- const pkg = JSON.parse(readFileSync2(join3(dir, "package.json"), "utf-8"));
4147
- if (pkg.name === PACKAGE_NAME && typeof pkg.version === "string") return pkg.version;
4148
- } catch {
4149
- }
4150
- const parent = dirname(dir);
4151
- if (parent === dir) return void 0;
4152
- dir = parent;
4153
- }
4154
- }
4155
- function moduleDir() {
4156
- return typeof __dirname === "string" ? __dirname : dirname(fileURLToPath(import.meta.url));
4157
- }
4158
- var VERSION = findPackageVersion(moduleDir()) ?? "0.0.0";
4159
-
4160
- // src/output-twine2.ts
4161
- var CREATOR_NAME = "Twee-ts";
4162
- function toTwine2Archive(story, startName, options) {
4163
- return getTwine2DataChunk(story, startName, options) + "\n";
4164
- }
4165
- function toTwine2HTML(story, format, startName, options) {
4166
- let template = readFormatSource(format);
4167
- if (template.includes("{{STORY_NAME}}")) {
4168
- const name = htmlEscape(story.name);
4169
- template = template.replaceAll("{{STORY_NAME}}", () => name);
4170
- }
4171
- if (template.includes("{{STORY_DATA}}")) {
4172
- const data = getTwine2DataChunk(story, startName, options);
4173
- template = template.replace("{{STORY_DATA}}", () => data);
4174
- }
4175
- return template;
4176
- }
4177
- function getTwine2DataChunk(story, startName, options) {
4178
- const parts = [];
4179
- let startID = "";
4180
- let pid = 0;
4181
- const scripts = [];
4182
- const stylesheets = [];
4183
- for (const p of story.passages) {
4184
- if (hasTag(p, "Twine.private")) continue;
4185
- if (hasTag(p, "script")) scripts.push(p);
4186
- else if (hasTag(p, "stylesheet")) stylesheets.push(p);
4187
- }
4188
- let styleContent = "";
4189
- if (stylesheets.length === 1) {
4190
- styleContent = stylesheets[0]?.text ?? "";
4191
- } else if (stylesheets.length > 1) {
4192
- let pidS = 1;
4193
- for (const p of stylesheets) {
4194
- if (pidS > 1 && !styleContent.endsWith("\n")) styleContent += "\n";
4195
- styleContent += `/* twine-user-stylesheet #${pidS}: "${commentSanitize(p.name)}" */
4196
- `;
4197
- styleContent += p.text;
4198
- pidS++;
4199
- }
4200
- }
4201
- parts.push(`<style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">${styleContent}</style>`);
4202
- let scriptContent = "";
4203
- if (scripts.length === 1) {
4204
- scriptContent = scripts[0]?.text ?? "";
4205
- } else if (scripts.length > 1) {
4206
- let pidS = 1;
4207
- for (const p of scripts) {
4208
- if (pidS > 1 && !scriptContent.endsWith("\n")) scriptContent += "\n";
4209
- scriptContent += `/* twine-user-script #${pidS}: "${commentSanitize(p.name)}" */
4210
- `;
4211
- scriptContent += p.text;
4212
- pidS++;
4213
- }
4214
- }
4215
- parts.push(`<script role="script" id="twine-user-script" type="text/twine-javascript">${scriptContent}</script>`);
4216
- const validTagColors = /* @__PURE__ */ new Set(["gray", "red", "orange", "yellow", "green", "blue", "purple"]);
4217
- const hexColorPattern = /^#[0-9a-fA-F]{3,8}$/;
4218
- for (const [tag, color] of story.twine2.tagColors) {
4219
- if (validTagColors.has(color) || hexColorPattern.test(color)) {
4220
- parts.push(`<tw-tag name="${attrEscape(tag)}" color="${attrEscape(color)}"></tw-tag>`);
4221
- }
4222
- }
4223
- pid = 1;
4224
- for (const p of story.passages) {
4225
- if (p.name === "StoryTitle" || p.name === "StoryData" || hasAnyTag(p, "script", "stylesheet", "Twine.private")) {
4226
- continue;
4227
- }
4228
- if (p.name === "StorySettings" && story.twine1.settings.size === 0) {
4229
- continue;
4230
- }
4231
- parts.push(passageToPassagedata(p, pid, options));
4232
- if (startName === p.name) {
4233
- startID = String(pid);
4234
- }
4235
- pid++;
4236
- }
4237
- const opts = [];
4238
- for (const [opt, val] of story.twine2.options) {
4239
- if (val) opts.push(opt);
4240
- }
4241
- const optionsStr = opts.join(" ");
4242
- const zoom = String(story.twine2.zoom);
4243
- const wrapper = `<!-- UUID://${htmlCommentSanitize(story.ifid)}// --><tw-storydata name="${attrEscape(story.name)}" startnode="${attrEscape(startID)}" creator="${attrEscape(CREATOR_NAME)}" creator-version="${attrEscape(VERSION)}" ifid="${attrEscape(story.ifid)}" zoom="${attrEscape(zoom)}" format="${attrEscape(story.twine2.format)}" format-version="${attrEscape(story.twine2.formatVersion)}" options="${attrEscape(optionsStr)}" tags="${attrEscape(story.twine2.tags)}" hidden>`;
4244
- return wrapper + parts.join("") + "</tw-storydata>";
4245
- }
4246
-
4247
- // src/output-twine1.ts
4248
- import { readFileSync as readFileSync3 } from "fs";
4249
- import { join as join4, dirname as dirname2 } from "path";
4250
- var CREATOR_NAME2 = "twee-ts";
4251
- function toTwine1Archive(story, _startName) {
4252
- const { data, count } = getTwine1PassageChunk(story);
4253
- return `<div id="storeArea" data-size="${count}">${data}</div>
4254
- `;
4255
- }
4256
- function toTwine1HTML(story, format, startName) {
4257
- const formatDir = dirname2(format.filename);
4258
- const parentDir = dirname2(formatDir);
4259
- let template = readFormatSource(format);
4260
- const { data, count } = getTwine1PassageChunk(story);
4261
- template = tryReplaceComponent(template, '"USER_LIB"', join4(formatDir, "userlib.js"), false);
4262
- template = tryReplaceComponent(template, '"ENGINE"', join4(parentDir, "engine.js"), true);
4263
- template = tryReplaceComponent(template, '"SUGARCANE"', join4(formatDir, "code.js"), true);
4264
- template = tryReplaceComponent(template, '"JONAH"', join4(formatDir, "code.js"), true);
4265
- if (story.twine1.settings.get("jquery") === "on") {
4266
- template = tryReplaceComponent(template, '"JQUERY"', join4(parentDir, "jquery.js"), true);
4267
- }
4268
- if (story.twine1.settings.get("modernizr") === "on") {
4269
- template = tryReplaceComponent(template, '"MODERNIZR"', join4(parentDir, "modernizr.js"), true);
4270
- }
4271
- const displayStart = startName === "Start" ? "" : startName;
4272
- template = template.replace('"VERSION"', `Compiled with ${CREATOR_NAME2}, ${VERSION}`);
4273
- template = template.replace('"TIME"', `Built on ${(/* @__PURE__ */ new Date()).toUTCString()}`);
4274
- const startAtValue = `"${jsStringEscape(displayStart)}"`;
4275
- template = template.replace('"START_AT"', () => startAtValue);
4276
- template = template.replace('"STORY_SIZE"', `"${count}"`);
4277
- if (template.includes('"STORY"')) {
4278
- template = template.replace('"STORY"', () => data);
4279
- } else {
4280
- let footer;
4281
- try {
4282
- footer = readFileSync3(join4(formatDir, "footer.html"), "utf-8");
4283
- } catch {
4284
- footer = "</div>\n</body>\n</html>\n";
4285
- }
4286
- template += data + footer;
4287
- }
4288
- if (story.ifid) {
4289
- const safeIfid = htmlCommentSanitize(story.ifid);
4290
- if (template.includes('<div id="store-area"')) {
4291
- template = template.replace('<div id="store-area"', `<!-- UUID://${safeIfid}// --><div id="store-area"`);
4292
- } else {
4293
- template = template.replace('<div id="storeArea"', `<!-- UUID://${safeIfid}// --><div id="storeArea"`);
4294
- }
4295
- }
4296
- return template;
4297
- }
4298
- function getTwine1PassageChunk(story) {
4299
- const obfuscateRot13 = story.twine1.settings.get("obfuscate") === "rot13";
4300
- let data = "";
4301
- let count = 0;
4302
- for (const p of story.passages) {
4303
- if (hasTag(p, "Twine.private")) continue;
4304
- count++;
4305
- data += passageToTiddler(p, count, obfuscateRot13);
4306
- }
4307
- return { data, count };
4308
- }
4309
- function tryReplaceComponent(template, placeholder, componentPath, required) {
4310
- if (!template.includes(placeholder)) return template;
4311
- try {
4312
- let content = readFileSync3(componentPath, "utf-8");
4313
- if (content.charCodeAt(0) === 65279) content = content.slice(1);
4314
- return template.replace(placeholder, () => content);
4315
- } catch (e) {
4316
- if (required) {
4317
- throw new Error(
4318
- `Required format component not found: ${componentPath}: ${e instanceof Error ? e.message : String(e)}`
4319
- );
4320
- }
4321
- return template;
4322
- }
4323
- }
4324
-
4325
- // src/output-twee.ts
4326
- function toTwee(story, outMode) {
4327
- let data = "";
4328
- for (const p of story.passages) {
4329
- data += passageToTwee(p, outMode);
4330
- }
4331
- return data;
4332
- }
4333
-
4334
- // src/modules.ts
4335
- function loadModules(filenames) {
4336
- const processed = /* @__PURE__ */ new Set();
4337
- const headTags = [];
4338
- for (const filename of filenames) {
4339
- if (processed.has(filename)) continue;
4340
- const ext = normalizedFileExt(filename);
4341
- let tag = null;
4342
- switch (ext) {
4343
- case "css":
4344
- tag = loadModuleTagged("style", filename);
4345
- break;
4346
- case "js":
4347
- tag = loadModuleTagged("script", filename);
4348
- break;
4349
- case "otf":
4350
- case "ttf":
4351
- case "woff":
4352
- case "woff2":
4353
- tag = loadModuleFont(filename);
4354
- break;
4355
- default:
4356
- continue;
4357
- }
4358
- if (tag) headTags.push(tag);
4359
- processed.add(filename);
4360
- }
4361
- return headTags.join("\n");
4362
- }
4363
- function loadModuleTagged(tag, filename) {
4364
- const source = readUTF8(filename).trim();
4365
- if (source.length === 0) return null;
4366
- const family = baseNameWithoutExt(filename);
4367
- const idSlug = `${tag}-module-${slugify(family)}`;
4368
- const mimeType = tag === "script" ? "text/javascript" : "text/css";
4369
- return `<${tag} id="${idSlug}" type="${mimeType}">${source}</${tag}>`;
4370
- }
4371
- function loadModuleFont(filename) {
4372
- const source = readBase64(filename);
4373
- const family = baseNameWithoutExt(filename);
4374
- const idSlug = `style-module-${slugify(family)}`;
4375
- const ext = normalizedFileExt(filename);
4376
- const mediaType = mediaTypeFromExt(ext);
4377
- const hint = fontFormatHint(ext);
4378
- return `<style id="${idSlug}" type="text/css">@font-face {
4379
- font-family: "${family}";
4380
- src: url("data:${mediaType};base64,${source}") format("${hint}");
4381
- }</style>`;
4382
- }
4383
- function modifyHead(html, modulePaths, headFile, diagnostics) {
4384
- const parts = [];
4385
- if (modulePaths.length > 0) {
4386
- const modules = loadModules(modulePaths).trim();
4387
- if (modules.length > 0) parts.push(modules);
4388
- }
4389
- if (headFile) {
4390
- try {
4391
- const source = readUTF8(headFile).trim();
4392
- if (source.length > 0) parts.push(source);
4393
- } catch (e) {
4394
- diagnostics?.push({
4395
- level: "warning",
4396
- message: `Failed to read head file "${headFile}": ${e instanceof Error ? e.message : String(e)}`
4397
- });
4398
- }
4399
- }
4400
- if (parts.length > 0) {
4401
- parts.push("</head>");
4402
- const replacement = parts.join("\n");
4403
- return html.replace("</head>", () => replacement);
4404
- }
4405
- return html;
4406
- }
4407
-
4408
- // src/remote-formats.ts
4409
- import { mkdirSync, readFileSync as readFileSync4, writeFileSync, existsSync, readdirSync as readdirSync3, statSync as statSync4, rmSync } from "fs";
4410
- import { join as join5 } from "path";
4411
- import { homedir as homedir2 } from "os";
4412
- var DEFAULT_SFA_INDICES = [
4413
- "https://videlais.github.io/story-formats-archive/official/index.json",
4414
- "https://videlais.github.io/story-formats-archive/unofficial/index.json"
4415
- ];
4416
- function getCacheDir() {
4417
- const xdg = process.env["XDG_CACHE_HOME"];
4418
- const base = xdg || join5(homedir2(), ".cache");
4419
- return join5(base, "twee-ts", "storyformats");
4420
- }
4421
- var indexCache = /* @__PURE__ */ new Map();
4422
- function clearIndexCache() {
4423
- indexCache.clear();
4424
- }
4425
- function isValidEntry(val) {
4426
- if (typeof val !== "object" || val === null || Array.isArray(val)) return false;
4427
- const obj = val;
4428
- return typeof obj.name === "string" && typeof obj.version === "string" && typeof obj.checksums === "object" && obj.checksums !== null && !Array.isArray(obj.checksums);
4429
- }
4430
- function validateSFAIndex(json) {
4431
- if (typeof json !== "object" || json === null) {
4432
- throw new Error("SFA index is not an object");
4433
- }
4434
- const obj = json;
4435
- const twine1 = Array.isArray(obj["twine1"]) ? obj["twine1"].filter(isValidEntry) : [];
4436
- const twine2 = Array.isArray(obj["twine2"]) ? obj["twine2"].filter(isValidEntry) : [];
4437
- return { twine1, twine2 };
4438
- }
4439
- function makeFormatId(name, version) {
4440
- const major = parseSemver(version)?.[0] ?? "0";
4441
- return `${name.toLowerCase().replace(/\s+/g, "-")}-${major}`;
4442
- }
4443
- async function fetchIndex(url) {
4444
- const cached = indexCache.get(url);
4445
- if (cached) return cached;
4446
- const res = await fetch(url);
4447
- if (!res.ok) {
4448
- throw new Error(`Failed to fetch format index from ${url}: ${res.status} ${res.statusText}`);
4449
- }
4450
- const data = validateSFAIndex(await res.json());
4451
- indexCache.set(url, data);
4452
- return data;
4453
- }
4454
- function findEntry(index, name, version) {
4455
- const wanted = parseSemver(version);
4456
- const searchArrays = [
4457
- { entries: index.twine2 ?? [], formatType: "twine2" },
4458
- { entries: index.twine1 ?? [], formatType: "twine1" }
4459
- ];
4460
- let bestResult;
4461
- let bestVersion = null;
4462
- for (const { entries, formatType } of searchArrays) {
4463
- for (const entry of entries) {
4464
- if (entry.name.toLowerCase() !== name.toLowerCase()) continue;
4465
- const have = parseSemver(entry.version);
4466
- if (!have) continue;
4467
- if (wanted && semverCompare(have, wanted) === 0) return { entry, formatType };
4468
- if (wanted === null || have[0] === wanted[0] && semverCompare(have, wanted) >= 0) {
4469
- if (!bestVersion || semverCompare(have, bestVersion) > 0) {
4470
- bestVersion = have;
4471
- bestResult = { entry, formatType };
4472
- }
4473
- }
4474
- }
4475
- }
4476
- return bestResult;
4477
- }
4478
- async function verifySHA256(content, expectedHex) {
4479
- const digest = await crypto.subtle.digest("SHA-256", content);
4480
- const hex = Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
4481
- return hex === expectedHex.toLowerCase();
4482
- }
4483
- function getDownloadUrl(indexUrl, entry, formatType) {
4484
- const base = indexUrl.replace(/\/index\.json$/, "");
4485
- return `${base}/${formatType}/${entry.name}/${entry.version}/format.js`;
4486
- }
4487
- async function fetchAndCacheFormat(entry, downloadUrl) {
4488
- const res = await fetch(downloadUrl);
4489
- if (!res.ok) {
4490
- throw new Error(`Failed to download format from ${downloadUrl}: ${res.status} ${res.statusText}`);
4491
- }
4492
- const text = await res.text();
4493
- const checksumKey = Object.keys(entry.checksums ?? {}).find((k) => k.endsWith("format.js"));
4494
- if (checksumKey) {
4495
- const expected = entry.checksums[checksumKey];
4496
- if (!expected) throw new Error(`Missing checksum value for key "${checksumKey}"`);
4497
- const encoder = new TextEncoder();
4498
- const valid = await verifySHA256(encoder.encode(text), expected);
4499
- if (!valid) {
4500
- throw new Error(`Checksum verification failed for ${entry.name} ${entry.version}`);
4501
- }
4502
- }
4503
- const id = makeFormatId(entry.name, entry.version);
4504
- const data = parseFormatJSON(text, id);
4505
- if (!data) {
4506
- throw new Error(`Failed to parse format JSON from ${downloadUrl}`);
4507
- }
4508
- const cacheDir = getCacheDir();
4509
- const formatDir = join5(cacheDir, entry.name, entry.version);
4510
- mkdirSync(formatDir, { recursive: true });
4511
- const formatPath = join5(formatDir, "format.js");
4512
- writeFileSync(formatPath, text, "utf-8");
4513
- return {
4514
- id,
4515
- filename: formatPath,
4516
- isTwine2: true,
4517
- name: data.name,
4518
- version: data.version,
4519
- proofing: data.proofing ?? false
4520
- };
4521
- }
4522
- async function fetchDirectFormat(url) {
4523
- const res = await fetch(url);
4524
- if (!res.ok) {
4525
- throw new Error(`Failed to download format from ${url}: ${res.status} ${res.statusText}`);
4526
- }
4527
- const text = await res.text();
4528
- const tempId = "direct-url";
4529
- const data = parseFormatJSON(text, tempId);
4530
- if (!data) {
4531
- throw new Error(`Failed to parse format JSON from ${url}`);
4532
- }
4533
- const id = makeFormatId(data.name, data.version);
4534
- const cacheDir = getCacheDir();
4535
- const formatDir = join5(cacheDir, data.name, data.version);
4536
- mkdirSync(formatDir, { recursive: true });
4537
- const formatPath = join5(formatDir, "format.js");
4538
- writeFileSync(formatPath, text, "utf-8");
4539
- return {
4540
- id,
4541
- filename: formatPath,
4542
- isTwine2: true,
4543
- name: data.name,
4544
- version: data.version,
4545
- proofing: data.proofing ?? false
4546
- };
4547
- }
4548
- async function resolveRemoteFormat(name, version, indices, urls) {
4549
- let lastError;
4550
- if (urls) {
4551
- for (const url of urls) {
4552
- try {
4553
- const info = await fetchDirectFormat(url);
4554
- if (info.name.toLowerCase() === name.toLowerCase()) {
4555
- const wanted = parseSemver(version);
4556
- const have = parseSemver(info.version);
4557
- if (!wanted || have && have[0] === wanted[0] && semverCompare(have, wanted) >= 0) {
4558
- return info;
4559
- }
4560
- }
4561
- } catch (e) {
4562
- lastError = e instanceof Error ? e : new Error(String(e));
4563
- }
4564
- }
4565
- }
4566
- const allIndices = [...indices ?? [], ...DEFAULT_SFA_INDICES];
4567
- for (const indexUrl of allIndices) {
4568
- try {
4569
- const index = await fetchIndex(indexUrl);
4570
- const result = findEntry(index, name, version);
4571
- if (result) {
4572
- const cached = getCachedFormat(result.entry.name, result.entry.version);
4573
- if (cached) return cached;
4574
- const downloadUrl = getDownloadUrl(indexUrl, result.entry, result.formatType);
4575
- return await fetchAndCacheFormat(result.entry, downloadUrl);
4576
- }
4577
- } catch (e) {
4578
- lastError = e instanceof Error ? e : new Error(String(e));
4579
- }
4580
- }
4581
- if (lastError) throw lastError;
4582
- return void 0;
4583
- }
4584
- function getCachedFormat(name, version) {
4585
- const formatPath = join5(getCacheDir(), name, version, "format.js");
4586
- try {
4587
- if (!existsSync(formatPath)) return void 0;
4588
- const source = readFileSync4(formatPath, "utf-8");
4589
- const id = makeFormatId(name, version);
4590
- const data = parseFormatJSON(source, id);
4591
- if (!data) return void 0;
4592
- return {
4593
- id,
4594
- filename: formatPath,
4595
- isTwine2: true,
4596
- name: data.name,
4597
- version: data.version,
4598
- proofing: data.proofing ?? false
4599
- };
4600
- } catch {
4601
- return void 0;
4602
- }
4603
- }
4604
-
4605
- // src/compiler.ts
4606
- var CREATOR_NAME3 = "Twee-ts";
4607
- var DEFAULT_FORMAT_ID = "sugarcube-2";
4608
- var DEFAULT_START_NAME = "Start";
4609
- var TweeTsError = class extends Error {
4610
- constructor(message, diagnostics = []) {
4611
- super(message);
4612
- this.diagnostics = diagnostics;
4613
- this.name = "TweeTsError";
4614
- }
4615
- };
4616
- async function compile(options) {
4617
- return buildOutput(options);
4618
- }
4619
- async function compileIncremental(options, cache, changedFiles) {
4620
- return buildOutput(options, cache, changedFiles);
4621
- }
4622
- async function buildOutput(options, cache, changedFiles) {
4623
- const diagnostics = [];
4624
- const outputMode = options.outputMode ?? "html";
4625
- const trim = options.trim ?? true;
4626
- const twee2Compat = options.twee2Compat ?? false;
4627
- const testMode = options.testMode ?? false;
4628
- const noRemote = options.noRemote ?? false;
4629
- const sourceInfo = options.sourceInfo ?? false;
4630
- clearIndexCache();
4631
- const filePaths = [];
4632
- const inlineSources = [];
4633
- for (const source of options.sources) {
4634
- if (typeof source === "string") {
4635
- filePaths.push(source);
4636
- } else {
4637
- inlineSources.push(source);
4638
- }
4639
- }
4640
- const sourceFilenames = getFilenames(filePaths);
4641
- const story = createStory();
4642
- const processedFiles = /* @__PURE__ */ new Set();
4643
- if (cache) {
4644
- loadSourcesCached(story, sourceFilenames, { trim, twee2Compat }, diagnostics, processedFiles, cache, changedFiles);
4645
- } else {
4646
- loadSources(story, sourceFilenames, { trim, twee2Compat }, diagnostics, processedFiles);
4647
- }
4648
- loadInlineSources(story, inlineSources, { trim, twee2Compat }, diagnostics);
4649
- if (options.tagAliases) {
4650
- story.passages = applyTagAliases(story.passages, options.tagAliases);
4651
- }
4652
- let format;
4653
- let formatId = options.formatId ?? "";
4654
- if (outputMode === "html") {
4655
- const formatSearchDirs = getFormatSearchDirs(options.formatPaths, options.useTweegoPath ?? true);
4656
- const formats = discoverFormats(formatSearchDirs);
4657
- if (!formatId && story.twine2.format) {
4658
- formatId = getFormatIdByNameAndVersion(formats, story.twine2.format, story.twine2.formatVersion) ?? "";
4659
- }
4660
- if (!formatId) {
4661
- formatId = DEFAULT_FORMAT_ID;
4662
- }
4663
- format = formats.get(formatId);
4664
- if (!format && !noRemote) {
4665
- const remoteName = story.twine2.format || formatId;
4666
- const remoteVersion = story.twine2.formatVersion || "";
4667
- try {
4668
- format = await resolveRemoteFormat(remoteName, remoteVersion, options.formatIndices, options.formatUrls);
4669
- } catch (e) {
4670
- diagnostics.push({
4671
- level: "warning",
4672
- message: `Remote format fetch failed for "${remoteName}": ${e instanceof Error ? e.message : String(e)}`
4673
- });
4674
- }
4675
- }
4676
- if (!format) {
4677
- const reason = noRemote ? " (remote fetching disabled)" : "";
4678
- diagnostics.push({
4679
- level: "error",
4680
- message: `Story format "${formatId}" is not available${reason}. Found: ${[...formats.keys()].join(", ") || "none"}`
4681
- });
4682
- }
4683
- }
4684
- const startName = options.startPassage || story.twine2.start || DEFAULT_START_NAME;
4685
- if (testMode) {
4686
- story.twine2.options.set("debug", true);
4687
- }
4688
- ensureIFID(story, diagnostics);
4689
- let output;
4690
- switch (outputMode) {
4691
- case "twee3":
4692
- case "twee1":
4693
- output = toTwee(story, outputMode);
4694
- break;
4695
- case "twine2-archive":
4696
- output = toTwine2Archive(story, startName, { sourceInfo });
4697
- break;
4698
- case "twine1-archive":
4699
- output = toTwine1Archive(story, startName);
4700
- break;
4701
- case "json":
4702
- output = storyToJSON(story);
4703
- break;
4704
- case "html": {
4705
- if (!storyHas(story, startName)) {
4706
- diagnostics.push({
4707
- level: "error",
4708
- message: `Starting passage "${startName}" not found.`
4709
- });
4710
- }
4711
- if (!format) {
4712
- throw new TweeTsError("No story format available for HTML output.", diagnostics);
4713
- }
4714
- if (format.isTwine2) {
4715
- output = toTwine2HTML(story, format, startName, { sourceInfo });
4716
- } else {
4717
- if (story.name === "" && !storyHas(story, "StoryTitle")) {
4718
- diagnostics.push({
4719
- level: "error",
4720
- message: 'Special passage "StoryTitle" not found.'
4721
- });
4722
- }
4723
- output = toTwine1HTML(story, format, startName);
4724
- }
4725
- const modulePaths = options.modules ? getFilenames(options.modules) : [];
4726
- output = modifyHead(output, modulePaths, options.headFile, diagnostics);
4727
- break;
4728
- }
4729
- default: {
4730
- const _exhaustive = outputMode;
4731
- throw new TweeTsError(`Unhandled output mode: ${_exhaustive}`, diagnostics);
4732
- }
4733
- }
4734
- const stats = {
4735
- ...getStoryStats(story, options.wordCountMethod),
4736
- files: [...processedFiles]
4737
- };
4738
- return { output, story, format, diagnostics, stats };
4739
- }
4740
- function ensureIFID(story, diagnostics) {
4741
- if (story.ifid !== "") return;
4742
- if (story.legacyIFID !== "") {
4743
- story.ifid = story.legacyIFID;
4744
- diagnostics.push({
4745
- level: "warning",
4746
- message: 'Story IFID not found; reusing "ifid" entry from the "StorySettings" special passage.'
4747
- });
4748
- } else {
4749
- const ifid = generateIFID();
4750
- story.ifid = ifid;
4751
- diagnostics.push({
4752
- level: "error",
4753
- message: `Story IFID not found. Add an IFID to your story: {"ifid":"${ifid}"}`
4754
- });
4755
- }
4756
- }
4757
- function storyToJSON(story) {
4758
- const scripts = [];
4759
- const stylesheets = [];
4760
- const storyPassages = [];
4761
- for (const p of story.passages) {
4762
- if (p.name === "StoryTitle" || p.name === "StoryData") continue;
4763
- if (hasTag(p, "Twine.private")) continue;
4764
- if (hasTag(p, "script")) {
4765
- scripts.push(p.text);
4766
- } else if (hasTag(p, "stylesheet")) {
4767
- stylesheets.push(p.text);
4768
- } else {
4769
- const entry = {
4770
- name: p.name,
4771
- tags: p.tags,
4772
- text: p.text
4773
- };
4774
- if (p.metadata) {
4775
- const meta = {};
4776
- for (const [key, value] of Object.entries(p.metadata)) {
4777
- if (typeof value === "string") meta[key] = value;
4778
- }
4779
- if (Object.keys(meta).length > 0) {
4780
- entry.metadata = meta;
4781
- }
4782
- }
4783
- storyPassages.push(entry);
4784
- }
4785
- }
4786
- const obj = {
4787
- name: story.name
4788
- };
4789
- if (story.ifid) obj.ifid = story.ifid;
4790
- if (story.twine2.format) obj.format = story.twine2.format;
4791
- if (story.twine2.formatVersion) obj["format-version"] = story.twine2.formatVersion;
4792
- if (story.twine2.start) obj.start = story.twine2.start;
4793
- if (story.twine2.tagColors.size > 0) obj["tag-colors"] = Object.fromEntries(story.twine2.tagColors);
4794
- if (story.twine2.zoom !== 1) obj.zoom = story.twine2.zoom;
4795
- obj.creator = CREATOR_NAME3;
4796
- obj["creator-version"] = VERSION;
4797
- obj.style = stylesheets.join("\n");
4798
- obj.script = scripts.join("\n");
4799
- obj.passages = storyPassages;
4800
- return JSON.stringify(obj, null, 2);
4801
- }
4802
-
4803
- export {
4804
- mediaTypeFromFilename,
4805
- TweeTsError,
4806
- compile,
4807
- compileIncremental
4808
- };
4809
- //# sourceMappingURL=chunk-W3SNMQO4.js.map