extwee 2.3.2 → 2.3.4
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.core.min.js +1 -0
- package/build/extwee.twine1html.min.js +1 -0
- package/build/extwee.twine2archive.min.js +1 -0
- package/build/extwee.tws.min.js +1 -0
- package/build/test-modular.html +126 -0
- package/docs/build/extwee.core.min.js +1 -0
- package/docs/build/extwee.twine1html.min.js +1 -0
- package/docs/build/extwee.twine2archive.min.js +1 -0
- package/docs/build/extwee.tws.min.js +1 -0
- package/docs/demos/compiler/extwee.core.min.js +1 -0
- package/docs/demos/compiler/index.css +105 -0
- package/docs/demos/compiler/index.html +359 -0
- package/eslint.config.js +4 -1
- package/package.json +25 -22
- package/src/IFID/generate.js +2 -2
- package/src/Story.js +1 -1
- package/src/Twine1HTML/parse-web.js +255 -0
- package/src/Twine2ArchiveHTML/parse-web.js +134 -0
- package/src/Twine2HTML/parse-web.js +434 -0
- package/src/Web/web-core.js +51 -0
- package/src/Web/web-twine1html.js +35 -0
- package/src/Web/web-twine2archive.js +35 -0
- package/src/Web/web-tws.js +30 -0
- package/test/Config/Config.test.js +1 -1
- package/test/Config/isDirectory.test.js +15 -9
- package/test/Config/isFile.test.js +14 -11
- package/test/Config/loadStoryFormat.test.js +49 -33
- package/test/Config/readDirectories.test.js +25 -15
- package/test/Objects/Story.test.js +1 -0
- package/test/StoryFormat/StoryFormat.Parse.test.js +1 -0
- package/test/Twine1HTML/Twine1HTML.Parse.Web.test.js +484 -0
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Parse.Web.test.js +293 -0
- package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Parse.test.js +1 -0
- package/test/Twine2HTML/Twine2HTML.Parse.Web.test.js +329 -0
- package/test/Twine2HTML/Twine2HTML.Parse.test.js +1 -0
- package/test/Web/web-core-coverage.test.js +175 -0
- package/test/Web/web-core-global.test.js +93 -0
- package/test/Web/web-core.test.js +156 -0
- package/test/Web/window.Extwee.test.js +25 -13
- package/types/src/Story.d.ts +1 -1
- package/types/src/Twine1HTML/parse-web.d.ts +10 -0
- package/types/src/Twine2ArchiveHTML/parse-web.d.ts +37 -0
- package/types/src/Twine2HTML/parse-web.d.ts +21 -0
- package/types/src/Web/html-entities-lite.d.ts +12 -0
- package/types/src/Web/semver-lite.d.ts +10 -0
- package/types/src/Web/uuid-lite.d.ts +6 -0
- package/types/src/Web/web-core.d.ts +23 -0
- package/types/src/Web/web-index.d.ts +1 -0
- package/types/src/Web/web-twine1html.d.ts +10 -0
- package/types/src/Web/web-twine2archive.d.ts +10 -0
- package/types/src/Web/web-tws.d.ts +7 -0
- package/webpack.config.js +23 -2
- package/build/extwee.web.min.js +0 -2
- package/build/extwee.web.min.js.LICENSE.txt +0 -1
- package/web-index.js +0 -31
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Test the global object detection logic more comprehensively
|
|
6
|
+
// by testing the function directly rather than trying to manipulate the environment
|
|
7
|
+
|
|
8
|
+
describe('web-core.js comprehensive global detection coverage', () => {
|
|
9
|
+
|
|
10
|
+
it('should test all branches of global object detection function', () => {
|
|
11
|
+
// Recreate the exact global object detection logic from web-core.js
|
|
12
|
+
const globalObjectDetectionFunction = function() {
|
|
13
|
+
if (typeof globalThis !== 'undefined') return globalThis;
|
|
14
|
+
if (typeof window !== 'undefined') return window;
|
|
15
|
+
if (typeof global !== 'undefined') return global;
|
|
16
|
+
if (typeof self !== 'undefined') return self;
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Test the function logic
|
|
21
|
+
const result = globalObjectDetectionFunction();
|
|
22
|
+
|
|
23
|
+
// In Node.js environment, should return global
|
|
24
|
+
expect(result).toBe(global);
|
|
25
|
+
|
|
26
|
+
// Verify that global exists
|
|
27
|
+
expect(typeof global).toBe('object');
|
|
28
|
+
expect(global).toBeTruthy();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should test globalThis branch when globalThis is defined', () => {
|
|
32
|
+
// Test what happens when globalThis is available
|
|
33
|
+
const mockGlobalThisTest = function() {
|
|
34
|
+
// Simulate having globalThis available
|
|
35
|
+
const fakeGlobalThis = { type: 'globalThis' };
|
|
36
|
+
|
|
37
|
+
// Mock the detection logic
|
|
38
|
+
const detect = function(mockGlobalThis, mockWindow, mockGlobal, mockSelf) {
|
|
39
|
+
if (typeof mockGlobalThis !== 'undefined' && mockGlobalThis !== null) return 'globalThis';
|
|
40
|
+
if (typeof mockWindow !== 'undefined' && mockWindow !== null) return 'window';
|
|
41
|
+
if (typeof mockGlobal !== 'undefined' && mockGlobal !== null) return 'global';
|
|
42
|
+
if (typeof mockSelf !== 'undefined' && mockSelf !== null) return 'self';
|
|
43
|
+
return 'null';
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return detect(fakeGlobalThis, undefined, undefined, undefined);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
expect(mockGlobalThisTest()).toBe('globalThis');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should test self branch when only self is available', () => {
|
|
53
|
+
// Test what happens when only self is available (Web Worker scenario)
|
|
54
|
+
const mockSelfTest = function() {
|
|
55
|
+
const fakeSelf = { type: 'self' };
|
|
56
|
+
|
|
57
|
+
const detect = function(mockGlobalThis, mockWindow, mockGlobal, mockSelf) {
|
|
58
|
+
if (typeof mockGlobalThis !== 'undefined' && mockGlobalThis !== null) return 'globalThis';
|
|
59
|
+
if (typeof mockWindow !== 'undefined' && mockWindow !== null) return 'window';
|
|
60
|
+
if (typeof mockGlobal !== 'undefined' && mockGlobal !== null) return 'global';
|
|
61
|
+
if (typeof mockSelf !== 'undefined' && mockSelf !== null) return 'self';
|
|
62
|
+
return 'null';
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return detect(undefined, undefined, undefined, fakeSelf);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
expect(mockSelfTest()).toBe('self');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should test null return when no global objects are available', () => {
|
|
72
|
+
// Test what happens when no global objects are available
|
|
73
|
+
const mockNullTest = function() {
|
|
74
|
+
const detect = function(mockGlobalThis, mockWindow, mockGlobal, mockSelf) {
|
|
75
|
+
if (typeof mockGlobalThis !== 'undefined' && mockGlobalThis !== null) return 'globalThis';
|
|
76
|
+
if (typeof mockWindow !== 'undefined' && mockWindow !== null) return 'window';
|
|
77
|
+
if (typeof mockGlobal !== 'undefined' && mockGlobal !== null) return 'global';
|
|
78
|
+
if (typeof mockSelf !== 'undefined' && mockSelf !== null) return 'self';
|
|
79
|
+
return null;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return detect(undefined, undefined, undefined, undefined);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
expect(mockNullTest()).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should test priority order of global object detection', () => {
|
|
89
|
+
// Test that globalThis > window > global > self priority is maintained
|
|
90
|
+
const mockPriorityTest = function() {
|
|
91
|
+
const fakeGlobalThis = { type: 'globalThis' };
|
|
92
|
+
const fakeWindow = { type: 'window' };
|
|
93
|
+
const fakeGlobal = { type: 'global' };
|
|
94
|
+
const fakeSelf = { type: 'self' };
|
|
95
|
+
|
|
96
|
+
const detect = function(mockGlobalThis, mockWindow, mockGlobal, mockSelf) {
|
|
97
|
+
if (typeof mockGlobalThis !== 'undefined' && mockGlobalThis !== null) return mockGlobalThis;
|
|
98
|
+
if (typeof mockWindow !== 'undefined' && mockWindow !== null) return mockWindow;
|
|
99
|
+
if (typeof mockGlobal !== 'undefined' && mockGlobal !== null) return mockGlobal;
|
|
100
|
+
if (typeof mockSelf !== 'undefined' && mockSelf !== null) return mockSelf;
|
|
101
|
+
return null;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// When all are available, should return globalThis
|
|
105
|
+
const result1 = detect(fakeGlobalThis, fakeWindow, fakeGlobal, fakeSelf);
|
|
106
|
+
expect(result1.type).toBe('globalThis');
|
|
107
|
+
|
|
108
|
+
// When globalThis not available, should return window
|
|
109
|
+
const result2 = detect(undefined, fakeWindow, fakeGlobal, fakeSelf);
|
|
110
|
+
expect(result2.type).toBe('window');
|
|
111
|
+
|
|
112
|
+
// When globalThis and window not available, should return global
|
|
113
|
+
const result3 = detect(undefined, undefined, fakeGlobal, fakeSelf);
|
|
114
|
+
expect(result3.type).toBe('global');
|
|
115
|
+
|
|
116
|
+
// When only self available, should return self
|
|
117
|
+
const result4 = detect(undefined, undefined, undefined, fakeSelf);
|
|
118
|
+
expect(result4.type).toBe('self');
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
mockPriorityTest();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should verify that global object assignment logic works', () => {
|
|
125
|
+
// Test the assignment logic without requiring module import
|
|
126
|
+
const mockAssignmentScenario = function() {
|
|
127
|
+
// Simulate Node.js environment where global is available
|
|
128
|
+
const fakeGlobal = {};
|
|
129
|
+
const fakeExtwee = { version: '2.3.3' };
|
|
130
|
+
|
|
131
|
+
// Simulate the assignment from web-core.js
|
|
132
|
+
if (fakeGlobal) {
|
|
133
|
+
fakeGlobal.Extwee = fakeExtwee;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return fakeGlobal;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const result = mockAssignmentScenario();
|
|
140
|
+
expect(result.Extwee).toBeDefined();
|
|
141
|
+
expect(result.Extwee.version).toBe('2.3.3');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should test that assignment is conditional on globalObject being truthy', () => {
|
|
145
|
+
// This tests the `if (globalObject)` condition in the web-core.js
|
|
146
|
+
const mockAssignmentTest = function() {
|
|
147
|
+
let assignmentCalled = false;
|
|
148
|
+
|
|
149
|
+
const mockAssign = function(globalObj, extweeObj) {
|
|
150
|
+
if (globalObj) {
|
|
151
|
+
globalObj.Extwee = extweeObj;
|
|
152
|
+
assignmentCalled = true;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Test with truthy global object
|
|
157
|
+
const fakeGlobal = {};
|
|
158
|
+
const fakeExtwee = { version: '2.3.3' };
|
|
159
|
+
mockAssign(fakeGlobal, fakeExtwee);
|
|
160
|
+
|
|
161
|
+
expect(assignmentCalled).toBe(true);
|
|
162
|
+
expect(fakeGlobal.Extwee).toBe(fakeExtwee);
|
|
163
|
+
|
|
164
|
+
// Test with null global object
|
|
165
|
+
assignmentCalled = false;
|
|
166
|
+
const fakeGlobal2 = {};
|
|
167
|
+
mockAssign(null, fakeExtwee);
|
|
168
|
+
|
|
169
|
+
expect(assignmentCalled).toBe(false);
|
|
170
|
+
expect(fakeGlobal2.Extwee).toBeUndefined();
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
mockAssignmentTest();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Import the module to test global assignment in jsdom environment
|
|
6
|
+
import '../../src/Web/web-core.js';
|
|
7
|
+
|
|
8
|
+
describe('web-core.js global assignment in browser environment', () => {
|
|
9
|
+
it('should assign Extwee to window in jsdom environment', () => {
|
|
10
|
+
// Should have assigned to window automatically on import
|
|
11
|
+
expect(window.Extwee).toBeDefined();
|
|
12
|
+
expect(window.Extwee.version).toBe('2.3.3');
|
|
13
|
+
expect(typeof window.Extwee).toBe('object');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should have all expected properties on window.Extwee', () => {
|
|
17
|
+
expect(window.Extwee.parseTwee).toBeDefined();
|
|
18
|
+
expect(window.Extwee.parseJSON).toBeDefined();
|
|
19
|
+
expect(window.Extwee.parseStoryFormat).toBeDefined();
|
|
20
|
+
expect(window.Extwee.parseTwine2HTML).toBeDefined();
|
|
21
|
+
expect(window.Extwee.compileTwine2HTML).toBeDefined();
|
|
22
|
+
expect(window.Extwee.generateIFID).toBeDefined();
|
|
23
|
+
expect(window.Extwee.Story).toBeDefined();
|
|
24
|
+
expect(window.Extwee.Passage).toBeDefined();
|
|
25
|
+
expect(window.Extwee.StoryFormat).toBeDefined();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should verify global object detection logic ran (window branch)', () => {
|
|
29
|
+
// This test verifies that the globalObject detection function found window
|
|
30
|
+
// and assigned Extwee to it
|
|
31
|
+
expect(typeof window).toBe('object');
|
|
32
|
+
expect(window).not.toBeNull();
|
|
33
|
+
expect(window.Extwee).toBeDefined();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should have working functions on window.Extwee', () => {
|
|
37
|
+
// Test that the assigned functions work correctly
|
|
38
|
+
const ifid = window.Extwee.generateIFID();
|
|
39
|
+
expect(ifid).toMatch(/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/);
|
|
40
|
+
|
|
41
|
+
const story = new window.Extwee.Story();
|
|
42
|
+
expect(story).toBeDefined();
|
|
43
|
+
expect(Array.isArray(story.passages)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Test to exercise the globalObject detection function
|
|
47
|
+
it('should test global object detection function directly', () => {
|
|
48
|
+
// This is a test to simulate what the global object detection function does
|
|
49
|
+
// We can't easily mock the environment during module loading, but we can
|
|
50
|
+
// verify the logic by recreating it
|
|
51
|
+
|
|
52
|
+
const globalObjectDetectionLogic = function() {
|
|
53
|
+
if (typeof globalThis !== 'undefined') return 'globalThis';
|
|
54
|
+
if (typeof window !== 'undefined') return 'window';
|
|
55
|
+
if (typeof global !== 'undefined') return 'global';
|
|
56
|
+
if (typeof self !== 'undefined') return 'self';
|
|
57
|
+
return null;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// In jsdom environment, could return 'globalThis' or 'window' depending on Node.js version
|
|
61
|
+
const result = globalObjectDetectionLogic();
|
|
62
|
+
expect(['globalThis', 'window']).toContain(result);
|
|
63
|
+
|
|
64
|
+
// Verify window exists and is truthy
|
|
65
|
+
expect(typeof window).toBe('object');
|
|
66
|
+
expect(window).toBeTruthy();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Test for globalThis availability (Modern browsers/Node.js 12+)
|
|
70
|
+
it('should handle globalThis when available', () => {
|
|
71
|
+
// Test the globalThis branch logic
|
|
72
|
+
if (typeof globalThis !== 'undefined') {
|
|
73
|
+
expect(globalThis).toBeDefined();
|
|
74
|
+
expect(typeof globalThis).toBe('object');
|
|
75
|
+
|
|
76
|
+
// In environments with globalThis, it should be preferred
|
|
77
|
+
const mockGlobalDetection = function() {
|
|
78
|
+
if (typeof globalThis !== 'undefined') return 'globalThis';
|
|
79
|
+
if (typeof window !== 'undefined') return 'window';
|
|
80
|
+
if (typeof global !== 'undefined') return 'global';
|
|
81
|
+
if (typeof self !== 'undefined') return 'self';
|
|
82
|
+
return null;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Should prefer globalThis if available
|
|
86
|
+
const detectionResult = mockGlobalDetection();
|
|
87
|
+
expect(['globalThis', 'window']).toContain(detectionResult);
|
|
88
|
+
} else {
|
|
89
|
+
// If globalThis not available, should fall back to window in jsdom
|
|
90
|
+
expect(typeof window).toBe('object');
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Import to test basic functionality
|
|
6
|
+
import { parseTwee, parseJSON, parseStoryFormat, parseTwine2HTML, compileTwine2HTML, generateIFID, Story, Passage, StoryFormat } from '../../src/Web/web-core.js';
|
|
7
|
+
import Extwee from '../../src/Web/web-core.js';
|
|
8
|
+
|
|
9
|
+
describe('web-core.js Node.js environment tests', () => {
|
|
10
|
+
describe('ES6 module exports', () => {
|
|
11
|
+
it('should export all individual functions and classes', () => {
|
|
12
|
+
expect(parseTwee).toBeDefined();
|
|
13
|
+
expect(typeof parseTwee).toBe('function');
|
|
14
|
+
|
|
15
|
+
expect(parseJSON).toBeDefined();
|
|
16
|
+
expect(typeof parseJSON).toBe('function');
|
|
17
|
+
|
|
18
|
+
expect(parseStoryFormat).toBeDefined();
|
|
19
|
+
expect(typeof parseStoryFormat).toBe('function');
|
|
20
|
+
|
|
21
|
+
expect(parseTwine2HTML).toBeDefined();
|
|
22
|
+
expect(typeof parseTwine2HTML).toBe('function');
|
|
23
|
+
|
|
24
|
+
expect(compileTwine2HTML).toBeDefined();
|
|
25
|
+
expect(typeof compileTwine2HTML).toBe('function');
|
|
26
|
+
|
|
27
|
+
expect(generateIFID).toBeDefined();
|
|
28
|
+
expect(typeof generateIFID).toBe('function');
|
|
29
|
+
|
|
30
|
+
expect(Story).toBeDefined();
|
|
31
|
+
expect(typeof Story).toBe('function');
|
|
32
|
+
|
|
33
|
+
expect(Passage).toBeDefined();
|
|
34
|
+
expect(typeof Passage).toBe('function');
|
|
35
|
+
|
|
36
|
+
expect(StoryFormat).toBeDefined();
|
|
37
|
+
expect(typeof StoryFormat).toBe('function');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should export default Extwee object with all properties', () => {
|
|
41
|
+
expect(Extwee).toBeDefined();
|
|
42
|
+
expect(typeof Extwee).toBe('object');
|
|
43
|
+
|
|
44
|
+
expect(Extwee.parseTwee).toBeDefined();
|
|
45
|
+
expect(Extwee.parseJSON).toBeDefined();
|
|
46
|
+
expect(Extwee.parseStoryFormat).toBeDefined();
|
|
47
|
+
expect(Extwee.parseTwine2HTML).toBeDefined();
|
|
48
|
+
expect(Extwee.compileTwine2HTML).toBeDefined();
|
|
49
|
+
expect(Extwee.generateIFID).toBeDefined();
|
|
50
|
+
expect(Extwee.Story).toBeDefined();
|
|
51
|
+
expect(Extwee.Passage).toBeDefined();
|
|
52
|
+
expect(Extwee.StoryFormat).toBeDefined();
|
|
53
|
+
expect(Extwee.version).toBe('2.3.3');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should have same functions in default export as individual exports', () => {
|
|
57
|
+
expect(Extwee.parseTwee).toBe(parseTwee);
|
|
58
|
+
expect(Extwee.parseJSON).toBe(parseJSON);
|
|
59
|
+
expect(Extwee.parseStoryFormat).toBe(parseStoryFormat);
|
|
60
|
+
expect(Extwee.parseTwine2HTML).toBe(parseTwine2HTML);
|
|
61
|
+
expect(Extwee.compileTwine2HTML).toBe(compileTwine2HTML);
|
|
62
|
+
expect(Extwee.generateIFID).toBe(generateIFID);
|
|
63
|
+
expect(Extwee.Story).toBe(Story);
|
|
64
|
+
expect(Extwee.Passage).toBe(Passage);
|
|
65
|
+
expect(Extwee.StoryFormat).toBe(StoryFormat);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('Functional integration tests', () => {
|
|
70
|
+
it('should have working parseTwee function', () => {
|
|
71
|
+
const tweeContent = `:: Start
|
|
72
|
+
This is the start passage.`;
|
|
73
|
+
|
|
74
|
+
const result = parseTwee(tweeContent);
|
|
75
|
+
expect(result).toBeDefined();
|
|
76
|
+
expect(result.passages).toBeDefined();
|
|
77
|
+
expect(result.passages.length).toBe(1);
|
|
78
|
+
expect(result.passages[0].name).toBe('Start');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should have working parseJSON function', () => {
|
|
82
|
+
const jsonContent = JSON.stringify({
|
|
83
|
+
passages: [{
|
|
84
|
+
name: 'Start',
|
|
85
|
+
text: 'This is the start passage.',
|
|
86
|
+
tags: [],
|
|
87
|
+
metadata: {}
|
|
88
|
+
}],
|
|
89
|
+
name: 'Test Story',
|
|
90
|
+
IFID: '12345678-1234-5678-9012-123456789012'
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const result = parseJSON(jsonContent);
|
|
94
|
+
expect(result).toBeDefined();
|
|
95
|
+
expect(result.passages).toBeDefined();
|
|
96
|
+
expect(result.passages.length).toBe(1);
|
|
97
|
+
expect(result.name).toBe('Test Story');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should have working generateIFID function', () => {
|
|
101
|
+
const ifid = generateIFID();
|
|
102
|
+
expect(ifid).toMatch(/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should have working Story constructor', () => {
|
|
106
|
+
const story = new Story();
|
|
107
|
+
expect(story).toBeDefined();
|
|
108
|
+
expect(story.passages).toBeDefined();
|
|
109
|
+
expect(Array.isArray(story.passages)).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should have working Passage constructor', () => {
|
|
113
|
+
const passage = new Passage();
|
|
114
|
+
expect(passage).toBeDefined();
|
|
115
|
+
expect(passage.name).toBeDefined();
|
|
116
|
+
expect(passage.text).toBeDefined();
|
|
117
|
+
expect(passage.tags).toBeDefined();
|
|
118
|
+
expect(Array.isArray(passage.tags)).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should have working StoryFormat constructor', () => {
|
|
122
|
+
const storyFormat = new StoryFormat();
|
|
123
|
+
expect(storyFormat).toBeDefined();
|
|
124
|
+
expect(storyFormat.name).toBeDefined();
|
|
125
|
+
expect(storyFormat.version).toBeDefined();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('Global assignment in Node.js environment', () => {
|
|
130
|
+
it('should assign to global.global in Node.js environment', () => {
|
|
131
|
+
// In Node.js environment, it should assign to global.global
|
|
132
|
+
expect(global.global.Extwee).toBeDefined();
|
|
133
|
+
expect(global.global.Extwee.version).toBe('2.3.3');
|
|
134
|
+
expect(global.global.Extwee).toBe(Extwee);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should verify global object detection logic execution', () => {
|
|
138
|
+
// This test ensures the global object detection function runs
|
|
139
|
+
// In Node.js, typeof globalThis is 'undefined' (in older versions),
|
|
140
|
+
// typeof window is 'undefined', typeof global is 'object', typeof self is 'undefined'
|
|
141
|
+
expect(typeof global).toBe('object');
|
|
142
|
+
expect(global.global).toBeDefined();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('Version consistency', () => {
|
|
147
|
+
it('should have consistent version across exports', () => {
|
|
148
|
+
expect(Extwee.version).toBe('2.3.3');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should have version property as string', () => {
|
|
152
|
+
expect(typeof Extwee.version).toBe('string');
|
|
153
|
+
expect(Extwee.version.length).toBeGreaterThan(0);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -2,41 +2,53 @@
|
|
|
2
2
|
* @jest-environment jsdom
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Load the core web module to set up window.Extwee
|
|
6
|
+
import '../../src/Web/web-core.js';
|
|
7
|
+
|
|
8
|
+
// Load additional modular parsers
|
|
9
|
+
import '../../src/Web/web-twine1html.js';
|
|
10
|
+
import '../../src/Web/web-twine2archive.js';
|
|
11
|
+
import '../../src/Web/web-tws.js';
|
|
6
12
|
|
|
7
13
|
describe('Extwee', () => {
|
|
8
|
-
it('should have all the expected properties', () => {
|
|
14
|
+
it('should have all the expected immediate properties', () => {
|
|
15
|
+
// Core functionality that's immediately available
|
|
9
16
|
expect(window.Extwee).toHaveProperty('parseTwee');
|
|
10
17
|
expect(window.Extwee).toHaveProperty('parseJSON');
|
|
11
|
-
expect(window.Extwee).toHaveProperty('parseTWS');
|
|
12
18
|
expect(window.Extwee).toHaveProperty('parseStoryFormat');
|
|
13
|
-
expect(window.Extwee).toHaveProperty('parseTwine1HTML');
|
|
14
19
|
expect(window.Extwee).toHaveProperty('parseTwine2HTML');
|
|
15
|
-
expect(window.Extwee).toHaveProperty('parseTwine2ArchiveHTML');
|
|
16
|
-
expect(window.Extwee).toHaveProperty('compileTwine1HTML');
|
|
17
20
|
expect(window.Extwee).toHaveProperty('compileTwine2HTML');
|
|
18
|
-
expect(window.Extwee).toHaveProperty('compileTwine2ArchiveHTML');
|
|
19
21
|
expect(window.Extwee).toHaveProperty('generateIFID');
|
|
20
22
|
expect(window.Extwee).toHaveProperty('Story');
|
|
21
23
|
expect(window.Extwee).toHaveProperty('Passage');
|
|
22
24
|
expect(window.Extwee).toHaveProperty('StoryFormat');
|
|
25
|
+
|
|
26
|
+
// Additional parsers loaded via modular imports
|
|
27
|
+
expect(window.Extwee).toHaveProperty('parseTwine1HTML');
|
|
28
|
+
expect(window.Extwee).toHaveProperty('parseTwine2ArchiveHTML');
|
|
29
|
+
expect(window.Extwee).toHaveProperty('parseTWS');
|
|
30
|
+
expect(window.Extwee).toHaveProperty('compileTwine1HTML');
|
|
31
|
+
expect(window.Extwee).toHaveProperty('compileTwine2ArchiveHTML');
|
|
23
32
|
});
|
|
24
33
|
|
|
25
|
-
it('should have the expected types', () => {
|
|
34
|
+
it('should have the expected types for immediate functions', () => {
|
|
35
|
+
// Core functionality
|
|
26
36
|
expect(typeof window.Extwee.parseTwee).toBe('function');
|
|
27
37
|
expect(typeof window.Extwee.parseJSON).toBe('function');
|
|
28
|
-
expect(typeof window.Extwee.parseTWS).toBe('function');
|
|
29
38
|
expect(typeof window.Extwee.parseStoryFormat).toBe('function');
|
|
30
|
-
expect(typeof window.Extwee.parseTwine1HTML).toBe('function');
|
|
31
39
|
expect(typeof window.Extwee.parseTwine2HTML).toBe('function');
|
|
32
|
-
expect(typeof window.Extwee.parseTwine2ArchiveHTML).toBe('function');
|
|
33
|
-
expect(typeof window.Extwee.compileTwine1HTML).toBe('function');
|
|
34
40
|
expect(typeof window.Extwee.compileTwine2HTML).toBe('function');
|
|
35
|
-
expect(typeof window.Extwee.compileTwine2ArchiveHTML).toBe('function');
|
|
36
41
|
expect(typeof window.Extwee.generateIFID).toBe('function');
|
|
37
42
|
expect(typeof window.Extwee.Story).toBe('function');
|
|
38
43
|
expect(typeof window.Extwee.Passage).toBe('function');
|
|
39
44
|
expect(typeof window.Extwee.StoryFormat).toBe('function');
|
|
45
|
+
|
|
46
|
+
// Additional parsers
|
|
47
|
+
expect(typeof window.Extwee.parseTwine1HTML).toBe('function');
|
|
48
|
+
expect(typeof window.Extwee.parseTwine2ArchiveHTML).toBe('function');
|
|
49
|
+
expect(typeof window.Extwee.parseTWS).toBe('function');
|
|
50
|
+
expect(typeof window.Extwee.compileTwine1HTML).toBe('function');
|
|
51
|
+
expect(typeof window.Extwee.compileTwine2ArchiveHTML).toBe('function');
|
|
40
52
|
});
|
|
41
53
|
|
|
42
54
|
it('should have the expected properties in StoryFormat', () => {
|
package/types/src/Story.d.ts
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web-optimized Twine 1 HTML parser with reduced dependencies
|
|
3
|
+
* Parses Twine 1 HTML into a Story object using lightweight DOM parsing
|
|
4
|
+
* @see {@link https://github.com/iftechfoundation/twine-specs/blob/master/twine-1-htmloutput-doc.md Twine 1 HTML Documentation}
|
|
5
|
+
* @function parse
|
|
6
|
+
* @param {string} content - Twine 1 HTML content to parse.
|
|
7
|
+
* @returns {Story} Story object
|
|
8
|
+
*/
|
|
9
|
+
export function parse(content: string): Story;
|
|
10
|
+
import { Story } from '../Story.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web-optimized Twine 2 Archive HTML parser with reduced dependencies
|
|
3
|
+
* Parse Twine 2 Archive HTML and returns an array of story objects using browser DOM APIs.
|
|
4
|
+
* @see {@link https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-archive-spec.md Twine 2 Archive Specification}
|
|
5
|
+
* @function parse
|
|
6
|
+
* @param {string} content - Content to parse for Twine 2 HTML elements.
|
|
7
|
+
* @throws {TypeError} - Content is not a string!
|
|
8
|
+
* @returns {Array} Array of stories found in content.
|
|
9
|
+
* @example
|
|
10
|
+
* const content = '<tw-storydata name="Untitled" startnode="1" creator="Twine" creator-version="2.3.9" ifid="A1B2C3D4-E5F6-G7H8-I9J0-K1L2M3N4O5P6" zoom="1" format="Harlowe" format-version="3.1.0" options="" 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="Untitled Passage" tags="" position="0,0" size="100,100"></tw-passagedata></tw-storydata>';
|
|
11
|
+
* console.log(parse(content));
|
|
12
|
+
* // => [
|
|
13
|
+
* // Story {
|
|
14
|
+
* // name: 'Untitled',
|
|
15
|
+
* // startnode: '1',
|
|
16
|
+
* // creator: 'Twine',
|
|
17
|
+
* // creatorVersion: '2.3.9',
|
|
18
|
+
* // ifid: 'A1B2C3D4-E5F6-G7H8-I9J0-K1L2M3N4O5P6',
|
|
19
|
+
* // zoom: '1',
|
|
20
|
+
* // format: 'Harlowe',
|
|
21
|
+
* // formatVersion: '3.1.0',
|
|
22
|
+
* // options: '',
|
|
23
|
+
* // hidden: '',
|
|
24
|
+
* // passages: [
|
|
25
|
+
* // Passage {
|
|
26
|
+
* // pid: '1',
|
|
27
|
+
* // name: 'Untitled Passage',
|
|
28
|
+
* // tags: '',
|
|
29
|
+
* // position: '0,0',
|
|
30
|
+
* // size: '100,100',
|
|
31
|
+
* // text: ''
|
|
32
|
+
* // }
|
|
33
|
+
* // ]
|
|
34
|
+
* // }
|
|
35
|
+
* // ]
|
|
36
|
+
*/
|
|
37
|
+
export function parse(content: string): any[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web-optimized Twine 2 HTML parser with reduced dependencies
|
|
3
|
+
* Parse Twine 2 HTML into Story object using lightweight DOM parsing
|
|
4
|
+
*
|
|
5
|
+
* See: Twine 2 HTML Output Specification
|
|
6
|
+
* (https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-htmloutput-spec.md)
|
|
7
|
+
*
|
|
8
|
+
* Produces warnings for:
|
|
9
|
+
* - Missing name attribute on `<tw-storydata>` element.
|
|
10
|
+
* - Missing IFID attribute on `<tw-storydata>` element.
|
|
11
|
+
* - Malformed IFID attribute on `<tw-storydata>` element.
|
|
12
|
+
* @function parse
|
|
13
|
+
* @param {string} content - Twine 2 HTML content to parse.
|
|
14
|
+
* @returns {Story} Story object based on Twine 2 HTML content.
|
|
15
|
+
* @throws {TypeError} Content is not a string.
|
|
16
|
+
* @throws {Error} Not Twine 2 HTML content!
|
|
17
|
+
* @throws {Error} Cannot parse passage data without name!
|
|
18
|
+
* @throws {Error} Passages are required to have PID!
|
|
19
|
+
*/
|
|
20
|
+
export function parse(content: string): Story;
|
|
21
|
+
import { Story } from '../Story.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encode HTML entities
|
|
3
|
+
* @param {string} str - String to encode
|
|
4
|
+
* @returns {string} Encoded string
|
|
5
|
+
*/
|
|
6
|
+
export function encode(str: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Decode HTML entities
|
|
9
|
+
* @param {string} str - String to decode
|
|
10
|
+
* @returns {string} Decoded string
|
|
11
|
+
*/
|
|
12
|
+
export function decode(str: string): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight semantic version validation for web builds
|
|
3
|
+
* This replaces the full semver package to reduce bundle size
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Validates if a string is a valid semantic version
|
|
7
|
+
* @param {string} version - Version string to validate
|
|
8
|
+
* @returns {string|null} Returns the version if valid, null if invalid
|
|
9
|
+
*/
|
|
10
|
+
export function valid(version: string): string | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export default Extwee;
|
|
2
|
+
import { parse as parseTwee } from '../Twee/parse.js';
|
|
3
|
+
import { parse as parseJSON } from '../JSON/parse.js';
|
|
4
|
+
import { parse as parseStoryFormat } from '../StoryFormat/parse.js';
|
|
5
|
+
import { parse as parseTwine2HTML } from '../Twine2HTML/parse-web.js';
|
|
6
|
+
import { compile as compileTwine2HTML } from '../Twine2HTML/compile.js';
|
|
7
|
+
import { generate as generateIFID } from '../IFID/generate.js';
|
|
8
|
+
import { Story } from '../Story.js';
|
|
9
|
+
import Passage from '../Passage.js';
|
|
10
|
+
import StoryFormat from '../StoryFormat.js';
|
|
11
|
+
declare namespace Extwee {
|
|
12
|
+
export { parseTwee };
|
|
13
|
+
export { parseJSON };
|
|
14
|
+
export { parseStoryFormat };
|
|
15
|
+
export { parseTwine2HTML };
|
|
16
|
+
export { compileTwine2HTML };
|
|
17
|
+
export { generateIFID };
|
|
18
|
+
export { Story };
|
|
19
|
+
export { Passage };
|
|
20
|
+
export { StoryFormat };
|
|
21
|
+
export let version: string;
|
|
22
|
+
}
|
|
23
|
+
export { parseTwee, parseJSON, parseStoryFormat, parseTwine2HTML, compileTwine2HTML, generateIFID, Story, Passage, StoryFormat };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default Extwee;
|
|
2
|
+
declare namespace Extwee {
|
|
3
|
+
export { parseTwine1HTML };
|
|
4
|
+
export { compileTwine1HTML };
|
|
5
|
+
export { parseTwine1HTML as parse };
|
|
6
|
+
export { compileTwine1HTML as compile };
|
|
7
|
+
}
|
|
8
|
+
import { parse as parseTwine1HTML } from '../Twine1HTML/parse-web.js';
|
|
9
|
+
import { compile as compileTwine1HTML } from '../Twine1HTML/compile.js';
|
|
10
|
+
export { parseTwine1HTML as parse, compileTwine1HTML as compile };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default Extwee;
|
|
2
|
+
declare namespace Extwee {
|
|
3
|
+
export { parseTwine2ArchiveHTML };
|
|
4
|
+
export { compileTwine2ArchiveHTML };
|
|
5
|
+
export { parseTwine2ArchiveHTML as parse };
|
|
6
|
+
export { compileTwine2ArchiveHTML as compile };
|
|
7
|
+
}
|
|
8
|
+
import { parse as parseTwine2ArchiveHTML } from '../Twine2ArchiveHTML/parse-web.js';
|
|
9
|
+
import { compile as compileTwine2ArchiveHTML } from '../Twine2ArchiveHTML/compile.js';
|
|
10
|
+
export { parseTwine2ArchiveHTML as parse, compileTwine2ArchiveHTML as compile };
|