@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2014-2024 Alex Warren, textadventures.co.uk and contributors
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
@@ -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
- includedFiles.push(filename);
15
- return (await fs.readFile(filename)).toString();
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);
@@ -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, createZip: boolean) => Promise<boolean>;
3
+ export declare const createPackageFiles: (inputFilename: string, outputPath: string, options?: PackageOptions) => Promise<boolean>;
@@ -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, createZip) => {
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, createZip);
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
+ });
@@ -0,0 +1,2 @@
1
+ export { externalFiles } from "./external-files.js";
2
+ export { createPackageFiles, writeScriptFile } from "./file-packager.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ // Public API exports for programmatic use
2
+ export { externalFiles } from "./external-files.js";
3
+ export { createPackageFiles, writeScriptFile } from "./file-packager.js";
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, !!options.zip);
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-alpha.20",
3
+ "version": "6.0.0-beta.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "main": "dist/squiffy.js",
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-alpha.20",
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-alpha.20",
28
- "squiffy-runtime": "^6.0.0-alpha.20",
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": "eee4b8836ed791b68d8d56b0d5071129e6214d2b"
54
+ "gitHead": "dadcab6940e5e7ffdd600e8821c9297fdbf93160"
44
55
  }