extwee 2.2.0 → 2.2.1
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/build/extwee +0 -0
- package/build/extwee.web.min.js +1 -1
- package/docs/_sidebar.md +1 -0
- package/docs/examples/dynamicPassages.md +1 -1
- package/docs/examples/twsToTwee.md +1 -1
- package/index.js +1 -1
- package/package.json +15 -12
- package/src/JSON/parse.js +1 -1
- package/src/Passage.js +9 -28
- package/src/Story.js +9 -6
- package/src/TWS/parse.js +1 -1
- package/src/Twee/parse.js +1 -1
- package/src/Twine1HTML/compile.js +1 -1
- package/src/Twine1HTML/parse.js +1 -1
- package/src/Twine2ArchiveHTML/compile.js +1 -1
- package/src/Twine2HTML/compile.js +1 -1
- package/src/Twine2HTML/parse.js +5 -4
- package/test/JSON/JSON.Parse.test.js +20 -20
- package/test/Passage.test.js +69 -0
- package/test/Story.test.js +42 -3
- package/test/Twine1HTML/Twine1HTML.Compile.test.js +1 -1
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Compile.test.js +1 -1
- package/test/Twine2HTML/Twine2HTML.Compile.test.js +1 -1
- package/test/Twine2HTML/Twine2HTML.Parse.test.js +6 -5
- package/test/Twine2HTML/Twine2HTMLParser/unescaping.html +33 -0
- package/tsconfig.json +21 -0
- package/types/index.d.ts +14 -0
- package/types/src/JSON/parse.d.ts +8 -0
- package/types/src/Passage.d.ts +72 -0
- package/types/src/Story.d.ts +161 -0
- package/types/src/StoryFormat/parse.d.ts +7 -0
- package/types/src/StoryFormat.d.ts +97 -0
- package/types/src/TWS/parse.d.ts +10 -0
- package/types/src/Twee/parse.d.ts +10 -0
- package/types/src/Twine1HTML/compile.d.ts +17 -0
- package/types/src/Twine1HTML/parse.d.ts +10 -0
- package/types/src/Twine2ArchiveHTML/compile.d.ts +6 -0
- package/types/src/Twine2ArchiveHTML/parse.d.ts +6 -0
- package/types/src/Twine2HTML/compile.d.ts +9 -0
- package/types/src/Twine2HTML/parse.d.ts +17 -0
- package/types/src/extwee.d.ts +2 -0
- package/web-index.js +1 -1
package/docs/_sidebar.md
CHANGED
|
@@ -16,7 +16,7 @@ import { writeFileSync } from 'node:fs';
|
|
|
16
16
|
const example = new Story( 'Example' );
|
|
17
17
|
|
|
18
18
|
// Generate 20 passages and add them to the story.
|
|
19
|
-
for(let i = 0; i < 20; i
|
|
19
|
+
for(let i = 0; i < 20; i++) {
|
|
20
20
|
example.addPassage( new Passage( `Passage ${i}`, 'Some Text') );
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TWS To Twee
|
|
2
2
|
|
|
3
|
-
Converting from TWS to Twee 3 is similar to many other conversion processes
|
|
3
|
+
Converting from TWS to Twee 3 is similar to many other conversion processes with one small difference. TWS conversion needs to be begin from the [**Buffer** data type in JavaScript](https://nodejs.org/api/buffer.html).
|
|
4
4
|
|
|
5
5
|
1. Read the binary file.
|
|
6
6
|
2. Convert the binary files into a Buffer.
|
package/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { parse as parseTWS } from './src/TWS/parse.js';
|
|
|
8
8
|
import { compile as compileTwine1HTML } from './src/Twine1HTML/compile.js';
|
|
9
9
|
import { compile as compileTwine2HTML } from './src/Twine2HTML/compile.js';
|
|
10
10
|
import { compile as compileTwine2ArchiveHTML } from './src/Twine2ArchiveHTML/compile.js';
|
|
11
|
-
import Story from './src/Story.js';
|
|
11
|
+
import { Story } from './src/Story.js';
|
|
12
12
|
import Passage from './src/Passage.js';
|
|
13
13
|
import StoryFormat from './src/StoryFormat.js';
|
|
14
14
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extwee",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "A story compiler tool using Twine-compatible formats",
|
|
5
5
|
"author": "Dan Cox",
|
|
6
6
|
"main": "index.js",
|
|
@@ -24,33 +24,35 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"commander": "^11.1.0",
|
|
27
|
-
"
|
|
27
|
+
"html-entities": "^2.4.0",
|
|
28
|
+
"node-html-parser": "^6.1.12",
|
|
28
29
|
"pickleparser": "^0.2.1",
|
|
29
|
-
"semver": "^7.
|
|
30
|
+
"semver": "^7.5.4",
|
|
30
31
|
"uuid": "^9.0.1"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@babel/cli": "^7.23.4",
|
|
34
|
-
"@babel/core": "^7.23.
|
|
35
|
+
"@babel/core": "^7.23.7",
|
|
35
36
|
"@babel/eslint-parser": "^7.23.3",
|
|
36
37
|
"@babel/eslint-plugin": "^7.23.5",
|
|
37
|
-
"@babel/preset-env": "^7.23.
|
|
38
|
+
"@babel/preset-env": "^7.23.8",
|
|
38
39
|
"babel-loader": "^9.1.3",
|
|
39
40
|
"clean-jsdoc-theme": "^4.2.17",
|
|
40
|
-
"core-js": "^3.
|
|
41
|
+
"core-js": "^3.35.0",
|
|
41
42
|
"docsify-cli": "^4.4.4",
|
|
42
|
-
"esbuild": "0.19.
|
|
43
|
+
"esbuild": "0.19.11",
|
|
43
44
|
"eslint": "^8.56.0",
|
|
44
45
|
"eslint-config-standard": "^17.1.0",
|
|
45
46
|
"eslint-plugin-import": "^2.29.1",
|
|
46
|
-
"eslint-plugin-jest": "^27.6.
|
|
47
|
-
"eslint-plugin-jsdoc": "^
|
|
48
|
-
"eslint-plugin-node": "^11.
|
|
47
|
+
"eslint-plugin-jest": "^27.6.1",
|
|
48
|
+
"eslint-plugin-jsdoc": "^48.0.2",
|
|
49
|
+
"eslint-plugin-node": "^11.1.0",
|
|
49
50
|
"eslint-plugin-promise": "^6.1.1",
|
|
50
51
|
"jest": "^29.7.0",
|
|
51
52
|
"pkg": "^5.8.1",
|
|
52
53
|
"regenerator-runtime": "^0.14.1",
|
|
53
|
-
"shelljs": "^0.8.
|
|
54
|
+
"shelljs": "^0.8.5",
|
|
55
|
+
"typescript": "^5.3.3",
|
|
54
56
|
"webpack": "^5.89.0",
|
|
55
57
|
"webpack-cli": "^5.1.4"
|
|
56
58
|
},
|
|
@@ -61,5 +63,6 @@
|
|
|
61
63
|
"bugs": {
|
|
62
64
|
"url": "https://github.com/videlais/extwee/issues"
|
|
63
65
|
},
|
|
64
|
-
"type": "module"
|
|
66
|
+
"type": "module",
|
|
67
|
+
"types": "./types/index.d.ts"
|
|
65
68
|
}
|
package/src/JSON/parse.js
CHANGED
package/src/Passage.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { encode } from 'html-entities';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* A passage is the smallest unit of a Twine story.
|
|
3
5
|
*/
|
|
@@ -76,6 +78,7 @@ export default class Passage {
|
|
|
76
78
|
set tags (t) {
|
|
77
79
|
// Test if tags is an array
|
|
78
80
|
if (Array.isArray(t)) {
|
|
81
|
+
// Set the tags.
|
|
79
82
|
this.#_tags = t;
|
|
80
83
|
} else {
|
|
81
84
|
throw new Error('Tags must be an array!');
|
|
@@ -185,13 +188,13 @@ export default class Passage {
|
|
|
185
188
|
* name: (string) Required.
|
|
186
189
|
* The name of the passage.
|
|
187
190
|
*/
|
|
188
|
-
passageData += ` name="${this.name}"`;
|
|
191
|
+
passageData += ` name="${ encode( this.name ) }"`;
|
|
189
192
|
|
|
190
193
|
/**
|
|
191
194
|
* tags: (string) Optional.
|
|
192
195
|
* Any tags for the passage separated by spaces.
|
|
193
196
|
*/
|
|
194
|
-
passageData += ` tags="${this.#_tags.join(' ')}" `;
|
|
197
|
+
passageData += ` tags="${ encode( this.#_tags.join(' ') ) }" `;
|
|
195
198
|
|
|
196
199
|
/**
|
|
197
200
|
* position: (string) Optional.
|
|
@@ -211,30 +214,8 @@ export default class Passage {
|
|
|
211
214
|
passageData += `size="${this.#_metadata.size}" `;
|
|
212
215
|
}
|
|
213
216
|
|
|
214
|
-
/**
|
|
215
|
-
* Escape passage Twine 2 passage text.
|
|
216
|
-
* @param {string} text - Text to escape.
|
|
217
|
-
* @returns {string} Escaped text.
|
|
218
|
-
*/
|
|
219
|
-
const escape = function (text) {
|
|
220
|
-
const rules = [
|
|
221
|
-
['&', '&'],
|
|
222
|
-
['<', '<'],
|
|
223
|
-
['>', '>'],
|
|
224
|
-
['"', '"'],
|
|
225
|
-
["'", '''],
|
|
226
|
-
['`', '`']
|
|
227
|
-
];
|
|
228
|
-
|
|
229
|
-
rules.forEach(([rule, template]) => {
|
|
230
|
-
text = text.replaceAll(rule, template);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
return text;
|
|
234
|
-
};
|
|
235
|
-
|
|
236
217
|
// Add the text and close the element.
|
|
237
|
-
passageData += `>${
|
|
218
|
+
passageData += `>${ encode( this.text ) }</tw-passagedata>\n`;
|
|
238
219
|
|
|
239
220
|
// Return the Twine 2 HTML element.
|
|
240
221
|
return passageData;
|
|
@@ -258,13 +239,13 @@ export default class Passage {
|
|
|
258
239
|
* tiddler: (string) Required.
|
|
259
240
|
* The name of the passage.
|
|
260
241
|
*/
|
|
261
|
-
passageData += ` tiddler="${this.name}"`;
|
|
242
|
+
passageData += ` tiddler="${ encode( this.name ) }"`;
|
|
262
243
|
|
|
263
244
|
/**
|
|
264
245
|
* tags: (string) Required.
|
|
265
246
|
* Any tags for the passage separated by spaces.
|
|
266
247
|
*/
|
|
267
|
-
passageData += ` tags="${this.#_tags.join(' ')}" `;
|
|
248
|
+
passageData += ` tags="${ encode( this.#_tags.join(' ') ) }" `;
|
|
268
249
|
|
|
269
250
|
/**
|
|
270
251
|
* modifier: (string) Optional.
|
|
@@ -290,7 +271,7 @@ export default class Passage {
|
|
|
290
271
|
* text: (string) Required.
|
|
291
272
|
* Text content of the passage.
|
|
292
273
|
*/
|
|
293
|
-
passageData += `>${this.#_text}</div>`;
|
|
274
|
+
passageData += `>${ encode( this.#_text ) }</div>`;
|
|
294
275
|
|
|
295
276
|
// Return the HTML representation.
|
|
296
277
|
return passageData;
|
package/src/Story.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import Passage from './Passage.js';
|
|
2
2
|
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { encode } from 'html-entities';
|
|
3
4
|
|
|
4
5
|
const creatorName = 'extwee';
|
|
5
|
-
const
|
|
6
|
+
const creatorVersion = '2.2.1';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
class Story {
|
|
8
9
|
/**
|
|
9
10
|
* Internal name of story
|
|
10
11
|
* @private
|
|
@@ -78,7 +79,7 @@ export default class Story {
|
|
|
78
79
|
this.name = name;
|
|
79
80
|
// Store the creator.
|
|
80
81
|
this.#_creator = creatorName;
|
|
81
|
-
this.#_creatorVersion =
|
|
82
|
+
this.#_creatorVersion = creatorVersion;
|
|
82
83
|
// Set metadata to an object.
|
|
83
84
|
this.#_metadata = {};
|
|
84
85
|
}
|
|
@@ -516,7 +517,7 @@ export default class Story {
|
|
|
516
517
|
*/
|
|
517
518
|
toTwine2HTML () {
|
|
518
519
|
// Prepare HTML content.
|
|
519
|
-
let storyData = `<tw-storydata name="${this.name}"`;
|
|
520
|
+
let storyData = `<tw-storydata name="${ encode( this.name ) }"`;
|
|
520
521
|
// Passage Identification (PID) counter.
|
|
521
522
|
// (Twine 2 starts with 1, so we mirror that.)
|
|
522
523
|
let PIDcounter = 1;
|
|
@@ -552,7 +553,7 @@ export default class Story {
|
|
|
552
553
|
storyData += ` startnode="${startPID}"`;
|
|
553
554
|
|
|
554
555
|
// Defaults to 'extwee' if missing.
|
|
555
|
-
storyData += ` creator="${this.creator}"`;
|
|
556
|
+
storyData += ` creator="${ encode( this.creator ) }"`;
|
|
556
557
|
|
|
557
558
|
// Default to extwee version.
|
|
558
559
|
storyData += ` creator-version="${this.creatorVersion}"`;
|
|
@@ -571,7 +572,7 @@ export default class Story {
|
|
|
571
572
|
storyData += ` zoom="${this.zoom}"`;
|
|
572
573
|
|
|
573
574
|
// Write existing or default value.
|
|
574
|
-
storyData += ` format="${this.#_format}"`;
|
|
575
|
+
storyData += ` format="${ encode(this.#_format) }"`;
|
|
575
576
|
|
|
576
577
|
// Write existing or default value.
|
|
577
578
|
storyData += ` format-version="${this.#_formatVersion}"`;
|
|
@@ -648,3 +649,5 @@ export default class Story {
|
|
|
648
649
|
return outputContents;
|
|
649
650
|
}
|
|
650
651
|
}
|
|
652
|
+
|
|
653
|
+
export { Story, creatorName, creatorVersion };
|
package/src/TWS/parse.js
CHANGED
package/src/Twee/parse.js
CHANGED
package/src/Twine1HTML/parse.js
CHANGED
package/src/Twine2HTML/parse.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parse as HtmlParser } from 'node-html-parser';
|
|
2
|
-
import Story from '../Story.js';
|
|
2
|
+
import { Story } from '../Story.js';
|
|
3
3
|
import Passage from '../Passage.js';
|
|
4
|
+
import { decode } from 'html-entities';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Parse Twine 2 HTML into Story object.
|
|
@@ -244,9 +245,9 @@ function parse (content) {
|
|
|
244
245
|
// Add a new Passage into an array
|
|
245
246
|
story.addPassage(
|
|
246
247
|
new Passage(
|
|
247
|
-
name,
|
|
248
|
-
text,
|
|
249
|
-
tags,
|
|
248
|
+
decode(name),
|
|
249
|
+
decode(text),
|
|
250
|
+
tags.map(tag => decode(tag)),
|
|
250
251
|
metadata
|
|
251
252
|
)
|
|
252
253
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Story from '../../src/Story.js';
|
|
1
|
+
import { Story, creatorVersion, creatorName } from '../../src/Story.js';
|
|
2
2
|
import Passage from '../../src/Passage.js';
|
|
3
3
|
import { parse as parseJSON } from '../../src/JSON/parse.js';
|
|
4
4
|
|
|
@@ -22,8 +22,8 @@ describe('JSON', () => {
|
|
|
22
22
|
expect(s.start).toBe('');
|
|
23
23
|
expect(s.formatVersion).toBe('');
|
|
24
24
|
expect(s.format).toBe('');
|
|
25
|
-
expect(s.creator).toBe(
|
|
26
|
-
expect(s.creatorVersion).toBe(
|
|
25
|
+
expect(s.creator).toBe(creatorName);
|
|
26
|
+
expect(s.creatorVersion).toBe(creatorVersion);
|
|
27
27
|
expect(s.zoom).toBe(0);
|
|
28
28
|
expect(Object.keys(s.metadata).length).toBe(0);
|
|
29
29
|
});
|
|
@@ -59,7 +59,7 @@ describe('JSON', () => {
|
|
|
59
59
|
expect(r.formatVersion).toBe('1.0');
|
|
60
60
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
61
61
|
expect(r.format).toBe('Snowman');
|
|
62
|
-
expect(r.creator).toBe(
|
|
62
|
+
expect(r.creator).toBe(creatorName);
|
|
63
63
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
64
64
|
expect(r.zoom).toBe(1);
|
|
65
65
|
expect(r.size()).toBe(1);
|
|
@@ -75,7 +75,7 @@ describe('JSON', () => {
|
|
|
75
75
|
expect(r.formatVersion).toBe('1.0');
|
|
76
76
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
77
77
|
expect(r.format).toBe('Snowman');
|
|
78
|
-
expect(r.creator).toBe(
|
|
78
|
+
expect(r.creator).toBe(creatorName);
|
|
79
79
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
80
80
|
expect(r.zoom).toBe(1);
|
|
81
81
|
expect(r.size()).toBe(1);
|
|
@@ -91,7 +91,7 @@ describe('JSON', () => {
|
|
|
91
91
|
expect(r.formatVersion).toBe('1.0');
|
|
92
92
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
93
93
|
expect(r.format).toBe('Snowman');
|
|
94
|
-
expect(r.creator).toBe(
|
|
94
|
+
expect(r.creator).toBe(creatorName);
|
|
95
95
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
96
96
|
expect(r.zoom).toBe(1);
|
|
97
97
|
expect(r.size()).toBe(1);
|
|
@@ -107,7 +107,7 @@ describe('JSON', () => {
|
|
|
107
107
|
expect(r.formatVersion).toBe('1.0');
|
|
108
108
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
109
109
|
expect(r.format).toBe('Snowman');
|
|
110
|
-
expect(r.creator).toBe(
|
|
110
|
+
expect(r.creator).toBe(creatorName);
|
|
111
111
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
112
112
|
expect(r.zoom).toBe(1);
|
|
113
113
|
expect(r.size()).toBe(1);
|
|
@@ -123,7 +123,7 @@ describe('JSON', () => {
|
|
|
123
123
|
expect(r.formatVersion).toBe('');
|
|
124
124
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
125
125
|
expect(r.format).toBe('Snowman');
|
|
126
|
-
expect(r.creator).toBe(
|
|
126
|
+
expect(r.creator).toBe(creatorName);
|
|
127
127
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
128
128
|
expect(r.zoom).toBe(1);
|
|
129
129
|
expect(r.size()).toBe(1);
|
|
@@ -139,7 +139,7 @@ describe('JSON', () => {
|
|
|
139
139
|
expect(r.formatVersion).toBe('1.0');
|
|
140
140
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
141
141
|
expect(r.format).toBe('');
|
|
142
|
-
expect(r.creator).toBe(
|
|
142
|
+
expect(r.creator).toBe(creatorName);
|
|
143
143
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
144
144
|
expect(r.zoom).toBe(1);
|
|
145
145
|
expect(r.size()).toBe(1);
|
|
@@ -155,7 +155,7 @@ describe('JSON', () => {
|
|
|
155
155
|
expect(r.formatVersion).toBe('1.0');
|
|
156
156
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
157
157
|
expect(r.format).toBe('Snowman');
|
|
158
|
-
expect(r.creator).toBe(
|
|
158
|
+
expect(r.creator).toBe(creatorName);
|
|
159
159
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
160
160
|
expect(r.zoom).toBe(1);
|
|
161
161
|
expect(r.size()).toBe(1);
|
|
@@ -171,8 +171,8 @@ describe('JSON', () => {
|
|
|
171
171
|
expect(r.formatVersion).toBe('1.0');
|
|
172
172
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
173
173
|
expect(r.format).toBe('Snowman');
|
|
174
|
-
expect(r.creator).toBe(
|
|
175
|
-
expect(r.creatorVersion).toBe(
|
|
174
|
+
expect(r.creator).toBe(creatorName);
|
|
175
|
+
expect(r.creatorVersion).toBe(creatorVersion);
|
|
176
176
|
expect(r.zoom).toBe(1);
|
|
177
177
|
expect(r.size()).toBe(1);
|
|
178
178
|
});
|
|
@@ -187,7 +187,7 @@ describe('JSON', () => {
|
|
|
187
187
|
expect(r.formatVersion).toBe('1.0');
|
|
188
188
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
189
189
|
expect(r.format).toBe('Snowman');
|
|
190
|
-
expect(r.creator).toBe(
|
|
190
|
+
expect(r.creator).toBe(creatorName);
|
|
191
191
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
192
192
|
expect(r.zoom).toBe(0);
|
|
193
193
|
expect(r.size()).toBe(1);
|
|
@@ -203,7 +203,7 @@ describe('JSON', () => {
|
|
|
203
203
|
expect(r.formatVersion).toBe('1.0');
|
|
204
204
|
expect(Object.keys(r.metadata).length).toBe(0);
|
|
205
205
|
expect(r.format).toBe('Snowman');
|
|
206
|
-
expect(r.creator).toBe(
|
|
206
|
+
expect(r.creator).toBe(creatorName);
|
|
207
207
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
208
208
|
expect(r.zoom).toBe(1);
|
|
209
209
|
expect(r.size()).toBe(1);
|
|
@@ -219,7 +219,7 @@ describe('JSON', () => {
|
|
|
219
219
|
expect(r.formatVersion).toBe('1.0');
|
|
220
220
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
221
221
|
expect(r.format).toBe('Snowman');
|
|
222
|
-
expect(r.creator).toBe(
|
|
222
|
+
expect(r.creator).toBe(creatorName);
|
|
223
223
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
224
224
|
expect(r.zoom).toBe(1);
|
|
225
225
|
expect(r.size()).toBe(0);
|
|
@@ -235,7 +235,7 @@ describe('JSON', () => {
|
|
|
235
235
|
expect(r.formatVersion).toBe('1.0');
|
|
236
236
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
237
237
|
expect(r.format).toBe('Snowman');
|
|
238
|
-
expect(r.creator).toBe(
|
|
238
|
+
expect(r.creator).toBe(creatorName);
|
|
239
239
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
240
240
|
expect(r.zoom).toBe(1);
|
|
241
241
|
expect(r.size()).toBe(0);
|
|
@@ -251,7 +251,7 @@ describe('JSON', () => {
|
|
|
251
251
|
expect(r.formatVersion).toBe('1.0');
|
|
252
252
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
253
253
|
expect(r.format).toBe('Snowman');
|
|
254
|
-
expect(r.creator).toBe(
|
|
254
|
+
expect(r.creator).toBe(creatorName);
|
|
255
255
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
256
256
|
expect(r.zoom).toBe(1);
|
|
257
257
|
expect(r.size()).toBe(1);
|
|
@@ -268,7 +268,7 @@ describe('JSON', () => {
|
|
|
268
268
|
expect(r.formatVersion).toBe('1.0');
|
|
269
269
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
270
270
|
expect(r.format).toBe('Snowman');
|
|
271
|
-
expect(r.creator).toBe(
|
|
271
|
+
expect(r.creator).toBe(creatorName);
|
|
272
272
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
273
273
|
expect(r.zoom).toBe(1);
|
|
274
274
|
expect(r.size()).toBe(1);
|
|
@@ -286,7 +286,7 @@ describe('JSON', () => {
|
|
|
286
286
|
expect(r.formatVersion).toBe('1.0');
|
|
287
287
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
288
288
|
expect(r.format).toBe('Snowman');
|
|
289
|
-
expect(r.creator).toBe(
|
|
289
|
+
expect(r.creator).toBe(creatorName);
|
|
290
290
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
291
291
|
expect(r.zoom).toBe(1);
|
|
292
292
|
expect(r.size()).toBe(1);
|
|
@@ -304,7 +304,7 @@ describe('JSON', () => {
|
|
|
304
304
|
expect(r.formatVersion).toBe('1.0');
|
|
305
305
|
expect(Object.keys(r.metadata).length).toBe(1);
|
|
306
306
|
expect(r.format).toBe('Snowman');
|
|
307
|
-
expect(r.creator).toBe(
|
|
307
|
+
expect(r.creator).toBe(creatorName);
|
|
308
308
|
expect(r.creatorVersion).toBe('2.2.0');
|
|
309
309
|
expect(r.zoom).toBe(1);
|
|
310
310
|
expect(r.size()).toBe(1);
|
package/test/Passage.test.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Passage from '../src/Passage.js';
|
|
2
|
+
import { parse as HTMLParser } from 'node-html-parser';
|
|
2
3
|
|
|
3
4
|
describe('Passage', () => {
|
|
4
5
|
describe('constructor()', () => {
|
|
@@ -172,4 +173,72 @@ describe('Passage', () => {
|
|
|
172
173
|
expect(result.includes('position="10,10"')).toBe(true);
|
|
173
174
|
});
|
|
174
175
|
});
|
|
176
|
+
|
|
177
|
+
describe('Escaping', function () {
|
|
178
|
+
it('Should escape double quotes', function () {
|
|
179
|
+
const p = new Passage('Test', 'Word "word"');
|
|
180
|
+
expect(p.toTwine2HTML().includes('Word "word"')).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('Should escape ampersands', function () {
|
|
184
|
+
const p = new Passage('Test', 'Word & word');
|
|
185
|
+
expect(p.toTwine2HTML().includes('Word & word')).toBe(true);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('Should escape less than', function () {
|
|
189
|
+
const p = new Passage('Test', 'Word < word');
|
|
190
|
+
expect(p.toTwine2HTML().includes('Word < word')).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('Should escape greater than', function () {
|
|
194
|
+
const p = new Passage('Test', 'Word > word');
|
|
195
|
+
expect(p.toTwine2HTML().includes('Word > word')).toBe(true);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('Should escape all', function () {
|
|
199
|
+
const p = new Passage('Test', 'Word &<>"\' word');
|
|
200
|
+
expect(p.toTwine2HTML().includes('>Word &<>"' word<')).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('Should escape meta-characters safely in name', function () {
|
|
204
|
+
const p = new Passage('"Test"');
|
|
205
|
+
expect(p.toTwine2HTML().includes('name=""Test""')).toBe(true);
|
|
206
|
+
expect(p.toTwine1HTML().includes('tiddler=""Test""')).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('Should escape meta-characters safely in text', function () {
|
|
210
|
+
const p = new Passage('Test', '"Word"');
|
|
211
|
+
expect(p.toTwine2HTML().includes('"Word"')).toBe(true);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('Should escape meta-characters safely in tags', function () {
|
|
215
|
+
const p = new Passage('Test', 'Word', ['&tag', '"bad"']);
|
|
216
|
+
expect(p.toTwine2HTML().includes('tags="&tag "bad""')).toBe(true);
|
|
217
|
+
expect(p.toTwine1HTML().includes('tags="&tag "bad""')).toBe(true);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('Should escape meta-characters safely in Twee header', function () {
|
|
221
|
+
const p = new Passage('Where do tags begin? [well', '', ['hmm']);
|
|
222
|
+
expect(p.toTwee().includes('Where do tags begin? \[well [hmm]')).toBe(true);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('Should produce valid HTML attributes', function () {
|
|
226
|
+
// Generate passage.
|
|
227
|
+
const p = new Passage('"Test"', '"Word"', ['&tag', '"bad"'], { position: '100,100' });
|
|
228
|
+
// Parse HTML.
|
|
229
|
+
const d = new HTMLParser(p.toTwine2HTML());
|
|
230
|
+
// Test attributes.
|
|
231
|
+
expect(d.querySelector('tw-passagedata').getAttribute('name')).toBe('"Test"');
|
|
232
|
+
expect(d.querySelector('tw-passagedata').getAttribute('tags')).toBe('&tag "bad"');
|
|
233
|
+
expect(d.querySelector('tw-passagedata').getAttribute('position')).toBe('100,100');
|
|
234
|
+
// Use Twine 2 result.
|
|
235
|
+
const s = `<tw-passagedata pid="1" name=""Test"" tags="&tag "bad"" position="100,100" size="100,100"></tw-passagedata>`;
|
|
236
|
+
// Parse HTML.
|
|
237
|
+
const t = new HTMLParser(s);
|
|
238
|
+
// Test Twine 2 attributes.
|
|
239
|
+
expect(t.querySelector('tw-passagedata').getAttribute('name')).toBe('"Test"');
|
|
240
|
+
expect(t.querySelector('tw-passagedata').getAttribute('tags')).toBe('&tag "bad"');
|
|
241
|
+
expect(t.querySelector('tw-passagedata').getAttribute('position')).toBe('100,100');
|
|
242
|
+
});
|
|
243
|
+
});
|
|
175
244
|
});
|
package/test/Story.test.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import Story from '../src/Story.js';
|
|
1
|
+
import { Story, creatorName, creatorVersion } from '../src/Story.js';
|
|
2
2
|
import Passage from '../src/Passage';
|
|
3
3
|
import { parse as parseTwee } from '../src/Twee/parse.js';
|
|
4
4
|
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { parse as HTMLParser } from 'node-html-parser';
|
|
5
6
|
|
|
6
7
|
// Pull the name and version of this project from package.json.
|
|
7
8
|
// These are used as the 'creator' and 'creator-version'.
|
|
@@ -405,8 +406,8 @@ describe('Story', () => {
|
|
|
405
406
|
expect(result.start).toBe('');
|
|
406
407
|
expect(result.formatVersion).toBe('');
|
|
407
408
|
expect(result.format).toBe('');
|
|
408
|
-
expect(result.creator).toBe(
|
|
409
|
-
expect(result.creatorVersion).toBe(
|
|
409
|
+
expect(result.creator).toBe(creatorName);
|
|
410
|
+
expect(result.creatorVersion).toBe(creatorVersion);
|
|
410
411
|
expect(result.zoom).toBe(0);
|
|
411
412
|
expect(Object.keys(result.metadata).length).toBe(0);
|
|
412
413
|
});
|
|
@@ -635,4 +636,42 @@ describe('Story', () => {
|
|
|
635
636
|
expect(result.includes('<div tiddler="Start"')).toBe(true);
|
|
636
637
|
});
|
|
637
638
|
});
|
|
639
|
+
|
|
640
|
+
describe('Escaping', function () {
|
|
641
|
+
it('Should produce valid Twine 2 Story HTML', function () {
|
|
642
|
+
// Create a new Story.
|
|
643
|
+
const s = new Story('"Abuse" &test');
|
|
644
|
+
// Add a passage.
|
|
645
|
+
s.addPassage(new Passage('"Test"', 'Word'));
|
|
646
|
+
// Set start.
|
|
647
|
+
s.start = '"Test"';
|
|
648
|
+
// Parse HTML.
|
|
649
|
+
const root = HTMLParser(s.toTwine2HTML());
|
|
650
|
+
// Expect correct name attribute for tw-storydata.
|
|
651
|
+
expect(root.querySelector('tw-storydata').getAttribute('name')).toBe('"Abuse" &test');
|
|
652
|
+
// Expect correct name attribute for tw-passagedata.
|
|
653
|
+
expect(root.querySelector('tw-passagedata').getAttribute('name')).toBe('"Test"');
|
|
654
|
+
// Use Twine 2 result.
|
|
655
|
+
const s2 = `<tw-storydata name=""Abuse" &test" startnode="1" creator="Twine" creator-version="2.8.1" format="Harlowe" format-version="3.3.8" ifid="452A9D80-C759-42C5-B001-5B861A2410C5" options="" tags="" zoom="1" hidden><style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css"></style><script role="script" id="twine-user-script" type="text/twine-javascript"></script><tw-passagedata pid="1" name=""Test"" tags="&tag "bad"" position="300,100" size="100,100"></tw-passagedata></tw-storydata>`;
|
|
656
|
+
// Parse HTML.
|
|
657
|
+
const root2 = HTMLParser(s2);
|
|
658
|
+
// Expect correct name attribute for tw-storydata.
|
|
659
|
+
expect(root2.querySelector('tw-storydata').getAttribute('name')).toBe('"Abuse" &test');
|
|
660
|
+
// Expect correct name attribute for tw-passagedata.
|
|
661
|
+
expect(root2.querySelector('tw-passagedata').getAttribute('name')).toBe('"Test"');
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
it('Should produce valid Twine 1 Story HTML', function () {
|
|
665
|
+
// Create a new Story.
|
|
666
|
+
const s = new Story('"Abuse" &test');
|
|
667
|
+
// Add a passage.
|
|
668
|
+
s.addPassage(new Passage('"Test"', 'Word'));
|
|
669
|
+
// Set start.
|
|
670
|
+
s.start = '"Test"';
|
|
671
|
+
// Parse HTML.
|
|
672
|
+
const root = HTMLParser(s.toTwine1HTML());
|
|
673
|
+
// Expect correct name attribute for div.
|
|
674
|
+
expect(root.querySelector('div').getAttribute('tiddler')).toBe('"Test"');
|
|
675
|
+
});
|
|
676
|
+
});
|
|
638
677
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { compile as compileTwine2ArchiveHTML } from '../../src/Twine2ArchiveHTML/compile.js';
|
|
2
|
-
import Story from '../../src/Story.js';
|
|
2
|
+
import { Story } from '../../src/Story.js';
|
|
3
3
|
import Passage from '../../src/Passage.js';
|
|
4
4
|
|
|
5
5
|
describe('Twine2ArchiveHTML', function () {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parse as parseStoryFormat } from '../../src/StoryFormat/parse.js';
|
|
2
2
|
import { parse as parseTwine2HTML } from '../../src/Twine2HTML/parse.js';
|
|
3
3
|
import { compile as compileTwine2HTML } from '../../src/Twine2HTML/compile.js';
|
|
4
|
-
import Story from '../../src/Story.js';
|
|
4
|
+
import { Story } from '../../src/Story.js';
|
|
5
5
|
import Passage from '../../src/Passage.js';
|
|
6
6
|
import { readFileSync } from 'node:fs';
|
|
7
7
|
|