Add .gitignore to exclude all node packages and lock files

This commit is contained in:
Adolfo Reyna
2026-02-23 21:56:04 -05:00
parent faae96c9ed
commit dcc5c6c044
9747 changed files with 1555105 additions and 2 deletions
@@ -0,0 +1,7 @@
# @remotion/studio-server
Run a Remotion Studio with a server backend
## Usage
This is an internal package and has no documentation.
@@ -0,0 +1,40 @@
type Options = {
width?: number;
height?: number;
};
export declare class AnsiDiff {
x: number;
y: number;
width: number;
height: number;
_buffer: string | null;
_out: Buffer[];
_lines: Line[];
finished: boolean;
constructor(opts?: Options);
toString(): string | null;
finish(): Buffer;
update(buffer: string | Buffer, opts?: {
moveTo?: [number, number];
}): Buffer;
_clearDown(y: number): void;
_newline(): void;
_write(line: Line): void;
_moveTo(x: number, y: number): void;
_push: (buf: Buffer) => void;
}
declare class Line {
y: number;
width: number;
parts: string[];
height: number;
remainder: number;
raw: string;
length: number;
newline: boolean;
constructor(str: string, y: number, nl: boolean, term: AnsiDiff);
diffLeft(other: Line): number;
diffRight(other: Line): number;
toBuffer(): Buffer;
}
export {};
@@ -0,0 +1,220 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnsiDiff = void 0;
const studio_shared_1 = require("@remotion/studio-shared");
const CLEAR_LINE = Buffer.from([0x1b, 0x5b, 0x30, 0x4b]);
const NEWLINE = Buffer.from('\n');
class AnsiDiff {
constructor(opts) {
this._push = (buf) => {
this._out.push(buf);
};
this.x = 0;
this.y = 0;
this.width = (opts === null || opts === void 0 ? void 0 : opts.width) || Infinity;
this.height = (opts === null || opts === void 0 ? void 0 : opts.height) || Infinity;
this._buffer = null;
this._out = [];
this._lines = [];
this.finished = false;
}
toString() {
return this._buffer;
}
finish() {
this.finished = true;
if (this._out.length === 0) {
return Buffer.from('');
}
if (!this._out[this._out.length - 1].toString().endsWith('\n')) {
return NEWLINE;
}
return Buffer.from('');
}
update(buffer, opts) {
if (this.finished) {
return Buffer.from('');
}
this._buffer = Buffer.isBuffer(buffer) ? buffer.toString() : buffer;
const other = this._buffer;
const oldLines = this._lines;
const lines = split(other, this);
this._lines = lines;
this._out = [];
const min = Math.min(lines.length, oldLines.length);
let i = 0;
let a;
let b;
let scrub = false;
for (; i < min; i++) {
a = lines[i];
b = oldLines[i];
if (same(a, b))
continue;
// if x === width there is an edgecase with inline diffing
// easiest solution is just not to do it then! :)
if (!scrub && this.x !== this.width && inlineDiff(a, b)) {
const left = a.diffLeft(b);
const right = a.diffRight(b);
const slice = a.raw.slice(left, right ? -right : a.length);
if (left + right > 4 && left + slice.length < this.width - 1) {
this._moveTo(left, a.y);
this._push(Buffer.from(slice));
this.x += slice.length;
continue;
}
}
this._moveTo(0, a.y);
this._write(a);
if (a.y !== b.y || a.height !== b.height)
scrub = true;
if (b.length > a.length || scrub)
this._push(CLEAR_LINE);
if (a.newline)
this._newline();
}
for (; i < lines.length; i++) {
a = lines[i];
this._moveTo(0, a.y);
this._write(a);
if (scrub)
this._push(CLEAR_LINE);
if (a.newline)
this._newline();
}
const oldLast = top(oldLines);
const last = top(lines);
if (oldLast &&
(!last || last.y + last.height < oldLast.y + oldLast.height)) {
this._clearDown(oldLast.y + oldLast.height);
}
if (opts === null || opts === void 0 ? void 0 : opts.moveTo) {
this._moveTo(opts.moveTo[0], opts.moveTo[1]);
}
else if (last) {
this._moveTo(last.remainder, last.y + last.height);
}
return Buffer.concat(this._out);
}
_clearDown(y) {
let { x } = this;
for (let i = this.y; i <= y; i++) {
this._moveTo(x, i);
this._push(CLEAR_LINE);
x = 0;
}
}
_newline() {
this._push(NEWLINE);
this.x = 0;
this.y++;
}
_write(line) {
this._out.push(line.toBuffer());
this.x = line.remainder;
this.y += line.height;
}
_moveTo(x, y) {
const dx = x - this.x;
const dy = y - this.y;
if (dx > 0)
this._push(moveRight(dx));
else if (dx < 0)
this._push(moveLeft(-dx));
if (dy > 0)
this._push(moveDown(dy));
else if (dy < 0)
this._push(moveUp(-dy));
this.x = x;
this.y = y;
}
}
exports.AnsiDiff = AnsiDiff;
function same(a, b) {
return (a.y === b.y &&
a.width === b.width &&
a.raw === b.raw &&
a.newline === b.newline);
}
function top(list) {
return list.length ? list[list.length - 1] : null;
}
class Line {
constructor(str, y, nl, term) {
this.y = y;
this.width = term.width;
this.parts = (0, studio_shared_1.splitAnsi)(str);
this.length = length(this.parts);
this.raw = str;
this.newline = nl;
this.height = Math.floor(this.length / term.width);
this.remainder = this.length - (this.height && this.height * term.width);
if (this.height && !this.remainder) {
this.height--;
this.remainder = this.width;
}
}
diffLeft(other) {
let left = 0;
for (; left < this.length; left++) {
if (this.raw[left] !== other.raw[left])
return left;
}
return left;
}
diffRight(other) {
let right = 0;
for (; right < this.length; right++) {
const r = this.length - right - 1;
if (this.raw[r] !== other.raw[r])
return right;
}
return right;
}
toBuffer() {
return Buffer.from(this.raw);
}
}
function inlineDiff(a, b) {
return (a.length === b.length &&
a.parts.length === 1 &&
b.parts.length === 1 &&
a.y === b.y &&
a.newline &&
b.newline &&
a.width === b.width);
}
function split(str, term) {
let y = 0;
const lines = str.split('\n');
const wrapped = [];
let line;
for (let i = 0; i < lines.length; i++) {
line = new Line(lines[i], y, i < lines.length - 1, term);
y += line.height + (line.newline ? 1 : 0);
wrapped.push(line);
}
return wrapped;
}
function moveUp(n) {
return Buffer.from('1b5b' + toHex(n) + '41', 'hex');
}
function moveDown(n) {
return Buffer.from('1b5b' + toHex(n) + '42', 'hex');
}
function moveRight(n) {
return Buffer.from('1b5b' + toHex(n) + '43', 'hex');
}
function moveLeft(n) {
return Buffer.from('1b5b' + toHex(n) + '44', 'hex');
}
function length(parts) {
let len = 0;
for (let i = 0; i < parts.length; i += 2) {
len += parts[i].length;
}
return len;
}
function toHex(n) {
return Buffer.from(String(n)).toString('hex');
}
@@ -0,0 +1,5 @@
export declare const openBrowser: ({ url, browserFlag, browserArgs, }: {
url: string;
browserFlag: string | undefined;
browserArgs: string | undefined;
}) => Promise<boolean | import("child_process").ChildProcess>;
@@ -0,0 +1,202 @@
"use strict";
// Copied from https://github.com/michaellzc/better-opn#readme
Object.defineProperty(exports, "__esModule", { value: true });
exports.openBrowser = void 0;
const node_child_process_1 = require("node:child_process");
const open = require("open");
const normalizeURLToMatch = (target) => {
// We may encounter URL parse error but want to fallback to default behavior
try {
// Url module is deprecated on newer version of NodeJS, only use it when URL class is not supported (like Node 8)
const URL = typeof global.URL === 'undefined' ? require('url').URL : global.URL;
const url = new URL(target);
return url.origin;
}
catch (_a) {
return target;
}
};
// Copy from
// https://github.com/facebook/create-react-app/blob/master/packages/react-dev-utils/openBrowser.js#L64
const startBrowserProcess = async ({ browser, url, args, }) => {
const tryNewInstance = args.length > 0;
const shouldTryOpenChromiumWithAppleScript = process.platform === 'darwin' &&
!tryNewInstance &&
(typeof browser !== 'string' ||
browser === 'google chrome' ||
browser === 'chrome');
if (shouldTryOpenChromiumWithAppleScript) {
let appleScriptDenied = false;
// Will use the first open browser found from list
const supportedChromiumBrowsers = [
'Google Chrome',
'Google Chrome Canary',
'Microsoft Edge',
'Brave Browser',
'Vivaldi',
'Chromium',
'Arc',
];
const processes = await new Promise((resolve, reject) => {
(0, node_child_process_1.exec)('ps cax', (err, stdout) => {
if (err) {
reject(err);
}
else {
resolve(stdout);
}
});
});
const browsersToTry = supportedChromiumBrowsers.filter((b) => processes.includes(b));
for (const chromiumBrowser of browsersToTry) {
if (appleScriptDenied) {
continue;
}
try {
// Try our best to reuse existing tab
// on OSX Chromium-based browser with AppleScript
const appleScript = `
(*
Copyright (c) 2015-present, Facebook, Inc.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*)
property targetTab: null
property targetTabIndex: -1
property targetWindow: null
property theProgram: "${chromiumBrowser}"
on run argv
set theURL to "${encodeURI(url)}"
set matchURL to "${process.env.OPEN_MATCH_HOST_ONLY === 'true'
? encodeURI(normalizeURLToMatch(url))
: encodeURI(url)}"
using terms from application "Google Chrome"
tell application theProgram
if (count every window) = 0 then
make new window
end if
-- 1: Looking for tab running debugger
-- then, Reload debugging tab if found
-- then return
set found to my lookupTabWithUrl(matchURL)
if found then
set targetWindow's active tab index to targetTabIndex
tell targetTab to reload
tell targetWindow to activate
set index of targetWindow to 1
return
end if
-- 2: Looking for Empty tab
-- In case debugging tab was not found
-- We try to find an empty tab instead
set found to my lookupTabWithUrl("chrome://newtab/")
if found then
set targetWindow's active tab index to targetTabIndex
set URL of targetTab to theURL
tell targetWindow to activate
return
end if
-- 3: Create new tab
-- both debugging and empty tab were not found
-- make a new tab with url
tell window 1
activate
make new tab with properties {URL:theURL}
end tell
end tell
end using terms from
end run
-- Function:
-- Lookup tab with given url
-- if found, store tab, index, and window in properties
-- (properties were declared on top of file)
on lookupTabWithUrl(lookupUrl)
using terms from application "Google Chrome"
tell application theProgram
-- Find a tab with the given url
set found to false
set theTabIndex to -1
repeat with theWindow in every window
set theTabIndex to 0
repeat with theTab in every tab of theWindow
set theTabIndex to theTabIndex + 1
if (theTab's URL as string) contains lookupUrl then
-- assign tab, tab index, and window to properties
set targetTab to theTab
set targetTabIndex to theTabIndex
set targetWindow to theWindow
set found to true
exit repeat
end if
end repeat
if found then
exit repeat
end if
end repeat
end tell
end using terms from
return found
end lookupTabWithUrl
`.trim();
await new Promise((resolve, reject) => {
const proc = (0, node_child_process_1.exec)(`osascript -`, (error) => {
if (error) {
reject(error);
}
else {
// Ignore errors.
// It it breaks, it will fallback to `opn` anyway
resolve();
}
});
proc.stdin.write(appleScript);
proc.stdin.end();
});
return Promise.resolve(true);
}
catch (error) {
const appleScriptError = error.message;
if (appleScriptError
.toLowerCase()
.includes('not authorised to send apple events')) {
appleScriptDenied = true;
}
// Ignore errors.
// It it breaks, it will fallback to `opn` anyway
}
}
}
// Fallback to opn
// (It will always open new tab)
return open(url, {
...(browser ? { app: { name: browser, arguments: args } } : {}),
newInstance: tryNewInstance,
wait: false,
});
};
const getBrowserArgs = (browserArgs) => {
if (browserArgs) {
return browserArgs.split(' ');
}
if (process.env.BROWSER_ARGS) {
return process.env.BROWSER_ARGS.split(' ');
}
return [];
};
const openBrowser = ({ url, browserFlag, browserArgs, }) => {
return startBrowserProcess({
browser: browserFlag !== null && browserFlag !== void 0 ? browserFlag : process.env.BROWSER,
url,
args: getBrowserArgs(browserArgs),
});
};
exports.openBrowser = openBrowser;
@@ -0,0 +1,7 @@
import type { CompletedClientRender } from '@remotion/studio-shared';
export declare const getCompletedClientRenders: () => CompletedClientRender[];
export declare const addCompletedClientRender: ({ render, remotionRoot, }: {
render: CompletedClientRender;
remotionRoot: string;
}) => void;
export declare const removeCompletedClientRender: (id: string) => void;
@@ -0,0 +1,59 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.removeCompletedClientRender = exports.addCompletedClientRender = exports.getCompletedClientRenders = void 0;
const file_watcher_1 = require("./file-watcher");
const resolve_output_path_1 = require("./helpers/resolve-output-path");
const live_events_1 = require("./preview-server/live-events");
let completedClientRenders = [];
const cleanupFns = new Map();
const notifyClientsOfUpdate = () => {
(0, live_events_1.waitForLiveEventsListener)().then((listener) => {
listener.sendEventToClient({
type: 'client-renders-updated',
renders: (0, exports.getCompletedClientRenders)(),
});
});
};
const getCompletedClientRenders = () => {
return completedClientRenders;
};
exports.getCompletedClientRenders = getCompletedClientRenders;
const addCompletedClientRender = ({ render, remotionRoot, }) => {
if (completedClientRenders.some((r) => r.id === render.id)) {
return;
}
completedClientRenders.push(render);
const filePath = (0, resolve_output_path_1.resolveOutputPath)(remotionRoot, render.outName);
const { unwatch } = (0, file_watcher_1.installFileWatcher)({
file: filePath,
onChange: (type) => {
if (type === 'created' || type === 'deleted') {
updateCompletedClientRender(render.id, {
deletedOutputLocation: type === 'deleted',
});
}
},
});
cleanupFns.set(render.id, unwatch);
notifyClientsOfUpdate();
};
exports.addCompletedClientRender = addCompletedClientRender;
const removeCompletedClientRender = (id) => {
const cleanup = cleanupFns.get(id);
if (cleanup) {
cleanup();
cleanupFns.delete(id);
}
completedClientRenders = completedClientRenders.filter((r) => r.id !== id);
notifyClientsOfUpdate();
};
exports.removeCompletedClientRender = removeCompletedClientRender;
const updateCompletedClientRender = (id, updates) => {
completedClientRenders = completedClientRenders.map((r) => {
if (r.id === id) {
return { ...r, ...updates };
}
return r;
});
notifyClientsOfUpdate();
};
@@ -0,0 +1,8 @@
import type { File } from '@babel/types';
import { type ApplyVisualControlCodemod } from '@remotion/studio-shared';
import type { ApplyCodeModReturnType, Change } from './recast-mods';
export declare const applyVisualControl: ({ file, transformation, changesMade, }: {
file: File;
transformation: ApplyVisualControlCodemod;
changesMade: Change[];
}) => ApplyCodeModReturnType;
@@ -0,0 +1,82 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyVisualControl = void 0;
const studio_shared_1 = require("@remotion/studio-shared");
const recast = __importStar(require("recast"));
const parse_ast_1 = require("./parse-ast");
const expectString = (node) => {
if (node.type === 'StringLiteral') {
return node.value;
}
if (node.type === 'TemplateLiteral') {
if (node.expressions.length > 0) {
throw new Error('applyVisualControl() must use a static identifier, the string may not be dynamic.');
}
return node.quasis[0].value.raw;
}
throw new Error('Expected a string literal');
};
const applyVisualControl = ({ file, transformation, changesMade, }) => {
recast.types.visit(file.program, {
visitCallExpression(path) {
const { node } = path;
if (node.type !== 'CallExpression') {
throw new Error('Expected a call expression');
}
if (node.callee.type !== 'Identifier') {
return this.traverse(path);
}
if (node.callee.name !== 'visualControl') {
return this.traverse(path);
}
const firstArgument = node.arguments[0];
const str = expectString(firstArgument);
for (const change of transformation.changes) {
if (change.id !== str) {
continue;
}
const parsed = (0, parse_ast_1.parseAst)(`a = ${(0, studio_shared_1.stringifyDefaultProps)({ props: JSON.parse(change.newValueSerialized), enumPaths: change.enumPaths })}`).program.body[0].expression.right;
node.arguments[1] = parsed;
changesMade.push({
description: `Applied visual control ${change.id}`,
});
}
return this.traverse(path);
},
});
return { newAst: file, changesMade };
};
exports.applyVisualControl = applyVisualControl;
@@ -0,0 +1,10 @@
import type { RecastCodemod } from '@remotion/studio-shared';
import type { Change } from './recast-mods';
export declare const formatOutput: (tsContent: string) => Promise<string>;
export declare const parseAndApplyCodemod: ({ input, codeMod, }: {
input: string;
codeMod: RecastCodemod;
}) => {
newContents: string;
changesMade: Change[];
};
@@ -0,0 +1,77 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseAndApplyCodemod = exports.formatOutput = void 0;
const parse_ast_1 = require("./parse-ast");
const recast_mods_1 = require("./recast-mods");
const getPrettier = async () => {
try {
return await Promise.resolve().then(() => __importStar(require('prettier')));
}
catch (_a) {
throw new Error('Prettier cannot be found in the current project.');
}
};
const formatOutput = async (tsContent) => {
const prettier = await getPrettier();
const { format, resolveConfig, resolveConfigFile } = prettier;
const configFilePath = await resolveConfigFile();
if (!configFilePath) {
throw new Error('The Prettier config file was not found. For this feature, the "prettier" package must be installed and a .prettierrc file must exist.');
}
const prettierConfig = await resolveConfig(configFilePath);
if (!prettierConfig) {
throw new Error(`The Prettier config at ${configFilePath} could not be read`);
}
const newContents = await format(tsContent, {
...prettierConfig,
filepath: 'test.tsx',
});
return newContents;
};
exports.formatOutput = formatOutput;
const parseAndApplyCodemod = ({ input, codeMod, }) => {
const ast = (0, parse_ast_1.parseAst)(input);
const { newAst, changesMade } = (0, recast_mods_1.applyCodemod)({
file: ast,
codeMod,
});
if (changesMade.length === 0) {
throw new Error('Unable to calculate the changes needed for this file. Edit the root file manually.');
}
const output = (0, parse_ast_1.serializeAst)(newAst);
return { changesMade, newContents: output };
};
exports.parseAndApplyCodemod = parseAndApplyCodemod;
@@ -0,0 +1,3 @@
import type { File } from '@babel/types';
export declare const parseAst: (input: string) => File;
export declare const serializeAst: (ast: File) => string;
@@ -0,0 +1,50 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.serializeAst = exports.parseAst = void 0;
const recast = __importStar(require("recast"));
const tsParser = __importStar(require("recast/parsers/babel-ts"));
const parseAst = (input) => {
return recast.parse(input, {
parser: tsParser,
});
};
exports.parseAst = parseAst;
const serializeAst = (ast) => {
return recast.print(ast, {
parser: tsParser,
}).code;
};
exports.serializeAst = serializeAst;
@@ -0,0 +1,13 @@
import type { File } from '@babel/types';
import type { RecastCodemod } from '@remotion/studio-shared';
export type Change = {
description: string;
};
export type ApplyCodeModReturnType = {
newAst: File;
changesMade: Change[];
};
export declare const applyCodemod: ({ file, codeMod, }: {
file: File;
codeMod: RecastCodemod;
}) => ApplyCodeModReturnType;
@@ -0,0 +1,358 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyCodemod = void 0;
const apply_visual_control_1 = require("./apply-visual-control");
const applyCodemod = ({ file, codeMod, }) => {
const changesMade = [];
if (codeMod.type === 'apply-visual-control') {
return (0, apply_visual_control_1.applyVisualControl)({ file, transformation: codeMod, changesMade });
}
const body = file.program.body.map((node) => {
return mapAll(node, codeMod, changesMade);
});
return {
newAst: {
...file,
program: {
...file.program,
body,
},
},
changesMade,
};
};
exports.applyCodemod = applyCodemod;
const mapAll = (node, transformation, changesMade) => {
if (isRecognizedType(node)) {
return mapRecognizedType(node, transformation, changesMade);
}
return node;
};
const isRecognizedType = (t) => {
return (t.type === 'ArrowFunctionExpression' ||
t.type === 'FunctionExpression' ||
t.type === 'JSXFragment' ||
t.type === 'JSXElement' ||
t.type === 'BlockStatement' ||
t.type === 'ReturnStatement' ||
t.type === 'VariableDeclaration' ||
t.type === 'FunctionDeclaration' ||
t.type === 'ExportNamedDeclaration' ||
t.type === 'ExportDefaultDeclaration');
};
const mapVariableDeclarator = (variableDeclarator, transformation, changesMade) => {
return {
...variableDeclarator,
init: variableDeclarator.init
? mapAll(variableDeclarator.init, transformation, changesMade)
: variableDeclarator.init,
};
};
const mapBlockStatement = (blockStatement, transformation, changesMade) => {
return {
...blockStatement,
body: blockStatement.body.map((a) => {
return mapAll(a, transformation, changesMade);
}),
};
};
const mapReturnStatement = (statement, transformation, changesMade) => {
if (!statement.argument) {
return statement;
}
return {
...statement,
argument: mapAll(statement.argument, transformation, changesMade),
};
};
const mapJsxElementOrFragment = (jsxFragment, transformation, changesMade) => {
return {
...jsxFragment,
children: jsxFragment.children
.map((c) => {
if (c.type !== 'JSXElement') {
return c;
}
return mapJsxChild(c, transformation, changesMade);
})
.flat(1),
};
};
const mapJsxChild = (c, transformation, changesMade) => {
const compId = getCompositionIdFromJSXElement(c);
if (transformation === null) {
return [c];
}
if (transformation.type === 'duplicate-composition' &&
compId === transformation.idToDuplicate) {
return [
c,
changeComposition({
jsxElement: c,
newCompositionId: transformation.newId,
newCompositionFps: transformation.newFps,
newCompositionDurationInFrames: transformation.newDurationInFrames,
newCompositionHeight: transformation.newHeight,
newCompositionWidth: transformation.newWidth,
newTagToUse: transformation.tag,
changesMade,
}),
];
}
if (transformation.type === 'rename-composition' &&
compId === transformation.idToRename) {
return [
changeComposition({
jsxElement: c,
newCompositionId: transformation.newId,
newCompositionFps: null,
newCompositionDurationInFrames: null,
newCompositionHeight: null,
newCompositionWidth: null,
changesMade,
newTagToUse: null,
}),
];
}
if (transformation.type === 'delete-composition' &&
compId === transformation.idToDelete) {
changesMade.push({
description: 'Deleted composition',
});
return [];
}
return [mapAll(c, transformation, changesMade)];
};
const mapRecognizedType = (expression, transformation, changesMade) => {
if (expression.type === 'JSXFragment' || expression.type === 'JSXElement') {
return mapJsxElementOrFragment(expression, transformation, changesMade);
}
if (expression.type === 'ArrowFunctionExpression' ||
expression.type === 'FunctionExpression') {
return {
...expression,
body: mapAll(expression.body, transformation, changesMade),
};
}
if (expression.type === 'VariableDeclaration') {
const declarations = expression.declarations.map((d) => {
return mapVariableDeclarator(d, transformation, changesMade);
});
return { ...expression, declarations };
}
if (expression.type === 'FunctionDeclaration') {
return {
...expression,
body: mapBlockStatement(expression.body, transformation, changesMade),
};
}
if (expression.type === 'ExportNamedDeclaration' ||
expression.type === 'ExportDefaultDeclaration') {
if (!expression.declaration) {
return expression;
}
return {
...expression,
declaration: mapAll(expression.declaration, transformation, changesMade),
};
}
if (expression.type === 'ReturnStatement') {
return mapReturnStatement(expression, transformation, changesMade);
}
if (expression.type === 'BlockStatement') {
return mapBlockStatement(expression, transformation, changesMade);
}
return expression;
};
const getCompositionIdFromJSXElement = (jsxElement) => {
if (jsxElement.type !== 'JSXElement') {
return null;
}
const { openingElement } = jsxElement;
const { name } = openingElement;
if (name.type !== 'JSXIdentifier') {
return null;
}
if (name.name !== 'Composition' && name.name !== 'Still') {
return null;
}
const id = openingElement.attributes
.map((attribute) => {
if (attribute.type === 'JSXSpreadAttribute') {
return null;
}
if (attribute.name.type === 'JSXNamespacedName') {
return null;
}
if (attribute.name.name !== 'id') {
return null;
}
if (!attribute.value) {
return null;
}
if (attribute.value.type === 'StringLiteral') {
return attribute.value.value;
}
if (attribute.value.type === 'JSXExpressionContainer' &&
attribute.value.expression.type === 'StringLiteral') {
return attribute.value.expression.value;
}
return null;
})
.filter(Boolean);
return id[0];
};
const changeComposition = ({ jsxElement, newCompositionId, newCompositionFps, newCompositionDurationInFrames, newCompositionHeight, newCompositionWidth, changesMade, newTagToUse, }) => {
const { openingElement } = jsxElement;
const { name } = openingElement;
if (name.type !== 'JSXIdentifier') {
return jsxElement;
}
if (name.name !== 'Composition' && name.name !== 'Still') {
return jsxElement;
}
const attributes = openingElement.attributes
.map((attribute) => {
if (attribute.type === 'JSXSpreadAttribute') {
return attribute;
}
if (attribute.name.type === 'JSXNamespacedName') {
return attribute;
}
if (attribute.name.name === 'fps' && newTagToUse === 'Still') {
changesMade.push({ description: 'Removed fps attribute' });
return null;
}
if (attribute.name.name === 'durationInFrames' &&
newTagToUse === 'Still') {
changesMade.push({ description: 'Removed durationInFrames' });
return null;
}
// id="one"
if (attribute.name.name === 'id' &&
attribute.value &&
attribute.value.type === 'StringLiteral') {
changesMade.push({
description: 'Replaced composition id',
});
return {
...attribute,
value: { ...attribute.value, value: newCompositionId },
};
}
// id={"one"}
if (attribute.name.name === 'id' &&
attribute.value &&
attribute.value.type === 'JSXExpressionContainer' &&
attribute.value.expression.type === 'StringLiteral') {
changesMade.push({
description: 'Replaced composition id',
});
return {
...attribute,
value: {
...attribute.value,
expression: {
...attribute.value.expression,
value: newCompositionId,
},
},
};
}
if (attribute.name.name === 'fps' &&
attribute.value &&
attribute.value.type === 'JSXExpressionContainer' &&
attribute.value.expression.type === 'NumericLiteral' &&
newCompositionFps !== null) {
changesMade.push({
description: 'Replaced FPS',
});
return {
...attribute,
value: {
...attribute.value,
expression: {
...attribute.value.expression,
value: newCompositionFps,
},
},
};
}
if (attribute.name.name === 'durationInFrames' &&
attribute.value &&
attribute.value.type === 'JSXExpressionContainer' &&
attribute.value.expression.type === 'NumericLiteral' &&
newCompositionDurationInFrames !== null) {
changesMade.push({
description: 'Replaced durationInFrames',
});
return {
...attribute,
value: {
...attribute.value,
expression: {
...attribute.value.expression,
value: newCompositionDurationInFrames,
},
},
};
}
if (attribute.name.name === 'width' &&
attribute.value &&
attribute.value.type === 'JSXExpressionContainer' &&
attribute.value.expression.type === 'NumericLiteral' &&
newCompositionWidth !== null) {
changesMade.push({
description: 'Replaced width',
});
return {
...attribute,
value: {
...attribute.value,
expression: {
...attribute.value.expression,
value: newCompositionWidth,
},
},
};
}
if (attribute.name.name === 'height' &&
attribute.value &&
attribute.value.type === 'JSXExpressionContainer' &&
attribute.value.expression.type === 'NumericLiteral' &&
newCompositionHeight !== null) {
changesMade.push({
description: 'Replaced height',
});
return {
...attribute,
value: {
...attribute.value,
expression: {
...attribute.value.expression,
value: newCompositionHeight,
},
},
};
}
return attribute;
})
.filter(Boolean);
const newName = newTagToUse !== null && newTagToUse !== void 0 ? newTagToUse : name.name;
if (newName !== name.name) {
changesMade.push({
description: `Changed tag`,
});
}
return {
...jsxElement,
openingElement: {
...jsxElement.openingElement,
name: {
...name,
name: newName,
},
attributes,
},
};
};
@@ -0,0 +1,5 @@
import type { SimpleDiff } from '@remotion/studio-shared';
export declare const simpleDiff: ({ oldLines, newLines, }: {
oldLines: string[];
newLines: string[];
}) => SimpleDiff;
@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.simpleDiff = void 0;
const findDeletions = ({ oldLines, newLines, }) => {
const linesChecked = [];
let totalDeletions = 0;
for (const line of oldLines) {
if (linesChecked.includes(line)) {
continue;
}
const timesInNewLines = newLines.filter((l) => l === line).length;
const timesInOldLines = oldLines.filter((l) => l === line).length;
const deletions = Math.max(0, timesInOldLines - timesInNewLines);
totalDeletions += deletions;
linesChecked.push(line);
}
return totalDeletions;
};
const findAdditions = ({ oldLines, newLines, }) => {
const linesChecked = [];
let totalAdditions = 0;
for (const line of newLines) {
if (linesChecked.includes(line)) {
continue;
}
const timesInNewLines = newLines.filter((l) => l === line).length;
const timesInOldLines = oldLines.filter((l) => l === line).length;
const additions = Math.max(0, timesInNewLines - timesInOldLines);
totalAdditions += additions;
linesChecked.push(line);
}
return totalAdditions;
};
const simpleDiff = ({ oldLines, newLines, }) => {
const deletions = findDeletions({ oldLines, newLines });
const additions = findAdditions({ oldLines, newLines });
return { deletions, additions };
};
exports.simpleDiff = simpleDiff;
@@ -0,0 +1,7 @@
import { type EnumPath } from '@remotion/studio-shared';
export declare const updateDefaultProps: ({ input, compositionId, newDefaultProps, enumPaths, }: {
input: string;
compositionId: string;
newDefaultProps: Record<string, unknown>;
enumPaths: EnumPath[];
}) => Promise<Promise<Promise<Promise<string>>>>;
@@ -0,0 +1,142 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateDefaultProps = void 0;
const studio_shared_1 = require("@remotion/studio-shared");
const recast = __importStar(require("recast"));
const parse_ast_1 = require("./parse-ast");
const updateDefaultProps = async ({ input, compositionId, newDefaultProps, enumPaths, }) => {
const ast = (0, parse_ast_1.parseAst)(input);
recast.types.visit(ast, {
visitJSXElement(path) {
var _a, _b;
const { openingElement } = path.node;
// 1: ensure its the element we're looking for
const openingName = openingElement.name;
if (openingName.type !== 'JSXIdentifier' &&
openingName.type !== 'JSXNamespacedName') {
this.traverse(path); // Continue traversing the AST
return;
}
if (openingName.name !== 'Composition' && openingName.name !== 'Still') {
this.traverse(path); // Continue traversing the AST
return;
}
if (!((_a = openingElement.attributes) === null || _a === void 0 ? void 0 : _a.some((attr) => {
if (attr.type === 'JSXSpreadAttribute') {
return;
}
if (!attr.value) {
return;
}
if (attr.value.type === 'JSXElement') {
return;
}
if (attr.value.type === 'JSXExpressionContainer') {
return;
}
if (attr.value.type === 'JSXFragment') {
return;
}
return attr.name.name === 'id' && attr.value.value === compositionId;
}))) {
this.traverse(path); // Continue traversing the AST
return;
}
// 2: Find the defaultProps attribute and handle related errors
const defaultPropsAttr = openingElement.attributes.find((attr) => {
if (attr.type === 'JSXSpreadAttribute') {
this.traverse(path); // Continue traversing the AST
return;
}
return attr.name.name === 'defaultProps';
});
if (!defaultPropsAttr) {
throw new Error(`No \`defaultProps\` prop found in the <Composition/> tag with the ID "${compositionId}".`);
}
if (defaultPropsAttr.type === 'JSXSpreadAttribute') {
this.traverse(path); // Continue traversing the AST
return;
}
// 3: ensure only hardcoded values are provided
if (!defaultPropsAttr.value ||
defaultPropsAttr.value.type === 'JSXElement' ||
defaultPropsAttr.value.type === 'JSXText' ||
defaultPropsAttr.value.type === 'StringLiteral' ||
defaultPropsAttr.value.type === 'NumericLiteral' ||
defaultPropsAttr.value.type === 'BigIntLiteral' ||
defaultPropsAttr.value.type === 'DecimalLiteral' ||
defaultPropsAttr.value.type === 'NullLiteral' ||
defaultPropsAttr.value.type === 'BooleanLiteral' ||
defaultPropsAttr.value.type === 'RegExpLiteral' ||
defaultPropsAttr.value.type === 'JSXFragment' ||
defaultPropsAttr.value.type === 'Literal') {
throw new Error(`\`defaultProps\` prop must be a hardcoded value in the <Composition/> tag, but it is a ${(_b = defaultPropsAttr.value) === null || _b === void 0 ? void 0 : _b.type}".`);
}
const defaultPropsValue = defaultPropsAttr.value.expression;
if (defaultPropsValue.type !== 'ObjectExpression' &&
defaultPropsValue.type !== 'TSAsExpression') {
throw new Error(`\`defaultProps\` prop must be a hardcoded value in the <Composition/> tag with the ID "${compositionId}".`);
}
defaultPropsAttr.value.expression = recast.types.builders.identifier((0, studio_shared_1.stringifyDefaultProps)({ props: newDefaultProps, enumPaths }));
this.traverse(path); // Continue traversing the AST
},
});
let prettier = null;
try {
prettier = await Promise.resolve().then(() => __importStar(require('prettier')));
}
catch (_a) {
throw new Error('Prettier cannot be found in the current project.');
}
const { format, resolveConfig, resolveConfigFile } = prettier;
const configFilePath = await resolveConfigFile();
if (!configFilePath) {
throw new Error('The Prettier config file was not found');
}
const prettierConfig = await resolveConfig(configFilePath);
if (!prettierConfig) {
throw new Error('The Prettier config file was not found. For this feature, the "prettier" package must be installed and a .prettierrc file must exist.');
}
const finalfile = (0, parse_ast_1.serializeAst)(ast);
const prettified = await format(finalfile, {
...prettierConfig,
filepath: 'test.tsx',
plugins: [],
endOfLine: 'auto',
});
return prettified;
};
exports.updateDefaultProps = updateDefaultProps;
@@ -0,0 +1,13 @@
type RemotionDetectionResult = {
type: 'match';
} | {
type: 'mismatch';
} | {
type: 'not-remotion';
};
export declare const detectRemotionServer: ({ port, cwd, hostname, }: {
port: number;
cwd: string;
hostname: string;
}) => Promise<RemotionDetectionResult>;
export {};
@@ -0,0 +1,51 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectRemotionServer = void 0;
const http_1 = __importDefault(require("http"));
const detectRemotionServer = ({ port, cwd, hostname, }) => {
return new Promise((resolve) => {
const req = http_1.default.get({
hostname,
port,
path: '/__remotion_config',
timeout: 1000,
}, (res) => {
if (res.statusCode !== 200) {
res.resume();
return resolve({ type: 'not-remotion' });
}
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('error', () => {
resolve({ type: 'not-remotion' });
});
res.on('end', () => {
try {
const json = JSON.parse(data);
if (json.isRemotion !== true) {
return resolve({ type: 'not-remotion' });
}
// Normalize paths for comparison to avoid issues with different separators or casing on Windows
const normalize = (p) => p.replace(/\\/g, '/').toLowerCase();
if (normalize(json.cwd) === normalize(cwd)) {
return resolve({ type: 'match' });
}
return resolve({ type: 'mismatch' });
}
catch (_a) {
resolve({ type: 'not-remotion' });
}
});
});
req.on('error', () => resolve({ type: 'not-remotion' }));
req.on('timeout', () => {
req.destroy();
});
});
};
exports.detectRemotionServer = detectRemotionServer;
@@ -0,0 +1,9 @@
type FileChangeType = 'created' | 'deleted' | 'changed';
export declare const installFileWatcher: ({ file, onChange, }: {
file: string;
onChange: (type: FileChangeType) => void;
}) => {
exists: boolean;
unwatch: () => void;
};
export {};
@@ -0,0 +1,40 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.installFileWatcher = void 0;
const node_fs_1 = __importDefault(require("node:fs"));
const installFileWatcher = ({ file, onChange, }) => {
const existedAtBeginning = node_fs_1.default.existsSync(file);
let existedBefore = existedAtBeginning;
const listener = () => {
const existsNow = node_fs_1.default.existsSync(file);
if (!existedBefore && existsNow) {
onChange('created');
existedBefore = true;
return;
}
if (existedBefore && !existsNow) {
onChange('deleted');
existedBefore = false;
return;
}
if (existsNow) {
if (typeof Deno !== 'undefined') {
// Deno always goes here, even if the file has not changed.
// Don't support this for now.
return;
}
onChange('changed');
}
};
node_fs_1.default.watchFile(file, { interval: 100 }, listener);
return {
exists: existedAtBeginning,
unwatch: () => {
node_fs_1.default.unwatchFile(file, listener);
},
};
};
exports.installFileWatcher = installFileWatcher;
@@ -0,0 +1 @@
export declare const getLatestRemotionVersion: () => Promise<any>;
@@ -0,0 +1,31 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLatestRemotionVersion = void 0;
const https_1 = __importDefault(require("https"));
const getPackageJsonForRemotion = () => {
return new Promise((resolve, reject) => {
const req = https_1.default.get('https://registry.npmjs.org/remotion', {
headers: {
accept: 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*',
},
}, (res) => {
let data = '';
res.on('data', (d) => {
data += d;
});
res.on('end', () => resolve(data));
});
req.on('error', (error) => {
reject(error);
});
req.end();
});
};
const getLatestRemotionVersion = async () => {
const pkgJson = await getPackageJsonForRemotion();
return JSON.parse(pkgJson)['dist-tags'].latest;
};
exports.getLatestRemotionVersion = getLatestRemotionVersion;
@@ -0,0 +1 @@
export declare const getNetworkAddress: () => string | undefined;
@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getNetworkAddress = void 0;
const node_os_1 = require("node:os");
const getNetworkAddress = () => {
for (const interfaceDetails of Object.values((0, node_os_1.networkInterfaces)())) {
if (!interfaceDetails)
continue;
for (const details of interfaceDetails) {
const { address, family, internal } = details;
if (family === 'IPv4' && !internal)
return address;
}
}
};
exports.getNetworkAddress = getNetworkAddress;
@@ -0,0 +1 @@
export declare const getFileSource: (remotionRoot: string, p: string) => Promise<string>;
@@ -0,0 +1,22 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFileSource = void 0;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const allowedFileExtensions = ['js', 'ts', 'tsx', 'jsx', 'map', 'mjs'];
// Must be async function for proper error handling
const getFileSource = (remotionRoot, p) => {
if (!allowedFileExtensions.find((extension) => p.endsWith(extension))) {
return Promise.reject(new Error(`Not allowed to open ${p}`));
}
const resolved = node_path_1.default.resolve(remotionRoot, p);
const relativeToProcessCwd = node_path_1.default.relative(remotionRoot, resolved);
if (relativeToProcessCwd.startsWith('..')) {
return Promise.reject(new Error(`Not allowed to open ${relativeToProcessCwd}`));
}
return node_fs_1.default.promises.readFile(p, 'utf-8');
};
exports.getFileSource = getFileSource;
@@ -0,0 +1,6 @@
export declare const getInstalledDependencies: (remotionRoot: string) => {
dependencies: string[];
devDependencies: string[];
optionalDependencies: string[];
peerDependencies: string[];
};
@@ -0,0 +1,24 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getInstalledDependencies = void 0;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const getInstalledDependencies = (remotionRoot) => {
var _a, _b, _c, _d;
const packageJsonFilePath = node_path_1.default.join(remotionRoot, 'package.json');
const packageJson = JSON.parse(node_fs_1.default.readFileSync(packageJsonFilePath, 'utf-8'));
const dependencies = Object.keys((_a = packageJson.dependencies) !== null && _a !== void 0 ? _a : {});
const devDependencies = Object.keys((_b = packageJson.devDependencies) !== null && _b !== void 0 ? _b : {});
const optionalDependencies = Object.keys((_c = packageJson.optionalDependencies) !== null && _c !== void 0 ? _c : {});
const peerDependencies = Object.keys((_d = packageJson.peerDependencies) !== null && _d !== void 0 ? _d : {});
return {
dependencies,
devDependencies,
optionalDependencies,
peerDependencies,
};
};
exports.getInstalledDependencies = getInstalledDependencies;
@@ -0,0 +1 @@
export declare const getInstalledInstallablePackages: (remotionRoot: string) => string[];
@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getInstalledInstallablePackages = void 0;
const studio_shared_1 = require("@remotion/studio-shared");
const get_installed_dependencies_1 = require("./get-installed-dependencies");
const getInstalledInstallablePackages = (remotionRoot) => {
const { dependencies, devDependencies, optionalDependencies } = (0, get_installed_dependencies_1.getInstalledDependencies)(remotionRoot);
const installablePackages = [
...dependencies,
...devDependencies,
...optionalDependencies,
];
const remotionPackages = Object.entries(studio_shared_1.installableMap)
.filter(([, _installable]) => _installable)
.map(([pkg]) => (pkg === 'core' ? 'remotion' : `@remotion/${pkg}`))
.filter((pkg) => installablePackages.includes(pkg));
const installedExtraPackages = studio_shared_1.extraPackages
.map((pkg) => pkg.name)
.filter((pkg) => installablePackages.includes(pkg));
return [...remotionPackages, ...installedExtraPackages];
};
exports.getInstalledInstallablePackages = getInstalledInstallablePackages;
@@ -0,0 +1,7 @@
import type { PackageManager } from '@remotion/studio-shared';
export declare const getInstallCommand: ({ manager, packages, version, additionalArgs, }: {
manager: PackageManager;
packages: string[];
version: string;
additionalArgs: string[];
}) => string[];
@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getInstallCommand = void 0;
const getInstallCommand = ({ manager, packages, version, additionalArgs, }) => {
const pkgList = packages.map((p) => (version ? `${p}@${version}` : p));
const commands = {
npm: [
'i',
'--save-exact',
'--no-fund',
'--no-audit',
...additionalArgs,
...pkgList,
],
pnpm: ['i', ...additionalArgs, ...pkgList],
yarn: ['add', '--exact', ...additionalArgs, ...pkgList],
bun: ['i', ...additionalArgs, ...pkgList],
};
return commands[manager];
};
exports.getInstallCommand = getInstallCommand;
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { LogLevel } from '@remotion/renderer';
declare const editorNames: readonly ["atom", "/Applications/Atom Beta.app/Contents/MacOS/Atom Beta", "brackets", "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl", "/Applications/Sublime Text Dev.app/Contents/SharedSupport/bin/subl", "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl", "code", "code-insiders", "vscodium", "/Applications/AppCode.app/Contents/MacOS/appcode", "/Applications/CLion.app/Contents/MacOS/clion", "/Applications/IntelliJ IDEA.app/Contents/MacOS/idea", "/Applications/PhpStorm.app/Contents/MacOS/phpstorm", "/Applications/PyCharm.app/Contents/MacOS/pycharm", "/Applications/PyCharm CE.app/Contents/MacOS/pycharm", "/Applications/RubyMine.app/Contents/MacOS/rubymine", "/Applications/WebStorm.app/Contents/MacOS/webstorm", "/Applications/GoLand.app/Contents/MacOS/goland", "/Applications/Rider.app/Contents/MacOS/rider", "mvim", "emacs", "gvim", "idea", "phpstorm", "pycharm", "rubymine", "subl", "sublime_text", "vim", "webstorm", "goland", "rider", "Brackets.exe", "Code.exe", "Code - Insiders.exe", "VSCodium.exe", "atom.exe", "sublime_text.exe", "notepad++.exe", "clion.exe", "clion64.exe", "idea.exe", "idea64.exe", "phpstorm.exe", "phpstorm64.exe", "pycharm.exe", "pycharm64.exe", "rubymine.exe", "rubymine64.exe", "webstorm.exe", "webstorm64.exe", "goland.exe", "goland64.exe", "rider.exe", "rider64.exe", "nano", "cursor", "/Applications/Cursor.app/Contents/MacOS/Cursor", "Cursor.exe", "windsurf", "/Applications/Windsurf.app/Contents/MacOS/Windsurf", "Windsurf.exe", "zed"];
export declare const getDisplayNameForEditor: (editor: Editor | null) => string | null;
type Editor = (typeof editorNames)[number];
type ProcessAndCommand = {
process: string;
command: Editor;
};
export declare function guessEditor(): Promise<ProcessAndCommand[]>;
export declare function launchEditor({ colNumber, editor, fileName, lineNumber, vsCodeNewWindow, logLevel, }: {
fileName: string;
lineNumber: number;
colNumber: number;
editor: ProcessAndCommand;
vsCodeNewWindow: boolean;
logLevel: LogLevel;
}): Promise<boolean>;
export {};
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
export declare const resolveOutputPath: (remotionRoot: string, filePath: string) => string;
@@ -0,0 +1,16 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveOutputPath = void 0;
const node_path_1 = __importDefault(require("node:path"));
const resolveOutputPath = (remotionRoot, filePath) => {
const absolutePath = node_path_1.default.join(remotionRoot, filePath);
const relativeToRoot = node_path_1.default.relative(remotionRoot, absolutePath);
if (relativeToRoot.startsWith('..')) {
throw new Error(`Not allowed to write to ${relativeToRoot}`);
}
return absolutePath;
};
exports.resolveOutputPath = resolveOutputPath;
@@ -0,0 +1,97 @@
export { ApiRoutes, CopyStillToClipboardRequest, getDefaultOutLocation, OpenInFileExplorerRequest, } from '@remotion/studio-shared';
export type { AggregateRenderProgress, BundlingState, CopyingState, DownloadProgress, HotMiddlewareOptions, JobProgressCallback, ModuleMap, PackageManager, ProjectInfo, RenderingProgressInput, RenderJob, RenderJobWithCleanup, RequiredChromiumOptions, StitchingProgressInput, UiOpenGlOptions, } from '@remotion/studio-shared';
import { AnsiDiff } from './ansi-diff';
export declare const StudioServerInternals: {
startStudio: ({ browserArgs, browserFlag, configValueShouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, parsedCliOpen, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, enableCrossSiteIsolation, askAIEnabled, forceNew, }: {
browserArgs: string;
browserFlag: string;
logLevel: import("@remotion/renderer").LogLevel;
configValueShouldOpenBrowser: boolean;
fullEntryPath: string;
getCurrentInputProps: () => object;
getEnvVariables: () => Record<string, string>;
desiredPort: number | null;
maxTimelineTracks: number | null;
bufferStateDelayInMilliseconds: number | null;
remotionRoot: string;
keyboardShortcutsEnabled: boolean;
experimentalClientSideRenderingEnabled: boolean;
relativePublicDir: string | null;
webpackOverride: import("@remotion/bundler").WebpackOverrideFn;
poll: number | null;
getRenderDefaults: () => import("@remotion/studio-shared").RenderDefaults;
getRenderQueue: () => import("@remotion/studio-shared").RenderJob[];
numberOfAudioTags: number;
audioLatencyHint: AudioContextLatencyCategory | null;
enableCrossSiteIsolation: boolean;
queueMethods: import("./preview-server/api-types").QueueMethods;
parsedCliOpen: boolean;
previewEntry: string;
gitSource: import("@remotion/studio-shared").GitSource | null;
binariesDirectory: string | null;
forceIPv4: boolean;
askAIEnabled: boolean;
forceNew: boolean;
}) => Promise<import("./start-studio").StartStudioResult>;
getRemotionVersion: () => any;
waitForLiveEventsListener: () => Promise<import("./preview-server/live-events").LiveEventsServer>;
lockFilePaths: {
manager: import("@remotion/studio-shared").PackageManager;
path: string;
installCommand: string;
startCommand: string;
}[];
getPackageManager: ({ remotionRoot, packageManager, dirUp, logLevel, }: {
remotionRoot: string;
packageManager: string | undefined;
dirUp: number;
logLevel: import("@remotion/renderer").LogLevel;
}) => {
manager: import("@remotion/studio-shared").PackageManager;
path: string;
installCommand: string;
startCommand: string;
} | "unknown";
getMaxTimelineTracks: () => number;
setMaxTimelineTracks: (maxTracks: number) => void;
getLatestRemotionVersion: () => Promise<any>;
installFileWatcher: ({ file, onChange, }: {
file: string;
onChange: (type: "created" | "deleted" | "changed") => void;
}) => {
exists: boolean;
unwatch: () => void;
};
AnsiDiff: typeof AnsiDiff;
formatBytes: (number: number, options?: Intl.NumberFormatOptions & {
locale: string;
bits?: boolean;
binary?: boolean;
signed: boolean;
}) => string;
parseAndApplyCodemod: ({ input, codeMod, }: {
input: string;
codeMod: import("@remotion/studio-shared").RecastCodemod;
}) => {
newContents: string;
changesMade: import("./codemods/recast-mods").Change[];
};
getInstalledDependencies: (remotionRoot: string) => {
dependencies: string[];
devDependencies: string[];
optionalDependencies: string[];
peerDependencies: string[];
};
getInstallCommand: ({ manager, packages, version, additionalArgs, }: {
manager: import("@remotion/studio-shared").PackageManager;
packages: string[];
version: string;
additionalArgs: string[];
}) => string[];
addCompletedClientRender: ({ render, remotionRoot, }: {
render: import("@remotion/studio-shared").CompletedClientRender;
remotionRoot: string;
}) => void;
getCompletedClientRenders: () => import("@remotion/studio-shared").CompletedClientRender[];
removeCompletedClientRender: (id: string) => void;
};
@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StudioServerInternals = exports.getDefaultOutLocation = void 0;
const studio_shared_1 = require("@remotion/studio-shared");
var studio_shared_2 = require("@remotion/studio-shared");
Object.defineProperty(exports, "getDefaultOutLocation", { enumerable: true, get: function () { return studio_shared_2.getDefaultOutLocation; } });
const ansi_diff_1 = require("./ansi-diff");
const client_render_queue_1 = require("./client-render-queue");
const duplicate_composition_1 = require("./codemods/duplicate-composition");
const file_watcher_1 = require("./file-watcher");
const get_latest_remotion_version_1 = require("./get-latest-remotion-version");
const get_installed_dependencies_1 = require("./helpers/get-installed-dependencies");
const install_command_1 = require("./helpers/install-command");
const max_timeline_tracks_1 = require("./max-timeline-tracks");
const get_package_manager_1 = require("./preview-server/get-package-manager");
const live_events_1 = require("./preview-server/live-events");
const update_available_1 = require("./preview-server/update-available");
const start_studio_1 = require("./start-studio");
exports.StudioServerInternals = {
startStudio: start_studio_1.startStudio,
getRemotionVersion: update_available_1.getRemotionVersion,
waitForLiveEventsListener: live_events_1.waitForLiveEventsListener,
lockFilePaths: get_package_manager_1.lockFilePaths,
getPackageManager: get_package_manager_1.getPackageManager,
getMaxTimelineTracks: max_timeline_tracks_1.getMaxTimelineTracks,
setMaxTimelineTracks: max_timeline_tracks_1.setMaxTimelineTracks,
getLatestRemotionVersion: get_latest_remotion_version_1.getLatestRemotionVersion,
installFileWatcher: file_watcher_1.installFileWatcher,
AnsiDiff: ansi_diff_1.AnsiDiff,
formatBytes: studio_shared_1.formatBytes,
parseAndApplyCodemod: duplicate_composition_1.parseAndApplyCodemod,
getInstalledDependencies: get_installed_dependencies_1.getInstalledDependencies,
getInstallCommand: install_command_1.getInstallCommand,
addCompletedClientRender: client_render_queue_1.addCompletedClientRender,
getCompletedClientRenders: client_render_queue_1.getCompletedClientRenders,
removeCompletedClientRender: client_render_queue_1.removeCompletedClientRender,
};
@@ -0,0 +1,2 @@
export declare const setMaxTimelineTracks: (maxTracks: number) => void;
export declare const getMaxTimelineTracks: () => number;
@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMaxTimelineTracks = exports.setMaxTimelineTracks = void 0;
const studio_shared_1 = require("@remotion/studio-shared");
let maxTimelineTracks = studio_shared_1.DEFAULT_TIMELINE_TRACKS;
const setMaxTimelineTracks = (maxTracks) => {
if (typeof maxTracks !== 'number') {
throw new Error(`Need to pass a number to Config.setMaxTimelineTracks(), got ${typeof maxTracks}`);
}
if (Number.isNaN(maxTracks)) {
throw new Error(`Need to pass a real number to Config.setMaxTimelineTracks(), got NaN`);
}
if (!Number.isFinite(maxTracks)) {
throw new Error(`Need to pass a real number to Config.setMaxTimelineTracks(), got ${maxTracks}`);
}
if (maxTracks < 0) {
throw new Error(`Need to pass a non-negative number to Config.setMaxTimelineTracks(), got ${maxTracks}`);
}
maxTimelineTracks = maxTracks;
};
exports.setMaxTimelineTracks = setMaxTimelineTracks;
const getMaxTimelineTracks = () => {
return maxTimelineTracks;
};
exports.getMaxTimelineTracks = getMaxTimelineTracks;
@@ -0,0 +1,11 @@
import type { LogLevel } from '@remotion/renderer';
export declare const maybeOpenBrowser: ({ browserArgs, browserFlag, configValueShouldOpenBrowser, parsedCliOpen, url, logLevel, }: {
browserArgs: string;
browserFlag: string;
configValueShouldOpenBrowser: boolean;
parsedCliOpen: boolean;
url: string;
logLevel: LogLevel;
}) => Promise<{
didOpenBrowser: boolean;
}>;
@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.maybeOpenBrowser = void 0;
const renderer_1 = require("@remotion/renderer");
const better_opn_1 = require("./better-opn");
const getShouldOpenBrowser = ({ configValueShouldOpenBrowser, parsedCliOpen, }) => {
var _a;
if (parsedCliOpen === false) {
return {
shouldOpenBrowser: false,
reasonForBrowserDecision: '--no-open specified',
};
}
if (((_a = process.env.BROWSER) !== null && _a !== void 0 ? _a : '').toLowerCase() === 'none') {
return {
shouldOpenBrowser: false,
reasonForBrowserDecision: 'env BROWSER=none was set',
};
}
if (configValueShouldOpenBrowser === false) {
return { shouldOpenBrowser: false, reasonForBrowserDecision: 'Config file' };
}
return { shouldOpenBrowser: true, reasonForBrowserDecision: 'default' };
};
const maybeOpenBrowser = async ({ browserArgs, browserFlag, configValueShouldOpenBrowser, parsedCliOpen, url, logLevel, }) => {
const { reasonForBrowserDecision, shouldOpenBrowser } = getShouldOpenBrowser({
configValueShouldOpenBrowser,
parsedCliOpen,
});
if (shouldOpenBrowser) {
await (0, better_opn_1.openBrowser)({
url,
browserArgs,
browserFlag,
});
}
else {
renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `Not opening browser, reason: ${reasonForBrowserDecision}`);
}
return { didOpenBrowser: shouldOpenBrowser };
};
exports.maybeOpenBrowser = maybeOpenBrowser;
@@ -0,0 +1 @@
export declare const openDirectoryInFinder: (dirToOpen: string, allowedDirectory: string) => Promise<void>;
@@ -0,0 +1,57 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.openDirectoryInFinder = void 0;
const node_child_process_1 = require("node:child_process");
const node_os_1 = require("node:os");
const node_path_1 = __importDefault(require("node:path"));
const no_react_1 = require("remotion/no-react");
const openDirectoryInFinder = (dirToOpen, allowedDirectory) => {
const resolved = node_path_1.default.resolve(allowedDirectory, dirToOpen);
const relativeToProcessCwd = node_path_1.default.relative(allowedDirectory, resolved);
if (relativeToProcessCwd.startsWith('..')) {
throw new Error(`Not allowed to open ${relativeToProcessCwd}`);
}
if ((0, node_os_1.platform)() === 'win32') {
const proc = (0, node_child_process_1.spawn)('explorer.exe', ['/select,', resolved]);
return new Promise((resolve, reject) => {
proc.on('exit', (code) => {
// explorer.exe returns 1 even on success
if (code === 0 || code === 1) {
resolve();
}
else {
reject(new Error(`explorer.exe exited with code ${code}`));
}
});
proc.on('error', (err) => {
proc.kill();
reject(err);
});
});
}
const command = (0, node_os_1.platform)() === 'darwin' ? 'open' : 'xdg-open';
const p = (0, node_child_process_1.spawn)(command, [(0, node_os_1.platform)() === 'darwin' ? '-R' : null, dirToOpen].filter(no_react_1.NoReactInternals.truthy));
const stderrChunks = [];
p.stderr.on('data', (d) => stderrChunks.push(d));
return new Promise((resolve, reject) => {
p.on('exit', (code) => {
if (code === 0) {
resolve();
}
else {
const message = Buffer.concat(stderrChunks.map((buf) => Uint8Array.from(buf))).toString('utf8');
reject(new Error(message));
}
});
p.on('error', (err) => {
p.kill();
if (err) {
reject(err);
}
});
});
};
exports.openDirectoryInFinder = openDirectoryInFinder;
@@ -0,0 +1,37 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bun_test_1 = require("bun:test");
const node_fs_1 = __importDefault(require("node:fs"));
const node_os_1 = __importDefault(require("node:os"));
const node_path_1 = __importDefault(require("node:path"));
const get_package_manager_1 = require("../get-package-manager");
(0, bun_test_1.describe)('getPackageManager multiple lockfiles', () => {
let tempDir;
(0, bun_test_1.beforeEach)(() => {
tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'remotion-test-'));
});
(0, bun_test_1.afterEach)(() => {
node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
});
(0, bun_test_1.test)('should not throw error when multiple lockfiles are detected', () => {
node_fs_1.default.writeFileSync(node_path_1.default.join(tempDir, 'package-lock.json'), '{}');
node_fs_1.default.writeFileSync(node_path_1.default.join(tempDir, 'bun.lock'), '');
// Should not throw
const manager = (0, get_package_manager_1.getPackageManager)(tempDir, undefined, 0);
// Should return one of them (usually the first one in the list, which is npm)
(0, bun_test_1.expect)(manager).not.toBe('unknown');
if (typeof manager !== 'string') {
(0, bun_test_1.expect)(['npm', 'bun']).toContain(manager.manager);
}
});
(0, bun_test_1.test)('should return npm if only package-lock.json exists', () => {
node_fs_1.default.writeFileSync(node_path_1.default.join(tempDir, 'package-lock.json'), '{}');
const manager = (0, get_package_manager_1.getPackageManager)(tempDir, undefined, 0);
if (typeof manager !== 'string') {
(0, bun_test_1.expect)(manager.manager).toBe('npm');
}
});
});
@@ -0,0 +1,5 @@
import type { ApiRoutes } from '@remotion/studio-shared';
import type { ApiHandler } from './api-types';
export declare const allApiRoutes: {
[key in keyof ApiRoutes]: ApiHandler<ApiRoutes[key]['Request'], ApiRoutes[key]['Response']>;
};
@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.allApiRoutes = void 0;
const add_render_1 = require("./routes/add-render");
const apply_codemod_1 = require("./routes/apply-codemod");
const apply_visual_control_change_1 = require("./routes/apply-visual-control-change");
const can_update_default_props_1 = require("./routes/can-update-default-props");
const cancel_render_1 = require("./routes/cancel-render");
const delete_static_file_1 = require("./routes/delete-static-file");
const install_dependency_1 = require("./routes/install-dependency");
const open_in_file_explorer_1 = require("./routes/open-in-file-explorer");
const project_info_1 = require("./routes/project-info");
const remove_render_1 = require("./routes/remove-render");
const restart_studio_1 = require("./routes/restart-studio");
const subscribe_to_file_existence_1 = require("./routes/subscribe-to-file-existence");
const unsubscribe_from_file_existence_1 = require("./routes/unsubscribe-from-file-existence");
const update_available_1 = require("./routes/update-available");
const update_default_props_1 = require("./routes/update-default-props");
exports.allApiRoutes = {
'/api/cancel': cancel_render_1.handleCancelRender,
'/api/render': add_render_1.handleAddRender,
'/api/unsubscribe-from-file-existence': unsubscribe_from_file_existence_1.unsubscribeFromFileExistence,
'/api/subscribe-to-file-existence': subscribe_to_file_existence_1.subscribeToFileExistence,
'/api/remove-render': remove_render_1.handleRemoveRender,
'/api/open-in-file-explorer': open_in_file_explorer_1.handleOpenInFileExplorer,
'/api/update-default-props': update_default_props_1.updateDefaultPropsHandler,
'/api/apply-visual-control-change': apply_visual_control_change_1.applyVisualControlHandler,
'/api/apply-codemod': apply_codemod_1.applyCodemodHandler,
'/api/can-update-default-props': can_update_default_props_1.canUpdateDefaultPropsHandler,
'/api/update-available': update_available_1.handleUpdate,
'/api/project-info': project_info_1.projectInfoHandler,
'/api/delete-static-file': delete_static_file_1.deleteStaticFileHandler,
'/api/restart-studio': restart_studio_1.handleRestartStudio,
'/api/install-package': install_dependency_1.handleInstallPackage,
};
@@ -0,0 +1,24 @@
import type { LogLevel } from '@remotion/renderer';
import type { RenderJobWithCleanup } from '@remotion/studio-shared';
import type { IncomingMessage, ServerResponse } from 'node:http';
export type QueueMethods = {
removeJob: (jobId: string) => void;
cancelJob: (jobId: string) => void;
addJob: ({ job, entryPoint, remotionRoot, logLevel, }: {
job: RenderJobWithCleanup;
entryPoint: string;
remotionRoot: string;
logLevel: LogLevel;
}) => void;
};
export type ApiHandler<ReqData, ResData> = (params: {
input: ReqData;
entryPoint: string;
remotionRoot: string;
request: IncomingMessage;
response: ServerResponse;
logLevel: LogLevel;
methods: QueueMethods;
publicDir: string;
binariesDirectory: string | null;
}) => Promise<ResData>;
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
export declare const noOpUntilRestart: () => Promise<void>;
export declare const signalRestart: () => void;
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.signalRestart = exports.noOpUntilRestart = void 0;
const resolveFunctions = [];
const noOpUntilRestart = () => {
return new Promise((resolve) => {
resolveFunctions.push(resolve);
});
};
exports.noOpUntilRestart = noOpUntilRestart;
const signalRestart = () => {
resolveFunctions.forEach((f) => {
f();
});
resolveFunctions.length = 0;
};
exports.signalRestart = signalRestart;
@@ -0,0 +1,4 @@
import type { ReadStream } from 'node:fs';
import type { IncomingMessage, ServerResponse } from 'node:http';
export declare function setHeaderForResponse(res: ServerResponse, name: string, value: string | number): void;
export declare function send(req: IncomingMessage, res: ServerResponse, bufferOtStream: ReadStream | string | Buffer, byteLength: number): void;
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setHeaderForResponse = setHeaderForResponse;
exports.send = send;
function setHeaderForResponse(res, name, value) {
res.setHeader(name, typeof value === 'number' ? String(value) : value);
}
function send(req, res, bufferOtStream, byteLength) {
if (typeof bufferOtStream === 'string' || Buffer.isBuffer(bufferOtStream)) {
res.end(bufferOtStream);
return;
}
setHeaderForResponse(res, 'Content-Length', byteLength);
if (req.method === 'HEAD') {
res.end();
return;
}
bufferOtStream.pipe(res);
}
@@ -0,0 +1,7 @@
import type { DevMiddlewareContext } from './types';
type PublicPath = {
outputPath: string;
publicPath: string;
};
export declare function getPaths(context: DevMiddlewareContext): PublicPath[];
export {};
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPaths = getPaths;
function getPaths(context) {
const { stats } = context;
const publicPaths = [];
if (!stats) {
return publicPaths;
}
const { compilation } = stats;
// The `output.path` is always present and always absolute
const outputPath = compilation.getPath(compilation.outputOptions.path || '');
const publicPath = compilation.outputOptions.publicPath
? compilation.getPath(compilation.outputOptions.publicPath)
: '';
publicPaths.push({ outputPath, publicPath });
return publicPaths;
}
@@ -0,0 +1,4 @@
import type { webpack } from '@remotion/bundler';
import type { LogLevel } from '@remotion/renderer';
import type { MiddleWare } from './middleware';
export declare const wdm: (compiler: webpack.Compiler, logLevel: LogLevel) => MiddleWare;
@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.wdm = void 0;
const middleware_1 = require("./middleware");
const setup_hooks_1 = require("./setup-hooks");
const setup_output_filesystem_1 = require("./setup-output-filesystem");
const wdm = (compiler, logLevel) => {
const context = {
state: false,
stats: undefined,
callbacks: [],
compiler,
logger: compiler.getInfrastructureLogger('remotion'),
outputFileSystem: undefined,
};
(0, setup_hooks_1.setupHooks)(context, logLevel);
(0, setup_output_filesystem_1.setupOutputFileSystem)(context);
const errorHandler = (error) => {
if (error) {
context.logger.error(error);
}
};
const watchOptions = context.compiler.options.watchOptions || {};
context.compiler.watch(watchOptions, errorHandler);
return (0, middleware_1.middleware)(context);
};
exports.wdm = wdm;
@@ -0,0 +1,8 @@
import type { IncomingMessage, ServerResponse } from 'node:http';
import type { DevMiddlewareContext } from './types';
export declare function getValueContentRangeHeader(type: string, size: number, range?: {
start: number;
end: number;
}): string;
export type MiddleWare = (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
export declare function middleware(context: DevMiddlewareContext): (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
@@ -0,0 +1,219 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getValueContentRangeHeader = getValueContentRangeHeader;
exports.middleware = middleware;
const renderer_1 = require("@remotion/renderer");
const node_path_1 = __importDefault(require("node:path"));
const node_querystring_1 = __importDefault(require("node:querystring"));
const node_url_1 = require("node:url");
const compatible_api_1 = require("./compatible-api");
const get_paths_1 = require("./get-paths");
const range_parser_1 = require("./range-parser");
const ready_1 = require("./ready");
const cacheStore = new WeakMap();
const mem = (fn, { cache = new Map() } = {}) => {
const memoized = (...arguments_) => {
const [key] = arguments_;
const cacheItem = cache.get(key);
if (cacheItem) {
return cacheItem.data;
}
const result = fn.apply(this, arguments_);
cache.set(key, {
data: result,
});
return result;
};
cacheStore.set(memoized, cache);
return memoized;
};
const memoizedParse = mem(node_url_1.parse);
function getFilenameFromUrl(context, url) {
var _a, _b;
const paths = (0, get_paths_1.getPaths)(context);
let foundFilename;
let urlObject;
try {
// The `url` property of the `request` is contains only `pathname`, `search` and `hash`
urlObject = memoizedParse(url, false, true);
}
catch (_c) {
return;
}
for (const { publicPath, outputPath } of paths) {
let filename;
let publicPathObject;
try {
publicPathObject = memoizedParse(publicPath !== 'auto' && publicPath ? publicPath : '/', false, true);
}
catch (_d) {
continue;
}
if ((_a = urlObject.pathname) === null || _a === void 0 ? void 0 : _a.startsWith(publicPathObject.pathname)) {
filename = outputPath;
// Strip the `pathname` property from the `publicPath` option from the start of requested url
// `/complex/foo.js` => `foo.js`
const pathname = urlObject.pathname.substr(publicPathObject.pathname.length);
if (pathname) {
filename = node_path_1.default.join(outputPath, node_querystring_1.default.unescape(pathname));
}
if (!context.outputFileSystem) {
continue;
}
try {
let fsStats = (_b = context.outputFileSystem) === null || _b === void 0 ? void 0 : _b.statSync(filename);
if (fsStats.isFile()) {
foundFilename = filename;
break;
}
else if (fsStats.isDirectory()) {
const indexValue = 'index.html';
filename = node_path_1.default.join(filename, indexValue);
try {
fsStats = context.outputFileSystem.statSync(filename);
}
catch (_e) {
continue;
}
if (fsStats.isFile()) {
foundFilename = filename;
break;
}
}
}
catch (_f) {
continue;
}
}
}
return foundFilename;
}
function getValueContentRangeHeader(type, size, range) {
return `${type} ${range ? `${range.start}-${range.end}` : '*'}/${size}`;
}
function createHtmlDocument(title, body) {
return (`${'<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'<head>\n' +
'<meta charset="utf-8">\n' +
'<title>'}${title}</title>\n` +
`</head>\n` +
`<body>\n` +
`<pre>${body}</pre>\n` +
`</body>\n` +
`</html>\n`);
}
const BYTES_RANGE_REGEXP = /^ *bytes/i;
function middleware(context) {
return function (req, res, next) {
const acceptedMethods = ['GET', 'HEAD'];
if (req.method && !acceptedMethods.includes(req.method)) {
goNext();
return;
}
(0, ready_1.ready)(context, processRequest);
function goNext() {
return next();
}
async function processRequest() {
var _a;
const filename = getFilenameFromUrl(context, req.url);
if (!filename) {
goNext();
return;
}
/**
* @type {{key: string, value: string | number}[]}
*/
if (!res.getHeader('Content-Type')) {
// content-type name(like application/javascript; charset=utf-8) or false
const contentType = renderer_1.RenderInternals.mimeContentType(node_path_1.default.extname(filename));
// Only set content-type header if media type is known
// https://tools.ietf.org/html/rfc7231#section-3.1.1.5
if (contentType) {
(0, compatible_api_1.setHeaderForResponse)(res, 'Content-Type', contentType);
}
}
if (!res.getHeader('Accept-Ranges')) {
res.setHeader('Accept-Ranges', 'bytes');
(0, compatible_api_1.setHeaderForResponse)(res, 'Accept-Ranges', 'bytes');
}
const rangeHeader = req.headers.range;
let start;
let end;
if (rangeHeader &&
BYTES_RANGE_REGEXP.test(rangeHeader) &&
context.outputFileSystem) {
const size = await new Promise((resolve) => {
var _a;
(_a = context.outputFileSystem) === null || _a === void 0 ? void 0 : _a.lstat(filename, (error, stats) => {
var _a;
if (error) {
context.logger.error(error);
return;
}
resolve((_a = stats === null || stats === void 0 ? void 0 : stats.size) !== null && _a !== void 0 ? _a : 0);
});
});
const parsedRanges = (0, range_parser_1.parseRange)(size, rangeHeader);
if (parsedRanges === -1) {
const message = "Unsatisfiable range for 'Range' header.";
context.logger.error(message);
const existingHeaders = res.getHeaderNames();
for (const header of existingHeaders) {
res.removeHeader(header);
}
res.statusCode = 416;
(0, compatible_api_1.setHeaderForResponse)(res, 'Content-Range', getValueContentRangeHeader('bytes', size));
(0, compatible_api_1.setHeaderForResponse)(res, 'Content-Type', 'text/html; charset=utf-8');
const document = createHtmlDocument(416, `Error: ${message}`);
const _byteLength = Buffer.byteLength(document);
(0, compatible_api_1.setHeaderForResponse)(res, 'Content-Length', Buffer.byteLength(document));
(0, compatible_api_1.send)(req, res, document, _byteLength);
return;
}
if (parsedRanges === -2) {
context.logger.error("A malformed 'Range' header was provided. A regular response will be sent for this request.");
}
else if (parsedRanges.length > 1) {
context.logger.error("A 'Range' header with multiple ranges was provided. Multiple ranges are not supported, so a regular response will be sent for this request.");
}
if (parsedRanges !== -2 && parsedRanges.length === 1) {
// Content-Range
res.statusCode = 206;
(0, compatible_api_1.setHeaderForResponse)(res, 'Content-Range', getValueContentRangeHeader('bytes', size, parsedRanges[0]));
[{ start, end }] = parsedRanges;
}
}
const isFsSupportsStream = typeof ((_a = context.outputFileSystem) === null || _a === void 0 ? void 0 : _a.createReadStream) === 'function';
let bufferOtStream;
let byteLength = 0;
try {
if (typeof start !== 'undefined' &&
typeof end !== 'undefined' &&
isFsSupportsStream &&
context.outputFileSystem) {
bufferOtStream = context.outputFileSystem.createReadStream(filename, {
start,
end,
});
byteLength = end - start + 1;
}
else if (context.outputFileSystem) {
bufferOtStream = context.outputFileSystem.readFileSync(filename);
byteLength = bufferOtStream.byteLength;
}
}
catch (_b) {
goNext();
return;
}
if (bufferOtStream) {
(0, compatible_api_1.send)(req, res, bufferOtStream, byteLength);
}
}
};
}
@@ -0,0 +1,15 @@
/*!
* range-parser
* Copyright(c) 2012-2014 TJ Holowaychuk
* Copyright(c) 2015-2016 Douglas Christopher Wilson
* MIT Licensed
*/
type Range = {
start: number;
end: number;
};
type Ranges = Range[] & {
type?: string;
};
export declare function parseRange(size: number, str: string | string[]): -1 | Ranges | -2;
export {};
@@ -0,0 +1,95 @@
"use strict";
/*!
* range-parser
* Copyright(c) 2012-2014 TJ Holowaychuk
* Copyright(c) 2015-2016 Douglas Christopher Wilson
* MIT Licensed
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseRange = parseRange;
function parseRange(size, str) {
if (typeof str !== 'string') {
throw new TypeError('argument str must be a string');
}
const index = str.indexOf('=');
if (index === -1) {
return -2;
}
// split the range string
const arr = str.slice(index + 1).split(',');
const ranges = [];
// add ranges type
ranges.type = str.slice(0, index);
// parse all ranges
for (let i = 0; i < arr.length; i++) {
const range = arr[i].split('-');
let start = parseInt(range[0], 10);
let end = parseInt(range[1], 10);
// -nnn
if (isNaN(start)) {
start = size - end;
end = size - 1;
// nnn-
}
else if (isNaN(end)) {
end = size - 1;
}
// limit last-byte-pos to current length
if (end > size - 1) {
end = size - 1;
}
// invalid or unsatisifiable
if (isNaN(start) || isNaN(end) || start > end || start < 0) {
continue;
}
// add range
ranges.push({
start,
end,
});
}
if (ranges.length < 1) {
return -1;
}
return combineRanges(ranges);
}
function combineRanges(ranges) {
const ordered = ranges.map(mapWithIndex).sort(sortByRangeStart);
let j = 0;
for (let i = 1; i < ordered.length; i++) {
const range = ordered[i];
const current = ordered[j];
if (range.start > current.end + 1) {
// next range
ordered[++j] = range;
}
else if (range.end > current.end) {
// extend range
current.end = range.end;
current.index = Math.min(current.index, range.index);
}
}
ordered.length = j + 1;
const combined = ordered.sort(sortByRangeIndex).map(mapWithoutIndex);
combined.type = ranges.type;
return combined;
}
function mapWithIndex(range, index) {
return {
start: range.start,
end: range.end,
index,
};
}
function mapWithoutIndex(range) {
return {
start: range.start,
end: range.end,
};
}
function sortByRangeIndex(a, b) {
return a.index - b.index;
}
function sortByRangeStart(a, b) {
return a.start - b.start;
}
@@ -0,0 +1,3 @@
import type { webpack } from '@remotion/bundler';
import type { DevMiddlewareContext } from './types';
export declare function ready(context: DevMiddlewareContext, callback: (stats: webpack.Stats | undefined) => undefined | Promise<void>): void;
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ready = ready;
function ready(context, callback) {
if (context.state) {
callback(context.stats);
return;
}
context.callbacks.push(callback);
}
@@ -0,0 +1,3 @@
import type { LogLevel } from '@remotion/renderer';
import type { DevMiddlewareContext } from './types';
export declare function setupHooks(context: DevMiddlewareContext, logLevel: LogLevel): void;
@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupHooks = setupHooks;
const renderer_1 = require("@remotion/renderer");
const no_react_1 = require("remotion/no-react");
function setupHooks(context, logLevel) {
function invalid() {
// We are now in invalid state
context.state = false;
context.stats = undefined;
}
function done(stats) {
context.state = true;
context.stats = stats;
// Do the stuff in nextTick, because bundle may be invalidated if a change happened while compiling
process.nextTick(() => {
const { logger, state, callbacks } = context;
// Check if still in valid state
if (!state || !stats) {
return;
}
logger.log('Compilation finished');
const statsOptions = {
preset: 'errors-warnings',
colors: renderer_1.RenderInternals.isColorSupported(),
};
const printedStats = stats.toString(statsOptions);
const lines = printedStats
.split('\n')
.map((a) => a.trimEnd())
.filter(no_react_1.NoReactInternals.truthy)
.map((a) => {
if (a.startsWith('webpack compiled')) {
return `Built in ${stats.endTime - stats.startTime}ms`;
}
return a;
})
.join('\n');
if (lines) {
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, lines);
if (process.argv.includes('--test-for-server-open')) {
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, 'Yes, the server started.');
process.exit(0);
}
}
context.callbacks = [];
callbacks.forEach((callback) => {
callback(stats);
});
});
}
context.compiler.hooks.watchRun.tap('remotion', invalid);
context.compiler.hooks.invalid.tap('remotion', invalid);
context.compiler.hooks.done.tap('remotion', done);
}
@@ -0,0 +1,2 @@
import type { DevMiddlewareContext } from './types';
export declare function setupOutputFileSystem(context: DevMiddlewareContext): void;
@@ -0,0 +1,13 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupOutputFileSystem = setupOutputFileSystem;
const memfs_1 = __importDefault(require("memfs"));
function setupOutputFileSystem(context) {
const outputFileSystem = memfs_1.default.createFsFromVolume(new memfs_1.default.Volume());
// @ts-expect-error output file sytem
context.compiler.outputFileSystem = outputFileSystem;
context.outputFileSystem = outputFileSystem;
}
@@ -0,0 +1,10 @@
import type { webpack } from '@remotion/bundler';
import type memfs from 'memfs';
export type DevMiddlewareContext = {
state: boolean;
stats: webpack.Stats | undefined;
callbacks: ((stats: webpack.Stats) => undefined | Promise<void>)[];
compiler: webpack.Compiler;
logger: ReturnType<webpack.Compiler['getInfrastructureLogger']>;
outputFileSystem: memfs.IFs | undefined;
};
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
export declare const envSupportsFsRecursive: () => boolean;
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.envSupportsFsRecursive = void 0;
const envSupportsFsRecursive = () => {
const nodeVersion = process.version.replace('v', '').split('.');
if (process.platform === 'darwin' || process.platform === 'win32') {
return true;
}
if (parseInt(nodeVersion[0], 10) > 19) {
return true;
}
if (parseInt(nodeVersion[0], 10) === 19 &&
parseInt(nodeVersion[1], 10) >= 1) {
return true;
}
return false;
};
exports.envSupportsFsRecursive = envSupportsFsRecursive;
@@ -0,0 +1,13 @@
export declare const subscribeToFileExistenceWatchers: ({ file: relativeFile, remotionRoot, clientId, }: {
file: string;
remotionRoot: string;
clientId: string;
}) => {
exists: boolean;
};
export declare const unsubscribeFromFileExistenceWatchers: ({ file, remotionRoot, clientId, }: {
file: string;
remotionRoot: string;
clientId: string;
}) => void;
export declare const unsubscribeClientFileExistenceWatchers: (clientId: string) => void;
@@ -0,0 +1,62 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.unsubscribeClientFileExistenceWatchers = exports.unsubscribeFromFileExistenceWatchers = exports.subscribeToFileExistenceWatchers = void 0;
const node_path_1 = __importDefault(require("node:path"));
const file_watcher_1 = require("../file-watcher");
const live_events_1 = require("./live-events");
const fileExistenceWatchers = {};
const subscribeToFileExistenceWatchers = ({ file: relativeFile, remotionRoot, clientId, }) => {
const file = node_path_1.default.resolve(remotionRoot, relativeFile);
const { unwatch, exists } = (0, file_watcher_1.installFileWatcher)({
file,
onChange: (type) => {
if (type === 'created') {
(0, live_events_1.waitForLiveEventsListener)().then((listener) => {
listener.sendEventToClient({
type: 'watched-file-undeleted',
// Must be relative file because that's what the client expects
file: relativeFile,
});
});
}
if (type === 'deleted') {
(0, live_events_1.waitForLiveEventsListener)().then((listener) => {
listener.sendEventToClient({
type: 'watched-file-deleted',
// Must be relative file because that's what the client expects
file: relativeFile,
});
});
}
},
});
if (!fileExistenceWatchers[clientId]) {
fileExistenceWatchers[clientId] = {};
}
fileExistenceWatchers[clientId][file] = unwatch;
return { exists };
};
exports.subscribeToFileExistenceWatchers = subscribeToFileExistenceWatchers;
const unsubscribeFromFileExistenceWatchers = ({ file, remotionRoot, clientId, }) => {
var _a, _b;
const actualPath = node_path_1.default.resolve(remotionRoot, file);
if (!fileExistenceWatchers[clientId]) {
return;
}
(_b = (_a = fileExistenceWatchers[clientId])[actualPath]) === null || _b === void 0 ? void 0 : _b.call(_a);
delete fileExistenceWatchers[clientId][actualPath];
};
exports.unsubscribeFromFileExistenceWatchers = unsubscribeFromFileExistenceWatchers;
const unsubscribeClientFileExistenceWatchers = (clientId) => {
if (!fileExistenceWatchers[clientId]) {
return;
}
Object.values(fileExistenceWatchers[clientId]).forEach((unwatch) => {
unwatch();
});
delete fileExistenceWatchers[clientId];
};
exports.unsubscribeClientFileExistenceWatchers = unsubscribeClientFileExistenceWatchers;
@@ -0,0 +1,4 @@
export declare const getAbsolutePublicDir: ({ relativePublicDir, remotionRoot, }: {
relativePublicDir: string | null;
remotionRoot: string;
}) => string;
@@ -0,0 +1,13 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAbsolutePublicDir = void 0;
const node_path_1 = __importDefault(require("node:path"));
const getAbsolutePublicDir = ({ relativePublicDir, remotionRoot, }) => {
return relativePublicDir
? node_path_1.default.resolve(remotionRoot, relativePublicDir)
: node_path_1.default.join(remotionRoot, 'public');
};
exports.getAbsolutePublicDir = getAbsolutePublicDir;
@@ -0,0 +1,16 @@
import type { LogLevel } from '@remotion/renderer';
import type { PackageManager } from '@remotion/studio-shared';
type LockfilePath = {
manager: PackageManager;
path: string;
installCommand: string;
startCommand: string;
};
export declare const lockFilePaths: LockfilePath[];
export declare const getPackageManager: ({ remotionRoot, packageManager, dirUp, logLevel, }: {
remotionRoot: string;
packageManager: string | undefined;
dirUp: number;
logLevel: LogLevel;
}) => LockfilePath | "unknown";
export {};
@@ -0,0 +1,76 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPackageManager = exports.lockFilePaths = void 0;
const renderer_1 = require("@remotion/renderer");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
exports.lockFilePaths = [
{
path: 'package-lock.json',
manager: 'npm',
installCommand: 'npm i',
startCommand: 'npx remotion studio',
},
{
path: 'yarn.lock',
manager: 'yarn',
installCommand: 'yarn add',
startCommand: 'yarn remotion studio',
},
{
path: 'pnpm-lock.yaml',
manager: 'pnpm',
installCommand: 'pnpm i',
startCommand: 'pnpm exec remotion studio',
},
{
path: 'bun.lock',
manager: 'bun',
installCommand: 'bun i',
startCommand: 'bunx remotion studio',
},
{
path: 'bun.lockb',
manager: 'bun',
installCommand: 'bun i',
startCommand: 'bunx remotion studio',
},
];
let warnedAboutMultipleLockfiles = false;
const getPackageManager = ({ remotionRoot, packageManager, dirUp, logLevel, }) => {
if (packageManager) {
const manager = exports.lockFilePaths.find((p) => p.manager === packageManager);
if (!manager) {
throw new Error(`The package manager ${packageManager} is not supported. Supported package managers are ${exports.lockFilePaths
.map((p) => p.manager)
.join(', ')}`);
}
return manager;
}
const existingPkgManagers = exports.lockFilePaths.filter((p) => node_fs_1.default.existsSync(node_path_1.default.join(remotionRoot, ...new Array(dirUp).fill('..'), p.path)));
if (existingPkgManagers.length === 0 && dirUp >= 2) {
return 'unknown';
}
if (existingPkgManagers.length === 0) {
return (0, exports.getPackageManager)({
remotionRoot,
packageManager,
dirUp: dirUp + 1,
logLevel,
});
}
if (existingPkgManagers.length > 1 && !warnedAboutMultipleLockfiles) {
warnedAboutMultipleLockfiles = true;
renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, '⚠️ Multiple lockfiles detected:');
for (const pkgManager of existingPkgManagers) {
renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, ` - ${pkgManager.path}`);
}
renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, '');
renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, 'This can lead to bugs, delete all but one of these files.');
}
return existingPkgManagers[0];
};
exports.getPackageManager = getPackageManager;
@@ -0,0 +1,14 @@
import type { LogLevel } from '@remotion/renderer';
import type { IncomingMessage, ServerResponse } from 'node:http';
import type { ApiHandler, QueueMethods } from './api-types';
export declare const handleRequest: <Req, Res>({ remotionRoot, request, response, entryPoint, handler, logLevel, methods, binariesDirectory, publicDir, }: {
remotionRoot: string;
publicDir: string;
request: IncomingMessage;
response: ServerResponse;
entryPoint: string;
binariesDirectory: string | null;
handler: ApiHandler<Req, Res>;
logLevel: LogLevel;
methods: QueueMethods;
}) => Promise<void>;
@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleRequest = void 0;
const parse_body_1 = require("./parse-body");
const handleRequest = async ({ remotionRoot, request, response, entryPoint, handler, logLevel, methods, binariesDirectory, publicDir, }) => {
if (request.method === 'OPTIONS') {
response.statusCode = 200;
response.end();
return;
}
response.setHeader('content-type', 'application/json');
response.writeHead(200);
try {
const body = (await (0, parse_body_1.parseRequestBody)(request));
const outputData = await handler({
entryPoint,
remotionRoot,
request,
response,
input: body,
logLevel,
methods,
binariesDirectory,
publicDir,
});
response.end(JSON.stringify({
success: true,
data: outputData,
}));
}
catch (err) {
response.end(JSON.stringify({
success: false,
error: err.message,
}));
}
};
exports.handleRequest = handleRequest;
@@ -0,0 +1,103 @@
/**
* Source code is adapted from
* https://github.com/webpack-contrib/webpack-hot-middleware#readme
* and rewritten in TypeScript. This file is MIT licensed
*/
import type { webpack } from '@remotion/bundler';
import type { LogLevel } from '@remotion/renderer';
import type { IncomingMessage, ServerResponse } from 'node:http';
declare global {
const __webpack_hash__: unknown;
interface HotNotifierInfo {
type: 'self-declined' | 'declined' | 'unaccepted' | 'accepted' | 'disposed' | 'accept-errored' | 'self-accept-errored' | 'self-accept-error-handler-errored';
/**
* The module in question.
*/
moduleId: number;
/**
* For errors: the module id owning the accept handler.
*/
dependencyId?: number | undefined;
/**
* For declined/accepted/unaccepted: the chain from where the update was propagated.
*/
chain?: number[] | undefined;
/**
* For declined: the module id of the declining parent
*/
parentId?: number | undefined;
/**
* For accepted: the modules that are outdated and will be disposed
*/
outdatedModules?: number[] | undefined;
/**
* For accepted: The location of accept handlers that will handle the update
*/
outdatedDependencies?: {
[dependencyId: number]: number[];
} | undefined;
/**
* For errors: the thrown error
*/
error?: Error | undefined;
/**
* For self-accept-error-handler-errored: the error thrown by the module
* before the error handler tried to handle it.
*/
originalError?: Error | undefined;
}
interface AcceptOptions {
/**
* If true the update process continues even if some modules are not accepted (and would bubble to the entry point).
*/
ignoreUnaccepted?: boolean | undefined;
/**
* Ignore changes made to declined modules.
*/
ignoreDeclined?: boolean | undefined;
/**
* Ignore errors throw in accept handlers, error handlers and while reevaluating module.
*/
ignoreErrored?: boolean | undefined;
/**
* Notifier for declined modules.
*/
onDeclined?: ((info: HotNotifierInfo) => void) | undefined;
/**
* Notifier for unaccepted modules.
*/
onUnaccepted?: ((info: HotNotifierInfo) => void) | undefined;
/**
* Notifier for accepted modules.
*/
onAccepted?: ((info: HotNotifierInfo) => void) | undefined;
/**
* Notifier for disposed modules.
*/
onDisposed?: ((info: HotNotifierInfo) => void) | undefined;
/**
* Notifier for errors.
*/
onErrored?: ((info: HotNotifierInfo) => void) | undefined;
/**
* Indicates that apply() is automatically called by check function
*/
autoApply?: boolean | undefined;
}
const __webpack_module__: {
id: string;
exports: unknown;
hot: {
accept: () => void;
dispose: (onDispose: (data: Record<string, unknown>) => void) => void;
invalidate: () => void;
data?: Record<string, unknown>;
addStatusHandler(callback: (status: string) => void): void;
status(): string;
apply(options?: AcceptOptions): Promise<ModuleId[]>;
check(autoApply?: boolean): Promise<null | ModuleId[]>;
};
};
type ModuleId = string | number;
}
export declare const webpackHotMiddleware: (compiler: webpack.Compiler, logLevel: LogLevel) => (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
@@ -0,0 +1,149 @@
"use strict";
/**
* Source code is adapted from
* https://github.com/webpack-contrib/webpack-hot-middleware#readme
* and rewritten in TypeScript. This file is MIT licensed
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.webpackHotMiddleware = void 0;
const renderer_1 = require("@remotion/renderer");
const studio_shared_1 = require("@remotion/studio-shared");
const node_url_1 = require("node:url");
const pathMatch = function (url, path) {
try {
return (0, node_url_1.parse)(url).pathname === path;
}
catch (_a) {
return false;
}
};
const webpackHotMiddleware = (compiler, logLevel) => {
const eventStream = createEventStream(studio_shared_1.hotMiddlewareOptions.heartbeat);
let latestStats = null;
compiler.hooks.invalid.tap('remotion', onInvalid);
compiler.hooks.done.tap('remotion', onDone);
function onInvalid() {
latestStats = null;
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, 'Building...');
eventStream === null || eventStream === void 0 ? void 0 : eventStream.publish({
action: 'building',
});
}
function onDone(statsResult) {
// Keep hold of latest stats so they can be propagated to new clients
latestStats = statsResult;
publishStats('built', latestStats, eventStream);
}
const middleware = function (req, res, next) {
if (!pathMatch(req.url, studio_shared_1.hotMiddlewareOptions.path))
return next();
eventStream === null || eventStream === void 0 ? void 0 : eventStream.handler(req, res);
if (latestStats) {
publishStats('sync', latestStats, eventStream);
}
};
return middleware;
};
exports.webpackHotMiddleware = webpackHotMiddleware;
function createEventStream(heartbeat) {
let clientId = 0;
let clients = {};
function everyClient(fn) {
Object.keys(clients).forEach((id) => {
fn(clients[id]);
});
}
const interval = setInterval(() => {
everyClient((client) => {
client.write('data: \uD83D\uDC93\n\n');
});
}, heartbeat).unref();
return {
close() {
clearInterval(interval);
everyClient((client) => {
if (!client.finished)
client.end();
});
clients = {};
},
handler(req, res) {
const headers = {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'text/event-stream;charset=utf-8',
'Cache-Control': 'no-cache, no-transform',
};
const isHttp1 = !(parseInt(req.httpVersion, 10) >= 2);
if (isHttp1) {
req.socket.setKeepAlive(true);
Object.assign(headers, {
Connection: 'keep-alive',
});
}
res.writeHead(200, headers);
res.write('\n');
const id = clientId++;
clients[id] = res;
req.on('close', () => {
if (!res.finished)
res.end();
delete clients[id];
});
},
publish(payload) {
everyClient((client) => {
client.write('data: ' + JSON.stringify(payload) + '\n\n');
});
},
};
}
function publishStats(action, statsResult, eventStream) {
const stats = statsResult.toJson({
all: false,
cached: true,
children: true,
modules: true,
timings: true,
hash: true,
});
// For multi-compiler, stats will be an object with a 'children' array of stats
const bundles = extractBundles(stats);
bundles.forEach((_stats) => {
let name = _stats.name || '';
// Fallback to compilation name in case of 1 bundle (if it exists)
if (bundles.length === 1 && !name && statsResult.compilation) {
name = statsResult.compilation.name || '';
}
eventStream === null || eventStream === void 0 ? void 0 : eventStream.publish({
name,
action,
time: _stats.time,
hash: _stats.hash,
warnings: _stats.warnings || [],
errors: _stats.errors || [],
modules: buildModuleMap(_stats.modules),
});
});
}
function extractBundles(stats) {
var _a;
// Stats has modules, single bundle
if (stats.modules)
return [stats];
// Stats has children, multiple bundles
if ((_a = stats.children) === null || _a === void 0 ? void 0 : _a.length)
return stats.children;
// Not sure, assume single
return [stats];
}
function buildModuleMap(modules) {
const map = {};
if (!modules) {
return map;
}
modules.forEach((module) => {
const id = module.id;
map[id] = module.name;
});
return map;
}
@@ -0,0 +1,2 @@
import type { webpack } from '@remotion/bundler';
export type WebpackStats = ReturnType<webpack.Stats['toJson']>;
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
import type { LogLevel } from '@remotion/renderer';
import type { EventSourceEvent } from '@remotion/studio-shared';
import type { IncomingMessage, ServerResponse } from 'node:http';
export type LiveEventsServer = {
sendEventToClient: (event: EventSourceEvent) => void;
router: (request: IncomingMessage, response: ServerResponse) => Promise<void>;
closeConnections: () => Promise<void>;
};
export declare const makeLiveEventsRouter: (logLevel: LogLevel) => LiveEventsServer;
export declare const waitForLiveEventsListener: () => Promise<LiveEventsServer>;
export declare const setLiveEventsListener: (listener: LiveEventsServer) => () => void;
@@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setLiveEventsListener = exports.waitForLiveEventsListener = exports.makeLiveEventsRouter = void 0;
const server_ready_1 = require("../server-ready");
const file_existence_watchers_1 = require("./file-existence-watchers");
const serializeMessage = (message) => {
return `data: ${JSON.stringify(message)}\n\n`;
};
let printPortMessageTimeout = null;
const makeLiveEventsRouter = (logLevel) => {
let clients = [];
const router = (request, response) => {
const headers = {
'content-type': 'text/event-stream;charset=utf-8',
connection: 'keep-alive',
'cache-control': 'no-cache',
};
response.writeHead(200, headers);
response.write('\n');
if (request.method === 'OPTIONS') {
response.end();
return Promise.resolve();
}
const clientId = String(Math.random());
response.write(serializeMessage({ type: 'init', clientId }));
const newClient = {
id: clientId,
response,
};
clients.push(newClient);
if (printPortMessageTimeout) {
clearTimeout(printPortMessageTimeout);
}
request.on('close', () => {
(0, file_existence_watchers_1.unsubscribeClientFileExistenceWatchers)(clientId);
clients = clients.filter((client) => client.id !== clientId);
// If all clients disconnected, print a comment so user can easily restart it.
if (clients.length === 0) {
if (printPortMessageTimeout) {
clearTimeout(printPortMessageTimeout);
}
printPortMessageTimeout = setTimeout(() => {
(0, server_ready_1.printServerReadyComment)('To restart', logLevel);
}, 2500);
}
});
return Promise.resolve();
};
const sendEventToClient = (event) => {
clients.forEach((client) => {
client.response.write(serializeMessage(event));
});
};
return {
sendEventToClient,
router,
closeConnections: () => {
return Promise.all(clients.map((client) => {
return new Promise((resolve) => {
client.response.end(() => {
resolve();
});
});
})).then(() => undefined);
},
};
};
exports.makeLiveEventsRouter = makeLiveEventsRouter;
let liveEventsListener = null;
const waiters = [];
const waitForLiveEventsListener = () => {
if (liveEventsListener) {
return Promise.resolve(liveEventsListener);
}
return new Promise((resolve) => {
waiters.push((list) => {
resolve(list);
});
});
};
exports.waitForLiveEventsListener = waitForLiveEventsListener;
const setLiveEventsListener = (listener) => {
liveEventsListener = listener;
waiters.forEach((w) => w(listener));
return () => {
liveEventsListener = null;
};
};
exports.setLiveEventsListener = setLiveEventsListener;
@@ -0,0 +1,2 @@
import type { IncomingMessage } from 'node:http';
export declare const parseRequestBody: (req: IncomingMessage) => Promise<unknown>;
@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseRequestBody = void 0;
const parseRequestBody = async (req) => {
const body = await new Promise((_resolve) => {
let data = '';
req.on('data', (chunk) => {
data += chunk;
});
req.on('end', () => {
_resolve(data.toString());
});
});
return JSON.parse(body);
};
exports.parseRequestBody = parseRequestBody;
@@ -0,0 +1,2 @@
import type { ProjectInfo } from '@remotion/studio-shared';
export declare const getProjectInfo: (remotionRoot: string, entryPoint: string) => Promise<ProjectInfo>;
@@ -0,0 +1,37 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getProjectInfo = void 0;
const node_fs_1 = require("node:fs");
const node_path_1 = __importDefault(require("node:path"));
const getProjectInfo = (remotionRoot, entryPoint) => {
var _a;
const knownPaths = [
'src/Root.tsx',
'src/Root.jsx',
'remotion/Root.tsx',
'remotion/Root.jsx',
'app/remotion/Root.tsx',
'src/Video.tsx',
'src/Video.jsx',
'src/remotion/Root.tsx',
'src/remotion/Root.jsx',
];
const pathsToLookFor = [
...knownPaths.map((p) => {
return node_path_1.default.join(remotionRoot, p);
}),
node_path_1.default.join(entryPoint, 'Root.tsx'),
node_path_1.default.join(entryPoint, 'Root.jsx'),
node_path_1.default.join(entryPoint, 'remotion/Root.tsx'),
node_path_1.default.join(entryPoint, 'remotion/Root.jsx'),
];
const rootFile = (_a = pathsToLookFor.find((p) => (0, node_fs_1.existsSync)(p))) !== null && _a !== void 0 ? _a : null;
return Promise.resolve({
rootFile,
relativeRootFile: rootFile ? node_path_1.default.relative(remotionRoot, rootFile) : null,
});
};
exports.getProjectInfo = getProjectInfo;
@@ -0,0 +1,12 @@
import type { StaticFile } from 'remotion';
export declare const initPublicFolderWatch: ({ publicDir, onUpdate, staticHash, }: {
publicDir: string;
remotionRoot: string;
onUpdate: () => void;
staticHash: string;
}) => void;
export declare const fetchFolder: ({ publicDir, staticHash, }: {
publicDir: string;
staticHash: string;
}) => void;
export declare const getFiles: () => StaticFile[];
@@ -0,0 +1,58 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFiles = exports.fetchFolder = exports.initPublicFolderWatch = void 0;
const bundler_1 = require("@remotion/bundler");
const node_fs_1 = require("node:fs");
const node_path_1 = __importDefault(require("node:path"));
const env_supports_fs_recursive_1 = require("./env-supports-fs-recursive");
let files = [];
const initPublicFolderWatch = ({ publicDir, onUpdate, staticHash, }) => {
(0, exports.fetchFolder)({ publicDir, staticHash });
watchPublicFolder({ publicDir, onUpdate, staticHash });
};
exports.initPublicFolderWatch = initPublicFolderWatch;
const fetchFolder = ({ publicDir, staticHash, }) => {
files = bundler_1.BundlerInternals.readRecursively({
folder: '.',
startPath: publicDir,
staticHash,
limit: 10000,
}).map((f) => {
return {
...f,
name: f.name.split(node_path_1.default.sep).join('/'),
};
});
};
exports.fetchFolder = fetchFolder;
const watchPublicFolder = ({ publicDir, onUpdate, staticHash, }) => {
if (!(0, node_fs_1.existsSync)(publicDir)) {
const parentDir = node_path_1.default.dirname(publicDir);
const onDirChange = () => {
if ((0, node_fs_1.existsSync)(publicDir)) {
watchPublicFolder({
publicDir,
onUpdate,
staticHash,
});
onUpdate();
watcher.close();
}
};
const watcher = (0, node_fs_1.watch)(parentDir, {}, onDirChange);
return;
}
// Known bug: If whole public folder is deleted, this will not be called on macOS.
// This is not severe, so a wontfix for now.
(0, node_fs_1.watch)(publicDir, { recursive: (0, env_supports_fs_recursive_1.envSupportsFsRecursive)() }, () => {
(0, exports.fetchFolder)({ publicDir, staticHash });
onUpdate();
});
};
const getFiles = () => {
return files;
};
exports.getFiles = getFiles;
@@ -0,0 +1,3 @@
import type { AddRenderRequest } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const handleAddRender: ApiHandler<AddRenderRequest, undefined>;
@@ -0,0 +1,140 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleAddRender = void 0;
const renderer_1 = require("@remotion/renderer");
const handleAddRender = ({ input, entryPoint, remotionRoot, logLevel, binariesDirectory, methods: { addJob }, }) => {
const id = String(Math.random()).replace('0.', '');
if (input.type === 'video') {
addJob({
entryPoint,
remotionRoot,
job: {
cleanup: [],
codec: input.codec,
audioCodec: input.audioCodec,
compositionId: input.compositionId,
deletedOutputLocation: false,
type: 'video',
status: 'idle',
id,
imageFormat: input.imageFormat,
outName: input.outName,
jpegQuality: input.jpegQuality,
scale: input.scale,
startedAt: Date.now(),
logLevel: input.logLevel,
cancelToken: (0, renderer_1.makeCancelSignal)(),
concurrency: input.concurrency,
crf: input.crf,
endFrame: input.endFrame,
startFrame: input.startFrame,
muted: input.muted,
enforceAudioTrack: input.enforceAudioTrack,
proResProfile: input.proResProfile,
x264Preset: input.x264Preset,
pixelFormat: input.pixelFormat,
audioBitrate: input.audioBitrate,
videoBitrate: input.videoBitrate,
encodingMaxRate: input.encodingMaxRate,
encodingBufferSize: input.encodingBufferSize,
everyNthFrame: input.everyNthFrame,
numberOfGifLoops: input.numberOfGifLoops,
delayRenderTimeout: input.delayRenderTimeout,
disallowParallelEncoding: input.disallowParallelEncoding,
chromiumOptions: input.chromiumOptions,
envVariables: input.envVariables,
serializedInputPropsWithCustomSchema: input.serializedInputPropsWithCustomSchema,
offthreadVideoCacheSizeInBytes: input.offthreadVideoCacheSizeInBytes,
colorSpace: input.colorSpace,
multiProcessOnLinux: input.multiProcessOnLinux,
beepOnFinish: input.beepOnFinish,
repro: input.repro,
binariesDirectory,
forSeamlessAacConcatenation: input.forSeamlessAacConcatenation,
separateAudioTo: input.separateAudioTo,
metadata: input.metadata,
hardwareAcceleration: input.hardwareAcceleration,
chromeMode: input.chromeMode,
offthreadVideoThreads: input.offthreadVideoThreads,
mediaCacheSizeInBytes: input.mediaCacheSizeInBytes,
},
logLevel,
});
}
if (input.type === 'sequence') {
addJob({
entryPoint,
remotionRoot,
job: {
cleanup: [],
compositionId: input.compositionId,
deletedOutputLocation: false,
type: 'sequence',
status: 'idle',
id,
imageFormat: input.imageFormat,
outName: input.outName,
jpegQuality: input.jpegQuality,
scale: input.scale,
startedAt: Date.now(),
logLevel: input.logLevel,
cancelToken: (0, renderer_1.makeCancelSignal)(),
concurrency: input.concurrency,
endFrame: input.endFrame,
startFrame: input.startFrame,
delayRenderTimeout: input.delayRenderTimeout,
chromiumOptions: input.chromiumOptions,
envVariables: input.envVariables,
serializedInputPropsWithCustomSchema: input.serializedInputPropsWithCustomSchema,
offthreadVideoCacheSizeInBytes: input.offthreadVideoCacheSizeInBytes,
multiProcessOnLinux: input.multiProcessOnLinux,
beepOnFinish: input.beepOnFinish,
repro: input.repro,
binariesDirectory,
metadata: input.metadata,
chromeMode: input.chromeMode,
offthreadVideoThreads: input.offthreadVideoThreads,
mediaCacheSizeInBytes: input.mediaCacheSizeInBytes,
},
logLevel,
});
}
if (input.type === 'still') {
addJob({
job: {
compositionId: input.compositionId,
id: String(Math.random()).replace('0.', ''),
startedAt: Date.now(),
type: 'still',
outName: input.outName,
status: 'idle',
imageFormat: input.imageFormat,
jpegQuality: input.jpegQuality,
frame: input.frame,
scale: input.scale,
cleanup: [],
deletedOutputLocation: false,
logLevel: input.logLevel,
cancelToken: (0, renderer_1.makeCancelSignal)(),
chromiumOptions: input.chromiumOptions,
delayRenderTimeout: input.delayRenderTimeout,
envVariables: input.envVariables,
serializedInputPropsWithCustomSchema: input.serializedInputPropsWithCustomSchema,
offthreadVideoCacheSizeInBytes: input.offthreadVideoCacheSizeInBytes,
multiProcessOnLinux: input.multiProcessOnLinux,
beepOnFinish: input.beepOnFinish,
repro: false,
binariesDirectory,
metadata: input.metadata,
chromeMode: input.chromeMode,
offthreadVideoThreads: input.offthreadVideoThreads,
mediaCacheSizeInBytes: input.mediaCacheSizeInBytes,
},
entryPoint,
remotionRoot,
logLevel,
});
}
return Promise.resolve(undefined);
};
exports.handleAddRender = handleAddRender;
@@ -0,0 +1,3 @@
import type { ApplyCodemodRequest, ApplyCodemodResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const applyCodemodHandler: ApiHandler<ApplyCodemodRequest, ApplyCodemodResponse>;
@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyCodemodHandler = void 0;
const renderer_1 = require("@remotion/renderer");
const node_fs_1 = require("node:fs");
const duplicate_composition_1 = require("../../codemods/duplicate-composition");
const simple_diff_1 = require("../../codemods/simple-diff");
const project_info_1 = require("../project-info");
const can_update_default_props_1 = require("./can-update-default-props");
const applyCodemodHandler = async ({ input: { codemod, dryRun }, logLevel, remotionRoot, entryPoint }) => {
try {
const time = Date.now();
const projectInfo = await (0, project_info_1.getProjectInfo)(remotionRoot, entryPoint);
if (!projectInfo.rootFile) {
throw new Error('Cannot find root file in project');
}
(0, can_update_default_props_1.checkIfTypeScriptFile)(projectInfo.rootFile);
const input = (0, node_fs_1.readFileSync)(projectInfo.rootFile, 'utf-8');
const { newContents } = (0, duplicate_composition_1.parseAndApplyCodemod)({
codeMod: codemod,
input,
});
const formatted = await (0, duplicate_composition_1.formatOutput)(newContents);
const diff = (0, simple_diff_1.simpleDiff)({
oldLines: input.split('\n'),
newLines: formatted.split('\n'),
});
if (!dryRun) {
(0, node_fs_1.writeFileSync)(projectInfo.rootFile, formatted);
const end = Date.now() - time;
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.blue(`Edited root file in ${end}ms`));
}
return {
success: true,
diff,
};
}
catch (err) {
return {
success: false,
reason: err.message,
stack: err.stack,
};
}
};
exports.applyCodemodHandler = applyCodemodHandler;
@@ -0,0 +1,3 @@
import type { ApplyVisualControlRequest, ApplyVisualControlResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const applyVisualControlHandler: ApiHandler<ApplyVisualControlRequest, ApplyVisualControlResponse>;

Some files were not shown because too many files have changed in this diff Show More