@scenar/cli 0.1.12 → 0.1.13
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/package.json +5 -5
- package/src/__tests__/render-command.test.ts +1 -0
- package/src/commands/render.d.ts.map +1 -1
- package/src/commands/render.js +109 -53
- package/src/commands/render.js.map +1 -1
- package/src/commands/render.ts +152 -51
- package/src/render/detect-render-export.d.ts +20 -0
- package/src/render/detect-render-export.d.ts.map +1 -0
- package/src/render/detect-render-export.js +62 -0
- package/src/render/detect-render-export.js.map +1 -0
- package/src/render/detect-render-export.ts +68 -0
- package/src/render/generate-entry.d.ts +41 -0
- package/src/render/generate-entry.d.ts.map +1 -0
- package/src/render/generate-entry.js +111 -0
- package/src/render/generate-entry.js.map +1 -0
- package/src/render/generate-entry.ts +155 -0
- package/src/render/resolve-providers.d.ts +9 -0
- package/src/render/resolve-providers.d.ts.map +1 -0
- package/src/render/resolve-providers.js +32 -0
- package/src/render/resolve-providers.js.map +1 -0
- package/src/render/resolve-providers.ts +36 -0
- package/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scenar/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Scenar CLI — validate scenarios, generate narration, and render videos.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/
|
|
9
|
+
"url": "https://github.com/stigmer/scenar.git",
|
|
10
10
|
"directory": "packages/cli"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"main": "./src/index.js",
|
|
28
28
|
"types": "./src/index.d.ts",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@scenar/core": "0.1.
|
|
31
|
-
"@scenar/preview": "0.1.
|
|
32
|
-
"@scenar/sdk": "0.1.
|
|
30
|
+
"@scenar/core": "0.1.13",
|
|
31
|
+
"@scenar/preview": "0.1.13",
|
|
32
|
+
"@scenar/sdk": "0.1.13",
|
|
33
33
|
"commander": "^13.0.0",
|
|
34
34
|
"yaml": "^2.7.0"
|
|
35
35
|
},
|
|
@@ -41,6 +41,7 @@ describe("scenar render", () => {
|
|
|
41
41
|
expect(optionNames).toContain("--height");
|
|
42
42
|
expect(optionNames).toContain("--entry");
|
|
43
43
|
expect(optionNames).toContain("--composition-id");
|
|
44
|
+
expect(optionNames).toContain("--webpack-override");
|
|
44
45
|
});
|
|
45
46
|
|
|
46
47
|
it("defaults fps to 30, width to 1920, height to 1080", () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/commands/render.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/commands/render.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8G5D"}
|
package/src/commands/render.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { resolve, join } from "node:path";
|
|
2
|
-
import { stat, mkdir, access } from "node:fs/promises";
|
|
2
|
+
import { stat, mkdir, access, writeFile, rm } from "node:fs/promises";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
3
4
|
import { loadBundle } from "../util/load-bundle.js";
|
|
5
|
+
import { generateRemotionEntry } from "../render/generate-entry.js";
|
|
6
|
+
import { resolveProvidersPath } from "../render/resolve-providers.js";
|
|
7
|
+
import { detectRenderExport } from "../render/detect-render-export.js";
|
|
4
8
|
export function registerRenderCommand(program) {
|
|
5
9
|
program
|
|
6
10
|
.command("render")
|
|
7
11
|
.description("Render a scenario as an MP4 video using Remotion.\n\n" +
|
|
8
12
|
"Accepts a scenario directory containing steps.ts and an optional\n" +
|
|
9
13
|
"narration/ subfolder with manifest.json + audio clips.\n\n" +
|
|
14
|
+
"When the scenario directory contains an index.tsx that exports a\n" +
|
|
15
|
+
"renderStep function, the CLI auto-generates the Remotion entry\n" +
|
|
16
|
+
"point — no remotion/ directory or bundle.ts needed.\n\n" +
|
|
10
17
|
"Output defaults to ./<scenario-id>.mp4 in the current working\n" +
|
|
11
18
|
"directory. Use --out to write to a different path.")
|
|
12
19
|
.argument("<dir>", "path to a scenario directory (must contain steps.ts)")
|
|
@@ -15,7 +22,8 @@ export function registerRenderCommand(program) {
|
|
|
15
22
|
.option("--width <number>", "video width in pixels (default: 1920)", "1920")
|
|
16
23
|
.option("--height <number>", "video height in pixels (default: 1080)", "1080")
|
|
17
24
|
.option("--composition-id <id>", "Remotion composition ID to render")
|
|
18
|
-
.option("--entry <path>", "path to a Remotion entry file
|
|
25
|
+
.option("--entry <path>", "path to a custom Remotion entry file")
|
|
26
|
+
.option("--webpack-override <path>", "path to a module that default-exports a Remotion WebpackOverrideFn")
|
|
19
27
|
.action(async (dir, options) => {
|
|
20
28
|
const resolved = resolve(dir);
|
|
21
29
|
let info;
|
|
@@ -36,25 +44,47 @@ export function registerRenderCommand(program) {
|
|
|
36
44
|
const fps = Number(options.fps) || 30;
|
|
37
45
|
const width = Number(options.width) || 1920;
|
|
38
46
|
const height = Number(options.height) || 1080;
|
|
47
|
+
let tempDir;
|
|
39
48
|
try {
|
|
40
49
|
const bundle = await loadBundle(resolved);
|
|
41
50
|
const scenarioId = bundle.id;
|
|
51
|
+
const compositionId = options.compositionId ?? scenarioId;
|
|
42
52
|
const outputPath = resolveOutputPath(options.out, scenarioId);
|
|
43
|
-
const entryPoint = await resolveEntryPoint(
|
|
53
|
+
const { entryPoint, generated } = await resolveEntryPoint({
|
|
54
|
+
entryOption: options.entry,
|
|
55
|
+
scenarioDir: resolved,
|
|
56
|
+
scenarioId,
|
|
57
|
+
compositionId,
|
|
58
|
+
hasNarration: !!bundle.narrationManifest,
|
|
59
|
+
fps,
|
|
60
|
+
width,
|
|
61
|
+
height,
|
|
62
|
+
});
|
|
63
|
+
tempDir = generated?.tempDir;
|
|
64
|
+
const webpackOverride = options.webpackOverride
|
|
65
|
+
? await loadWebpackOverride(options.webpackOverride)
|
|
66
|
+
: undefined;
|
|
44
67
|
process.stderr.write(`Scenario: ${scenarioId}\n`);
|
|
45
68
|
process.stderr.write(`Steps: ${bundle.steps.length}\n`);
|
|
46
69
|
process.stderr.write(`Audio: ${bundle.narrationManifest ? "yes (manifest found)" : "none"}\n`);
|
|
47
|
-
process.stderr.write(`Entry: ${entryPoint}\n`);
|
|
70
|
+
process.stderr.write(`Entry: ${generated ? "(auto-generated)" : entryPoint}\n`);
|
|
71
|
+
if (generated?.providersPath) {
|
|
72
|
+
process.stderr.write(`Providers: ${generated.providersPath}\n`);
|
|
73
|
+
}
|
|
74
|
+
if (webpackOverride) {
|
|
75
|
+
process.stderr.write(`Webpack: custom override loaded\n`);
|
|
76
|
+
}
|
|
48
77
|
process.stderr.write(`Output: ${outputPath}\n`);
|
|
49
78
|
process.stderr.write(`Config: ${width}x${height} @ ${fps}fps\n\n`);
|
|
50
79
|
await renderScenario({
|
|
51
80
|
bundle,
|
|
52
81
|
entryPoint,
|
|
53
|
-
compositionId
|
|
82
|
+
compositionId,
|
|
54
83
|
outputPath,
|
|
55
84
|
fps,
|
|
56
85
|
width,
|
|
57
86
|
height,
|
|
87
|
+
webpackOverride,
|
|
58
88
|
});
|
|
59
89
|
process.stderr.write(`\n\x1b[32m✓\x1b[0m Video saved to ${outputPath}\n`);
|
|
60
90
|
}
|
|
@@ -63,6 +93,11 @@ export function registerRenderCommand(program) {
|
|
|
63
93
|
process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
|
|
64
94
|
process.exitCode = 1;
|
|
65
95
|
}
|
|
96
|
+
finally {
|
|
97
|
+
if (tempDir) {
|
|
98
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
66
101
|
});
|
|
67
102
|
}
|
|
68
103
|
// ---------------------------------------------------------------------------
|
|
@@ -77,62 +112,77 @@ function resolveOutputPath(outOption, scenarioId) {
|
|
|
77
112
|
}
|
|
78
113
|
return resolve(join(outOption, `${scenarioId}.mp4`));
|
|
79
114
|
}
|
|
80
|
-
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (entryOption) {
|
|
85
|
-
const resolved = resolve(entryOption);
|
|
115
|
+
async function resolveEntryPoint(input) {
|
|
116
|
+
// Explicit --entry: use as-is, no generation.
|
|
117
|
+
if (input.entryOption) {
|
|
118
|
+
const resolved = resolve(input.entryOption);
|
|
86
119
|
try {
|
|
87
120
|
await access(resolved);
|
|
88
121
|
}
|
|
89
122
|
catch {
|
|
90
|
-
throw new Error(`Remotion entry point not found: ${entryOption}`);
|
|
123
|
+
throw new Error(`Remotion entry point not found: ${input.entryOption}`);
|
|
91
124
|
}
|
|
92
|
-
return resolved;
|
|
125
|
+
return { entryPoint: resolved };
|
|
93
126
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
127
|
+
// Auto-generate: detect renderStep export, find providers, write temp entry.
|
|
128
|
+
const renderFilePath = await detectRenderExport(input.scenarioDir);
|
|
129
|
+
const providersPath = await resolveProvidersPath(input.scenarioDir);
|
|
130
|
+
const entrySource = generateRemotionEntry({
|
|
131
|
+
scenarioDir: input.scenarioDir,
|
|
132
|
+
renderFilePath,
|
|
133
|
+
scenarioId: input.scenarioId,
|
|
134
|
+
hasNarration: input.hasNarration,
|
|
135
|
+
providersPath,
|
|
136
|
+
fps: input.fps,
|
|
137
|
+
width: input.width,
|
|
138
|
+
height: input.height,
|
|
139
|
+
compositionId: input.compositionId,
|
|
140
|
+
});
|
|
141
|
+
// Write the entry inside the scenario directory so webpack's standard
|
|
142
|
+
// node_modules resolution walks up from here into the consumer project.
|
|
143
|
+
// A /tmp/ location would fail because no node_modules exist there.
|
|
144
|
+
const tempDir = join(input.scenarioDir, ".scenar-render");
|
|
145
|
+
await mkdir(tempDir, { recursive: true });
|
|
146
|
+
const entryPath = join(tempDir, "index.tsx");
|
|
147
|
+
await writeFile(entryPath, entrySource, "utf-8");
|
|
148
|
+
return {
|
|
149
|
+
entryPoint: entryPath,
|
|
150
|
+
generated: { tempDir, providersPath },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Webpack override loading
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
async function loadWebpackOverride(overridePath) {
|
|
157
|
+
const resolved = resolve(overridePath);
|
|
158
|
+
try {
|
|
159
|
+
await access(resolved);
|
|
108
160
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
" );\n");
|
|
161
|
+
catch {
|
|
162
|
+
throw new Error(`Webpack override file not found: ${overridePath}`);
|
|
163
|
+
}
|
|
164
|
+
let mod;
|
|
165
|
+
try {
|
|
166
|
+
mod = await import(pathToFileURL(resolved).href);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
170
|
+
throw new Error(`Failed to import webpack override from ${overridePath}:\n${detail}`);
|
|
171
|
+
}
|
|
172
|
+
const override = (mod.default ?? mod.webpackOverride);
|
|
173
|
+
if (typeof override !== "function") {
|
|
174
|
+
throw new Error(`${overridePath} does not export a webpack override function.\n\n` +
|
|
175
|
+
"Expected a default export or named 'webpackOverride' export:\n\n" +
|
|
176
|
+
' import type { WebpackOverrideFn } from "@remotion/bundler";\n\n' +
|
|
177
|
+
" const webpackOverride: WebpackOverrideFn = (config) => ({\n" +
|
|
178
|
+
" ...config,\n" +
|
|
179
|
+
" // your overrides\n" +
|
|
180
|
+
" });\n\n" +
|
|
181
|
+
" export default webpackOverride;\n");
|
|
182
|
+
}
|
|
183
|
+
return override;
|
|
133
184
|
}
|
|
134
185
|
async function renderScenario(config) {
|
|
135
|
-
// Dynamic imports — these are optional peer deps.
|
|
136
186
|
const bundler = await import("@remotion/bundler").catch(() => {
|
|
137
187
|
throw new Error("Could not load @remotion/bundler.\n" +
|
|
138
188
|
"Install it: pnpm add @remotion/bundler@4.0.448");
|
|
@@ -142,7 +192,13 @@ async function renderScenario(config) {
|
|
|
142
192
|
"Install it: pnpm add @remotion/renderer@4.0.448");
|
|
143
193
|
});
|
|
144
194
|
process.stderr.write("Bundling Remotion project...\n");
|
|
145
|
-
const
|
|
195
|
+
const bundleOptions = {
|
|
196
|
+
entryPoint: config.entryPoint,
|
|
197
|
+
};
|
|
198
|
+
if (config.webpackOverride) {
|
|
199
|
+
bundleOptions.webpackOverride = config.webpackOverride;
|
|
200
|
+
}
|
|
201
|
+
const serveUrl = await bundler.bundle(bundleOptions);
|
|
146
202
|
process.stderr.write(`Selecting composition: ${config.compositionId}\n`);
|
|
147
203
|
const composition = await renderer.selectComposition({
|
|
148
204
|
serveUrl,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/commands/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/commands/render.ts"],"names":[],"mappings":"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"}
|
package/src/commands/render.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { resolve, join } from "node:path";
|
|
2
|
-
import { stat, mkdir, access } from "node:fs/promises";
|
|
2
|
+
import { stat, mkdir, access, writeFile, rm } from "node:fs/promises";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
3
4
|
import { Command } from "commander";
|
|
4
5
|
import { loadBundle, type CliBundle } from "../util/load-bundle.js";
|
|
6
|
+
import { generateRemotionEntry } from "../render/generate-entry.js";
|
|
7
|
+
import { resolveProvidersPath } from "../render/resolve-providers.js";
|
|
8
|
+
import { detectRenderExport } from "../render/detect-render-export.js";
|
|
5
9
|
|
|
6
10
|
interface RenderOptions {
|
|
7
11
|
out?: string;
|
|
@@ -10,6 +14,7 @@ interface RenderOptions {
|
|
|
10
14
|
height?: string;
|
|
11
15
|
compositionId?: string;
|
|
12
16
|
entry?: string;
|
|
17
|
+
webpackOverride?: string;
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
export function registerRenderCommand(program: Command): void {
|
|
@@ -19,6 +24,9 @@ export function registerRenderCommand(program: Command): void {
|
|
|
19
24
|
"Render a scenario as an MP4 video using Remotion.\n\n" +
|
|
20
25
|
"Accepts a scenario directory containing steps.ts and an optional\n" +
|
|
21
26
|
"narration/ subfolder with manifest.json + audio clips.\n\n" +
|
|
27
|
+
"When the scenario directory contains an index.tsx that exports a\n" +
|
|
28
|
+
"renderStep function, the CLI auto-generates the Remotion entry\n" +
|
|
29
|
+
"point — no remotion/ directory or bundle.ts needed.\n\n" +
|
|
22
30
|
"Output defaults to ./<scenario-id>.mp4 in the current working\n" +
|
|
23
31
|
"directory. Use --out to write to a different path.",
|
|
24
32
|
)
|
|
@@ -28,7 +36,11 @@ export function registerRenderCommand(program: Command): void {
|
|
|
28
36
|
.option("--width <number>", "video width in pixels (default: 1920)", "1920")
|
|
29
37
|
.option("--height <number>", "video height in pixels (default: 1080)", "1080")
|
|
30
38
|
.option("--composition-id <id>", "Remotion composition ID to render")
|
|
31
|
-
.option("--entry <path>", "path to a Remotion entry file
|
|
39
|
+
.option("--entry <path>", "path to a custom Remotion entry file")
|
|
40
|
+
.option(
|
|
41
|
+
"--webpack-override <path>",
|
|
42
|
+
"path to a module that default-exports a Remotion WebpackOverrideFn",
|
|
43
|
+
)
|
|
32
44
|
.action(async (dir: string, options: RenderOptions) => {
|
|
33
45
|
const resolved = resolve(dir);
|
|
34
46
|
|
|
@@ -54,29 +66,54 @@ export function registerRenderCommand(program: Command): void {
|
|
|
54
66
|
const width = Number(options.width) || 1920;
|
|
55
67
|
const height = Number(options.height) || 1080;
|
|
56
68
|
|
|
69
|
+
let tempDir: string | undefined;
|
|
70
|
+
|
|
57
71
|
try {
|
|
58
72
|
const bundle = await loadBundle(resolved);
|
|
59
73
|
const scenarioId = bundle.id;
|
|
74
|
+
const compositionId = options.compositionId ?? scenarioId;
|
|
60
75
|
const outputPath = resolveOutputPath(options.out, scenarioId);
|
|
61
|
-
|
|
76
|
+
|
|
77
|
+
const { entryPoint, generated } = await resolveEntryPoint({
|
|
78
|
+
entryOption: options.entry,
|
|
79
|
+
scenarioDir: resolved,
|
|
80
|
+
scenarioId,
|
|
81
|
+
compositionId,
|
|
82
|
+
hasNarration: !!bundle.narrationManifest,
|
|
83
|
+
fps,
|
|
84
|
+
width,
|
|
85
|
+
height,
|
|
86
|
+
});
|
|
87
|
+
tempDir = generated?.tempDir;
|
|
88
|
+
|
|
89
|
+
const webpackOverride = options.webpackOverride
|
|
90
|
+
? await loadWebpackOverride(options.webpackOverride)
|
|
91
|
+
: undefined;
|
|
62
92
|
|
|
63
93
|
process.stderr.write(`Scenario: ${scenarioId}\n`);
|
|
64
94
|
process.stderr.write(`Steps: ${bundle.steps.length}\n`);
|
|
65
95
|
process.stderr.write(
|
|
66
96
|
`Audio: ${bundle.narrationManifest ? "yes (manifest found)" : "none"}\n`,
|
|
67
97
|
);
|
|
68
|
-
process.stderr.write(`Entry: ${entryPoint}\n`);
|
|
98
|
+
process.stderr.write(`Entry: ${generated ? "(auto-generated)" : entryPoint}\n`);
|
|
99
|
+
if (generated?.providersPath) {
|
|
100
|
+
process.stderr.write(`Providers: ${generated.providersPath}\n`);
|
|
101
|
+
}
|
|
102
|
+
if (webpackOverride) {
|
|
103
|
+
process.stderr.write(`Webpack: custom override loaded\n`);
|
|
104
|
+
}
|
|
69
105
|
process.stderr.write(`Output: ${outputPath}\n`);
|
|
70
106
|
process.stderr.write(`Config: ${width}x${height} @ ${fps}fps\n\n`);
|
|
71
107
|
|
|
72
108
|
await renderScenario({
|
|
73
109
|
bundle,
|
|
74
110
|
entryPoint,
|
|
75
|
-
compositionId
|
|
111
|
+
compositionId,
|
|
76
112
|
outputPath,
|
|
77
113
|
fps,
|
|
78
114
|
width,
|
|
79
115
|
height,
|
|
116
|
+
webpackOverride,
|
|
80
117
|
});
|
|
81
118
|
|
|
82
119
|
process.stderr.write(`\n\x1b[32m✓\x1b[0m Video saved to ${outputPath}\n`);
|
|
@@ -84,6 +121,10 @@ export function registerRenderCommand(program: Command): void {
|
|
|
84
121
|
const msg = error instanceof Error ? error.message : String(error);
|
|
85
122
|
process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
|
|
86
123
|
process.exitCode = 1;
|
|
124
|
+
} finally {
|
|
125
|
+
if (tempDir) {
|
|
126
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
127
|
+
}
|
|
87
128
|
}
|
|
88
129
|
});
|
|
89
130
|
}
|
|
@@ -106,59 +147,110 @@ function resolveOutputPath(outOption: string | undefined, scenarioId: string): s
|
|
|
106
147
|
// Entry point resolution
|
|
107
148
|
// ---------------------------------------------------------------------------
|
|
108
149
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
150
|
+
interface EntryResolutionInput {
|
|
151
|
+
entryOption: string | undefined;
|
|
152
|
+
scenarioDir: string;
|
|
153
|
+
scenarioId: string;
|
|
154
|
+
compositionId: string;
|
|
155
|
+
hasNarration: boolean;
|
|
156
|
+
fps: number;
|
|
157
|
+
width: number;
|
|
158
|
+
height: number;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
interface EntryResolutionResult {
|
|
162
|
+
entryPoint: string;
|
|
163
|
+
generated?: {
|
|
164
|
+
tempDir: string;
|
|
165
|
+
providersPath: string | null;
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function resolveEntryPoint(
|
|
170
|
+
input: EntryResolutionInput,
|
|
171
|
+
): Promise<EntryResolutionResult> {
|
|
172
|
+
// Explicit --entry: use as-is, no generation.
|
|
173
|
+
if (input.entryOption) {
|
|
174
|
+
const resolved = resolve(input.entryOption);
|
|
112
175
|
try {
|
|
113
176
|
await access(resolved);
|
|
114
177
|
} catch {
|
|
115
|
-
throw new Error(`Remotion entry point not found: ${entryOption}`);
|
|
178
|
+
throw new Error(`Remotion entry point not found: ${input.entryOption}`);
|
|
116
179
|
}
|
|
117
|
-
return resolved;
|
|
180
|
+
return { entryPoint: resolved };
|
|
118
181
|
}
|
|
119
182
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
resolve("src/remotion/index.tsx"),
|
|
124
|
-
resolve("src/remotion/index.ts"),
|
|
125
|
-
];
|
|
183
|
+
// Auto-generate: detect renderStep export, find providers, write temp entry.
|
|
184
|
+
const renderFilePath = await detectRenderExport(input.scenarioDir);
|
|
185
|
+
const providersPath = await resolveProvidersPath(input.scenarioDir);
|
|
126
186
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
187
|
+
const entrySource = generateRemotionEntry({
|
|
188
|
+
scenarioDir: input.scenarioDir,
|
|
189
|
+
renderFilePath,
|
|
190
|
+
scenarioId: input.scenarioId,
|
|
191
|
+
hasNarration: input.hasNarration,
|
|
192
|
+
providersPath,
|
|
193
|
+
fps: input.fps,
|
|
194
|
+
width: input.width,
|
|
195
|
+
height: input.height,
|
|
196
|
+
compositionId: input.compositionId,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Write the entry inside the scenario directory so webpack's standard
|
|
200
|
+
// node_modules resolution walks up from here into the consumer project.
|
|
201
|
+
// A /tmp/ location would fail because no node_modules exist there.
|
|
202
|
+
const tempDir = join(input.scenarioDir, ".scenar-render");
|
|
203
|
+
await mkdir(tempDir, { recursive: true });
|
|
204
|
+
|
|
205
|
+
const entryPath = join(tempDir, "index.tsx");
|
|
206
|
+
await writeFile(entryPath, entrySource, "utf-8");
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
entryPoint: entryPath,
|
|
210
|
+
generated: { tempDir, providersPath },
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// Webpack override loading
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
async function loadWebpackOverride(
|
|
219
|
+
overridePath: string,
|
|
220
|
+
): Promise<(config: unknown) => unknown> {
|
|
221
|
+
const resolved = resolve(overridePath);
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
await access(resolved);
|
|
225
|
+
} catch {
|
|
226
|
+
throw new Error(`Webpack override file not found: ${overridePath}`);
|
|
134
227
|
}
|
|
135
228
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
);
|
|
229
|
+
let mod: Record<string, unknown>;
|
|
230
|
+
try {
|
|
231
|
+
mod = await import(pathToFileURL(resolved).href) as Record<string, unknown>;
|
|
232
|
+
} catch (err) {
|
|
233
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
234
|
+
throw new Error(
|
|
235
|
+
`Failed to import webpack override from ${overridePath}:\n${detail}`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const override = (mod.default ?? mod.webpackOverride) as unknown;
|
|
240
|
+
if (typeof override !== "function") {
|
|
241
|
+
throw new Error(
|
|
242
|
+
`${overridePath} does not export a webpack override function.\n\n` +
|
|
243
|
+
"Expected a default export or named 'webpackOverride' export:\n\n" +
|
|
244
|
+
' import type { WebpackOverrideFn } from "@remotion/bundler";\n\n' +
|
|
245
|
+
" const webpackOverride: WebpackOverrideFn = (config) => ({\n" +
|
|
246
|
+
" ...config,\n" +
|
|
247
|
+
" // your overrides\n" +
|
|
248
|
+
" });\n\n" +
|
|
249
|
+
" export default webpackOverride;\n",
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return override as (config: unknown) => unknown;
|
|
162
254
|
}
|
|
163
255
|
|
|
164
256
|
// ---------------------------------------------------------------------------
|
|
@@ -173,10 +265,10 @@ interface RenderConfig {
|
|
|
173
265
|
fps: number;
|
|
174
266
|
width: number;
|
|
175
267
|
height: number;
|
|
268
|
+
webpackOverride?: (config: unknown) => unknown;
|
|
176
269
|
}
|
|
177
270
|
|
|
178
271
|
async function renderScenario(config: RenderConfig): Promise<void> {
|
|
179
|
-
// Dynamic imports — these are optional peer deps.
|
|
180
272
|
const bundler = await import("@remotion/bundler").catch(() => {
|
|
181
273
|
throw new Error(
|
|
182
274
|
"Could not load @remotion/bundler.\n" +
|
|
@@ -192,7 +284,16 @@ async function renderScenario(config: RenderConfig): Promise<void> {
|
|
|
192
284
|
});
|
|
193
285
|
|
|
194
286
|
process.stderr.write("Bundling Remotion project...\n");
|
|
195
|
-
|
|
287
|
+
|
|
288
|
+
const bundleOptions: Record<string, unknown> = {
|
|
289
|
+
entryPoint: config.entryPoint,
|
|
290
|
+
};
|
|
291
|
+
if (config.webpackOverride) {
|
|
292
|
+
bundleOptions.webpackOverride = config.webpackOverride;
|
|
293
|
+
}
|
|
294
|
+
const serveUrl = await (bundler.bundle as (opts: Record<string, unknown>) => Promise<string>)(
|
|
295
|
+
bundleOptions,
|
|
296
|
+
);
|
|
196
297
|
|
|
197
298
|
process.stderr.write(`Selecting composition: ${config.compositionId}\n`);
|
|
198
299
|
const composition = await renderer.selectComposition({
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locate the file that exports `renderStep` in the scenario directory.
|
|
3
|
+
*
|
|
4
|
+
* Checks `render.tsx` first (recommended — a lean file that only imports
|
|
5
|
+
* view components), then falls back to `index.tsx`. Using a dedicated
|
|
6
|
+
* `render.tsx` avoids pulling browser-only dependencies (preview
|
|
7
|
+
* runtime, MSW, etc.) into the Remotion webpack bundle.
|
|
8
|
+
*
|
|
9
|
+
* Uses a lightweight source-text scan rather than a dynamic import.
|
|
10
|
+
* The actual module resolution and type checking happens later when
|
|
11
|
+
* Remotion's webpack bundler compiles the generated entry — which has
|
|
12
|
+
* the full project dependency graph available. Importing the file
|
|
13
|
+
* here at CLI time would pull in React, MUI, and other view-layer
|
|
14
|
+
* dependencies that are not available in the Node CLI context.
|
|
15
|
+
*
|
|
16
|
+
* Returns the absolute path to the file containing renderStep.
|
|
17
|
+
* Throws with a descriptive message if no suitable file is found.
|
|
18
|
+
*/
|
|
19
|
+
export declare function detectRenderExport(scenarioDir: string): Promise<string>;
|
|
20
|
+
//# sourceMappingURL=detect-render-export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-render-export.d.ts","sourceRoot":"","sources":["../../../src/render/detect-render-export.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4B7E"}
|