@sprig-and-prose/sprig 0.6.2 → 0.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @sprig-and-prose/sprig
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4a9cd76: **sprig-scenes (scene compiler):**
8
+
9
+ - **Shape groups:** Only treat `group` as a group declaration when it is followed by an identifier (group name). `group { string }` is now parsed as a top-level field named `group` of type string.
10
+ - **Field order:** Manifest fields are emitted in definition order (group fields first in group order, then top-level fields).
11
+ - **Validation:** A field may only define a single type. Error when two or more types appear in a field block, with message naming the field.
12
+ - **Validation:** Error when a field block is empty (no type), with message naming the field (e.g. "Field 'id' does not contain any type block...").
13
+ - **Errors:** `parseFieldBlock` accepts an optional field name for clearer, field-named error messages.
14
+
15
+ **sprig (CLI):**
16
+
17
+ - **Invalid scene output:** When a scene fails to compile, write `<base>.error.txt` in the output directory with the error message(s) and a location line: ` at <scene-name>` when the scene name can be parsed from source, or ` in <filename>` otherwise. Enables testing invalid prose by asserting on generated error files.
18
+ - Remove any existing `.error.txt` for a scene when that scene later compiles successfully.
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies [4a9cd76]
23
+ - @sprig-and-prose/sprig-scenes@0.3.0
24
+
3
25
  ## 0.6.2
4
26
 
5
27
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"scene-compiler.d.ts","sourceRoot":"","sources":["../src/scene-compiler.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7F;AAiBD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC,CA4F7B"}
1
+ {"version":3,"file":"scene-compiler.d.ts","sourceRoot":"","sources":["../src/scene-compiler.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7F;AA2CD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC,CAwG7B"}
@@ -1,10 +1,29 @@
1
- import { readFileSync, writeFileSync, mkdirSync, renameSync } from "node:fs";
2
- import { join } from "node:path";
1
+ import { readFileSync, writeFileSync, mkdirSync, renameSync, unlinkSync } from "node:fs";
2
+ import { join, basename } from "node:path";
3
3
  import { execSync } from "node:child_process";
4
4
  import fg from "fast-glob";
5
5
  // Import directly from compile_scene to avoid scene-engine dependency
6
6
  // @ts-expect-error - sprig-scenes doesn't have full TypeScript types
7
7
  import { compileSceneFromText } from "@sprig-and-prose/sprig-scenes/compiler/compile_scene";
8
+ /** Base name for output files derived from scene prose path (e.g. 05-group.scene.prose → 05-group). */
9
+ function sceneFileBaseName(sceneFile) {
10
+ return basename(sceneFile).replace(/\.scene\.prose$/i, "") || "scene";
11
+ }
12
+ /**
13
+ * Write compile errors to <base>.error.txt in outputDir.
14
+ * One line per error, then " at <scene-name>" if scene name was parsed from source, else " in <filename>".
15
+ */
16
+ function writeSceneErrors(outputDir, sceneFile, errors, sourceText) {
17
+ const base = sceneFileBaseName(sceneFile);
18
+ const sceneNameMatch = sourceText.match(/\bscene\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{/);
19
+ const locationLine = sceneNameMatch
20
+ ? " at " + sceneNameMatch[1] + "\n"
21
+ : " in " + base + "\n";
22
+ const errorPath = join(outputDir, `${base}.error.txt`);
23
+ const errorBlock = errors.join("\n").trim() + (errors.length ? "\n" : "");
24
+ const content = errorBlock + locationLine;
25
+ writeFileSync(errorPath, content, "utf-8");
26
+ }
8
27
  /**
9
28
  * Generate manifestId: git:<sha> if available, else time:<iso>
10
29
  */
@@ -55,15 +74,18 @@ export async function compileScenes(universeRoot, outputDir) {
55
74
  const manifestId = generateManifestId();
56
75
  // Compile each scene
57
76
  for (const sceneFile of sceneFiles) {
77
+ let sourceText = "";
58
78
  try {
59
- const sourceText = readFileSync(sceneFile, "utf-8");
79
+ sourceText = readFileSync(sceneFile, "utf-8");
60
80
  const manifest = compileSceneFromText(sourceText);
61
81
  if (!manifest || !manifest.sceneName) {
82
+ const message = "Failed to compile scene: no sceneName found";
62
83
  diagnostics.push({
63
84
  severity: "error",
64
- message: `Failed to compile scene: no sceneName found`,
85
+ message,
65
86
  source: sceneFile,
66
87
  });
88
+ writeSceneErrors(outputDir, sceneFile, [message], sourceText);
67
89
  continue;
68
90
  }
69
91
  // Inject metadata
@@ -80,6 +102,14 @@ export async function compileScenes(universeRoot, outputDir) {
80
102
  const json = JSON.stringify(manifestWithMeta, null, 2);
81
103
  writeFileSync(tempPath, json, "utf-8");
82
104
  renameSync(tempPath, outputPath);
105
+ // Remove any prior .error.txt for this scene (same source file base name)
106
+ const errorPath = join(outputDir, `${sceneFileBaseName(sceneFile)}.error.txt`);
107
+ try {
108
+ unlinkSync(errorPath);
109
+ }
110
+ catch {
111
+ // ignore if missing
112
+ }
83
113
  compiledScenes.push(manifest.sceneName);
84
114
  }
85
115
  catch (error) {
@@ -89,6 +119,7 @@ export async function compileScenes(universeRoot, outputDir) {
89
119
  message: `Scene compile failed: ${message}`,
90
120
  source: sceneFile,
91
121
  });
122
+ writeSceneErrors(outputDir, sceneFile, [message], sourceText);
92
123
  }
93
124
  }
94
125
  const hasErrors = diagnostics.some((d) => d.severity === "error");
@@ -1 +1 @@
1
- {"version":3,"file":"scene-compiler.js","sourceRoot":"","sources":["../src/scene-compiler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scene-compiler.js","sourceRoot":"","sources":["../src/scene-compiler.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sprig-and-prose/sprig",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "description": "Sprig CLI tool for compiling and serving universes",
6
6
  "main": "dist/cli.js",
@@ -1,5 +1,5 @@
1
- import { readFileSync, writeFileSync, mkdirSync, renameSync } from "node:fs";
2
- import { join, dirname } from "node:path";
1
+ import { readFileSync, writeFileSync, mkdirSync, renameSync, unlinkSync } from "node:fs";
2
+ import { join, basename } from "node:path";
3
3
  import { execSync } from "node:child_process";
4
4
  import fg from "fast-glob";
5
5
  // Import directly from compile_scene to avoid scene-engine dependency
@@ -12,6 +12,32 @@ export interface SceneCompileResult {
12
12
  diagnostics: Array<{ severity: string; message: string; source?: unknown; scene?: string }>;
13
13
  }
14
14
 
15
+ /** Base name for output files derived from scene prose path (e.g. 05-group.scene.prose → 05-group). */
16
+ function sceneFileBaseName(sceneFile: string): string {
17
+ return basename(sceneFile).replace(/\.scene\.prose$/i, "") || "scene";
18
+ }
19
+
20
+ /**
21
+ * Write compile errors to <base>.error.txt in outputDir.
22
+ * One line per error, then " at <scene-name>" if scene name was parsed from source, else " in <filename>".
23
+ */
24
+ function writeSceneErrors(
25
+ outputDir: string,
26
+ sceneFile: string,
27
+ errors: string[],
28
+ sourceText: string,
29
+ ): void {
30
+ const base = sceneFileBaseName(sceneFile);
31
+ const sceneNameMatch = sourceText.match(/\bscene\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{/);
32
+ const locationLine = sceneNameMatch
33
+ ? " at " + sceneNameMatch[1] + "\n"
34
+ : " in " + base + "\n";
35
+ const errorPath = join(outputDir, `${base}.error.txt`);
36
+ const errorBlock = errors.join("\n").trim() + (errors.length ? "\n" : "");
37
+ const content = errorBlock + locationLine;
38
+ writeFileSync(errorPath, content, "utf-8");
39
+ }
40
+
15
41
  /**
16
42
  * Generate manifestId: git:<sha> if available, else time:<iso>
17
43
  */
@@ -69,16 +95,19 @@ export async function compileScenes(
69
95
 
70
96
  // Compile each scene
71
97
  for (const sceneFile of sceneFiles) {
98
+ let sourceText = "";
72
99
  try {
73
- const sourceText = readFileSync(sceneFile, "utf-8");
100
+ sourceText = readFileSync(sceneFile, "utf-8");
74
101
  const manifest = compileSceneFromText(sourceText);
75
102
 
76
103
  if (!manifest || !manifest.sceneName) {
104
+ const message = "Failed to compile scene: no sceneName found";
77
105
  diagnostics.push({
78
106
  severity: "error",
79
- message: `Failed to compile scene: no sceneName found`,
107
+ message,
80
108
  source: sceneFile,
81
109
  });
110
+ writeSceneErrors(outputDir, sceneFile, [message], sourceText);
82
111
  continue;
83
112
  }
84
113
 
@@ -98,6 +127,14 @@ export async function compileScenes(
98
127
  writeFileSync(tempPath, json, "utf-8");
99
128
  renameSync(tempPath, outputPath);
100
129
 
130
+ // Remove any prior .error.txt for this scene (same source file base name)
131
+ const errorPath = join(outputDir, `${sceneFileBaseName(sceneFile)}.error.txt`);
132
+ try {
133
+ unlinkSync(errorPath);
134
+ } catch {
135
+ // ignore if missing
136
+ }
137
+
101
138
  compiledScenes.push(manifest.sceneName);
102
139
  } catch (error) {
103
140
  const message = error instanceof Error ? error.message : String(error);
@@ -106,6 +143,7 @@ export async function compileScenes(
106
143
  message: `Scene compile failed: ${message}`,
107
144
  source: sceneFile,
108
145
  });
146
+ writeSceneErrors(outputDir, sceneFile, [message], sourceText);
109
147
  }
110
148
  }
111
149