@rohal12/twee-ts 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/twee-ts.js +225 -58
- package/dist/bin/twee-ts.js.map +1 -1
- package/dist/{chunk-76URFKGD.js → chunk-HXL3JC4L.js} +209 -51
- package/dist/chunk-HXL3JC4L.js.map +1 -0
- package/dist/index.cjs +240 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +221 -57
- package/dist/index.js.map +1 -1
- package/dist/plugins/rollup.d.ts +1 -1
- package/dist/plugins/rollup.js +1 -1
- package/dist/plugins/vite.d.ts +1 -1
- package/dist/plugins/vite.js +1 -1
- package/dist/{types-B3qtPxs8.d.ts → types-BbEu2Szm.d.ts} +2 -0
- package/package.json +1 -1
- package/dist/chunk-76URFKGD.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -38,6 +38,8 @@ interface CompileOptions {
|
|
|
38
38
|
noRemote?: boolean;
|
|
39
39
|
/** Map alias tags to canonical special tags (e.g. { library: 'script' }). */
|
|
40
40
|
tagAliases?: Record<string, string>;
|
|
41
|
+
/** Emit source file and line as data- attributes on passage elements. Default: false. */
|
|
42
|
+
sourceInfo?: boolean;
|
|
41
43
|
}
|
|
42
44
|
interface CompileToFileOptions extends CompileOptions {
|
|
43
45
|
/** Output file path. */
|
|
@@ -73,6 +75,10 @@ interface CompileStats {
|
|
|
73
75
|
words: number;
|
|
74
76
|
files: string[];
|
|
75
77
|
}
|
|
78
|
+
interface SourceLocation {
|
|
79
|
+
readonly file: string;
|
|
80
|
+
readonly line: number;
|
|
81
|
+
}
|
|
76
82
|
interface PassageMetadata {
|
|
77
83
|
position?: string;
|
|
78
84
|
size?: string;
|
|
@@ -83,6 +89,7 @@ interface Passage {
|
|
|
83
89
|
tags: string[];
|
|
84
90
|
text: string;
|
|
85
91
|
metadata?: PassageMetadata;
|
|
92
|
+
source?: SourceLocation;
|
|
86
93
|
}
|
|
87
94
|
interface Twine1Metadata {
|
|
88
95
|
settings: Map<string, string>;
|
|
@@ -171,6 +178,7 @@ interface TweeTsConfig {
|
|
|
171
178
|
testMode?: boolean;
|
|
172
179
|
noRemote?: boolean;
|
|
173
180
|
tagAliases?: Record<string, string>;
|
|
181
|
+
sourceInfo?: boolean;
|
|
174
182
|
}
|
|
175
183
|
|
|
176
184
|
declare class TweeTsError extends Error {
|
|
@@ -365,4 +373,4 @@ declare function resolveRemoteFormat(name: string, version: string, indices?: st
|
|
|
365
373
|
/** Discover all cached remote formats. Returns a Map like discoverFormats(). */
|
|
366
374
|
declare function discoverCachedFormats(): Map<string, StoryFormatInfo>;
|
|
367
375
|
|
|
368
|
-
export { type BrokenLink, CONFIG_FILENAME, type CompileOptions, type CompileResult, type CompileStats, type CompileToFileOptions, type Diagnostic, type InlineSource, ItemType, type LexerItem, type OutputMode, type Passage, type PassageMetadata, type SFAIndex, type SFAIndexEntry, type SourceInput, type Story, type StoryFormatInfo, type StoryMap, TweeLexer, type TweeTsConfig, TweeTsError, type WatchOptions, applyTagAliases, compile, compileToFile, discoverCachedFormats, discoverFormats, fetchAndCacheFormat, fetchDirectFormat, generateIFID, getCacheDir, getFormatSearchDirs, loadConfig, loadConfigFile, parseFormatJSON, parseSemver, parseTwee, resolveRemoteFormat, scaffoldConfig, semverCompare, storyInspect, tweeLexer, validateConfig, validateIFID, watch };
|
|
376
|
+
export { type BrokenLink, CONFIG_FILENAME, type CompileOptions, type CompileResult, type CompileStats, type CompileToFileOptions, type Diagnostic, type InlineSource, ItemType, type LexerItem, type OutputMode, type Passage, type PassageMetadata, type SFAIndex, type SFAIndexEntry, type SourceInput, type SourceLocation, type Story, type StoryFormatInfo, type StoryMap, TweeLexer, type TweeTsConfig, TweeTsError, type WatchOptions, applyTagAliases, compile, compileToFile, discoverCachedFormats, discoverFormats, fetchAndCacheFormat, fetchDirectFormat, generateIFID, getCacheDir, getFormatSearchDirs, loadConfig, loadConfigFile, parseFormatJSON, parseSemver, parseTwee, resolveRemoteFormat, scaffoldConfig, semverCompare, storyInspect, tweeLexer, validateConfig, validateIFID, watch };
|
package/dist/index.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ interface CompileOptions {
|
|
|
38
38
|
noRemote?: boolean;
|
|
39
39
|
/** Map alias tags to canonical special tags (e.g. { library: 'script' }). */
|
|
40
40
|
tagAliases?: Record<string, string>;
|
|
41
|
+
/** Emit source file and line as data- attributes on passage elements. Default: false. */
|
|
42
|
+
sourceInfo?: boolean;
|
|
41
43
|
}
|
|
42
44
|
interface CompileToFileOptions extends CompileOptions {
|
|
43
45
|
/** Output file path. */
|
|
@@ -73,6 +75,10 @@ interface CompileStats {
|
|
|
73
75
|
words: number;
|
|
74
76
|
files: string[];
|
|
75
77
|
}
|
|
78
|
+
interface SourceLocation {
|
|
79
|
+
readonly file: string;
|
|
80
|
+
readonly line: number;
|
|
81
|
+
}
|
|
76
82
|
interface PassageMetadata {
|
|
77
83
|
position?: string;
|
|
78
84
|
size?: string;
|
|
@@ -83,6 +89,7 @@ interface Passage {
|
|
|
83
89
|
tags: string[];
|
|
84
90
|
text: string;
|
|
85
91
|
metadata?: PassageMetadata;
|
|
92
|
+
source?: SourceLocation;
|
|
86
93
|
}
|
|
87
94
|
interface Twine1Metadata {
|
|
88
95
|
settings: Map<string, string>;
|
|
@@ -171,6 +178,7 @@ interface TweeTsConfig {
|
|
|
171
178
|
testMode?: boolean;
|
|
172
179
|
noRemote?: boolean;
|
|
173
180
|
tagAliases?: Record<string, string>;
|
|
181
|
+
sourceInfo?: boolean;
|
|
174
182
|
}
|
|
175
183
|
|
|
176
184
|
declare class TweeTsError extends Error {
|
|
@@ -365,4 +373,4 @@ declare function resolveRemoteFormat(name: string, version: string, indices?: st
|
|
|
365
373
|
/** Discover all cached remote formats. Returns a Map like discoverFormats(). */
|
|
366
374
|
declare function discoverCachedFormats(): Map<string, StoryFormatInfo>;
|
|
367
375
|
|
|
368
|
-
export { type BrokenLink, CONFIG_FILENAME, type CompileOptions, type CompileResult, type CompileStats, type CompileToFileOptions, type Diagnostic, type InlineSource, ItemType, type LexerItem, type OutputMode, type Passage, type PassageMetadata, type SFAIndex, type SFAIndexEntry, type SourceInput, type Story, type StoryFormatInfo, type StoryMap, TweeLexer, type TweeTsConfig, TweeTsError, type WatchOptions, applyTagAliases, compile, compileToFile, discoverCachedFormats, discoverFormats, fetchAndCacheFormat, fetchDirectFormat, generateIFID, getCacheDir, getFormatSearchDirs, loadConfig, loadConfigFile, parseFormatJSON, parseSemver, parseTwee, resolveRemoteFormat, scaffoldConfig, semverCompare, storyInspect, tweeLexer, validateConfig, validateIFID, watch };
|
|
376
|
+
export { type BrokenLink, CONFIG_FILENAME, type CompileOptions, type CompileResult, type CompileStats, type CompileToFileOptions, type Diagnostic, type InlineSource, ItemType, type LexerItem, type OutputMode, type Passage, type PassageMetadata, type SFAIndex, type SFAIndexEntry, type SourceInput, type SourceLocation, type Story, type StoryFormatInfo, type StoryMap, TweeLexer, type TweeTsConfig, TweeTsError, type WatchOptions, applyTagAliases, compile, compileToFile, discoverCachedFormats, discoverFormats, fetchAndCacheFormat, fetchDirectFormat, generateIFID, getCacheDir, getFormatSearchDirs, loadConfig, loadConfigFile, parseFormatJSON, parseSemver, parseTwee, resolveRemoteFormat, scaffoldConfig, semverCompare, storyInspect, tweeLexer, validateConfig, validateIFID, watch };
|
package/dist/index.js
CHANGED
|
@@ -190,7 +190,7 @@ function passageToTwee(p, outMode) {
|
|
|
190
190
|
output += "\n\n";
|
|
191
191
|
return output;
|
|
192
192
|
}
|
|
193
|
-
function passageToPassagedata(p, pid) {
|
|
193
|
+
function passageToPassagedata(p, pid, options) {
|
|
194
194
|
let position;
|
|
195
195
|
let size;
|
|
196
196
|
if (hasMetadataPosition(p)) {
|
|
@@ -207,7 +207,11 @@ function passageToPassagedata(p, pid) {
|
|
|
207
207
|
} else {
|
|
208
208
|
size = "100,100";
|
|
209
209
|
}
|
|
210
|
-
|
|
210
|
+
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))}`;
|
|
211
|
+
if (options?.sourceInfo && p.source) {
|
|
212
|
+
attrs += ` data-source-file=${quote(attrEscape(p.source.file))} data-source-line="${p.source.line}"`;
|
|
213
|
+
}
|
|
214
|
+
return `${attrs}>${htmlEscape(p.text)}</tw-passagedata>`;
|
|
211
215
|
}
|
|
212
216
|
function passageToTiddler(p, pid, obfuscateRot13 = false) {
|
|
213
217
|
let position;
|
|
@@ -254,8 +258,21 @@ function quote(s) {
|
|
|
254
258
|
}
|
|
255
259
|
|
|
256
260
|
// src/story.ts
|
|
261
|
+
var passageIndex = /* @__PURE__ */ new WeakMap();
|
|
262
|
+
function getIndex(story) {
|
|
263
|
+
let index = passageIndex.get(story);
|
|
264
|
+
if (!index) {
|
|
265
|
+
index = /* @__PURE__ */ new Map();
|
|
266
|
+
for (let i = 0; i < story.passages.length; i++) {
|
|
267
|
+
const passage = story.passages[i];
|
|
268
|
+
if (passage) index.set(passage.name, i);
|
|
269
|
+
}
|
|
270
|
+
passageIndex.set(story, index);
|
|
271
|
+
}
|
|
272
|
+
return index;
|
|
273
|
+
}
|
|
257
274
|
function createStory() {
|
|
258
|
-
|
|
275
|
+
const story = {
|
|
259
276
|
name: "",
|
|
260
277
|
ifid: "",
|
|
261
278
|
passages: [],
|
|
@@ -271,16 +288,17 @@ function createStory() {
|
|
|
271
288
|
zoom: 1
|
|
272
289
|
}
|
|
273
290
|
};
|
|
291
|
+
passageIndex.set(story, /* @__PURE__ */ new Map());
|
|
292
|
+
return story;
|
|
274
293
|
}
|
|
275
294
|
function storyHas(story, name) {
|
|
276
|
-
return story.
|
|
277
|
-
}
|
|
278
|
-
function storyIndex(story, name) {
|
|
279
|
-
return story.passages.findIndex((p) => p.name === name);
|
|
295
|
+
return getIndex(story).has(name);
|
|
280
296
|
}
|
|
281
297
|
function storyAppend(story, p, diagnostics) {
|
|
282
|
-
const
|
|
298
|
+
const index = getIndex(story);
|
|
299
|
+
const i = index.get(p.name) ?? -1;
|
|
283
300
|
if (i === -1) {
|
|
301
|
+
index.set(p.name, story.passages.length);
|
|
284
302
|
story.passages.push(p);
|
|
285
303
|
} else {
|
|
286
304
|
diagnostics.push({
|
|
@@ -291,9 +309,15 @@ function storyAppend(story, p, diagnostics) {
|
|
|
291
309
|
}
|
|
292
310
|
}
|
|
293
311
|
function storyPrepend(story, p, diagnostics) {
|
|
294
|
-
const
|
|
312
|
+
const index = getIndex(story);
|
|
313
|
+
const i = index.get(p.name) ?? -1;
|
|
295
314
|
if (i === -1) {
|
|
296
315
|
story.passages.unshift(p);
|
|
316
|
+
index.clear();
|
|
317
|
+
for (let j = 0; j < story.passages.length; j++) {
|
|
318
|
+
const passage = story.passages[j];
|
|
319
|
+
if (passage) index.set(passage.name, j);
|
|
320
|
+
}
|
|
297
321
|
} else {
|
|
298
322
|
diagnostics.push({
|
|
299
323
|
level: "warning",
|
|
@@ -583,11 +607,15 @@ function watchFilesystem(pathnames, outFilename, callback) {
|
|
|
583
607
|
const watchers = [];
|
|
584
608
|
let buildTimer = null;
|
|
585
609
|
const BUILD_DEBOUNCE = 500;
|
|
586
|
-
|
|
610
|
+
const pendingFiles = /* @__PURE__ */ new Set();
|
|
611
|
+
function scheduleBuild(changedFile) {
|
|
612
|
+
if (changedFile) pendingFiles.add(changedFile);
|
|
587
613
|
if (buildTimer) clearTimeout(buildTimer);
|
|
588
614
|
buildTimer = setTimeout(() => {
|
|
589
615
|
buildTimer = null;
|
|
590
|
-
|
|
616
|
+
const files = pendingFiles.size > 0 ? new Set(pendingFiles) : void 0;
|
|
617
|
+
pendingFiles.clear();
|
|
618
|
+
callback(files);
|
|
591
619
|
}, BUILD_DEBOUNCE);
|
|
592
620
|
}
|
|
593
621
|
for (const pathname of pathnames) {
|
|
@@ -597,7 +625,8 @@ function watchFilesystem(pathnames, outFilename, callback) {
|
|
|
597
625
|
const abs = resolve(pathname, filename);
|
|
598
626
|
if (abs === absOutFile) return;
|
|
599
627
|
if (isKnownFileType(filename)) {
|
|
600
|
-
|
|
628
|
+
const rel = relative(process.cwd(), abs);
|
|
629
|
+
scheduleBuild(rel || abs);
|
|
601
630
|
}
|
|
602
631
|
});
|
|
603
632
|
watchers.push(watcher);
|
|
@@ -856,6 +885,7 @@ function readFormatSource(format) {
|
|
|
856
885
|
|
|
857
886
|
// src/loader.ts
|
|
858
887
|
import { basename } from "path";
|
|
888
|
+
import { statSync as statSync3 } from "fs";
|
|
859
889
|
|
|
860
890
|
// src/lexer.ts
|
|
861
891
|
var EOF = -1;
|
|
@@ -1187,7 +1217,7 @@ function parseTwee(source, options = {}) {
|
|
|
1187
1217
|
if (pCount > 1 && current) {
|
|
1188
1218
|
passages.push(current);
|
|
1189
1219
|
}
|
|
1190
|
-
current = { name: "", tags: [], text: "" };
|
|
1220
|
+
current = { name: "", tags: [], text: "", source: { file: filename, line: item.line } };
|
|
1191
1221
|
break;
|
|
1192
1222
|
case 3 /* Name */: {
|
|
1193
1223
|
if (!current) break;
|
|
@@ -1379,54 +1409,182 @@ function loadInlineSources(story, sources, opts, diagnostics) {
|
|
|
1379
1409
|
}
|
|
1380
1410
|
}
|
|
1381
1411
|
}
|
|
1382
|
-
function
|
|
1412
|
+
function parseTweeFile(filename, opts) {
|
|
1383
1413
|
const source = readUTF8(filename);
|
|
1384
1414
|
const result = parseTwee(source, {
|
|
1385
1415
|
filename,
|
|
1386
1416
|
trim: opts.trim ?? true,
|
|
1387
1417
|
twee2Compat: opts.twee2Compat ?? false
|
|
1388
1418
|
});
|
|
1389
|
-
diagnostics
|
|
1390
|
-
for (const p of result.passages) {
|
|
1391
|
-
storyAdd(story, p, diagnostics);
|
|
1392
|
-
}
|
|
1419
|
+
return { passages: result.passages, diagnostics: [...result.diagnostics] };
|
|
1393
1420
|
}
|
|
1394
|
-
function
|
|
1421
|
+
function parseTaggedFile(tag, filename) {
|
|
1395
1422
|
const source = readUTF8(filename);
|
|
1396
|
-
|
|
1423
|
+
return { passages: [{ name: basename(filename), tags: [tag], text: source }], diagnostics: [] };
|
|
1397
1424
|
}
|
|
1398
|
-
function
|
|
1425
|
+
function parseMediaFile(tag, filename) {
|
|
1399
1426
|
const source = readBase64(filename);
|
|
1400
1427
|
const name = baseNameWithoutExt(filename);
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
tags: [tag],
|
|
1406
|
-
text: `data:${mediaTypeFromFilename(filename)};base64,${source}`
|
|
1407
|
-
},
|
|
1408
|
-
diagnostics
|
|
1409
|
-
);
|
|
1428
|
+
return {
|
|
1429
|
+
passages: [{ name, tags: [tag], text: `data:${mediaTypeFromFilename(filename)};base64,${source}` }],
|
|
1430
|
+
diagnostics: []
|
|
1431
|
+
};
|
|
1410
1432
|
}
|
|
1411
|
-
function
|
|
1433
|
+
function parseFontFile(filename) {
|
|
1412
1434
|
const source = readBase64(filename);
|
|
1413
1435
|
const name = basename(filename);
|
|
1414
1436
|
const family = baseNameWithoutExt(filename);
|
|
1415
1437
|
const ext = normalizedFileExt(filename);
|
|
1416
1438
|
const mediaType = mediaTypeFromExt(ext);
|
|
1417
1439
|
const hint = fontFormatHint(ext);
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1440
|
+
return {
|
|
1441
|
+
passages: [
|
|
1442
|
+
{
|
|
1443
|
+
name,
|
|
1444
|
+
tags: ["stylesheet"],
|
|
1445
|
+
text: `@font-face {
|
|
1424
1446
|
font-family: "${family}";
|
|
1425
1447
|
src: url("data:${mediaType};base64,${source}") format("${hint}");
|
|
1426
1448
|
}`
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1449
|
+
}
|
|
1450
|
+
],
|
|
1451
|
+
diagnostics: []
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
function parseFile(filename, opts) {
|
|
1455
|
+
const ext = normalizedFileExt(filename);
|
|
1456
|
+
switch (ext) {
|
|
1457
|
+
case "tw":
|
|
1458
|
+
case "twee":
|
|
1459
|
+
return parseTweeFile(filename, opts);
|
|
1460
|
+
case "tw2":
|
|
1461
|
+
case "twee2":
|
|
1462
|
+
return parseTweeFile(filename, { ...opts, twee2Compat: true });
|
|
1463
|
+
case "css":
|
|
1464
|
+
return parseTaggedFile("stylesheet", filename);
|
|
1465
|
+
case "js":
|
|
1466
|
+
return parseTaggedFile("script", filename);
|
|
1467
|
+
case "otf":
|
|
1468
|
+
case "ttf":
|
|
1469
|
+
case "woff":
|
|
1470
|
+
case "woff2":
|
|
1471
|
+
return parseFontFile(filename);
|
|
1472
|
+
case "gif":
|
|
1473
|
+
case "jpeg":
|
|
1474
|
+
case "jpg":
|
|
1475
|
+
case "png":
|
|
1476
|
+
case "svg":
|
|
1477
|
+
case "tif":
|
|
1478
|
+
case "tiff":
|
|
1479
|
+
case "webp":
|
|
1480
|
+
return parseMediaFile("Twine.image", filename);
|
|
1481
|
+
case "aac":
|
|
1482
|
+
case "flac":
|
|
1483
|
+
case "m4a":
|
|
1484
|
+
case "mp3":
|
|
1485
|
+
case "oga":
|
|
1486
|
+
case "ogg":
|
|
1487
|
+
case "opus":
|
|
1488
|
+
case "wav":
|
|
1489
|
+
case "wave":
|
|
1490
|
+
case "weba":
|
|
1491
|
+
return parseMediaFile("Twine.audio", filename);
|
|
1492
|
+
case "mp4":
|
|
1493
|
+
case "ogv":
|
|
1494
|
+
case "webm":
|
|
1495
|
+
return parseMediaFile("Twine.video", filename);
|
|
1496
|
+
case "vtt":
|
|
1497
|
+
return parseMediaFile("Twine.vtt", filename);
|
|
1498
|
+
default:
|
|
1499
|
+
return void 0;
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
function loadTwee(story, filename, opts, diagnostics) {
|
|
1503
|
+
const result = parseTweeFile(filename, opts);
|
|
1504
|
+
diagnostics.push(...result.diagnostics);
|
|
1505
|
+
for (const p of result.passages) {
|
|
1506
|
+
storyAdd(story, p, diagnostics);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
function loadTagged(story, tag, filename, diagnostics) {
|
|
1510
|
+
const result = parseTaggedFile(tag, filename);
|
|
1511
|
+
for (const p of result.passages) {
|
|
1512
|
+
storyAdd(story, p, diagnostics);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
function loadMedia(story, tag, filename, diagnostics) {
|
|
1516
|
+
const result = parseMediaFile(tag, filename);
|
|
1517
|
+
for (const p of result.passages) {
|
|
1518
|
+
storyAdd(story, p, diagnostics);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
function loadFont(story, filename, diagnostics) {
|
|
1522
|
+
const result = parseFontFile(filename);
|
|
1523
|
+
for (const p of result.passages) {
|
|
1524
|
+
storyAdd(story, p, diagnostics);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
function loadSourcesCached(story, filenames, opts, diagnostics, processedFiles, cache, changedFiles) {
|
|
1528
|
+
const currentFiles = /* @__PURE__ */ new Set();
|
|
1529
|
+
for (const filename of filenames) {
|
|
1530
|
+
if (processedFiles.has(filename)) {
|
|
1531
|
+
diagnostics.push({ level: "warning", message: `load ${filename}: Skipping duplicate.` });
|
|
1532
|
+
continue;
|
|
1533
|
+
}
|
|
1534
|
+
currentFiles.add(filename);
|
|
1535
|
+
const cached = cache.get(filename);
|
|
1536
|
+
if (changedFiles && cached && !changedFiles.has(filename)) {
|
|
1537
|
+
diagnostics.push(...cached.diagnostics);
|
|
1538
|
+
for (const p of cached.passages) {
|
|
1539
|
+
storyAdd(story, p, diagnostics);
|
|
1540
|
+
}
|
|
1541
|
+
processedFiles.add(filename);
|
|
1542
|
+
continue;
|
|
1543
|
+
}
|
|
1544
|
+
let mtimeMs;
|
|
1545
|
+
try {
|
|
1546
|
+
mtimeMs = statSync3(filename).mtimeMs;
|
|
1547
|
+
} catch {
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
if (cached && cached.mtimeMs === mtimeMs) {
|
|
1551
|
+
diagnostics.push(...cached.diagnostics);
|
|
1552
|
+
for (const p of cached.passages) {
|
|
1553
|
+
storyAdd(story, p, diagnostics);
|
|
1554
|
+
}
|
|
1555
|
+
processedFiles.add(filename);
|
|
1556
|
+
continue;
|
|
1557
|
+
}
|
|
1558
|
+
try {
|
|
1559
|
+
const result = parseFile(filename, opts);
|
|
1560
|
+
if (!result) continue;
|
|
1561
|
+
cache.set(filename, {
|
|
1562
|
+
mtimeMs,
|
|
1563
|
+
passages: result.passages,
|
|
1564
|
+
diagnostics: result.diagnostics
|
|
1565
|
+
});
|
|
1566
|
+
diagnostics.push(...result.diagnostics);
|
|
1567
|
+
for (const p of result.passages) {
|
|
1568
|
+
storyAdd(story, p, diagnostics);
|
|
1569
|
+
}
|
|
1570
|
+
} catch (e) {
|
|
1571
|
+
diagnostics.push({
|
|
1572
|
+
level: "error",
|
|
1573
|
+
message: `load ${filename}: ${e instanceof Error ? e.message : String(e)}`,
|
|
1574
|
+
file: filename
|
|
1575
|
+
});
|
|
1576
|
+
continue;
|
|
1577
|
+
}
|
|
1578
|
+
processedFiles.add(filename);
|
|
1579
|
+
}
|
|
1580
|
+
for (const key of cache.keys()) {
|
|
1581
|
+
if (!currentFiles.has(key)) {
|
|
1582
|
+
cache.delete(key);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
if (story.name !== "" && !story.passages.some((p) => p.name === "StoryTitle")) {
|
|
1586
|
+
storyPrepend(story, { name: "StoryTitle", tags: [], text: story.name }, diagnostics);
|
|
1587
|
+
}
|
|
1430
1588
|
}
|
|
1431
1589
|
|
|
1432
1590
|
// src/version.ts
|
|
@@ -1434,20 +1592,20 @@ var VERSION = "1.2.0";
|
|
|
1434
1592
|
|
|
1435
1593
|
// src/output-twine2.ts
|
|
1436
1594
|
var CREATOR_NAME = "Twee-ts";
|
|
1437
|
-
function toTwine2Archive(story, startName, diagnostics) {
|
|
1438
|
-
return getTwine2DataChunk(story, startName, diagnostics) + "\n";
|
|
1595
|
+
function toTwine2Archive(story, startName, diagnostics, options) {
|
|
1596
|
+
return getTwine2DataChunk(story, startName, diagnostics, options) + "\n";
|
|
1439
1597
|
}
|
|
1440
|
-
function toTwine2HTML(story, format, startName, diagnostics) {
|
|
1598
|
+
function toTwine2HTML(story, format, startName, diagnostics, options) {
|
|
1441
1599
|
let template = readFormatSource(format);
|
|
1442
1600
|
if (template.includes("{{STORY_NAME}}")) {
|
|
1443
1601
|
template = template.replaceAll("{{STORY_NAME}}", htmlEscape(story.name));
|
|
1444
1602
|
}
|
|
1445
1603
|
if (template.includes("{{STORY_DATA}}")) {
|
|
1446
|
-
template = template.replace("{{STORY_DATA}}", getTwine2DataChunk(story, startName, diagnostics));
|
|
1604
|
+
template = template.replace("{{STORY_DATA}}", getTwine2DataChunk(story, startName, diagnostics, options));
|
|
1447
1605
|
}
|
|
1448
1606
|
return template;
|
|
1449
1607
|
}
|
|
1450
|
-
function getTwine2DataChunk(story, startName, diagnostics) {
|
|
1608
|
+
function getTwine2DataChunk(story, startName, diagnostics, options) {
|
|
1451
1609
|
ensureIFID(story, diagnostics);
|
|
1452
1610
|
const parts = [];
|
|
1453
1611
|
let startID = "";
|
|
@@ -1502,7 +1660,7 @@ function getTwine2DataChunk(story, startName, diagnostics) {
|
|
|
1502
1660
|
if (p.name === "StorySettings" && story.twine1.settings.size === 0) {
|
|
1503
1661
|
continue;
|
|
1504
1662
|
}
|
|
1505
|
-
parts.push(passageToPassagedata(p, pid));
|
|
1663
|
+
parts.push(passageToPassagedata(p, pid, options));
|
|
1506
1664
|
if (startName === p.name) {
|
|
1507
1665
|
startID = String(pid);
|
|
1508
1666
|
}
|
|
@@ -1512,9 +1670,9 @@ function getTwine2DataChunk(story, startName, diagnostics) {
|
|
|
1512
1670
|
for (const [opt, val] of story.twine2.options) {
|
|
1513
1671
|
if (val) opts.push(opt);
|
|
1514
1672
|
}
|
|
1515
|
-
const
|
|
1673
|
+
const optionsStr = opts.join(" ");
|
|
1516
1674
|
const zoom = String(story.twine2.zoom);
|
|
1517
|
-
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(
|
|
1675
|
+
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>`;
|
|
1518
1676
|
return wrapper + parts.join("") + "</tw-storydata>";
|
|
1519
1677
|
}
|
|
1520
1678
|
function ensureIFID(story, diagnostics) {
|
|
@@ -1695,7 +1853,7 @@ function modifyHead(html, modulePaths, headFile, diagnostics) {
|
|
|
1695
1853
|
}
|
|
1696
1854
|
|
|
1697
1855
|
// src/remote-formats.ts
|
|
1698
|
-
import { mkdirSync, readFileSync as readFileSync3, writeFileSync, existsSync, readdirSync as readdirSync3, statSync as
|
|
1856
|
+
import { mkdirSync, readFileSync as readFileSync3, writeFileSync, existsSync, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
|
|
1699
1857
|
import { join as join4 } from "path";
|
|
1700
1858
|
import { homedir as homedir2 } from "os";
|
|
1701
1859
|
var DEFAULT_SFA_INDICES = [
|
|
@@ -1907,7 +2065,7 @@ function discoverCachedFormats() {
|
|
|
1907
2065
|
for (const name of names) {
|
|
1908
2066
|
const nameDir = join4(cacheDir, name);
|
|
1909
2067
|
try {
|
|
1910
|
-
if (!
|
|
2068
|
+
if (!statSync4(nameDir).isDirectory()) continue;
|
|
1911
2069
|
} catch {
|
|
1912
2070
|
continue;
|
|
1913
2071
|
}
|
|
@@ -1948,11 +2106,12 @@ async function compileToFile(options) {
|
|
|
1948
2106
|
}
|
|
1949
2107
|
async function watch(options) {
|
|
1950
2108
|
const controller = new AbortController();
|
|
2109
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1951
2110
|
const filePaths = options.sources.filter((s) => typeof s === "string");
|
|
1952
2111
|
const modulePaths = options.modules ?? [];
|
|
1953
2112
|
const allPaths = [...filePaths, ...modulePaths];
|
|
1954
|
-
const handle = watchFilesystem(allPaths, options.outFile, () => {
|
|
1955
|
-
buildOutput(options).then((result) => {
|
|
2113
|
+
const handle = watchFilesystem(allPaths, options.outFile, (changedFiles) => {
|
|
2114
|
+
buildOutput(options, cache, changedFiles).then((result) => {
|
|
1956
2115
|
writeFileSync2(options.outFile, result.output, "utf-8");
|
|
1957
2116
|
options.onBuild?.(result);
|
|
1958
2117
|
}).catch((e) => {
|
|
@@ -1962,13 +2121,14 @@ async function watch(options) {
|
|
|
1962
2121
|
controller.signal.addEventListener("abort", () => handle.close());
|
|
1963
2122
|
return controller;
|
|
1964
2123
|
}
|
|
1965
|
-
async function buildOutput(options) {
|
|
2124
|
+
async function buildOutput(options, cache, changedFiles) {
|
|
1966
2125
|
const diagnostics = [];
|
|
1967
2126
|
const outputMode = options.outputMode ?? "html";
|
|
1968
2127
|
const trim = options.trim ?? true;
|
|
1969
2128
|
const twee2Compat = options.twee2Compat ?? false;
|
|
1970
2129
|
const testMode = options.testMode ?? false;
|
|
1971
2130
|
const noRemote = options.noRemote ?? false;
|
|
2131
|
+
const sourceInfo = options.sourceInfo ?? false;
|
|
1972
2132
|
clearIndexCache();
|
|
1973
2133
|
const filePaths = [];
|
|
1974
2134
|
const inlineSources = [];
|
|
@@ -1982,7 +2142,11 @@ async function buildOutput(options) {
|
|
|
1982
2142
|
const sourceFilenames = getFilenames(filePaths);
|
|
1983
2143
|
const story = createStory();
|
|
1984
2144
|
const processedFiles = /* @__PURE__ */ new Set();
|
|
1985
|
-
|
|
2145
|
+
if (cache) {
|
|
2146
|
+
loadSourcesCached(story, sourceFilenames, { trim, twee2Compat }, diagnostics, processedFiles, cache, changedFiles);
|
|
2147
|
+
} else {
|
|
2148
|
+
loadSources(story, sourceFilenames, { trim, twee2Compat }, diagnostics, processedFiles);
|
|
2149
|
+
}
|
|
1986
2150
|
loadInlineSources(story, inlineSources, { trim, twee2Compat }, diagnostics);
|
|
1987
2151
|
if (options.tagAliases) {
|
|
1988
2152
|
story.passages = applyTagAliases(story.passages, options.tagAliases);
|
|
@@ -2030,7 +2194,7 @@ async function buildOutput(options) {
|
|
|
2030
2194
|
output = toTwee(story, outputMode);
|
|
2031
2195
|
break;
|
|
2032
2196
|
case "twine2-archive":
|
|
2033
|
-
output = toTwine2Archive(story, startName, diagnostics);
|
|
2197
|
+
output = toTwine2Archive(story, startName, diagnostics, { sourceInfo });
|
|
2034
2198
|
break;
|
|
2035
2199
|
case "twine1-archive":
|
|
2036
2200
|
output = toTwine1Archive(story, startName);
|
|
@@ -2049,7 +2213,7 @@ async function buildOutput(options) {
|
|
|
2049
2213
|
throw new TweeTsError("No story format available for HTML output.", diagnostics);
|
|
2050
2214
|
}
|
|
2051
2215
|
if (format.isTwine2) {
|
|
2052
|
-
output = toTwine2HTML(story, format, startName, diagnostics);
|
|
2216
|
+
output = toTwine2HTML(story, format, startName, diagnostics, { sourceInfo });
|
|
2053
2217
|
} else {
|
|
2054
2218
|
if (story.name === "" && !storyHas(story, "StoryTitle")) {
|
|
2055
2219
|
diagnostics.push({
|
|
@@ -2282,7 +2446,7 @@ function validateConfig(data) {
|
|
|
2282
2446
|
errors.push(`"${key}" must be a string.`);
|
|
2283
2447
|
}
|
|
2284
2448
|
}
|
|
2285
|
-
for (const key of ["useTweegoPath", "trim", "twee2Compat", "testMode", "noRemote"]) {
|
|
2449
|
+
for (const key of ["useTweegoPath", "trim", "twee2Compat", "testMode", "noRemote", "sourceInfo"]) {
|
|
2286
2450
|
if (key in obj && typeof obj[key] !== "boolean") {
|
|
2287
2451
|
errors.push(`"${key}" must be a boolean.`);
|
|
2288
2452
|
}
|