@rohal12/twee-ts 1.1.2 → 1.2.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 +214 -142
- package/dist/bin/twee-ts.js.map +1 -1
- package/dist/{chunk-QTXRKA4T.js → chunk-S4SWCQLW.js} +211 -136
- package/dist/chunk-S4SWCQLW.js.map +1 -0
- package/dist/index.cjs +244 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +213 -140
- package/dist/index.js.map +1 -1
- package/dist/plugins/rollup.d.ts +7 -1
- package/dist/plugins/rollup.js +2 -2
- package/dist/plugins/rollup.js.map +1 -1
- package/dist/plugins/vite.d.ts +7 -1
- package/dist/plugins/vite.js +2 -2
- package/dist/plugins/vite.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-QTXRKA4T.js.map +0 -1
package/dist/bin/twee-ts.js
CHANGED
|
@@ -76,13 +76,26 @@ function tweeEscape(s) {
|
|
|
76
76
|
if (s.length === 0) return s;
|
|
77
77
|
return s.replace(/[\\[\]{}]/g, (ch) => "\\" + ch);
|
|
78
78
|
}
|
|
79
|
+
function jsStringEscape(s) {
|
|
80
|
+
if (s.length === 0) return s;
|
|
81
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
82
|
+
}
|
|
83
|
+
function commentSanitize(s) {
|
|
84
|
+
return s.replace(/\*\//g, "* /");
|
|
85
|
+
}
|
|
86
|
+
function htmlCommentSanitize(s) {
|
|
87
|
+
return s.replace(/-->/g, "-- >");
|
|
88
|
+
}
|
|
79
89
|
function tweeUnescape(s) {
|
|
80
90
|
if (s.length === 0) return s;
|
|
81
91
|
let result = "";
|
|
82
92
|
for (let i = 0; i < s.length; i++) {
|
|
83
93
|
if (s[i] === "\\") {
|
|
84
94
|
i++;
|
|
85
|
-
if (i >= s.length)
|
|
95
|
+
if (i >= s.length) {
|
|
96
|
+
result += "\\";
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
86
99
|
}
|
|
87
100
|
result += s[i];
|
|
88
101
|
}
|
|
@@ -172,16 +185,16 @@ function passageToPassagedata(p, pid) {
|
|
|
172
185
|
let position;
|
|
173
186
|
let size;
|
|
174
187
|
if (hasMetadataPosition(p)) {
|
|
175
|
-
position = p.metadata
|
|
188
|
+
position = p.metadata?.position ?? "";
|
|
176
189
|
} else {
|
|
177
190
|
const x = pid % 10;
|
|
178
|
-
|
|
191
|
+
const y = Math.floor(pid / 10);
|
|
179
192
|
const xp = x === 0 ? 10 : x;
|
|
180
193
|
const yp = x === 0 ? y : y + 1;
|
|
181
194
|
position = `${xp * 125 - 25},${yp * 125 - 25}`;
|
|
182
195
|
}
|
|
183
196
|
if (hasMetadataSize(p)) {
|
|
184
|
-
size = p.metadata
|
|
197
|
+
size = p.metadata?.size ?? "";
|
|
185
198
|
} else {
|
|
186
199
|
size = "100,100";
|
|
187
200
|
}
|
|
@@ -190,10 +203,10 @@ function passageToPassagedata(p, pid) {
|
|
|
190
203
|
function passageToTiddler(p, pid) {
|
|
191
204
|
let position;
|
|
192
205
|
if (hasMetadataPosition(p)) {
|
|
193
|
-
position = p.metadata
|
|
206
|
+
position = p.metadata?.position ?? "";
|
|
194
207
|
} else {
|
|
195
208
|
const x = pid % 10;
|
|
196
|
-
|
|
209
|
+
const y = Math.floor(pid / 10);
|
|
197
210
|
const xp = x === 0 ? 10 : x;
|
|
198
211
|
const yp = x === 0 ? y : y + 1;
|
|
199
212
|
position = `${xp * 140 - 130},${yp * 140 - 130}`;
|
|
@@ -212,15 +225,17 @@ function countWords(p) {
|
|
|
212
225
|
}
|
|
213
226
|
function applyTagAliases(passages, aliases) {
|
|
214
227
|
const entries = Object.entries(aliases);
|
|
215
|
-
if (entries.length === 0) return;
|
|
216
|
-
|
|
217
|
-
const original =
|
|
228
|
+
if (entries.length === 0) return [...passages];
|
|
229
|
+
return passages.map((p) => {
|
|
230
|
+
const original = p.tags;
|
|
231
|
+
const added = [];
|
|
218
232
|
for (const [alias, canonical] of entries) {
|
|
219
|
-
if (original.includes(alias) && !
|
|
220
|
-
|
|
233
|
+
if (original.includes(alias) && !original.includes(canonical) && !added.includes(canonical)) {
|
|
234
|
+
added.push(canonical);
|
|
221
235
|
}
|
|
222
236
|
}
|
|
223
|
-
|
|
237
|
+
return added.length > 0 ? { ...p, tags: [...original, ...added] } : p;
|
|
238
|
+
});
|
|
224
239
|
}
|
|
225
240
|
function quote(s) {
|
|
226
241
|
return `"${s}"`;
|
|
@@ -292,27 +307,32 @@ function marshalStoryData(story) {
|
|
|
292
307
|
return JSON.stringify(data, null, " ");
|
|
293
308
|
}
|
|
294
309
|
function unmarshalStoryData(story, json) {
|
|
295
|
-
let
|
|
310
|
+
let raw;
|
|
296
311
|
try {
|
|
297
|
-
|
|
312
|
+
raw = JSON.parse(json);
|
|
298
313
|
} catch (e) {
|
|
299
314
|
return `Cannot unmarshal "StoryData"; ${e instanceof Error ? e.message : String(e)}`;
|
|
300
315
|
}
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
316
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
317
|
+
return 'Cannot unmarshal "StoryData"; expected a JSON object';
|
|
318
|
+
}
|
|
319
|
+
const data = raw;
|
|
320
|
+
if (typeof data.ifid === "string" && data.ifid) story.ifid = data.ifid.toUpperCase();
|
|
321
|
+
if (typeof data.format === "string" && data.format) story.twine2.format = data.format;
|
|
322
|
+
if (typeof data["format-version"] === "string" && data["format-version"])
|
|
323
|
+
story.twine2.formatVersion = data["format-version"];
|
|
324
|
+
if (Array.isArray(data.options)) {
|
|
305
325
|
for (const opt of data.options) {
|
|
306
|
-
story.twine2.options.set(opt, true);
|
|
326
|
+
if (typeof opt === "string") story.twine2.options.set(opt, true);
|
|
307
327
|
}
|
|
308
328
|
}
|
|
309
|
-
if (data.start) story.twine2.start = data.start;
|
|
310
|
-
if (data["tag-colors"]) {
|
|
329
|
+
if (typeof data.start === "string" && data.start) story.twine2.start = data.start;
|
|
330
|
+
if (typeof data["tag-colors"] === "object" && data["tag-colors"] !== null && !Array.isArray(data["tag-colors"])) {
|
|
311
331
|
for (const [tag, color] of Object.entries(data["tag-colors"])) {
|
|
312
|
-
story.twine2.tagColors.set(tag, color);
|
|
332
|
+
if (typeof color === "string") story.twine2.tagColors.set(tag, color);
|
|
313
333
|
}
|
|
314
334
|
}
|
|
315
|
-
if (data.zoom
|
|
335
|
+
if (typeof data.zoom === "number" && data.zoom !== 0) story.twine2.zoom = data.zoom;
|
|
316
336
|
return null;
|
|
317
337
|
}
|
|
318
338
|
function unmarshalStorySettings(story, text, diagnostics) {
|
|
@@ -355,6 +375,7 @@ function unmarshalStorySettings(story, text, diagnostics) {
|
|
|
355
375
|
}
|
|
356
376
|
}
|
|
357
377
|
function storyAdd(story, p, diagnostics) {
|
|
378
|
+
let processed = p;
|
|
358
379
|
switch (p.name) {
|
|
359
380
|
case "StoryIncludes":
|
|
360
381
|
diagnostics.push({
|
|
@@ -374,7 +395,7 @@ function storyAdd(story, p, diagnostics) {
|
|
|
374
395
|
});
|
|
375
396
|
}
|
|
376
397
|
}
|
|
377
|
-
p
|
|
398
|
+
processed = { ...p, text: marshalStoryData(story) };
|
|
378
399
|
} else {
|
|
379
400
|
diagnostics.push({
|
|
380
401
|
level: "error",
|
|
@@ -386,12 +407,14 @@ function storyAdd(story, p, diagnostics) {
|
|
|
386
407
|
case "StorySettings":
|
|
387
408
|
unmarshalStorySettings(story, p.text, diagnostics);
|
|
388
409
|
break;
|
|
389
|
-
case "StoryTitle":
|
|
390
|
-
|
|
391
|
-
|
|
410
|
+
case "StoryTitle": {
|
|
411
|
+
const trimmed = p.text.trim();
|
|
412
|
+
processed = { ...p, text: trimmed };
|
|
413
|
+
story.name = trimmed;
|
|
392
414
|
break;
|
|
415
|
+
}
|
|
393
416
|
}
|
|
394
|
-
storyAppend(story,
|
|
417
|
+
storyAppend(story, processed, diagnostics);
|
|
395
418
|
}
|
|
396
419
|
function getStoryStats(story) {
|
|
397
420
|
let storyPassages = 0;
|
|
@@ -445,7 +468,7 @@ var MEDIA_TYPE_MAP = {
|
|
|
445
468
|
webm: "video/webm"
|
|
446
469
|
};
|
|
447
470
|
function mediaTypeFromExt(ext) {
|
|
448
|
-
return MEDIA_TYPE_MAP[ext] ?? "";
|
|
471
|
+
return MEDIA_TYPE_MAP[ext] ?? "application/octet-stream";
|
|
449
472
|
}
|
|
450
473
|
function mediaTypeFromFilename(filename) {
|
|
451
474
|
return mediaTypeFromExt(normalizedFileExt(filename));
|
|
@@ -467,8 +490,6 @@ var KNOWN_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
467
490
|
"twee",
|
|
468
491
|
"tw2",
|
|
469
492
|
"twee2",
|
|
470
|
-
"htm",
|
|
471
|
-
"html",
|
|
472
493
|
"css",
|
|
473
494
|
"js",
|
|
474
495
|
"otf",
|
|
@@ -578,23 +599,49 @@ function watchFilesystem(pathnames, outFilename, callback) {
|
|
|
578
599
|
}
|
|
579
600
|
|
|
580
601
|
// src/formats.ts
|
|
581
|
-
import {
|
|
602
|
+
import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
582
603
|
import { join as join2 } from "path";
|
|
583
604
|
import { homedir } from "os";
|
|
605
|
+
|
|
606
|
+
// src/util.ts
|
|
607
|
+
import { readFileSync } from "fs";
|
|
608
|
+
import { parse as parsePath } from "path";
|
|
609
|
+
function readUTF8(filename) {
|
|
610
|
+
let content = readFileSync(filename, "utf-8");
|
|
611
|
+
if (content.charCodeAt(0) === 65279) content = content.slice(1);
|
|
612
|
+
content = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
613
|
+
return content;
|
|
614
|
+
}
|
|
615
|
+
function readBase64(filename) {
|
|
616
|
+
return readFileSync(filename).toString("base64");
|
|
617
|
+
}
|
|
618
|
+
function baseNameWithoutExt(filename) {
|
|
619
|
+
const { name, ext } = parsePath(filename);
|
|
620
|
+
return name || ext;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/formats.ts
|
|
584
624
|
function parseFormatJSON(source, formatId) {
|
|
585
625
|
const first = source.indexOf("{");
|
|
586
626
|
const last = source.lastIndexOf("}");
|
|
587
627
|
if (first === -1 || last === -1) return null;
|
|
588
628
|
let chunk = source.slice(first, last + 1);
|
|
629
|
+
const parse = (json) => {
|
|
630
|
+
const raw = JSON.parse(json);
|
|
631
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
632
|
+
const obj = raw;
|
|
633
|
+
if (typeof obj.name !== "string" || typeof obj.version !== "string" || typeof obj.source !== "string") return null;
|
|
634
|
+
return { name: obj.name, version: obj.version, source: obj.source, proofing: obj.proofing === true };
|
|
635
|
+
};
|
|
589
636
|
try {
|
|
590
|
-
return
|
|
637
|
+
return parse(chunk);
|
|
591
638
|
} catch {
|
|
592
639
|
if (formatId.toLowerCase().startsWith("harlowe")) {
|
|
593
640
|
const setupIdx = chunk.lastIndexOf(',"setup": function');
|
|
594
641
|
if (setupIdx !== -1) {
|
|
595
642
|
chunk = chunk.slice(0, setupIdx) + "}";
|
|
596
643
|
try {
|
|
597
|
-
return
|
|
644
|
+
return parse(chunk);
|
|
598
645
|
} catch {
|
|
599
646
|
}
|
|
600
647
|
}
|
|
@@ -602,12 +649,6 @@ function parseFormatJSON(source, formatId) {
|
|
|
602
649
|
return null;
|
|
603
650
|
}
|
|
604
651
|
}
|
|
605
|
-
function readUTF8(filename) {
|
|
606
|
-
let content = readFileSync(filename, "utf-8");
|
|
607
|
-
if (content.charCodeAt(0) === 65279) content = content.slice(1);
|
|
608
|
-
content = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
609
|
-
return content;
|
|
610
|
-
}
|
|
611
652
|
function discoverFormats(searchDirs) {
|
|
612
653
|
const formats = /* @__PURE__ */ new Map();
|
|
613
654
|
const baseFilenames = ["format.js", "header.html"];
|
|
@@ -702,9 +743,11 @@ function parseSemver(v) {
|
|
|
702
743
|
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
703
744
|
}
|
|
704
745
|
function semverCompare(a, b) {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
746
|
+
const [aMajor, aMinor, aPatch] = a;
|
|
747
|
+
const [bMajor, bMinor, bPatch] = b;
|
|
748
|
+
if (aMajor !== bMajor) return aMajor - bMajor;
|
|
749
|
+
if (aMinor !== bMinor) return aMinor - bMinor;
|
|
750
|
+
if (aPatch !== bPatch) return aPatch - bPatch;
|
|
708
751
|
return 0;
|
|
709
752
|
}
|
|
710
753
|
function getFormatIdByNameAndVersion(formats, name, version) {
|
|
@@ -735,7 +778,6 @@ function readFormatSource(format) {
|
|
|
735
778
|
}
|
|
736
779
|
|
|
737
780
|
// src/loader.ts
|
|
738
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
739
781
|
import { basename } from "path";
|
|
740
782
|
|
|
741
783
|
// src/lexer.ts
|
|
@@ -1012,13 +1054,13 @@ function hasTwee2Syntax(s) {
|
|
|
1012
1054
|
}
|
|
1013
1055
|
function twee2ToV3(s) {
|
|
1014
1056
|
if (!hasTwee2Syntax(s)) return s;
|
|
1015
|
-
|
|
1057
|
+
let result = s.replace(twee2HeaderRe, (_, p1, p2, p3) => {
|
|
1016
1058
|
const tags = p2 ?? "";
|
|
1017
1059
|
const pos = p3 ?? "";
|
|
1018
1060
|
return `${p1}${tags} {"position":"${pos}"}`;
|
|
1019
1061
|
});
|
|
1020
|
-
|
|
1021
|
-
return
|
|
1062
|
+
result = result.replace(twee2BadPosRe, "$1");
|
|
1063
|
+
return result;
|
|
1022
1064
|
}
|
|
1023
1065
|
|
|
1024
1066
|
// src/parser.ts
|
|
@@ -1096,7 +1138,11 @@ function parseTwee(source, options = {}) {
|
|
|
1096
1138
|
return { passages, diagnostics };
|
|
1097
1139
|
}
|
|
1098
1140
|
try {
|
|
1099
|
-
const
|
|
1141
|
+
const raw = JSON.parse(item.val);
|
|
1142
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
1143
|
+
throw new Error("expected a JSON object");
|
|
1144
|
+
}
|
|
1145
|
+
const parsed = raw;
|
|
1100
1146
|
const meta = {};
|
|
1101
1147
|
if (typeof parsed.position === "string") meta.position = parsed.position;
|
|
1102
1148
|
if (typeof parsed.size === "string") meta.size = parsed.size;
|
|
@@ -1116,6 +1162,16 @@ function parseTwee(source, options = {}) {
|
|
|
1116
1162
|
current.text = trim ? item.val.trim() : item.val;
|
|
1117
1163
|
break;
|
|
1118
1164
|
}
|
|
1165
|
+
default: {
|
|
1166
|
+
const _exhaustive = item.type;
|
|
1167
|
+
diagnostics.push({
|
|
1168
|
+
level: "error",
|
|
1169
|
+
message: `line ${item.line}: Unhandled lexer item type: ${_exhaustive}.`,
|
|
1170
|
+
file: filename,
|
|
1171
|
+
line: item.line
|
|
1172
|
+
});
|
|
1173
|
+
return { passages, diagnostics };
|
|
1174
|
+
}
|
|
1119
1175
|
}
|
|
1120
1176
|
lastType = item.type;
|
|
1121
1177
|
}
|
|
@@ -1126,15 +1182,6 @@ function parseTwee(source, options = {}) {
|
|
|
1126
1182
|
}
|
|
1127
1183
|
|
|
1128
1184
|
// src/loader.ts
|
|
1129
|
-
function readUTF82(filename) {
|
|
1130
|
-
let content = readFileSync2(filename, "utf-8");
|
|
1131
|
-
if (content.charCodeAt(0) === 65279) content = content.slice(1);
|
|
1132
|
-
content = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1133
|
-
return content;
|
|
1134
|
-
}
|
|
1135
|
-
function readBase64(filename) {
|
|
1136
|
-
return readFileSync2(filename).toString("base64");
|
|
1137
|
-
}
|
|
1138
1185
|
function loadSources(story, filenames, opts, diagnostics, processedFiles) {
|
|
1139
1186
|
for (const filename of filenames) {
|
|
1140
1187
|
if (processedFiles.has(filename)) {
|
|
@@ -1233,7 +1280,7 @@ function loadInlineSources(story, sources, opts, diagnostics) {
|
|
|
1233
1280
|
}
|
|
1234
1281
|
}
|
|
1235
1282
|
function loadTwee(story, filename, opts, diagnostics) {
|
|
1236
|
-
const source =
|
|
1283
|
+
const source = readUTF8(filename);
|
|
1237
1284
|
const result = parseTwee(source, {
|
|
1238
1285
|
filename,
|
|
1239
1286
|
trim: opts.trim ?? true,
|
|
@@ -1245,12 +1292,12 @@ function loadTwee(story, filename, opts, diagnostics) {
|
|
|
1245
1292
|
}
|
|
1246
1293
|
}
|
|
1247
1294
|
function loadTagged(story, tag, filename, diagnostics) {
|
|
1248
|
-
const source =
|
|
1295
|
+
const source = readUTF8(filename);
|
|
1249
1296
|
storyAdd(story, { name: basename(filename), tags: [tag], text: source }, diagnostics);
|
|
1250
1297
|
}
|
|
1251
1298
|
function loadMedia(story, tag, filename, diagnostics) {
|
|
1252
1299
|
const source = readBase64(filename);
|
|
1253
|
-
const name =
|
|
1300
|
+
const name = baseNameWithoutExt(filename);
|
|
1254
1301
|
storyAdd(
|
|
1255
1302
|
story,
|
|
1256
1303
|
{
|
|
@@ -1264,7 +1311,7 @@ function loadMedia(story, tag, filename, diagnostics) {
|
|
|
1264
1311
|
function loadFont(story, filename, diagnostics) {
|
|
1265
1312
|
const source = readBase64(filename);
|
|
1266
1313
|
const name = basename(filename);
|
|
1267
|
-
const family =
|
|
1314
|
+
const family = baseNameWithoutExt(filename);
|
|
1268
1315
|
const ext = normalizedFileExt(filename);
|
|
1269
1316
|
const mediaType = mediaTypeFromExt(ext);
|
|
1270
1317
|
const hint = fontFormatHint(ext);
|
|
@@ -1282,9 +1329,11 @@ function loadFont(story, filename, diagnostics) {
|
|
|
1282
1329
|
);
|
|
1283
1330
|
}
|
|
1284
1331
|
|
|
1332
|
+
// src/version.ts
|
|
1333
|
+
var VERSION = "1.2.0";
|
|
1334
|
+
|
|
1285
1335
|
// src/output-twine2.ts
|
|
1286
1336
|
var CREATOR_NAME = "Twee-ts";
|
|
1287
|
-
var CREATOR_VERSION = "0.1.0";
|
|
1288
1337
|
function toTwine2Archive(story, startName, diagnostics) {
|
|
1289
1338
|
return getTwine2DataChunk(story, startName, diagnostics) + "\n";
|
|
1290
1339
|
}
|
|
@@ -1312,12 +1361,12 @@ function getTwine2DataChunk(story, startName, diagnostics) {
|
|
|
1312
1361
|
}
|
|
1313
1362
|
let styleContent = "";
|
|
1314
1363
|
if (stylesheets.length === 1) {
|
|
1315
|
-
styleContent = stylesheets[0]
|
|
1364
|
+
styleContent = stylesheets[0]?.text ?? "";
|
|
1316
1365
|
} else if (stylesheets.length > 1) {
|
|
1317
1366
|
let pidS = 1;
|
|
1318
1367
|
for (const p of stylesheets) {
|
|
1319
1368
|
if (pidS > 1 && !styleContent.endsWith("\n")) styleContent += "\n";
|
|
1320
|
-
styleContent += `/* twine-user-stylesheet #${pidS}: "${p.name}" */
|
|
1369
|
+
styleContent += `/* twine-user-stylesheet #${pidS}: "${commentSanitize(p.name)}" */
|
|
1321
1370
|
`;
|
|
1322
1371
|
styleContent += p.text;
|
|
1323
1372
|
pidS++;
|
|
@@ -1326,12 +1375,12 @@ function getTwine2DataChunk(story, startName, diagnostics) {
|
|
|
1326
1375
|
parts.push(`<style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">${styleContent}</style>`);
|
|
1327
1376
|
let scriptContent = "";
|
|
1328
1377
|
if (scripts.length === 1) {
|
|
1329
|
-
scriptContent = scripts[0]
|
|
1378
|
+
scriptContent = scripts[0]?.text ?? "";
|
|
1330
1379
|
} else if (scripts.length > 1) {
|
|
1331
1380
|
let pidS = 1;
|
|
1332
1381
|
for (const p of scripts) {
|
|
1333
1382
|
if (pidS > 1 && !scriptContent.endsWith("\n")) scriptContent += "\n";
|
|
1334
|
-
scriptContent += `/* twine-user-script #${pidS}: "${p.name}" */
|
|
1383
|
+
scriptContent += `/* twine-user-script #${pidS}: "${commentSanitize(p.name)}" */
|
|
1335
1384
|
`;
|
|
1336
1385
|
scriptContent += p.text;
|
|
1337
1386
|
pidS++;
|
|
@@ -1361,7 +1410,7 @@ function getTwine2DataChunk(story, startName, diagnostics) {
|
|
|
1361
1410
|
}
|
|
1362
1411
|
const options = opts.join(" ");
|
|
1363
1412
|
const zoom = story.twine2.zoom === Math.floor(story.twine2.zoom) ? story.twine2.zoom.toString() : story.twine2.zoom.toFixed(1);
|
|
1364
|
-
const wrapper = `<!-- UUID://${story.ifid}// --><tw-storydata name="${attrEscape(story.name)}" startnode="${startID}" creator="${attrEscape(CREATOR_NAME)}" creator-version="${attrEscape(
|
|
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(options)}" hidden>`;
|
|
1365
1414
|
return wrapper + parts.join("") + "</tw-storydata>";
|
|
1366
1415
|
}
|
|
1367
1416
|
function ensureIFID(story, diagnostics) {
|
|
@@ -1382,10 +1431,9 @@ function ensureIFID(story, diagnostics) {
|
|
|
1382
1431
|
}
|
|
1383
1432
|
|
|
1384
1433
|
// src/output-twine1.ts
|
|
1385
|
-
import { readFileSync as
|
|
1434
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1386
1435
|
import { join as join3, dirname } from "path";
|
|
1387
1436
|
var CREATOR_NAME2 = "twee-ts";
|
|
1388
|
-
var CREATOR_VERSION2 = "0.1.0";
|
|
1389
1437
|
function toTwine1Archive(story, _startName) {
|
|
1390
1438
|
const { data, count } = getTwine1PassageChunk(story);
|
|
1391
1439
|
return `<div id="storeArea" data-size="${count}" hidden>${data}</div>
|
|
@@ -1407,26 +1455,27 @@ function toTwine1HTML(story, format, startName, _diagnostics) {
|
|
|
1407
1455
|
template = tryReplaceComponent(template, '"MODERNIZR"', join3(parentDir, "modernizr.js"), true);
|
|
1408
1456
|
}
|
|
1409
1457
|
const displayStart = startName === "Start" ? "" : startName;
|
|
1410
|
-
template = template.replace('"VERSION"', `Compiled with ${CREATOR_NAME2}, ${
|
|
1458
|
+
template = template.replace('"VERSION"', `Compiled with ${CREATOR_NAME2}, ${VERSION}`);
|
|
1411
1459
|
template = template.replace('"TIME"', `Built on ${(/* @__PURE__ */ new Date()).toUTCString()}`);
|
|
1412
|
-
template = template.replace('"START_AT"', `"${displayStart}"`);
|
|
1460
|
+
template = template.replace('"START_AT"', `"${jsStringEscape(displayStart)}"`);
|
|
1413
1461
|
template = template.replace('"STORY_SIZE"', `"${count}"`);
|
|
1414
1462
|
if (template.includes('"STORY"')) {
|
|
1415
1463
|
template = template.replace('"STORY"', data);
|
|
1416
1464
|
} else {
|
|
1417
1465
|
let footer;
|
|
1418
1466
|
try {
|
|
1419
|
-
footer =
|
|
1467
|
+
footer = readFileSync2(join3(formatDir, "footer.html"), "utf-8");
|
|
1420
1468
|
} catch {
|
|
1421
1469
|
footer = "</div>\n</body>\n</html>\n";
|
|
1422
1470
|
}
|
|
1423
1471
|
template += data + footer;
|
|
1424
1472
|
}
|
|
1425
1473
|
if (story.ifid) {
|
|
1474
|
+
const safeIfid = htmlCommentSanitize(story.ifid);
|
|
1426
1475
|
if (template.includes('<div id="store-area"')) {
|
|
1427
|
-
template = template.replace('<div id="store-area"', `<!-- UUID://${
|
|
1476
|
+
template = template.replace('<div id="store-area"', `<!-- UUID://${safeIfid}// --><div id="store-area"`);
|
|
1428
1477
|
} else {
|
|
1429
|
-
template = template.replace('<div id="storeArea"', `<!-- UUID://${
|
|
1478
|
+
template = template.replace('<div id="storeArea"', `<!-- UUID://${safeIfid}// --><div id="storeArea"`);
|
|
1430
1479
|
}
|
|
1431
1480
|
}
|
|
1432
1481
|
return template;
|
|
@@ -1441,13 +1490,18 @@ function getTwine1PassageChunk(story) {
|
|
|
1441
1490
|
}
|
|
1442
1491
|
return { data, count };
|
|
1443
1492
|
}
|
|
1444
|
-
function tryReplaceComponent(template, placeholder, componentPath,
|
|
1493
|
+
function tryReplaceComponent(template, placeholder, componentPath, required) {
|
|
1445
1494
|
if (!template.includes(placeholder)) return template;
|
|
1446
1495
|
try {
|
|
1447
|
-
let content =
|
|
1496
|
+
let content = readFileSync2(componentPath, "utf-8");
|
|
1448
1497
|
if (content.charCodeAt(0) === 65279) content = content.slice(1);
|
|
1449
1498
|
return template.replace(placeholder, content);
|
|
1450
|
-
} catch {
|
|
1499
|
+
} catch (e) {
|
|
1500
|
+
if (required) {
|
|
1501
|
+
throw new Error(
|
|
1502
|
+
`Required format component not found: ${componentPath}: ${e instanceof Error ? e.message : String(e)}`
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1451
1505
|
return template;
|
|
1452
1506
|
}
|
|
1453
1507
|
}
|
|
@@ -1462,16 +1516,6 @@ function toTwee(story, outMode) {
|
|
|
1462
1516
|
}
|
|
1463
1517
|
|
|
1464
1518
|
// src/modules.ts
|
|
1465
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
1466
|
-
import { basename as basename2 } from "path";
|
|
1467
|
-
function readUTF83(filename) {
|
|
1468
|
-
let content = readFileSync4(filename, "utf-8");
|
|
1469
|
-
if (content.charCodeAt(0) === 65279) content = content.slice(1);
|
|
1470
|
-
return content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1471
|
-
}
|
|
1472
|
-
function readBase642(filename) {
|
|
1473
|
-
return readFileSync4(filename).toString("base64");
|
|
1474
|
-
}
|
|
1475
1519
|
function loadModules(filenames) {
|
|
1476
1520
|
const processed = /* @__PURE__ */ new Set();
|
|
1477
1521
|
const headTags = [];
|
|
@@ -1501,16 +1545,16 @@ function loadModules(filenames) {
|
|
|
1501
1545
|
return headTags.join("\n");
|
|
1502
1546
|
}
|
|
1503
1547
|
function loadModuleTagged(tag, filename) {
|
|
1504
|
-
const source =
|
|
1548
|
+
const source = readUTF8(filename).trim();
|
|
1505
1549
|
if (source.length === 0) return null;
|
|
1506
|
-
const family =
|
|
1550
|
+
const family = baseNameWithoutExt(filename);
|
|
1507
1551
|
const idSlug = `${tag}-module-${slugify(family)}`;
|
|
1508
1552
|
const mimeType = tag === "script" ? "text/javascript" : "text/css";
|
|
1509
1553
|
return `<${tag} id="${idSlug}" type="${mimeType}">${source}</${tag}>`;
|
|
1510
1554
|
}
|
|
1511
1555
|
function loadModuleFont(filename) {
|
|
1512
|
-
const source =
|
|
1513
|
-
const family =
|
|
1556
|
+
const source = readBase64(filename);
|
|
1557
|
+
const family = baseNameWithoutExt(filename);
|
|
1514
1558
|
const idSlug = `style-module-${slugify(family)}`;
|
|
1515
1559
|
const ext = normalizedFileExt(filename);
|
|
1516
1560
|
const mediaType = mediaTypeFromExt(ext);
|
|
@@ -1520,7 +1564,7 @@ function loadModuleFont(filename) {
|
|
|
1520
1564
|
src: url("data:${mediaType};base64,${source}") format("${hint}");
|
|
1521
1565
|
}</style>`;
|
|
1522
1566
|
}
|
|
1523
|
-
function modifyHead(html, modulePaths, headFile) {
|
|
1567
|
+
function modifyHead(html, modulePaths, headFile, diagnostics) {
|
|
1524
1568
|
const parts = [];
|
|
1525
1569
|
if (modulePaths.length > 0) {
|
|
1526
1570
|
const modules = loadModules(modulePaths).trim();
|
|
@@ -1528,9 +1572,13 @@ function modifyHead(html, modulePaths, headFile) {
|
|
|
1528
1572
|
}
|
|
1529
1573
|
if (headFile) {
|
|
1530
1574
|
try {
|
|
1531
|
-
const source =
|
|
1575
|
+
const source = readUTF8(headFile).trim();
|
|
1532
1576
|
if (source.length > 0) parts.push(source);
|
|
1533
|
-
} catch {
|
|
1577
|
+
} catch (e) {
|
|
1578
|
+
diagnostics?.push({
|
|
1579
|
+
level: "warning",
|
|
1580
|
+
message: `Failed to read head file "${headFile}": ${e instanceof Error ? e.message : String(e)}`
|
|
1581
|
+
});
|
|
1534
1582
|
}
|
|
1535
1583
|
}
|
|
1536
1584
|
if (parts.length > 0) {
|
|
@@ -1541,7 +1589,7 @@ function modifyHead(html, modulePaths, headFile) {
|
|
|
1541
1589
|
}
|
|
1542
1590
|
|
|
1543
1591
|
// src/remote-formats.ts
|
|
1544
|
-
import { mkdirSync, readFileSync as
|
|
1592
|
+
import { mkdirSync, readFileSync as readFileSync3, writeFileSync, existsSync, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
1545
1593
|
import { join as join4 } from "path";
|
|
1546
1594
|
import { homedir as homedir2 } from "os";
|
|
1547
1595
|
var DEFAULT_SFA_INDICES = [
|
|
@@ -1557,6 +1605,24 @@ var indexCache = /* @__PURE__ */ new Map();
|
|
|
1557
1605
|
function clearIndexCache() {
|
|
1558
1606
|
indexCache.clear();
|
|
1559
1607
|
}
|
|
1608
|
+
function isValidEntry(val) {
|
|
1609
|
+
if (typeof val !== "object" || val === null || Array.isArray(val)) return false;
|
|
1610
|
+
const obj = val;
|
|
1611
|
+
return typeof obj.name === "string" && typeof obj.version === "string" && typeof obj.checksums === "object" && obj.checksums !== null && !Array.isArray(obj.checksums);
|
|
1612
|
+
}
|
|
1613
|
+
function validateSFAIndex(json) {
|
|
1614
|
+
if (typeof json !== "object" || json === null) {
|
|
1615
|
+
throw new Error("SFA index is not an object");
|
|
1616
|
+
}
|
|
1617
|
+
const obj = json;
|
|
1618
|
+
const twine1 = Array.isArray(obj["twine1"]) ? obj["twine1"].filter(isValidEntry) : [];
|
|
1619
|
+
const twine2 = Array.isArray(obj["twine2"]) ? obj["twine2"].filter(isValidEntry) : [];
|
|
1620
|
+
return { twine1, twine2 };
|
|
1621
|
+
}
|
|
1622
|
+
function makeFormatId(name, version) {
|
|
1623
|
+
const major = parseSemver(version)?.[0] ?? "0";
|
|
1624
|
+
return `${name.toLowerCase().replace(/\s+/g, "-")}-${major}`;
|
|
1625
|
+
}
|
|
1560
1626
|
async function fetchIndex(url) {
|
|
1561
1627
|
const cached = indexCache.get(url);
|
|
1562
1628
|
if (cached) return cached;
|
|
@@ -1564,37 +1630,42 @@ async function fetchIndex(url) {
|
|
|
1564
1630
|
if (!res.ok) {
|
|
1565
1631
|
throw new Error(`Failed to fetch format index from ${url}: ${res.status} ${res.statusText}`);
|
|
1566
1632
|
}
|
|
1567
|
-
const data = await res.json();
|
|
1633
|
+
const data = validateSFAIndex(await res.json());
|
|
1568
1634
|
indexCache.set(url, data);
|
|
1569
1635
|
return data;
|
|
1570
1636
|
}
|
|
1571
1637
|
function findEntry(index, name, version) {
|
|
1572
1638
|
const wanted = parseSemver(version);
|
|
1573
|
-
const
|
|
1574
|
-
|
|
1639
|
+
const searchArrays = [
|
|
1640
|
+
{ entries: index.twine2 ?? [], formatType: "twine2" },
|
|
1641
|
+
{ entries: index.twine1 ?? [], formatType: "twine1" }
|
|
1642
|
+
];
|
|
1643
|
+
let bestResult;
|
|
1575
1644
|
let bestVersion = null;
|
|
1576
|
-
for (const
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
if (
|
|
1583
|
-
bestVersion
|
|
1584
|
-
|
|
1645
|
+
for (const { entries, formatType } of searchArrays) {
|
|
1646
|
+
for (const entry of entries) {
|
|
1647
|
+
if (entry.name.toLowerCase() !== name.toLowerCase()) continue;
|
|
1648
|
+
const have = parseSemver(entry.version);
|
|
1649
|
+
if (!have) continue;
|
|
1650
|
+
if (wanted && semverCompare(have, wanted) === 0) return { entry, formatType };
|
|
1651
|
+
if (wanted === null || have[0] === wanted[0] && semverCompare(have, wanted) >= 0) {
|
|
1652
|
+
if (!bestVersion || semverCompare(have, bestVersion) > 0) {
|
|
1653
|
+
bestVersion = have;
|
|
1654
|
+
bestResult = { entry, formatType };
|
|
1655
|
+
}
|
|
1585
1656
|
}
|
|
1586
1657
|
}
|
|
1587
1658
|
}
|
|
1588
|
-
return
|
|
1659
|
+
return bestResult;
|
|
1589
1660
|
}
|
|
1590
1661
|
async function verifySHA256(content, expectedHex) {
|
|
1591
1662
|
const digest = await crypto.subtle.digest("SHA-256", content);
|
|
1592
1663
|
const hex = Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1593
1664
|
return hex === expectedHex.toLowerCase();
|
|
1594
1665
|
}
|
|
1595
|
-
function getDownloadUrl(indexUrl, entry) {
|
|
1666
|
+
function getDownloadUrl(indexUrl, entry, formatType) {
|
|
1596
1667
|
const base = indexUrl.replace(/\/index\.json$/, "");
|
|
1597
|
-
return `${base}
|
|
1668
|
+
return `${base}/${formatType}/${entry.name}/${entry.version}/format.js`;
|
|
1598
1669
|
}
|
|
1599
1670
|
async function fetchAndCacheFormat(entry, downloadUrl) {
|
|
1600
1671
|
const res = await fetch(downloadUrl);
|
|
@@ -1605,13 +1676,14 @@ async function fetchAndCacheFormat(entry, downloadUrl) {
|
|
|
1605
1676
|
const checksumKey = Object.keys(entry.checksums ?? {}).find((k) => k.endsWith("format.js"));
|
|
1606
1677
|
if (checksumKey) {
|
|
1607
1678
|
const expected = entry.checksums[checksumKey];
|
|
1679
|
+
if (!expected) throw new Error(`Missing checksum value for key "${checksumKey}"`);
|
|
1608
1680
|
const encoder = new TextEncoder();
|
|
1609
1681
|
const valid = await verifySHA256(encoder.encode(text), expected);
|
|
1610
1682
|
if (!valid) {
|
|
1611
1683
|
throw new Error(`Checksum verification failed for ${entry.name} ${entry.version}`);
|
|
1612
1684
|
}
|
|
1613
1685
|
}
|
|
1614
|
-
const id =
|
|
1686
|
+
const id = makeFormatId(entry.name, entry.version);
|
|
1615
1687
|
const data = parseFormatJSON(text, id);
|
|
1616
1688
|
if (!data) {
|
|
1617
1689
|
throw new Error(`Failed to parse format JSON from ${downloadUrl}`);
|
|
@@ -1641,7 +1713,7 @@ async function fetchDirectFormat(url) {
|
|
|
1641
1713
|
if (!data) {
|
|
1642
1714
|
throw new Error(`Failed to parse format JSON from ${url}`);
|
|
1643
1715
|
}
|
|
1644
|
-
const id =
|
|
1716
|
+
const id = makeFormatId(data.name, data.version);
|
|
1645
1717
|
const cacheDir = getCacheDir();
|
|
1646
1718
|
const formatDir = join4(cacheDir, data.name, data.version);
|
|
1647
1719
|
mkdirSync(formatDir, { recursive: true });
|
|
@@ -1657,6 +1729,7 @@ async function fetchDirectFormat(url) {
|
|
|
1657
1729
|
};
|
|
1658
1730
|
}
|
|
1659
1731
|
async function resolveRemoteFormat(name, version, indices, urls) {
|
|
1732
|
+
let lastError;
|
|
1660
1733
|
if (urls) {
|
|
1661
1734
|
for (const url of urls) {
|
|
1662
1735
|
try {
|
|
@@ -1668,7 +1741,8 @@ async function resolveRemoteFormat(name, version, indices, urls) {
|
|
|
1668
1741
|
return info;
|
|
1669
1742
|
}
|
|
1670
1743
|
}
|
|
1671
|
-
} catch {
|
|
1744
|
+
} catch (e) {
|
|
1745
|
+
lastError = e instanceof Error ? e : new Error(String(e));
|
|
1672
1746
|
}
|
|
1673
1747
|
}
|
|
1674
1748
|
}
|
|
@@ -1676,24 +1750,26 @@ async function resolveRemoteFormat(name, version, indices, urls) {
|
|
|
1676
1750
|
for (const indexUrl of allIndices) {
|
|
1677
1751
|
try {
|
|
1678
1752
|
const index = await fetchIndex(indexUrl);
|
|
1679
|
-
const
|
|
1680
|
-
if (
|
|
1681
|
-
const cached = getCachedFormat(entry.name, entry.version);
|
|
1753
|
+
const result = findEntry(index, name, version);
|
|
1754
|
+
if (result) {
|
|
1755
|
+
const cached = getCachedFormat(result.entry.name, result.entry.version);
|
|
1682
1756
|
if (cached) return cached;
|
|
1683
|
-
const downloadUrl = getDownloadUrl(indexUrl, entry);
|
|
1684
|
-
return await fetchAndCacheFormat(entry, downloadUrl);
|
|
1757
|
+
const downloadUrl = getDownloadUrl(indexUrl, result.entry, result.formatType);
|
|
1758
|
+
return await fetchAndCacheFormat(result.entry, downloadUrl);
|
|
1685
1759
|
}
|
|
1686
|
-
} catch {
|
|
1760
|
+
} catch (e) {
|
|
1761
|
+
lastError = e instanceof Error ? e : new Error(String(e));
|
|
1687
1762
|
}
|
|
1688
1763
|
}
|
|
1764
|
+
if (lastError) throw lastError;
|
|
1689
1765
|
return void 0;
|
|
1690
1766
|
}
|
|
1691
1767
|
function getCachedFormat(name, version) {
|
|
1692
1768
|
const formatPath = join4(getCacheDir(), name, version, "format.js");
|
|
1693
1769
|
try {
|
|
1694
1770
|
if (!existsSync(formatPath)) return void 0;
|
|
1695
|
-
const source =
|
|
1696
|
-
const id =
|
|
1771
|
+
const source = readFileSync3(formatPath, "utf-8");
|
|
1772
|
+
const id = makeFormatId(name, version);
|
|
1697
1773
|
const data = parseFormatJSON(source, id);
|
|
1698
1774
|
if (!data) return void 0;
|
|
1699
1775
|
return {
|
|
@@ -1802,10 +1878,7 @@ async function buildOutput(options) {
|
|
|
1802
1878
|
loadSources(story, sourceFilenames, { trim, twee2Compat }, diagnostics, processedFiles);
|
|
1803
1879
|
loadInlineSources(story, inlineSources, { trim, twee2Compat }, diagnostics);
|
|
1804
1880
|
if (options.tagAliases) {
|
|
1805
|
-
applyTagAliases(story.passages, options.tagAliases);
|
|
1806
|
-
}
|
|
1807
|
-
const fatalErrors = diagnostics.filter((d) => d.level === "error");
|
|
1808
|
-
if (fatalErrors.length > 0 && outputMode === "html") {
|
|
1881
|
+
story.passages = applyTagAliases(story.passages, options.tagAliases);
|
|
1809
1882
|
}
|
|
1810
1883
|
let format;
|
|
1811
1884
|
let formatId = options.formatId ?? "";
|
|
@@ -1824,7 +1897,11 @@ async function buildOutput(options) {
|
|
|
1824
1897
|
const remoteVersion = story.twine2.formatVersion || "";
|
|
1825
1898
|
try {
|
|
1826
1899
|
format = await resolveRemoteFormat(remoteName, remoteVersion, options.formatIndices, options.formatUrls);
|
|
1827
|
-
} catch {
|
|
1900
|
+
} catch (e) {
|
|
1901
|
+
diagnostics.push({
|
|
1902
|
+
level: "warning",
|
|
1903
|
+
message: `Remote format fetch failed for "${remoteName}": ${e instanceof Error ? e.message : String(e)}`
|
|
1904
|
+
});
|
|
1828
1905
|
}
|
|
1829
1906
|
}
|
|
1830
1907
|
if (!format) {
|
|
@@ -1835,14 +1912,7 @@ async function buildOutput(options) {
|
|
|
1835
1912
|
});
|
|
1836
1913
|
}
|
|
1837
1914
|
}
|
|
1838
|
-
|
|
1839
|
-
if (options.startPassage) {
|
|
1840
|
-
startName = options.startPassage;
|
|
1841
|
-
} else if (story.twine2.start) {
|
|
1842
|
-
startName = story.twine2.start;
|
|
1843
|
-
} else {
|
|
1844
|
-
startName = DEFAULT_START_NAME;
|
|
1845
|
-
}
|
|
1915
|
+
const startName = options.startPassage || story.twine2.start || DEFAULT_START_NAME;
|
|
1846
1916
|
if (testMode) {
|
|
1847
1917
|
story.twine2.options.set("debug", true);
|
|
1848
1918
|
}
|
|
@@ -1861,8 +1931,7 @@ async function buildOutput(options) {
|
|
|
1861
1931
|
case "json":
|
|
1862
1932
|
output = storyToJSON(story);
|
|
1863
1933
|
break;
|
|
1864
|
-
case "html":
|
|
1865
|
-
default: {
|
|
1934
|
+
case "html": {
|
|
1866
1935
|
if (!storyHas(story, startName)) {
|
|
1867
1936
|
diagnostics.push({
|
|
1868
1937
|
level: "error",
|
|
@@ -1884,9 +1953,13 @@ async function buildOutput(options) {
|
|
|
1884
1953
|
output = toTwine1HTML(story, format, startName, diagnostics);
|
|
1885
1954
|
}
|
|
1886
1955
|
const modulePaths = options.modules ? getFilenames(options.modules) : [];
|
|
1887
|
-
output = modifyHead(output, modulePaths, options.headFile);
|
|
1956
|
+
output = modifyHead(output, modulePaths, options.headFile, diagnostics);
|
|
1888
1957
|
break;
|
|
1889
1958
|
}
|
|
1959
|
+
default: {
|
|
1960
|
+
const _exhaustive = outputMode;
|
|
1961
|
+
throw new TweeTsError(`Unhandled output mode: ${_exhaustive}`, diagnostics);
|
|
1962
|
+
}
|
|
1890
1963
|
}
|
|
1891
1964
|
const stats = {
|
|
1892
1965
|
...getStoryStats(story),
|
|
@@ -1920,7 +1993,7 @@ function storyToJSON(story) {
|
|
|
1920
1993
|
}
|
|
1921
1994
|
|
|
1922
1995
|
// src/config.ts
|
|
1923
|
-
import { readFileSync as
|
|
1996
|
+
import { readFileSync as readFileSync4, existsSync as existsSync2 } from "fs";
|
|
1924
1997
|
import { join as join5 } from "path";
|
|
1925
1998
|
var CONFIG_FILENAME = "twee-ts.config.json";
|
|
1926
1999
|
var VALID_OUTPUT_MODES = ["html", "twee3", "twee1", "twine2-archive", "twine1-archive", "json"];
|
|
@@ -1930,7 +2003,7 @@ function loadConfig(dir) {
|
|
|
1930
2003
|
if (!existsSync2(configPath)) return null;
|
|
1931
2004
|
let raw;
|
|
1932
2005
|
try {
|
|
1933
|
-
raw =
|
|
2006
|
+
raw = readFileSync4(configPath, "utf-8");
|
|
1934
2007
|
} catch {
|
|
1935
2008
|
return null;
|
|
1936
2009
|
}
|
|
@@ -1950,7 +2023,7 @@ function loadConfig(dir) {
|
|
|
1950
2023
|
function loadConfigFile(filePath) {
|
|
1951
2024
|
let raw;
|
|
1952
2025
|
try {
|
|
1953
|
-
raw =
|
|
2026
|
+
raw = readFileSync4(filePath, "utf-8");
|
|
1954
2027
|
} catch (e) {
|
|
1955
2028
|
throw new Error(`Cannot read config file ${filePath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
1956
2029
|
}
|
|
@@ -2021,7 +2094,6 @@ function scaffoldConfig() {
|
|
|
2021
2094
|
}
|
|
2022
2095
|
|
|
2023
2096
|
// bin/twee-ts.ts
|
|
2024
|
-
var VERSION = "0.1.0";
|
|
2025
2097
|
var { values, positionals } = parseArgs({
|
|
2026
2098
|
allowPositionals: true,
|
|
2027
2099
|
options: {
|
|
@@ -2251,7 +2323,7 @@ Options:
|
|
|
2251
2323
|
-v, --version Show version`);
|
|
2252
2324
|
}
|
|
2253
2325
|
main().catch((err) => {
|
|
2254
|
-
console.error(err.message
|
|
2326
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
2255
2327
|
process.exit(1);
|
|
2256
2328
|
});
|
|
2257
2329
|
//# sourceMappingURL=twee-ts.js.map
|