@textadventures/squiffy-cli 6.0.0-alpha.20 → 6.0.0-beta.0
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/LICENSE +1 -1
- package/dist/external-files.js +4 -2
- package/dist/file-packager.d.ts +2 -1
- package/dist/file-packager.js +3 -3
- package/dist/file-packager.test.d.ts +1 -0
- package/dist/file-packager.test.js +179 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/squiffy.js +7 -2
- package/package.json +19 -8
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2014-
|
|
3
|
+
Copyright (c) 2014-2026 Alex Warren, textadventures.co.uk and contributors
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/dist/external-files.js
CHANGED
|
@@ -11,8 +11,10 @@ export const externalFiles = (inputFilename) => {
|
|
|
11
11
|
return result.filter((filename) => !includedFiles.includes(filename));
|
|
12
12
|
},
|
|
13
13
|
getContent: async (filename) => {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// Resolve relative paths against the base path
|
|
15
|
+
const resolvedPath = path.isAbsolute(filename) ? filename : path.join(basePath, filename);
|
|
16
|
+
includedFiles.push(resolvedPath);
|
|
17
|
+
return (await fs.readFile(resolvedPath)).toString();
|
|
16
18
|
},
|
|
17
19
|
getLocalFilename(filename) {
|
|
18
20
|
return path.relative(basePath, filename);
|
package/dist/file-packager.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
import { PackageOptions } from "@textadventures/squiffy-packager";
|
|
1
2
|
export declare const writeScriptFile: (inputFilename: string, outputPath: string, outputFilename: string) => Promise<false | undefined>;
|
|
2
|
-
export declare const createPackageFiles: (inputFilename: string, outputPath: string,
|
|
3
|
+
export declare const createPackageFiles: (inputFilename: string, outputPath: string, options?: PackageOptions) => Promise<boolean>;
|
package/dist/file-packager.js
CHANGED
|
@@ -24,20 +24,20 @@ export const writeScriptFile = async (inputFilename, outputPath, outputFilename)
|
|
|
24
24
|
console.log(`Writing ${outputFilename}`);
|
|
25
25
|
fs.writeFileSync(path.join(outputPath, outputFilename), await result.getJs());
|
|
26
26
|
};
|
|
27
|
-
export const createPackageFiles = async (inputFilename, outputPath,
|
|
27
|
+
export const createPackageFiles = async (inputFilename, outputPath, options = {}) => {
|
|
28
28
|
console.log("Loading " + inputFilename);
|
|
29
29
|
const result = await getCompileResult(inputFilename);
|
|
30
30
|
if (!result.success) {
|
|
31
31
|
console.log("Failed.");
|
|
32
32
|
return false;
|
|
33
33
|
}
|
|
34
|
-
const pkg = await createPackage(result,
|
|
34
|
+
const pkg = await createPackage(result, options);
|
|
35
35
|
const files = pkg.files;
|
|
36
36
|
for (const file of Object.keys(files)) {
|
|
37
37
|
console.log(`Writing ${file}`);
|
|
38
38
|
fs.writeFileSync(path.join(outputPath, file), files[file]);
|
|
39
39
|
}
|
|
40
|
-
if (createZip && pkg.zip) {
|
|
40
|
+
if (options.createZip && pkg.zip) {
|
|
41
41
|
console.log("Writing output.zip");
|
|
42
42
|
fs.writeFileSync(path.join(outputPath, "output.zip"), pkg.zip);
|
|
43
43
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { writeScriptFile, createPackageFiles } from "./file-packager.js";
|
|
3
|
+
import { externalFiles } from "./external-files.js";
|
|
4
|
+
import fs from "fs/promises";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
// Test fixtures directory
|
|
9
|
+
const TEST_DIR = path.join(__dirname, "..", "test-fixtures");
|
|
10
|
+
const OUTPUT_DIR = path.join(TEST_DIR, "output");
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
// Create test directories
|
|
13
|
+
await fs.mkdir(TEST_DIR, { recursive: true });
|
|
14
|
+
await fs.mkdir(OUTPUT_DIR, { recursive: true });
|
|
15
|
+
});
|
|
16
|
+
afterEach(async () => {
|
|
17
|
+
// Clean up all test files
|
|
18
|
+
try {
|
|
19
|
+
await fs.rm(TEST_DIR, { recursive: true, force: true });
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Ignore cleanup errors
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
describe("file-packager", () => {
|
|
26
|
+
test("writeScriptFile creates JavaScript file", async () => {
|
|
27
|
+
// Create a simple test story
|
|
28
|
+
const testStory = path.join(TEST_DIR, "simple.squiffy");
|
|
29
|
+
await fs.writeFile(testStory, `
|
|
30
|
+
@title Simple Test
|
|
31
|
+
|
|
32
|
+
[[start]]:
|
|
33
|
+
Hello world!
|
|
34
|
+
|
|
35
|
+
[[next]]:
|
|
36
|
+
This is a test.
|
|
37
|
+
`);
|
|
38
|
+
const result = await writeScriptFile(testStory, OUTPUT_DIR, "story.js");
|
|
39
|
+
expect(result).not.toBe(false);
|
|
40
|
+
// Check that story.js was created
|
|
41
|
+
const storyPath = path.join(OUTPUT_DIR, "story.js");
|
|
42
|
+
const storyContent = await fs.readFile(storyPath, "utf8");
|
|
43
|
+
expect(storyContent).toContain("var story");
|
|
44
|
+
expect(storyContent).toContain("start");
|
|
45
|
+
expect(storyContent).toContain("Hello world!");
|
|
46
|
+
});
|
|
47
|
+
test("writeScriptFile with custom filename", async () => {
|
|
48
|
+
const testStory = path.join(TEST_DIR, "custom.squiffy");
|
|
49
|
+
await fs.writeFile(testStory, `
|
|
50
|
+
@title Custom
|
|
51
|
+
|
|
52
|
+
[[section]]:
|
|
53
|
+
Content here.
|
|
54
|
+
`);
|
|
55
|
+
await writeScriptFile(testStory, OUTPUT_DIR, "custom-story.js");
|
|
56
|
+
// Check that custom-story.js was created
|
|
57
|
+
const exists = await fs.access(path.join(OUTPUT_DIR, "custom-story.js"))
|
|
58
|
+
.then(() => true)
|
|
59
|
+
.catch(() => false);
|
|
60
|
+
expect(exists).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
test("writeScriptFile handles compilation errors", async () => {
|
|
63
|
+
// Create a story with errors (passage without section)
|
|
64
|
+
const testStory = path.join(TEST_DIR, "error.squiffy");
|
|
65
|
+
await fs.writeFile(testStory, `
|
|
66
|
+
[my passage]:
|
|
67
|
+
This is invalid - passage without section.
|
|
68
|
+
`);
|
|
69
|
+
const result = await writeScriptFile(testStory, OUTPUT_DIR, "story.js");
|
|
70
|
+
expect(result).toBe(false);
|
|
71
|
+
// story.js should not have been created
|
|
72
|
+
const exists = await fs.access(path.join(OUTPUT_DIR, "story.js"))
|
|
73
|
+
.then(() => true)
|
|
74
|
+
.catch(() => false);
|
|
75
|
+
expect(exists).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
test("createPackageFiles creates full package", async () => {
|
|
78
|
+
const testStory = path.join(TEST_DIR, "package.squiffy");
|
|
79
|
+
await fs.writeFile(testStory, `
|
|
80
|
+
@title Package Test
|
|
81
|
+
|
|
82
|
+
[[main]]:
|
|
83
|
+
This is a complete story.
|
|
84
|
+
|
|
85
|
+
[Click here]:
|
|
86
|
+
You clicked!
|
|
87
|
+
`);
|
|
88
|
+
const result = await createPackageFiles(testStory, OUTPUT_DIR, {});
|
|
89
|
+
expect(result).toBe(true);
|
|
90
|
+
// Check that all package files were created
|
|
91
|
+
const files = await fs.readdir(OUTPUT_DIR);
|
|
92
|
+
expect(files).toContain("index.html");
|
|
93
|
+
expect(files).toContain("style.css");
|
|
94
|
+
expect(files).toContain("story.js");
|
|
95
|
+
expect(files).toContain("squiffy.runtime.global.js");
|
|
96
|
+
// Verify content
|
|
97
|
+
const html = await fs.readFile(path.join(OUTPUT_DIR, "index.html"), "utf8");
|
|
98
|
+
expect(html).toContain("Package Test");
|
|
99
|
+
expect(html).toContain("<script");
|
|
100
|
+
const js = await fs.readFile(path.join(OUTPUT_DIR, "story.js"), "utf8");
|
|
101
|
+
expect(js).toContain("var story");
|
|
102
|
+
});
|
|
103
|
+
test("createPackageFiles creates zip file", async () => {
|
|
104
|
+
const testStory = path.join(TEST_DIR, "zip.squiffy");
|
|
105
|
+
await fs.writeFile(testStory, `
|
|
106
|
+
@title Zip Test
|
|
107
|
+
|
|
108
|
+
[[section]]:
|
|
109
|
+
Content for zip test.
|
|
110
|
+
`);
|
|
111
|
+
const result = await createPackageFiles(testStory, OUTPUT_DIR, { createZip: true });
|
|
112
|
+
expect(result).toBe(true);
|
|
113
|
+
// Check that zip file was created
|
|
114
|
+
const files = await fs.readdir(OUTPUT_DIR);
|
|
115
|
+
expect(files).toContain("output.zip");
|
|
116
|
+
// Verify zip file has content
|
|
117
|
+
const zipStats = await fs.stat(path.join(OUTPUT_DIR, "output.zip"));
|
|
118
|
+
expect(zipStats.size).toBeGreaterThan(0);
|
|
119
|
+
});
|
|
120
|
+
test("createPackageFiles handles compilation errors", async () => {
|
|
121
|
+
const testStory = path.join(TEST_DIR, "error2.squiffy");
|
|
122
|
+
await fs.writeFile(testStory, `
|
|
123
|
+
[[section]]:
|
|
124
|
+
Some content.
|
|
125
|
+
|
|
126
|
+
@ui
|
|
127
|
+
This should fail - text in @ui block
|
|
128
|
+
`);
|
|
129
|
+
const result = await createPackageFiles(testStory, OUTPUT_DIR, {});
|
|
130
|
+
expect(result).toBe(false);
|
|
131
|
+
// No files should have been created
|
|
132
|
+
const files = await fs.readdir(OUTPUT_DIR);
|
|
133
|
+
expect(files.length).toBe(0);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe("external-files", () => {
|
|
137
|
+
test("getMatchingFilenames finds files by pattern", async () => {
|
|
138
|
+
// Create some test files
|
|
139
|
+
await fs.writeFile(path.join(TEST_DIR, "test1.txt"), "content 1");
|
|
140
|
+
await fs.writeFile(path.join(TEST_DIR, "test2.txt"), "content 2");
|
|
141
|
+
await fs.writeFile(path.join(TEST_DIR, "other.md"), "markdown");
|
|
142
|
+
const testFile = path.join(TEST_DIR, "main.squiffy");
|
|
143
|
+
const ext = externalFiles(testFile);
|
|
144
|
+
const matches = await ext.getMatchingFilenames("*.txt");
|
|
145
|
+
expect(matches.length).toBe(2);
|
|
146
|
+
expect(matches.some(f => f.includes("test1.txt"))).toBe(true);
|
|
147
|
+
expect(matches.some(f => f.includes("test2.txt"))).toBe(true);
|
|
148
|
+
expect(matches.some(f => f.includes("other.md"))).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
test("getContent reads file content", async () => {
|
|
151
|
+
const contentFile = path.join(TEST_DIR, "content.txt");
|
|
152
|
+
await fs.writeFile(contentFile, "Hello from external file!");
|
|
153
|
+
const testFile = path.join(TEST_DIR, "main.squiffy");
|
|
154
|
+
const ext = externalFiles(testFile);
|
|
155
|
+
const content = await ext.getContent(contentFile);
|
|
156
|
+
expect(content).toBe("Hello from external file!");
|
|
157
|
+
});
|
|
158
|
+
test("getLocalFilename returns relative path", () => {
|
|
159
|
+
const testFile = path.join(TEST_DIR, "main.squiffy");
|
|
160
|
+
const ext = externalFiles(testFile);
|
|
161
|
+
const absolutePath = path.join(TEST_DIR, "subdir", "file.js");
|
|
162
|
+
const localPath = ext.getLocalFilename(absolutePath);
|
|
163
|
+
expect(localPath).toBe(path.join("subdir", "file.js"));
|
|
164
|
+
});
|
|
165
|
+
test("getMatchingFilenames excludes already included files", async () => {
|
|
166
|
+
const file1 = path.join(TEST_DIR, "included.txt");
|
|
167
|
+
const file2 = path.join(TEST_DIR, "notincluded.txt");
|
|
168
|
+
await fs.writeFile(file1, "already included");
|
|
169
|
+
await fs.writeFile(file2, "not yet included");
|
|
170
|
+
const testFile = path.join(TEST_DIR, "main.squiffy");
|
|
171
|
+
const ext = externalFiles(testFile);
|
|
172
|
+
// Simulate including file1
|
|
173
|
+
await ext.getContent(file1);
|
|
174
|
+
const matches = await ext.getMatchingFilenames("*.txt");
|
|
175
|
+
// Should only find file2, not file1 (already included)
|
|
176
|
+
expect(matches.length).toBe(1);
|
|
177
|
+
expect(matches[0]).toContain("notincluded.txt");
|
|
178
|
+
});
|
|
179
|
+
});
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/squiffy.js
CHANGED
|
@@ -15,13 +15,15 @@ const argv = yargs(hideBin(process.argv))
|
|
|
15
15
|
.describe("p", "Port for HTTP server (only with --serve)")
|
|
16
16
|
.describe("scriptonly", "Only generate JavaScript file (and optionally specify a name)")
|
|
17
17
|
.describe("zip", "Create zip file")
|
|
18
|
+
.describe("inline", "Create single HTML file with everything inlined")
|
|
18
19
|
.version(version)
|
|
19
20
|
.parseSync();
|
|
20
21
|
console.log("Squiffy " + version);
|
|
21
22
|
const options = {
|
|
22
23
|
serve: argv.s,
|
|
23
24
|
scriptOnly: argv.scriptonly,
|
|
24
|
-
zip: argv.zip
|
|
25
|
+
zip: argv.zip,
|
|
26
|
+
inline: argv.inline
|
|
25
27
|
};
|
|
26
28
|
const inputFilename = argv._[0];
|
|
27
29
|
const outputPath = path.resolve(path.dirname(inputFilename));
|
|
@@ -30,7 +32,10 @@ if (options.scriptOnly) {
|
|
|
30
32
|
await writeScriptFile(inputFilename, outputPath, outputFilename);
|
|
31
33
|
}
|
|
32
34
|
else {
|
|
33
|
-
const result = await createPackageFiles(inputFilename, outputPath,
|
|
35
|
+
const result = await createPackageFiles(inputFilename, outputPath, {
|
|
36
|
+
createZip: !!options.zip,
|
|
37
|
+
inlineHtml: !!options.inline
|
|
38
|
+
});
|
|
34
39
|
if (result && options.serve) {
|
|
35
40
|
const port = argv.p || 8282;
|
|
36
41
|
serve(outputPath, port);
|
package/package.json
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@textadventures/squiffy-cli",
|
|
3
|
-
"version": "6.0.0-
|
|
3
|
+
"version": "6.0.0-beta.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"main": "dist/
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
8
16
|
"scripts": {
|
|
9
|
-
"build": "tsc"
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"test": "vitest --run",
|
|
19
|
+
"dev": "vitest"
|
|
10
20
|
},
|
|
11
21
|
"author": "Alex Warren",
|
|
12
22
|
"contributors": [
|
|
@@ -20,12 +30,12 @@
|
|
|
20
30
|
"squiffy-packager": "dist/squiffy.js"
|
|
21
31
|
},
|
|
22
32
|
"dependencies": {
|
|
23
|
-
"@textadventures/squiffy-packager": "^6.0.0-
|
|
33
|
+
"@textadventures/squiffy-packager": "^6.0.0-beta.0",
|
|
24
34
|
"finalhandler": "^2.1.0",
|
|
25
35
|
"glob": "^11.0.3",
|
|
26
36
|
"serve-static": "^2.2.0",
|
|
27
|
-
"squiffy-compiler": "^6.0.0-
|
|
28
|
-
"squiffy-runtime": "^6.0.0-
|
|
37
|
+
"squiffy-compiler": "^6.0.0-beta.0",
|
|
38
|
+
"squiffy-runtime": "^6.0.0-beta.0",
|
|
29
39
|
"yargs": "^18.0.0"
|
|
30
40
|
},
|
|
31
41
|
"devDependencies": {
|
|
@@ -34,11 +44,12 @@
|
|
|
34
44
|
"@types/serve-static": "^1.15.8",
|
|
35
45
|
"@types/yargs": "^17.0.33",
|
|
36
46
|
"tsx": "^4.20.4",
|
|
37
|
-
"typescript": "^5.9.2"
|
|
47
|
+
"typescript": "^5.9.2",
|
|
48
|
+
"vitest": "^3.2.4"
|
|
38
49
|
},
|
|
39
50
|
"files": [
|
|
40
51
|
"dist"
|
|
41
52
|
],
|
|
42
53
|
"type": "module",
|
|
43
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "dadcab6940e5e7ffdd600e8821c9297fdbf93160"
|
|
44
55
|
}
|