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
@@ -2,30 +2,83 @@ import StoryFormat from '../StoryFormat.js';
2
2
  import { valid } from 'semver';
3
3
 
4
4
  /**
5
- * Parse a Story Format file.
6
- * @param {string} contents - Content
7
- * @returns {StoryFormat} StoryFormat object
5
+ * Parses story format content into a {@link StoryFormat} object.
6
+ *
7
+ * Story formats are generally JSONP files containing a JSON object with the following properties:
8
+ * - name: (string) Optional. (Omitting the name will lead to an Untitled Story Format.)
9
+ * - version: (string) Required, and semantic version-style formatting (x.y.z, e.g., 1.2.1) of the version is also required.
10
+ * - author: (string) Optional.
11
+ * - description: (string) Optional.
12
+ * - image: (string) Optional.
13
+ * - url: (string) Optional.
14
+ * - license: (string) Optional.
15
+ * - proofing: (boolean) Optional (defaults to false).
16
+ * - source: (string) Required.
17
+ *
18
+ * If existing properties do not match their expected type, a warning will be produced and incoming value will be ignored.
19
+ *
20
+ * This function does "soft parsing." It will not throw an error if a specific property is missing or malformed.
21
+ * @see {@link https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-storyformats-spec.md Twine 2 Story Formats Specification}
22
+ * @function parse
23
+ * @param {string} contents - JSONP content.
24
+ * @throws {Error} - Unable to find Twine 2 JSON chunk!
25
+ * @throws {Error} - Unable to parse Twine 2 JSON chunk!
26
+ * @returns {StoryFormat} StoryFormat object.
27
+ * @example
28
+ * const contents = `{
29
+ * "name": "My Story Format",
30
+ * "version": "1.0.0",
31
+ * "author": "Twine",
32
+ * "description": "A story format.",
33
+ * "image": "icon.svg",
34
+ * "url": "https://example.com",
35
+ * "license": "MIT",
36
+ * "proofing": false,
37
+ * "source": "<html></html>"
38
+ * }`;
39
+ * const storyFormat = parse(contents);
40
+ * console.log(storyFormat);
41
+ * // => StoryFormat {
42
+ * // name: 'My Story Format',
43
+ * // version: '1.0.0',
44
+ * // description: 'A story format.',
45
+ * // image: 'icon.svg',
46
+ * // url: 'https://example.com',
47
+ * // license: 'MIT',
48
+ * // proofing: false,
49
+ * // source: '<html></html>'
50
+ * // }
8
51
  */
9
52
  function parse (contents) {
10
- // Harlowe has malformed JSON, so we have to test for it
11
- const harlowePosition = contents.indexOf('harlowe');
53
+ // Create a StoryFormat object.
54
+ const sf = new StoryFormat();
12
55
 
13
- if (harlowePosition !== -1) {
14
- // The 'setup' property is malformed
56
+ /**
57
+ * Beginning with Harlowe 2.0.0 (2021), the `setup` property is added.
58
+ * It is not a valid JSON property and must be removed.
59
+ */
60
+
61
+ // Some versions of Harlowe can have a `setup` property, so we have to test for it.
62
+ const setupPosition = contents.indexOf('harlowe');
63
+
64
+ // If the `setup` property is found, we have to remove it.
65
+ if (setupPosition !== -1) {
66
+ // The 'setup' property is malformed.
15
67
  const setupPosition = contents.lastIndexOf(',"setup": function');
68
+ // Remove the 'setup' property.
16
69
  contents = contents.slice(0, setupPosition) + '}';
17
70
  }
18
71
 
19
- // Find the start of story format or -1, if not found
72
+ // Find the start of story format or -1, if not found.
20
73
  const openingCurlyBracketPosition = contents.indexOf('{');
21
- // Find the end of story format or -1, if not found
74
+ // Find the end of story format or -1, if not found.
22
75
  const closingCurlyBracketPosition = contents.lastIndexOf('}');
23
76
 
24
- // Look for JSON among the story format
25
- // If either is -1, this is not valid JSON
77
+ // Look for JSON among the story format.
78
+ // If either is -1, this is not valid JSON.
26
79
  if (openingCurlyBracketPosition === -1 || closingCurlyBracketPosition === -1) {
27
80
  // Either start or end curly brackets were now found!
28
- throw new Error('Unable to find Twine2 JSON chunk!');
81
+ throw new Error('Error: Unable to find Twine 2 JSON chunk!');
29
82
  } else {
30
83
  // Slice out the JSON based on curly brackets
31
84
  contents = contents.slice(openingCurlyBracketPosition, closingCurlyBracketPosition + 1);
@@ -34,101 +87,158 @@ function parse (contents) {
34
87
  // Create an object literal
35
88
  let jsonContent = {};
36
89
 
90
+ // Attempt to parse the JSON.
37
91
  try {
38
92
  jsonContent = JSON.parse(contents);
39
93
  } catch (event) {
40
- throw new Error('Unable to parse Twine2 JSON chunk!');
94
+ throw new Error('Error: Unable to parse Twine 2 JSON chunk!');
41
95
  }
42
96
 
43
97
  /**
44
- * The following keys are found in most or all story formats:
45
98
  * - name: (string) Optional. Name of the story format.
46
99
  * (Omitting the name will lead to an Untitled Story Format.)
47
- * - version: (string) Required, and semantic version-style formatting
48
- * (x.y.z, e.g., 1.2.1) of the version is also required.
49
- * - author: (string) Optional.
50
- * - description: (string) Optional.
51
- * - image: (string) Optional. The filename of an image (ideally SVG)
52
- * served from the same directory as the format.js file.
53
- * - url: (string) Optional. The URL of the directory containing the format.js file.
54
- * - license: (string) Optional.
55
- * - proofing: (boolean) Optional (defaults to false). True if the story format
56
- * is a "proofing" format. The distinction is relevant only in the Twine 2 UI.
57
- * - source: (string) Required. Full HTML output of the story format.
58
- * (See: https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-storyformats-spec.md)
59
100
  */
60
-
61
- // Name is optional, so we have to test for it
62
- if (!Object.prototype.hasOwnProperty.call(jsonContent, 'name')) {
63
- // Use the default name
64
- jsonContent.name = 'Untitled Story Format';
101
+ if (Object.prototype.hasOwnProperty.call(jsonContent, 'name')) {
102
+ // Test if name is a string.
103
+ if (typeof jsonContent.name !== 'string') {
104
+ // Produce a warning.
105
+ console.warn('Warning: Processed story format\'s name is not a string. It will be ignored.');
106
+ } else {
107
+ // Save the name.
108
+ sf.name = jsonContent.name;
109
+ }
65
110
  }
66
111
 
67
- // Author is optional, so we have to test for it
68
- if (!Object.prototype.hasOwnProperty.call(jsonContent, 'author')) {
69
- // Use the default author
70
- jsonContent.author = '';
71
- }
72
-
73
- // Description is optional, so we have to test for it
74
- if (!Object.prototype.hasOwnProperty.call(jsonContent, 'description')) {
75
- // Use the default description
76
- jsonContent.description = '';
112
+ /**
113
+ * author: (string) Optional.
114
+ */
115
+ if (Object.prototype.hasOwnProperty.call(jsonContent, 'author')) {
116
+ // Test if author is a string.
117
+ if (typeof jsonContent.author !== 'string') {
118
+ // Produce a warning.
119
+ console.warn('Warning: Processed story format\'s author is not a string. It will be ignored.');
120
+ } else {
121
+ // Save the author.
122
+ sf.author = jsonContent.author;
123
+ }
77
124
  }
78
125
 
79
- // Image is optional, so we have to test for it
80
- if (!Object.prototype.hasOwnProperty.call(jsonContent, 'image')) {
81
- // Use the default image
82
- jsonContent.image = '';
126
+ /**
127
+ * description: (string) Optional.
128
+ */
129
+ if (Object.prototype.hasOwnProperty.call(jsonContent, 'description')) {
130
+ // Test if description is a string.
131
+ if (typeof jsonContent.description !== 'string') {
132
+ // Produce a warning.
133
+ console.warn('Warning: Processed story format\'s description is not a string. It will be ignored.');
134
+ } else {
135
+ // Save the description.
136
+ sf.description = jsonContent.description;
137
+ }
83
138
  }
84
139
 
85
- // URL is optional, so we have to test for it
86
- if (!Object.prototype.hasOwnProperty.call(jsonContent, 'url')) {
87
- // Use the default url
88
- jsonContent.url = '';
140
+ /**
141
+ * image: (string) Optional. The filename of an image (ideally SVG)
142
+ * served from the same directory as the format.js file.
143
+ */
144
+ if (Object.prototype.hasOwnProperty.call(jsonContent, 'image')) {
145
+ // Test if image is a string.
146
+ if (typeof jsonContent.image !== 'string') {
147
+ // Produce a warning.
148
+ console.warn('Warning: Processed story format\'s image is not a string. It will be ignored.');
149
+ } else {
150
+ // Save the image.
151
+ sf.image = jsonContent.image;
152
+ }
89
153
  }
90
154
 
91
- // License is optional, so we have to test for it
92
- if (!Object.prototype.hasOwnProperty.call(jsonContent, 'license')) {
93
- // Use the default license
94
- jsonContent.license = '';
155
+ /**
156
+ * url: (string) Optional. The URL of the directory containing the `format.js` file.
157
+ */
158
+ if (Object.prototype.hasOwnProperty.call(jsonContent, 'url')) {
159
+ // Test if url is a string.
160
+ if (typeof jsonContent.url !== 'string') {
161
+ // Produce a warning.
162
+ console.warn('Warning: Processed story format\'s url is not a string. It will be ignored.');
163
+ } else {
164
+ // Save the url.
165
+ sf.url = jsonContent.url;
166
+ }
95
167
  }
96
168
 
97
- // Proofing is optional, so we have to test for it
98
- if (!Object.prototype.hasOwnProperty.call(jsonContent, 'proofing')) {
99
- // Use the default proofing
100
- jsonContent.proofing = false;
169
+ /**
170
+ * license: (string) Optional. The license under which the story format is released.
171
+ */
172
+ if (Object.prototype.hasOwnProperty.call(jsonContent, 'license')) {
173
+ // Test if license is a string.
174
+ if (typeof jsonContent.license !== 'string') {
175
+ // Produce a warning.
176
+ console.warn('Warning: Processed story format\'s license is not a string. It will be ignored.');
177
+ } else {
178
+ // Save the license.
179
+ sf.license = jsonContent.license;
180
+ }
101
181
  }
102
182
 
103
- // Version is required, so we have to test for it
104
- if (!Object.prototype.hasOwnProperty.call(jsonContent, 'version')) {
105
- // Throw error
106
- throw new Error('Processed story format does not have required version property!');
183
+ /**
184
+ * proofing: (boolean) Optional (defaults to false). True if the story format
185
+ * is a "proofing" format. The distinction is relevant only in the Twine 2 UI.
186
+ */
187
+ if (Object.prototype.hasOwnProperty.call(jsonContent, 'proofing')) {
188
+ // Test if proofing is a boolean.
189
+ if (typeof jsonContent.proofing !== 'boolean') {
190
+ // Produce a warning.
191
+ console.warn('Warning: Processed story format\'s proofing is not a boolean. It will be ignored.');
192
+ } else {
193
+ // Save the proofing.
194
+ sf.proofing = jsonContent.proofing;
195
+ }
107
196
  }
108
197
 
109
- // Test if version is semantic-style, which is required
110
- if (valid(jsonContent.version) === null) {
111
- throw new Error('Processed story format\'s version is not a valid semantic value!');
198
+ /**
199
+ * version: (string) Required, and semantic version-style formatting
200
+ * (x.y.z, e.g., 1.2.1) of the version is also required.
201
+ */
202
+ if (Object.prototype.hasOwnProperty.call(jsonContent, 'version')) {
203
+ // Test if version is a string.
204
+ if (typeof jsonContent.version !== 'string') {
205
+ // Produce a warning.
206
+ console.warn('Warning: Processed story format\'s version is not a string! It will be ignored.');
207
+ } else {
208
+ // Test if version is a valid semantic version.
209
+ if (valid(jsonContent.version)) {
210
+ // Save the version.
211
+ sf.version = jsonContent.version;
212
+ } else {
213
+ // Produce a warning.
214
+ console.warn('Warning: Processed story format\'s version is not semantic! It will be ignored.');
215
+ }
216
+ }
217
+ } else {
218
+ // Produce a warning.
219
+ console.warn('Warning: Processed story format does not have version property!');
112
220
  }
113
221
 
114
- // Source is required, so we have to test for it
115
- if (!Object.prototype.hasOwnProperty.call(jsonContent, 'source')) {
116
- // Throw error
117
- throw new Error('Processed story format does not have required source property!');
222
+ /**
223
+ * source: (string) Required. Full HTML output of the story format.
224
+ * @see {@link https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-storyformats-spec.md Twine 2 Story Formats Specification}
225
+ */
226
+ if (Object.prototype.hasOwnProperty.call(jsonContent, 'source')) {
227
+ // Test if source is a string.
228
+ if (typeof jsonContent.source !== 'string') {
229
+ // Produce a warning.
230
+ console.warn('Warning: Processed story format\'s source is not a string! It will be ignored.');
231
+ } else {
232
+ // Save the source.
233
+ sf.source = jsonContent.source;
234
+ }
235
+ } else {
236
+ // Warn if source is not found.
237
+ console.warn('Warning: Processed story format does not have source property!');
118
238
  }
119
239
 
120
- // Pass all values to the constructor and return the result
121
- return new StoryFormat(
122
- jsonContent.name,
123
- jsonContent.version,
124
- jsonContent.description,
125
- jsonContent.author,
126
- jsonContent.image,
127
- jsonContent.url,
128
- jsonContent.license,
129
- jsonContent.proofing,
130
- jsonContent.source
131
- );
240
+ // Return the StoryFormat object.
241
+ return sf;
132
242
  }
133
243
 
134
244
  export { parse };
@@ -1,9 +1,41 @@
1
+ /**
2
+ * StoryFormat representing a Twine 2 story format.
3
+ *
4
+ * This class has type checking on all of its properties.
5
+ * If a property is set to a value of the wrong type, a TypeError will be thrown.
6
+ *
7
+ * @see {@link https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-storyformats-spec.md Twine 2 Story Formats Specification}
8
+ *
9
+ * @class
10
+ * @classdesc A class representing a Twine 2 story format.
11
+ * @property {string} name - The name of the story format.
12
+ * @property {string} version - The semantic version of the story format.
13
+ * @property {string} description - The description of the story format.
14
+ * @property {string} author - The author of the story format.
15
+ * @property {string} image - The image of the story format.
16
+ * @property {string} url - The URL of the story format.
17
+ * @property {string} license - The license of the story format.
18
+ * @property {boolean} proofing - The proofing of the story format.
19
+ * @property {string} source - The source of the story format.
20
+ * @example
21
+ * const sf = new StoryFormat();
22
+ * sf.name = 'New';
23
+ * sf.version = '1.0.0';
24
+ * sf.description = 'New';
25
+ * sf.author = 'New';
26
+ * sf.image = 'New';
27
+ * sf.url = 'New';
28
+ * sf.license = 'New';
29
+ * sf.proofing = true;
30
+ * sf.source = 'New';
31
+ */
32
+
1
33
  export default class StoryFormat {
2
34
  /**
3
35
  * Internal name.
4
36
  * @private
5
37
  */
6
- #_name = '';
38
+ #_name = 'Untitled Story Format';
7
39
 
8
40
  /**
9
41
  * Internal version.
@@ -45,7 +77,7 @@ export default class StoryFormat {
45
77
  * Internal proofing.
46
78
  * @private
47
79
  */
48
- #_proofing = '';
80
+ #_proofing = false;
49
81
 
50
82
  /**
51
83
  * Internal source.
@@ -53,207 +85,165 @@ export default class StoryFormat {
53
85
  */
54
86
  #_source = '';
55
87
 
56
- /**
57
- * Create a story format.
58
- * @param {string} name - Name
59
- * @param {string} version - Version
60
- * @param {string} description - Description
61
- * @param {string} author - Author
62
- * @param {string} image - Image
63
- * @param {string} url - URL
64
- * @param {string} license - License
65
- * @param {boolean} proofing - If proofing or not
66
- * @param {string} source - Source
67
- */
68
- constructor (
69
- name = 'Untitled Story Format',
70
- version = '',
71
- description = '',
72
- author = '',
73
- image = '',
74
- url = '',
75
- license = '',
76
- proofing = false,
77
- source = ''
78
- ) {
79
- // Set name
80
- this.name = name;
81
-
82
- // Set version
83
- this.version = version;
84
-
85
- // Set description
86
- this.description = description;
87
-
88
- // Set author
89
- this.author = author;
90
-
91
- // Set image
92
- this.image = image;
93
-
94
- // Set URL
95
- this.url = url;
96
-
97
- // Set license
98
- this.license = license;
99
-
100
- // Set proofing
101
- this.proofing = proofing;
102
-
103
- // Set source
104
- this.source = source;
105
- }
106
-
107
88
  /**
108
89
  * Name
109
- * @returns {string} Name
90
+ * @returns {string} Name.
110
91
  */
111
92
  get name () { return this.#_name; }
112
93
 
113
94
  /**
114
- * @param {string} n - Replacement name
95
+ * @param {string} n - Replacement name.
115
96
  */
116
97
  set name (n) {
117
98
  if (typeof n === 'string') {
118
99
  this.#_name = n;
119
100
  } else {
120
- throw new Error('Name must be a string!');
101
+ throw new TypeError('Name must be a string!');
121
102
  }
122
103
  }
123
104
 
124
105
  /**
125
- * Version
126
- * @returns {string} Version
106
+ * Version.
107
+ * @returns {string} Version.
127
108
  */
128
109
  get version () { return this.#_version; }
129
110
 
130
111
  /**
131
- * @param {string} n - Replacement version
112
+ * @param {string} n - Replacement version.
132
113
  */
133
114
  set version (n) {
134
115
  if (typeof n === 'string') {
135
116
  this.#_version = n;
136
117
  } else {
137
- throw new Error('Version must be a string!');
118
+ throw new TypeError('Version must be a string!');
138
119
  }
139
120
  }
140
121
 
141
122
  /**
142
- * Description
143
- * @returns {string} Description
123
+ * Description.
124
+ * @returns {string} Description.
144
125
  */
145
126
  get description () { return this.#_description; }
146
127
 
147
128
  /**
148
- * @param {string} d - Replacement description
129
+ * @param {string} d - Replacement description.
149
130
  */
150
131
  set description (d) {
151
132
  if (typeof d === 'string') {
152
133
  this.#_description = d;
153
134
  } else {
154
- throw new Error('Description must be a string!');
135
+ throw new TypeError('Description must be a string!');
155
136
  }
156
137
  }
157
138
 
158
139
  /**
159
- * Author
160
- * @returns {string} Author
140
+ * Author.
141
+ * @returns {string} Author.
161
142
  */
162
143
  get author () { return this.#_author; }
163
144
 
164
145
  /**
165
- * @param {string} a - Replacement author
146
+ * @param {string} a - Replacement author.
166
147
  */
167
148
  set author (a) {
168
149
  if (typeof a === 'string') {
169
150
  this.#_author = a;
170
151
  } else {
171
- throw new Error('Author must be a string!');
152
+ throw new TypeError('Author must be a string!');
172
153
  }
173
154
  }
174
155
 
175
156
  /**
176
- * Image
177
- * @returns {string} Image
157
+ * Image.
158
+ * @returns {string} Image.
178
159
  */
179
160
  get image () { return this.#_image; }
180
161
 
181
162
  /**
182
- * @param {string} i - Replacement image
163
+ * @param {string} i - Replacement image.
183
164
  */
184
165
  set image (i) {
185
166
  if (typeof i === 'string') {
186
167
  this.#_image = i;
187
168
  } else {
188
- throw new Error('Image must be a string!');
169
+ throw new TypeError('Image must be a string!');
189
170
  }
190
171
  }
191
172
 
192
173
  /**
193
- * URL
194
- * @returns {string} URL
174
+ * URL.
175
+ * @returns {string} URL.
195
176
  */
196
177
  get url () { return this.#_url; }
197
178
 
198
179
  /**
199
- * @param {string} u - Replacement URL
180
+ * @param {string} u - Replacement URL.
200
181
  */
201
182
  set url (u) {
202
183
  if (typeof u === 'string') {
203
184
  this.#_url = u;
204
185
  } else {
205
- throw new Error('URL must be a string!');
186
+ throw new TypeError('URL must be a string!');
206
187
  }
207
188
  }
208
189
 
209
190
  /**
210
- * License
211
- * @returns {string} License
191
+ * License.
192
+ * @returns {string} License.
212
193
  */
213
194
  get license () { return this.#_license; }
214
195
 
215
196
  /**
216
- * @param {string} l - Replacement license
197
+ * @param {string} l - Replacement license.
217
198
  */
218
199
  set license (l) {
219
200
  if (typeof l === 'string') {
220
201
  this.#_license = l;
221
202
  } else {
222
- throw new Error('License must be a string!');
203
+ throw new TypeError('License must be a string!');
223
204
  }
224
205
  }
225
206
 
226
207
  /**
227
- * Proofing
228
- * @returns {boolean} Proofing
208
+ * Proofing.
209
+ * @returns {boolean} Proofing.
229
210
  */
230
211
  get proofing () { return this.#_proofing; }
231
212
 
232
213
  /**
233
- * @param {boolean} p - Replacement proofing
214
+ * @param {boolean} p - Replacement proofing.
234
215
  */
235
216
  set proofing (p) {
236
217
  if (typeof p === 'boolean') {
237
218
  this.#_proofing = p;
238
219
  } else {
239
- throw new Error('Proofing must be a Boolean!');
220
+ throw new TypeError('Proofing must be a Boolean!');
240
221
  }
241
222
  }
242
223
 
243
224
  /**
244
- * Source
245
- * @returns {string} Source
225
+ * Source.
226
+ * @returns {string} Source.
246
227
  */
247
228
  get source () { return this.#_source; }
248
229
 
249
230
  /**
250
- * @param {string} s - Replacement source
231
+ * @param {string} s - Replacement source.
251
232
  */
252
233
  set source (s) {
253
234
  if (typeof s === 'string') {
254
235
  this.#_source = s;
255
236
  } else {
256
- throw new Error('Source must be a String!');
237
+ throw new TypeError('Source must be a String!');
257
238
  }
258
239
  }
240
+
241
+ /**
242
+ * Produces a string representation of the story format object.
243
+ * @method toString
244
+ * @returns {string} - A string representation of the story format.
245
+ */
246
+ toString() {
247
+ return JSON.stringify(this, null, "\t");
248
+ }
259
249
  }
package/src/TWS/parse.js CHANGED
@@ -5,8 +5,8 @@ import { Parser } from 'pickleparser';
5
5
  /**
6
6
  * Parse TWS file (as Buffer) into Story.
7
7
  * Unless it throws an error, it will return a Story object.
8
- *
9
- * See: Twine 1 TWS Documentation [Approval Pending]
8
+ * @see {@link https://github.com/iftechfoundation/twine-specs/blob/master/twine-1-htmloutput-doc.md Twine 1 HTML Documentation}
9
+ * @function parse
10
10
  * @param {Buffer} binaryFileContents - File contents to parse as Buffer.
11
11
  * @returns {Story} Story object.
12
12
  */
package/src/Twee/parse.js CHANGED
@@ -3,9 +3,8 @@ import { Story } from '../Story.js';
3
3
 
4
4
  /**
5
5
  * Parses Twee 3 text into a Story object.
6
- *
7
- * See: Twee 3 Specification
8
- * (https://github.com/iftechfoundation/twine-specs/blob/master/twee-3-specification.md)
6
+ * @see {@link https://github.com/iftechfoundation/twine-specs/blob/master/twee-3-specification.md Twee 3 Specification}
7
+ * @function parse
9
8
  * @param {string} fileContents - File contents to parse
10
9
  * @returns {Story} story
11
10
  */
@@ -2,6 +2,8 @@ import { Story } from '../Story.js';
2
2
 
3
3
  /**
4
4
  * Write a combination of Story object, `engine.js` (from Twine 1), `header.html`, and optional `code.js`.
5
+ * @see {@link https://github.com/iftechfoundation/twine-specs/blob/master/twine-1-htmloutput-doc.md Twine 1 HTML Documentation}
6
+ * @function compile
5
7
  * @param {Story} story - Story object to write.
6
8
  * @param {string} engine - Source of `engine.js` file from Twine 1.
7
9
  * @param {string} header - `header.html` content for Twine 1 story format.
@@ -4,9 +4,8 @@ import { Story } from '../Story.js';
4
4
 
5
5
  /**
6
6
  * Parses Twine 1 HTML into a Story object.
7
- *
8
- * See: Twine 1 HTML Output Documentation
9
- * (https://github.com/iftechfoundation/twine-specs/blob/master/twine-1-htmloutput-doc.md)
7
+ * @see {@link https://github.com/iftechfoundation/twine-specs/blob/master/twine-1-htmloutput-doc.md Twine 1 HTML Documentation}
8
+ * @function parse
10
9
  * @param {string} content - Twine 1 HTML content to parse.
11
10
  * @returns {Story} Story object
12
11
  */