Skip to content
This repository has been archived by the owner on Sep 21, 2023. It is now read-only.

Rework npm specifiers #689

Closed
137 changes: 76 additions & 61 deletions node/npm_specifiers.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,34 @@
# `npm:` specifiers

Since version 1.28, Deno has native support for importing npm packages. This is
done by importing using `npm:` specifiers.

The way these work is best described with an example that you can run with
`deno run --allow-env`:
done by importing using `npm:` specifiers. For example the following code:

```ts, ignore
import chalk from "npm:chalk@5";

console.log(chalk.green("Hello!"));
```

These npm specifiers have the following format:
import { emojify } from "npm:node-emoji@2";

```ts, ignore
npm:<package-name>[@<version-requirement>][/<sub-path>]
console.log(emojify(":t-rex: :heart: NPM"));
```

Another example with express:

```js, ignore
// main.js
import express from "npm:express@^4.17";
const app = express();

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

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

Then doing the following will start a simple express server:
Can be run with:

```sh
$ deno run -A main.js
listening on http://localhost:3000/
$ deno run main.js
🦖 ❤️ NPM
```

When doing this, no `npm install` is necessary and no `node_modules` folder is
created. These packages are also subject to the same permissions as Deno
applications.
created. These packages are also subject to the same
[permissions](../basics/permissions.md) as other code in Deno.

## npm executable scripts
npm specifiers have the following format:

npm packages with `bin` entries can be executed from the command line without an
`npm install` using a specifier in the following format:

```ts, ignore
npm:<package-name>[@<version-requirement>][/<binary-name>]
```

For example:

```sh
$ deno run --allow-env --allow-read npm:[email protected] Hello there!
______________
< Hello there! >
--------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||

$ deno run --allow-env --allow-read npm:[email protected]/cowthink What to eat?
______________
( What to eat? )
--------------
o ^__^
o (oo)\_______
(__)\ )\/\
||----w |
|| ||
npm:<package-name>[@<version-requirement>][/<sub-path>]
```

For examples with popular libraries, please refer to our
[how-to guides](./how_to_with_npm.md).

## TypeScript types

Many packages ship with types out of the box, you can import those and use them
Expand All @@ -96,6 +48,36 @@ package:
import express from "npm:express@^4.17";
```

### Module resolution

The official TypeScript compiler `tsc` supports different
[moduleResolution](https://www.typescriptlang.org/tsconfig#moduleResolution)
settings. Deno only supports the modern `node16` resolution. Unfortunately many
NPM packages fail to correctly provide types under node16 module resolution,
which can result in `deno check` reporting type errors, that `tsc` does not
report.

If a default export from an `npm:` import appears to have a wrong type (with the
right type seemingly being available under the `.default` property), it's most
likely that the package provides wrong types under node16 module resolution for
imports from ESM. You can verify this by checking if the error also occurs with
`tsc --module node16` and `"type": "module"` in `package.json` or by consulting
the [Are the types wrong?](https://arethetypeswrong.github.io/) website
(particularly the "node16 from ESM" row).

If you want to use a package that doesn't support TypeScript's node16 module
resolution, you can:

1. Open an issue at the issue tracker of the package about the problem. (And
perhaps contribute a fix :) (Although there unfortunately currently is a lack
of tooling for packages to support both ESM and CJS, since default exports
require different syntaxes, see also
[microsoft/TypeScript#54593](https://github.com/microsoft/TypeScript/issues/54593))
2. Use a [CDN](./cdns.md), that rebuilds the packages for Deno support, instead
of an `npm:` identifier.
3. Ignore the type errors you get in your code base with `// @ts-expect-error`
or `// @ts-ignore`.

### Including Node types

Node ships with many built-in types like `Buffer` that might be referenced in an
Expand All @@ -110,6 +92,39 @@ Note that it is fine to not specify a version for this in most cases because
Deno will try to keep it in sync with its internal Node code, but you can always
override the version used if necessary.

## npm executable scripts

npm packages with `bin` entries can be executed from the command line without an
`npm install` using a specifier in the following format:

```
npm:<package-name>[@<version-requirement>][/<binary-name>]
```

For example:

```sh
$ deno run --allow-read npm:[email protected] Hello there!
______________
< Hello there! >
--------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||

$ deno run --allow-read npm:[email protected]/cowthink What to eat?
______________
( What to eat? )
--------------
o ^__^
o (oo)\_______
(__)\ )\/\
||----w |
|| ||
```

## `--node-modules-dir` flag

npm specifiers resolve npm packages to a central global npm cache. This works
Expand Down