init commit

This commit is contained in:
Carlos
2026-02-21 10:33:18 +01:00
parent c863a943ed
commit 9d955bf338
9512 changed files with 2015317 additions and 1305 deletions

View File

@@ -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.

View File

@@ -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 {};

View File

@@ -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');
}

View File

@@ -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>;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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[];
};

View File

@@ -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;

View File

@@ -0,0 +1,3 @@
import type { File } from '@babel/types';
export declare const parseAst: (input: string) => File;
export declare const serializeAst: (ast: File) => string;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,
},
};
};

View File

@@ -0,0 +1,5 @@
import type { SimpleDiff } from '@remotion/studio-shared';
export declare const simpleDiff: ({ oldLines, newLines, }: {
oldLines: string[];
newLines: string[];
}) => SimpleDiff;

View File

@@ -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;

View File

@@ -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>>>>;

View File

@@ -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;

View File

@@ -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 {};

View File

@@ -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;

View File

@@ -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 {};

View File

@@ -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;

View File

@@ -0,0 +1 @@
export declare const getLatestRemotionVersion: () => Promise<any>;

View File

@@ -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;

View File

@@ -0,0 +1 @@
export declare const getNetworkAddress: () => string | undefined;

View File

@@ -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;

View File

@@ -0,0 +1 @@
export declare const getFileSource: (remotionRoot: string, p: string) => Promise<string>;

View File

@@ -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;

View File

@@ -0,0 +1,6 @@
export declare const getInstalledDependencies: (remotionRoot: string) => {
dependencies: string[];
devDependencies: string[];
optionalDependencies: string[];
peerDependencies: string[];
};

View File

@@ -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;

View File

@@ -0,0 +1 @@
export declare const getInstalledInstallablePackages: (remotionRoot: string) => string[];

View File

@@ -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;

View File

@@ -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[];

View File

@@ -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;

View File

@@ -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

View File

@@ -0,0 +1,86 @@
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: string, packageManager: string | undefined, dirUp: number) => {
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[];
};

View File

@@ -0,0 +1,33 @@
"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 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,
};

View File

@@ -0,0 +1,2 @@
export declare const setMaxTimelineTracks: (maxTracks: number) => void;
export declare const getMaxTimelineTracks: () => number;

View File

@@ -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;

View File

@@ -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;
}>;

View File

@@ -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;

View File

@@ -0,0 +1 @@
export declare const openDirectoryInFinder: (dirToOpen: string, allowedDirectory: string) => Promise<void>;

View File

@@ -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;

View File

@@ -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']>;
};

View File

@@ -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,
};

View File

@@ -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>;

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,2 @@
export declare const noOpUntilRestart: () => Promise<void>;
export declare const signalRestart: () => void;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -0,0 +1,7 @@
import type { DevMiddlewareContext } from './types';
type PublicPath = {
outputPath: string;
publicPath: string;
};
export declare function getPaths(context: DevMiddlewareContext): PublicPath[];
export {};

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
}
};
}

View File

@@ -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 {};

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -0,0 +1,3 @@
import type { LogLevel } from '@remotion/renderer';
import type { DevMiddlewareContext } from './types';
export declare function setupHooks(context: DevMiddlewareContext, logLevel: LogLevel): void;

View File

@@ -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);
}

View File

@@ -0,0 +1,2 @@
import type { DevMiddlewareContext } from './types';
export declare function setupOutputFileSystem(context: DevMiddlewareContext): void;

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1 @@
export declare const envSupportsFsRecursive: () => boolean;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,4 @@
export declare const getAbsolutePublicDir: ({ relativePublicDir, remotionRoot, }: {
relativePublicDir: string | null;
remotionRoot: string;
}) => string;

View File

@@ -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;

View File

@@ -0,0 +1,10 @@
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: string, packageManager: string | undefined, dirUp: number) => LockfilePath | "unknown";
export {};

View File

@@ -0,0 +1,71 @@
"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 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',
},
];
const getPackageManager = (remotionRoot, packageManager, dirUp) => {
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 + 1);
}
if (existingPkgManagers.length > 1) {
const error = [
`Found multiple lockfiles:`,
...existingPkgManagers.map((m) => {
return `- ${m.path}`;
}),
'',
'This can lead to bugs, delete all but one of these files and run this command again.',
].join('\n');
throw new Error(error);
}
return existingPkgManagers[0];
};
exports.getPackageManager = getPackageManager;

View File

@@ -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>;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -0,0 +1,2 @@
import type { webpack } from '@remotion/bundler';
export type WebpackStats = ReturnType<webpack.Stats['toJson']>;

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,2 @@
import type { IncomingMessage } from 'node:http';
export declare const parseRequestBody: (req: IncomingMessage) => Promise<unknown>;

View File

@@ -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;

View File

@@ -0,0 +1,2 @@
import type { ProjectInfo } from '@remotion/studio-shared';
export declare const getProjectInfo: (remotionRoot: string, entryPoint: string) => Promise<ProjectInfo>;

View File

@@ -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;

View File

@@ -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[];

View File

@@ -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;

View File

@@ -0,0 +1,3 @@
import type { AddRenderRequest } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const handleAddRender: ApiHandler<AddRenderRequest, undefined>;

View File

@@ -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;

View File

@@ -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>;

View File

@@ -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;

View File

@@ -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>;

View File

@@ -0,0 +1,35 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyVisualControlHandler = void 0;
const node_fs_1 = require("node:fs");
const node_path_1 = __importDefault(require("node:path"));
const parse_ast_1 = require("../../codemods/parse-ast");
const recast_mods_1 = require("../../codemods/recast-mods");
const applyVisualControlHandler = ({ input: { fileName, changes }, remotionRoot }) => {
const absolutePath = node_path_1.default.resolve(remotionRoot, fileName);
const fileRelativeToRoot = node_path_1.default.relative(remotionRoot, absolutePath);
if (fileRelativeToRoot.startsWith('..')) {
throw new Error('Cannot apply visual control change to a file outside the project');
}
const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
const ast = (0, parse_ast_1.parseAst)(fileContents);
const { newAst, changesMade } = (0, recast_mods_1.applyCodemod)({
file: ast,
codeMod: {
type: 'apply-visual-control',
changes,
},
});
if (changesMade.length === 0) {
throw new Error('No changes were made to the file');
}
const output = (0, parse_ast_1.serializeAst)(newAst);
(0, node_fs_1.writeFileSync)(absolutePath, output);
return Promise.resolve({
success: true,
});
};
exports.applyVisualControlHandler = applyVisualControlHandler;

View File

@@ -0,0 +1,4 @@
import type { CanUpdateDefaultPropsRequest, CanUpdateDefaultPropsResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const checkIfTypeScriptFile: (file: string) => void;
export declare const canUpdateDefaultPropsHandler: ApiHandler<CanUpdateDefaultPropsRequest, CanUpdateDefaultPropsResponse>;

View File

@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.canUpdateDefaultPropsHandler = exports.checkIfTypeScriptFile = void 0;
const node_fs_1 = require("node:fs");
const update_default_props_1 = require("../../codemods/update-default-props");
const project_info_1 = require("../project-info");
const checkIfTypeScriptFile = (file) => {
if (!file.endsWith('.tsx') &&
!file.endsWith('.ts') &&
!file.endsWith('.mtsx') &&
!file.endsWith('.mts')) {
throw new Error('Cannot update Root file if not using TypeScript');
}
};
exports.checkIfTypeScriptFile = checkIfTypeScriptFile;
const canUpdateDefaultPropsHandler = async ({ input: { compositionId }, remotionRoot, entryPoint }) => {
try {
const projectInfo = await (0, project_info_1.getProjectInfo)(remotionRoot, entryPoint);
if (!projectInfo.rootFile) {
throw new Error('Cannot find root file in project');
}
(0, exports.checkIfTypeScriptFile)(projectInfo.rootFile);
await (0, update_default_props_1.updateDefaultProps)({
compositionId,
input: (0, node_fs_1.readFileSync)(projectInfo.rootFile, 'utf-8'),
newDefaultProps: {},
enumPaths: [],
});
return {
canUpdate: true,
};
}
catch (err) {
return {
canUpdate: false,
reason: err.message,
};
}
};
exports.canUpdateDefaultPropsHandler = canUpdateDefaultPropsHandler;

View File

@@ -0,0 +1,3 @@
import type { CancelRenderRequest, CancelRenderResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const handleCancelRender: ApiHandler<CancelRenderRequest, CancelRenderResponse>;

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleCancelRender = void 0;
const handleCancelRender = ({ input: { jobId }, methods: { cancelJob } }) => {
cancelJob(jobId);
return Promise.resolve({});
};
exports.handleCancelRender = handleCancelRender;

View File

@@ -0,0 +1,3 @@
import type { DeleteStaticFileRequest, DeleteStaticFileResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const deleteStaticFileHandler: ApiHandler<DeleteStaticFileRequest, DeleteStaticFileResponse>;

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