remotion-dev/remotion

Bun Support #50

JLarky posted onGitHub

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:react@17.0.1/cjs/react-jsx-runtime.development.dew.js:323:57
    at dew (https://dev.jspm.io/npm:react@17.0.1/cjs/react-jsx-runtime.development.dew.js:1196:7)
    at dew (https://dev.jspm.io/npm:react@17.0.1/jsx-runtime.dew.js:8:15)
    at dew (https://dev.jspm.io/npm:remotion@1.1.1/dist/AbsoluteFill.dew.js:13:25)
    at dew (https://dev.jspm.io/npm:remotion@1.1.1/dist/index.dew.js:51:16)
    at https://dev.jspm.io/remotion:2:16

@jonnyburger has funded $80.00 to this issue.


posted by issuehunt-app[bot] almost 4 years ago

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.

posted by JonnyBurger almost 4 years ago

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 ?

posted by rafaelramalho19 over 3 years ago

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.

posted by JonnyBurger over 3 years ago

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.
posted by JonnyBurger over 2 years ago

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!

posted by JonnyBurger over 2 years ago

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.

posted by tomByrer over 2 years ago
posted by JonnyBurger over 2 years ago

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? :)

posted by ndren over 2 years ago

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

posted by JonnyBurger over 2 years ago

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

posted by ndren over 2 years ago

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:human-signals@2.1.0!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:human-signals@2.1.0!cjs:297:14)
    at Array.map (<anonymous>)
    at getSignals (https://jspm.dev/npm:human-signals@2.1.0!cjs:282:58)
    at getSignalsByName (https://jspm.dev/npm:human-signals@2.1.0!cjs:324:43)
    at https://jspm.dev/npm:human-signals@2.1.0!cjs:350:23
posted by ndren over 2 years ago

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?

posted by JonnyBurger over 2 years ago

The following works for me: (deno run demo.js)

import * as os from "https://deno.land/std@0.42.0/node/os.ts";
console.log(os.constants)
posted by ndren over 2 years ago

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.

posted by JonnyBurger over 2 years ago

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.

posted by JonnyBurger over 2 years ago

A bit hacky, but I suppose you always have this: navigator.userAgent.substring(0,4) == "Deno"

posted by ndren over 2 years ago

@ndren But we cannot use this for import statements, very clunky. We need a clean solution

posted by JonnyBurger over 2 years ago

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 :)

posted by JLarky over 2 years ago

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.

posted by MehmetAdemi over 2 years ago

@mehmetademi has cancelled @jonnyburger's funding for this issue.(Cancelled amount: $80.00) See it on IssueHunt

posted by issuehunt-app[bot] over 2 years ago

I'm occasionally checking Bun support, the current blocker is support for fs.watchFile. But Bun is looking promising!

posted by JonnyBurger over 1 year ago

Some progress towards Bun support is achieved, current blocker is https://github.com/oven-sh/bun/issues/4316

posted by JonnyBurger over 1 year ago

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.

posted by JonnyBurger over 1 year ago

I think, this issue can be closed as Bun is supported now.

posted by ken0x0a over 1 year ago

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.

posted by MehmetAdemi over 1 year ago

With Bun 1.0.3, at least issues 2 + 3 seem to be resolved!

<img width="896" alt="image" src="https://github.com/remotion-dev/remotion/assets/1629785/dd31f541-7c8b-4680-bc22-c5a7655c0b58">

Will test 1 + 4 too and then we can indeed call Bun supported :)

posted by JonnyBurger over 1 year ago

then we can indeed call Bun supported :)

Hopefully won't break again after Bun 1.1 🀞

posted by thecmdrunner over 1 year ago

Bun is now mostly supported with minor limitations. Those live under https://remotion.dev/bun.

As mentioned before, regarding the original issue title, we have no plans supporting Deno.

posted by JonnyBurger over 1 year ago

Fund this Issue

$0.00
Funded

Pull requests

Recent activities

mehmetademi cancelled funding 80.00 for  remotion-dev/ remotion#50
over 2 years ago
jonnyburger funded 80.00 for JonnyBurger/remotion# 50
almost 4 years ago