From 69a61ca83149138502f1e9f4679e8da27e857906 Mon Sep 17 00:00:00 2001 From: KnorpelSenf Date: Wed, 8 Sep 2021 22:29:27 +0200 Subject: [PATCH 01/12] Rework README file --- README.md | 200 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 150 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index cf74a73..87a0e3a 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,108 @@ - # deno2node -Transpiles Deno projects into `.js` and `.d.ts` for Node.js. +Compile your [Deno] project to run under [Node.js]. -Uses [`ts-morph`] to rewrite imports, typecheck, and emit. +## CLI Usage from Deno -## Motivation +No installation needed. Simply `cd` into the directory of your project, and run: + +```sh +deno run --unstable --allow-read=. --allow-write=. \ + https://deno.land/x/deno2node/src/cli.ts +``` -Writing libraries Deno-first -makes it easy to publish to https://deno.land/x, -and simplifies development experience: +You need to substitute `` by a path to your `tsconfig.json` +file. `deno2node` passes it on to the built-in `tsc` for compiling your code. -> Deno \[...\] requires no explicit transpilation step, -> and ships with 0conf tooling that works well together. +## CLI Usage from Node.js -## CLI Usage +As a by-product of end-to-end testing, a Node.js build is also available: ```sh -$ deno run \ - --no-check \ - --unstable \ - --allow-read \ - --allow-write= \ - https://deno.land/x/deno2node/src/cli.ts \ - +npm install --save-dev --save-prefix='~' deno2node ``` - -As a by-product of end-to-end testing, -Node.js build is also available: +Now put you can add a script to `package.json` so you can run it with `npm run build`: -```sh -$ npm install --save-dev --save-prefix='~' deno2node +```jsonc +{ + // yada yada ... + "scripts": { + "build": "deno2node " + } +} ``` +You can also run it directly: + ```sh -$ deno2node +npx deno2node ``` -`tsconfig.json` is used to specify `compilerOptions` and source `files` to `include`. +## Motivation + +Writing code for Deno is fun. It's so much simpler. + +If you have tried it, you know what we mean. + +If not, here is how you develop for Deno: Don't wait for TypeScript to +transpile. Just run it. Don't wait for unit tests. They are truly fast. Linting? +It is always instant. Zero configuration files. The tooling is standardised, +built-in, and works well together. No `package.json` to tediously fill out. No +more “did you try `rm -rf node_modules`?” Everything just works out of the box. +What a relief. + +Try it, and you will know what we mean. + +## How it Works + +There are three main steps to this. -[API reference] explains transformations and configuration. +1. Transform the code base. This means: + - Rewrite all import statements, e.g. to handle explicit vs. implicit file + extensions + - Reverse npm package transformations from `skypack.dev`, `esm.sh`, + `deno.land/std`, and `nest.land/std`. +2. Typecheck the code. You want that. +3. Emit `.js` and `.d.ts` files. These files can directly be run by Node or + published on npm. -Note: output and diagnostics will change across minor versions. +Note that `deno2node` does all of this in memory. It does not touch your source +files, and only writes the build artefacts to your disk. + +`deno2node` uses [`ts-morph`] under the hood, which in turn builds on top of the +TypeScript compiler `tsc`. Hence, you get the same behaviour as if you had +developed your code directly for Node. + +`deno2node` can perform more powerful transpilation steps that make it flexible +enough for most needs. ### Shimming -To use Deno globals not available in Node.js, -create and register a file exporting all the shims you need: +Some things are global in Deno, but not in Node.js. One example for this is +`fetch`. Consequently, you need to _shim_ them, i.e. provide code that +supplements the missing globals. + +For instance, you can use [`node-fetch`] as a substitue for the built-in `fetch` +of Deno. ```sh -$ npm install deno.ns +npm install node-fetch ``` -```js -// @filename: src/shim.node.ts -export * from "deno.ns"; +Now, create a file that exports the globals you need: + +```ts +// src/shim.node.ts +export { default as fetch } from "node-fetch"; + +// more shims exported here ``` +Lastly, you need to register your shims in `tsconfig.json` so `deno2node` can +inject them for you: + ```jsonc -// @filename: tsconfig.json { "deno2node": { "shim": "src/shim.node.ts" // path to shim file, relative to tsconfig @@ -67,26 +110,71 @@ export * from "deno.ns"; } ``` +If you simply want to shim all Deno globals, you can install the `deno.ns` +package. Then do: + +```ts +// src/shim.node.ts +export * from "deno.ns"; +``` + +That package was specifically created for this use case. + ### Runtime-specific code -When the provided transformations are not enough, -you can provide a Node-specific (`.node.ts`) -and a Deno-specific (`.deno.ts`) version of any file. +In same cases you may want to have two different implementations, depending on +whether you're running on Deno or on Node. When shimming is not enough, you can +provide a Node-specific `.node.ts` and a Deno-specific +`.deno.ts` version of any file. They need to reside next to each other +in the same directory. + +`deno2node` will ignore the Deno version and rewrite imports to use the Node.js +version instead. Thus, the Deno-specific file will not be part of the build +output. + +For example, provide `greet.deno.ts` for Deno: + +```ts +// src/greet.deno.ts +export function greet() { + console.log("Hello Deno!"); + // access Deno-specific APIs here +} +``` + +Now, provide `greet.node.ts` for Node: + +```ts +// src/greet.node.ts +export function greet() { + console.log("Hello Node!"); + // access Node-specific APIs here +} +``` -`deno2node` will ignore the Deno version -and rewrite imports to use the Node.js version instead. +Finally, use it in `foo.ts`: -This technique has many uses. -`deno2node` uses it to import from https://deno.land/x. -[`grammy`] will probably also use it to abstract away platform-specific APIs. +```ts +import { greet } from "./platform.deno.ts"; + +// Prints "Hello Deno!" on Deno, +// and "Hello Node!" on Node: +greet(); +``` + +This technique has many uses. `deno2node` itself uses it to import from +https://deno.land/x. A plugin of the Telegram bot framework [`grammY`] uses it +to abstract away platform-specific APIs. ### Vendoring -If you import a module which has no npm equivalent, -`deno2node` will vendor it in `vendorDir`, if specified: +If you import a module which has no npm equivalent, `deno2node` can extract the +code out of Deno's module cache. On Deno, this directory can be used for +vendoring (by specifying the `$DENO_DIR` environment variable), so `deno2node` +calls it `vendorDir`. ```jsonc -// @filename: tsconfig.json +// tsconfig.json { "deno2node": { "vendorDir": "src/.deno2node/vendor/" // path within `rootDir` @@ -94,14 +182,26 @@ If you import a module which has no npm equivalent, } ``` -Vendoring requires `--allow-env`, to locate Deno cache. +> Note that vendoring requires `--allow-env` in order to locate Deno cache. + +Vendoring is still experimental, so be welcome to open an issue if you encounter +a problem with it! + +Also, consider recommending [`pnpm`] to users of your library. It might be able +to deduplicate vendored files across packages. + +## API -Note: vendoring is currently slow and poorly tested. +Confer the automatically generated [API Reference] if you want to use +`deno2node` from code. -Consider recommending [`pnpm`] to users of your library. -It might be able to deduplicate vendored files across packages. +Note that updates to the `tsc` dependency will be performed in minor versions of +`deno2node`, so output and diagnostics will change across them. -[`grammY`]: https://github.com/grammyjs/grammY +[deno]: https://deno.land/ +[node.js]: https://nodejs.org/ +[`grammy`]: https://github.com/grammyjs/grammY [`pnpm`]: https://github.com/pnpm/pnpm#background [`ts-morph`]: https://github.com/dsherret/ts-morph -[API reference]: https://doc.deno.land/https/deno.land/x/deno2node/src/mod.ts +[`node-fetch`]: https://github.com/node-fetch/node-fetch +[api reference]: https://doc.deno.land/https/deno.land/x/deno2node/src/mod.ts From 2d8137352dab4a5f6662782d02d6f57b9b7ff5be Mon Sep 17 00:00:00 2001 From: KnorpelSenf Date: Wed, 8 Sep 2021 22:35:55 +0200 Subject: [PATCH 02/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87a0e3a..8b8c53a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ deno run --unstable --allow-read=. --allow-write=. \ ``` You need to substitute `` by a path to your `tsconfig.json` -file. `deno2node` passes it on to the built-in `tsc` for compiling your code. +file. `deno2node` passes it on to the shipped `tsc` for compiling your code. ## CLI Usage from Node.js From e3136392e72e8fcb6870fd8608d5a7adc7338626 Mon Sep 17 00:00:00 2001 From: KnorpelSenf Date: Wed, 8 Sep 2021 22:37:06 +0200 Subject: [PATCH 03/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b8c53a..6eac554 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ As a by-product of end-to-end testing, a Node.js build is also available: npm install --save-dev --save-prefix='~' deno2node ``` -Now put you can add a script to `package.json` so you can run it with `npm run build`: +Now add a script to `package.json` so you can run it with `npm run build`: ```jsonc { From 421f60732f1ab16b6e19a823a3bf34a7a52f03ca Mon Sep 17 00:00:00 2001 From: KnorpelSenf Date: Wed, 8 Sep 2021 22:37:47 +0200 Subject: [PATCH 04/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6eac554..6645fc6 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Now add a script to `package.json` so you can run it with `npm run build`: } ``` -You can also run it directly: +Instead of adding a script, you can also run it directly: ```sh npx deno2node From 661b1ba109d54f2172fd9d3610de270b79d9ef6f Mon Sep 17 00:00:00 2001 From: KnorpelSenf Date: Wed, 8 Sep 2021 22:42:45 +0200 Subject: [PATCH 05/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6645fc6..767460f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # deno2node -Compile your [Deno] project to run under [Node.js]. +Compile your [Deno] project to run on [Node.js]. ## CLI Usage from Deno From 8985655c5552f2e9ad83c33c7a5b786d98816ca3 Mon Sep 17 00:00:00 2001 From: KnorpelSenf Date: Tue, 9 Nov 2021 11:35:25 +0100 Subject: [PATCH 06/12] Explain better how shimming works --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 767460f..67cb82f 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,9 @@ Some things are global in Deno, but not in Node.js. One example for this is `fetch`. Consequently, you need to _shim_ them, i.e. provide code that supplements the missing globals. +> Note that `deno2node` does not actually touch global definitions. Instead, it +> only injects import statements in the respective modules. + For instance, you can use [`node-fetch`] as a substitue for the built-in `fetch` of Deno. From 100e2edfe153d0a838a4ddd7e812cc26f689689e Mon Sep 17 00:00:00 2001 From: KnorpelSenf Date: Fri, 26 Nov 2021 10:48:15 +0100 Subject: [PATCH 07/12] Update reference to grammY --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 67cb82f..36b97fd 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ greet(); ``` This technique has many uses. `deno2node` itself uses it to import from -https://deno.land/x. A plugin of the Telegram bot framework [`grammY`] uses it +https://deno.land/x. The Telegram bot framework [`grammY`] uses it to abstract away platform-specific APIs. ### Vendoring From c9c41efe2f1c214ae9604cb46291615211eaf178 Mon Sep 17 00:00:00 2001 From: Wojciech Pawlik Date: Mon, 29 Nov 2021 21:58:51 +0100 Subject: [PATCH 08/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36b97fd..d07c4fc 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Compile your [Deno] project to run on [Node.js]. No installation needed. Simply `cd` into the directory of your project, and run: ```sh -deno run --unstable --allow-read=. --allow-write=. \ +deno run --no-check --allow-read=. --allow-write=. \ https://deno.land/x/deno2node/src/cli.ts ``` From 3ed921c6c6bb2dab8164e0cfcc4f92900f97db84 Mon Sep 17 00:00:00 2001 From: Wojciech Pawlik Date: Sat, 4 Dec 2021 16:49:36 +0100 Subject: [PATCH 09/12] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d07c4fc..4533044 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,8 @@ There are three main steps to this. 1. Transform the code base. This means: - Rewrite all import statements, e.g. to handle explicit vs. implicit file - extensions - - Reverse npm package transformations from `skypack.dev`, `esm.sh`, - `deno.land/std`, and `nest.land/std`. + extensions. + - Use bare specifiers in `std/node`, https://esm.sh and https://skypack.dev imports. 2. Typecheck the code. You want that. 3. Emit `.js` and `.d.ts` files. These files can directly be run by Node or published on npm. From 5c41be8ff384ac845b4f03566976cf90bc679584 Mon Sep 17 00:00:00 2001 From: Wojciech Pawlik Date: Sat, 4 Dec 2021 17:27:56 +0100 Subject: [PATCH 10/12] Tweaks --- README.md | 67 ++++++++++++++++++------------------------------------- 1 file changed, 22 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 4533044..65f60e4 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,15 @@ Compile your [Deno] project to run on [Node.js]. +![Because Deno's tooling is simpler than Node's](https://pbs.twimg.com/media/FBba11IXMAQB7pX?format=jpg) + ## CLI Usage from Deno No installation needed. Simply `cd` into the directory of your project, and run: ```sh deno run --no-check --allow-read=. --allow-write=. \ - https://deno.land/x/deno2node/src/cli.ts + https://deno.land/x/deno2node/src/cli.ts ``` You need to substitute `` by a path to your `tsconfig.json` @@ -19,56 +21,38 @@ file. `deno2node` passes it on to the shipped `tsc` for compiling your code. As a by-product of end-to-end testing, a Node.js build is also available: ```sh +# New fearues or TypeScript upgrades +# may alter output or diagnostics across minor versions. npm install --save-dev --save-prefix='~' deno2node ``` -Now add a script to `package.json` so you can run it with `npm run build`: +Now add a script to `package.json` so you can run it with `npm run prepare`: ```jsonc +// @filename: package.json { // yada yada ... "scripts": { - "build": "deno2node " + "prepare": "deno2node " } } ``` -Instead of adding a script, you can also run it directly: +You can also run it directly: ```sh npx deno2node ``` -## Motivation - -Writing code for Deno is fun. It's so much simpler. - -If you have tried it, you know what we mean. - -If not, here is how you develop for Deno: Don't wait for TypeScript to -transpile. Just run it. Don't wait for unit tests. They are truly fast. Linting? -It is always instant. Zero configuration files. The tooling is standardised, -built-in, and works well together. No `package.json` to tediously fill out. No -more “did you try `rm -rf node_modules`?” Everything just works out of the box. -What a relief. - -Try it, and you will know what we mean. - -## How it Works +## How it works There are three main steps to this. -1. Transform the code base. This means: - - Rewrite all import statements, e.g. to handle explicit vs. implicit file - extensions. - - Use bare specifiers in `std/node`, https://esm.sh and https://skypack.dev imports. -2. Typecheck the code. You want that. +1. Transform the code base in-memory, by rewriting all import statements. +2. Typecheck the code. 3. Emit `.js` and `.d.ts` files. These files can directly be run by Node or published on npm. -Note that `deno2node` does all of this in memory. It does not touch your source -files, and only writes the build artefacts to your disk. - `deno2node` uses [`ts-morph`] under the hood, which in turn builds on top of the TypeScript compiler `tsc`. Hence, you get the same behaviour as if you had developed your code directly for Node. @@ -95,7 +79,7 @@ npm install node-fetch Now, create a file that exports the globals you need: ```ts -// src/shim.node.ts +// @filename: src/shim.node.ts export { default as fetch } from "node-fetch"; // more shims exported here @@ -105,6 +89,7 @@ Lastly, you need to register your shims in `tsconfig.json` so `deno2node` can inject them for you: ```jsonc +// @filename: tsconfig.json { "deno2node": { "shim": "src/shim.node.ts" // path to shim file, relative to tsconfig @@ -112,16 +97,13 @@ inject them for you: } ``` -If you simply want to shim all Deno globals, you can install the `deno.ns` -package. Then do: +If you simply want to shim all Deno globals, you can use the `deno.ns` package: ```ts -// src/shim.node.ts +// @filename: src/shim.node.ts export * from "deno.ns"; ``` -That package was specifically created for this use case. - ### Runtime-specific code In same cases you may want to have two different implementations, depending on @@ -137,7 +119,7 @@ output. For example, provide `greet.deno.ts` for Deno: ```ts -// src/greet.deno.ts +// @filename: src/greet.deno.ts export function greet() { console.log("Hello Deno!"); // access Deno-specific APIs here @@ -147,7 +129,7 @@ export function greet() { Now, provide `greet.node.ts` for Node: ```ts -// src/greet.node.ts +// @filename: src/greet.node.ts export function greet() { console.log("Hello Node!"); // access Node-specific APIs here @@ -165,18 +147,16 @@ greet(); ``` This technique has many uses. `deno2node` itself uses it to import from -https://deno.land/x. The Telegram bot framework [`grammY`] uses it -to abstract away platform-specific APIs. +https://deno.land/std. The Telegram bot framework [`grammY`] uses it to abstract +away platform-specific APIs. ### Vendoring If you import a module which has no npm equivalent, `deno2node` can extract the -code out of Deno's module cache. On Deno, this directory can be used for -vendoring (by specifying the `$DENO_DIR` environment variable), so `deno2node` -calls it `vendorDir`. +code out of Deno's module cache, and put it in virtual `vendorDir`. ```jsonc -// tsconfig.json +// @filename: tsconfig.json { "deno2node": { "vendorDir": "src/.deno2node/vendor/" // path within `rootDir` @@ -197,9 +177,6 @@ to deduplicate vendored files across packages. Confer the automatically generated [API Reference] if you want to use `deno2node` from code. -Note that updates to the `tsc` dependency will be performed in minor versions of -`deno2node`, so output and diagnostics will change across them. - [deno]: https://deno.land/ [node.js]: https://nodejs.org/ [`grammy`]: https://github.com/grammyjs/grammY From 2a85c7e0e8730c2d3e0b588b2dc7d356bffe9732 Mon Sep 17 00:00:00 2001 From: KnorpelSenf Date: Sat, 4 Dec 2021 22:22:33 +0100 Subject: [PATCH 11/12] Apply suggestions from code review Co-authored-by: Wojciech Pawlik --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 65f60e4..214a0d7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Compile your [Deno] project to run on [Node.js]. -![Because Deno's tooling is simpler than Node's](https://pbs.twimg.com/media/FBba11IXMAQB7pX?format=jpg) +![Because Deno's tooling is way simpler than Node's](https://pbs.twimg.com/media/FBba11IXMAQB7pX?format=jpg) ## CLI Usage from Deno From e5258a3876a9724dbbbdeb27aea7069dd4423c4f Mon Sep 17 00:00:00 2001 From: Wojciech Pawlik Date: Sat, 4 Dec 2021 23:06:15 +0100 Subject: [PATCH 12/12] Use title case Co-authored-by: KnorpelSenf --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 214a0d7..32b7834 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Compile your [Deno] project to run on [Node.js]. ![Because Deno's tooling is way simpler than Node's](https://pbs.twimg.com/media/FBba11IXMAQB7pX?format=jpg) -## CLI Usage from Deno +## CLI Usage From Deno No installation needed. Simply `cd` into the directory of your project, and run: @@ -16,7 +16,7 @@ deno run --no-check --allow-read=. --allow-write=. \ You need to substitute `` by a path to your `tsconfig.json` file. `deno2node` passes it on to the shipped `tsc` for compiling your code. -## CLI Usage from Node.js +## CLI Usage From Node.js As a by-product of end-to-end testing, a Node.js build is also available: @@ -44,7 +44,7 @@ You can also run it directly: npx deno2node ``` -## How it works +## How It Works There are three main steps to this.