unjs / unenv

🕊️ Convert javaScript code to be runtime agnostic
MIT License
460 stars 22 forks source link

Polyfilling Node APIs #39

Open wobsoriano opened 2 years ago

wobsoriano commented 2 years ago

I am trying to polyfill a certain module that uses process, buffer, utils, etc.

I'm not sure how to use unenv since it's lacking an example or I've not seen a usage somewhere yet. So far, this is what I've accomplished with vite:

import { defineConfig } from 'vite'
import { env, nodeless } from 'unenv'

const { alias } = env(nodeless)

// https://vitejs.dev/config/
export default defineConfig({
  define: {
    global: 'globalThis',
  },
  resolve: {
    alias
  }
})

It throws this error when running the app:

✘ [ERROR] Could not resolve "./_buffer"

    node_modules/unenv/runtime/node/buffer/index.cjs:34:22:
      34 │ var _buffer = require("./_buffer");
         ╵                       ~~~~~~~~~~~

✘ [ERROR] Could not resolve "../../_internal/utils"

    node_modules/unenv/runtime/node/buffer/index.cjs:36:21:
      36 │ var _utils = require("../../_internal/utils");
         ╵                      ~~~~~~~~~~~~~~~~~~~~~~~

Am I missing something here? Thank you!

wobsoriano commented 2 years ago

Okay. I'm not sure if this is a proper way of using unenv but it works

// vite.config.ts
import { env, nodeless } from 'unenv'

const { alias } = env(nodeless)

// remove buffer to fix the issue "could not resolve "./_buffer"
const { buffer: _, ...rest } = alias

// https://vitejs.dev/config/
export default defineConfig({
  define: {
    global: 'globalThis',
  },
  resolve: {
    alias: {
      ...rest,
    },
  },
})
<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module">
      import process from 'node:process'
      import { Buffer } from 'node:buffer'
      import EventEmitter from 'node:events'
      window.process = process
      window.Buffer = Buffer
      window.EventEmitter = EventEmitter
    </script>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
pi0 commented 2 years ago

Hi @wobsoriano. I believe unenv would return defines as well but yeah overall this is a good integration! Would you like to help on adding documentation for Rollup and Vite usage?

wobsoriano commented 2 years ago

Hello @pi0 . So the module I was trying to polyfill is ethers. It needs different built-in modules and access to global/globalThis in client.

Normally you would do npm install buffer events process util but since unenv already offers a polyfill of it you won't have to install them all!

This is how I do it before:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  define: {
    global: 'globalThis',
  },
  resolve: {
    alias: {
      process: 'process/browser',
      util: 'util',
    },
  },
})
<!DOCTYPE html>
<html lang="en">
  <head>
    <script>window.global = window;</script>
    <script type="module">
      import process from "process";
      import { Buffer } from "buffer";
      import EventEmitter from "events";

      window.process = process;
      window.Buffer = Buffer;
      window.EventEmitter = EventEmitter;
    </script>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

and now, using unenv

import { env, nodeless } from 'unenv'

const { alias } = env(nodeless)

export default defineConfig({
  define: {
    global: 'globalThis',
  },
  resolve: {
    alias,
  },
})
<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module">
      import process from 'node:process'
      import { Buffer } from 'node:buffer'
      import EventEmitter from 'node:events'
      window.process = process
      window.Buffer = Buffer
      window.EventEmitter = EventEmitter
    </script>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
manniL commented 6 months ago

Might work similar as it does in the PR ☺️

kleinpetr commented 3 days ago

Hi I am getting similar error while I build for cloudflare_pages

Cannot resolve "unenv/runtime/node/buffer/index/" from "~/Projects/custod-io/node_modules/.pnpm/@keystonehq+bc-ur-registry@0.5.5/node_modules/@keystonehq/bc-ur-registry/dist/index.js" and externals are not allowed!

But interesting thing is that for vercel is built with no issue :thinking:

When I enable experimental.clientNodeCompat I'll get

Nuxt Build Error: [commonjs--resolver] Could not load unenv/runtime/node/buffer/index/: ENOENT: no such file or directory, open 'unenv/runtime/node/buffer/index/'
file: ~/Projects/custod-io/node_modules/.pnpm/@keystonehq+sol-keyring@0.3.1_bufferutil@4.0.8_encoding@0.1.13_utf-8-validate@5.0.10/node_modules/@keystonehq/sol-keyring/dist/index.js
pi0 commented 3 days ago

@kleinpetr can you please kindly report it in a separate issue possibly with a reproduction? It is not clear what setup are you using and this issue is tracker for generic documentation question. (if it is with Nuxt, Nuxt repository is probably best place!)