remotion icon indicating copy to clipboard operation
remotion copied to clipboard

More JS runtime support (Deno, Bun)

Open JLarky opened this issue 3 years ago • 24 comments

Issuehunt badges

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

JLarky avatar Feb 11 '21 04:02 JLarky

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!

JonnyBurger avatar Feb 11 '21 17:02 JonnyBurger

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 avatar Feb 12 '21 21:02 yuta0801

@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

JLarky avatar Feb 13 '21 02:02 JLarky

@jonnyburger has funded $80.00 to this issue.


issuehunt-oss[bot] avatar Apr 25 '21 13:04 issuehunt-oss[bot]

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.

JonnyBurger avatar Apr 25 '21 13:04 JonnyBurger

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 ?

rafaelramalho19 avatar Dec 01 '21 03:12 rafaelramalho19

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.

JonnyBurger avatar Dec 04 '21 11:12 JonnyBurger

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.

JonnyBurger avatar Jun 29 '22 07:06 JonnyBurger

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!

JonnyBurger avatar Jul 07 '22 06:07 JonnyBurger

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.

tomByrer avatar Jul 24 '22 05:07 tomByrer

Interesting: https://deno.com/blog/changes

JonnyBurger avatar Aug 15 '22 12:08 JonnyBurger

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 avatar Nov 14 '22 16:11 ndren

@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 avatar Nov 14 '22 16:11 JonnyBurger

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

ndren avatar Nov 14 '22 16:11 ndren

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

ndren avatar Nov 14 '22 16:11 ndren

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?

JonnyBurger avatar Nov 15 '22 10:11 JonnyBurger

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)

ndren avatar Nov 15 '22 16:11 ndren

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.

JonnyBurger avatar Nov 16 '22 10:11 JonnyBurger

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.

JonnyBurger avatar Nov 16 '22 10:11 JonnyBurger

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

ndren avatar Nov 23 '22 07:11 ndren

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

JonnyBurger avatar Nov 28 '22 10:11 JonnyBurger

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

JLarky avatar Nov 28 '22 21:11 JLarky

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 avatar Dec 08 '22 09:12 MehmetAdemi

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

issuehunt-oss[bot] avatar Dec 08 '22 09:12 issuehunt-oss[bot]

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

JonnyBurger avatar Aug 08 '23 13:08 JonnyBurger

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

JonnyBurger avatar Aug 25 '23 09:08 JonnyBurger

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.

JonnyBurger avatar Aug 25 '23 09:08 JonnyBurger

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

ken0x0a avatar Sep 23 '23 09:09 ken0x0a

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.

MehmetAdemi avatar Sep 23 '23 09:09 MehmetAdemi

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

image

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

JonnyBurger avatar Sep 23 '23 10:09 JonnyBurger