How to use npm module in DENO?
Asked Answered
T

4

106

Deno is super cool. I saw it in the morning and want to migrate to deno now. I was trying to move my existing nodejs script to deno. Can any one help me on how to use npm modules in deno. I need esprima module. This one has the package https://github.com/denoland/deno_third_party/tree/master/node_modules but i am not able to figure out how to use that.

Tarnation answered 15/5, 2020 at 13:58 Comment(2)
deno.land/manual#comparison-to-nodejsCalorifacient
for current info see the manual section specifically about npm+node interop (the section, section 5, was renamed across versions, so if the url changes again just check the section navigation) deno.land/manual@main/nodeToile
B
108

Deno provides a Node Compatibility Library, that allows using some NPM packages that do not use non-polyfilled Node.js APIs.

As of Deno 1.25 there's an experimental NPM support by using npm: specifier

npm:<package-name>[@<version-requirement>][/<sub-path>]
import express from "npm:express";
const app = express();

app.get("/", function (req, res) {
  res.send("Hello World");
});

app.listen(3000);
console.log("listening on http://localhost:3000/");

The --unstable flag is required.

When doing this, no npm install is necessary and no node_modules folder is created


You can also require and installed npm package by using https://deno.land/std/node/module.ts

The following works on deno >= 1.0.0

npm install esprima
import { createRequire } from "https://deno.land/std/node/module.ts";

const require = createRequire(import.meta.url);
const esprima = require("esprima");

const program = 'const answer = 42';
console.log(esprima.tokenize(program))

The above code will use esprima from node_modules/.

To run it, you'll need --allow-read && --allow-env flag

# you can also use --allow-all
deno run --allow-read --allow-env esprima.js

You can restrict it only to node_modules

deno run --allow-read=node_modules esprima.js

Which outputs:

[
 { type: "Keyword", value: "const" },
 { type: "Identifier", value: "answer" },
 { type: "Punctuator", value: "=" },
 { type: "Numeric", value: "42" }
]

Note: many APIs used by std/ are still unstable, so you may need to run it with --unstable flag.


Although since that whole project is written in TypeScript already, and it's not using any dependencies, it will be very easy for them to adapt it to Deno. All they need to do is use .ts extension on their imports. You can also fork the project and do the changes.

// import { CommentHandler } from './comment-handler';
import { CommentHandler } from './comment-handler.ts';
// ...

Once they do, you'll be able to just do:

// Ideally they would issue a tagged release and you'll use that instead of master
import esprima from 'https://raw.githubusercontent.com/jquery/esprima/master/src/esprima.ts';

const program = 'const answer = 42';
console.log(esprima.tokenize(program))

Alternative

You can also use https://jspm.io/ which will convert NPM modules to ES Modules

All modules on npm are converted into ES modules handling full CommonJS compatibility including strict mode conversions.

import esprima from "https://dev.jspm.io/esprima";

const program = 'const answer = 42';
console.log(esprima.tokenize(program))

For packages that use Node.js modules not supported by jspm it will throw an error:

Uncaught Error: Node.js fs module is not supported by jspm core. 
Deno support here is tracking in 
https://github.com/jspm/jspm-core/issues/4, +1's are appreciated!

To polyfill those Node.js APIs you'll have to include std/node.

// import so polyfilled Buffer is exposed                                                                                                  
import "https://deno.land/std/node/module.ts";
import BJSON from 'https://dev.jspm.io/buffer-json';

const str = BJSON.stringify({ buf: Buffer.from('hello') })

console.log(str);
Blocker answered 15/5, 2020 at 14:3 Comment(9)
great, thanks for your effort. tried that getting these kind of errors TS2339 [ERROR]: Property 'dir' does not exist on type 'typeof Deno'.Tarnation
The above snippet works I tested it. what Deno version are you using?Blocker
latest version. 1.0.0 After i used --unstable flag it worked. thanksTarnation
but will that flag causes any issue in production :)Tarnation
You have something else on your code then, because I'm not using --unstable flag. All you have is the snippet I submitted in my answer?Blocker
You're probably using some other import, that's using an unstable API.Blocker
can we add permission --allow-read only the node_modules as it requires only node modules?Tarnation
it seems the solution is not applicable for all modues. I tried to use the same way with mongoose, but got an error related to its dependency.Elfrieda
import * as toxicity from "npm:tensorflow-models/toxicity"; error: Unable to load /Users/username/Library/Caches/deno/npm/registry.npmjs.org/tensorflow-models/0.0.1/toxicity imported from file:///Users/username/test/toxicity.ts Caused by: Is a directory (os error 21) Madai
B
37

Issue

In general, there are two issues with npm packages in Deno:

  1. ES Module (ESM) conformity is not given.
  • Bare imports like import _ from "lodash" don't work - no "magic" node_modules resolution
  • All import specifiers need to include the file extension - .ts,.js etc.
  • CommonJS module system is not usable in Deno
  1. The npm package uses native Node.js builtins like fs or path.

Solutions to issue 1

1.1: Third party modules

The Third Party Modules section is the quickest way to discover compatible packages.

1.2: ESM CDN providers

Also take a look at CDN providers, that can auto-convert npm packages to ES Modules (ESM):

Skypack CDN can deliver auto-converted packages, that e.g. have set a "module" entrypoint in package.json. For TypeScript users: It fetches .d.ts type definitions along with .js files (via X-TypeScript-Types HTTP headers used by Deno).

unpkg.com describes its ?module flag as follows: "Expands all 'bare' import specifiers in JavaScript modules to unpkg URLs. This feature is very experimental".

Esprima does not depend on Node.js builtins, so we can simplify its import by a CDN URL:
import esprima from "https://cdn.skypack.dev/esprima@^4.0.1"; // Option 1: Skypack
import esprima from "https://dev.jspm.io/esprima"; // Option 2: jspm 
// your program
const tokens = esprima.tokenize("const foo = 'bar'"); // works

jspm would be a good choice here - Skypack TS types didn't work for me in this particular case.

1.3: Other approaches

You might also try to import an ESM compatible version directly from repository sources (e.g. an ESM branch). Though for Esprima it won't work because of missing file extensions in code.

Snowpack and jspm stand in for a more manual approach to convert CommonJS → ESM. The rollup plugin @rollup/plugin-commonjs (internally used by Snowpack) is even a more low-level tool.


Solution to issue 2

Deno provides a Node compatibility layer, see Marcos Casagrande's answer. However, not all native Node.js built-ins are fully supported.

As Esprima doesn't rely on Node builtins, you can go with the simpler CDN option.

Bloemfontein answered 7/6, 2020 at 23:7 Comment(1)
CDN provider option 2 import esprima from "https://dev.jspm.io/esprima"; doesn't work for me - deno complains ` failed: 500 Internal Server Error` and indeed visiting dev.jspm.io/esprima gives me Internal error on package lookup.. Luckily Option 1: Skypack works.Ctenoid
E
5

As of version Deno 1.25 (released today) deno is now included with experimental npm support.

// main.ts
import express from "npm:express";
const app = express();

app.get("/", function (req, res) {
  res.send("Hello World");
});

app.listen(3000);
console.log("listening on http://localhost:3000/");

You can now run deno run --unstable --A main.ts and express will be downloaded.

Engler answered 25/8, 2022 at 21:32 Comment(0)
N
1

Starting with v1.15 Deno provides Node compatibility mode that makes it possible to run a subset of programs authored for Node.js directly in Deno. Compatibility mode can be activated by passing --compat flag in CLI.

deno run  --compat --unstable --allow-read test.js

Currently, not all node.js built-in modules are supported and many are partially supported.

The following modules are not yet implemented:

  • cluster, dgram, http2, https, repl, tls, vm, lib
Ninety answered 9/12, 2021 at 10:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.