Add .gitignore to exclude all node packages and lock files

This commit is contained in:
Adolfo Reyna
2026-02-23 21:56:04 -05:00
parent faae96c9ed
commit dcc5c6c044
9747 changed files with 1555105 additions and 2 deletions
@@ -0,0 +1,37 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bun_test_1 = require("bun:test");
const node_fs_1 = __importDefault(require("node:fs"));
const node_os_1 = __importDefault(require("node:os"));
const node_path_1 = __importDefault(require("node:path"));
const get_package_manager_1 = require("../get-package-manager");
(0, bun_test_1.describe)('getPackageManager multiple lockfiles', () => {
let tempDir;
(0, bun_test_1.beforeEach)(() => {
tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'remotion-test-'));
});
(0, bun_test_1.afterEach)(() => {
node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
});
(0, bun_test_1.test)('should not throw error when multiple lockfiles are detected', () => {
node_fs_1.default.writeFileSync(node_path_1.default.join(tempDir, 'package-lock.json'), '{}');
node_fs_1.default.writeFileSync(node_path_1.default.join(tempDir, 'bun.lock'), '');
// Should not throw
const manager = (0, get_package_manager_1.getPackageManager)(tempDir, undefined, 0);
// Should return one of them (usually the first one in the list, which is npm)
(0, bun_test_1.expect)(manager).not.toBe('unknown');
if (typeof manager !== 'string') {
(0, bun_test_1.expect)(['npm', 'bun']).toContain(manager.manager);
}
});
(0, bun_test_1.test)('should return npm if only package-lock.json exists', () => {
node_fs_1.default.writeFileSync(node_path_1.default.join(tempDir, 'package-lock.json'), '{}');
const manager = (0, get_package_manager_1.getPackageManager)(tempDir, undefined, 0);
if (typeof manager !== 'string') {
(0, bun_test_1.expect)(manager.manager).toBe('npm');
}
});
});
@@ -0,0 +1,5 @@
import type { ApiRoutes } from '@remotion/studio-shared';
import type { ApiHandler } from './api-types';
export declare const allApiRoutes: {
[key in keyof ApiRoutes]: ApiHandler<ApiRoutes[key]['Request'], ApiRoutes[key]['Response']>;
};
@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.allApiRoutes = void 0;
const add_render_1 = require("./routes/add-render");
const apply_codemod_1 = require("./routes/apply-codemod");
const apply_visual_control_change_1 = require("./routes/apply-visual-control-change");
const can_update_default_props_1 = require("./routes/can-update-default-props");
const cancel_render_1 = require("./routes/cancel-render");
const delete_static_file_1 = require("./routes/delete-static-file");
const install_dependency_1 = require("./routes/install-dependency");
const open_in_file_explorer_1 = require("./routes/open-in-file-explorer");
const project_info_1 = require("./routes/project-info");
const remove_render_1 = require("./routes/remove-render");
const restart_studio_1 = require("./routes/restart-studio");
const subscribe_to_file_existence_1 = require("./routes/subscribe-to-file-existence");
const unsubscribe_from_file_existence_1 = require("./routes/unsubscribe-from-file-existence");
const update_available_1 = require("./routes/update-available");
const update_default_props_1 = require("./routes/update-default-props");
exports.allApiRoutes = {
'/api/cancel': cancel_render_1.handleCancelRender,
'/api/render': add_render_1.handleAddRender,
'/api/unsubscribe-from-file-existence': unsubscribe_from_file_existence_1.unsubscribeFromFileExistence,
'/api/subscribe-to-file-existence': subscribe_to_file_existence_1.subscribeToFileExistence,
'/api/remove-render': remove_render_1.handleRemoveRender,
'/api/open-in-file-explorer': open_in_file_explorer_1.handleOpenInFileExplorer,
'/api/update-default-props': update_default_props_1.updateDefaultPropsHandler,
'/api/apply-visual-control-change': apply_visual_control_change_1.applyVisualControlHandler,
'/api/apply-codemod': apply_codemod_1.applyCodemodHandler,
'/api/can-update-default-props': can_update_default_props_1.canUpdateDefaultPropsHandler,
'/api/update-available': update_available_1.handleUpdate,
'/api/project-info': project_info_1.projectInfoHandler,
'/api/delete-static-file': delete_static_file_1.deleteStaticFileHandler,
'/api/restart-studio': restart_studio_1.handleRestartStudio,
'/api/install-package': install_dependency_1.handleInstallPackage,
};
@@ -0,0 +1,24 @@
import type { LogLevel } from '@remotion/renderer';
import type { RenderJobWithCleanup } from '@remotion/studio-shared';
import type { IncomingMessage, ServerResponse } from 'node:http';
export type QueueMethods = {
removeJob: (jobId: string) => void;
cancelJob: (jobId: string) => void;
addJob: ({ job, entryPoint, remotionRoot, logLevel, }: {
job: RenderJobWithCleanup;
entryPoint: string;
remotionRoot: string;
logLevel: LogLevel;
}) => void;
};
export type ApiHandler<ReqData, ResData> = (params: {
input: ReqData;
entryPoint: string;
remotionRoot: string;
request: IncomingMessage;
response: ServerResponse;
logLevel: LogLevel;
methods: QueueMethods;
publicDir: string;
binariesDirectory: string | null;
}) => Promise<ResData>;
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
export declare const noOpUntilRestart: () => Promise<void>;
export declare const signalRestart: () => void;
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.signalRestart = exports.noOpUntilRestart = void 0;
const resolveFunctions = [];
const noOpUntilRestart = () => {
return new Promise((resolve) => {
resolveFunctions.push(resolve);
});
};
exports.noOpUntilRestart = noOpUntilRestart;
const signalRestart = () => {
resolveFunctions.forEach((f) => {
f();
});
resolveFunctions.length = 0;
};
exports.signalRestart = signalRestart;
@@ -0,0 +1,4 @@
import type { ReadStream } from 'node:fs';
import type { IncomingMessage, ServerResponse } from 'node:http';
export declare function setHeaderForResponse(res: ServerResponse, name: string, value: string | number): void;
export declare function send(req: IncomingMessage, res: ServerResponse, bufferOtStream: ReadStream | string | Buffer, byteLength: number): void;
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setHeaderForResponse = setHeaderForResponse;
exports.send = send;
function setHeaderForResponse(res, name, value) {
res.setHeader(name, typeof value === 'number' ? String(value) : value);
}
function send(req, res, bufferOtStream, byteLength) {
if (typeof bufferOtStream === 'string' || Buffer.isBuffer(bufferOtStream)) {
res.end(bufferOtStream);
return;
}
setHeaderForResponse(res, 'Content-Length', byteLength);
if (req.method === 'HEAD') {
res.end();
return;
}
bufferOtStream.pipe(res);
}
@@ -0,0 +1,7 @@
import type { DevMiddlewareContext } from './types';
type PublicPath = {
outputPath: string;
publicPath: string;
};
export declare function getPaths(context: DevMiddlewareContext): PublicPath[];
export {};
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPaths = getPaths;
function getPaths(context) {
const { stats } = context;
const publicPaths = [];
if (!stats) {
return publicPaths;
}
const { compilation } = stats;
// The `output.path` is always present and always absolute
const outputPath = compilation.getPath(compilation.outputOptions.path || '');
const publicPath = compilation.outputOptions.publicPath
? compilation.getPath(compilation.outputOptions.publicPath)
: '';
publicPaths.push({ outputPath, publicPath });
return publicPaths;
}
@@ -0,0 +1,4 @@
import type { webpack } from '@remotion/bundler';
import type { LogLevel } from '@remotion/renderer';
import type { MiddleWare } from './middleware';
export declare const wdm: (compiler: webpack.Compiler, logLevel: LogLevel) => MiddleWare;
@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.wdm = void 0;
const middleware_1 = require("./middleware");
const setup_hooks_1 = require("./setup-hooks");
const setup_output_filesystem_1 = require("./setup-output-filesystem");
const wdm = (compiler, logLevel) => {
const context = {
state: false,
stats: undefined,
callbacks: [],
compiler,
logger: compiler.getInfrastructureLogger('remotion'),
outputFileSystem: undefined,
};
(0, setup_hooks_1.setupHooks)(context, logLevel);
(0, setup_output_filesystem_1.setupOutputFileSystem)(context);
const errorHandler = (error) => {
if (error) {
context.logger.error(error);
}
};
const watchOptions = context.compiler.options.watchOptions || {};
context.compiler.watch(watchOptions, errorHandler);
return (0, middleware_1.middleware)(context);
};
exports.wdm = wdm;
@@ -0,0 +1,8 @@
import type { IncomingMessage, ServerResponse } from 'node:http';
import type { DevMiddlewareContext } from './types';
export declare function getValueContentRangeHeader(type: string, size: number, range?: {
start: number;
end: number;
}): string;
export type MiddleWare = (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
export declare function middleware(context: DevMiddlewareContext): (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
@@ -0,0 +1,219 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getValueContentRangeHeader = getValueContentRangeHeader;
exports.middleware = middleware;
const renderer_1 = require("@remotion/renderer");
const node_path_1 = __importDefault(require("node:path"));
const node_querystring_1 = __importDefault(require("node:querystring"));
const node_url_1 = require("node:url");
const compatible_api_1 = require("./compatible-api");
const get_paths_1 = require("./get-paths");
const range_parser_1 = require("./range-parser");
const ready_1 = require("./ready");
const cacheStore = new WeakMap();
const mem = (fn, { cache = new Map() } = {}) => {
const memoized = (...arguments_) => {
const [key] = arguments_;
const cacheItem = cache.get(key);
if (cacheItem) {
return cacheItem.data;
}
const result = fn.apply(this, arguments_);
cache.set(key, {
data: result,
});
return result;
};
cacheStore.set(memoized, cache);
return memoized;
};
const memoizedParse = mem(node_url_1.parse);
function getFilenameFromUrl(context, url) {
var _a, _b;
const paths = (0, get_paths_1.getPaths)(context);
let foundFilename;
let urlObject;
try {
// The `url` property of the `request` is contains only `pathname`, `search` and `hash`
urlObject = memoizedParse(url, false, true);
}
catch (_c) {
return;
}
for (const { publicPath, outputPath } of paths) {
let filename;
let publicPathObject;
try {
publicPathObject = memoizedParse(publicPath !== 'auto' && publicPath ? publicPath : '/', false, true);
}
catch (_d) {
continue;
}
if ((_a = urlObject.pathname) === null || _a === void 0 ? void 0 : _a.startsWith(publicPathObject.pathname)) {
filename = outputPath;
// Strip the `pathname` property from the `publicPath` option from the start of requested url
// `/complex/foo.js` => `foo.js`
const pathname = urlObject.pathname.substr(publicPathObject.pathname.length);
if (pathname) {
filename = node_path_1.default.join(outputPath, node_querystring_1.default.unescape(pathname));
}
if (!context.outputFileSystem) {
continue;
}
try {
let fsStats = (_b = context.outputFileSystem) === null || _b === void 0 ? void 0 : _b.statSync(filename);
if (fsStats.isFile()) {
foundFilename = filename;
break;
}
else if (fsStats.isDirectory()) {
const indexValue = 'index.html';
filename = node_path_1.default.join(filename, indexValue);
try {
fsStats = context.outputFileSystem.statSync(filename);
}
catch (_e) {
continue;
}
if (fsStats.isFile()) {
foundFilename = filename;
break;
}
}
}
catch (_f) {
continue;
}
}
}
return foundFilename;
}
function getValueContentRangeHeader(type, size, range) {
return `${type} ${range ? `${range.start}-${range.end}` : '*'}/${size}`;
}
function createHtmlDocument(title, body) {
return (`${'<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'<head>\n' +
'<meta charset="utf-8">\n' +
'<title>'}${title}</title>\n` +
`</head>\n` +
`<body>\n` +
`<pre>${body}</pre>\n` +
`</body>\n` +
`</html>\n`);
}
const BYTES_RANGE_REGEXP = /^ *bytes/i;
function middleware(context) {
return function (req, res, next) {
const acceptedMethods = ['GET', 'HEAD'];
if (req.method && !acceptedMethods.includes(req.method)) {
goNext();
return;
}
(0, ready_1.ready)(context, processRequest);
function goNext() {
return next();
}
async function processRequest() {
var _a;
const filename = getFilenameFromUrl(context, req.url);
if (!filename) {
goNext();
return;
}
/**
* @type {{key: string, value: string | number}[]}
*/
if (!res.getHeader('Content-Type')) {
// content-type name(like application/javascript; charset=utf-8) or false
const contentType = renderer_1.RenderInternals.mimeContentType(node_path_1.default.extname(filename));
// Only set content-type header if media type is known
// https://tools.ietf.org/html/rfc7231#section-3.1.1.5
if (contentType) {
(0, compatible_api_1.setHeaderForResponse)(res, 'Content-Type', contentType);
}
}
if (!res.getHeader('Accept-Ranges')) {
res.setHeader('Accept-Ranges', 'bytes');
(0, compatible_api_1.setHeaderForResponse)(res, 'Accept-Ranges', 'bytes');
}
const rangeHeader = req.headers.range;
let start;
let end;
if (rangeHeader &&
BYTES_RANGE_REGEXP.test(rangeHeader) &&
context.outputFileSystem) {
const size = await new Promise((resolve) => {
var _a;
(_a = context.outputFileSystem) === null || _a === void 0 ? void 0 : _a.lstat(filename, (error, stats) => {
var _a;
if (error) {
context.logger.error(error);
return;
}
resolve((_a = stats === null || stats === void 0 ? void 0 : stats.size) !== null && _a !== void 0 ? _a : 0);
});
});
const parsedRanges = (0, range_parser_1.parseRange)(size, rangeHeader);
if (parsedRanges === -1) {
const message = "Unsatisfiable range for 'Range' header.";
context.logger.error(message);
const existingHeaders = res.getHeaderNames();
for (const header of existingHeaders) {
res.removeHeader(header);
}
res.statusCode = 416;
(0, compatible_api_1.setHeaderForResponse)(res, 'Content-Range', getValueContentRangeHeader('bytes', size));
(0, compatible_api_1.setHeaderForResponse)(res, 'Content-Type', 'text/html; charset=utf-8');
const document = createHtmlDocument(416, `Error: ${message}`);
const _byteLength = Buffer.byteLength(document);
(0, compatible_api_1.setHeaderForResponse)(res, 'Content-Length', Buffer.byteLength(document));
(0, compatible_api_1.send)(req, res, document, _byteLength);
return;
}
if (parsedRanges === -2) {
context.logger.error("A malformed 'Range' header was provided. A regular response will be sent for this request.");
}
else if (parsedRanges.length > 1) {
context.logger.error("A 'Range' header with multiple ranges was provided. Multiple ranges are not supported, so a regular response will be sent for this request.");
}
if (parsedRanges !== -2 && parsedRanges.length === 1) {
// Content-Range
res.statusCode = 206;
(0, compatible_api_1.setHeaderForResponse)(res, 'Content-Range', getValueContentRangeHeader('bytes', size, parsedRanges[0]));
[{ start, end }] = parsedRanges;
}
}
const isFsSupportsStream = typeof ((_a = context.outputFileSystem) === null || _a === void 0 ? void 0 : _a.createReadStream) === 'function';
let bufferOtStream;
let byteLength = 0;
try {
if (typeof start !== 'undefined' &&
typeof end !== 'undefined' &&
isFsSupportsStream &&
context.outputFileSystem) {
bufferOtStream = context.outputFileSystem.createReadStream(filename, {
start,
end,
});
byteLength = end - start + 1;
}
else if (context.outputFileSystem) {
bufferOtStream = context.outputFileSystem.readFileSync(filename);
byteLength = bufferOtStream.byteLength;
}
}
catch (_b) {
goNext();
return;
}
if (bufferOtStream) {
(0, compatible_api_1.send)(req, res, bufferOtStream, byteLength);
}
}
};
}
@@ -0,0 +1,15 @@
/*!
* range-parser
* Copyright(c) 2012-2014 TJ Holowaychuk
* Copyright(c) 2015-2016 Douglas Christopher Wilson
* MIT Licensed
*/
type Range = {
start: number;
end: number;
};
type Ranges = Range[] & {
type?: string;
};
export declare function parseRange(size: number, str: string | string[]): -1 | Ranges | -2;
export {};
@@ -0,0 +1,95 @@
"use strict";
/*!
* range-parser
* Copyright(c) 2012-2014 TJ Holowaychuk
* Copyright(c) 2015-2016 Douglas Christopher Wilson
* MIT Licensed
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseRange = parseRange;
function parseRange(size, str) {
if (typeof str !== 'string') {
throw new TypeError('argument str must be a string');
}
const index = str.indexOf('=');
if (index === -1) {
return -2;
}
// split the range string
const arr = str.slice(index + 1).split(',');
const ranges = [];
// add ranges type
ranges.type = str.slice(0, index);
// parse all ranges
for (let i = 0; i < arr.length; i++) {
const range = arr[i].split('-');
let start = parseInt(range[0], 10);
let end = parseInt(range[1], 10);
// -nnn
if (isNaN(start)) {
start = size - end;
end = size - 1;
// nnn-
}
else if (isNaN(end)) {
end = size - 1;
}
// limit last-byte-pos to current length
if (end > size - 1) {
end = size - 1;
}
// invalid or unsatisifiable
if (isNaN(start) || isNaN(end) || start > end || start < 0) {
continue;
}
// add range
ranges.push({
start,
end,
});
}
if (ranges.length < 1) {
return -1;
}
return combineRanges(ranges);
}
function combineRanges(ranges) {
const ordered = ranges.map(mapWithIndex).sort(sortByRangeStart);
let j = 0;
for (let i = 1; i < ordered.length; i++) {
const range = ordered[i];
const current = ordered[j];
if (range.start > current.end + 1) {
// next range
ordered[++j] = range;
}
else if (range.end > current.end) {
// extend range
current.end = range.end;
current.index = Math.min(current.index, range.index);
}
}
ordered.length = j + 1;
const combined = ordered.sort(sortByRangeIndex).map(mapWithoutIndex);
combined.type = ranges.type;
return combined;
}
function mapWithIndex(range, index) {
return {
start: range.start,
end: range.end,
index,
};
}
function mapWithoutIndex(range) {
return {
start: range.start,
end: range.end,
};
}
function sortByRangeIndex(a, b) {
return a.index - b.index;
}
function sortByRangeStart(a, b) {
return a.start - b.start;
}
@@ -0,0 +1,3 @@
import type { webpack } from '@remotion/bundler';
import type { DevMiddlewareContext } from './types';
export declare function ready(context: DevMiddlewareContext, callback: (stats: webpack.Stats | undefined) => undefined | Promise<void>): void;
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ready = ready;
function ready(context, callback) {
if (context.state) {
callback(context.stats);
return;
}
context.callbacks.push(callback);
}
@@ -0,0 +1,3 @@
import type { LogLevel } from '@remotion/renderer';
import type { DevMiddlewareContext } from './types';
export declare function setupHooks(context: DevMiddlewareContext, logLevel: LogLevel): void;
@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupHooks = setupHooks;
const renderer_1 = require("@remotion/renderer");
const no_react_1 = require("remotion/no-react");
function setupHooks(context, logLevel) {
function invalid() {
// We are now in invalid state
context.state = false;
context.stats = undefined;
}
function done(stats) {
context.state = true;
context.stats = stats;
// Do the stuff in nextTick, because bundle may be invalidated if a change happened while compiling
process.nextTick(() => {
const { logger, state, callbacks } = context;
// Check if still in valid state
if (!state || !stats) {
return;
}
logger.log('Compilation finished');
const statsOptions = {
preset: 'errors-warnings',
colors: renderer_1.RenderInternals.isColorSupported(),
};
const printedStats = stats.toString(statsOptions);
const lines = printedStats
.split('\n')
.map((a) => a.trimEnd())
.filter(no_react_1.NoReactInternals.truthy)
.map((a) => {
if (a.startsWith('webpack compiled')) {
return `Built in ${stats.endTime - stats.startTime}ms`;
}
return a;
})
.join('\n');
if (lines) {
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, lines);
if (process.argv.includes('--test-for-server-open')) {
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, 'Yes, the server started.');
process.exit(0);
}
}
context.callbacks = [];
callbacks.forEach((callback) => {
callback(stats);
});
});
}
context.compiler.hooks.watchRun.tap('remotion', invalid);
context.compiler.hooks.invalid.tap('remotion', invalid);
context.compiler.hooks.done.tap('remotion', done);
}
@@ -0,0 +1,2 @@
import type { DevMiddlewareContext } from './types';
export declare function setupOutputFileSystem(context: DevMiddlewareContext): void;
@@ -0,0 +1,13 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupOutputFileSystem = setupOutputFileSystem;
const memfs_1 = __importDefault(require("memfs"));
function setupOutputFileSystem(context) {
const outputFileSystem = memfs_1.default.createFsFromVolume(new memfs_1.default.Volume());
// @ts-expect-error output file sytem
context.compiler.outputFileSystem = outputFileSystem;
context.outputFileSystem = outputFileSystem;
}
@@ -0,0 +1,10 @@
import type { webpack } from '@remotion/bundler';
import type memfs from 'memfs';
export type DevMiddlewareContext = {
state: boolean;
stats: webpack.Stats | undefined;
callbacks: ((stats: webpack.Stats) => undefined | Promise<void>)[];
compiler: webpack.Compiler;
logger: ReturnType<webpack.Compiler['getInfrastructureLogger']>;
outputFileSystem: memfs.IFs | undefined;
};
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
export declare const envSupportsFsRecursive: () => boolean;
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.envSupportsFsRecursive = void 0;
const envSupportsFsRecursive = () => {
const nodeVersion = process.version.replace('v', '').split('.');
if (process.platform === 'darwin' || process.platform === 'win32') {
return true;
}
if (parseInt(nodeVersion[0], 10) > 19) {
return true;
}
if (parseInt(nodeVersion[0], 10) === 19 &&
parseInt(nodeVersion[1], 10) >= 1) {
return true;
}
return false;
};
exports.envSupportsFsRecursive = envSupportsFsRecursive;
@@ -0,0 +1,13 @@
export declare const subscribeToFileExistenceWatchers: ({ file: relativeFile, remotionRoot, clientId, }: {
file: string;
remotionRoot: string;
clientId: string;
}) => {
exists: boolean;
};
export declare const unsubscribeFromFileExistenceWatchers: ({ file, remotionRoot, clientId, }: {
file: string;
remotionRoot: string;
clientId: string;
}) => void;
export declare const unsubscribeClientFileExistenceWatchers: (clientId: string) => void;
@@ -0,0 +1,62 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.unsubscribeClientFileExistenceWatchers = exports.unsubscribeFromFileExistenceWatchers = exports.subscribeToFileExistenceWatchers = void 0;
const node_path_1 = __importDefault(require("node:path"));
const file_watcher_1 = require("../file-watcher");
const live_events_1 = require("./live-events");
const fileExistenceWatchers = {};
const subscribeToFileExistenceWatchers = ({ file: relativeFile, remotionRoot, clientId, }) => {
const file = node_path_1.default.resolve(remotionRoot, relativeFile);
const { unwatch, exists } = (0, file_watcher_1.installFileWatcher)({
file,
onChange: (type) => {
if (type === 'created') {
(0, live_events_1.waitForLiveEventsListener)().then((listener) => {
listener.sendEventToClient({
type: 'watched-file-undeleted',
// Must be relative file because that's what the client expects
file: relativeFile,
});
});
}
if (type === 'deleted') {
(0, live_events_1.waitForLiveEventsListener)().then((listener) => {
listener.sendEventToClient({
type: 'watched-file-deleted',
// Must be relative file because that's what the client expects
file: relativeFile,
});
});
}
},
});
if (!fileExistenceWatchers[clientId]) {
fileExistenceWatchers[clientId] = {};
}
fileExistenceWatchers[clientId][file] = unwatch;
return { exists };
};
exports.subscribeToFileExistenceWatchers = subscribeToFileExistenceWatchers;
const unsubscribeFromFileExistenceWatchers = ({ file, remotionRoot, clientId, }) => {
var _a, _b;
const actualPath = node_path_1.default.resolve(remotionRoot, file);
if (!fileExistenceWatchers[clientId]) {
return;
}
(_b = (_a = fileExistenceWatchers[clientId])[actualPath]) === null || _b === void 0 ? void 0 : _b.call(_a);
delete fileExistenceWatchers[clientId][actualPath];
};
exports.unsubscribeFromFileExistenceWatchers = unsubscribeFromFileExistenceWatchers;
const unsubscribeClientFileExistenceWatchers = (clientId) => {
if (!fileExistenceWatchers[clientId]) {
return;
}
Object.values(fileExistenceWatchers[clientId]).forEach((unwatch) => {
unwatch();
});
delete fileExistenceWatchers[clientId];
};
exports.unsubscribeClientFileExistenceWatchers = unsubscribeClientFileExistenceWatchers;
@@ -0,0 +1,4 @@
export declare const getAbsolutePublicDir: ({ relativePublicDir, remotionRoot, }: {
relativePublicDir: string | null;
remotionRoot: string;
}) => string;
@@ -0,0 +1,13 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAbsolutePublicDir = void 0;
const node_path_1 = __importDefault(require("node:path"));
const getAbsolutePublicDir = ({ relativePublicDir, remotionRoot, }) => {
return relativePublicDir
? node_path_1.default.resolve(remotionRoot, relativePublicDir)
: node_path_1.default.join(remotionRoot, 'public');
};
exports.getAbsolutePublicDir = getAbsolutePublicDir;
@@ -0,0 +1,16 @@
import type { LogLevel } from '@remotion/renderer';
import type { PackageManager } from '@remotion/studio-shared';
type LockfilePath = {
manager: PackageManager;
path: string;
installCommand: string;
startCommand: string;
};
export declare const lockFilePaths: LockfilePath[];
export declare const getPackageManager: ({ remotionRoot, packageManager, dirUp, logLevel, }: {
remotionRoot: string;
packageManager: string | undefined;
dirUp: number;
logLevel: LogLevel;
}) => LockfilePath | "unknown";
export {};
@@ -0,0 +1,76 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPackageManager = exports.lockFilePaths = void 0;
const renderer_1 = require("@remotion/renderer");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
exports.lockFilePaths = [
{
path: 'package-lock.json',
manager: 'npm',
installCommand: 'npm i',
startCommand: 'npx remotion studio',
},
{
path: 'yarn.lock',
manager: 'yarn',
installCommand: 'yarn add',
startCommand: 'yarn remotion studio',
},
{
path: 'pnpm-lock.yaml',
manager: 'pnpm',
installCommand: 'pnpm i',
startCommand: 'pnpm exec remotion studio',
},
{
path: 'bun.lock',
manager: 'bun',
installCommand: 'bun i',
startCommand: 'bunx remotion studio',
},
{
path: 'bun.lockb',
manager: 'bun',
installCommand: 'bun i',
startCommand: 'bunx remotion studio',
},
];
let warnedAboutMultipleLockfiles = false;
const getPackageManager = ({ remotionRoot, packageManager, dirUp, logLevel, }) => {
if (packageManager) {
const manager = exports.lockFilePaths.find((p) => p.manager === packageManager);
if (!manager) {
throw new Error(`The package manager ${packageManager} is not supported. Supported package managers are ${exports.lockFilePaths
.map((p) => p.manager)
.join(', ')}`);
}
return manager;
}
const existingPkgManagers = exports.lockFilePaths.filter((p) => node_fs_1.default.existsSync(node_path_1.default.join(remotionRoot, ...new Array(dirUp).fill('..'), p.path)));
if (existingPkgManagers.length === 0 && dirUp >= 2) {
return 'unknown';
}
if (existingPkgManagers.length === 0) {
return (0, exports.getPackageManager)({
remotionRoot,
packageManager,
dirUp: dirUp + 1,
logLevel,
});
}
if (existingPkgManagers.length > 1 && !warnedAboutMultipleLockfiles) {
warnedAboutMultipleLockfiles = true;
renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, '⚠️ Multiple lockfiles detected:');
for (const pkgManager of existingPkgManagers) {
renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, ` - ${pkgManager.path}`);
}
renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, '');
renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, 'This can lead to bugs, delete all but one of these files.');
}
return existingPkgManagers[0];
};
exports.getPackageManager = getPackageManager;
@@ -0,0 +1,14 @@
import type { LogLevel } from '@remotion/renderer';
import type { IncomingMessage, ServerResponse } from 'node:http';
import type { ApiHandler, QueueMethods } from './api-types';
export declare const handleRequest: <Req, Res>({ remotionRoot, request, response, entryPoint, handler, logLevel, methods, binariesDirectory, publicDir, }: {
remotionRoot: string;
publicDir: string;
request: IncomingMessage;
response: ServerResponse;
entryPoint: string;
binariesDirectory: string | null;
handler: ApiHandler<Req, Res>;
logLevel: LogLevel;
methods: QueueMethods;
}) => Promise<void>;
@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleRequest = void 0;
const parse_body_1 = require("./parse-body");
const handleRequest = async ({ remotionRoot, request, response, entryPoint, handler, logLevel, methods, binariesDirectory, publicDir, }) => {
if (request.method === 'OPTIONS') {
response.statusCode = 200;
response.end();
return;
}
response.setHeader('content-type', 'application/json');
response.writeHead(200);
try {
const body = (await (0, parse_body_1.parseRequestBody)(request));
const outputData = await handler({
entryPoint,
remotionRoot,
request,
response,
input: body,
logLevel,
methods,
binariesDirectory,
publicDir,
});
response.end(JSON.stringify({
success: true,
data: outputData,
}));
}
catch (err) {
response.end(JSON.stringify({
success: false,
error: err.message,
}));
}
};
exports.handleRequest = handleRequest;
@@ -0,0 +1,103 @@
/**
* Source code is adapted from
* https://github.com/webpack-contrib/webpack-hot-middleware#readme
* and rewritten in TypeScript. This file is MIT licensed
*/
import type { webpack } from '@remotion/bundler';
import type { LogLevel } from '@remotion/renderer';
import type { IncomingMessage, ServerResponse } from 'node:http';
declare global {
const __webpack_hash__: unknown;
interface HotNotifierInfo {
type: 'self-declined' | 'declined' | 'unaccepted' | 'accepted' | 'disposed' | 'accept-errored' | 'self-accept-errored' | 'self-accept-error-handler-errored';
/**
* The module in question.
*/
moduleId: number;
/**
* For errors: the module id owning the accept handler.
*/
dependencyId?: number | undefined;
/**
* For declined/accepted/unaccepted: the chain from where the update was propagated.
*/
chain?: number[] | undefined;
/**
* For declined: the module id of the declining parent
*/
parentId?: number | undefined;
/**
* For accepted: the modules that are outdated and will be disposed
*/
outdatedModules?: number[] | undefined;
/**
* For accepted: The location of accept handlers that will handle the update
*/
outdatedDependencies?: {
[dependencyId: number]: number[];
} | undefined;
/**
* For errors: the thrown error
*/
error?: Error | undefined;
/**
* For self-accept-error-handler-errored: the error thrown by the module
* before the error handler tried to handle it.
*/
originalError?: Error | undefined;
}
interface AcceptOptions {
/**
* If true the update process continues even if some modules are not accepted (and would bubble to the entry point).
*/
ignoreUnaccepted?: boolean | undefined;
/**
* Ignore changes made to declined modules.
*/
ignoreDeclined?: boolean | undefined;
/**
* Ignore errors throw in accept handlers, error handlers and while reevaluating module.
*/
ignoreErrored?: boolean | undefined;
/**
* Notifier for declined modules.
*/
onDeclined?: ((info: HotNotifierInfo) => void) | undefined;
/**
* Notifier for unaccepted modules.
*/
onUnaccepted?: ((info: HotNotifierInfo) => void) | undefined;
/**
* Notifier for accepted modules.
*/
onAccepted?: ((info: HotNotifierInfo) => void) | undefined;
/**
* Notifier for disposed modules.
*/
onDisposed?: ((info: HotNotifierInfo) => void) | undefined;
/**
* Notifier for errors.
*/
onErrored?: ((info: HotNotifierInfo) => void) | undefined;
/**
* Indicates that apply() is automatically called by check function
*/
autoApply?: boolean | undefined;
}
const __webpack_module__: {
id: string;
exports: unknown;
hot: {
accept: () => void;
dispose: (onDispose: (data: Record<string, unknown>) => void) => void;
invalidate: () => void;
data?: Record<string, unknown>;
addStatusHandler(callback: (status: string) => void): void;
status(): string;
apply(options?: AcceptOptions): Promise<ModuleId[]>;
check(autoApply?: boolean): Promise<null | ModuleId[]>;
};
};
type ModuleId = string | number;
}
export declare const webpackHotMiddleware: (compiler: webpack.Compiler, logLevel: LogLevel) => (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
@@ -0,0 +1,149 @@
"use strict";
/**
* Source code is adapted from
* https://github.com/webpack-contrib/webpack-hot-middleware#readme
* and rewritten in TypeScript. This file is MIT licensed
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.webpackHotMiddleware = void 0;
const renderer_1 = require("@remotion/renderer");
const studio_shared_1 = require("@remotion/studio-shared");
const node_url_1 = require("node:url");
const pathMatch = function (url, path) {
try {
return (0, node_url_1.parse)(url).pathname === path;
}
catch (_a) {
return false;
}
};
const webpackHotMiddleware = (compiler, logLevel) => {
const eventStream = createEventStream(studio_shared_1.hotMiddlewareOptions.heartbeat);
let latestStats = null;
compiler.hooks.invalid.tap('remotion', onInvalid);
compiler.hooks.done.tap('remotion', onDone);
function onInvalid() {
latestStats = null;
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, 'Building...');
eventStream === null || eventStream === void 0 ? void 0 : eventStream.publish({
action: 'building',
});
}
function onDone(statsResult) {
// Keep hold of latest stats so they can be propagated to new clients
latestStats = statsResult;
publishStats('built', latestStats, eventStream);
}
const middleware = function (req, res, next) {
if (!pathMatch(req.url, studio_shared_1.hotMiddlewareOptions.path))
return next();
eventStream === null || eventStream === void 0 ? void 0 : eventStream.handler(req, res);
if (latestStats) {
publishStats('sync', latestStats, eventStream);
}
};
return middleware;
};
exports.webpackHotMiddleware = webpackHotMiddleware;
function createEventStream(heartbeat) {
let clientId = 0;
let clients = {};
function everyClient(fn) {
Object.keys(clients).forEach((id) => {
fn(clients[id]);
});
}
const interval = setInterval(() => {
everyClient((client) => {
client.write('data: \uD83D\uDC93\n\n');
});
}, heartbeat).unref();
return {
close() {
clearInterval(interval);
everyClient((client) => {
if (!client.finished)
client.end();
});
clients = {};
},
handler(req, res) {
const headers = {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'text/event-stream;charset=utf-8',
'Cache-Control': 'no-cache, no-transform',
};
const isHttp1 = !(parseInt(req.httpVersion, 10) >= 2);
if (isHttp1) {
req.socket.setKeepAlive(true);
Object.assign(headers, {
Connection: 'keep-alive',
});
}
res.writeHead(200, headers);
res.write('\n');
const id = clientId++;
clients[id] = res;
req.on('close', () => {
if (!res.finished)
res.end();
delete clients[id];
});
},
publish(payload) {
everyClient((client) => {
client.write('data: ' + JSON.stringify(payload) + '\n\n');
});
},
};
}
function publishStats(action, statsResult, eventStream) {
const stats = statsResult.toJson({
all: false,
cached: true,
children: true,
modules: true,
timings: true,
hash: true,
});
// For multi-compiler, stats will be an object with a 'children' array of stats
const bundles = extractBundles(stats);
bundles.forEach((_stats) => {
let name = _stats.name || '';
// Fallback to compilation name in case of 1 bundle (if it exists)
if (bundles.length === 1 && !name && statsResult.compilation) {
name = statsResult.compilation.name || '';
}
eventStream === null || eventStream === void 0 ? void 0 : eventStream.publish({
name,
action,
time: _stats.time,
hash: _stats.hash,
warnings: _stats.warnings || [],
errors: _stats.errors || [],
modules: buildModuleMap(_stats.modules),
});
});
}
function extractBundles(stats) {
var _a;
// Stats has modules, single bundle
if (stats.modules)
return [stats];
// Stats has children, multiple bundles
if ((_a = stats.children) === null || _a === void 0 ? void 0 : _a.length)
return stats.children;
// Not sure, assume single
return [stats];
}
function buildModuleMap(modules) {
const map = {};
if (!modules) {
return map;
}
modules.forEach((module) => {
const id = module.id;
map[id] = module.name;
});
return map;
}
@@ -0,0 +1,2 @@
import type { webpack } from '@remotion/bundler';
export type WebpackStats = ReturnType<webpack.Stats['toJson']>;
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
import type { LogLevel } from '@remotion/renderer';
import type { EventSourceEvent } from '@remotion/studio-shared';
import type { IncomingMessage, ServerResponse } from 'node:http';
export type LiveEventsServer = {
sendEventToClient: (event: EventSourceEvent) => void;
router: (request: IncomingMessage, response: ServerResponse) => Promise<void>;
closeConnections: () => Promise<void>;
};
export declare const makeLiveEventsRouter: (logLevel: LogLevel) => LiveEventsServer;
export declare const waitForLiveEventsListener: () => Promise<LiveEventsServer>;
export declare const setLiveEventsListener: (listener: LiveEventsServer) => () => void;
@@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setLiveEventsListener = exports.waitForLiveEventsListener = exports.makeLiveEventsRouter = void 0;
const server_ready_1 = require("../server-ready");
const file_existence_watchers_1 = require("./file-existence-watchers");
const serializeMessage = (message) => {
return `data: ${JSON.stringify(message)}\n\n`;
};
let printPortMessageTimeout = null;
const makeLiveEventsRouter = (logLevel) => {
let clients = [];
const router = (request, response) => {
const headers = {
'content-type': 'text/event-stream;charset=utf-8',
connection: 'keep-alive',
'cache-control': 'no-cache',
};
response.writeHead(200, headers);
response.write('\n');
if (request.method === 'OPTIONS') {
response.end();
return Promise.resolve();
}
const clientId = String(Math.random());
response.write(serializeMessage({ type: 'init', clientId }));
const newClient = {
id: clientId,
response,
};
clients.push(newClient);
if (printPortMessageTimeout) {
clearTimeout(printPortMessageTimeout);
}
request.on('close', () => {
(0, file_existence_watchers_1.unsubscribeClientFileExistenceWatchers)(clientId);
clients = clients.filter((client) => client.id !== clientId);
// If all clients disconnected, print a comment so user can easily restart it.
if (clients.length === 0) {
if (printPortMessageTimeout) {
clearTimeout(printPortMessageTimeout);
}
printPortMessageTimeout = setTimeout(() => {
(0, server_ready_1.printServerReadyComment)('To restart', logLevel);
}, 2500);
}
});
return Promise.resolve();
};
const sendEventToClient = (event) => {
clients.forEach((client) => {
client.response.write(serializeMessage(event));
});
};
return {
sendEventToClient,
router,
closeConnections: () => {
return Promise.all(clients.map((client) => {
return new Promise((resolve) => {
client.response.end(() => {
resolve();
});
});
})).then(() => undefined);
},
};
};
exports.makeLiveEventsRouter = makeLiveEventsRouter;
let liveEventsListener = null;
const waiters = [];
const waitForLiveEventsListener = () => {
if (liveEventsListener) {
return Promise.resolve(liveEventsListener);
}
return new Promise((resolve) => {
waiters.push((list) => {
resolve(list);
});
});
};
exports.waitForLiveEventsListener = waitForLiveEventsListener;
const setLiveEventsListener = (listener) => {
liveEventsListener = listener;
waiters.forEach((w) => w(listener));
return () => {
liveEventsListener = null;
};
};
exports.setLiveEventsListener = setLiveEventsListener;
@@ -0,0 +1,2 @@
import type { IncomingMessage } from 'node:http';
export declare const parseRequestBody: (req: IncomingMessage) => Promise<unknown>;
@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseRequestBody = void 0;
const parseRequestBody = async (req) => {
const body = await new Promise((_resolve) => {
let data = '';
req.on('data', (chunk) => {
data += chunk;
});
req.on('end', () => {
_resolve(data.toString());
});
});
return JSON.parse(body);
};
exports.parseRequestBody = parseRequestBody;
@@ -0,0 +1,2 @@
import type { ProjectInfo } from '@remotion/studio-shared';
export declare const getProjectInfo: (remotionRoot: string, entryPoint: string) => Promise<ProjectInfo>;
@@ -0,0 +1,37 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getProjectInfo = void 0;
const node_fs_1 = require("node:fs");
const node_path_1 = __importDefault(require("node:path"));
const getProjectInfo = (remotionRoot, entryPoint) => {
var _a;
const knownPaths = [
'src/Root.tsx',
'src/Root.jsx',
'remotion/Root.tsx',
'remotion/Root.jsx',
'app/remotion/Root.tsx',
'src/Video.tsx',
'src/Video.jsx',
'src/remotion/Root.tsx',
'src/remotion/Root.jsx',
];
const pathsToLookFor = [
...knownPaths.map((p) => {
return node_path_1.default.join(remotionRoot, p);
}),
node_path_1.default.join(entryPoint, 'Root.tsx'),
node_path_1.default.join(entryPoint, 'Root.jsx'),
node_path_1.default.join(entryPoint, 'remotion/Root.tsx'),
node_path_1.default.join(entryPoint, 'remotion/Root.jsx'),
];
const rootFile = (_a = pathsToLookFor.find((p) => (0, node_fs_1.existsSync)(p))) !== null && _a !== void 0 ? _a : null;
return Promise.resolve({
rootFile,
relativeRootFile: rootFile ? node_path_1.default.relative(remotionRoot, rootFile) : null,
});
};
exports.getProjectInfo = getProjectInfo;
@@ -0,0 +1,12 @@
import type { StaticFile } from 'remotion';
export declare const initPublicFolderWatch: ({ publicDir, onUpdate, staticHash, }: {
publicDir: string;
remotionRoot: string;
onUpdate: () => void;
staticHash: string;
}) => void;
export declare const fetchFolder: ({ publicDir, staticHash, }: {
publicDir: string;
staticHash: string;
}) => void;
export declare const getFiles: () => StaticFile[];
@@ -0,0 +1,58 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFiles = exports.fetchFolder = exports.initPublicFolderWatch = void 0;
const bundler_1 = require("@remotion/bundler");
const node_fs_1 = require("node:fs");
const node_path_1 = __importDefault(require("node:path"));
const env_supports_fs_recursive_1 = require("./env-supports-fs-recursive");
let files = [];
const initPublicFolderWatch = ({ publicDir, onUpdate, staticHash, }) => {
(0, exports.fetchFolder)({ publicDir, staticHash });
watchPublicFolder({ publicDir, onUpdate, staticHash });
};
exports.initPublicFolderWatch = initPublicFolderWatch;
const fetchFolder = ({ publicDir, staticHash, }) => {
files = bundler_1.BundlerInternals.readRecursively({
folder: '.',
startPath: publicDir,
staticHash,
limit: 10000,
}).map((f) => {
return {
...f,
name: f.name.split(node_path_1.default.sep).join('/'),
};
});
};
exports.fetchFolder = fetchFolder;
const watchPublicFolder = ({ publicDir, onUpdate, staticHash, }) => {
if (!(0, node_fs_1.existsSync)(publicDir)) {
const parentDir = node_path_1.default.dirname(publicDir);
const onDirChange = () => {
if ((0, node_fs_1.existsSync)(publicDir)) {
watchPublicFolder({
publicDir,
onUpdate,
staticHash,
});
onUpdate();
watcher.close();
}
};
const watcher = (0, node_fs_1.watch)(parentDir, {}, onDirChange);
return;
}
// Known bug: If whole public folder is deleted, this will not be called on macOS.
// This is not severe, so a wontfix for now.
(0, node_fs_1.watch)(publicDir, { recursive: (0, env_supports_fs_recursive_1.envSupportsFsRecursive)() }, () => {
(0, exports.fetchFolder)({ publicDir, staticHash });
onUpdate();
});
};
const getFiles = () => {
return files;
};
exports.getFiles = getFiles;
@@ -0,0 +1,3 @@
import type { AddRenderRequest } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const handleAddRender: ApiHandler<AddRenderRequest, undefined>;
@@ -0,0 +1,140 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleAddRender = void 0;
const renderer_1 = require("@remotion/renderer");
const handleAddRender = ({ input, entryPoint, remotionRoot, logLevel, binariesDirectory, methods: { addJob }, }) => {
const id = String(Math.random()).replace('0.', '');
if (input.type === 'video') {
addJob({
entryPoint,
remotionRoot,
job: {
cleanup: [],
codec: input.codec,
audioCodec: input.audioCodec,
compositionId: input.compositionId,
deletedOutputLocation: false,
type: 'video',
status: 'idle',
id,
imageFormat: input.imageFormat,
outName: input.outName,
jpegQuality: input.jpegQuality,
scale: input.scale,
startedAt: Date.now(),
logLevel: input.logLevel,
cancelToken: (0, renderer_1.makeCancelSignal)(),
concurrency: input.concurrency,
crf: input.crf,
endFrame: input.endFrame,
startFrame: input.startFrame,
muted: input.muted,
enforceAudioTrack: input.enforceAudioTrack,
proResProfile: input.proResProfile,
x264Preset: input.x264Preset,
pixelFormat: input.pixelFormat,
audioBitrate: input.audioBitrate,
videoBitrate: input.videoBitrate,
encodingMaxRate: input.encodingMaxRate,
encodingBufferSize: input.encodingBufferSize,
everyNthFrame: input.everyNthFrame,
numberOfGifLoops: input.numberOfGifLoops,
delayRenderTimeout: input.delayRenderTimeout,
disallowParallelEncoding: input.disallowParallelEncoding,
chromiumOptions: input.chromiumOptions,
envVariables: input.envVariables,
serializedInputPropsWithCustomSchema: input.serializedInputPropsWithCustomSchema,
offthreadVideoCacheSizeInBytes: input.offthreadVideoCacheSizeInBytes,
colorSpace: input.colorSpace,
multiProcessOnLinux: input.multiProcessOnLinux,
beepOnFinish: input.beepOnFinish,
repro: input.repro,
binariesDirectory,
forSeamlessAacConcatenation: input.forSeamlessAacConcatenation,
separateAudioTo: input.separateAudioTo,
metadata: input.metadata,
hardwareAcceleration: input.hardwareAcceleration,
chromeMode: input.chromeMode,
offthreadVideoThreads: input.offthreadVideoThreads,
mediaCacheSizeInBytes: input.mediaCacheSizeInBytes,
},
logLevel,
});
}
if (input.type === 'sequence') {
addJob({
entryPoint,
remotionRoot,
job: {
cleanup: [],
compositionId: input.compositionId,
deletedOutputLocation: false,
type: 'sequence',
status: 'idle',
id,
imageFormat: input.imageFormat,
outName: input.outName,
jpegQuality: input.jpegQuality,
scale: input.scale,
startedAt: Date.now(),
logLevel: input.logLevel,
cancelToken: (0, renderer_1.makeCancelSignal)(),
concurrency: input.concurrency,
endFrame: input.endFrame,
startFrame: input.startFrame,
delayRenderTimeout: input.delayRenderTimeout,
chromiumOptions: input.chromiumOptions,
envVariables: input.envVariables,
serializedInputPropsWithCustomSchema: input.serializedInputPropsWithCustomSchema,
offthreadVideoCacheSizeInBytes: input.offthreadVideoCacheSizeInBytes,
multiProcessOnLinux: input.multiProcessOnLinux,
beepOnFinish: input.beepOnFinish,
repro: input.repro,
binariesDirectory,
metadata: input.metadata,
chromeMode: input.chromeMode,
offthreadVideoThreads: input.offthreadVideoThreads,
mediaCacheSizeInBytes: input.mediaCacheSizeInBytes,
},
logLevel,
});
}
if (input.type === 'still') {
addJob({
job: {
compositionId: input.compositionId,
id: String(Math.random()).replace('0.', ''),
startedAt: Date.now(),
type: 'still',
outName: input.outName,
status: 'idle',
imageFormat: input.imageFormat,
jpegQuality: input.jpegQuality,
frame: input.frame,
scale: input.scale,
cleanup: [],
deletedOutputLocation: false,
logLevel: input.logLevel,
cancelToken: (0, renderer_1.makeCancelSignal)(),
chromiumOptions: input.chromiumOptions,
delayRenderTimeout: input.delayRenderTimeout,
envVariables: input.envVariables,
serializedInputPropsWithCustomSchema: input.serializedInputPropsWithCustomSchema,
offthreadVideoCacheSizeInBytes: input.offthreadVideoCacheSizeInBytes,
multiProcessOnLinux: input.multiProcessOnLinux,
beepOnFinish: input.beepOnFinish,
repro: false,
binariesDirectory,
metadata: input.metadata,
chromeMode: input.chromeMode,
offthreadVideoThreads: input.offthreadVideoThreads,
mediaCacheSizeInBytes: input.mediaCacheSizeInBytes,
},
entryPoint,
remotionRoot,
logLevel,
});
}
return Promise.resolve(undefined);
};
exports.handleAddRender = handleAddRender;
@@ -0,0 +1,3 @@
import type { ApplyCodemodRequest, ApplyCodemodResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const applyCodemodHandler: ApiHandler<ApplyCodemodRequest, ApplyCodemodResponse>;
@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyCodemodHandler = void 0;
const renderer_1 = require("@remotion/renderer");
const node_fs_1 = require("node:fs");
const duplicate_composition_1 = require("../../codemods/duplicate-composition");
const simple_diff_1 = require("../../codemods/simple-diff");
const project_info_1 = require("../project-info");
const can_update_default_props_1 = require("./can-update-default-props");
const applyCodemodHandler = async ({ input: { codemod, dryRun }, logLevel, remotionRoot, entryPoint }) => {
try {
const time = Date.now();
const projectInfo = await (0, project_info_1.getProjectInfo)(remotionRoot, entryPoint);
if (!projectInfo.rootFile) {
throw new Error('Cannot find root file in project');
}
(0, can_update_default_props_1.checkIfTypeScriptFile)(projectInfo.rootFile);
const input = (0, node_fs_1.readFileSync)(projectInfo.rootFile, 'utf-8');
const { newContents } = (0, duplicate_composition_1.parseAndApplyCodemod)({
codeMod: codemod,
input,
});
const formatted = await (0, duplicate_composition_1.formatOutput)(newContents);
const diff = (0, simple_diff_1.simpleDiff)({
oldLines: input.split('\n'),
newLines: formatted.split('\n'),
});
if (!dryRun) {
(0, node_fs_1.writeFileSync)(projectInfo.rootFile, formatted);
const end = Date.now() - time;
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.blue(`Edited root file in ${end}ms`));
}
return {
success: true,
diff,
};
}
catch (err) {
return {
success: false,
reason: err.message,
stack: err.stack,
};
}
};
exports.applyCodemodHandler = applyCodemodHandler;
@@ -0,0 +1,3 @@
import type { ApplyVisualControlRequest, ApplyVisualControlResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const applyVisualControlHandler: ApiHandler<ApplyVisualControlRequest, ApplyVisualControlResponse>;
@@ -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;
@@ -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>;
@@ -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;
@@ -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>;
@@ -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;
@@ -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>;
@@ -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.deleteStaticFileHandler = void 0;
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const deleteStaticFileHandler = ({ input: { relativePath }, publicDir }) => {
const resolved = path_1.default.join(publicDir, relativePath);
const relativeToPublicDir = path_1.default.relative(publicDir, resolved);
if (relativeToPublicDir.startsWith('..')) {
throw new Error(`Not allowed to write to ${relativeToPublicDir}`);
}
const exists = (0, fs_1.existsSync)(resolved);
if (exists) {
(0, fs_1.unlinkSync)(resolved);
}
return Promise.resolve({
success: true,
existed: exists,
});
};
exports.deleteStaticFileHandler = deleteStaticFileHandler;
@@ -0,0 +1,3 @@
import { type InstallPackageRequest, type InstallPackageResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const handleInstallPackage: ApiHandler<InstallPackageRequest, InstallPackageResponse>;
@@ -0,0 +1,82 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleInstallPackage = void 0;
const renderer_1 = require("@remotion/renderer");
const studio_shared_1 = require("@remotion/studio-shared");
const node_child_process_1 = require("node:child_process");
const version_1 = require("remotion/version");
const install_command_1 = require("../../helpers/install-command");
const get_package_manager_1 = require("../get-package-manager");
const extraPackageNames = studio_shared_1.extraPackages.map((pkg) => pkg.name);
const isExtraPackage = (packageName) => {
return extraPackageNames.includes(packageName);
};
const getExtraPackageVersion = (packageName) => {
const pkg = studio_shared_1.extraPackages.find((p) => p.name === packageName);
return pkg ? pkg.version : null;
};
const handleInstallPackage = async ({ logLevel, remotionRoot, input: { packageNames } }) => {
for (const packageName of packageNames) {
if (!packageName.startsWith('@remotion/') && !isExtraPackage(packageName)) {
return Promise.reject(new Error(`Package ${packageName} is not allowed to be installed.`));
}
}
const manager = (0, get_package_manager_1.getPackageManager)({
remotionRoot,
packageManager: undefined,
dirUp: 0,
logLevel,
});
if (manager === 'unknown') {
throw new Error(`No lockfile was found in your project (one of ${get_package_manager_1.lockFilePaths
.map((p) => p.path)
.join(', ')}). Install dependencies using your favorite manager!`);
}
// Build packages with appropriate versions
const packagesWithVersions = packageNames.map((pkg) => {
const extraVersion = getExtraPackageVersion(pkg);
if (extraVersion) {
return `${pkg}@${extraVersion}`;
}
return `${pkg}@${version_1.VERSION}`;
});
const command = (0, install_command_1.getInstallCommand)({
manager: manager.manager,
packages: packagesWithVersions,
version: '',
additionalArgs: [],
});
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.gray(`╭─ ${manager.manager} ${command.join(' ')}`));
const time = Date.now();
try {
await new Promise((resolve, reject) => {
const cmd = (0, node_child_process_1.spawn)(manager.manager, command, {});
cmd.stdout.on('data', (d) => {
const splitted = d.toString().trim().split('\n');
splitted.forEach((line) => {
renderer_1.RenderInternals.Log.info({ indent: true, logLevel }, line);
});
});
cmd.stdout.on('end', () => {
resolve();
});
cmd.on('close', (code, signal) => {
if (code === 0) {
resolve();
}
else {
reject(new Error(`Command exited with code ${code} and signal ${signal}`));
}
});
});
const timeEnd = Date.now();
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.gray('╰─ '), `Done in ${timeEnd - time}ms`);
return Promise.resolve({});
}
catch (err) {
const timeEnd = Date.now();
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.gray('╰─ '), renderer_1.RenderInternals.chalk.red(`Errored in ${timeEnd - time}ms`));
return Promise.reject(err);
}
};
exports.handleInstallPackage = handleInstallPackage;
@@ -0,0 +1,3 @@
import type { OpenInFileExplorerRequest } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const handleOpenInFileExplorer: ApiHandler<OpenInFileExplorerRequest, void>;
@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleOpenInFileExplorer = void 0;
const open_directory_in_finder_1 = require("../../open-directory-in-finder");
const handleOpenInFileExplorer = ({ input: { directory }, remotionRoot }) => {
return (0, open_directory_in_finder_1.openDirectoryInFinder)(directory, remotionRoot);
};
exports.handleOpenInFileExplorer = handleOpenInFileExplorer;
@@ -0,0 +1,3 @@
import type { ProjectInfoRequest, ProjectInfoResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const projectInfoHandler: ApiHandler<ProjectInfoRequest, ProjectInfoResponse>;
@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.projectInfoHandler = void 0;
const project_info_1 = require("../project-info");
const projectInfoHandler = async ({ remotionRoot, entryPoint }) => {
const info = await (0, project_info_1.getProjectInfo)(remotionRoot, entryPoint);
return { projectInfo: info };
};
exports.projectInfoHandler = projectInfoHandler;
@@ -0,0 +1,3 @@
import type { RemoveRenderRequest } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const handleRemoveRender: ApiHandler<RemoveRenderRequest, undefined>;
@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleRemoveRender = void 0;
const handleRemoveRender = ({ input: { jobId }, methods: { removeJob }, }) => {
removeJob(jobId);
return Promise.resolve(undefined);
};
exports.handleRemoveRender = handleRemoveRender;
@@ -0,0 +1,3 @@
import type { RestartStudioRequest, RestartStudioResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const handleRestartStudio: ApiHandler<RestartStudioRequest, RestartStudioResponse>;
@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleRestartStudio = void 0;
const close_and_restart_1 = require("../close-and-restart");
const handleRestartStudio = () => {
(0, close_and_restart_1.signalRestart)();
return Promise.resolve({});
};
exports.handleRestartStudio = handleRestartStudio;
@@ -0,0 +1,3 @@
import type { SubscribeToFileExistenceRequest, SubscribeToFileExistenceResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const subscribeToFileExistence: ApiHandler<SubscribeToFileExistenceRequest, SubscribeToFileExistenceResponse>;
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.subscribeToFileExistence = void 0;
const file_existence_watchers_1 = require("../file-existence-watchers");
const subscribeToFileExistence = ({ input: { file, clientId }, remotionRoot }) => {
const { exists } = (0, file_existence_watchers_1.subscribeToFileExistenceWatchers)({
file,
remotionRoot,
clientId,
});
return Promise.resolve({ exists });
};
exports.subscribeToFileExistence = subscribeToFileExistence;
@@ -0,0 +1,3 @@
import type { UnsubscribeFromFileExistenceRequest } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const unsubscribeFromFileExistence: ApiHandler<UnsubscribeFromFileExistenceRequest, undefined>;
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.unsubscribeFromFileExistence = void 0;
const file_existence_watchers_1 = require("../file-existence-watchers");
const unsubscribeFromFileExistence = ({ input, remotionRoot }) => {
(0, file_existence_watchers_1.unsubscribeFromFileExistenceWatchers)({
file: input.file,
clientId: input.clientId,
remotionRoot,
});
return Promise.resolve(undefined);
};
exports.unsubscribeFromFileExistence = unsubscribeFromFileExistence;
@@ -0,0 +1,3 @@
import type { UpdateAvailableRequest, UpdateAvailableResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const handleUpdate: ApiHandler<UpdateAvailableRequest, UpdateAvailableResponse>;
@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleUpdate = void 0;
const update_available_1 = require("../update-available");
const handleUpdate = async ({ remotionRoot, logLevel }) => {
const data = await (0, update_available_1.isUpdateAvailableWithTimeout)(remotionRoot, logLevel);
return data;
};
exports.handleUpdate = handleUpdate;
@@ -0,0 +1,3 @@
import type { UpdateDefaultPropsRequest, UpdateDefaultPropsResponse } from '@remotion/studio-shared';
import type { ApiHandler } from '../api-types';
export declare const updateDefaultPropsHandler: ApiHandler<UpdateDefaultPropsRequest, UpdateDefaultPropsResponse>;
@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateDefaultPropsHandler = 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 can_update_default_props_1 = require("./can-update-default-props");
const updateDefaultPropsHandler = async ({ input: { compositionId, defaultProps, enumPaths }, 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, can_update_default_props_1.checkIfTypeScriptFile)(projectInfo.rootFile);
const updated = await (0, update_default_props_1.updateDefaultProps)({
compositionId,
input: (0, node_fs_1.readFileSync)(projectInfo.rootFile, 'utf-8'),
newDefaultProps: JSON.parse(defaultProps),
enumPaths,
});
(0, node_fs_1.writeFileSync)(projectInfo.rootFile, updated);
return {
success: true,
};
}
catch (err) {
return {
success: false,
reason: err.message,
stack: err.stack,
};
}
};
exports.updateDefaultPropsHandler = updateDefaultPropsHandler;
@@ -0,0 +1,15 @@
/*!
* serve-static
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2014-2016 Douglas Christopher Wilson
* MIT Licensed
*/
import type { IncomingMessage, ServerResponse } from 'node:http';
export declare const serveStatic: ({ root, path, req, res, allowOutsidePublicFolder, }: {
root: string;
path: string;
req: IncomingMessage;
res: ServerResponse;
allowOutsidePublicFolder: boolean;
}) => Promise<void>;
@@ -0,0 +1,85 @@
"use strict";
/*!
* serve-static
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2014-2016 Douglas Christopher Wilson
* MIT Licensed
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.serveStatic = void 0;
const renderer_1 = require("@remotion/renderer");
const node_fs_1 = require("node:fs");
const middleware_1 = require("./dev-middleware/middleware");
const range_parser_1 = require("./dev-middleware/range-parser");
const serveStatic = async function ({ root, path, req, res, allowOutsidePublicFolder, }) {
if (req.method !== 'GET' && req.method !== 'HEAD') {
// method not allowed
res.statusCode = 405;
res.setHeader('Allow', 'GET, HEAD');
res.setHeader('Content-Length', '0');
res.end();
return;
}
if (!renderer_1.RenderInternals.isPathInside(path, root) && !allowOutsidePublicFolder) {
res.writeHead(500);
res.write('Not allowed to read');
res.end();
return;
}
const exists = (0, node_fs_1.existsSync)(path);
if (!exists) {
res.writeHead(404);
res.write(`${path} does not exist`);
res.end();
return;
}
const lstat = await node_fs_1.promises.lstat(path);
if (lstat.isSymbolicLink()) {
const target = await node_fs_1.promises.readlink(path);
return (0, exports.serveStatic)({
path: target,
root,
req,
res,
allowOutsidePublicFolder: true,
});
}
const isDirectory = lstat.isDirectory();
if (isDirectory) {
res.writeHead(500);
res.write('Is a directory');
res.end();
return;
}
const hasRange = req.headers.range && lstat.size;
if (!hasRange) {
const readStream = (0, node_fs_1.createReadStream)(path);
res.setHeader('content-type', renderer_1.RenderInternals.mimeLookup(path) || 'application/octet-stream');
res.setHeader('content-length', lstat.size);
res.writeHead(200);
readStream.pipe(res);
return;
}
const range = (0, range_parser_1.parseRange)(lstat.size, req.headers.range);
if (typeof range === 'object' && range.type === 'bytes') {
const { start, end } = range[0];
res.setHeader('content-type', renderer_1.RenderInternals.mimeLookup(path) || 'application/octet-stream');
res.setHeader('content-range', (0, middleware_1.getValueContentRangeHeader)('bytes', lstat.size, {
end,
start,
}));
res.setHeader('content-length', end - start + 1);
res.writeHead(206);
const readStream = (0, node_fs_1.createReadStream)(path, {
start,
end,
});
readStream.pipe(res);
return;
}
res.statusCode = 416;
res.setHeader('Content-Range', `bytes */${lstat.size}`);
res.end();
};
exports.serveStatic = serveStatic;
@@ -0,0 +1,45 @@
import type { WebpackOverrideFn } from '@remotion/bundler';
import type { LogLevel } from '@remotion/renderer';
import type { GitSource, RenderDefaults, RenderJob } from '@remotion/studio-shared';
import type { QueueMethods } from './api-types';
import type { LiveEventsServer } from './live-events';
export type StartServerResult = {
type: 'started';
port: number;
liveEventsServer: LiveEventsServer;
close: () => Promise<void>;
} | {
type: 'already-running';
port: number;
};
export declare const startServer: (options: {
entry: string;
userDefinedComponent: string;
webpackOverride: WebpackOverrideFn;
getCurrentInputProps: () => object;
getEnvVariables: () => Record<string, string>;
port: number | null;
maxTimelineTracks: number | null;
bufferStateDelayInMilliseconds: number | null;
remotionRoot: string;
keyboardShortcutsEnabled: boolean;
experimentalClientSideRenderingEnabled: boolean;
publicDir: string;
poll: number | null;
staticHash: string;
staticHashPrefix: string;
outputHash: string;
outputHashPrefix: string;
logLevel: LogLevel;
getRenderQueue: () => RenderJob[];
getRenderDefaults: () => RenderDefaults;
numberOfAudioTags: number;
queueMethods: QueueMethods;
gitSource: GitSource | null;
binariesDirectory: string | null;
forceIPv4: boolean;
audioLatencyHint: AudioContextLatencyCategory | null;
enableCrossSiteIsolation: boolean;
askAIEnabled: boolean;
forceNew: boolean;
}) => Promise<StartServerResult>;
@@ -0,0 +1,177 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.startServer = void 0;
const bundler_1 = require("@remotion/bundler");
const renderer_1 = require("@remotion/renderer");
const node_http_1 = __importDefault(require("node:http"));
const detect_remotion_server_1 = require("../detect-remotion-server");
const routes_1 = require("../routes");
const dev_middleware_1 = require("./dev-middleware");
const hot_middleware_1 = require("./hot-middleware");
const live_events_1 = require("./live-events");
const startServer = async (options) => {
var _a, _b, _c;
const desiredPort = (_b = (_a = options === null || options === void 0 ? void 0 : options.port) !== null && _a !== void 0 ? _a : (process.env.PORT ? Number(process.env.PORT) : undefined)) !== null && _b !== void 0 ? _b : undefined;
const portConfig = renderer_1.RenderInternals.getPortConfig(options.forceIPv4);
const onPortUnavailable = options.forceNew
? undefined
: async (port) => {
const detection = await (0, detect_remotion_server_1.detectRemotionServer)({
port,
cwd: options.remotionRoot,
hostname: portConfig.hostsToTry[0],
});
return detection.type === 'match' ? 'stop' : 'continue';
};
const [, config] = await bundler_1.BundlerInternals.webpackConfig({
entry: options.entry,
userDefinedComponent: options.userDefinedComponent,
outDir: null,
environment: 'development',
webpackOverride: options === null || options === void 0 ? void 0 : options.webpackOverride,
maxTimelineTracks: (_c = options === null || options === void 0 ? void 0 : options.maxTimelineTracks) !== null && _c !== void 0 ? _c : null,
remotionRoot: options.remotionRoot,
keyboardShortcutsEnabled: options.keyboardShortcutsEnabled,
experimentalClientSideRenderingEnabled: options.experimentalClientSideRenderingEnabled,
poll: options.poll,
bufferStateDelayInMilliseconds: options.bufferStateDelayInMilliseconds,
askAIEnabled: options.askAIEnabled,
});
const compiler = (0, bundler_1.webpack)(config);
const wdmMiddleware = (0, dev_middleware_1.wdm)(compiler, options.logLevel);
const whm = (0, hot_middleware_1.webpackHotMiddleware)(compiler, options.logLevel);
const liveEventsServer = (0, live_events_1.makeLiveEventsRouter)(options.logLevel);
const server = node_http_1.default.createServer((request, response) => {
if (options.enableCrossSiteIsolation) {
response.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
response.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
}
new Promise((resolve) => {
wdmMiddleware(request, response, () => {
resolve();
});
})
.then(() => {
return new Promise((resolve) => {
whm(request, response, () => {
resolve();
});
});
})
.then(() => {
return (0, routes_1.handleRoutes)({
staticHash: options.staticHash,
staticHashPrefix: options.staticHashPrefix,
outputHash: options.outputHash,
outputHashPrefix: options.outputHashPrefix,
request: request,
response,
liveEventsServer,
getCurrentInputProps: options.getCurrentInputProps,
getEnvVariables: options.getEnvVariables,
remotionRoot: options.remotionRoot,
entryPoint: options.userDefinedComponent,
publicDir: options.publicDir,
logLevel: options.logLevel,
getRenderQueue: options.getRenderQueue,
getRenderDefaults: options.getRenderDefaults,
numberOfAudioTags: options.numberOfAudioTags,
queueMethods: options.queueMethods,
gitSource: options.gitSource,
binariesDirectory: options.binariesDirectory,
audioLatencyHint: options.audioLatencyHint,
enableCrossSiteIsolation: options.enableCrossSiteIsolation,
});
})
.catch((err) => {
renderer_1.RenderInternals.Log.error({ indent: false, logLevel: options.logLevel }, `Error while calling ${request.url}`, err);
if (!response.headersSent) {
response.setHeader('content-type', 'application/json');
response.writeHead(500);
}
if (!response.writableEnded) {
response.end(JSON.stringify({
err: err.message,
}));
}
});
});
const maxTries = 5;
for (let i = 0; i < maxTries; i++) {
try {
const { port, unlockPort, didUsePort } = await renderer_1.RenderInternals.getDesiredPort({
desiredPort,
from: 3000,
to: 3100,
hostsToTry: portConfig.hostsToTry,
onPortUnavailable,
});
if (didUsePort) {
unlockPort();
await Promise.all([
new Promise((resolve) => {
server.close(() => resolve());
}),
new Promise((resolve) => {
compiler.close(() => resolve());
}),
]);
return {
type: 'already-running',
port,
};
}
const selectedPort = await new Promise((resolve, reject) => {
renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel: options.logLevel }, `Binding server to host ${portConfig.host}, port ${port}`);
server.listen({
port,
host: portConfig.host,
});
server.on('listening', () => {
resolve(port);
return unlockPort();
});
server.on('error', (err) => {
reject(err);
});
});
return {
type: 'started',
port: selectedPort,
liveEventsServer,
close: async () => {
server.closeAllConnections();
await Promise.all([
new Promise((resolve) => {
server.close(() => {
resolve();
});
}),
new Promise((resolve) => {
compiler.close(() => {
resolve();
});
}),
]);
},
};
}
catch (err) {
if (!(err instanceof Error)) {
throw err;
}
const codedError = err;
if (codedError.code === 'EADDRINUSE') {
renderer_1.RenderInternals.Log.error({ indent: false, logLevel: options.logLevel }, `Port ${codedError.port} is already in use. Trying another port...`);
}
else {
throw err;
}
}
}
throw new Error(`Tried ${maxTries} times to find a free port. Giving up.`);
};
exports.startServer = startServer;
@@ -0,0 +1,4 @@
import type { LogLevel } from '@remotion/renderer';
import type { UpdateAvailableResponse } from '@remotion/studio-shared';
export declare const getRemotionVersion: () => any;
export declare const isUpdateAvailableWithTimeout: (remotionRoot: string, logLevel: LogLevel) => Promise<UpdateAvailableResponse>;
@@ -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.isUpdateAvailableWithTimeout = exports.getRemotionVersion = void 0;
const semver_1 = __importDefault(require("semver"));
const get_latest_remotion_version_1 = require("../get-latest-remotion-version");
const get_package_manager_1 = require("./get-package-manager");
const isUpdateAvailable = async ({ remotionRoot, currentVersion, logLevel, }) => {
const latest = await (0, get_latest_remotion_version_1.getLatestRemotionVersion)();
const pkgManager = (0, get_package_manager_1.getPackageManager)({
remotionRoot,
packageManager: undefined,
dirUp: 0,
logLevel,
});
return {
updateAvailable: semver_1.default.lt(currentVersion, latest),
currentVersion,
latestVersion: latest,
timedOut: false,
packageManager: pkgManager === 'unknown' ? 'unknown' : pkgManager.manager,
};
};
const getRemotionVersion = () => {
// careful when refactoring this file, path must be adjusted
const packageJson = require('../../package.json');
const { version } = packageJson;
return version;
};
exports.getRemotionVersion = getRemotionVersion;
const isUpdateAvailableWithTimeout = (remotionRoot, logLevel) => {
const version = (0, exports.getRemotionVersion)();
const threeSecTimeout = new Promise((resolve) => {
const pkgManager = (0, get_package_manager_1.getPackageManager)({
remotionRoot,
packageManager: undefined,
dirUp: 0,
logLevel,
});
setTimeout(() => {
resolve({
currentVersion: version,
latestVersion: version,
updateAvailable: false,
timedOut: true,
packageManager: pkgManager === 'unknown' ? 'unknown' : pkgManager.manager,
});
}, 3000);
});
return Promise.race([
threeSecTimeout,
isUpdateAvailable({ remotionRoot, currentVersion: version, logLevel }),
]);
};
exports.isUpdateAvailableWithTimeout = isUpdateAvailableWithTimeout;