extwee 1.6.0 → 2.0.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 (151) hide show
  1. package/.eslintrc.json +25 -0
  2. package/.github/workflows/nodejs.yml +24 -25
  3. package/.travis.yml +13 -13
  4. package/CODE_OF_CONDUCT.md +82 -0
  5. package/LICENSE +21 -21
  6. package/README.md +36 -205
  7. package/SECURITY.md +12 -0
  8. package/babel.config.json +22 -0
  9. package/bin/extwee.js +49 -0
  10. package/index.js +31 -26
  11. package/package.json +59 -37
  12. package/src/FileReader.js +33 -36
  13. package/src/HTMLParser.js +343 -206
  14. package/src/HTMLWriter.js +227 -177
  15. package/src/Passage.js +202 -20
  16. package/src/Story.js +461 -148
  17. package/src/StoryFormat.js +300 -41
  18. package/src/StoryFormatParser.js +142 -65
  19. package/src/TweeParser.js +161 -255
  20. package/src/TweeWriter.js +98 -111
  21. package/story-formats/chapbook-1.2.0/format.js +1 -0
  22. package/story-formats/chapbook-1.2.0/logo.svg +1 -0
  23. package/story-formats/harlowe-1.2.4/format.js +1 -0
  24. package/story-formats/harlowe-1.2.4/icon.svg +78 -0
  25. package/story-formats/harlowe-2.1.0/format.js +2 -0
  26. package/story-formats/harlowe-2.1.0/icon.svg +78 -0
  27. package/story-formats/harlowe-3.1.0/format.js +3 -0
  28. package/story-formats/harlowe-3.1.0/icon.svg +78 -0
  29. package/story-formats/paperthin-1.0.0/format.js +1 -0
  30. package/story-formats/paperthin-1.0.0/icon.svg +5 -0
  31. package/story-formats/snowman-1.4.0/format.js +1 -0
  32. package/story-formats/snowman-1.4.0/icon.svg +436 -0
  33. package/story-formats/snowman-2.0.2/format.js +1 -0
  34. package/story-formats/snowman-2.0.2/icon.svg +436 -0
  35. package/story-formats/sugarcube-1.0.35/LICENSE +23 -0
  36. package/story-formats/sugarcube-1.0.35/format.js +1 -0
  37. package/story-formats/sugarcube-1.0.35/icon.svg +56 -0
  38. package/story-formats/sugarcube-2.31.1/LICENSE +22 -0
  39. package/story-formats/sugarcube-2.31.1/format.js +1 -0
  40. package/story-formats/sugarcube-2.31.1/icon.svg +56 -0
  41. package/test/{HTMLWriter/example7.twee → CLI/example6.twee} +16 -16
  42. package/test/CLI/harlowe.js +3 -0
  43. package/test/CLI/input.html +47 -0
  44. package/test/CLI/test.twee +18 -0
  45. package/test/CLI/test2.html +47 -0
  46. package/test/CLI/tweeExample.twee +17 -0
  47. package/test/CLI/twineExample.html +15 -0
  48. package/test/CLI.test.js +30 -0
  49. package/test/FileReader.test.js +14 -0
  50. package/test/HTMLParser/Example1.html +53 -0
  51. package/test/HTMLParser/Tags.html +15 -0
  52. package/test/HTMLParser/lyingStartnode.html +15 -0
  53. package/test/HTMLParser/lyingTagColors.html +48 -0
  54. package/test/HTMLParser/missingCreator.html +11 -0
  55. package/test/HTMLParser/missingCreatorVersion.html +11 -0
  56. package/test/HTMLParser/missingFormat.html +11 -0
  57. package/test/HTMLParser/missingFormatVersion.html +11 -0
  58. package/test/HTMLParser/missingIFID.html +11 -0
  59. package/test/HTMLParser/missingName.html +33 -0
  60. package/test/HTMLParser/missingPID.html +15 -0
  61. package/test/HTMLParser/missingPassageName.html +15 -0
  62. package/test/HTMLParser/missingPassageTags.html +15 -0
  63. package/test/HTMLParser/missingPosition.html +15 -0
  64. package/test/HTMLParser/missingScript.html +14 -0
  65. package/test/HTMLParser/missingSize.html +35 -0
  66. package/test/HTMLParser/missingStartnode.html +11 -0
  67. package/test/HTMLParser/missingStyle.html +14 -0
  68. package/test/HTMLParser/missingZoom.html +11 -0
  69. package/test/HTMLParser/tagColors.html +31 -0
  70. package/test/HTMLParser/twineExample.html +15 -46
  71. package/test/HTMLParser/twineExample2.html +15 -0
  72. package/test/HTMLParser/twineExample3.html +15 -0
  73. package/test/HTMLParser.test.js +177 -0
  74. package/test/HTMLWriter/TestTags.html +42 -0
  75. package/test/HTMLWriter/{test10.html → creator.html} +8 -5
  76. package/test/HTMLWriter/example6.twee +16 -16
  77. package/test/HTMLWriter/{example.twee → missingStoryTitle.twee} +29 -29
  78. package/test/HTMLWriter/test11.html +123 -0
  79. package/test/HTMLWriter/test2.html +15 -12
  80. package/test/HTMLWriter/test3.html +7 -13
  81. package/test/HTMLWriter/test4.html +8 -5
  82. package/test/HTMLWriter/test6.html +7 -5
  83. package/test/HTMLWriter.test.js +289 -0
  84. package/test/Passage.test.js +104 -0
  85. package/test/Roundtrip/Example1.html +64 -0
  86. package/test/Roundtrip/example1.twee +21 -0
  87. package/test/Roundtrip/example2.twee +18 -0
  88. package/test/Roundtrip/harlowe.js +3 -0
  89. package/test/Roundtrip/round.html +50 -0
  90. package/test/Roundtrip.test.js +48 -0
  91. package/test/Story/startmeta.twee +29 -29
  92. package/test/Story/test.twee +25 -25
  93. package/test/Story.test.js +282 -0
  94. package/test/StoryFormat.test.js +152 -0
  95. package/test/StoryFormatParser/example.js +3 -0
  96. package/test/StoryFormatParser/{test2.js → example2.js} +3 -3
  97. package/test/StoryFormatParser/format_doublename.js +1 -0
  98. package/test/StoryFormatParser/harlowe.js +2 -2
  99. package/test/StoryFormatParser/missingAuthor.js +1 -0
  100. package/test/StoryFormatParser/missingDescription.js +1 -0
  101. package/test/StoryFormatParser/missingImage.js +1 -0
  102. package/test/StoryFormatParser/missingLicense.js +1 -0
  103. package/test/StoryFormatParser/missingName.js +1 -0
  104. package/test/StoryFormatParser/missingProofing.js +1 -0
  105. package/test/StoryFormatParser/missingSource.js +1 -0
  106. package/test/StoryFormatParser/missingURL.js +1 -0
  107. package/test/StoryFormatParser/missingVersion.js +1 -0
  108. package/test/StoryFormatParser/versionWrong.js +1 -0
  109. package/test/StoryFormatParser.test.js +91 -0
  110. package/test/TweeParser/emptytags.twee +2 -2
  111. package/test/TweeParser/example.twee +32 -29
  112. package/test/TweeParser/missing.twee +19 -0
  113. package/test/{HTMLWriter/example5.twee → TweeParser/multipleScriptPassages.twee} +19 -13
  114. package/test/{HTMLWriter/example4.twee → TweeParser/multipleStyleTag.twee} +19 -13
  115. package/test/TweeParser/multipletags.twee +10 -2
  116. package/test/TweeParser/noTitle.twee +2 -0
  117. package/test/TweeParser/notes.twee +16 -0
  118. package/test/TweeParser/pasagemetadataerror.twee +2 -2
  119. package/test/{HTMLWriter/example2.twee → TweeParser/scriptPassage.twee} +16 -13
  120. package/test/TweeParser/singletag.twee +13 -2
  121. package/test/TweeParser/startMetadata.twee +14 -0
  122. package/test/TweeParser/storydataerror.twee +25 -25
  123. package/test/{HTMLWriter/example3.twee → TweeParser/stylePassage.twee} +16 -13
  124. package/test/TweeParser/test.twee +25 -25
  125. package/test/TweeParser.test.js +79 -0
  126. package/test/TweeWriter/test1.twee +14 -9
  127. package/test/TweeWriter/test3.twee +7 -10
  128. package/test/TweeWriter/test4.twee +14 -0
  129. package/test/TweeWriter/test5.twee +20 -0
  130. package/test/TweeWriter.test.js +85 -0
  131. package/main.js +0 -106
  132. package/src/DirectoryReader.js +0 -107
  133. package/src/DirectoryWatcher.js +0 -92
  134. package/test/DirectoryReader/css/test.css +0 -3
  135. package/test/DirectoryReader1/js/Site.js +0 -1
  136. package/test/DirectoryReader2/error.js +0 -1
  137. package/test/DirectoryReader2/example.css +0 -3
  138. package/test/DirectoryReader2/index.twee +0 -6
  139. package/test/DirectoryReader3/twee/index.twee +0 -6
  140. package/test/DirectoryWatcher/example.txt +0 -0
  141. package/test/DirectoryWatcher/test.txt +0 -0
  142. package/test/DirectoryWatcher/test1.txt +0 -0
  143. package/test/HTMLWriter/test5.html +0 -48
  144. package/test/HTMLWriter/test7.html +0 -48
  145. package/test/HTMLWriter/test8.html +0 -48
  146. package/test/HTMLWriter/test9.html +0 -48
  147. package/test/StoryFormatParser/test.js +0 -2
  148. package/test/TweeParser/test.twee3 +0 -11
  149. package/test/TweeWriter/metatest.twee +0 -12
  150. package/test/TweeWriter/test2.twee +0 -15
  151. package/test/test.js +0 -722
@@ -1,41 +1,300 @@
1
- /**
2
- * @class StoryFormat
3
- * @module StoryFormat
4
- */
5
- class StoryFormat {
6
- /**
7
- * @method StoryFormat
8
- * @constructor
9
- */
10
- constructor (object) {
11
-
12
- // Borrowed from Underscore
13
- // https://github.com/jashkenas/underscore/blob/master/underscore.js#L1319-L1323
14
- let isObject = function(obj) {
15
- var type = typeof obj;
16
- return type === 'function' || type === 'object' && !!obj;
17
- };
18
-
19
- if(isObject(object) ) {
20
-
21
- this.name = object.name || null;
22
- this.version = object.version || null;
23
- this.description = object.description || null;
24
- this.author = object.author || null;
25
- this.image = object.image || null;
26
- this.url = object.url || null;
27
- this.license = object.license || null;
28
- this.proofing = object.proofing || null;
29
- this.source = object.source || null;
30
-
31
- } else {
32
-
33
- throw new Error("Expected JSON object!");
34
-
35
- }
36
-
37
- }
38
-
39
- }
40
-
41
- module.exports = StoryFormat;
1
+ /**
2
+ * @class StoryFormat
3
+ * @module StoryFormat
4
+ */
5
+ export default class StoryFormat {
6
+ /**
7
+ * Internal name
8
+ *
9
+ * @private
10
+ */
11
+ #_name = '';
12
+
13
+ /**
14
+ * Internal version
15
+ *
16
+ * @private
17
+ */
18
+ #_version = '';
19
+
20
+ /**
21
+ * Internal description
22
+ *
23
+ * @private
24
+ */
25
+ #_description = '';
26
+
27
+ /**
28
+ * Internal author
29
+ *
30
+ * @private
31
+ */
32
+ #_author = '';
33
+
34
+ /**
35
+ * Internal image
36
+ *
37
+ * @private
38
+ */
39
+ #_image = '';
40
+
41
+ /**
42
+ * Internal URL
43
+ *
44
+ * @private
45
+ */
46
+ #_url = '';
47
+
48
+ /**
49
+ * Internal license
50
+ *
51
+ * @private
52
+ */
53
+ #_license = '';
54
+
55
+ /**
56
+ * Internal proofing
57
+ *
58
+ * @private
59
+ */
60
+ #_proofing = '';
61
+
62
+ /**
63
+ * Internal source
64
+ *
65
+ * @private
66
+ */
67
+ #_source = '';
68
+
69
+ /**
70
+ * @class
71
+ * @function StoryFormat
72
+ * @param {string} name - Name
73
+ * @param {string} version - Version
74
+ * @param {string} description - Description
75
+ * @param {string} author - Author
76
+ * @param {string} image - Image
77
+ * @param {string} url - URL
78
+ * @param {string} license - License
79
+ * @param {boolean} proofing - If proofing or not
80
+ * @param {string} source - Source
81
+ */
82
+ constructor (
83
+ name = '',
84
+ version = '',
85
+ description = '',
86
+ author = '',
87
+ image = '',
88
+ url = '',
89
+ license = '',
90
+ proofing = false,
91
+ source = ''
92
+ ) {
93
+ // Set name
94
+ this.name = name;
95
+
96
+ // Set version
97
+ this.version = version;
98
+
99
+ // Set description
100
+ this.description = description;
101
+
102
+ // Set author
103
+ this.author = author;
104
+
105
+ // Set image
106
+ this.image = image;
107
+
108
+ // Set URL
109
+ this.url = url;
110
+
111
+ // Set license
112
+ this.license = license;
113
+
114
+ // Set proofing
115
+ this.proofing = proofing;
116
+
117
+ // Set source
118
+ this.source = source;
119
+ }
120
+
121
+ /**
122
+ * Name
123
+ *
124
+ * @public
125
+ * @memberof StoryFormat
126
+ * @returns {string} Name
127
+ */
128
+ get name () { return this.#_name; }
129
+
130
+ /**
131
+ * @param {string} n - Replacement name
132
+ */
133
+ set name (n) {
134
+ if (typeof n === 'string') {
135
+ this.#_name = n;
136
+ } else {
137
+ throw new Error('Name must be a string!');
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Version
143
+ *
144
+ * @public
145
+ * @memberof StoryFormat
146
+ * @returns {string} Version
147
+ */
148
+ get version () { return this.#_version; }
149
+
150
+ /**
151
+ * @param {string} n - Replacement version
152
+ */
153
+ set version (n) {
154
+ if (typeof n === 'string') {
155
+ this.#_version = n;
156
+ } else {
157
+ throw new Error('Version must be a string!');
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Description
163
+ *
164
+ * @public
165
+ * @memberof StoryFormat
166
+ * @returns {string} Description
167
+ */
168
+ get description () { return this.#_description; }
169
+
170
+ /**
171
+ * @param {string} d - Replacement description
172
+ */
173
+ set description (d) {
174
+ if (typeof d === 'string') {
175
+ this.#_description = d;
176
+ } else {
177
+ throw new Error('Description must be a string!');
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Author
183
+ *
184
+ * @public
185
+ * @memberof StoryFormat
186
+ * @returns {string} Author
187
+ */
188
+ get author () { return this.#_author; }
189
+
190
+ /**
191
+ * @param {string} a - Replacement author
192
+ */
193
+ set author (a) {
194
+ if (typeof a === 'string') {
195
+ this.#_author = a;
196
+ } else {
197
+ throw new Error('Author must be a string!');
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Image
203
+ *
204
+ * @public
205
+ * @memberof StoryFormat
206
+ * @returns {string} Image
207
+ */
208
+ get image () { return this.#_image; }
209
+
210
+ /**
211
+ * @param {string} i - Replacement image
212
+ */
213
+ set image (i) {
214
+ if (typeof i === 'string') {
215
+ this.#_image = i;
216
+ } else {
217
+ throw new Error('Image must be a string!');
218
+ }
219
+ }
220
+
221
+ /**
222
+ * URL
223
+ *
224
+ * @public
225
+ * @memberof StoryFormat
226
+ * @returns {string} URL
227
+ */
228
+ get url () { return this.#_url; }
229
+
230
+ /**
231
+ * @param {string} u - Replacement URL
232
+ */
233
+ set url (u) {
234
+ if (typeof u === 'string') {
235
+ this.#_url = u;
236
+ } else {
237
+ throw new Error('URL must be a string!');
238
+ }
239
+ }
240
+
241
+ /**
242
+ * License
243
+ *
244
+ * @public
245
+ * @memberof StoryFormat
246
+ * @returns {string} License
247
+ */
248
+ get license () { return this.#_license; }
249
+
250
+ /**
251
+ * @param {string} l - Replacement license
252
+ */
253
+ set license (l) {
254
+ if (typeof l === 'string') {
255
+ this.#_license = l;
256
+ } else {
257
+ throw new Error('License must be a string!');
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Proofing
263
+ *
264
+ * @public
265
+ * @memberof StoryFormat
266
+ * @returns {boolean} Proofing
267
+ */
268
+ get proofing () { return this.#_proofing; }
269
+
270
+ /**
271
+ * @param {boolean} p - Replacement proofing
272
+ */
273
+ set proofing (p) {
274
+ if (typeof p === 'boolean') {
275
+ this.#_proofing = p;
276
+ } else {
277
+ throw new Error('Proofing must be a Boolean!');
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Source
283
+ *
284
+ * @public
285
+ * @memberof StoryFormat
286
+ * @returns {string} Source
287
+ */
288
+ get source () { return this.#_source; }
289
+
290
+ /**
291
+ * @param {string} s - Replacement source
292
+ */
293
+ set source (s) {
294
+ if (typeof s === 'string') {
295
+ this.#_source = s;
296
+ } else {
297
+ throw new Error('Source must be a String!');
298
+ }
299
+ }
300
+ }
@@ -1,65 +1,142 @@
1
- const StoryFormat = require('./StoryFormat.js');
2
- const FileReader = require('./FileReader.js');
3
- /**
4
- * @class StoryFormatParser
5
- * @module StoryFormatParser
6
- */
7
- class StoryFormatParser {
8
- /**
9
- * @method StoryFormatParser
10
- * @constructor
11
- */
12
- constructor (file) {
13
-
14
- this.storyformat = null;
15
-
16
- this.parse(file);
17
- }
18
-
19
- parse(file) {
20
-
21
- let contents = new FileReader(file).contents;
22
-
23
- // Harlowe has malformed JSON, so we have to test for it
24
- let harlowePosition = contents.indexOf('harlowe');
25
-
26
- if(harlowePosition != -1) {
27
- // The 'setup' property is malformed
28
- let setupPosition = contents.lastIndexOf(',"setup": function');
29
- contents = contents.slice(0, setupPosition) + '}';
30
-
31
- }
32
-
33
- let openingCurlyBracketPosition = contents.indexOf('{');
34
- let closingCurlyBracketPosition = contents.lastIndexOf('}');
35
-
36
- if(openingCurlyBracketPosition != -1 && closingCurlyBracketPosition != -1) {
37
-
38
- // Slice out the JSON
39
- contents = contents.slice(openingCurlyBracketPosition, closingCurlyBracketPosition+1);
40
-
41
- } else {
42
-
43
- throw new Error("Unable to find Twine2 JSON chunk!");
44
-
45
- }
46
-
47
- let jsonContent = "";
48
-
49
- try {
50
-
51
- jsonContent = JSON.parse(contents);
52
-
53
- } catch (event) {
54
-
55
- throw new Error("Unable to parse Twine2 JSON chunk!");
56
-
57
- }
58
-
59
- this.storyformat = new StoryFormat(jsonContent);
60
-
61
- }
62
-
63
- }
64
-
65
- module.exports = StoryFormatParser;
1
+ import StoryFormat from './StoryFormat.js';
2
+ import semver from 'semver';
3
+ /**
4
+ * @class StoryFormatParser
5
+ * @module StoryFormatParser
6
+ */
7
+ export default class StoryFormatParser {
8
+ /**
9
+ * Parse a Story Format file
10
+ *
11
+ * @public
12
+ * @static
13
+ * @memberof StoryFormatParser
14
+ * @function parse
15
+ * @param {string} contents - Content
16
+ * @returns {StoryFormat} StoryFormat object
17
+ */
18
+ static parse (contents) {
19
+ // Harlowe has malformed JSON, so we have to test for it
20
+ const harlowePosition = contents.indexOf('harlowe');
21
+
22
+ if (harlowePosition !== -1) {
23
+ // The 'setup' property is malformed
24
+ const setupPosition = contents.lastIndexOf(',"setup": function');
25
+ contents = contents.slice(0, setupPosition) + '}';
26
+ }
27
+
28
+ // Find the start of story format or -1, if not found
29
+ const openingCurlyBracketPosition = contents.indexOf('{');
30
+ // Find the end of story format or -1, if not found
31
+ const closingCurlyBracketPosition = contents.lastIndexOf('}');
32
+
33
+ // Look for JSON among the story format
34
+ // If either is -1, this is not valid JSON
35
+ if (openingCurlyBracketPosition === -1 || closingCurlyBracketPosition === -1) {
36
+ // Either start or end curly brackets were now found!
37
+ throw new Error('Unable to find Twine2 JSON chunk!');
38
+ } else {
39
+ // Slice out the JSON based on curly brackets
40
+ contents = contents.slice(openingCurlyBracketPosition, closingCurlyBracketPosition + 1);
41
+ }
42
+
43
+ // Create an object literal
44
+ let jsonContent = {};
45
+
46
+ try {
47
+ jsonContent = JSON.parse(contents);
48
+ } catch (event) {
49
+ throw new Error('Unable to parse Twine2 JSON chunk!');
50
+ }
51
+
52
+ /**
53
+ * The following keys are found in most or all story formats:
54
+ * - name: (string) Optional. Name of the story format.
55
+ * (Omitting the name will lead to an Untitled Story Format.)
56
+ * - version: (string) Required, and semantic version-style formatting
57
+ * (x.y.z, e.g., 1.2.1) of the version is also required.
58
+ * - author: (string) Optional.
59
+ * - description: (string) Optional.
60
+ * - image: (string) Optional. The filename of an image (ideally SVG)
61
+ * served from the same directory as the format.js file.
62
+ * - url: (string) Optional. The URL of the directory containing the format.js file.
63
+ * - license: (string) Optional.
64
+ * - proofing: (boolean) Optional (defaults to false). True if the story format
65
+ * is a "proofing" format. The distinction is relevant only in the Twine 2 UI.
66
+ * - source: (string) Required. Full HTML output of the story format.
67
+ * (See: https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-storyformats-spec.md)
68
+ */
69
+
70
+ // Name is optional, so we have to test for it
71
+ if (!Object.prototype.hasOwnProperty.call(jsonContent, 'name')) {
72
+ // Use the default name
73
+ jsonContent.name = 'Untitled Story Format';
74
+ }
75
+
76
+ // Author is optional, so we have to test for it
77
+ if (!Object.prototype.hasOwnProperty.call(jsonContent, 'author')) {
78
+ // Use the default author
79
+ jsonContent.author = '';
80
+ }
81
+
82
+ // Description is optional, so we have to test for it
83
+ if (!Object.prototype.hasOwnProperty.call(jsonContent, 'description')) {
84
+ // Use the default description
85
+ jsonContent.description = '';
86
+ }
87
+
88
+ // Image is optional, so we have to test for it
89
+ if (!Object.prototype.hasOwnProperty.call(jsonContent, 'image')) {
90
+ // Use the default image
91
+ jsonContent.image = '';
92
+ }
93
+
94
+ // URL is optional, so we have to test for it
95
+ if (!Object.prototype.hasOwnProperty.call(jsonContent, 'url')) {
96
+ // Use the default url
97
+ jsonContent.url = '';
98
+ }
99
+
100
+ // License is optional, so we have to test for it
101
+ if (!Object.prototype.hasOwnProperty.call(jsonContent, 'license')) {
102
+ // Use the default license
103
+ jsonContent.license = '';
104
+ }
105
+
106
+ // Proofing is optional, so we have to test for it
107
+ if (!Object.prototype.hasOwnProperty.call(jsonContent, 'proofing')) {
108
+ // Use the default proofing
109
+ jsonContent.proofing = false;
110
+ }
111
+
112
+ // Version is required, so we have to test for it
113
+ if (!Object.prototype.hasOwnProperty.call(jsonContent, 'version')) {
114
+ // Throw error
115
+ throw new Error('Processed story format does not have required version property!');
116
+ }
117
+
118
+ // Test if version is semantic-style, which is required
119
+ if (semver.valid(jsonContent.version) === null) {
120
+ throw new Error('Processed story format\'s version is not a valid semantic value!');
121
+ }
122
+
123
+ // Source is required, so we have to test for it
124
+ if (!Object.prototype.hasOwnProperty.call(jsonContent, 'source')) {
125
+ // Throw error
126
+ throw new Error('Processed story format does not have required source property!');
127
+ }
128
+
129
+ // Pass all values to the constructor and return the result
130
+ return new StoryFormat(
131
+ jsonContent.name,
132
+ jsonContent.version,
133
+ jsonContent.description,
134
+ jsonContent.author,
135
+ jsonContent.image,
136
+ jsonContent.url,
137
+ jsonContent.license,
138
+ jsonContent.proofing,
139
+ jsonContent.source
140
+ );
141
+ }
142
+ }