remotion
remotion copied to clipboard
More JS runtime support (Deno, Bun)
Asking for Deno support is new "asking for typescript support". So I've done it.
deno eval 'import * as x from "https://dev.jspm.io/remotion"; console.log(x)'
example of the expected result:
$ deno eval 'import * as x from "https://dev.jspm.io/lodash"; console.log(x)'
Module { default: [Function: lodash], [Symbol(Symbol.toStringTag)]: "Module" }
actual result:
$ deno eval 'import * as x from "https://dev.jspm.io/remotion"; console.log(x)'
error: Uncaught TypeError: Cannot read property 'ReactCurrentDispatcher' of undefined
var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
^
at https://dev.jspm.io/npm:[email protected]/cjs/react-jsx-runtime.development.dew.js:323:57
at dew (https://dev.jspm.io/npm:[email protected]/cjs/react-jsx-runtime.development.dew.js:1196:7)
at dew (https://dev.jspm.io/npm:[email protected]/jsx-runtime.dew.js:8:15)
at dew (https://dev.jspm.io/npm:[email protected]/dist/AbsoluteFill.dew.js:13:25)
at dew (https://dev.jspm.io/npm:[email protected]/dist/index.dew.js:51:16)
at https://dev.jspm.io/remotion:2:16
IssueHunt Summary
Backers (Total: $0.00)
Become a backer now!
Or submit a pull request to get the deposits!
Tips
- Checkout the Issuehunt explorer to discover more funded issues.
- Need some help from other developers? Add your repositories on IssueHunt to raise funds.
Haha, true that!
I have no experience with Deno, but it is super cool. Realistically, it is probably not the top priority for me personally, other more important features are, but if someone wants to pick it up we can consider it!
It seems to be an issue with dev.jspm.io, it can import successfully with jspm.dev which is the new replacement for dev.jspm.io.
$ deno eval 'import * as x from "https://jspm.dev/remotion"; console.log(x)'
Module {
AbsoluteFill: [Function: AbsoluteFill],
Audio: [Function: Audio],
Composition: [Function: Composition],
....
[Symbol(Symbol.toStringTag)]: "Module"
}
@yuta0801 thanks! but this is just one step on the way there :) I created this issue for general Deno support :) but just to continue playing into Deno whack-a-mole I have this:
$ deno eval 'import * as x from "https://jspm.dev/@remotion/renderer"; console.log(x)'
error: Uncaught TypeError: The "original" argument must be of type Function
@jonnyburger has funded $80.00 to this issue.
- Submit pull request via IssueHunt to receive this reward.
- Want to contribute? Chip in to this issue via IssueHunt.
- Checkout the IssueHunt Issue Explorer to see more funded issues.
- Need help from developers? Add your repository on IssueHunt to raise funds.
Added a bounty to whoever can demonstrate Remotion working on Deno!
I don't know how hard it is or what needs to be changed. This is a low priority issue for me, so putting out a small incentive.
Currently there's no way to allow Deno support while still using typescript (More info here: https://github.com/microsoft/TypeScript/issues/37582)
Basically, you cannot have something like:
import {AnyComponent} from './any-component.ts';
Because the TS compiler will just throw: An import path cannot end with a '.ts' extension. Consider importing './any-component' instead.ts(2691
and deno requires the import to specify the file extension.
This is a long going discussion with the TS core team and I don't think they're gonna change any time soon.
One solution would just to migrate totally from typescript to deno. I'm up for this, what you think about it @JonnyBurger ?
I think a complete migration to only Deno is out of the question, with Node being the much bigger platform.
If it is sure that it's not possible to support Node and Deno at the same time with one codebase, I would close this issue as the effort to support Deno is too much. Personally I haven't invested time into it yet, and the priority is rather low, although I see it as a nice feature.
Some thoughts:
- We can get closer to Deno support by removing as many npm dependencies as possible and inlining them into Remotion. This makes it easier to replace calls to native Node.JS modules (path, os, child_process etc.)
- Besides Deno, there's also https://bun.sh/ which I also find very promising. So it's not just about supporting one more runtime, but being runtime-agnostic possibly.
Remotion does not currently run on Bun at the moment at least because of child_process
.
I'm continuing to eliminate dependencies and try to avoid runtime-specific APIs so when the new JS runtimes mature, we are ready!
This is a long going discussion with the TS core team and I don't think they're gonna change any time soon.
One solution would just to migrate totally from typescript to deno.
Which is odd, since one of Deno's earliest goals was to have built-in TS support, so to be Deno-compat is to drop TS is... bad DX (& I dislike TS, so JSDoc?). & improved DX is the only reason to support Deno IMHO, because it is not performance AFAIK.
Bun on the other hand, has both improved DX & better performance for short-lived processes.
Either way, once some of the Node-specific calls are handled directly by Remotion, codemods/monkey-patching might be worth exploring, instead of rewriting.
Interesting: https://deno.com/blog/changes
I've hacked around and it looks like human-signals is a hurdle. I think updating it to version 3 would resolve the problem though. I got this far (not there yet, but a start!):
deno eval 'import * as x from "https://jspm.dev/@remotion/renderer"; console.log(x)'
Module {
ErrorWithStackFrame: [Function: ErrorWithStackFrame],
RenderInternals: {
ensureLocalBrowser: [AsyncFunction: ensureLocalBrowser],
ffmpegHasFeature: [AsyncFunction: ffmpegHasFeature],
getActualConcurrency: [Function: getActualConcurrency],
validateFfmpeg: [AsyncFunction: validateFfmpeg],
serveStatic: [AsyncFunction: serveStatic],
validateEvenDimensionsWithCodec: [Function: validateEvenDimensionsWithCodec],
getFileExtensionFromCodec: [Function: getFileExtensionFromCodec],
tmpDir: [Function: tmpDir],
deleteDirectory: [AsyncFunction: deleteDirectory],
isServeUrl: [Function: isServeUrl],
ensureOutputDirectory: [Function: ensureOutputDirectory],
getRealFrameRange: [Function: getRealFrameRange],
validatePuppeteerTimeout: [Function: validatePuppeteerTimeout],
downloadFile: [Function: downloadFile],
killAllBrowsers: [AsyncFunction: killAllBrowsers],
parseStack: [Function: parseStack],
symbolicateError: [AsyncFunction: symbolicateError],
SymbolicateableError: [Function: SymbolicateableError],
getFramesToRender: [Function: getFramesToRender],
getExtensionOfFilename: [Function: getExtensionOfFilename],
getDesiredPort: [Function: getDesiredPort],
isPathInside: [Function: isPathInside],
execa: [Function: execa] { sync: [Function], command: [Function], commandSync: [Function], node: [Function] },
registerErrorSymbolicationLock: [Function: registerErrorSymbolicationLock],
unlockErrorSymbolicationLock: [Function: unlockErrorSymbolicationLock],
canUseParallelEncoding: [Function: canUseParallelEncoding],
mimeContentType: [Function: mimeContentType],
mimeLookup: [Function: mimeLookup],
validateConcurrency: [Function: validateConcurrency],
validPixelFormats: [
"yuv420p",
"yuva420p",
"yuv422p",
"yuv444p",
"yuv420p10le",
"yuv422p10le",
"yuv444p10le",
"yuva444p10le"
],
DEFAULT_BROWSER: "chrome",
validateFrameRange: [Function: validateFrameRange],
DEFAULT_OPENGL_RENDERER: null,
validateOpenGlRenderer: [Function: validateOpenGlRenderer],
validImageFormats: [ "png", "jpeg", "none" ],
validCodecs: [
"h264", "h265",
"vp8", "vp9",
"mp3", "aac",
"wav", "prores",
"h264-mkv", "gif"
],
DEFAULT_PIXEL_FORMAT: "yuv420p",
validateQuality: [Function: validateQuality],
validateFrame: [Function: validateFrame],
DEFAULT_TIMEOUT: 30000,
DEFAULT_CODEC: "h264",
isAudioCodec: [Function: isAudioCodec],
logLevels: [ "verbose", "info", "warn", "error" ],
isEqualOrBelowLogLevel: [Function: isEqualOrBelowLogLevel],
isValidLogLevel: [Function: isValidLogLevel],
perf: {
startPerfMeasure: [Function: startPerfMeasure],
stopPerfMeasure: [Function: stopPerfMeasure],
logPerf: [Function: logPerf]
},
makeDownloadMap: [Function: makeDownloadMap],
cleanDownloadMap: [AsyncFunction: cleanDownloadMap],
convertToPositiveFrameIndex: [Function: convertToPositiveFrameIndex],
validateBitrate: [Function: validateBitrate],
getFfmpegVersion: [AsyncFunction: getFfmpegVersion]
},
__esModule: true,
combineVideos: [AsyncFunction: combineVideos],
default: {
combineVideos: [Getter],
ErrorWithStackFrame: [Getter],
getCompositions: [Getter],
validateSelectedPixelFormatAndImageFormatCombination: [Getter],
validImageFormats: [Getter],
makeCancelSignal: [Getter],
openBrowser: [Getter],
renderFrames: [Getter],
renderMedia: [Getter],
renderStill: [Getter],
stitchFramesToVideo: [Getter],
validateOutputFilename: [Getter],
RenderInternals: {
ensureLocalBrowser: [AsyncFunction: ensureLocalBrowser],
ffmpegHasFeature: [AsyncFunction: ffmpegHasFeature],
getActualConcurrency: [Function: getActualConcurrency],
validateFfmpeg: [AsyncFunction: validateFfmpeg],
serveStatic: [AsyncFunction: serveStatic],
validateEvenDimensionsWithCodec: [Function: validateEvenDimensionsWithCodec],
getFileExtensionFromCodec: [Function: getFileExtensionFromCodec],
tmpDir: [Function: tmpDir],
deleteDirectory: [AsyncFunction: deleteDirectory],
isServeUrl: [Function: isServeUrl],
ensureOutputDirectory: [Function: ensureOutputDirectory],
getRealFrameRange: [Function: getRealFrameRange],
validatePuppeteerTimeout: [Function: validatePuppeteerTimeout],
downloadFile: [Function: downloadFile],
killAllBrowsers: [AsyncFunction: killAllBrowsers],
parseStack: [Function: parseStack],
symbolicateError: [AsyncFunction: symbolicateError],
SymbolicateableError: [Function: SymbolicateableError],
getFramesToRender: [Function: getFramesToRender],
getExtensionOfFilename: [Function: getExtensionOfFilename],
getDesiredPort: [Function: getDesiredPort],
isPathInside: [Function: isPathInside],
execa: [Function: execa] {
sync: [Function],
command: [Function],
commandSync: [Function],
node: [Function]
},
registerErrorSymbolicationLock: [Function: registerErrorSymbolicationLock],
unlockErrorSymbolicationLock: [Function: unlockErrorSymbolicationLock],
canUseParallelEncoding: [Function: canUseParallelEncoding],
mimeContentType: [Function: mimeContentType],
mimeLookup: [Function: mimeLookup],
validateConcurrency: [Function: validateConcurrency],
validPixelFormats: [
"yuv420p",
"yuva420p",
"yuv422p",
"yuv444p",
"yuv420p10le",
"yuv422p10le",
"yuv444p10le",
"yuva444p10le"
],
DEFAULT_BROWSER: "chrome",
validateFrameRange: [Function: validateFrameRange],
DEFAULT_OPENGL_RENDERER: null,
validateOpenGlRenderer: [Function: validateOpenGlRenderer],
validImageFormats: [ "png", "jpeg", "none" ],
validCodecs: [
"h264", "h265",
"vp8", "vp9",
"mp3", "aac",
"wav", "prores",
"h264-mkv", "gif"
],
DEFAULT_PIXEL_FORMAT: "yuv420p",
validateQuality: [Function: validateQuality],
validateFrame: [Function: validateFrame],
DEFAULT_TIMEOUT: 30000,
DEFAULT_CODEC: "h264",
isAudioCodec: [Function: isAudioCodec],
logLevels: [ "verbose", "info", "warn", "error" ],
isEqualOrBelowLogLevel: [Function: isEqualOrBelowLogLevel],
isValidLogLevel: [Function: isValidLogLevel],
perf: {
startPerfMeasure: [Function: startPerfMeasure],
stopPerfMeasure: [Function: stopPerfMeasure],
logPerf: [Function: logPerf]
},
makeDownloadMap: [Function: makeDownloadMap],
cleanDownloadMap: [AsyncFunction: cleanDownloadMap],
convertToPositiveFrameIndex: [Function: convertToPositiveFrameIndex],
validateBitrate: [Function: validateBitrate],
getFfmpegVersion: [AsyncFunction: getFfmpegVersion]
}
},
getCompositions: [AsyncFunction: getCompositions],
makeCancelSignal: [Function: makeCancelSignal],
openBrowser: [AsyncFunction: openBrowser],
renderFrames: [Function: renderFrames],
renderMedia: [Function: renderMedia],
renderStill: [Function: renderStill],
stitchFramesToVideo: [AsyncFunction: stitchFramesToVideo],
validImageFormats: [ "png", "jpeg", "none" ],
validateOutputFilename: [Function: validateOutputFilename],
validateSelectedPixelFormatAndImageFormatCombination: [Function: validateSelectedPixelFormatAndImageFormatCombination]
}
The way I was able to do this is by removing the code that won't run in human-signals forcefully. To reproduce:
deno eval 'import * as x from "https://jspm.dev/@remotion/renderer"; console.log(x)' # Crash
cp 40b93ab56b85efc2352b611ed158d81be50f4235bcd005ba64f9c56115ffaee2.txt ~/.cache/deno/deps/https/jspm.dev/40b93ab56b85efc2352b611ed158d81be50f4235bcd005ba64f9c56115ffaee2
deno eval 'import * as x from "https://jspm.dev/@remotion/renderer"; console.log(x)' # Success!
File to copy: 40b93ab56b85efc2352b611ed158d81be50f4235bcd005ba64f9c56115ffaee2.txt
@JonnyBurger is that bounty still on? :)
@ndren Very nice! It certainly is, if it is hard, we are even willing to increase!
human-signals
seems to be a dependency of execa
, maybe it has to be abstracted to not rely on child_process, since this is a Node.JS only module, right?
@JonnyBurger Maybe? It looks like deno handles it fine:
import {execa} from 'npm:execa';
const {stdout} = await execa('id');
console.log(stdout);
deno run test.ts
works as expected. So the issue does not look like it has to do with spawning processes by itself.
The error is the following, which I was not able to figure out fully (my patch throws away the signal number code). I read through https://jspm.dev/npm:[email protected]!cjs, though nothing popped out as the source of the error.
deno eval 'import * as x from "https://jspm.dev/@remotion/renderer"; console.log(x)'
error: Uncaught TypeError: Cannot destructure property 'signals' of '_os.constants' as it is undefined.
signals: {
^
at normalizeSignal (https://jspm.dev/npm:[email protected]!cjs:297:14)
at Array.map (<anonymous>)
at getSignals (https://jspm.dev/npm:[email protected]!cjs:282:58)
at getSignalsByName (https://jspm.dev/npm:[email protected]!cjs:324:43)
at https://jspm.dev/npm:[email protected]!cjs:350:23
I see, require("os").constants
does not work in Deno.
How does that work in Deno? Can it be patched or does the module need to be replaced?
The following works for me: (deno run demo.js
)
import * as os from "https://deno.land/[email protected]/node/os.ts";
console.log(os.constants)
I see, we could inline that, but how could we make that import so it works on both Node and Deno?
I did a quick test myself:
import {cli} from 'npm:@remotion/cli';
cli();
and tried to run it with
deno run --allow-read --allow-env --allow-sys --allow-run --allow-write --allow-net src/deno.ts preview src/index.ts
and I got the following:
Updated env file /Users/jonathanburger/remotion/packages/example/.env
Updated env file /Users/jonathanburger/remotion/packages/example/.env
Updated env file /Users/jonathanburger/remotion/packages/example/.env
Updated env file /Users/jonathanburger/remotion/packages/example/.env
An error occurred:
Error: No available ports found
at getPort (file:///Users/jonathanburger/Library/Caches/deno/npm/registry.npmjs.org/@remotion/renderer/3.3.0/dist/get-port.js:36:11)
at async getDesiredPortUnlimited (file:///Users/jonathanburger/Library/Caches/deno/npm/registry.npmjs.org/@remotion/renderer/3.3.0/dist/get-port.js:43:24)
So it seems like there are two more problems, fs.watchFile
being implemented differently, and the net
module working differently than in Node. We have to get over those differences as well.
Latest update on Bun - child_process support is supposed to be coming soon, once it gets released, I'll give it a try as well.
A bit hacky, but I suppose you always have this:
navigator.userAgent.substring(0,4) == "Deno"
@ndren But we cannot use this for import
statements, very clunky. We need a clean solution
A bit hacky, but I suppose you always have this:
navigator.userAgent.substring(0,4) == "Deno"
I'm pretty sure you just do typeof Deno
:)
Hi there, we removed the bounty because it came out that implementing this feature is more complex than we initially thought. We don’t expect a contributor to implement it. Nevertheless, any help is appreciated. We will put the bounty on another issue.
@mehmetademi has cancelled @jonnyburger's funding for this issue.(Cancelled amount: $80.00) See it on IssueHunt
I'm occasionally checking Bun support, the current blocker is support for fs.watchFile
. But Bun is looking promising!
Some progress towards Bun support is achieved, current blocker is https://github.com/oven-sh/bun/issues/4316
We don't plan on supporting Deno. In our opinion, the benefits over Node are only minor so that it is not worth the effort to us.
I think, this issue can be closed as Bun is supported now.
Initially, this issue was meant to support Bun as a runtime. Currently, we support it as a package manager. We are working on supporting Bun as a runtime. See https://remotion.dev/bun for an overview.
With Bun 1.0.3, at least issues 2 + 3 seem to be resolved!
Will test 1 + 4 too and then we can indeed call Bun supported :)