extwee 2.2.1 → 2.2.2

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.
Files changed (49) hide show
  1. package/.github/codeql-analysis.yml +51 -0
  2. package/README.md +9 -3
  3. package/docs/objects/story.md +1 -2
  4. package/index.js +2 -0
  5. package/package.json +17 -17
  6. package/src/IFID/generate.js +20 -0
  7. package/src/JSON/parse.js +43 -0
  8. package/src/Passage.js +52 -3
  9. package/src/Story.js +266 -107
  10. package/src/StoryFormat/parse.js +190 -80
  11. package/src/StoryFormat.js +78 -88
  12. package/src/TWS/parse.js +2 -2
  13. package/src/Twee/parse.js +2 -3
  14. package/src/Twine1HTML/compile.js +2 -0
  15. package/src/Twine1HTML/parse.js +2 -3
  16. package/src/Twine2ArchiveHTML/compile.js +8 -0
  17. package/src/Twine2ArchiveHTML/parse.js +33 -3
  18. package/src/Twine2HTML/compile.js +31 -6
  19. package/src/Twine2HTML/parse.js +49 -54
  20. package/test/IFID/IFID.Generate.test.js +10 -0
  21. package/test/JSON/JSON.Parse.test.js +4 -4
  22. package/test/Story.test.js +256 -46
  23. package/test/StoryFormat/StoryFormat.Parse.test.js +442 -55
  24. package/test/StoryFormat.test.js +9 -2
  25. package/test/TWS/Parse.test.js +1 -1
  26. package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Parse.test.js +20 -4
  27. package/test/Twine2HTML/Twine2HTML.Compile.test.js +35 -120
  28. package/test/Twine2HTML/Twine2HTML.Parse.test.js +57 -38
  29. package/test/Twine2HTML/Twine2HTMLCompiler/format.js +9 -0
  30. package/test/Twine2HTML/Twine2HTMLParser/missingZoom.html +1 -1
  31. package/web-index.js +2 -0
  32. package/test/StoryFormat/StoryFormatParser/example.js +0 -3
  33. package/test/StoryFormat/StoryFormatParser/example2.js +0 -3
  34. package/test/StoryFormat/StoryFormatParser/format.js +0 -1
  35. package/test/StoryFormat/StoryFormatParser/format_doublename.js +0 -1
  36. package/test/StoryFormat/StoryFormatParser/harlowe.js +0 -3
  37. package/test/StoryFormat/StoryFormatParser/missingAuthor.js +0 -1
  38. package/test/StoryFormat/StoryFormatParser/missingDescription.js +0 -1
  39. package/test/StoryFormat/StoryFormatParser/missingImage.js +0 -1
  40. package/test/StoryFormat/StoryFormatParser/missingLicense.js +0 -1
  41. package/test/StoryFormat/StoryFormatParser/missingName.js +0 -1
  42. package/test/StoryFormat/StoryFormatParser/missingProofing.js +0 -1
  43. package/test/StoryFormat/StoryFormatParser/missingSource.js +0 -1
  44. package/test/StoryFormat/StoryFormatParser/missingURL.js +0 -1
  45. package/test/StoryFormat/StoryFormatParser/missingVersion.js +0 -1
  46. package/test/StoryFormat/StoryFormatParser/versionWrong.js +0 -1
  47. package/test/Twine2HTML/Twine2HTMLParser/missingName.html +0 -33
  48. package/test/Twine2HTML/Twine2HTMLParser/missingPID.html +0 -15
  49. package/test/Twine2HTML/Twine2HTMLParser/missingPassageName.html +0 -15
@@ -0,0 +1,51 @@
1
+ name: "CodeQL"
2
+
3
+ on:
4
+ push:
5
+ branches: [ develop ]
6
+ paths-ignore:
7
+ - 'test/**'
8
+ pull_request:
9
+ branches: [ develop ]
10
+ paths-ignore:
11
+ - 'test/**'
12
+
13
+ jobs:
14
+ analyze:
15
+ name: Analyze
16
+ runs-on: ubuntu-latest
17
+
18
+ strategy:
19
+ fail-fast: false
20
+ matrix:
21
+ language: [ 'javascript' ]
22
+
23
+ steps:
24
+ - name: Checkout repository
25
+ uses: actions/checkout@v3
26
+
27
+ - name: Initialize CodeQL
28
+ uses: github/codeql-action/init@v2
29
+ with:
30
+ languages: ${{ matrix.language }}
31
+
32
+ - name: Perform CodeQL Analysis
33
+ uses: github/codeql-action/analyze@v2
34
+ with:
35
+ upload: false # disable the upload here - we will upload in a different action
36
+ output: sarif-results
37
+
38
+ - name: filter-sarif
39
+ uses: advanced-security/filter-sarif@v1
40
+ with:
41
+ # filter out all test files unless they contain a sql-injection vulnerability
42
+ patterns:
43
+ -test/**/*
44
+ +test/**/*:js/sql-injection
45
+ input: sarif-results/${{ matrix.language }}.sarif
46
+ output: sarif-results/${{ matrix.language }}.sarif
47
+
48
+ - name: Upload SARIF
49
+ uses: github/codeql-action/upload-sarif@v2
50
+ with:
51
+ sarif_file: sarif-results/${{ matrix.language }}.sarif
package/README.md CHANGED
@@ -27,11 +27,11 @@
27
27
 
28
28
  ## Story Compilation
29
29
 
30
- The process of *story compilation* converts human-readable content, what is generally called a *story*, into a playable format such as HyperText Markup Language (HTML) able to be read by another application such as a web browser. The result is then presented as links or other visual, interactive elements to a player or reader, depending on the work created.
30
+ The process of *story compilation* converts human-readable content, what is generally called a *story*, into a playable format such as HyperText Markup Language (HTML). The result is then presented as links or other visual, interactive elements for another party to interact with to see its content.
31
31
 
32
- The term *compilation* is used because different parts of code are put together in a specific arrangement to enable later play. As part of Twine-compatible HTML, this means combining story format JavaScript code with story HTML data.
32
+ The term *compilation* is used because different parts of code are put together in a specific arrangement to enable later play. As part of Twine-compatible HTML, this means combining JavaScript code (generally a "story format") with story HTML data.
33
33
 
34
- Extwee is **not** an authoring tool. It cannot be used to create story content. This data must be created using other tools or processes. Extwee can then *compile* the story with story format code to produce something playable.
34
+ Extwee is **not** an authoring tool. It cannot be used to create story content. This data must be created using other tools or processes. Extwee can then *compile* the content with story format code to produce something playable.
35
35
 
36
36
  Playable formats are the following and require external story formats[^1] to enable play:
37
37
 
@@ -113,6 +113,12 @@ Compiles story, story formats, or other data into an archive or playable format.
113
113
 
114
114
  **Note:** In order to create playable Twine 1 HTML, an `engine.js` file must be supplied.
115
115
 
116
+ ### Support Functionality
117
+
118
+ Multiple Twine formats support using [an IFID](https://ifdb.org/help-ifid) to identify one work from another.
119
+
120
+ As part of its API, the Extwee method `generateIFID()` can be used to create a new IFID for a `Story` object or as part of other processes.
121
+
116
122
  <p align="right">(<a href="#readme-top">back to top</a>)</p>
117
123
 
118
124
  ## Documentation
@@ -12,7 +12,7 @@ Depending on the incoming format or creation method, many possible properties ca
12
12
  - format ( string ) Name of the story format for Twine 2 HTML.
13
13
  - formatVersion ( string ) Semantic version of the named story format for Twine 2 HTML or Twee 3.
14
14
  - zoom ( float ) Zoom level for Twine 2 HTML or Twee 3.
15
- - passages ( array(Passage) ) **[Read-only]** Collection of internal passages.
15
+ - passages ( array(Passage) ) Collection of internal passages.
16
16
  - creator ( string ) Name of story creation tool. (Defaults to "Extwee").
17
17
  - creatorVersion ( string ) Semantic version of named creation tool.
18
18
  - metadata ( object ) Key-value pairs of metadata values.
@@ -41,7 +41,6 @@ As collections of passages, each **Story** has multiple methods for accessing an
41
41
  - `getPassagesByTags(string)`: Returns an array of any passages containing a particular tag value.
42
42
  - `getPassageByName(string)`: Returns either `null`` or the named passage.
43
43
  - `size()`: Returns the number of passages in the collection.
44
- - `forEachPassage(callback)`: Allows for iterating over the passage collection.
45
44
 
46
45
  ## Passage Creation Example
47
46
 
package/index.js CHANGED
@@ -8,6 +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 { generate as generateIFID } from './src/IFID/generate.js';
11
12
  import { Story } from './src/Story.js';
12
13
  import Passage from './src/Passage.js';
13
14
  import StoryFormat from './src/StoryFormat.js';
@@ -23,6 +24,7 @@ export {
23
24
  compileTwine1HTML,
24
25
  compileTwine2HTML,
25
26
  compileTwine2ArchiveHTML,
27
+ generateIFID,
26
28
  Story,
27
29
  Passage,
28
30
  StoryFormat
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "extwee",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "description": "A story compiler tool using Twine-compatible formats",
5
5
  "author": "Dan Cox",
6
6
  "main": "index.js",
@@ -8,7 +8,7 @@
8
8
  "extwee": "src/extwee.js"
9
9
  },
10
10
  "scripts": {
11
- "test": "jest --silent --runInBand --coverage --colors",
11
+ "test": "jest --runInBand --coverage --colors",
12
12
  "lint": "eslint ./src/**/*.js --fix",
13
13
  "lint:test": "eslint ./test/**/*.test.js --fix",
14
14
  "build:web": "webpack",
@@ -23,42 +23,42 @@
23
23
  ],
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
- "commander": "^11.1.0",
27
- "html-entities": "^2.4.0",
26
+ "commander": "^12.0.0",
27
+ "html-entities": "^2.5.2",
28
28
  "node-html-parser": "^6.1.12",
29
29
  "pickleparser": "^0.2.1",
30
- "semver": "^7.5.4",
30
+ "semver": "^7.6.0",
31
31
  "uuid": "^9.0.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@babel/cli": "^7.23.4",
35
- "@babel/core": "^7.23.7",
36
- "@babel/eslint-parser": "^7.23.3",
35
+ "@babel/core": "^7.24.0",
36
+ "@babel/eslint-parser": "^7.23.10",
37
37
  "@babel/eslint-plugin": "^7.23.5",
38
- "@babel/preset-env": "^7.23.8",
38
+ "@babel/preset-env": "^7.24.0",
39
+ "@types/uuid": "^9.0.7",
39
40
  "babel-loader": "^9.1.3",
40
41
  "clean-jsdoc-theme": "^4.2.17",
41
- "core-js": "^3.35.0",
42
- "docsify-cli": "^4.4.4",
43
- "esbuild": "0.19.11",
44
- "eslint": "^8.56.0",
42
+ "core-js": "^3.36.0",
43
+ "esbuild": "^0.20.1",
44
+ "eslint": "^8.57.0",
45
45
  "eslint-config-standard": "^17.1.0",
46
46
  "eslint-plugin-import": "^2.29.1",
47
- "eslint-plugin-jest": "^27.6.1",
48
- "eslint-plugin-jsdoc": "^48.0.2",
47
+ "eslint-plugin-jest": "^27.9.0",
48
+ "eslint-plugin-jsdoc": "^48.2.0",
49
49
  "eslint-plugin-node": "^11.1.0",
50
50
  "eslint-plugin-promise": "^6.1.1",
51
51
  "jest": "^29.7.0",
52
52
  "pkg": "^5.8.1",
53
53
  "regenerator-runtime": "^0.14.1",
54
54
  "shelljs": "^0.8.5",
55
- "typescript": "^5.3.3",
56
- "webpack": "^5.89.0",
55
+ "typescript": "^5.4.2",
56
+ "webpack": "^5.90.3",
57
57
  "webpack-cli": "^5.1.4"
58
58
  },
59
59
  "repository": {
60
60
  "type": "git",
61
- "url": "https://github.com/videlais/extwee"
61
+ "url": "git+https://github.com/videlais/extwee.git"
62
62
  },
63
63
  "bugs": {
64
64
  "url": "https://github.com/videlais/extwee/issues"
@@ -0,0 +1,20 @@
1
+ import { v4 } from 'uuid';
2
+
3
+ /**
4
+ * Generates an Interactive Fiction Identification (IFID) based the Treaty of Babel.
5
+ *
6
+ * For Twine works, the IFID is a UUID (v4) in uppercase.
7
+ * @see Treaty of Babel ({@link https://babel.ifarchive.org/babel_rev11.html#the-ifid-for-an-html-story-file})
8
+ * @function generate
9
+ * @description Generates a new IFID.
10
+ * @returns {string} IFID
11
+ * @example
12
+ * const ifid = generate();
13
+ * console.log(ifid);
14
+ * // => 'A1B2C3D4-E5F6-G7H8-I9J0-K1L2M3N4O5P6'
15
+ */
16
+ function generate () {
17
+ return v4().toUpperCase();
18
+ }
19
+
20
+ export { generate };
package/src/JSON/parse.js CHANGED
@@ -3,9 +3,52 @@ import Passage from '../Passage.js';
3
3
 
4
4
  /**
5
5
  * Parse JSON representation into Story.
6
+ * @see {@link https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-jsonoutput-doc.md Twine 2 JSON Specification}
6
7
  * @function parse
7
8
  * @param {string} jsonString - JSON string to convert to Story.
9
+ * @throws {Error} - Invalid JSON!
8
10
  * @returns {Story} Story object.
11
+ * @example
12
+ * const jsonString = `{
13
+ * "name": "My Story",
14
+ * "start": "First Passage",
15
+ * "ifid": "A1B2C3D4-E5F6-G7H8-I9J0-K1L2M3N4O5P6",
16
+ * "format": "SugarCube",
17
+ * "formatVersion": "2.31.0",
18
+ * "creator": "Twine",
19
+ * "creatorVersion": "2.3.9",
20
+ * "zoom": 1,
21
+ * "passages": [
22
+ * {
23
+ * "name": "First Passage",
24
+ * "tags": "",
25
+ * "metadata": "",
26
+ * "text": "This is the first passage."
27
+ * },
28
+ * ]
29
+ * }`;
30
+ * const story = parse(jsonString);
31
+ * console.log(story);
32
+ * // => Story {
33
+ * // name: 'My Story',
34
+ * // start: 'First Passage',
35
+ * // IFID: 'A1B2C3D4-E5F6-G7H8-I9J0-K1L2M3N4O5P6',
36
+ * // format: 'SugarCube',
37
+ * // formatVersion: '2.31.0',
38
+ * // creator: 'Twine',
39
+ * // creatorVersion: '2.3.9',
40
+ * // zoom: 1,
41
+ * // tagColors: {},
42
+ * // metadata: {},
43
+ * // passages: [
44
+ * // Passage {
45
+ * // name: 'First Passage',
46
+ * // tags: '',
47
+ * // metadata: '',
48
+ * // text: 'This is the first passage.'
49
+ * // }
50
+ * // ]
51
+ * // }
9
52
  */
10
53
  function parse (jsonString) {
11
54
  // Create future object.
package/src/Passage.js CHANGED
@@ -1,30 +1,68 @@
1
1
  import { encode } from 'html-entities';
2
2
 
3
3
  /**
4
- * A passage is the smallest unit of a Twine story.
5
- */
4
+ * Passage class.
5
+ * @class
6
+ * @classdesc Represents a passage in a Twine story.
7
+ * @property {string} name - Name of the passage.
8
+ * @property {Array} tags - Tags for the passage.
9
+ * @property {object} metadata - Metadata for the passage.
10
+ * @property {string} text - Text content of the passage.
11
+ * @method {string} toTwee - Return a Twee representation.
12
+ * @method {string} toJSON - Return JSON representation.
13
+ * @method {string} toTwine2HTML - Return Twine 2 HTML representation.
14
+ * @method {string} toTwine1HTML - Return Twine 1 HTML representation.
15
+ * @example
16
+ * const p = new Passage('Start', 'This is the start of the story.');
17
+ * console.log(p.toTwee());
18
+ * // :: Start
19
+ * // This is the start of the story.
20
+ * //
21
+ * console.log(p.toJSON());
22
+ * // {"name":"Start","tags":[],"metadata":{},"text":"This is the start of the story."}
23
+ * console.log(p.toTwine2HTML());
24
+ * // <tw-passagedata pid="1" name="Start" tags="" >This is the start of the story.</tw-passagedata>
25
+ * console.log(p.toTwine1HTML());
26
+ * // <div tiddler="Start" tags="" modifier="extwee" twine-position="10,10">This is the start of the story.</div>
27
+ * @example
28
+ * const p = new Passage('Start', 'This is the start of the story.', ['start', 'beginning'], {position: '10,10', size: '100,100'});
29
+ * console.log(p.toTwee());
30
+ * // :: Start [start beginning] {"position":"10,10","size":"100,100"}
31
+ * // This is the start of the story.
32
+ * //
33
+ * console.log(p.toJSON());
34
+ * // {"name":"Start","tags":["start","beginning"],"metadata":{"position":"10,10","size":"100,100"},"text":"This is the start of the story."}
35
+ * console.log(p.toTwine2HTML());
36
+ * // <tw-passagedata pid="1" name="Start" tags="start beginning" position="10,10" size="100,100">This is the start of the story.</tw-passagedata>
37
+ * console.log(p.toTwine1HTML());
38
+ * // <div tiddler="Start" tags="start beginning" modifier="extwee" twine-position="10,10">This is the start of the story.</div>
39
+ */
6
40
  export default class Passage {
7
41
  /**
8
42
  * Name of the Passage
9
43
  * @private
44
+ * @type {string}
10
45
  */
11
- #_name = null;
46
+ #_name = '';
12
47
 
13
48
  /**
14
49
  * Internal array of tags
15
50
  * @private
51
+ * @type {Array}
16
52
  */
17
53
  #_tags = [];
18
54
 
19
55
  /**
20
56
  * Internal metadata of passage
21
57
  * @private
58
+ * @type {object}
22
59
  */
23
60
  #_metadata = {};
24
61
 
25
62
  /**
26
63
  * Internal text of the passage
27
64
  * @private
65
+ * @type {string}
28
66
  */
29
67
  #_text = '';
30
68
 
@@ -57,6 +95,7 @@ export default class Passage {
57
95
 
58
96
  /**
59
97
  * @param {string} s - Name to replace
98
+ * @throws {Error} Name must be a String!
60
99
  */
61
100
  set name (s) {
62
101
  if (typeof s === 'string') {
@@ -74,6 +113,7 @@ export default class Passage {
74
113
 
75
114
  /**
76
115
  * @param {Array} t - Replacement array
116
+ * @throws {Error} Tags must be an array!
77
117
  */
78
118
  set tags (t) {
79
119
  // Test if tags is an array
@@ -93,6 +133,7 @@ export default class Passage {
93
133
 
94
134
  /**
95
135
  * @param {object} m - Replacement object
136
+ * @throws {Error} Metadata must be an object literal!
96
137
  */
97
138
  set metadata (m) {
98
139
  // Test if metadata was an object
@@ -111,6 +152,7 @@ export default class Passage {
111
152
 
112
153
  /**
113
154
  * @param {string} t - Replacement text
155
+ * @throws {Error} Text should be a String!
114
156
  */
115
157
  set text (t) {
116
158
  // Test if text is a String
@@ -123,6 +165,10 @@ export default class Passage {
123
165
 
124
166
  /**
125
167
  * Return a Twee representation.
168
+ *
169
+ * See: https://github.com/iftechfoundation/twine-specs/blob/master/twee-3-specification.md
170
+ *
171
+ * @method toTwee
126
172
  * @returns {string} String form of passage.
127
173
  */
128
174
  toTwee () {
@@ -153,6 +199,7 @@ export default class Passage {
153
199
 
154
200
  /**
155
201
  * Return JSON representation.
202
+ * @method toJSON
156
203
  * @returns {string} JSON string.
157
204
  */
158
205
  toJSON () {
@@ -171,6 +218,7 @@ export default class Passage {
171
218
  /**
172
219
  * Return Twine 2 HTML representation.
173
220
  * (Default Passage ID is 1.)
221
+ * @method toTwine2HTML
174
222
  * @param {number} pid - Passage ID (PID) to record in HTML.
175
223
  * @returns {string} Twine 2 HTML string.
176
224
  */
@@ -223,6 +271,7 @@ export default class Passage {
223
271
 
224
272
  /**
225
273
  * Return Twine 1 HTML representation.
274
+ * @method toTwine1HTML
226
275
  * @returns {string} Twine 1 HTML string.
227
276
  */
228
277
  toTwine1HTML () {