@scenar/cli 0.9.0 → 0.9.1
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/example-bundle/assets/index-DlmG1Uh7.js +388 -0
- package/example-bundle/assets/{style-YRMeJPPX.css → style-BjquDnTE.css} +1 -1
- package/example-bundle/index.html +2 -2
- package/example-bundle/pack-manifest.json +9 -9
- package/example-bundle/scenario.json +6 -2
- package/package.json +4 -4
- package/src/__tests__/collect-pack-shots.test.ts +97 -0
- package/src/__tests__/pack-manifest.test.ts +17 -0
- package/src/__tests__/read-shots.test.ts +57 -0
- package/src/__tests__/run-shoot.test.ts +51 -2
- package/src/bundle/read-shots.d.ts +28 -0
- package/src/bundle/read-shots.d.ts.map +1 -0
- package/src/bundle/read-shots.js +28 -0
- package/src/bundle/read-shots.js.map +1 -0
- package/src/bundle/read-shots.ts +38 -0
- package/src/pack/build.d.ts +33 -1
- package/src/pack/build.d.ts.map +1 -1
- package/src/pack/build.js +35 -10
- package/src/pack/build.js.map +1 -1
- package/src/pack/build.ts +63 -17
- package/src/pack/collect-pack-shots.d.ts +40 -0
- package/src/pack/collect-pack-shots.d.ts.map +1 -0
- package/src/pack/collect-pack-shots.js +59 -0
- package/src/pack/collect-pack-shots.js.map +1 -0
- package/src/pack/collect-pack-shots.ts +76 -0
- package/src/pack/generate-embed-entry.d.ts +7 -0
- package/src/pack/generate-embed-entry.d.ts.map +1 -1
- package/src/pack/generate-embed-entry.js +10 -2
- package/src/pack/generate-embed-entry.js.map +1 -1
- package/src/pack/generate-embed-entry.ts +11 -2
- package/src/pack/pack-manifest.d.ts +12 -3
- package/src/pack/pack-manifest.d.ts.map +1 -1
- package/src/pack/pack-manifest.js +13 -3
- package/src/pack/pack-manifest.js.map +1 -1
- package/src/pack/pack-manifest.ts +13 -2
- package/src/pack/run-pack.d.ts +6 -0
- package/src/pack/run-pack.d.ts.map +1 -1
- package/src/pack/run-pack.js +41 -14
- package/src/pack/run-pack.js.map +1 -1
- package/src/pack/run-pack.ts +55 -14
- package/src/shoot/run-shoot.d.ts +6 -0
- package/src/shoot/run-shoot.d.ts.map +1 -1
- package/src/shoot/run-shoot.js +18 -0
- package/src/shoot/run-shoot.js.map +1 -1
- package/src/shoot/run-shoot.ts +19 -0
- package/src/util/load-ts.d.ts +12 -2
- package/src/util/load-ts.d.ts.map +1 -1
- package/src/util/load-ts.js +25 -10
- package/src/util/load-ts.js.map +1 -1
- package/src/util/load-ts.ts +25 -11
- package/tsconfig.tsbuildinfo +1 -1
- package/example-bundle/assets/index-BoIR0_nN.js +0 -147
|
@@ -41,14 +41,24 @@ export const SCENARIO_JSON_FILE = "scenario.json";
|
|
|
41
41
|
* `viewport` baked into the bundle: the player reads its steps from the bundled
|
|
42
42
|
* JS, so the full serialized spec is still a follow-up, but `deploy` needs the
|
|
43
43
|
* viewport to derive a correctly-proportioned embed snippet (DD-004). The
|
|
44
|
-
* viewport mirrors `ViewportConfig { width, height }`.
|
|
45
|
-
*
|
|
44
|
+
* viewport mirrors `ViewportConfig { width, height }`.
|
|
45
|
+
*
|
|
46
|
+
* `shots` records the scenario's declared shot names (in step order) when
|
|
47
|
+
* pack could discover them, so tooling — `scenar shoot` first of all — can
|
|
48
|
+
* learn a bundle's shots without booting a browser. The key is written only
|
|
49
|
+
* when the list is known: absent means "unknown", present (even empty) is
|
|
50
|
+
* authoritative. Names only, never times — shot times depend on narration
|
|
51
|
+
* and are derived at the point of use (DD-004's derive-don't-store rule).
|
|
52
|
+
*
|
|
53
|
+
* Validated against the backend rules before writing (which accept the
|
|
54
|
+
* extra `viewport`/`shots` keys).
|
|
46
55
|
*/
|
|
47
56
|
export async function writeScenarioJson(
|
|
48
57
|
outDir: string,
|
|
49
58
|
scenarioId: string,
|
|
50
59
|
generatorVersion: string,
|
|
51
60
|
viewport: Viewport,
|
|
61
|
+
shots?: readonly string[],
|
|
52
62
|
): Promise<void> {
|
|
53
63
|
const content = JSON.stringify(
|
|
54
64
|
{
|
|
@@ -56,6 +66,7 @@ export async function writeScenarioJson(
|
|
|
56
66
|
id: scenarioId,
|
|
57
67
|
generator: `@scenar/cli pack ${generatorVersion}`,
|
|
58
68
|
viewport: { width: viewport.width, height: viewport.height },
|
|
69
|
+
...(shots !== undefined ? { shots } : {}),
|
|
59
70
|
},
|
|
60
71
|
null,
|
|
61
72
|
2,
|
package/src/pack/run-pack.d.ts
CHANGED
|
@@ -27,6 +27,12 @@ export interface PackResult {
|
|
|
27
27
|
readonly renderFilePath: string;
|
|
28
28
|
readonly providersPath: string | null;
|
|
29
29
|
readonly hasNarration: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The declared shot names recorded in scenario.json (empty = the scenario
|
|
32
|
+
* authoritatively declares none), or undefined when the steps module could
|
|
33
|
+
* not be loaded under Node and the shots are unknown.
|
|
34
|
+
*/
|
|
35
|
+
readonly shots?: readonly string[];
|
|
30
36
|
readonly manifest: PackManifest;
|
|
31
37
|
readonly totalBytes: number;
|
|
32
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-pack.d.ts","sourceRoot":"","sources":["../../../src/pack/run-pack.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run-pack.d.ts","sourceRoot":"","sources":["../../../src/pack/run-pack.ts"],"names":[],"mappings":"AAQA,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAS5B,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAED,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAiH1E"}
|
package/src/pack/run-pack.js
CHANGED
|
@@ -3,12 +3,16 @@ import { stat, mkdir, writeFile, rm, readdir, copyFile, access } from "node:fs/p
|
|
|
3
3
|
import { detectRenderExport } from "../render/detect-render-export.js";
|
|
4
4
|
import { resolveProvidersPath } from "../render/resolve-providers.js";
|
|
5
5
|
import { generateEmbedEntry, generateEmbedHtml } from "./generate-embed-entry.js";
|
|
6
|
+
import { collectPackShots } from "./collect-pack-shots.js";
|
|
6
7
|
import { runViteBuild } from "./build.js";
|
|
7
8
|
import { copyEmbedLoader } from "./embed-loader.js";
|
|
8
9
|
import { buildPackManifest, writePackManifest, writeScenarioJson, verifyManifestFilesExist, } from "./pack-manifest.js";
|
|
9
10
|
import { DEFAULT_VIEWPORT } from "./viewport.js";
|
|
10
|
-
/**
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Generator version stamped into scenario.json (kept in sync with the CLI).
|
|
13
|
+
* 0.0.2: scenario.json may carry `shots` (declared shot names, in step order).
|
|
14
|
+
*/
|
|
15
|
+
const PACK_GENERATOR_VERSION = "0.0.2";
|
|
12
16
|
/**
|
|
13
17
|
* Bundle a scenario directory into a self-contained, hosted embed — the pure
|
|
14
18
|
* orchestration behind `scenar pack`, with no process/exit coupling so both the
|
|
@@ -42,7 +46,14 @@ export async function runPack(options) {
|
|
|
42
46
|
onLog(`Providers: ${providersPath ?? "none"}`);
|
|
43
47
|
onLog(`Narration: ${hasNarration ? "yes (manifest found)" : "none"}`);
|
|
44
48
|
onLog(`Output: ${outDir}`);
|
|
45
|
-
// 1.
|
|
49
|
+
// 1. Discover the declared shots by SSR-loading the steps module (same
|
|
50
|
+
// Vite resolution as the build below). Before the build on purpose:
|
|
51
|
+
// authoring errors — bad shot names, no steps array — fail fast here,
|
|
52
|
+
// without paying for a bundle. An import failure is tolerated: the
|
|
53
|
+
// shots are then unknown and scenario.json omits the key.
|
|
54
|
+
const collectedShots = await collectPackShots(scenarioDir);
|
|
55
|
+
onLog(`Shots: ${describeCollectedShots(collectedShots)}`);
|
|
56
|
+
// 2. Generate the browser entry + index.html into the temp dir.
|
|
46
57
|
const stage = options.stage ?? false;
|
|
47
58
|
const entrySource = generateEmbedEntry({
|
|
48
59
|
scenarioDir,
|
|
@@ -59,34 +70,42 @@ export async function runPack(options) {
|
|
|
59
70
|
const entryHtmlPath = join(tempDir, "index.html");
|
|
60
71
|
await writeFile(join(tempDir, entryFileName), entrySource, "utf-8");
|
|
61
72
|
await writeFile(entryHtmlPath, generateEmbedHtml(scenarioId, entryFileName, stage), "utf-8");
|
|
62
|
-
//
|
|
73
|
+
// 3. Build the static bundle with Vite.
|
|
63
74
|
onLog("Bundling embed with Vite...");
|
|
64
75
|
await runViteBuild({ root: tempDir, outDir, entryHtmlPath });
|
|
65
|
-
//
|
|
76
|
+
// 4. Copy the narration manifest + audio. The embed fetches
|
|
66
77
|
// ./narration/manifest.json at runtime and resolves each clip's
|
|
67
78
|
// relative src against it, so both must ship in the bundle.
|
|
68
79
|
if (hasNarration) {
|
|
69
80
|
await copyNarration(scenarioDir, outDir);
|
|
70
81
|
}
|
|
71
|
-
//
|
|
82
|
+
// 5. Write the required scenario.json descriptor, recording the canonical
|
|
72
83
|
// viewport baked into the bundle so `deploy`/`serve`/`publish` can derive
|
|
73
|
-
// a correctly-proportioned embed snippet (DD-004)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// 4b. Copy the optional <scenar-embed> loader into the bundle as embed.js
|
|
84
|
+
// a correctly-proportioned embed snippet (DD-004), and — when known —
|
|
85
|
+
// the declared shot names so `shoot` and other tooling never boot a
|
|
86
|
+
// browser just to learn a bundle has nothing to capture.
|
|
87
|
+
await writeScenarioJson(outDir, scenarioId, PACK_GENERATOR_VERSION, { width, height: shellHeight }, collectedShots.recorded ? collectedShots.shots : undefined);
|
|
88
|
+
// 5b. Copy the optional <scenar-embed> loader into the bundle as embed.js
|
|
79
89
|
// (sibling of index.html), so the enhanced snippet works on any static
|
|
80
90
|
// host (GitHub Pages, `serve`, the edge) with no extra setup. It lands
|
|
81
91
|
// before the manifest pass below so it ships as a first-class bundle file.
|
|
82
92
|
await copyEmbedLoader(outDir);
|
|
83
|
-
//
|
|
93
|
+
// 6. Compute + write the deploy-facing pack manifest (validates the bundle
|
|
84
94
|
// against the backend allowlist; throws on any violation).
|
|
85
95
|
const manifest = await buildPackManifest(outDir, scenarioId);
|
|
86
96
|
await verifyManifestFilesExist(outDir, manifest);
|
|
87
97
|
await writePackManifest(outDir, manifest);
|
|
88
98
|
const totalBytes = manifest.files.reduce((sum, f) => sum + f.sizeBytes, 0);
|
|
89
|
-
return {
|
|
99
|
+
return {
|
|
100
|
+
scenarioId,
|
|
101
|
+
outDir,
|
|
102
|
+
renderFilePath,
|
|
103
|
+
providersPath,
|
|
104
|
+
hasNarration,
|
|
105
|
+
shots: collectedShots.recorded ? collectedShots.shots : undefined,
|
|
106
|
+
manifest,
|
|
107
|
+
totalBytes,
|
|
108
|
+
};
|
|
90
109
|
}
|
|
91
110
|
finally {
|
|
92
111
|
if (!options.keepTemp) {
|
|
@@ -113,6 +132,14 @@ async function copyNarration(scenarioDir, outDir) {
|
|
|
113
132
|
}
|
|
114
133
|
}
|
|
115
134
|
}
|
|
135
|
+
/** One log-friendly line for the shot discovery outcome. */
|
|
136
|
+
function describeCollectedShots(collected) {
|
|
137
|
+
if (!collected.recorded) {
|
|
138
|
+
// Vite errors can run many lines; the first carries the message.
|
|
139
|
+
return `unknown — steps module not loadable under Node (${collected.reason.split("\n")[0]})`;
|
|
140
|
+
}
|
|
141
|
+
return collected.shots.length === 0 ? "none declared" : collected.shots.join(", ");
|
|
142
|
+
}
|
|
116
143
|
async function fileExists(path) {
|
|
117
144
|
try {
|
|
118
145
|
await access(path);
|
package/src/pack/run-pack.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-pack.js","sourceRoot":"","sources":["../../../src/pack/run-pack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,GAEzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD
|
|
1
|
+
{"version":3,"file":"run-pack.js","sourceRoot":"","sources":["../../../src/pack/run-pack.ts"],"names":[],"mappings":"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"}
|
package/src/pack/run-pack.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { stat, mkdir, writeFile, rm, readdir, copyFile, access } from "node:fs/p
|
|
|
3
3
|
import { detectRenderExport } from "../render/detect-render-export.js";
|
|
4
4
|
import { resolveProvidersPath } from "../render/resolve-providers.js";
|
|
5
5
|
import { generateEmbedEntry, generateEmbedHtml } from "./generate-embed-entry.js";
|
|
6
|
+
import { collectPackShots, type CollectedPackShots } from "./collect-pack-shots.js";
|
|
6
7
|
import { runViteBuild } from "./build.js";
|
|
7
8
|
import { copyEmbedLoader } from "./embed-loader.js";
|
|
8
9
|
import {
|
|
@@ -14,8 +15,11 @@ import {
|
|
|
14
15
|
} from "./pack-manifest.js";
|
|
15
16
|
import { DEFAULT_VIEWPORT } from "./viewport.js";
|
|
16
17
|
|
|
17
|
-
/**
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Generator version stamped into scenario.json (kept in sync with the CLI).
|
|
20
|
+
* 0.0.2: scenario.json may carry `shots` (declared shot names, in step order).
|
|
21
|
+
*/
|
|
22
|
+
const PACK_GENERATOR_VERSION = "0.0.2";
|
|
19
23
|
|
|
20
24
|
/** Options for {@link runPack}. Paths may be relative; they are resolved here. */
|
|
21
25
|
export interface RunPackOptions {
|
|
@@ -46,6 +50,12 @@ export interface PackResult {
|
|
|
46
50
|
readonly renderFilePath: string;
|
|
47
51
|
readonly providersPath: string | null;
|
|
48
52
|
readonly hasNarration: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* The declared shot names recorded in scenario.json (empty = the scenario
|
|
55
|
+
* authoritatively declares none), or undefined when the steps module could
|
|
56
|
+
* not be loaded under Node and the shots are unknown.
|
|
57
|
+
*/
|
|
58
|
+
readonly shots?: readonly string[];
|
|
49
59
|
readonly manifest: PackManifest;
|
|
50
60
|
readonly totalBytes: number;
|
|
51
61
|
}
|
|
@@ -91,7 +101,15 @@ export async function runPack(options: RunPackOptions): Promise<PackResult> {
|
|
|
91
101
|
onLog(`Narration: ${hasNarration ? "yes (manifest found)" : "none"}`);
|
|
92
102
|
onLog(`Output: ${outDir}`);
|
|
93
103
|
|
|
94
|
-
// 1.
|
|
104
|
+
// 1. Discover the declared shots by SSR-loading the steps module (same
|
|
105
|
+
// Vite resolution as the build below). Before the build on purpose:
|
|
106
|
+
// authoring errors — bad shot names, no steps array — fail fast here,
|
|
107
|
+
// without paying for a bundle. An import failure is tolerated: the
|
|
108
|
+
// shots are then unknown and scenario.json omits the key.
|
|
109
|
+
const collectedShots = await collectPackShots(scenarioDir);
|
|
110
|
+
onLog(`Shots: ${describeCollectedShots(collectedShots)}`);
|
|
111
|
+
|
|
112
|
+
// 2. Generate the browser entry + index.html into the temp dir.
|
|
95
113
|
const stage = options.stage ?? false;
|
|
96
114
|
const entrySource = generateEmbedEntry({
|
|
97
115
|
scenarioDir,
|
|
@@ -109,39 +127,53 @@ export async function runPack(options: RunPackOptions): Promise<PackResult> {
|
|
|
109
127
|
await writeFile(join(tempDir, entryFileName), entrySource, "utf-8");
|
|
110
128
|
await writeFile(entryHtmlPath, generateEmbedHtml(scenarioId, entryFileName, stage), "utf-8");
|
|
111
129
|
|
|
112
|
-
//
|
|
130
|
+
// 3. Build the static bundle with Vite.
|
|
113
131
|
onLog("Bundling embed with Vite...");
|
|
114
132
|
await runViteBuild({ root: tempDir, outDir, entryHtmlPath });
|
|
115
133
|
|
|
116
|
-
//
|
|
134
|
+
// 4. Copy the narration manifest + audio. The embed fetches
|
|
117
135
|
// ./narration/manifest.json at runtime and resolves each clip's
|
|
118
136
|
// relative src against it, so both must ship in the bundle.
|
|
119
137
|
if (hasNarration) {
|
|
120
138
|
await copyNarration(scenarioDir, outDir);
|
|
121
139
|
}
|
|
122
140
|
|
|
123
|
-
//
|
|
141
|
+
// 5. Write the required scenario.json descriptor, recording the canonical
|
|
124
142
|
// viewport baked into the bundle so `deploy`/`serve`/`publish` can derive
|
|
125
|
-
// a correctly-proportioned embed snippet (DD-004)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
143
|
+
// a correctly-proportioned embed snippet (DD-004), and — when known —
|
|
144
|
+
// the declared shot names so `shoot` and other tooling never boot a
|
|
145
|
+
// browser just to learn a bundle has nothing to capture.
|
|
146
|
+
await writeScenarioJson(
|
|
147
|
+
outDir,
|
|
148
|
+
scenarioId,
|
|
149
|
+
PACK_GENERATOR_VERSION,
|
|
150
|
+
{ width, height: shellHeight },
|
|
151
|
+
collectedShots.recorded ? collectedShots.shots : undefined,
|
|
152
|
+
);
|
|
130
153
|
|
|
131
|
-
//
|
|
154
|
+
// 5b. Copy the optional <scenar-embed> loader into the bundle as embed.js
|
|
132
155
|
// (sibling of index.html), so the enhanced snippet works on any static
|
|
133
156
|
// host (GitHub Pages, `serve`, the edge) with no extra setup. It lands
|
|
134
157
|
// before the manifest pass below so it ships as a first-class bundle file.
|
|
135
158
|
await copyEmbedLoader(outDir);
|
|
136
159
|
|
|
137
|
-
//
|
|
160
|
+
// 6. Compute + write the deploy-facing pack manifest (validates the bundle
|
|
138
161
|
// against the backend allowlist; throws on any violation).
|
|
139
162
|
const manifest = await buildPackManifest(outDir, scenarioId);
|
|
140
163
|
await verifyManifestFilesExist(outDir, manifest);
|
|
141
164
|
await writePackManifest(outDir, manifest);
|
|
142
165
|
|
|
143
166
|
const totalBytes = manifest.files.reduce((sum, f) => sum + f.sizeBytes, 0);
|
|
144
|
-
return {
|
|
167
|
+
return {
|
|
168
|
+
scenarioId,
|
|
169
|
+
outDir,
|
|
170
|
+
renderFilePath,
|
|
171
|
+
providersPath,
|
|
172
|
+
hasNarration,
|
|
173
|
+
shots: collectedShots.recorded ? collectedShots.shots : undefined,
|
|
174
|
+
manifest,
|
|
175
|
+
totalBytes,
|
|
176
|
+
};
|
|
145
177
|
} finally {
|
|
146
178
|
if (!options.keepTemp) {
|
|
147
179
|
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
@@ -168,6 +200,15 @@ async function copyNarration(scenarioDir: string, outDir: string): Promise<void>
|
|
|
168
200
|
}
|
|
169
201
|
}
|
|
170
202
|
|
|
203
|
+
/** One log-friendly line for the shot discovery outcome. */
|
|
204
|
+
function describeCollectedShots(collected: CollectedPackShots): string {
|
|
205
|
+
if (!collected.recorded) {
|
|
206
|
+
// Vite errors can run many lines; the first carries the message.
|
|
207
|
+
return `unknown — steps module not loadable under Node (${collected.reason.split("\n")[0]})`;
|
|
208
|
+
}
|
|
209
|
+
return collected.shots.length === 0 ? "none declared" : collected.shots.join(", ");
|
|
210
|
+
}
|
|
211
|
+
|
|
171
212
|
async function fileExists(path: string): Promise<boolean> {
|
|
172
213
|
try {
|
|
173
214
|
await access(path);
|
package/src/shoot/run-shoot.d.ts
CHANGED
|
@@ -47,6 +47,12 @@ export interface ShootResult {
|
|
|
47
47
|
* or renamed shot must never linger in a deployed bundle, and a manifest
|
|
48
48
|
* listing deleted files would fail its own existence check at the next
|
|
49
49
|
* publish.
|
|
50
|
+
*
|
|
51
|
+
* When scenario.json records an authoritatively empty shot list (pack ≥
|
|
52
|
+
* generator 0.0.2), the whole server + browser boot is skipped — both
|
|
53
|
+
* invariants above still hold on that path. A recorded non-empty list does
|
|
54
|
+
* NOT bypass the capture page: the running bundle stays the runtime truth
|
|
55
|
+
* for what gets shot; the record only proves there is something to boot for.
|
|
50
56
|
*/
|
|
51
57
|
export declare function runShoot(options: RunShootOptions): Promise<ShootResult>;
|
|
52
58
|
//# sourceMappingURL=run-shoot.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-shoot.d.ts","sourceRoot":"","sources":["../../../src/shoot/run-shoot.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run-shoot.d.ts","sourceRoot":"","sources":["../../../src/shoot/run-shoot.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAe,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF,uEAAuE;AACvE,eAAO,MAAM,UAAU,WAAW,CAAC;AAWnC,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;CACtD;AAED,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACzC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAsF7E"}
|
package/src/shoot/run-shoot.js
CHANGED
|
@@ -2,6 +2,7 @@ import { join, resolve } from "node:path";
|
|
|
2
2
|
import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import { PACK_MANIFEST_FILE, SCENARIO_JSON_FILE, buildPackManifest, verifyManifestFilesExist, writePackManifest, } from "../pack/pack-manifest.js";
|
|
4
4
|
import { readBundleViewport } from "../bundle/read-viewport.js";
|
|
5
|
+
import { readBundleShots } from "../bundle/read-shots.js";
|
|
5
6
|
import { startBundleServer } from "../serve/static-server.js";
|
|
6
7
|
import { createPlaywrightShotBrowser } from "./playwright-browser.js";
|
|
7
8
|
/** Directory inside the bundle where stills land (and deploy from). */
|
|
@@ -28,6 +29,12 @@ const VIEWPORT_MARGIN_PX = 64;
|
|
|
28
29
|
* or renamed shot must never linger in a deployed bundle, and a manifest
|
|
29
30
|
* listing deleted files would fail its own existence check at the next
|
|
30
31
|
* publish.
|
|
32
|
+
*
|
|
33
|
+
* When scenario.json records an authoritatively empty shot list (pack ≥
|
|
34
|
+
* generator 0.0.2), the whole server + browser boot is skipped — both
|
|
35
|
+
* invariants above still hold on that path. A recorded non-empty list does
|
|
36
|
+
* NOT bypass the capture page: the running bundle stays the runtime truth
|
|
37
|
+
* for what gets shot; the record only proves there is something to boot for.
|
|
31
38
|
*/
|
|
32
39
|
export async function runShoot(options) {
|
|
33
40
|
const onLog = options.onLog ?? (() => { });
|
|
@@ -41,6 +48,17 @@ export async function runShoot(options) {
|
|
|
41
48
|
onLog(`Viewport: ${viewport.width}x${viewport.height} (DPR 2)`);
|
|
42
49
|
onLog(`Themes: ${themes.join(", ")}`);
|
|
43
50
|
await rm(join(bundleDir, STILLS_DIR), { recursive: true, force: true });
|
|
51
|
+
// Short-circuit on pack's authority: a recorded-and-empty shot list means
|
|
52
|
+
// there is provably nothing to capture, so don't pay for a static server
|
|
53
|
+
// and a Chromium launch just to learn that. Absent record = unknown — fall
|
|
54
|
+
// through and let the capture page answer, exactly as before the record
|
|
55
|
+
// existed.
|
|
56
|
+
const recordedShots = await readBundleShots(bundleDir);
|
|
57
|
+
if (recordedShots.recorded && recordedShots.shots.length === 0) {
|
|
58
|
+
onLog("No steps declare a `shot` — nothing to capture.");
|
|
59
|
+
await rebuildManifest(bundleDir, scenarioId);
|
|
60
|
+
return { scenarioId, bundleDir, shots: [], files: [], themes, verified: false };
|
|
61
|
+
}
|
|
44
62
|
const server = await startBundleServer({ rootDir: bundleDir, port: 0 });
|
|
45
63
|
let browser;
|
|
46
64
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-shoot.js","sourceRoot":"","sources":["../../../src/shoot/run-shoot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAGtE,uEAAuE;AACvE,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC;AAEnC,qEAAqE;AACrE,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC;;;GAGG;AACH,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAmC9B
|
|
1
|
+
{"version":3,"file":"run-shoot.js","sourceRoot":"","sources":["../../../src/shoot/run-shoot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAGtE,uEAAuE;AACvE,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC;AAEnC,qEAAqE;AACrE,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC;;;GAGG;AACH,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAmC9B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAwB;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAK,CAAC,OAAO,EAAE,MAAM,CAAW,CAAC;IAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,kBAAkB,CAAC;IAE1D,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;IACnD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAEzD,KAAK,CAAC,cAAc,UAAU,EAAE,CAAC,CAAC;IAClC,KAAK,CAAC,cAAc,SAAS,EAAE,CAAC,CAAC;IACjC,KAAK,CAAC,cAAc,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,MAAM,UAAU,CAAC,CAAC;IACjE,KAAK,CAAC,cAAc,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEzC,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAExE,0EAA0E;IAC1E,yEAAyE;IACzE,2EAA2E;IAC3E,wEAAwE;IACxE,WAAW;IACX,MAAM,aAAa,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;IACvD,IAAI,aAAa,CAAC,QAAQ,IAAI,aAAa,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/D,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACzD,MAAM,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAC7C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClF,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IACxE,IAAI,OAAgC,CAAC;IACrC,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,IAAI,2BAA2B,CAAC,EAAE,CAAC;QAE1E,MAAM,eAAe,GAAG;YACtB,KAAK,EAAE,QAAQ,CAAC,KAAK,GAAG,kBAAkB;YAC1C,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,kBAAkB;SAC7C,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA0D,CAAC;QACnF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC,CAAC;QAClG,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAE,CAAE,CAAC,IAAI,CAAC;QAC7C,4BAA4B,CAAC,QAAQ,CAAC,CAAC;QAEvC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,iDAAiD,CAAC,CAAC;YACzD,MAAM,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;YAC7C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAClF,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,6DAA6D,CAAC,CAAC;YACrE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;gBACzF,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAE,EAAE,KAAK,CAAC,CAAC;YAC1D,CAAC;YACD,KAAK,CAAC,mEAAmE,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC;YAC9C,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC7C,MAAM,QAAQ,GAAG,GAAG,UAAU,IAAI,IAAI,CAAC,IAAI,IAAI,KAAK,MAAM,CAAC;gBAC3D,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC;gBACrE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACrB,KAAK,CAAC,cAAc,QAAQ,EAAE,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QAED,MAAM,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAC7C,OAAO;YACL,UAAU;YACV,SAAS;YACT,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,KAAK;YACL,MAAM;YACN,QAAQ,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;SAClC,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACvC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,KAAK,UAAU,YAAY,CACzB,OAAoB,EACpB,OAAe,EACf,KAAgB,EAChB,QAA2C,EAC3C,SAAiB;IAEjB,yEAAyE;IACzE,qEAAqE;IACrE,wDAAwD;IACxD,MAAM,GAAG,GAAG,GAAG,OAAO,QAAQ,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACtE,MAAM,OAAO,GAAgB,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAChD,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,oEAAoE;QACpE,0DAA0D;QAC1D,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,KAAK,UAAU,cAAc,CAAC,SAAiB;IAC7C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACrD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,iEAAiE,CAAC,CAAC;IACjG,CAAC;IACD,KAAK,MAAM,QAAQ,IAAI,CAAC,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,CAAC,EAAE,CAAC;QAC9E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACzE,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACb,MAAM,QAAQ,OAAO,SAAS,4DAA4D,CAC3F,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAC7B,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,OAAO,CAAC,CACzC,CAAC;IACtB,IAAI,OAAO,YAAY,CAAC,EAAE,KAAK,QAAQ,IAAI,YAAY,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,GAAG,kBAAkB,OAAO,SAAS,sBAAsB,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,YAAY,CAAC,EAAE,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,SAAS,4BAA4B,CACnC,QAAqE;IAErE,MAAM,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CACvC,CAAC,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAU,CACpE,CAAC;IACF,MAAM,CAAC,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IAChC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAClC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,uBAAuB,KAAK,gDAAgD,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,EAAE,CAC3F,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,SAAS,mBAAmB,CAC1B,KAAgB,EAChB,KAAkD,EAClD,MAAmD;IAEnD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK;SAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC;SAC7D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,6BAA6B,KAAK,cAAc,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY;YAC9E,uEAAuE;YACvE,yEAAyE,CAC5E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,eAAe,CAAC,SAAiB,EAAE,UAAkB;IAClE,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAChE,MAAM,wBAAwB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACpD,MAAM,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC/C,CAAC"}
|
package/src/shoot/run-shoot.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
writePackManifest,
|
|
9
9
|
} from "../pack/pack-manifest.js";
|
|
10
10
|
import { readBundleViewport } from "../bundle/read-viewport.js";
|
|
11
|
+
import { readBundleShots } from "../bundle/read-shots.js";
|
|
11
12
|
import { startBundleServer } from "../serve/static-server.js";
|
|
12
13
|
import { createPlaywrightShotBrowser } from "./playwright-browser.js";
|
|
13
14
|
import type { ShotBrowser, ShotCaptureInfo, ShotSession, ShotTheme } from "./types.js";
|
|
@@ -72,6 +73,12 @@ export interface ShootResult {
|
|
|
72
73
|
* or renamed shot must never linger in a deployed bundle, and a manifest
|
|
73
74
|
* listing deleted files would fail its own existence check at the next
|
|
74
75
|
* publish.
|
|
76
|
+
*
|
|
77
|
+
* When scenario.json records an authoritatively empty shot list (pack ≥
|
|
78
|
+
* generator 0.0.2), the whole server + browser boot is skipped — both
|
|
79
|
+
* invariants above still hold on that path. A recorded non-empty list does
|
|
80
|
+
* NOT bypass the capture page: the running bundle stays the runtime truth
|
|
81
|
+
* for what gets shot; the record only proves there is something to boot for.
|
|
75
82
|
*/
|
|
76
83
|
export async function runShoot(options: RunShootOptions): Promise<ShootResult> {
|
|
77
84
|
const onLog = options.onLog ?? (() => {});
|
|
@@ -89,6 +96,18 @@ export async function runShoot(options: RunShootOptions): Promise<ShootResult> {
|
|
|
89
96
|
|
|
90
97
|
await rm(join(bundleDir, STILLS_DIR), { recursive: true, force: true });
|
|
91
98
|
|
|
99
|
+
// Short-circuit on pack's authority: a recorded-and-empty shot list means
|
|
100
|
+
// there is provably nothing to capture, so don't pay for a static server
|
|
101
|
+
// and a Chromium launch just to learn that. Absent record = unknown — fall
|
|
102
|
+
// through and let the capture page answer, exactly as before the record
|
|
103
|
+
// existed.
|
|
104
|
+
const recordedShots = await readBundleShots(bundleDir);
|
|
105
|
+
if (recordedShots.recorded && recordedShots.shots.length === 0) {
|
|
106
|
+
onLog("No steps declare a `shot` — nothing to capture.");
|
|
107
|
+
await rebuildManifest(bundleDir, scenarioId);
|
|
108
|
+
return { scenarioId, bundleDir, shots: [], files: [], themes, verified: false };
|
|
109
|
+
}
|
|
110
|
+
|
|
92
111
|
const server = await startBundleServer({ rootDir: bundleDir, port: 0 });
|
|
93
112
|
let browser: ShotBrowser | undefined;
|
|
94
113
|
try {
|
package/src/util/load-ts.d.ts
CHANGED
|
@@ -7,10 +7,20 @@ export interface ImportedStep {
|
|
|
7
7
|
delayMs: number;
|
|
8
8
|
narration?: string;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Find the steps array in a loaded module's exports by duck-typing: the
|
|
12
|
+
* first exported array whose first element has a `delayMs` property.
|
|
13
|
+
* Returns null when the module exports no such array.
|
|
14
|
+
*
|
|
15
|
+
* This is THE steps-discovery rule, shared by every Node-side loader and
|
|
16
|
+
* mirrored verbatim by the browser-side `_findSteps` in the generated
|
|
17
|
+
* pack entry (see generate-embed-entry.ts) — the mirrors must agree, or
|
|
18
|
+
* pack-time tooling would see different steps than the packed bundle.
|
|
19
|
+
*/
|
|
20
|
+
export declare function findStepsArray(exports: Record<string, unknown>): ImportedStep[] | null;
|
|
10
21
|
/**
|
|
11
22
|
* Dynamically import a TypeScript steps file and extract the
|
|
12
|
-
* steps array by duck-typing (
|
|
13
|
-
* whose elements have a `delayMs` property).
|
|
23
|
+
* steps array by duck-typing ({@link findStepsArray}).
|
|
14
24
|
*
|
|
15
25
|
* Requires the caller's Node process to have a TypeScript loader
|
|
16
26
|
* active (e.g. running via `tsx`). The CLI itself does not depend
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-ts.d.ts","sourceRoot":"","sources":["../../../src/util/load-ts.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED
|
|
1
|
+
{"version":3,"file":"load-ts.d.ts","sourceRoot":"","sources":["../../../src/util/load-ts.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,YAAY,EAAE,GAAG,IAAI,CAatF;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAO/E"}
|
package/src/util/load-ts.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { pathToFileURL } from "node:url";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Find the steps array in a loaded module's exports by duck-typing: the
|
|
4
|
+
* first exported array whose first element has a `delayMs` property.
|
|
5
|
+
* Returns null when the module exports no such array.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* This is THE steps-discovery rule, shared by every Node-side loader and
|
|
8
|
+
* mirrored verbatim by the browser-side `_findSteps` in the generated
|
|
9
|
+
* pack entry (see generate-embed-entry.ts) — the mirrors must agree, or
|
|
10
|
+
* pack-time tooling would see different steps than the packed bundle.
|
|
10
11
|
*/
|
|
11
|
-
export
|
|
12
|
-
const mod = await import(pathToFileURL(filePath).href);
|
|
13
|
-
const exports = mod.default ?? mod;
|
|
12
|
+
export function findStepsArray(exports) {
|
|
14
13
|
for (const value of Object.values(exports)) {
|
|
15
14
|
if (Array.isArray(value) &&
|
|
16
15
|
value.length > 0 &&
|
|
@@ -20,6 +19,22 @@ export async function loadStepsFromTs(filePath) {
|
|
|
20
19
|
return value;
|
|
21
20
|
}
|
|
22
21
|
}
|
|
23
|
-
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Dynamically import a TypeScript steps file and extract the
|
|
26
|
+
* steps array by duck-typing ({@link findStepsArray}).
|
|
27
|
+
*
|
|
28
|
+
* Requires the caller's Node process to have a TypeScript loader
|
|
29
|
+
* active (e.g. running via `tsx`). The CLI itself does not depend
|
|
30
|
+
* on any TS compilation tool.
|
|
31
|
+
*/
|
|
32
|
+
export async function loadStepsFromTs(filePath) {
|
|
33
|
+
const mod = await import(pathToFileURL(filePath).href);
|
|
34
|
+
const steps = findStepsArray(mod.default ?? mod);
|
|
35
|
+
if (steps === null) {
|
|
36
|
+
throw new Error(`No steps array found in ${filePath}`);
|
|
37
|
+
}
|
|
38
|
+
return steps;
|
|
24
39
|
}
|
|
25
40
|
//# sourceMappingURL=load-ts.js.map
|
package/src/util/load-ts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-ts.js","sourceRoot":"","sources":["../../../src/util/load-ts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAYzC
|
|
1
|
+
{"version":3,"file":"load-ts.js","sourceRoot":"","sources":["../../../src/util/load-ts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAYzC;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgC;IAC7D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,IACE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACpB,KAAK,CAAC,MAAM,GAAG,CAAC;YAChB,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ;YAC5B,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;YACjB,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,EACrB,CAAC;YACD,OAAO,KAAuB,CAAC;QACjC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;IACjD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/src/util/load-ts.ts
CHANGED
|
@@ -11,18 +11,16 @@ export interface ImportedStep {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Find the steps array in a loaded module's exports by duck-typing: the
|
|
15
|
+
* first exported array whose first element has a `delayMs` property.
|
|
16
|
+
* Returns null when the module exports no such array.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* This is THE steps-discovery rule, shared by every Node-side loader and
|
|
19
|
+
* mirrored verbatim by the browser-side `_findSteps` in the generated
|
|
20
|
+
* pack entry (see generate-embed-entry.ts) — the mirrors must agree, or
|
|
21
|
+
* pack-time tooling would see different steps than the packed bundle.
|
|
21
22
|
*/
|
|
22
|
-
export
|
|
23
|
-
const mod = await import(pathToFileURL(filePath).href);
|
|
24
|
-
const exports = mod.default ?? mod;
|
|
25
|
-
|
|
23
|
+
export function findStepsArray(exports: Record<string, unknown>): ImportedStep[] | null {
|
|
26
24
|
for (const value of Object.values(exports)) {
|
|
27
25
|
if (
|
|
28
26
|
Array.isArray(value) &&
|
|
@@ -34,6 +32,22 @@ export async function loadStepsFromTs(filePath: string): Promise<ImportedStep[]>
|
|
|
34
32
|
return value as ImportedStep[];
|
|
35
33
|
}
|
|
36
34
|
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Dynamically import a TypeScript steps file and extract the
|
|
40
|
+
* steps array by duck-typing ({@link findStepsArray}).
|
|
41
|
+
*
|
|
42
|
+
* Requires the caller's Node process to have a TypeScript loader
|
|
43
|
+
* active (e.g. running via `tsx`). The CLI itself does not depend
|
|
44
|
+
* on any TS compilation tool.
|
|
45
|
+
*/
|
|
46
|
+
export async function loadStepsFromTs(filePath: string): Promise<ImportedStep[]> {
|
|
47
|
+
const mod = await import(pathToFileURL(filePath).href);
|
|
48
|
+
const steps = findStepsArray(mod.default ?? mod);
|
|
49
|
+
if (steps === null) {
|
|
50
|
+
throw new Error(`No steps array found in ${filePath}`);
|
|
51
|
+
}
|
|
52
|
+
return steps;
|
|
39
53
|
}
|