@scenar/cli 0.8.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-BjquDnTE.css +1 -0
- 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 +9 -5
- package/src/__tests__/collect-pack-shots.test.ts +97 -0
- package/src/__tests__/pack-generate-embed-entry.test.ts +68 -3
- 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 +319 -0
- package/src/api.d.ts +3 -0
- package/src/api.d.ts.map +1 -1
- package/src/api.js +2 -0
- package/src/api.js.map +1 -1
- package/src/api.ts +5 -0
- 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/commands/shoot.d.ts +3 -0
- package/src/commands/shoot.d.ts.map +1 -0
- package/src/commands/shoot.js +52 -0
- package/src/commands/shoot.js.map +1 -0
- package/src/commands/shoot.ts +72 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +2 -0
- package/src/index.js.map +1 -1
- package/src/index.ts +2 -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 +18 -0
- package/src/pack/generate-embed-entry.d.ts.map +1 -1
- package/src/pack/generate-embed-entry.js +92 -3
- package/src/pack/generate-embed-entry.js.map +1 -1
- package/src/pack/generate-embed-entry.ts +94 -3
- 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/playwright-browser.d.ts +9 -0
- package/src/shoot/playwright-browser.d.ts.map +1 -0
- package/src/shoot/playwright-browser.js +92 -0
- package/src/shoot/playwright-browser.js.map +1 -0
- package/src/shoot/playwright-browser.ts +121 -0
- package/src/shoot/run-shoot.d.ts +58 -0
- package/src/shoot/run-shoot.d.ts.map +1 -0
- package/src/shoot/run-shoot.js +190 -0
- package/src/shoot/run-shoot.js.map +1 -0
- package/src/shoot/run-shoot.ts +270 -0
- package/src/shoot/types.d.ts +40 -0
- package/src/shoot/types.d.ts.map +1 -0
- package/src/shoot/types.js +2 -0
- package/src/shoot/types.js.map +1 -0
- package/src/shoot/types.ts +40 -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-CBaiZ4ek.js +0 -147
- package/example-bundle/assets/style-Ci-h7jik.css +0 -1
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { registerNarrateCommand } from "./commands/narrate.js";
|
|
|
4
4
|
import { registerInstallCommand } from "./commands/install.js";
|
|
5
5
|
import { registerRenderCommand } from "./commands/render.js";
|
|
6
6
|
import { registerPackCommand } from "./commands/pack.js";
|
|
7
|
+
import { registerShootCommand } from "./commands/shoot.js";
|
|
7
8
|
import { registerServeCommand } from "./commands/serve.js";
|
|
8
9
|
import { registerTryCommand } from "./commands/try.js";
|
|
9
10
|
import { registerPublishCommand } from "./commands/publish.js";
|
|
@@ -21,6 +22,7 @@ export function createProgram(): Command {
|
|
|
21
22
|
registerRenderCommand(program);
|
|
22
23
|
registerInstallCommand(program);
|
|
23
24
|
registerPackCommand(program);
|
|
25
|
+
registerShootCommand(program);
|
|
24
26
|
registerServeCommand(program);
|
|
25
27
|
registerTryCommand(program);
|
|
26
28
|
registerPublishCommand(program);
|
package/src/pack/build.d.ts
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Vite build invocation for `scenar pack`.
|
|
2
|
+
* Vite loading + build invocation for `scenar pack`.
|
|
3
3
|
*
|
|
4
4
|
* Vite is loaded dynamically (like the Remotion bundler in `render`) so the CLI
|
|
5
5
|
* stays lean for users who only validate/narrate. The build runs in "app mode"
|
|
6
6
|
* from a generated index.html, producing a self-contained static bundle.
|
|
7
|
+
*
|
|
8
|
+
* The load and the resolution config are exported separately because pack has
|
|
9
|
+
* a second Vite consumer: `collect-pack-shots` SSR-loads the scenario's steps
|
|
10
|
+
* module to record its shot names in scenario.json. Both consumers must
|
|
11
|
+
* resolve the scenario's imports identically — what pack records may never
|
|
12
|
+
* disagree with what the bundle contains — so they share one config source.
|
|
13
|
+
*/
|
|
14
|
+
/** The subset of Vite's module surface pack uses (typed loosely — Vite is an optional peer). */
|
|
15
|
+
interface ViteModule {
|
|
16
|
+
build: (config: Record<string, unknown>) => Promise<unknown>;
|
|
17
|
+
createServer: (config: Record<string, unknown>) => Promise<ViteDevServer>;
|
|
18
|
+
}
|
|
19
|
+
/** The subset of Vite's dev-server surface `collect-pack-shots` uses. */
|
|
20
|
+
export interface ViteDevServer {
|
|
21
|
+
ssrLoadModule: (specifier: string) => Promise<Record<string, unknown>>;
|
|
22
|
+
close: () => Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/** Vite + the React plugin factory, dynamically loaded as a pair. */
|
|
25
|
+
export interface ViteToolkit {
|
|
26
|
+
readonly vite: ViteModule;
|
|
27
|
+
readonly react: (...args: unknown[]) => unknown;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Dynamically import Vite and @vitejs/plugin-react, with an actionable
|
|
31
|
+
* install hint when either is missing (they are optional peers).
|
|
32
|
+
*/
|
|
33
|
+
export declare function loadViteToolkit(): Promise<ViteToolkit>;
|
|
34
|
+
/**
|
|
35
|
+
* The resolution config shared by the pack build and the pack-time steps
|
|
36
|
+
* load — the parts that decide WHICH files an import specifier reaches.
|
|
7
37
|
*/
|
|
38
|
+
export declare function sharedViteConfig(toolkit: ViteToolkit): Record<string, unknown>;
|
|
8
39
|
export interface ViteBuildInput {
|
|
9
40
|
/** Build root — the temp dir holding index.html + the generated entry. */
|
|
10
41
|
root: string;
|
|
@@ -14,4 +45,5 @@ export interface ViteBuildInput {
|
|
|
14
45
|
entryHtmlPath: string;
|
|
15
46
|
}
|
|
16
47
|
export declare function runViteBuild(input: ViteBuildInput): Promise<void>;
|
|
48
|
+
export {};
|
|
17
49
|
//# sourceMappingURL=build.d.ts.map
|
package/src/pack/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/pack/build.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/pack/build.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,gGAAgG;AAChG,UAAU,UAAU;IAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;CAC3E;AAED,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,qEAAqE;AACrE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CACjD;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CAsB5D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQ9E;AAED,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBvE"}
|
package/src/pack/build.js
CHANGED
|
@@ -1,29 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Vite build invocation for `scenar pack`.
|
|
2
|
+
* Vite loading + build invocation for `scenar pack`.
|
|
3
3
|
*
|
|
4
4
|
* Vite is loaded dynamically (like the Remotion bundler in `render`) so the CLI
|
|
5
5
|
* stays lean for users who only validate/narrate. The build runs in "app mode"
|
|
6
6
|
* from a generated index.html, producing a self-contained static bundle.
|
|
7
|
+
*
|
|
8
|
+
* The load and the resolution config are exported separately because pack has
|
|
9
|
+
* a second Vite consumer: `collect-pack-shots` SSR-loads the scenario's steps
|
|
10
|
+
* module to record its shot names in scenario.json. Both consumers must
|
|
11
|
+
* resolve the scenario's imports identically — what pack records may never
|
|
12
|
+
* disagree with what the bundle contains — so they share one config source.
|
|
7
13
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Dynamically import Vite and @vitejs/plugin-react, with an actionable
|
|
16
|
+
* install hint when either is missing (they are optional peers).
|
|
17
|
+
*/
|
|
18
|
+
export async function loadViteToolkit() {
|
|
19
|
+
const vite = await import("vite").catch((error) => {
|
|
10
20
|
throw new Error("Could not load vite.\n" +
|
|
11
21
|
"scenar pack bundles the embed with Vite. Install it:\n" +
|
|
12
|
-
" pnpm add -D vite @vitejs/plugin-react"
|
|
22
|
+
" pnpm add -D vite @vitejs/plugin-react\n" +
|
|
23
|
+
`(import failed with: ${error instanceof Error ? error.message : String(error)})`);
|
|
13
24
|
});
|
|
14
|
-
const reactPluginMod = await import("@vitejs/plugin-react").catch(() => {
|
|
25
|
+
const reactPluginMod = await import("@vitejs/plugin-react").catch((error) => {
|
|
15
26
|
throw new Error("Could not load @vitejs/plugin-react.\n" +
|
|
16
|
-
"Install it: pnpm add -D vite @vitejs/plugin-react"
|
|
27
|
+
"Install it: pnpm add -D vite @vitejs/plugin-react\n" +
|
|
28
|
+
`(import failed with: ${error instanceof Error ? error.message : String(error)})`);
|
|
17
29
|
});
|
|
18
30
|
const react = (reactPluginMod.default ?? reactPluginMod);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
return { vite: vite, react };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The resolution config shared by the pack build and the pack-time steps
|
|
35
|
+
* load — the parts that decide WHICH files an import specifier reaches.
|
|
36
|
+
*/
|
|
37
|
+
export function sharedViteConfig(toolkit) {
|
|
38
|
+
return {
|
|
22
39
|
configFile: false, // never pick up a vite.config.* from the consumer project.
|
|
23
40
|
logLevel: "warn",
|
|
24
|
-
plugins: [react()],
|
|
41
|
+
plugins: [toolkit.react()],
|
|
25
42
|
// A single React copy — the scenario, @scenar/react, and react-dom must agree.
|
|
26
43
|
resolve: { dedupe: ["react", "react-dom"] },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export async function runViteBuild(input) {
|
|
47
|
+
const toolkit = await loadViteToolkit();
|
|
48
|
+
await toolkit.vite.build({
|
|
49
|
+
...sharedViteConfig(toolkit),
|
|
50
|
+
root: input.root,
|
|
51
|
+
base: "./", // relative asset URLs: the bundle is served from a per-deploy origin root.
|
|
27
52
|
build: {
|
|
28
53
|
outDir: input.outDir,
|
|
29
54
|
emptyOutDir: true,
|
package/src/pack/build.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/pack/build.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/pack/build.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoBH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACzD,MAAM,IAAI,KAAK,CACb,wBAAwB;YACtB,wDAAwD;YACxD,2CAA2C;YAC3C,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CACpF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACnF,MAAM,IAAI,KAAK,CACb,wCAAwC;YACtC,qDAAqD;YACrD,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CACpF,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,CAAC,cAAc,CAAC,OAAO,IAAI,cAAc,CAE3C,CAAC;IAEb,OAAO,EAAE,IAAI,EAAE,IAA6B,EAAE,KAAK,EAAE,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAoB;IACnD,OAAO;QACL,UAAU,EAAE,KAAK,EAAE,2DAA2D;QAC9E,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC1B,+EAA+E;QAC/E,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE;KAC5C,CAAC;AACJ,CAAC;AAWD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAqB;IACtD,MAAM,OAAO,GAAG,MAAM,eAAe,EAAE,CAAC;IAExC,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QACvB,GAAG,gBAAgB,CAAC,OAAO,CAAC;QAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,IAAI,EAAE,2EAA2E;QACvF,KAAK,EAAE;YACL,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,WAAW,EAAE,IAAI;YACjB,YAAY,EAAE,KAAK,EAAE,iDAAiD;YACtE,SAAS,EAAE,KAAK,EAAE,uCAAuC;YACzD,mFAAmF;YACnF,qFAAqF;YACrF,gFAAgF;YAChF,QAAQ,EAAE,KAAK,EAAE,wDAAwD;YACzE,gFAAgF;YAChF,aAAa,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;YAClC,aAAa,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,aAAa,EAAE;SAC9C;KACF,CAAC,CAAC;AACL,CAAC"}
|
package/src/pack/build.ts
CHANGED
|
@@ -1,47 +1,93 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Vite build invocation for `scenar pack`.
|
|
2
|
+
* Vite loading + build invocation for `scenar pack`.
|
|
3
3
|
*
|
|
4
4
|
* Vite is loaded dynamically (like the Remotion bundler in `render`) so the CLI
|
|
5
5
|
* stays lean for users who only validate/narrate. The build runs in "app mode"
|
|
6
6
|
* from a generated index.html, producing a self-contained static bundle.
|
|
7
|
+
*
|
|
8
|
+
* The load and the resolution config are exported separately because pack has
|
|
9
|
+
* a second Vite consumer: `collect-pack-shots` SSR-loads the scenario's steps
|
|
10
|
+
* module to record its shot names in scenario.json. Both consumers must
|
|
11
|
+
* resolve the scenario's imports identically — what pack records may never
|
|
12
|
+
* disagree with what the bundle contains — so they share one config source.
|
|
7
13
|
*/
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
outDir: string;
|
|
14
|
-
/** Absolute path to the generated index.html (the Rollup input). */
|
|
15
|
-
entryHtmlPath: string;
|
|
15
|
+
/** The subset of Vite's module surface pack uses (typed loosely — Vite is an optional peer). */
|
|
16
|
+
interface ViteModule {
|
|
17
|
+
build: (config: Record<string, unknown>) => Promise<unknown>;
|
|
18
|
+
createServer: (config: Record<string, unknown>) => Promise<ViteDevServer>;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
/** The subset of Vite's dev-server surface `collect-pack-shots` uses. */
|
|
22
|
+
export interface ViteDevServer {
|
|
23
|
+
ssrLoadModule: (specifier: string) => Promise<Record<string, unknown>>;
|
|
24
|
+
close: () => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Vite + the React plugin factory, dynamically loaded as a pair. */
|
|
28
|
+
export interface ViteToolkit {
|
|
29
|
+
readonly vite: ViteModule;
|
|
30
|
+
readonly react: (...args: unknown[]) => unknown;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Dynamically import Vite and @vitejs/plugin-react, with an actionable
|
|
35
|
+
* install hint when either is missing (they are optional peers).
|
|
36
|
+
*/
|
|
37
|
+
export async function loadViteToolkit(): Promise<ViteToolkit> {
|
|
38
|
+
const vite = await import("vite").catch((error: unknown) => {
|
|
20
39
|
throw new Error(
|
|
21
40
|
"Could not load vite.\n" +
|
|
22
41
|
"scenar pack bundles the embed with Vite. Install it:\n" +
|
|
23
|
-
" pnpm add -D vite @vitejs/plugin-react"
|
|
42
|
+
" pnpm add -D vite @vitejs/plugin-react\n" +
|
|
43
|
+
`(import failed with: ${error instanceof Error ? error.message : String(error)})`,
|
|
24
44
|
);
|
|
25
45
|
});
|
|
26
46
|
|
|
27
|
-
const reactPluginMod = await import("@vitejs/plugin-react").catch(() => {
|
|
47
|
+
const reactPluginMod = await import("@vitejs/plugin-react").catch((error: unknown) => {
|
|
28
48
|
throw new Error(
|
|
29
49
|
"Could not load @vitejs/plugin-react.\n" +
|
|
30
|
-
"Install it: pnpm add -D vite @vitejs/plugin-react"
|
|
50
|
+
"Install it: pnpm add -D vite @vitejs/plugin-react\n" +
|
|
51
|
+
`(import failed with: ${error instanceof Error ? error.message : String(error)})`,
|
|
31
52
|
);
|
|
32
53
|
});
|
|
33
54
|
const react = (reactPluginMod.default ?? reactPluginMod) as (
|
|
34
55
|
...args: unknown[]
|
|
35
56
|
) => unknown;
|
|
36
57
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
58
|
+
return { vite: vite as unknown as ViteModule, react };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The resolution config shared by the pack build and the pack-time steps
|
|
63
|
+
* load — the parts that decide WHICH files an import specifier reaches.
|
|
64
|
+
*/
|
|
65
|
+
export function sharedViteConfig(toolkit: ViteToolkit): Record<string, unknown> {
|
|
66
|
+
return {
|
|
40
67
|
configFile: false, // never pick up a vite.config.* from the consumer project.
|
|
41
68
|
logLevel: "warn",
|
|
42
|
-
plugins: [react()],
|
|
69
|
+
plugins: [toolkit.react()],
|
|
43
70
|
// A single React copy — the scenario, @scenar/react, and react-dom must agree.
|
|
44
71
|
resolve: { dedupe: ["react", "react-dom"] },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ViteBuildInput {
|
|
76
|
+
/** Build root — the temp dir holding index.html + the generated entry. */
|
|
77
|
+
root: string;
|
|
78
|
+
/** Absolute output directory for the static bundle. */
|
|
79
|
+
outDir: string;
|
|
80
|
+
/** Absolute path to the generated index.html (the Rollup input). */
|
|
81
|
+
entryHtmlPath: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function runViteBuild(input: ViteBuildInput): Promise<void> {
|
|
85
|
+
const toolkit = await loadViteToolkit();
|
|
86
|
+
|
|
87
|
+
await toolkit.vite.build({
|
|
88
|
+
...sharedViteConfig(toolkit),
|
|
89
|
+
root: input.root,
|
|
90
|
+
base: "./", // relative asset URLs: the bundle is served from a per-deploy origin root.
|
|
45
91
|
build: {
|
|
46
92
|
outDir: input.outDir,
|
|
47
93
|
emptyOutDir: true,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The outcome of pack-time shot discovery.
|
|
3
|
+
*
|
|
4
|
+
* `recorded: true` is authoritative — `shots` is exactly what the packed
|
|
5
|
+
* capture page will report (possibly empty, which lets `scenar shoot` skip
|
|
6
|
+
* the browser entirely). `recorded: false` means the steps module could not
|
|
7
|
+
* be loaded under Node, so the shots are unknown and scenario.json must omit
|
|
8
|
+
* the key: absent = "boot a browser to find out".
|
|
9
|
+
*/
|
|
10
|
+
export type CollectedPackShots = {
|
|
11
|
+
readonly recorded: true;
|
|
12
|
+
readonly shots: readonly string[];
|
|
13
|
+
} | {
|
|
14
|
+
readonly recorded: false;
|
|
15
|
+
readonly reason: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Discover a scenario's declared shot names at pack time, so they can be
|
|
19
|
+
* recorded in the bundle's scenario.json.
|
|
20
|
+
*
|
|
21
|
+
* The steps module is loaded through Vite's SSR pipeline using the exact
|
|
22
|
+
* specifier the generated browser entry imports ({@link stepsModuleSpecifier})
|
|
23
|
+
* and the same resolution config as the pack build ({@link sharedViteConfig}) —
|
|
24
|
+
* so what pack records and what the bundle contains agree by construction.
|
|
25
|
+
* This is runtime truth, not source-text approximation: a shot name built
|
|
26
|
+
* from a constant is discovered just like a literal one.
|
|
27
|
+
*
|
|
28
|
+
* Failure semantics split by who is at fault:
|
|
29
|
+
* - *Environmental* — the module fails to import under Node's SSR runtime
|
|
30
|
+
* (e.g. it touches `window` at import time). Returns `recorded: false`
|
|
31
|
+
* with the reason; the caller packs on without a `shots` record. Anything
|
|
32
|
+
* that packs today still packs.
|
|
33
|
+
* - *Authoring* — the module imports but is broken: no `delayMs`-bearing
|
|
34
|
+
* steps array (the packed embed would throw the same error in the browser
|
|
35
|
+
* the moment it loads), or an invalid/duplicate shot name (the capture
|
|
36
|
+
* page would throw at shoot time). Throws, failing the pack — these
|
|
37
|
+
* bundles must not ship, and the error is most fixable right here.
|
|
38
|
+
*/
|
|
39
|
+
export declare function collectPackShots(scenarioDir: string): Promise<CollectedPackShots>;
|
|
40
|
+
//# sourceMappingURL=collect-pack-shots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-pack-shots.d.ts","sourceRoot":"","sources":["../../../src/pack/collect-pack-shots.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC9D;IAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAmCvF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { collectShotNames } from "@scenar/core";
|
|
2
|
+
import { findStepsArray } from "../util/load-ts.js";
|
|
3
|
+
import { loadViteToolkit, sharedViteConfig } from "./build.js";
|
|
4
|
+
import { stepsModuleSpecifier } from "./generate-embed-entry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Discover a scenario's declared shot names at pack time, so they can be
|
|
7
|
+
* recorded in the bundle's scenario.json.
|
|
8
|
+
*
|
|
9
|
+
* The steps module is loaded through Vite's SSR pipeline using the exact
|
|
10
|
+
* specifier the generated browser entry imports ({@link stepsModuleSpecifier})
|
|
11
|
+
* and the same resolution config as the pack build ({@link sharedViteConfig}) —
|
|
12
|
+
* so what pack records and what the bundle contains agree by construction.
|
|
13
|
+
* This is runtime truth, not source-text approximation: a shot name built
|
|
14
|
+
* from a constant is discovered just like a literal one.
|
|
15
|
+
*
|
|
16
|
+
* Failure semantics split by who is at fault:
|
|
17
|
+
* - *Environmental* — the module fails to import under Node's SSR runtime
|
|
18
|
+
* (e.g. it touches `window` at import time). Returns `recorded: false`
|
|
19
|
+
* with the reason; the caller packs on without a `shots` record. Anything
|
|
20
|
+
* that packs today still packs.
|
|
21
|
+
* - *Authoring* — the module imports but is broken: no `delayMs`-bearing
|
|
22
|
+
* steps array (the packed embed would throw the same error in the browser
|
|
23
|
+
* the moment it loads), or an invalid/duplicate shot name (the capture
|
|
24
|
+
* page would throw at shoot time). Throws, failing the pack — these
|
|
25
|
+
* bundles must not ship, and the error is most fixable right here.
|
|
26
|
+
*/
|
|
27
|
+
export async function collectPackShots(scenarioDir) {
|
|
28
|
+
const specifier = stepsModuleSpecifier(scenarioDir);
|
|
29
|
+
const toolkit = await loadViteToolkit();
|
|
30
|
+
const server = await toolkit.vite.createServer({
|
|
31
|
+
...sharedViteConfig(toolkit),
|
|
32
|
+
root: scenarioDir,
|
|
33
|
+
// A transform-only server: no HTTP listener, no HMR websocket, and no
|
|
34
|
+
// dependency pre-bundling scan — ssrLoadModule is the only consumer.
|
|
35
|
+
server: { middlewareMode: true, hmr: false },
|
|
36
|
+
optimizeDeps: { noDiscovery: true },
|
|
37
|
+
});
|
|
38
|
+
let stepsModule;
|
|
39
|
+
try {
|
|
40
|
+
stepsModule = await server.ssrLoadModule(specifier);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return {
|
|
44
|
+
recorded: false,
|
|
45
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
await server.close();
|
|
50
|
+
}
|
|
51
|
+
const steps = findStepsArray(stepsModule);
|
|
52
|
+
if (steps === null) {
|
|
53
|
+
throw new Error(`No steps array found in ${specifier} — expected an exported array whose ` +
|
|
54
|
+
`first element has a delayMs property. The packed embed would throw this ` +
|
|
55
|
+
`same error in the browser the moment it loads, so pack refuses to ship it.`);
|
|
56
|
+
}
|
|
57
|
+
return { recorded: true, shots: collectShotNames(steps) };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=collect-pack-shots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-pack-shots.js","sourceRoot":"","sources":["../../../src/pack/collect-pack-shots.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAejE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,WAAmB;IACxD,MAAM,SAAS,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAEpD,MAAM,OAAO,GAAG,MAAM,eAAe,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;QAC7C,GAAG,gBAAgB,CAAC,OAAO,CAAC;QAC5B,IAAI,EAAE,WAAW;QACjB,sEAAsE;QACtE,qEAAqE;QACrE,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE;QAC5C,YAAY,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;KACpC,CAAC,CAAC;IAEH,IAAI,WAAoC,CAAC;IACzC,IAAI,CAAC;QACH,WAAW,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IACtD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC/D,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,MAAM,KAAK,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;IAC1C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,2BAA2B,SAAS,sCAAsC;YACxE,0EAA0E;YAC1E,4EAA4E,CAC/E,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { collectShotNames } from "@scenar/core";
|
|
2
|
+
import { findStepsArray } from "../util/load-ts.js";
|
|
3
|
+
import { loadViteToolkit, sharedViteConfig } from "./build.js";
|
|
4
|
+
import { stepsModuleSpecifier } from "./generate-embed-entry.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The outcome of pack-time shot discovery.
|
|
8
|
+
*
|
|
9
|
+
* `recorded: true` is authoritative — `shots` is exactly what the packed
|
|
10
|
+
* capture page will report (possibly empty, which lets `scenar shoot` skip
|
|
11
|
+
* the browser entirely). `recorded: false` means the steps module could not
|
|
12
|
+
* be loaded under Node, so the shots are unknown and scenario.json must omit
|
|
13
|
+
* the key: absent = "boot a browser to find out".
|
|
14
|
+
*/
|
|
15
|
+
export type CollectedPackShots =
|
|
16
|
+
| { readonly recorded: true; readonly shots: readonly string[] }
|
|
17
|
+
| { readonly recorded: false; readonly reason: string };
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Discover a scenario's declared shot names at pack time, so they can be
|
|
21
|
+
* recorded in the bundle's scenario.json.
|
|
22
|
+
*
|
|
23
|
+
* The steps module is loaded through Vite's SSR pipeline using the exact
|
|
24
|
+
* specifier the generated browser entry imports ({@link stepsModuleSpecifier})
|
|
25
|
+
* and the same resolution config as the pack build ({@link sharedViteConfig}) —
|
|
26
|
+
* so what pack records and what the bundle contains agree by construction.
|
|
27
|
+
* This is runtime truth, not source-text approximation: a shot name built
|
|
28
|
+
* from a constant is discovered just like a literal one.
|
|
29
|
+
*
|
|
30
|
+
* Failure semantics split by who is at fault:
|
|
31
|
+
* - *Environmental* — the module fails to import under Node's SSR runtime
|
|
32
|
+
* (e.g. it touches `window` at import time). Returns `recorded: false`
|
|
33
|
+
* with the reason; the caller packs on without a `shots` record. Anything
|
|
34
|
+
* that packs today still packs.
|
|
35
|
+
* - *Authoring* — the module imports but is broken: no `delayMs`-bearing
|
|
36
|
+
* steps array (the packed embed would throw the same error in the browser
|
|
37
|
+
* the moment it loads), or an invalid/duplicate shot name (the capture
|
|
38
|
+
* page would throw at shoot time). Throws, failing the pack — these
|
|
39
|
+
* bundles must not ship, and the error is most fixable right here.
|
|
40
|
+
*/
|
|
41
|
+
export async function collectPackShots(scenarioDir: string): Promise<CollectedPackShots> {
|
|
42
|
+
const specifier = stepsModuleSpecifier(scenarioDir);
|
|
43
|
+
|
|
44
|
+
const toolkit = await loadViteToolkit();
|
|
45
|
+
const server = await toolkit.vite.createServer({
|
|
46
|
+
...sharedViteConfig(toolkit),
|
|
47
|
+
root: scenarioDir,
|
|
48
|
+
// A transform-only server: no HTTP listener, no HMR websocket, and no
|
|
49
|
+
// dependency pre-bundling scan — ssrLoadModule is the only consumer.
|
|
50
|
+
server: { middlewareMode: true, hmr: false },
|
|
51
|
+
optimizeDeps: { noDiscovery: true },
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
let stepsModule: Record<string, unknown>;
|
|
55
|
+
try {
|
|
56
|
+
stepsModule = await server.ssrLoadModule(specifier);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return {
|
|
59
|
+
recorded: false,
|
|
60
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
61
|
+
};
|
|
62
|
+
} finally {
|
|
63
|
+
await server.close();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const steps = findStepsArray(stepsModule);
|
|
67
|
+
if (steps === null) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`No steps array found in ${specifier} — expected an exported array whose ` +
|
|
70
|
+
`first element has a delayMs property. The packed embed would throw this ` +
|
|
71
|
+
`same error in the browser the moment it loads, so pack refuses to ship it.`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { recorded: true, shots: collectShotNames(steps) };
|
|
76
|
+
}
|
|
@@ -47,8 +47,26 @@ export interface EmbedEntryInput {
|
|
|
47
47
|
* selector). The default is light, so existing embeds are unaffected; a host can
|
|
48
48
|
* theme-sync simply by framing `…/?theme=dark`.
|
|
49
49
|
*
|
|
50
|
+
* Capture (`?shot`): with a `shot` query param present, the entry mounts
|
|
51
|
+
* `ScenarioCaptureMount` instead of the playback tree — the TimeSource-driven
|
|
52
|
+
* catch-up mount behind `scenar shoot` (DD-02). The driver installs on
|
|
53
|
+
* `window.__scenarShot`; failures surface on `window.__scenarShotError`.
|
|
54
|
+
* `?shot=<name>` additionally auto-walks to that shot, so an author can
|
|
55
|
+
* eyeball any still in a plain browser. When the scenario has narration, the
|
|
56
|
+
* capture branch fetches the manifest itself and fails LOUDLY on error —
|
|
57
|
+
* step durations are narration-driven, so a silent fallback would capture
|
|
58
|
+
* every shot at the wrong time (which is why it must not reuse
|
|
59
|
+
* `useNarrationManifest`, whose fetch failure is silent by design).
|
|
60
|
+
*
|
|
50
61
|
* Pure function, no side effects — the caller writes the result to disk.
|
|
51
62
|
*/
|
|
63
|
+
/**
|
|
64
|
+
* The import specifier the generated entry uses for the scenario's steps
|
|
65
|
+
* module (extensionless — Vite resolves .ts/.tsx). Exported so pack-time
|
|
66
|
+
* tooling (`collect-pack-shots`) loads the very same module the bundle bakes
|
|
67
|
+
* in, rather than re-deriving a path that could drift from it.
|
|
68
|
+
*/
|
|
69
|
+
export declare function stepsModuleSpecifier(scenarioDir: string): string;
|
|
52
70
|
export declare function generateEmbedEntry(input: EmbedEntryInput): string;
|
|
53
71
|
/**
|
|
54
72
|
* Staged pre-paint backdrop — a hand-maintained mirror of the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-embed-entry.d.ts","sourceRoot":"","sources":["../../../src/pack/generate-embed-entry.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,YAAY,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED
|
|
1
|
+
{"version":3,"file":"generate-embed-entry.d.ts","sourceRoot":"","sources":["../../../src/pack/generate-embed-entry.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,YAAY,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAwPjE;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAMA,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAKhD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,KAAK,UAAQ,GACZ,MAAM,CAmCR"}
|
|
@@ -20,11 +20,30 @@ import { posix } from "node:path";
|
|
|
20
20
|
* selector). The default is light, so existing embeds are unaffected; a host can
|
|
21
21
|
* theme-sync simply by framing `…/?theme=dark`.
|
|
22
22
|
*
|
|
23
|
+
* Capture (`?shot`): with a `shot` query param present, the entry mounts
|
|
24
|
+
* `ScenarioCaptureMount` instead of the playback tree — the TimeSource-driven
|
|
25
|
+
* catch-up mount behind `scenar shoot` (DD-02). The driver installs on
|
|
26
|
+
* `window.__scenarShot`; failures surface on `window.__scenarShotError`.
|
|
27
|
+
* `?shot=<name>` additionally auto-walks to that shot, so an author can
|
|
28
|
+
* eyeball any still in a plain browser. When the scenario has narration, the
|
|
29
|
+
* capture branch fetches the manifest itself and fails LOUDLY on error —
|
|
30
|
+
* step durations are narration-driven, so a silent fallback would capture
|
|
31
|
+
* every shot at the wrong time (which is why it must not reuse
|
|
32
|
+
* `useNarrationManifest`, whose fetch failure is silent by design).
|
|
33
|
+
*
|
|
23
34
|
* Pure function, no side effects — the caller writes the result to disk.
|
|
24
35
|
*/
|
|
36
|
+
/**
|
|
37
|
+
* The import specifier the generated entry uses for the scenario's steps
|
|
38
|
+
* module (extensionless — Vite resolves .ts/.tsx). Exported so pack-time
|
|
39
|
+
* tooling (`collect-pack-shots`) loads the very same module the bundle bakes
|
|
40
|
+
* in, rather than re-deriving a path that could drift from it.
|
|
41
|
+
*/
|
|
42
|
+
export function stepsModuleSpecifier(scenarioDir) {
|
|
43
|
+
return `${toPosix(scenarioDir)}/steps`;
|
|
44
|
+
}
|
|
25
45
|
export function generateEmbedEntry(input) {
|
|
26
|
-
const
|
|
27
|
-
const stepsImport = `${scenarioPath}/steps`;
|
|
46
|
+
const stepsImport = stepsModuleSpecifier(input.scenarioDir);
|
|
28
47
|
const renderImport = toPosix(input.renderFilePath).replace(/\.[^.]+$/, "");
|
|
29
48
|
const lines = [];
|
|
30
49
|
// --- Imports ---
|
|
@@ -42,6 +61,7 @@ export function generateEmbedEntry(input) {
|
|
|
42
61
|
"useStepInteractions",
|
|
43
62
|
"ViewportTransformLayer",
|
|
44
63
|
"VIEWPORT_TRANSFORM_IDENTITY",
|
|
64
|
+
"ScenarioCaptureMount",
|
|
45
65
|
];
|
|
46
66
|
if (input.stage)
|
|
47
67
|
reactImports.push("ScenarioStage");
|
|
@@ -90,6 +110,15 @@ export function generateEmbedEntry(input) {
|
|
|
90
110
|
lines.push(` catch { return null; }`);
|
|
91
111
|
lines.push(`})();`);
|
|
92
112
|
lines.push(`const _rootClass = _theme === "dark" ? SCENAR_CLASS + " dark" : SCENAR_CLASS;`);
|
|
113
|
+
// --- Capture mode: ?shot (bare = driver only; ?shot=<name> = auto-walk) ---
|
|
114
|
+
// Read the same way as _theme: once, at module load, outside React. A null
|
|
115
|
+
// value (param absent) means normal playback; "" (bare ?shot) means capture
|
|
116
|
+
// mode without an auto-walk target.
|
|
117
|
+
lines.push(``);
|
|
118
|
+
lines.push(`const _shotParam = (() => {`);
|
|
119
|
+
lines.push(` try { return new URLSearchParams(window.location.search).get("shot"); }`);
|
|
120
|
+
lines.push(` catch { return null; }`);
|
|
121
|
+
lines.push(`})();`);
|
|
93
122
|
// --- Page canvas: transparent when framed, theme surface when top-level ---
|
|
94
123
|
// The static HTML keeps html/body transparent (see generateEmbedHtml): a
|
|
95
124
|
// framed embed's canvas then shows the *host page's* background through any
|
|
@@ -183,11 +212,71 @@ export function generateEmbedEntry(input) {
|
|
|
183
212
|
lines.push(` </div>`);
|
|
184
213
|
lines.push(` );`);
|
|
185
214
|
lines.push(`}`);
|
|
215
|
+
// --- Capture mount (?shot) ---
|
|
216
|
+
// The whole capture tree lives in @scenar/react (ScenarioCaptureMount);
|
|
217
|
+
// this branch only feeds it the same inputs the playback tree gets and
|
|
218
|
+
// wires the window contract `scenar shoot` consumes: the driver on
|
|
219
|
+
// `window.__scenarShot`, failures on `window.__scenarShotError`. The
|
|
220
|
+
// narration manifest is fetched HERE, before mounting, and errors are
|
|
221
|
+
// fatal — shot times are narration-driven, so capturing without the
|
|
222
|
+
// manifest would silently shoot every frame at the wrong moment.
|
|
223
|
+
lines.push(``);
|
|
224
|
+
lines.push(`async function _mountCapture(rootEl: HTMLElement) {`);
|
|
225
|
+
if (input.hasNarration) {
|
|
226
|
+
lines.push(` const _manifestResponse = await fetch("./narration/manifest.json");`);
|
|
227
|
+
lines.push(` if (!_manifestResponse.ok) {`);
|
|
228
|
+
lines.push(` throw new Error(`);
|
|
229
|
+
lines.push(` "capture: failed to fetch ./narration/manifest.json (HTTP " +`);
|
|
230
|
+
lines.push(` _manifestResponse.status + ") — shot times are narration-driven, refusing to capture without it",`);
|
|
231
|
+
lines.push(` );`);
|
|
232
|
+
lines.push(` }`);
|
|
233
|
+
lines.push(` const _captureManifest = await _manifestResponse.json();`);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
lines.push(` const _captureManifest = undefined;`);
|
|
237
|
+
}
|
|
238
|
+
lines.push(` createRoot(rootEl).render(`);
|
|
239
|
+
lines.push(` <div className={_rootClass}>`);
|
|
240
|
+
lines.push(` <ScenarioCaptureMount`);
|
|
241
|
+
lines.push(` scenarioId={${JSON.stringify(input.scenarioId)}}`);
|
|
242
|
+
lines.push(` steps={_steps}`);
|
|
243
|
+
lines.push(` renderStep={renderStep}`);
|
|
244
|
+
lines.push(` canonicalWidth={${input.canonicalWidth}}`);
|
|
245
|
+
lines.push(` shellHeight={${input.shellHeight}}`);
|
|
246
|
+
lines.push(` narrationManifest={_captureManifest}`);
|
|
247
|
+
lines.push(` stage={${input.stage ? "true" : "false"}}`);
|
|
248
|
+
lines.push(` providers={${input.providersPath ? "_Providers" : "undefined"}}`);
|
|
249
|
+
lines.push(` onReady={(driver: any) => {`);
|
|
250
|
+
lines.push(` (window as any).__scenarShot = driver;`);
|
|
251
|
+
lines.push(` if (_shotParam) {`);
|
|
252
|
+
lines.push(` const _target = driver.shots.find((s: any) => s.name === _shotParam);`);
|
|
253
|
+
lines.push(` if (!_target) {`);
|
|
254
|
+
lines.push(` (window as any).__scenarShotError =`);
|
|
255
|
+
lines.push(` 'no shot named "' + _shotParam + '" — declared: ' +`);
|
|
256
|
+
lines.push(` (driver.shots.map((s: any) => s.name).join(", ") || "(none)");`);
|
|
257
|
+
lines.push(` return;`);
|
|
258
|
+
lines.push(` }`);
|
|
259
|
+
lines.push(` void driver.walkTo(_target.timeMs);`);
|
|
260
|
+
lines.push(` }`);
|
|
261
|
+
lines.push(` }}`);
|
|
262
|
+
lines.push(` onError={(error: any) => {`);
|
|
263
|
+
lines.push(` (window as any).__scenarShotError = error?.message ?? String(error);`);
|
|
264
|
+
lines.push(` }}`);
|
|
265
|
+
lines.push(` />`);
|
|
266
|
+
lines.push(` </div>,`);
|
|
267
|
+
lines.push(` );`);
|
|
268
|
+
lines.push(`}`);
|
|
186
269
|
// --- Mount ---
|
|
187
270
|
lines.push(``);
|
|
188
271
|
lines.push(`const _rootEl = document.getElementById("root");`);
|
|
189
272
|
lines.push(`if (!_rootEl) throw new Error("Embed root element #root not found");`);
|
|
190
|
-
lines.push(`
|
|
273
|
+
lines.push(`if (_shotParam !== null) {`);
|
|
274
|
+
lines.push(` _mountCapture(_rootEl).catch((error) => {`);
|
|
275
|
+
lines.push(` (window as any).__scenarShotError = error instanceof Error ? error.message : String(error);`);
|
|
276
|
+
lines.push(` });`);
|
|
277
|
+
lines.push(`} else {`);
|
|
278
|
+
lines.push(` createRoot(_rootEl).render(<_App />);`);
|
|
279
|
+
lines.push(`}`);
|
|
191
280
|
lines.push(``);
|
|
192
281
|
return lines.join("\n");
|
|
193
282
|
}
|