extwee 2.3.1 → 2.3.3
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/eslint.config.js +4 -1
- package/package.json +20 -17
- 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 +428 -0
- package/src/Web/web-core.js +31 -0
- package/src/Web/web-index.js +31 -0
- package/src/Web/web-twine1html.js +15 -0
- package/src/Web/web-twine2archive.js +15 -0
- package/src/Web/web-tws.js +12 -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/Twine2ArchiveHTML/Twine2ArchiveHTML.Parse.test.js +1 -0
- package/test/Twine2HTML/Twine2HTML.Parse.test.js +1 -0
- package/test/Web/window.Extwee.test.js +20 -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 +1 -0
- package/types/src/Web/web-index.d.ts +1 -0
- package/types/src/Web/web-twine1html.d.ts +3 -0
- package/types/src/Web/web-twine2archive.d.ts +3 -0
- package/types/src/Web/web-tws.d.ts +2 -0
- package/webpack.config.js +22 -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
|
@@ -1,13 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isDirectory } from "../../src/CLI/isDirectory.js";
|
|
3
|
-
import { isFile } from "../../src/CLI/isFile.js";
|
|
4
|
-
import { readDirectories } from "../../src/CLI/ProcessConfig/readDirectories.js";
|
|
5
|
-
import { readFileSync } from "node:fs";
|
|
1
|
+
import {jest} from '@jest/globals';
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
jest.
|
|
9
|
-
jest.
|
|
10
|
-
jest.
|
|
3
|
+
// Mock all dependencies before importing anything that uses them
|
|
4
|
+
const mockIsDirectory = jest.fn();
|
|
5
|
+
const mockIsFile = jest.fn();
|
|
6
|
+
const mockReadDirectories = jest.fn();
|
|
7
|
+
const mockReadFileSync = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.unstable_mockModule("../../src/CLI/isDirectory.js", () => ({
|
|
10
|
+
isDirectory: mockIsDirectory
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
jest.unstable_mockModule("../../src/CLI/isFile.js", () => ({
|
|
14
|
+
isFile: mockIsFile
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
jest.unstable_mockModule("../../src/CLI/ProcessConfig/readDirectories.js", () => ({
|
|
18
|
+
readDirectories: mockReadDirectories
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
jest.unstable_mockModule("node:fs", () => ({
|
|
22
|
+
readFileSync: mockReadFileSync
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
// Now import the modules that depend on the mocked modules
|
|
26
|
+
const { loadStoryFormat } = await import("../../src/CLI/ProcessConfig/loadStoryFormat.js");
|
|
11
27
|
|
|
12
28
|
describe("loadStoryFormat", () => {
|
|
13
29
|
afterEach(() => {
|
|
@@ -15,7 +31,7 @@ describe("loadStoryFormat", () => {
|
|
|
15
31
|
});
|
|
16
32
|
|
|
17
33
|
it("should throw an error if the story-formats directory does not exist", () => {
|
|
18
|
-
|
|
34
|
+
mockIsDirectory.mockReturnValueOnce(false);
|
|
19
35
|
|
|
20
36
|
expect(() => loadStoryFormat("Harlowe", "3.2.0")).toThrow(
|
|
21
37
|
"Error: story-formats directory does not exist. Consider running 'npx sfa-get' to download the latest story formats."
|
|
@@ -23,7 +39,7 @@ describe("loadStoryFormat", () => {
|
|
|
23
39
|
});
|
|
24
40
|
|
|
25
41
|
it("should throw an error if the named story format directory does not exist", () => {
|
|
26
|
-
|
|
42
|
+
mockIsDirectory.mockReturnValueOnce(true).mockReturnValueOnce(false);
|
|
27
43
|
|
|
28
44
|
expect(() => loadStoryFormat("Harlowe", "3.2.0")).toThrow(
|
|
29
45
|
"Error: story format Harlowe does not exist in the story-formats directory."
|
|
@@ -31,7 +47,7 @@ describe("loadStoryFormat", () => {
|
|
|
31
47
|
});
|
|
32
48
|
|
|
33
49
|
it("should throw an error if the version directory does not exist", () => {
|
|
34
|
-
|
|
50
|
+
mockIsDirectory.mockReturnValueOnce(true).mockReturnValueOnce(true).mockReturnValueOnce(false);
|
|
35
51
|
|
|
36
52
|
expect(() => loadStoryFormat("Harlowe", "3.2.0")).toThrow(
|
|
37
53
|
"Error: story format Harlowe version 3.2.0 does not exist in the story-formats directory."
|
|
@@ -39,8 +55,8 @@ describe("loadStoryFormat", () => {
|
|
|
39
55
|
});
|
|
40
56
|
|
|
41
57
|
it("should throw an error if the format.js file does not exist in the version directory", () => {
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
mockIsDirectory.mockReturnValueOnce(true).mockReturnValueOnce(true).mockReturnValueOnce(true);
|
|
59
|
+
mockIsFile.mockReturnValueOnce(false);
|
|
44
60
|
|
|
45
61
|
expect(() => loadStoryFormat("Harlowe", "3.2.0")).toThrow(
|
|
46
62
|
"Error: story format Harlowe version 3.2.0 does not have a format.js file."
|
|
@@ -48,29 +64,29 @@ describe("loadStoryFormat", () => {
|
|
|
48
64
|
});
|
|
49
65
|
|
|
50
66
|
it("should return the contents of the format.js file if all checks pass", () => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
67
|
+
mockIsDirectory.mockReturnValueOnce(true).mockReturnValueOnce(true).mockReturnValueOnce(true);
|
|
68
|
+
mockIsFile.mockReturnValueOnce(true);
|
|
69
|
+
mockReadFileSync.mockReturnValueOnce("format.js content");
|
|
54
70
|
|
|
55
71
|
const result = loadStoryFormat("Harlowe", "3.2.0");
|
|
56
72
|
expect(result).toBe("format.js content");
|
|
57
73
|
});
|
|
58
74
|
|
|
59
75
|
it("should handle 'latest' version and return the contents of the format.js file", () => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
76
|
+
mockIsDirectory.mockReturnValueOnce(true).mockReturnValueOnce(true);
|
|
77
|
+
mockIsFile.mockReturnValueOnce(false).mockReturnValueOnce(true);
|
|
78
|
+
mockReadDirectories.mockReturnValueOnce(["3.2.0", "3.1.0"]);
|
|
79
|
+
mockReadFileSync.mockReturnValueOnce("latest format.js content");
|
|
64
80
|
|
|
65
81
|
const result = loadStoryFormat("Harlowe", "latest");
|
|
66
82
|
expect(result).toBe("latest format.js content");
|
|
67
|
-
expect(
|
|
83
|
+
expect(mockReadDirectories).toHaveBeenCalledWith("story-formats/Harlowe");
|
|
68
84
|
});
|
|
69
85
|
|
|
70
86
|
it("should throw an error if 'latest' version has no format.js file", () => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
mockIsDirectory.mockReturnValueOnce(true).mockReturnValueOnce(true);
|
|
88
|
+
mockIsFile.mockReturnValueOnce(false).mockReturnValueOnce(false);
|
|
89
|
+
mockReadDirectories.mockReturnValueOnce(["3.2.0", "3.1.0"]);
|
|
74
90
|
|
|
75
91
|
expect(() => loadStoryFormat("Harlowe", "latest")).toThrow(
|
|
76
92
|
"Error: story format Harlowe version latest does not have a format.js file."
|
|
@@ -78,20 +94,20 @@ describe("loadStoryFormat", () => {
|
|
|
78
94
|
});
|
|
79
95
|
|
|
80
96
|
it("should read format.js file from the story format directory if it exists", () => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
mockIsDirectory.mockReturnValueOnce(true).mockReturnValueOnce(true);
|
|
98
|
+
mockIsFile.mockReturnValueOnce(true);
|
|
99
|
+
mockReadDirectories.mockReturnValueOnce([]);
|
|
100
|
+
mockReadFileSync.mockReturnValueOnce("latest format.js content");
|
|
85
101
|
|
|
86
102
|
const result = loadStoryFormat("Harlowe", "latest");
|
|
87
103
|
expect(result).toBe("latest format.js content");
|
|
88
|
-
expect(
|
|
104
|
+
expect(mockReadFileSync).toHaveBeenCalledWith("story-formats/Harlowe/format.js", "utf-8");
|
|
89
105
|
});
|
|
90
106
|
|
|
91
107
|
it("should throw an error if the story format version is not 'latest' and version directories do not exist", () => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
108
|
+
mockIsDirectory.mockReturnValueOnce(true).mockReturnValueOnce(true);
|
|
109
|
+
mockIsFile.mockReturnValueOnce(false);
|
|
110
|
+
mockReadDirectories.mockReturnValueOnce([]);
|
|
95
111
|
|
|
96
112
|
expect(() => loadStoryFormat("Harlowe", "latest")).toThrow(
|
|
97
113
|
`Error: story format Harlowe does not have any version directories.`
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { readdirSync } from 'node:fs';
|
|
3
|
-
import { isDirectory } from '../../src/CLI/isDirectory.js';
|
|
1
|
+
import {jest} from '@jest/globals';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
jest.
|
|
3
|
+
// Mock the fs module and isDirectory before importing anything that uses them
|
|
4
|
+
const mockReaddirSync = jest.fn();
|
|
5
|
+
const mockIsDirectory = jest.fn();
|
|
6
|
+
|
|
7
|
+
jest.unstable_mockModule('node:fs', () => ({
|
|
8
|
+
readdirSync: mockReaddirSync
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
jest.unstable_mockModule('../../src/CLI/isDirectory.js', () => ({
|
|
12
|
+
isDirectory: mockIsDirectory
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
// Now import the modules that depend on the mocked modules
|
|
16
|
+
const { readDirectories } = await import('../../src/CLI/ProcessConfig/readDirectories.js');
|
|
7
17
|
|
|
8
18
|
describe('readDirectories', () => {
|
|
9
19
|
afterEach(() => {
|
|
@@ -12,7 +22,7 @@ describe('readDirectories', () => {
|
|
|
12
22
|
|
|
13
23
|
it('should return an empty array and log an error if the directory does not exist', () => {
|
|
14
24
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
15
|
-
|
|
25
|
+
mockIsDirectory.mockReturnValue(false);
|
|
16
26
|
|
|
17
27
|
const result = readDirectories('/nonexistent');
|
|
18
28
|
|
|
@@ -23,8 +33,8 @@ describe('readDirectories', () => {
|
|
|
23
33
|
|
|
24
34
|
it('should return an empty array and log an error if readdirSync throws an error', () => {
|
|
25
35
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
mockIsDirectory.mockReturnValue(true);
|
|
37
|
+
mockReaddirSync.mockImplementation(() => {
|
|
28
38
|
throw new Error('Permission denied');
|
|
29
39
|
});
|
|
30
40
|
|
|
@@ -36,8 +46,8 @@ describe('readDirectories', () => {
|
|
|
36
46
|
});
|
|
37
47
|
|
|
38
48
|
it('should return an empty array if the directory is empty', () => {
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
mockIsDirectory.mockReturnValue(true);
|
|
50
|
+
mockReaddirSync.mockReturnValue([]);
|
|
41
51
|
|
|
42
52
|
const result = readDirectories('/empty');
|
|
43
53
|
|
|
@@ -45,10 +55,10 @@ describe('readDirectories', () => {
|
|
|
45
55
|
});
|
|
46
56
|
|
|
47
57
|
it('should return an array of directories', () => {
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
mockIsDirectory.mockReturnValue(true);
|
|
59
|
+
mockReaddirSync.mockReturnValue(['dir1', 'file1', 'dir2']);
|
|
50
60
|
|
|
51
|
-
|
|
61
|
+
mockIsDirectory.mockImplementation((path) => {
|
|
52
62
|
return path === '/test/dir1' || path === '/test/dir2';
|
|
53
63
|
});
|
|
54
64
|
|
|
@@ -58,8 +68,8 @@ describe('readDirectories', () => {
|
|
|
58
68
|
});
|
|
59
69
|
|
|
60
70
|
it('should return an empty array if the result is not an array', () => {
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
mockIsDirectory.mockReturnValue(true);
|
|
72
|
+
mockReaddirSync.mockReturnValue('not an array');
|
|
63
73
|
|
|
64
74
|
const result = readDirectories('/test');
|
|
65
75
|
|
|
@@ -2,41 +2,48 @@
|
|
|
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-index.js';
|
|
6
7
|
|
|
7
8
|
describe('Extwee', () => {
|
|
8
|
-
it('should have all the expected properties', () => {
|
|
9
|
+
it('should have all the expected immediate properties', () => {
|
|
10
|
+
// Core functionality that's immediately available
|
|
9
11
|
expect(window.Extwee).toHaveProperty('parseTwee');
|
|
10
12
|
expect(window.Extwee).toHaveProperty('parseJSON');
|
|
11
|
-
expect(window.Extwee).toHaveProperty('parseTWS');
|
|
12
13
|
expect(window.Extwee).toHaveProperty('parseStoryFormat');
|
|
13
|
-
expect(window.Extwee).toHaveProperty('parseTwine1HTML');
|
|
14
14
|
expect(window.Extwee).toHaveProperty('parseTwine2HTML');
|
|
15
|
-
expect(window.Extwee).toHaveProperty('parseTwine2ArchiveHTML');
|
|
16
|
-
expect(window.Extwee).toHaveProperty('compileTwine1HTML');
|
|
17
15
|
expect(window.Extwee).toHaveProperty('compileTwine2HTML');
|
|
18
|
-
expect(window.Extwee).toHaveProperty('compileTwine2ArchiveHTML');
|
|
19
16
|
expect(window.Extwee).toHaveProperty('generateIFID');
|
|
20
17
|
expect(window.Extwee).toHaveProperty('Story');
|
|
21
18
|
expect(window.Extwee).toHaveProperty('Passage');
|
|
22
19
|
expect(window.Extwee).toHaveProperty('StoryFormat');
|
|
20
|
+
|
|
21
|
+
// Additional parsers available in unified build
|
|
22
|
+
expect(window.Extwee).toHaveProperty('parseTwine1HTML');
|
|
23
|
+
expect(window.Extwee).toHaveProperty('parseTwine2ArchiveHTML');
|
|
24
|
+
expect(window.Extwee).toHaveProperty('parseTWS');
|
|
25
|
+
expect(window.Extwee).toHaveProperty('compileTwine1HTML');
|
|
26
|
+
expect(window.Extwee).toHaveProperty('compileTwine2ArchiveHTML');
|
|
23
27
|
});
|
|
24
28
|
|
|
25
|
-
it('should have the expected types', () => {
|
|
29
|
+
it('should have the expected types for immediate functions', () => {
|
|
30
|
+
// Core functionality
|
|
26
31
|
expect(typeof window.Extwee.parseTwee).toBe('function');
|
|
27
32
|
expect(typeof window.Extwee.parseJSON).toBe('function');
|
|
28
|
-
expect(typeof window.Extwee.parseTWS).toBe('function');
|
|
29
33
|
expect(typeof window.Extwee.parseStoryFormat).toBe('function');
|
|
30
|
-
expect(typeof window.Extwee.parseTwine1HTML).toBe('function');
|
|
31
34
|
expect(typeof window.Extwee.parseTwine2HTML).toBe('function');
|
|
32
|
-
expect(typeof window.Extwee.parseTwine2ArchiveHTML).toBe('function');
|
|
33
|
-
expect(typeof window.Extwee.compileTwine1HTML).toBe('function');
|
|
34
35
|
expect(typeof window.Extwee.compileTwine2HTML).toBe('function');
|
|
35
|
-
expect(typeof window.Extwee.compileTwine2ArchiveHTML).toBe('function');
|
|
36
36
|
expect(typeof window.Extwee.generateIFID).toBe('function');
|
|
37
37
|
expect(typeof window.Extwee.Story).toBe('function');
|
|
38
38
|
expect(typeof window.Extwee.Passage).toBe('function');
|
|
39
39
|
expect(typeof window.Extwee.StoryFormat).toBe('function');
|
|
40
|
+
|
|
41
|
+
// Additional parsers
|
|
42
|
+
expect(typeof window.Extwee.parseTwine1HTML).toBe('function');
|
|
43
|
+
expect(typeof window.Extwee.parseTwine2ArchiveHTML).toBe('function');
|
|
44
|
+
expect(typeof window.Extwee.parseTWS).toBe('function');
|
|
45
|
+
expect(typeof window.Extwee.compileTwine1HTML).toBe('function');
|
|
46
|
+
expect(typeof window.Extwee.compileTwine2ArchiveHTML).toBe('function');
|
|
40
47
|
});
|
|
41
48
|
|
|
42
49
|
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 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/webpack.config.js
CHANGED
|
@@ -3,10 +3,30 @@ import path from 'node:path';
|
|
|
3
3
|
export default {
|
|
4
4
|
mode: 'production',
|
|
5
5
|
entry: {
|
|
6
|
-
|
|
6
|
+
// Core bundle with most common functionality
|
|
7
|
+
'extwee.core': './src/Web/web-core.js',
|
|
8
|
+
// Individual parser modules
|
|
9
|
+
'extwee.twine1html': './src/Web/web-twine1html.js',
|
|
10
|
+
'extwee.twine2archive': './src/Web/web-twine2archive.js',
|
|
11
|
+
'extwee.tws': './src/Web/web-tws.js'
|
|
7
12
|
},
|
|
8
13
|
output: {
|
|
9
14
|
path: path.resolve('./', 'build'),
|
|
10
|
-
filename: '[name].js',
|
|
15
|
+
filename: '[name].min.js',
|
|
16
|
+
library: {
|
|
17
|
+
type: 'umd',
|
|
18
|
+
name: 'Extwee' // Use a single library name for all modules
|
|
19
|
+
},
|
|
20
|
+
globalObject: 'this'
|
|
11
21
|
},
|
|
22
|
+
optimization: {
|
|
23
|
+
usedExports: true,
|
|
24
|
+
sideEffects: false,
|
|
25
|
+
splitChunks: false // Don't split chunks for individual modules
|
|
26
|
+
},
|
|
27
|
+
performance: {
|
|
28
|
+
maxAssetSize: 250000, // 244 KiB
|
|
29
|
+
maxEntrypointSize: 250000,
|
|
30
|
+
hints: 'warning'
|
|
31
|
+
}
|
|
12
32
|
};
|