@rohal12/twee-ts 1.3.0 → 1.5.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 +139 -35
- package/dist/bin/twee-ts.js.map +1 -1
- package/dist/{chunk-OGP2NXLJ.js → chunk-KI4QMLEN.js} +135 -34
- package/dist/chunk-KI4QMLEN.js.map +1 -0
- package/dist/index.cjs +135 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +135 -34
- 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-OGP2NXLJ.js.map +0 -1
package/dist/bin/twee-ts.js
CHANGED
|
@@ -64,6 +64,10 @@ function attrEscape(s) {
|
|
|
64
64
|
if (s.length === 0) return s;
|
|
65
65
|
return s.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'");
|
|
66
66
|
}
|
|
67
|
+
function fullAttrEscape(s) {
|
|
68
|
+
if (s.length === 0) return s;
|
|
69
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
70
|
+
}
|
|
67
71
|
function htmlEscape(s) {
|
|
68
72
|
if (s.length === 0) return s;
|
|
69
73
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -86,6 +90,13 @@ function commentSanitize(s) {
|
|
|
86
90
|
function htmlCommentSanitize(s) {
|
|
87
91
|
return s.replace(/-->/g, "-- >");
|
|
88
92
|
}
|
|
93
|
+
function rot13(s) {
|
|
94
|
+
if (s.length === 0) return s;
|
|
95
|
+
return s.replace(/[A-Za-z]/g, (ch) => {
|
|
96
|
+
const base = ch <= "Z" ? 65 : 97;
|
|
97
|
+
return String.fromCharCode((ch.charCodeAt(0) - base + 13) % 26 + base);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
89
100
|
function tweeUnescape(s) {
|
|
90
101
|
if (s.length === 0) return s;
|
|
91
102
|
let result = "";
|
|
@@ -150,12 +161,14 @@ function hasMetadataSize(p) {
|
|
|
150
161
|
return p.metadata != null && p.metadata.size != null && p.metadata.size !== "";
|
|
151
162
|
}
|
|
152
163
|
function hasAnyMetadata(p) {
|
|
153
|
-
|
|
164
|
+
if (!p.metadata) return false;
|
|
165
|
+
return Object.values(p.metadata).some((v) => v != null && v !== "");
|
|
154
166
|
}
|
|
155
167
|
function marshalMetadata(meta) {
|
|
156
168
|
const obj = {};
|
|
157
|
-
|
|
158
|
-
|
|
169
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
170
|
+
if (typeof value === "string" && value) obj[key] = value;
|
|
171
|
+
}
|
|
159
172
|
return JSON.stringify(obj);
|
|
160
173
|
}
|
|
161
174
|
function passageToTwee(p, outMode) {
|
|
@@ -181,7 +194,7 @@ function passageToTwee(p, outMode) {
|
|
|
181
194
|
output += "\n\n";
|
|
182
195
|
return output;
|
|
183
196
|
}
|
|
184
|
-
function passageToPassagedata(p, pid) {
|
|
197
|
+
function passageToPassagedata(p, pid, options) {
|
|
185
198
|
let position;
|
|
186
199
|
let size;
|
|
187
200
|
if (hasMetadataPosition(p)) {
|
|
@@ -198,9 +211,13 @@ function passageToPassagedata(p, pid) {
|
|
|
198
211
|
} else {
|
|
199
212
|
size = "100,100";
|
|
200
213
|
}
|
|
201
|
-
|
|
214
|
+
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))}`;
|
|
215
|
+
if (options?.sourceInfo && p.source) {
|
|
216
|
+
attrs += ` data-source-file=${quote(attrEscape(p.source.file))} data-source-line="${p.source.line}"`;
|
|
217
|
+
}
|
|
218
|
+
return `${attrs}>${htmlEscape(p.text)}</tw-passagedata>`;
|
|
202
219
|
}
|
|
203
|
-
function passageToTiddler(p, pid) {
|
|
220
|
+
function passageToTiddler(p, pid, obfuscateRot13 = false) {
|
|
204
221
|
let position;
|
|
205
222
|
if (hasMetadataPosition(p)) {
|
|
206
223
|
position = p.metadata?.position ?? "";
|
|
@@ -211,7 +228,10 @@ function passageToTiddler(p, pid) {
|
|
|
211
228
|
const yp = x === 0 ? y : y + 1;
|
|
212
229
|
position = `${xp * 140 - 130},${yp * 140 - 130}`;
|
|
213
230
|
}
|
|
214
|
-
|
|
231
|
+
const created = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 12);
|
|
232
|
+
const content = obfuscateRot13 && p.name !== "StorySettings" ? tiddlerEscape(rot13(p.text)) : tiddlerEscape(p.text);
|
|
233
|
+
const nameComment = obfuscateRot13 ? `<!-- ${commentSanitize(p.name)} -->` : "";
|
|
234
|
+
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>`;
|
|
215
235
|
}
|
|
216
236
|
function countWords(p) {
|
|
217
237
|
let text = p.text;
|
|
@@ -254,6 +274,7 @@ function createStory() {
|
|
|
254
274
|
formatVersion: "",
|
|
255
275
|
options: /* @__PURE__ */ new Map(),
|
|
256
276
|
start: "",
|
|
277
|
+
tags: "",
|
|
257
278
|
tagColors: /* @__PURE__ */ new Map(),
|
|
258
279
|
zoom: 1
|
|
259
280
|
}
|
|
@@ -300,6 +321,7 @@ function marshalStoryData(story) {
|
|
|
300
321
|
}
|
|
301
322
|
if (options.length > 0) data.options = options;
|
|
302
323
|
if (story.twine2.start) data.start = story.twine2.start;
|
|
324
|
+
if (story.twine2.tags) data.tags = story.twine2.tags;
|
|
303
325
|
if (story.twine2.tagColors.size > 0) {
|
|
304
326
|
data["tag-colors"] = Object.fromEntries(story.twine2.tagColors);
|
|
305
327
|
}
|
|
@@ -327,6 +349,7 @@ function unmarshalStoryData(story, json) {
|
|
|
327
349
|
}
|
|
328
350
|
}
|
|
329
351
|
if (typeof data.start === "string" && data.start) story.twine2.start = data.start;
|
|
352
|
+
if (typeof data.tags === "string") story.twine2.tags = data.tags;
|
|
330
353
|
if (typeof data["tag-colors"] === "object" && data["tag-colors"] !== null && !Array.isArray(data["tag-colors"])) {
|
|
331
354
|
for (const [tag, color] of Object.entries(data["tag-colors"])) {
|
|
332
355
|
if (typeof color === "string") story.twine2.tagColors.set(tag, color);
|
|
@@ -398,7 +421,7 @@ function storyAdd(story, p, diagnostics) {
|
|
|
398
421
|
processed = { ...p, text: marshalStoryData(story) };
|
|
399
422
|
} else {
|
|
400
423
|
diagnostics.push({
|
|
401
|
-
level: "
|
|
424
|
+
level: "warning",
|
|
402
425
|
message: `Cannot unmarshal "StoryData" compiler special passage; ${err}.`
|
|
403
426
|
});
|
|
404
427
|
}
|
|
@@ -621,17 +644,41 @@ function baseNameWithoutExt(filename) {
|
|
|
621
644
|
}
|
|
622
645
|
|
|
623
646
|
// src/formats.ts
|
|
647
|
+
function relaxedJSONParse(json) {
|
|
648
|
+
try {
|
|
649
|
+
return JSON.parse(json);
|
|
650
|
+
} catch {
|
|
651
|
+
}
|
|
652
|
+
let fixed = json;
|
|
653
|
+
fixed = fixed.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
|
|
654
|
+
fixed = fixed.replace(/(?<=^|[{,]\s*)([A-Za-z_$][A-Za-z0-9_$]*)\s*:/gm, '"$1":');
|
|
655
|
+
fixed = fixed.replace(/,\s*([}\]])/g, "$1");
|
|
656
|
+
return JSON.parse(fixed);
|
|
657
|
+
}
|
|
624
658
|
function parseFormatJSON(source, formatId) {
|
|
625
659
|
const first = source.indexOf("{");
|
|
626
660
|
const last = source.lastIndexOf("}");
|
|
627
661
|
if (first === -1 || last === -1) return null;
|
|
628
662
|
let chunk = source.slice(first, last + 1);
|
|
629
663
|
const parse = (json) => {
|
|
630
|
-
const raw =
|
|
664
|
+
const raw = relaxedJSONParse(json);
|
|
631
665
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
632
666
|
const obj = raw;
|
|
633
|
-
if (typeof obj.
|
|
634
|
-
|
|
667
|
+
if (typeof obj.version !== "string" || typeof obj.source !== "string") return null;
|
|
668
|
+
if (!parseSemver(obj.version)) return null;
|
|
669
|
+
const name = typeof obj.name === "string" ? obj.name : "Untitled Story Format";
|
|
670
|
+
const result = {
|
|
671
|
+
name,
|
|
672
|
+
version: obj.version,
|
|
673
|
+
source: obj.source,
|
|
674
|
+
proofing: obj.proofing === true
|
|
675
|
+
};
|
|
676
|
+
if (typeof obj.author === "string") result.author = obj.author;
|
|
677
|
+
if (typeof obj.description === "string") result.description = obj.description;
|
|
678
|
+
if (typeof obj.image === "string") result.image = obj.image;
|
|
679
|
+
if (typeof obj.url === "string") result.url = obj.url;
|
|
680
|
+
if (typeof obj.license === "string") result.license = obj.license;
|
|
681
|
+
return result;
|
|
635
682
|
};
|
|
636
683
|
try {
|
|
637
684
|
return parse(chunk);
|
|
@@ -697,17 +744,55 @@ function discoverFormats(searchDirs) {
|
|
|
697
744
|
format.name = data.name;
|
|
698
745
|
format.version = data.version;
|
|
699
746
|
format.proofing = data.proofing ?? false;
|
|
747
|
+
if (data.author) format.author = data.author;
|
|
748
|
+
if (data.description) format.description = data.description;
|
|
749
|
+
if (data.image) format.image = data.image;
|
|
750
|
+
if (data.url) format.url = data.url;
|
|
751
|
+
if (data.license) format.license = data.license;
|
|
700
752
|
} catch {
|
|
701
753
|
continue;
|
|
702
754
|
}
|
|
755
|
+
} else {
|
|
756
|
+
format.name = entry;
|
|
703
757
|
}
|
|
704
758
|
formats.set(entry, format);
|
|
705
759
|
break;
|
|
706
760
|
}
|
|
707
761
|
}
|
|
708
762
|
}
|
|
763
|
+
pruneFormats(formats);
|
|
709
764
|
return formats;
|
|
710
765
|
}
|
|
766
|
+
function pruneFormats(formats) {
|
|
767
|
+
const bestByNameMajor = /* @__PURE__ */ new Map();
|
|
768
|
+
const seenNameVersion = /* @__PURE__ */ new Set();
|
|
769
|
+
for (const [id, f] of formats) {
|
|
770
|
+
if (!f.isTwine2 || !f.name || !f.version) continue;
|
|
771
|
+
const v = parseSemver(f.version);
|
|
772
|
+
if (!v) continue;
|
|
773
|
+
const nameVersionKey = `${f.name}@${f.version}`;
|
|
774
|
+
if (seenNameVersion.has(nameVersionKey)) {
|
|
775
|
+
formats.delete(id);
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
seenNameVersion.add(nameVersionKey);
|
|
779
|
+
const groupKey = `${f.name}@${v[0]}`;
|
|
780
|
+
const existing = bestByNameMajor.get(groupKey);
|
|
781
|
+
if (!existing || semverCompare(v, existing.version) > 0) {
|
|
782
|
+
bestByNameMajor.set(groupKey, { id, version: v });
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
for (const [id, f] of [...formats]) {
|
|
786
|
+
if (!f.isTwine2 || !f.name || !f.version) continue;
|
|
787
|
+
const v = parseSemver(f.version);
|
|
788
|
+
if (!v) continue;
|
|
789
|
+
const groupKey = `${f.name}@${v[0]}`;
|
|
790
|
+
const best = bestByNameMajor.get(groupKey);
|
|
791
|
+
if (best && best.id !== id) {
|
|
792
|
+
formats.delete(id);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
711
796
|
function getFormatSearchDirs(extraPaths = [], useTweegoPath = true) {
|
|
712
797
|
const subdirNames = ["storyformats", ".storyformats", "story-formats", "storyFormats", "targets"];
|
|
713
798
|
const basePaths = /* @__PURE__ */ new Set();
|
|
@@ -1094,7 +1179,7 @@ function parseTwee(source, options = {}) {
|
|
|
1094
1179
|
if (pCount > 1 && current) {
|
|
1095
1180
|
passages.push(current);
|
|
1096
1181
|
}
|
|
1097
|
-
current = { name: "", tags: [], text: "" };
|
|
1182
|
+
current = { name: "", tags: [], text: "", source: { file: filename, line: item.line } };
|
|
1098
1183
|
break;
|
|
1099
1184
|
case 3 /* Name */: {
|
|
1100
1185
|
if (!current) break;
|
|
@@ -1144,8 +1229,11 @@ function parseTwee(source, options = {}) {
|
|
|
1144
1229
|
}
|
|
1145
1230
|
const parsed = raw;
|
|
1146
1231
|
const meta = {};
|
|
1147
|
-
|
|
1148
|
-
|
|
1232
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
1233
|
+
if (typeof value === "string") {
|
|
1234
|
+
meta[key] = value;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1149
1237
|
current.metadata = meta;
|
|
1150
1238
|
} catch (e) {
|
|
1151
1239
|
diagnostics.push({
|
|
@@ -1159,7 +1247,11 @@ function parseTwee(source, options = {}) {
|
|
|
1159
1247
|
}
|
|
1160
1248
|
case 6 /* Content */: {
|
|
1161
1249
|
if (!current) break;
|
|
1162
|
-
|
|
1250
|
+
if (trim) {
|
|
1251
|
+
current.text = item.val.trim();
|
|
1252
|
+
} else {
|
|
1253
|
+
current.text = item.val.replace(/\n\s*$/, "");
|
|
1254
|
+
}
|
|
1163
1255
|
break;
|
|
1164
1256
|
}
|
|
1165
1257
|
default: {
|
|
@@ -1334,20 +1426,20 @@ var VERSION = "1.2.0";
|
|
|
1334
1426
|
|
|
1335
1427
|
// src/output-twine2.ts
|
|
1336
1428
|
var CREATOR_NAME = "Twee-ts";
|
|
1337
|
-
function toTwine2Archive(story, startName, diagnostics) {
|
|
1338
|
-
return getTwine2DataChunk(story, startName, diagnostics) + "\n";
|
|
1429
|
+
function toTwine2Archive(story, startName, diagnostics, options) {
|
|
1430
|
+
return getTwine2DataChunk(story, startName, diagnostics, options) + "\n";
|
|
1339
1431
|
}
|
|
1340
|
-
function toTwine2HTML(story, format, startName, diagnostics) {
|
|
1432
|
+
function toTwine2HTML(story, format, startName, diagnostics, options) {
|
|
1341
1433
|
let template = readFormatSource(format);
|
|
1342
1434
|
if (template.includes("{{STORY_NAME}}")) {
|
|
1343
1435
|
template = template.replaceAll("{{STORY_NAME}}", htmlEscape(story.name));
|
|
1344
1436
|
}
|
|
1345
1437
|
if (template.includes("{{STORY_DATA}}")) {
|
|
1346
|
-
template = template.replace("{{STORY_DATA}}", getTwine2DataChunk(story, startName, diagnostics));
|
|
1438
|
+
template = template.replace("{{STORY_DATA}}", getTwine2DataChunk(story, startName, diagnostics, options));
|
|
1347
1439
|
}
|
|
1348
1440
|
return template;
|
|
1349
1441
|
}
|
|
1350
|
-
function getTwine2DataChunk(story, startName, diagnostics) {
|
|
1442
|
+
function getTwine2DataChunk(story, startName, diagnostics, options) {
|
|
1351
1443
|
ensureIFID(story, diagnostics);
|
|
1352
1444
|
const parts = [];
|
|
1353
1445
|
let startID = "";
|
|
@@ -1387,8 +1479,12 @@ function getTwine2DataChunk(story, startName, diagnostics) {
|
|
|
1387
1479
|
}
|
|
1388
1480
|
}
|
|
1389
1481
|
parts.push(`<script role="script" id="twine-user-script" type="text/twine-javascript">${scriptContent}</script>`);
|
|
1482
|
+
const validTagColors = /* @__PURE__ */ new Set(["gray", "red", "orange", "yellow", "green", "blue", "purple"]);
|
|
1483
|
+
const hexColorPattern = /^#[0-9a-fA-F]{3,8}$/;
|
|
1390
1484
|
for (const [tag, color] of story.twine2.tagColors) {
|
|
1391
|
-
|
|
1485
|
+
if (validTagColors.has(color) || hexColorPattern.test(color)) {
|
|
1486
|
+
parts.push(`<tw-tag name="${attrEscape(tag)}" color="${attrEscape(color)}"></tw-tag>`);
|
|
1487
|
+
}
|
|
1392
1488
|
}
|
|
1393
1489
|
pid = 1;
|
|
1394
1490
|
for (const p of story.passages) {
|
|
@@ -1398,7 +1494,7 @@ function getTwine2DataChunk(story, startName, diagnostics) {
|
|
|
1398
1494
|
if (p.name === "StorySettings" && story.twine1.settings.size === 0) {
|
|
1399
1495
|
continue;
|
|
1400
1496
|
}
|
|
1401
|
-
parts.push(passageToPassagedata(p, pid));
|
|
1497
|
+
parts.push(passageToPassagedata(p, pid, options));
|
|
1402
1498
|
if (startName === p.name) {
|
|
1403
1499
|
startID = String(pid);
|
|
1404
1500
|
}
|
|
@@ -1408,9 +1504,9 @@ function getTwine2DataChunk(story, startName, diagnostics) {
|
|
|
1408
1504
|
for (const [opt, val] of story.twine2.options) {
|
|
1409
1505
|
if (val) opts.push(opt);
|
|
1410
1506
|
}
|
|
1411
|
-
const
|
|
1507
|
+
const optionsStr = opts.join(" ");
|
|
1412
1508
|
const zoom = String(story.twine2.zoom);
|
|
1413
|
-
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(
|
|
1509
|
+
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>`;
|
|
1414
1510
|
return wrapper + parts.join("") + "</tw-storydata>";
|
|
1415
1511
|
}
|
|
1416
1512
|
function ensureIFID(story, diagnostics) {
|
|
@@ -1426,7 +1522,7 @@ function ensureIFID(story, diagnostics) {
|
|
|
1426
1522
|
story.ifid = ifid;
|
|
1427
1523
|
diagnostics.push({
|
|
1428
1524
|
level: "error",
|
|
1429
|
-
message: `Story IFID not found. Add
|
|
1525
|
+
message: `Story IFID not found. Add an IFID to your story: {"ifid":"${ifid}"}`
|
|
1430
1526
|
});
|
|
1431
1527
|
}
|
|
1432
1528
|
}
|
|
@@ -1437,7 +1533,7 @@ import { join as join3, dirname } from "path";
|
|
|
1437
1533
|
var CREATOR_NAME2 = "twee-ts";
|
|
1438
1534
|
function toTwine1Archive(story, _startName) {
|
|
1439
1535
|
const { data, count } = getTwine1PassageChunk(story);
|
|
1440
|
-
return `<div id="storeArea" data-size="${count}"
|
|
1536
|
+
return `<div id="storeArea" data-size="${count}">${data}</div>
|
|
1441
1537
|
`;
|
|
1442
1538
|
}
|
|
1443
1539
|
function toTwine1HTML(story, format, startName, _diagnostics) {
|
|
@@ -1482,12 +1578,13 @@ function toTwine1HTML(story, format, startName, _diagnostics) {
|
|
|
1482
1578
|
return template;
|
|
1483
1579
|
}
|
|
1484
1580
|
function getTwine1PassageChunk(story) {
|
|
1581
|
+
const obfuscateRot13 = story.twine1.settings.get("obfuscate") === "rot13";
|
|
1485
1582
|
let data = "";
|
|
1486
1583
|
let count = 0;
|
|
1487
1584
|
for (const p of story.passages) {
|
|
1488
1585
|
if (hasTag(p, "Twine.private")) continue;
|
|
1489
1586
|
count++;
|
|
1490
|
-
data += passageToTiddler(p, count);
|
|
1587
|
+
data += passageToTiddler(p, count, obfuscateRot13);
|
|
1491
1588
|
}
|
|
1492
1589
|
return { data, count };
|
|
1493
1590
|
}
|
|
@@ -1864,6 +1961,7 @@ async function buildOutput(options) {
|
|
|
1864
1961
|
const twee2Compat = options.twee2Compat ?? false;
|
|
1865
1962
|
const testMode = options.testMode ?? false;
|
|
1866
1963
|
const noRemote = options.noRemote ?? false;
|
|
1964
|
+
const sourceInfo = options.sourceInfo ?? false;
|
|
1867
1965
|
clearIndexCache();
|
|
1868
1966
|
const filePaths = [];
|
|
1869
1967
|
const inlineSources = [];
|
|
@@ -1925,7 +2023,7 @@ async function buildOutput(options) {
|
|
|
1925
2023
|
output = toTwee(story, outputMode);
|
|
1926
2024
|
break;
|
|
1927
2025
|
case "twine2-archive":
|
|
1928
|
-
output = toTwine2Archive(story, startName, diagnostics);
|
|
2026
|
+
output = toTwine2Archive(story, startName, diagnostics, { sourceInfo });
|
|
1929
2027
|
break;
|
|
1930
2028
|
case "twine1-archive":
|
|
1931
2029
|
output = toTwine1Archive(story, startName);
|
|
@@ -1944,7 +2042,7 @@ async function buildOutput(options) {
|
|
|
1944
2042
|
throw new TweeTsError("No story format available for HTML output.", diagnostics);
|
|
1945
2043
|
}
|
|
1946
2044
|
if (format.isTwine2) {
|
|
1947
|
-
output = toTwine2HTML(story, format, startName, diagnostics);
|
|
2045
|
+
output = toTwine2HTML(story, format, startName, diagnostics, { sourceInfo });
|
|
1948
2046
|
} else {
|
|
1949
2047
|
if (story.name === "" && !storyHas(story, "StoryTitle")) {
|
|
1950
2048
|
diagnostics.push({
|
|
@@ -1986,11 +2084,14 @@ function storyToJSON(story) {
|
|
|
1986
2084
|
tags: p.tags,
|
|
1987
2085
|
text: p.text
|
|
1988
2086
|
};
|
|
1989
|
-
if (p.metadata
|
|
2087
|
+
if (p.metadata) {
|
|
1990
2088
|
const meta = {};
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2089
|
+
for (const [key, value] of Object.entries(p.metadata)) {
|
|
2090
|
+
if (typeof value === "string") meta[key] = value;
|
|
2091
|
+
}
|
|
2092
|
+
if (Object.keys(meta).length > 0) {
|
|
2093
|
+
entry.metadata = meta;
|
|
2094
|
+
}
|
|
1994
2095
|
}
|
|
1995
2096
|
storyPassages.push(entry);
|
|
1996
2097
|
}
|
|
@@ -2072,7 +2173,7 @@ function validateConfig(data) {
|
|
|
2072
2173
|
errors.push(`"${key}" must be a string.`);
|
|
2073
2174
|
}
|
|
2074
2175
|
}
|
|
2075
|
-
for (const key of ["useTweegoPath", "trim", "twee2Compat", "testMode", "noRemote"]) {
|
|
2176
|
+
for (const key of ["useTweegoPath", "trim", "twee2Compat", "testMode", "noRemote", "sourceInfo"]) {
|
|
2076
2177
|
if (key in obj && typeof obj[key] !== "boolean") {
|
|
2077
2178
|
errors.push(`"${key}" must be a boolean.`);
|
|
2078
2179
|
}
|
|
@@ -2142,6 +2243,7 @@ var { values, positionals } = parseArgs({
|
|
|
2142
2243
|
"format-url": { type: "string", multiple: true },
|
|
2143
2244
|
"no-remote": { type: "boolean" },
|
|
2144
2245
|
"tag-alias": { type: "string", multiple: true },
|
|
2246
|
+
"source-info": { type: "boolean" },
|
|
2145
2247
|
config: { type: "string", short: "c" },
|
|
2146
2248
|
"no-config": { type: "boolean" }
|
|
2147
2249
|
}
|
|
@@ -2210,7 +2312,8 @@ async function main() {
|
|
|
2210
2312
|
formatIndices: values["format-index"] ?? config?.formatIndices,
|
|
2211
2313
|
formatUrls: values["format-url"] ?? config?.formatUrls,
|
|
2212
2314
|
noRemote: values["no-remote"] ?? config?.noRemote ?? false,
|
|
2213
|
-
tagAliases
|
|
2315
|
+
tagAliases,
|
|
2316
|
+
sourceInfo: values["source-info"] ?? config?.sourceInfo ?? false
|
|
2214
2317
|
};
|
|
2215
2318
|
const outFile = values.output ?? config?.output ?? "-";
|
|
2216
2319
|
if (values.watch) {
|
|
@@ -2336,6 +2439,7 @@ Options:
|
|
|
2336
2439
|
--format-index <url> SFA-compatible format index URL (repeatable)
|
|
2337
2440
|
--format-url <url> Direct format.js URL (repeatable)
|
|
2338
2441
|
--tag-alias <alias=target> Map a tag to a special tag (repeatable)
|
|
2442
|
+
--source-info Emit source file/line as data- attributes on passages
|
|
2339
2443
|
--no-remote Disable remote format fetching
|
|
2340
2444
|
-c, --config <file> Config file path (default: ${CONFIG_FILENAME})
|
|
2341
2445
|
--no-config Skip config file loading
|