Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deno support #250

Open
maxdumas opened this issue Dec 13, 2019 · 32 comments
Open

Deno support #250

maxdumas opened this issue Dec 13, 2019 · 32 comments

Comments

@maxdumas
Copy link

maxdumas commented Dec 13, 2019

deno seems like a great environment for running ink, as it aims to be a simple, secure, and fast way to ship JavaScript executables to users while being extremely light in setup for developers. Looking at ink and putzing around a bit (I'm admittedly pretty new to deno and ink), it seems that there are a few issues with its current setup that make it difficult to use with deno:

  1. ink does not publish a version of itself as a bundled ESM. deno requires that packages be consumed as ESM, and that ESM would likely have to have bundled with it all of inks dependencies. To fix that would require introducing an ESM bundling step as part of the build for the package, using something like rollup. I took a quick stab at doing that in a fork, which can be found here. The fork successfully bundles but still does not work in deno.
  2. Once the ESM bundle is being published as part of the build, the simplest way to vend that bundle would be to just point to the generated bundle using the "module" field in package.json. Then package managers and CDNs that vend ES6 modules, like pika.dev, could pick up ink and serve the ESM bundle. Note how currently pika.dev has ink listed but cannot serve it because there is no "module" field in the package.json.
  3. Any dependencies that ink or its npm dependencies have on the Node standard library or Node-specific runtime behavior may need to be refactored to accommodate a deno environment. See here for the deno standard library. It does deviate somewhat from the Node standard library. I have not taken the time to actually identify which libraries or parts of ink might fall under this category, but this could be the largest chunk of work to provide deno support and will potentially involve updating multiple libraries.

Have any of the library maintainers considered deno support? Points (1) and (2) seem valuable in general, even outside of a goal of deno support, as moving the JS ecosystem towards an ESM-first world will make a big impact in the usability of libraries in a more progressive, lightweight manner. However, with deno, getting up and started with ink could be as simple as (sloppily adapting the Counter demo):

import React from 'https://cdn.pika.dev/react/latest';
import {render, Color} from 'https://cdn.pika.dev/ink/latest';

const Counter = () => {
	const [counter, setCounter] = React.useState(0);

	React.useEffect(() => {
		const timer = setInterval(() => {
			setCounter(prevCounter => prevCounter + 1);
		}, 100);

		return () => {
			clearInterval(timer);
		};
	});

	return (
		<Color green>
			{counter} tests passed
		</Color>
	);
};

render(<Counter/>);

Note that the code hasn't changed much, but the real difference is that this one file would be the entire project for getting started with an ink app. No package.json, no Babel, no Typescript compiler, no need for associated code generators like create-ink-app. It would allow new developers and folks just experimenting to use ink much more quickly and easily.

Comments? Thoughts?

@sindresorhus
Copy link
Collaborator

  1. We'll just wait until we can target ESM in Node.js, which should be possible soon.
  2. We would rather use the "type": "module" field: https://nodejs.org/api/esm.html#esm_code_package_json_code_code_type_code_field
  3. This would be very large undertaking.

Have any of the library maintainers considered deno support?

I've been passively following the Deno development, and while it has great potential and has done many things right (Rust, TS, security), it's still very immature project. I don't think it makes sense at this point to even consider adding Deno support to large complex Node.js modules like Ink.

If you really want to see this happen, I would recommend forking and trying to implement support for it yourself. Could be a fun exercise. But don't expect anything to be merged upstream anytime soon.

No package.json, no Babel, no Typescript compiler, no need for associated code generators like create-ink-app.

The only thing you need is a package.json, and it can be small. Babel/TypeScript/etc are not required.

@maxdumas
Copy link
Author

Thanks for the response @sindresorhus! Those are definitely fair points, especially regarding (3). I may continue exploring the idea in my own fork. If I am able to get anywhere then I'll definitely keep you posted.

@songkeys
Copy link

Hi! It's been a year. Deno has published its official 1.x version as well. Since v1.7, we can even compile a deno project into a single binary file. I'd be much happy to see ink supporting it! Any plan so far?

@aadamsx
Copy link

aadamsx commented Mar 26, 2021

Any word on if this is ever going to happen? We are at Deno 1.8 now and Deno is heavly used on the command line.

@vadimdemedes
Copy link
Owner

Hey, I'm currently spending all my free time on Lotus, so unfortunately I can't promise anything. As Sindre suggested, feel free to experiment with it by forking Ink and let's see what we can do to support it out of the box if possible.

@vadimdemedes
Copy link
Owner

Going to re-open this issue to increase visibility.

@brad-jones
Copy link

I'd love Deno support too, this would be great to pair with https://cliffy.io/

I gave it a go with the following code.

import { render, Text } from "https://esm.sh/[email protected]";
import React from "https://esm.sh/[email protected]";

export const Example = () => (
  <>
    <Text color="green">I am green</Text>
    <Text color="black" backgroundColor="white">
      I am black on white
    </Text>
    <Text color="#ffffff">I am white</Text>
    <Text bold>I am bold</Text>
    <Text italic>I am italic</Text>
    <Text underline>I am underline</Text>
    <Text strikethrough>I am strikethrough</Text>
    <Text inverse>I am inversed</Text>
  </>
);

render(<Example />);

But ultimately it failed with:

This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills
This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills
error: Uncaught TypeError: _ is not a function
    at https://esm.sh/v90/[email protected]/deno/restore-cursor.js:2:1092
    at https://esm.sh/v90/[email protected]/deno/restore-cursor.js:2:700
    at https://esm.sh/v90/[email protected]/deno/restore-cursor.js:2:1173

I haven't bothered looking at it any further. I imagine it's likely to be very long & dark rabbit hole for someone to go down.

Also just had a look at https://bun.sh & boy is it fast. It seems to be striving to have better node compat than deno too so it might one day be easier to make this work on bun than deno. Although granted today bun is really just an experiment not a useable bit of software IMO. Defo one to keep an eye on though.

@clo4
Copy link

clo4 commented Aug 14, 2022

A couple months ago I put in about an hour into making Ink work with Deno, did get it working albeit with the same warning. Screenshot below has two of the builtin demos and the code @brad-jones used above:

Demo of Ink working in Deno

Ultimately didn't end up pursuing it further, but it's definitely possible to do!

@Morantron
Copy link

apparently deno is improving node/npm compatiblity, so it might just work out of the box in the following months https://deno.com/blog/changes#compatibility-with-node-and-npm

@clo4
Copy link

clo4 commented Aug 25, 2022

Deno 1.25.0 is out, it's now possible to use NPM module specifiers (experimental).

There's still React's obnoxious rAF warning though.

$ deno run --unstable --allow-all ink-test.jsx
// required because ink does some trickery with node's Console object
import console from "https://deno.land/[email protected]/node/console.ts";
window.console = console;

import React from "npm:react";
import { render, Box, Text, useFocus } from "npm:ink";

const Focus = () => (
  <Box flexDirection="column" padding={1}>
    <Box marginBottom={1}>
      <Text>
        Press Tab to focus next element, Shift+Tab to focus previous element,
        Esc to reset focus.
      </Text>
    </Box>
    <Item label="First" />
    <Item label="Second" />
    <Item label="Third" />
  </Box>
);

const Item = ({label}) => {
  const {isFocused} = useFocus();
  return (
    <Text>
      {label} {isFocused && <Text color="green">(focused)</Text>}
    </Text>
  );
};

render(<Focus />)

Edit: raf polyfill - make sure this is the first import!

raf.js

// based on https://gist.github.com/paulirish/1579671

let lastTime = 0;

window.requestAnimationFrame = function (callback, _element) {
  let currTime = new Date().getTime();
  let timeToCall = Math.max(0, 16 - (currTime - lastTime));
  let id = setTimeout(() => callback(currTime + timeToCall), timeToCall);
  lastTime = currTime + timeToCall;
  return id;
};

window.cancelAnimationFrame = function (id) {
  clearTimeout(id);
};

ink-test.jsx

import "./raf.js";

import console from "https://deno.land/[email protected]/node/console.ts";
window.console = console;

import React from "npm:react";
import { render, Box, Text, useFocus } from "npm:ink";

// ...

@airtonix
Copy link

Protip: don't use react 18 with deno and ink.

import_map.json

{
  "imports": {
    "react": "npm:react@17",
    "ink": "npm:ink",
    "cli-spinners": "npm:cli-spinners"
  }
}

You'll just end up with lots of errors around either:

  • can't find types
  • something something something useContext
  • something something something null of useState
  • etc etc etc, blah-blah-blah: boring-non-working-ink-in-deno-things

@vadimdemedes vadimdemedes changed the title deno support? Deno support Apr 24, 2023
@pethin
Copy link

pethin commented Aug 16, 2023

With the latest Deno. And the above suggestion. This seems to work without the --unstable flag.

$ deno run --allow-all ink-test.jsx
// required because ink does some trickery with node's Console object
import console from "node:console";
window.console = console;

import React from "https://esm.sh/react@18";
import { render, Box, Text, useFocus } from "https://esm.sh/ink@4";

const Focus = () => (
  <Box flexDirection="column" padding={1}>
    <Box marginBottom={1}>
      <Text>
        Press Tab to focus next element, Shift+Tab to focus previous element,
        Esc to reset focus.
      </Text>
    </Box>
    <Item label="First" />
    <Item label="Second" />
    <Item label="Third" />
  </Box>
);

const Item = ({label}) => {
  const {isFocused} = useFocus();
  return (
    <Text>
      {label} {isFocused && <Text color="green">(focused)</Text>}
    </Text>
  );
};

render(<Focus />)

@lanceturbes
Copy link

lanceturbes commented Sep 30, 2023

Copying above instructions hasn't worked for me when making use of useStdin() or useFocus() inside an ink app with deno 1.37.0, tested with Windows/Mac/Linux.

Getting a stdin.ref is not a function error.

@lanceturbes
Copy link

lanceturbes commented Sep 30, 2023

Downgrading to ink 4.4.0 (vs 4.4.1) seems to remove the stdin.ref is not a function error for me.

App does not exit gracefully with CTRL+C, though.

@vadimdemedes
Copy link
Owner

https://nodejs.org/dist/latest-v20.x/docs/api/net.html#socketref must be missing in Deno then.

@ulken
Copy link

ulken commented Nov 11, 2023

https://nodejs.org/dist/latest-v20.x/docs/api/net.html#socketref must be missing in Deno then.

https://deno.land/[email protected]/node/net.ts?s=Socket&p=prototype.ref seems to suggest otherwise? I might be mistaken, though.

@ulken
Copy link

ulken commented Nov 15, 2023

@vadimdemedes you are right, it's indeed missing. I don't know what the docs above are referring to. Opened an issue.

@hugojosefson
Copy link

https://deno.land/[email protected]/node/net.ts?s=Socket&p=prototype.ref seems to suggest otherwise? I might be mistaken, though.

That looks like an older version of the standard library. I'm not sure where that code went, since the file does not exist in the latest version of the standard lib, or if it was removed for some reason.

I'm quite sure the standard library is not part of the Deno runtime environment, and I think it's not part of what's included in the import ... from "npm:..." support in Deno, but I could be mistaken :)

Hopefully you will find help through the issue you filed, @ulken!

@ulken
Copy link

ulken commented Nov 16, 2023

@hugojosefson you're absolutely right. Silly me.

I think Node API Compatibility List is a better resource.

Under node:tty one can read:

Missing ReadStream and WriteStream implementation.

ReadStream is essential here.

@mabasic
Copy link

mabasic commented Feb 10, 2024

I have managed to use this library with Deno 1.40.4. I could not get useInput to work because of the difference between Deno and Node with stdin, but I got it working by using the "input" from Cliffy (deno library).

@cowboyd
Copy link

cowboyd commented Mar 23, 2024

@mabasic I was able to run the demo as well with the latest version of Deno, but among other things, hitting CTRL-C didn't work. Do you have a snippet or a gist of what you did to get keyboard input to function?

@mabasic
Copy link

mabasic commented Mar 23, 2024

@mabasic I was able to run the demo as well with the latest version of Deno, but among other things, hitting CTRL-C didn't work. Do you have a snippet or a gist of what you did to get keyboard input to function?

There is a trick which I have found somewhere in the issues ... I'll try to find it in my code and send you the example

@mabasic
Copy link

mabasic commented Mar 24, 2024

@cowboyd

  const handleKeyPresses = useCallback(async (
    event: KeyPressEvent,
  ) => {
    if (event.key === "c" && event.ctrlKey) {
      exit(); // exits ink
      Deno.exit(0); // exits deno process
    }
  }, []);

  useEffect(() => {
    keypress().addEventListener("keydown", handleKeyPresses);

    return () => {
      keypress().removeEventListener("keydown", handleKeyPresses);
    };
  }, [handleKeyPresses]);

@mbostwick
Copy link

mbostwick commented Apr 3, 2024

You may want to also consider supporting https://bun.sh/ at the same time, in theory its suppose to be a light replacement, but I confirmed that the display is not getting the inputs when running in bun

https://www.builder.io/blog/bun-vs-node-js has more about the whole bun and other run times ...

I did do a deep dive to see what I could do, but it looks like the biggest issue is the way use input is hooked into the context change detection from process.stdin and the event call backs around it. There are also some defaults of process. but giving up on it now ..

@jlandowner
Copy link

I arrived at here for trying to create tui app by Deno.
With deno v1.43.1, I found the @pethin code works without node:console and CTRL-C also works without any tricks🙌

import React from "npm:react";
import { Box, render, Text, useFocus } from "npm:ink";

const Focus = () => (
  <Box flexDirection="column" padding={1}>
    <Box marginBottom={1}>
      <Text>
        Press Tab to focus next element, Shift+Tab to focus previous element,
        Esc to reset focus.
      </Text>
    </Box>
    <Item label="First" />
    <Item label="Second" />
    <Item label="Third" />
  </Box>
);

const Item = ({ label }) => {
  const { isFocused } = useFocus();
  return (
    <Text>
      {label} {isFocused && <Text color="green">(focused)</Text>}
    </Text>
  );
};

render(<Focus />);
スクリーンショット 2024-05-07 20 38 17

@foster-hangdaan
Copy link

I am on Deno v1.43.3 and my TUI app is using Ink v5.0.0 without any hacks.

My only gripe is my app now requires certain read and env permissions after installing the react and ink packages. The app requires these permissions even after compiling the executable with deno compile. More specifically, it requires read access to the current working directory and .cache/deno/npm/registry.npmjs.org/yoga-wasm-web/0.3.3/dist/yoga.wasm; as well as access to all environment variables. Does anyone know of a way around this? I'd rather not grant all permissions by supplying -A as that defeats the purpose of Deno's sandboxing capability.

@awildeep
Copy link

+1 for bun support as well. As a dev tool it is working for my team on production (though we do compile to node for now).

Initial testing confirms previous post issues about input as a primary issue.

@cowboyd
Copy link

cowboyd commented Nov 14, 2024

Does anyone know of a way around this? I'd rather not grant all permissions by supplying -A as that defeats the purpose of Deno's sandboxing capability.

@foster-hangdaan You can use very specific permissions with --allow-env and --allow-read even to the level of specifying which environment variables are allowed, and which files and directories can be read. You may want to use an --include option to specifically include the wasm module into the executable.

@robertpkoenig
Copy link

robertpkoenig commented Dec 9, 2024

In my small test of ink with Deno 2, using this code, it works. Compiled to a binary, and that works too.

Just a small word of caution. I got an error "Objects are not valid as a React child" when I ran the code as written in the example linked above. To get around that error, I specified the version of React and Ink (note that @version suffix):

import React, {useState, useEffect} from 'npm:react@18';
import {render, Text} from 'npm:ink@5';

@yedhink
Copy link

yedhink commented Feb 13, 2025

Just a small word of caution. I got an error "Objects are not valid as a React child" when I ran the code as written in the example linked above. To get around that error, I specified the version of React and Ink (note that @Version suffix):

Please note that this will only work for React versions upto 18. If we specify React version 19, then it fails with same error. Probably because of #688.

@citypaul
Copy link

citypaul commented Mar 1, 2025

Just wanted to second what @yedhink has said above - react 18 is working in deno 2 for me, but react 19 fails like this:

❯ deno run --allow-read --allow-env --allow-run main.jsx
error: Uncaught (in promise) Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, key, props, _owner, _store}). If you meant to render a collection of children, use an array instead.
    at throwOnInvalidObjectType (file:///Users/paulhammond/Library/Caches/deno/npm/registry.npmjs.org/react-reconciler/0.29.2/cjs/react-reconciler.development.js:4279:9)
    at reconcileChildFibers (file:///Users/paulhammond/Library/Caches/deno/npm/registry.npmjs.org/react-reconciler/0.29.2/cjs/react-reconciler.development.js:5220:7)
    at reconcileChildren (file:///Users/paulhammond/Library/Caches/deno/npm/registry.npmjs.org/react-reconciler/0.29.2/cjs/react-reconciler.development.js:10366:28)
    at updateHostRoot (file:///Users/paulhammond/Library/Caches/deno/npm/registry.npmjs.org/react-reconciler/0.29.2/cjs/react-reconciler.development.js:11085:5)
    at beginWork (file:///Users/paulhammond/Library/Caches/deno/npm/registry.npmjs.org/react-reconciler/0.29.2/cjs/react-reconciler.development.js:12827:14)
    at beginWork$1 (file:///Users/paulhammond/Library/Caches/deno/npm/registry.npmjs.org/react-reconciler/0.29.2/cjs/react-reconciler.development.js:19608:14)
    at performUnitOfWork (file:///Users/paulhammond/Library/Caches/deno/npm/registry.npmjs.org/react-reconciler/0.29.2/cjs/react-reconciler.development.js:18742:12)
    at workLoopSync (file:///Users/paulhammond/Library/Caches/deno/npm/registry.npmjs.org/react-reconciler/0.29.2/cjs/react-reconciler.development.js:18648:5)
    at renderRootSync (file:///Users/paulhammond/Library/Caches/deno/npm/registry.npmjs.org/react-reconciler/0.29.2/cjs/react-reconciler.development.js:18616:7)
    at performSyncWorkOnRoot (file:///Users/paulhammond/Library/Caches/deno/npm/registry.npmjs.org/react-reconciler/0.29.2/cjs/react-reconciler.development.js:18232:20)

@Macil
Copy link

Macil commented Mar 4, 2025

When using Ink with React 18 in Deno, the key prop on any component will be considered a type error in any Typescript components unless you set up the compilerOptions section of deno.json correctly like this:

{
  "imports": {
    "ink": "npm:ink@^5.1.1",
    "react": "npm:react@^18.0.0",
  },
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "react",
    "jsxImportSourceTypes": "npm:@types/react@^18"
  }
}

With that, this works fine:

import { render, Text } from "ink";

const list = ["a", "b"];

render(list.map((item) => <Text key={item}>{item}</Text>));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests