-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
support deno #936
Comments
What's stopping you from using esbuild with deno? I've been doing so happily for a while. Also, I've actually been working with people on making Node features work in Deno using esbuild in the past. |
@benjamingr Hi, is there any sample code? Uncaught ReferenceError: require is not defined What is the way you use esbuild in deno? I know a way: const p = Deno.run({
cmd: ["node", "./esbuild.js"],
stdin: "piped",
stdout: "piped",
stderr: "piped",
}); But this requires you to install node and write node code, and its compilation speed will also slow down. I hope I can directly import esbuild in deno and use it: import esbuild from 'https://esm.sh/esbuild'; |
esbuild is written in Go, the Node.js wrapper just calls into it it's a native module - see https://github.com/evanw/esbuild/tree/master/npm - you can technically probably use the WASM version or build a native Deno module - but you can also just grab the binary and run the command in another process. |
Right but you can't run plugins via the binary alone. You need the Node library (here: https://www.unpkg.com/browse/esbuild@0.9.0/lib/main.js) which uses Edit: You might also be able to use the Node compat module from Deno's std library... Not sure. |
Are those APIs not compatible with std/node? |
@benjamingr I know, the official document mentions this. https://esbuild.github.io/getting-started/#other-ways-to-install.
@heyheyhello Maybe this is a good way. |
Give it a shot with Deno's std/node. @benjamingr is probably right - the compatibility layer should work. |
@heyheyhello I checked your link, this is the node module referenced by esbuild: // lib/node.ts
--
1274 | var child_process = require("child_process");
1275 | var crypto = require("crypto");
1276 | var path = require("path");
1277 | var fs = require("fs");
1278 | var os = require("os");
1279 | var tty = require("tty"); I compared std/node, https://github.com/denoland/deno_std/tree/0.90.0/node, these modules are still not implemented or only partially implemented in std/node. |
Deno uses cached HTTP imports. |
So I'm very unfamiliar with Deno. I have literally never used it before. However, I just tried it out now and I was able to get it to work with this code: import * as esbuild from 'https://esm.sh/esbuild-wasm/esm/browser.js'
await esbuild.initialize({
wasmURL: 'https://esm.sh/esbuild-wasm/esbuild.wasm',
worker: false,
})
console.log(await esbuild.transform('1+2+3')) It looks like you have to use
Apparently With that you should be able write some esbuild plugins that interact with the file system using Deno's file system APIs. It would be interesting to see someone make a proof of concept for this. As far as running the native version of esbuild in Deno, I assume the code to do this would need to download and unzip the appropriate binary executable from npm at run-time. Is that correct? Am I right that Deno doesn't have a notion of install scripts? If so, is there a standard way to do this in Deno? For example, I'm guessing that Deno has some form of HTTP download cache already built in if you import modules by URL. Then you could imagine esbuild having support for Deno by doing something like I'm not saying I'm definitely going to support Deno in esbuild as a first-class citizen. I'm just trying to think through what a potential implementation could look like. |
Deno is very easy to use, I believe you can learn and use it within a few hours. import * as esbuild from 'https://esm.sh/esbuild-wasm/esm/browser.js'
const t = performance.now()
await esbuild.initialize({
wasmURL: 'https://esm.sh/esbuild-wasm/esbuild.wasm',
worker: false,
})
console.log(await esbuild.transform('1+2+3'))
console.log(`compile in ${Math.round(performance.now() - t)}ms`) @evanw The esbuild-wasm does work, but it is very slow. The above build took 2000ms, but the node's native version is less than 20ms, which is a difference of 100 times.
Yeah, you are right. Deno does not have node_modules, it will be downloaded from the url and cached locally. The url does not point to a binary file, but the actual code. E.g: https://esm.sh/react (You can open it in the browser address bar.)
I haven't tried it, I'm not sure. But as you describe in the documentation(https://esbuild.github.io/getting-started/#download-a-build):
Deno follows the esm specification, and any code that conforms to the esm specification can be run in deno. // esbuild
// lib/node.ts
--
1274 | var child_process = require("child_process");
1275 | var crypto = require("crypto");
1276 | var path = require("path");
1277 | var fs = require("fs");
1278 | var os = require("os");
1279 | var tty = require("tty"); These node modules follow the commonjs specification and are not compatible with deno.Deno is trying to be compatible with node core modules, but it is still in progress.https://github.com/denoland/deno_std/tree/0.90.0/node.
I think the best solution is to include deno compatibility code in esbuild code so that esbuild can run in node and deno at the same time. There are currently many libraries that do this. E.g: https://github.com/cacjs/cac . I use it in node, and now I also use it in deno, there is no obstacle. // in node
import { cac } from 'cac'
// in deno
import { cac } from 'https://unpkg.com/cac/mod.ts' |
I assume that this delay isn't due to Deno re-downloading everything every time. Is there a way to verify that? Given that, I assume that the overhead is due to WebAssembly compilation. Which is interesting because in theory this doesn't have to be the case. The way WebAssembly is designed, it is pretty straightforward to compile and cache the translation of the WebAssembly module to assembly so that repeated invocations can skip the compilation time completely and be very fast. V8 actually implements this: https://v8.dev/blog/wasm-code-caching. In the browser, triggering this involves using
I wanted to explore what it would take to do this as a thought experiment. I think the way to do this would have to be to have a separate entry point file (or equivalently a separate package) for Deno because esbuild's node integration is very specific to node. The entry point file would also have to be an installer, which further differentiates it from the node entry point. Deno's compatibility with node's packages is irrelevant with that approach. |
@evanw Deno will cache all downloads, unless you specify
You are right, as cac did. Deno does not need to be installed, just provide an entry file for download. Like the deno entry point file of cac : source https://github.com/cacjs/cac/blob/master/mod.ts esm: https://unpkg.com/cac/mod.ts . |
I just tried this out. I couldn't get it to work with local files because a) Deno deliberately uses the |
Sad...o(╥﹏╥)o
@evanw They seem to be solving this problem. Deno.readFileSync(new URL("file:///Volumes/work/deno-test/index.ts").pathname) Or set up a static server locally and request local files through http. If it is not under the same domain name, you can use a proxy: // ./static-server.ts
import { Application, send } from "https://deno.land/x/oak/mod.ts";
const app = new Application();
// set static dir
app.use(async (context) => {
await send(context, context.request.url.pathname, {
root: Deno.cwd(),
});
});
await app.listen({ port: 8000 }); // ./fetch.ts
const content = await fetch(`http://localhost:8000/test.ts`).then(res=res.text());
console.log(content);
This is really bad, but it is mentioned in their
Q1 2021 roadmap: denoland/deno#8824
I don’t know, maybe we can send them a pr. |
I am kind of walking in here with little context, but I think it should be noted that Deno has a native plugin API for Rust - maybe you could bind the Rust to the Go then have it use the native plugin API. |
Just wondering, any reason you are not using |
Hey 👋, Deno maintainer here.
Actually it is due to repeated downloads. There is no cache on
Not quite - you can do this, but it requires some trickery. You can open the file with Regarding native esbuild using the esbuild binary: I think the most straight-forward way would be to indeed download, unzip, and cache the binary at runtime (again you could use https://deno.land/x/cache for this), and then start the subprocess with Please ping me if there are any more questions :-) |
I just published an experimental Deno package for esbuild version 0.11.10. Using it looks like this: import * as esbuild from 'https://deno.land/x/esbuild@v0.11.10/mod.js'
const ts = 'let hasProcess: boolean = typeof process != "null"'
const result = await esbuild.transform(ts, { loader: 'ts', logLevel: 'warning' })
console.log('result:', result)
esbuild.stop() It has basically the same API as the npm package with one addition: you need to call
Thanks for reaching out @lucacasonato. It's good to have additional detail about the WebAssembly implementation. I'm going to focus on the native approach given that it's much faster and has more features than the WebAssembly version. I didn't end up using https://deno.land/x/cache because the file I need to cache is inside the downloaded file and needs to be extracted first. You can see my current implementation here. I'm just writing the same information to the same cache directory as my npm package installer. The one big hiccup that I hit when porting my node-based implementation to Deno is that I didn't see any equivalent to node's |
I have confirmed that it works on x86_64 macOS and Linux but not on Windows or on ARM macOS. Can someone with either of those two platforms confirm whether it works or not? |
I confirm it is working on Windows 10. |
Running @dalcib's example I get this error on my M1 Macbook Air (using deno 1.9.0) > deno run --allow-env --allow-read --allow-write --allow-net --allow-run server.js
Download https://deno.land/std/http/server.ts
Warning Implicitly using latest version (0.93.0) for https://deno.land/std/http/server.ts
Download https://deno.land/std@0.93.0/http/server.ts
Download https://deno.land/std@0.93.0/http/_io.ts
Download https://deno.land/std@0.93.0/io/bufio.ts
Download https://deno.land/std@0.93.0/async/mod.ts
Download https://deno.land/std@0.93.0/http/http_status.ts
Download https://deno.land/std@0.93.0/io/util.ts
Download https://deno.land/std@0.93.0/textproto/mod.ts
Download https://deno.land/std@0.93.0/async/delay.ts
Download https://deno.land/std@0.93.0/async/deferred.ts
Download https://deno.land/std@0.93.0/async/mux_async_iterator.ts
Download https://deno.land/std@0.93.0/async/pool.ts
Download https://deno.land/std@0.93.0/bytes/mod.ts
Download https://deno.land/std@0.93.0/io/buffer.ts
Check file:///Users/tobias/dev/github/esbuild-plugin-cache/example-deno/server.js
error: Uncaught NotFound: No such file or directory (os error 2)
if (clients.length === 0) Deno.run({ cmd: ['cmd', '/c', 'start', `http://localhost:3000`] })
^
at unwrapOpResult (deno:core/core.js:100:13)
at Object.opSync (deno:core/core.js:114:12)
at opRun (deno:runtime/js/40_process.js:20:17)
at Object.run (deno:runtime/js/40_process.js:104:17)
at file:///Users/tobias/dev/github/esbuild-plugin-cache/example-deno/server.js:52:36
at fire (deno:runtime/js/11_timers.js:443:7)
at handleTimerMacrotask (deno:runtime/js/11_timers.js:303:7) Also tested
That specific issue ☝️ is fixed (for me and I think all windows machines) by using I tried adding this line 👇 to the Deno.env.set("FOLDERID_LocalAppData", Deno.env.get("LOCALAPPDATA")) Then |
@tolu, thank you for point this out. a) In my case, I don't have the FOLDERID_LocalAppData system variable defined, and, surprisingly, it worked. let baseDir;
switch (Deno.build.os) {
case "windows":
baseDir = Deno.env.get("FOLDERID_LocalAppData");
break;
}
if (!baseDir) {
baseDir = Deno.env.get("HOME");
if (baseDir)
baseDir += "/.cache";
} b) About the stop, you are right. In esbuild
.build({
//...
})
.then((result, error) => {
esbuild.stop()
}) c) By the way, the if (clients.length === 0) Deno.run({ cmd: ['cmd', '/c', 'start', `http://localhost:3000`] }) for it: const open = { darwin: ['open'], linux: ['xdg-open'], windows: ['cmd', '/c', 'start'] }
if (clients.length === 0) Deno.run({ cmd: [...open[Deno.build.os], 'http://localhost:3000'] }) I already updated it in the repo. |
🙏 @dalcib 🙇
☝️ Great that it worked but I'm just not sure
🎉 🙌 Worked like a charm (for me on Windows, will test more on M1 Mac)! |
Yes, I agree with you. This library also uses LOCALAPPDATA : |
Sorry about that. I copied that from the |
Is there an obvious reason I am missing that this doesn't work?
With |
Yes. You are stopping esbuild as soon as it starts. With watch mode you'll need to keep esbuild running. |
I got it working:
Is there any documentation with examples available on the site, and is this awesome extension becoming 'official' soon? |
Here is a working example for 2024: 1. Create a new file:
|
Hi guy!
Deno's excellence made me give up node, but I need a build tool like esbuild.
I want to transform npm modules to esm to run in deno, which allows deno to have the entire web ecosystem.
I know swc, but it is not reliable yet, the worst case is that it will transform to wrong code, so I need esbuild, just like node, but not webAssembly(esbuild-wasm).
The text was updated successfully, but these errors were encountered: