@rohal12/twee-ts 1.2.0 → 1.4.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/README.md +9 -0
- package/dist/bin/twee-ts.js +157 -41
- package/dist/bin/twee-ts.js.map +1 -1
- package/dist/{chunk-S4SWCQLW.js → chunk-76URFKGD.js} +158 -42
- package/dist/chunk-76URFKGD.js.map +1 -0
- package/dist/index.cjs +157 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +157 -41
- package/dist/index.js.map +1 -1
- package/dist/plugins/rollup.js +1 -1
- package/dist/plugins/vite.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-S4SWCQLW.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -76,6 +76,7 @@ interface CompileStats {
|
|
|
76
76
|
interface PassageMetadata {
|
|
77
77
|
position?: string;
|
|
78
78
|
size?: string;
|
|
79
|
+
[key: string]: string | undefined;
|
|
79
80
|
}
|
|
80
81
|
interface Passage {
|
|
81
82
|
name: string;
|
|
@@ -91,6 +92,7 @@ interface Twine2Metadata {
|
|
|
91
92
|
formatVersion: string;
|
|
92
93
|
options: Map<string, boolean>;
|
|
93
94
|
start: string;
|
|
95
|
+
tags: string;
|
|
94
96
|
tagColors: Map<string, string>;
|
|
95
97
|
zoom: number;
|
|
96
98
|
}
|
|
@@ -109,12 +111,22 @@ interface StoryFormatInfo {
|
|
|
109
111
|
name: string;
|
|
110
112
|
version: string;
|
|
111
113
|
proofing: boolean;
|
|
114
|
+
author?: string;
|
|
115
|
+
description?: string;
|
|
116
|
+
image?: string;
|
|
117
|
+
url?: string;
|
|
118
|
+
license?: string;
|
|
112
119
|
}
|
|
113
120
|
interface Twine2FormatJSON {
|
|
114
121
|
name: string;
|
|
115
122
|
version: string;
|
|
116
123
|
proofing?: boolean;
|
|
117
124
|
source: string;
|
|
125
|
+
author?: string;
|
|
126
|
+
description?: string;
|
|
127
|
+
image?: string;
|
|
128
|
+
url?: string;
|
|
129
|
+
license?: string;
|
|
118
130
|
}
|
|
119
131
|
declare const enum ItemType {
|
|
120
132
|
Error = 0,
|
|
@@ -301,10 +313,13 @@ declare function parseTwee(source: string, options?: ParseOptions): ParseResult;
|
|
|
301
313
|
/**
|
|
302
314
|
* Parse the Twine 2 format.js JSON chunk.
|
|
303
315
|
* Handles Harlowe's malformed JSON by stripping the "setup" function property.
|
|
316
|
+
* Per spec, the name key is Optional.
|
|
304
317
|
*/
|
|
305
318
|
declare function parseFormatJSON(source: string, formatId: string): Twine2FormatJSON | null;
|
|
306
319
|
/**
|
|
307
320
|
* Discover all story formats in the given search directories.
|
|
321
|
+
* Implements SemVer-based version pruning: within each (name, major) group,
|
|
322
|
+
* only the highest minor.patch survives.
|
|
308
323
|
*/
|
|
309
324
|
declare function discoverFormats(searchDirs: string[]): Map<string, StoryFormatInfo>;
|
|
310
325
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -76,6 +76,7 @@ interface CompileStats {
|
|
|
76
76
|
interface PassageMetadata {
|
|
77
77
|
position?: string;
|
|
78
78
|
size?: string;
|
|
79
|
+
[key: string]: string | undefined;
|
|
79
80
|
}
|
|
80
81
|
interface Passage {
|
|
81
82
|
name: string;
|
|
@@ -91,6 +92,7 @@ interface Twine2Metadata {
|
|
|
91
92
|
formatVersion: string;
|
|
92
93
|
options: Map<string, boolean>;
|
|
93
94
|
start: string;
|
|
95
|
+
tags: string;
|
|
94
96
|
tagColors: Map<string, string>;
|
|
95
97
|
zoom: number;
|
|
96
98
|
}
|
|
@@ -109,12 +111,22 @@ interface StoryFormatInfo {
|
|
|
109
111
|
name: string;
|
|
110
112
|
version: string;
|
|
111
113
|
proofing: boolean;
|
|
114
|
+
author?: string;
|
|
115
|
+
description?: string;
|
|
116
|
+
image?: string;
|
|
117
|
+
url?: string;
|
|
118
|
+
license?: string;
|
|
112
119
|
}
|
|
113
120
|
interface Twine2FormatJSON {
|
|
114
121
|
name: string;
|
|
115
122
|
version: string;
|
|
116
123
|
proofing?: boolean;
|
|
117
124
|
source: string;
|
|
125
|
+
author?: string;
|
|
126
|
+
description?: string;
|
|
127
|
+
image?: string;
|
|
128
|
+
url?: string;
|
|
129
|
+
license?: string;
|
|
118
130
|
}
|
|
119
131
|
declare const enum ItemType {
|
|
120
132
|
Error = 0,
|
|
@@ -301,10 +313,13 @@ declare function parseTwee(source: string, options?: ParseOptions): ParseResult;
|
|
|
301
313
|
/**
|
|
302
314
|
* Parse the Twine 2 format.js JSON chunk.
|
|
303
315
|
* Handles Harlowe's malformed JSON by stripping the "setup" function property.
|
|
316
|
+
* Per spec, the name key is Optional.
|
|
304
317
|
*/
|
|
305
318
|
declare function parseFormatJSON(source: string, formatId: string): Twine2FormatJSON | null;
|
|
306
319
|
/**
|
|
307
320
|
* Discover all story formats in the given search directories.
|
|
321
|
+
* Implements SemVer-based version pruning: within each (name, major) group,
|
|
322
|
+
* only the highest minor.patch survives.
|
|
308
323
|
*/
|
|
309
324
|
declare function discoverFormats(searchDirs: string[]): Map<string, StoryFormatInfo>;
|
|
310
325
|
/**
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,10 @@ function attrEscape(s) {
|
|
|
57
57
|
if (s.length === 0) return s;
|
|
58
58
|
return s.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'");
|
|
59
59
|
}
|
|
60
|
+
function fullAttrEscape(s) {
|
|
61
|
+
if (s.length === 0) return s;
|
|
62
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
63
|
+
}
|
|
60
64
|
function htmlEscape(s) {
|
|
61
65
|
if (s.length === 0) return s;
|
|
62
66
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -79,6 +83,13 @@ function commentSanitize(s) {
|
|
|
79
83
|
function htmlCommentSanitize(s) {
|
|
80
84
|
return s.replace(/-->/g, "-- >");
|
|
81
85
|
}
|
|
86
|
+
function rot13(s) {
|
|
87
|
+
if (s.length === 0) return s;
|
|
88
|
+
return s.replace(/[A-Za-z]/g, (ch) => {
|
|
89
|
+
const base = ch <= "Z" ? 65 : 97;
|
|
90
|
+
return String.fromCharCode((ch.charCodeAt(0) - base + 13) % 26 + base);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
82
93
|
function tweeUnescape(s) {
|
|
83
94
|
if (s.length === 0) return s;
|
|
84
95
|
let result = "";
|
|
@@ -146,12 +157,14 @@ function hasMetadataSize(p) {
|
|
|
146
157
|
return p.metadata != null && p.metadata.size != null && p.metadata.size !== "";
|
|
147
158
|
}
|
|
148
159
|
function hasAnyMetadata(p) {
|
|
149
|
-
|
|
160
|
+
if (!p.metadata) return false;
|
|
161
|
+
return Object.values(p.metadata).some((v) => v != null && v !== "");
|
|
150
162
|
}
|
|
151
163
|
function marshalMetadata(meta) {
|
|
152
164
|
const obj = {};
|
|
153
|
-
|
|
154
|
-
|
|
165
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
166
|
+
if (typeof value === "string" && value) obj[key] = value;
|
|
167
|
+
}
|
|
155
168
|
return JSON.stringify(obj);
|
|
156
169
|
}
|
|
157
170
|
function passageToTwee(p, outMode) {
|
|
@@ -194,9 +207,9 @@ function passageToPassagedata(p, pid) {
|
|
|
194
207
|
} else {
|
|
195
208
|
size = "100,100";
|
|
196
209
|
}
|
|
197
|
-
return `<tw-passagedata pid="${pid}" name=${quote(
|
|
210
|
+
return `<tw-passagedata pid="${pid}" name=${quote(fullAttrEscape(p.name))} tags=${quote(attrEscape(p.tags.join(" ")))} position=${quote(attrEscape(position))} size=${quote(attrEscape(size))}>${htmlEscape(p.text)}</tw-passagedata>`;
|
|
198
211
|
}
|
|
199
|
-
function passageToTiddler(p, pid) {
|
|
212
|
+
function passageToTiddler(p, pid, obfuscateRot13 = false) {
|
|
200
213
|
let position;
|
|
201
214
|
if (hasMetadataPosition(p)) {
|
|
202
215
|
position = p.metadata?.position ?? "";
|
|
@@ -207,7 +220,10 @@ function passageToTiddler(p, pid) {
|
|
|
207
220
|
const yp = x === 0 ? y : y + 1;
|
|
208
221
|
position = `${xp * 140 - 130},${yp * 140 - 130}`;
|
|
209
222
|
}
|
|
210
|
-
|
|
223
|
+
const created = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 12);
|
|
224
|
+
const content = obfuscateRot13 && p.name !== "StorySettings" ? tiddlerEscape(rot13(p.text)) : tiddlerEscape(p.text);
|
|
225
|
+
const nameComment = obfuscateRot13 ? `<!-- ${commentSanitize(p.name)} -->` : "";
|
|
226
|
+
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>`;
|
|
211
227
|
}
|
|
212
228
|
function countWords(p) {
|
|
213
229
|
let text = p.text;
|
|
@@ -250,6 +266,7 @@ function createStory() {
|
|
|
250
266
|
formatVersion: "",
|
|
251
267
|
options: /* @__PURE__ */ new Map(),
|
|
252
268
|
start: "",
|
|
269
|
+
tags: "",
|
|
253
270
|
tagColors: /* @__PURE__ */ new Map(),
|
|
254
271
|
zoom: 1
|
|
255
272
|
}
|
|
@@ -296,6 +313,7 @@ function marshalStoryData(story) {
|
|
|
296
313
|
}
|
|
297
314
|
if (options.length > 0) data.options = options;
|
|
298
315
|
if (story.twine2.start) data.start = story.twine2.start;
|
|
316
|
+
if (story.twine2.tags) data.tags = story.twine2.tags;
|
|
299
317
|
if (story.twine2.tagColors.size > 0) {
|
|
300
318
|
data["tag-colors"] = Object.fromEntries(story.twine2.tagColors);
|
|
301
319
|
}
|
|
@@ -323,6 +341,7 @@ function unmarshalStoryData(story, json) {
|
|
|
323
341
|
}
|
|
324
342
|
}
|
|
325
343
|
if (typeof data.start === "string" && data.start) story.twine2.start = data.start;
|
|
344
|
+
if (typeof data.tags === "string") story.twine2.tags = data.tags;
|
|
326
345
|
if (typeof data["tag-colors"] === "object" && data["tag-colors"] !== null && !Array.isArray(data["tag-colors"])) {
|
|
327
346
|
for (const [tag, color] of Object.entries(data["tag-colors"])) {
|
|
328
347
|
if (typeof color === "string") story.twine2.tagColors.set(tag, color);
|
|
@@ -394,7 +413,7 @@ function storyAdd(story, p, diagnostics) {
|
|
|
394
413
|
processed = { ...p, text: marshalStoryData(story) };
|
|
395
414
|
} else {
|
|
396
415
|
diagnostics.push({
|
|
397
|
-
level: "
|
|
416
|
+
level: "warning",
|
|
398
417
|
message: `Cannot unmarshal "StoryData" compiler special passage; ${err}.`
|
|
399
418
|
});
|
|
400
419
|
}
|
|
@@ -617,17 +636,41 @@ function baseNameWithoutExt(filename) {
|
|
|
617
636
|
}
|
|
618
637
|
|
|
619
638
|
// src/formats.ts
|
|
639
|
+
function relaxedJSONParse(json) {
|
|
640
|
+
try {
|
|
641
|
+
return JSON.parse(json);
|
|
642
|
+
} catch {
|
|
643
|
+
}
|
|
644
|
+
let fixed = json;
|
|
645
|
+
fixed = fixed.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
|
|
646
|
+
fixed = fixed.replace(/(?<=^|[{,]\s*)([A-Za-z_$][A-Za-z0-9_$]*)\s*:/gm, '"$1":');
|
|
647
|
+
fixed = fixed.replace(/,\s*([}\]])/g, "$1");
|
|
648
|
+
return JSON.parse(fixed);
|
|
649
|
+
}
|
|
620
650
|
function parseFormatJSON(source, formatId) {
|
|
621
651
|
const first = source.indexOf("{");
|
|
622
652
|
const last = source.lastIndexOf("}");
|
|
623
653
|
if (first === -1 || last === -1) return null;
|
|
624
654
|
let chunk = source.slice(first, last + 1);
|
|
625
655
|
const parse = (json) => {
|
|
626
|
-
const raw =
|
|
656
|
+
const raw = relaxedJSONParse(json);
|
|
627
657
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
628
658
|
const obj = raw;
|
|
629
|
-
if (typeof obj.
|
|
630
|
-
|
|
659
|
+
if (typeof obj.version !== "string" || typeof obj.source !== "string") return null;
|
|
660
|
+
if (!parseSemver(obj.version)) return null;
|
|
661
|
+
const name = typeof obj.name === "string" ? obj.name : "Untitled Story Format";
|
|
662
|
+
const result = {
|
|
663
|
+
name,
|
|
664
|
+
version: obj.version,
|
|
665
|
+
source: obj.source,
|
|
666
|
+
proofing: obj.proofing === true
|
|
667
|
+
};
|
|
668
|
+
if (typeof obj.author === "string") result.author = obj.author;
|
|
669
|
+
if (typeof obj.description === "string") result.description = obj.description;
|
|
670
|
+
if (typeof obj.image === "string") result.image = obj.image;
|
|
671
|
+
if (typeof obj.url === "string") result.url = obj.url;
|
|
672
|
+
if (typeof obj.license === "string") result.license = obj.license;
|
|
673
|
+
return result;
|
|
631
674
|
};
|
|
632
675
|
try {
|
|
633
676
|
return parse(chunk);
|
|
@@ -693,17 +736,55 @@ function discoverFormats(searchDirs) {
|
|
|
693
736
|
format.name = data.name;
|
|
694
737
|
format.version = data.version;
|
|
695
738
|
format.proofing = data.proofing ?? false;
|
|
739
|
+
if (data.author) format.author = data.author;
|
|
740
|
+
if (data.description) format.description = data.description;
|
|
741
|
+
if (data.image) format.image = data.image;
|
|
742
|
+
if (data.url) format.url = data.url;
|
|
743
|
+
if (data.license) format.license = data.license;
|
|
696
744
|
} catch {
|
|
697
745
|
continue;
|
|
698
746
|
}
|
|
747
|
+
} else {
|
|
748
|
+
format.name = entry;
|
|
699
749
|
}
|
|
700
750
|
formats.set(entry, format);
|
|
701
751
|
break;
|
|
702
752
|
}
|
|
703
753
|
}
|
|
704
754
|
}
|
|
755
|
+
pruneFormats(formats);
|
|
705
756
|
return formats;
|
|
706
757
|
}
|
|
758
|
+
function pruneFormats(formats) {
|
|
759
|
+
const bestByNameMajor = /* @__PURE__ */ new Map();
|
|
760
|
+
const seenNameVersion = /* @__PURE__ */ new Set();
|
|
761
|
+
for (const [id, f] of formats) {
|
|
762
|
+
if (!f.isTwine2 || !f.name || !f.version) continue;
|
|
763
|
+
const v = parseSemver(f.version);
|
|
764
|
+
if (!v) continue;
|
|
765
|
+
const nameVersionKey = `${f.name}@${f.version}`;
|
|
766
|
+
if (seenNameVersion.has(nameVersionKey)) {
|
|
767
|
+
formats.delete(id);
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
seenNameVersion.add(nameVersionKey);
|
|
771
|
+
const groupKey = `${f.name}@${v[0]}`;
|
|
772
|
+
const existing = bestByNameMajor.get(groupKey);
|
|
773
|
+
if (!existing || semverCompare(v, existing.version) > 0) {
|
|
774
|
+
bestByNameMajor.set(groupKey, { id, version: v });
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
for (const [id, f] of [...formats]) {
|
|
778
|
+
if (!f.isTwine2 || !f.name || !f.version) continue;
|
|
779
|
+
const v = parseSemver(f.version);
|
|
780
|
+
if (!v) continue;
|
|
781
|
+
const groupKey = `${f.name}@${v[0]}`;
|
|
782
|
+
const best = bestByNameMajor.get(groupKey);
|
|
783
|
+
if (best && best.id !== id) {
|
|
784
|
+
formats.delete(id);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
707
788
|
function getFormatSearchDirs(extraPaths = [], useTweegoPath = true) {
|
|
708
789
|
const subdirNames = ["storyformats", ".storyformats", "story-formats", "storyFormats", "targets"];
|
|
709
790
|
const basePaths = /* @__PURE__ */ new Set();
|
|
@@ -1156,8 +1237,11 @@ function parseTwee(source, options = {}) {
|
|
|
1156
1237
|
}
|
|
1157
1238
|
const parsed = raw;
|
|
1158
1239
|
const meta = {};
|
|
1159
|
-
|
|
1160
|
-
|
|
1240
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
1241
|
+
if (typeof value === "string") {
|
|
1242
|
+
meta[key] = value;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1161
1245
|
current.metadata = meta;
|
|
1162
1246
|
} catch (e) {
|
|
1163
1247
|
diagnostics.push({
|
|
@@ -1171,7 +1255,11 @@ function parseTwee(source, options = {}) {
|
|
|
1171
1255
|
}
|
|
1172
1256
|
case 6 /* Content */: {
|
|
1173
1257
|
if (!current) break;
|
|
1174
|
-
|
|
1258
|
+
if (trim) {
|
|
1259
|
+
current.text = item.val.trim();
|
|
1260
|
+
} else {
|
|
1261
|
+
current.text = item.val.replace(/\n\s*$/, "");
|
|
1262
|
+
}
|
|
1175
1263
|
break;
|
|
1176
1264
|
}
|
|
1177
1265
|
default: {
|
|
@@ -1399,8 +1487,12 @@ function getTwine2DataChunk(story, startName, diagnostics) {
|
|
|
1399
1487
|
}
|
|
1400
1488
|
}
|
|
1401
1489
|
parts.push(`<script role="script" id="twine-user-script" type="text/twine-javascript">${scriptContent}</script>`);
|
|
1490
|
+
const validTagColors = /* @__PURE__ */ new Set(["gray", "red", "orange", "yellow", "green", "blue", "purple"]);
|
|
1491
|
+
const hexColorPattern = /^#[0-9a-fA-F]{3,8}$/;
|
|
1402
1492
|
for (const [tag, color] of story.twine2.tagColors) {
|
|
1403
|
-
|
|
1493
|
+
if (validTagColors.has(color) || hexColorPattern.test(color)) {
|
|
1494
|
+
parts.push(`<tw-tag name="${attrEscape(tag)}" color="${attrEscape(color)}"></tw-tag>`);
|
|
1495
|
+
}
|
|
1404
1496
|
}
|
|
1405
1497
|
pid = 1;
|
|
1406
1498
|
for (const p of story.passages) {
|
|
@@ -1421,23 +1513,24 @@ function getTwine2DataChunk(story, startName, diagnostics) {
|
|
|
1421
1513
|
if (val) opts.push(opt);
|
|
1422
1514
|
}
|
|
1423
1515
|
const options = opts.join(" ");
|
|
1424
|
-
const zoom =
|
|
1425
|
-
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>`;
|
|
1516
|
+
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(options)}" tags="${attrEscape(story.twine2.tags)}" hidden>`;
|
|
1426
1518
|
return wrapper + parts.join("") + "</tw-storydata>";
|
|
1427
1519
|
}
|
|
1428
1520
|
function ensureIFID(story, diagnostics) {
|
|
1429
1521
|
if (story.ifid !== "") return;
|
|
1430
1522
|
if (story.legacyIFID !== "") {
|
|
1523
|
+
story.ifid = story.legacyIFID;
|
|
1431
1524
|
diagnostics.push({
|
|
1432
1525
|
level: "warning",
|
|
1433
1526
|
message: 'Story IFID not found; reusing "ifid" entry from the "StorySettings" special passage.'
|
|
1434
1527
|
});
|
|
1435
|
-
story.ifid = story.legacyIFID;
|
|
1436
1528
|
} else {
|
|
1437
|
-
|
|
1529
|
+
const ifid = generateIFID();
|
|
1530
|
+
story.ifid = ifid;
|
|
1438
1531
|
diagnostics.push({
|
|
1439
|
-
level: "
|
|
1440
|
-
message: `Story IFID not found
|
|
1532
|
+
level: "error",
|
|
1533
|
+
message: `Story IFID not found. Add an IFID to your story: {"ifid":"${ifid}"}`
|
|
1441
1534
|
});
|
|
1442
1535
|
}
|
|
1443
1536
|
}
|
|
@@ -1448,7 +1541,7 @@ import { join as join3, dirname } from "path";
|
|
|
1448
1541
|
var CREATOR_NAME2 = "twee-ts";
|
|
1449
1542
|
function toTwine1Archive(story, _startName) {
|
|
1450
1543
|
const { data, count } = getTwine1PassageChunk(story);
|
|
1451
|
-
return `<div id="storeArea" data-size="${count}"
|
|
1544
|
+
return `<div id="storeArea" data-size="${count}">${data}</div>
|
|
1452
1545
|
`;
|
|
1453
1546
|
}
|
|
1454
1547
|
function toTwine1HTML(story, format, startName, _diagnostics) {
|
|
@@ -1493,12 +1586,13 @@ function toTwine1HTML(story, format, startName, _diagnostics) {
|
|
|
1493
1586
|
return template;
|
|
1494
1587
|
}
|
|
1495
1588
|
function getTwine1PassageChunk(story) {
|
|
1589
|
+
const obfuscateRot13 = story.twine1.settings.get("obfuscate") === "rot13";
|
|
1496
1590
|
let data = "";
|
|
1497
1591
|
let count = 0;
|
|
1498
1592
|
for (const p of story.passages) {
|
|
1499
1593
|
if (hasTag(p, "Twine.private")) continue;
|
|
1500
1594
|
count++;
|
|
1501
|
-
data += passageToTiddler(p, count);
|
|
1595
|
+
data += passageToTiddler(p, count, obfuscateRot13);
|
|
1502
1596
|
}
|
|
1503
1597
|
return { data, count };
|
|
1504
1598
|
}
|
|
@@ -1834,6 +1928,7 @@ function discoverCachedFormats() {
|
|
|
1834
1928
|
}
|
|
1835
1929
|
|
|
1836
1930
|
// src/compiler.ts
|
|
1931
|
+
var CREATOR_NAME3 = "Twee-ts";
|
|
1837
1932
|
var DEFAULT_FORMAT_ID = "sugarcube-2";
|
|
1838
1933
|
var DEFAULT_START_NAME = "Start";
|
|
1839
1934
|
var TweeTsError = class extends Error {
|
|
@@ -1980,28 +2075,49 @@ async function buildOutput(options) {
|
|
|
1980
2075
|
return { output, story, format, diagnostics, stats };
|
|
1981
2076
|
}
|
|
1982
2077
|
function storyToJSON(story) {
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
2078
|
+
const scripts = [];
|
|
2079
|
+
const stylesheets = [];
|
|
2080
|
+
const storyPassages = [];
|
|
2081
|
+
for (const p of story.passages) {
|
|
2082
|
+
if (p.name === "StoryTitle" || p.name === "StoryData") continue;
|
|
2083
|
+
if (hasTag(p, "Twine.private")) continue;
|
|
2084
|
+
if (hasTag(p, "script")) {
|
|
2085
|
+
scripts.push(p.text);
|
|
2086
|
+
} else if (hasTag(p, "stylesheet")) {
|
|
2087
|
+
stylesheets.push(p.text);
|
|
2088
|
+
} else {
|
|
2089
|
+
const entry = {
|
|
1988
2090
|
name: p.name,
|
|
1989
2091
|
tags: p.tags,
|
|
1990
|
-
text: p.text
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2092
|
+
text: p.text
|
|
2093
|
+
};
|
|
2094
|
+
if (p.metadata) {
|
|
2095
|
+
const meta = {};
|
|
2096
|
+
for (const [key, value] of Object.entries(p.metadata)) {
|
|
2097
|
+
if (typeof value === "string") meta[key] = value;
|
|
2098
|
+
}
|
|
2099
|
+
if (Object.keys(meta).length > 0) {
|
|
2100
|
+
entry.metadata = meta;
|
|
2101
|
+
}
|
|
2000
2102
|
}
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2103
|
+
storyPassages.push(entry);
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
const obj = {
|
|
2107
|
+
name: story.name
|
|
2108
|
+
};
|
|
2109
|
+
if (story.ifid) obj.ifid = story.ifid;
|
|
2110
|
+
if (story.twine2.format) obj.format = story.twine2.format;
|
|
2111
|
+
if (story.twine2.formatVersion) obj["format-version"] = story.twine2.formatVersion;
|
|
2112
|
+
if (story.twine2.start) obj.start = story.twine2.start;
|
|
2113
|
+
if (story.twine2.tagColors.size > 0) obj["tag-colors"] = Object.fromEntries(story.twine2.tagColors);
|
|
2114
|
+
if (story.twine2.zoom !== 1) obj.zoom = story.twine2.zoom;
|
|
2115
|
+
obj.creator = CREATOR_NAME3;
|
|
2116
|
+
obj["creator-version"] = VERSION;
|
|
2117
|
+
obj.style = stylesheets.join("\n");
|
|
2118
|
+
obj.script = scripts.join("\n");
|
|
2119
|
+
obj.passages = storyPassages;
|
|
2120
|
+
return JSON.stringify(obj, null, 2);
|
|
2005
2121
|
}
|
|
2006
2122
|
|
|
2007
2123
|
// src/inspect.ts
|