js-algorand-sdk icon indicating copy to clipboard operation
js-algorand-sdk copied to clipboard

Error while importing in Vite (SvelteKit)

Open NoelJacob opened this issue 3 years ago • 13 comments

NoelJacob avatar Jul 07 '21 19:07 NoelJacob

@NoelJacob please give more context for this issue

jasonpaulos avatar Jul 08 '21 16:07 jasonpaulos

I did the following commands

pnpm init svelte@next
pnpm install
pnpm install algosdk
pnpm run dev

Then I get: image

I could not add any kind of nodejs polyfills.

I tried few rollup polyfill plugins with vite and also changing vite configurations kit.vite.optimiseDeps.exclude and kit.vite.ssr.noExternal but all this throws different errors. Maybe this is solvable by somehow configuring @rollup/plugin-node-resolve in vite plugins?

The library works perfectly with Webpack with node-polyfill-webpack-plugin. I also could not getting it working with Snowpack or Rollup, although I didn't change their configs much.

My repo with the error.

NoelJacob avatar Jul 08 '21 17:07 NoelJacob

I'm not familiar with vite, but you will need to add two polyfills for node packages in order for this package to work: buffer and path-browserify. You can see where these packages are declared in our webpack config here: https://github.com/algorand/js-algorand-sdk/blob/develop/webpack.config.js. Note that this package expects Buffer to be a global variable, so just installing the buffer package won't be enough.

jasonpaulos avatar Jul 08 '21 20:07 jasonpaulos

I'm not familiar with vite, but you will need to add two polyfills for node packages in order for this package to work: buffer and path-browserify. You can see where these packages are declared in our webpack config here: https://github.com/algorand/js-algorand-sdk/blob/develop/webpack.config.js. Note that this package expects Buffer to be a global variable, so just installing the buffer package won't be enough.

Hello @jasonpaulos: I also ran into this error as well (while working with Reach. You mentioned being unfamiliar with Vite, and so I hope the following either provides clues or helps with suggestions.

Background

I previously encountered an issue where process was undefined (vite uses import.meta. ...). Vite's config has a define section for adding global constants: adding "process.env": someCustomFallbackObject there worked.

A few Reach-sh library updates later, this (Buffer) problem surfaced.

Solution Attempts

  1. I installed the buffer package and added it as a global in the config file.
import { Buffer } from 'buffer';

export default defineConfig({
  plugins: [ ... ],
  define: {
    "process.env": env,
    Buffer: Buffer
  },
 ...
})

This moved the error message from OP's original report to:

Uncaught TypeError: Cannot read property 'from' of undefined
Screen Shot 2021-07-24 at 5 08 21 PM

After finding this thread, I added the following packages (buffer ^6.0.3 was already installed):

   "path-browserify": "^1.0.1",

And the following to my vite.config.js file:

resolve: {
    alias: {
      ...
      path: require.resolve("path-browserify")
    },

The error message is unchanged. I hunted through node_modules and my best guess is that it still can't find Buffer (since I see references to Buffer.from( ... ).

Any advice? Cheers for taking the time.

(edit: @jdtzmn [thanks!] was right about my omission, which made my sample a bit confusing)

MrJackdaw avatar Jul 25 '21 00:07 MrJackdaw

Knowing nothing about Vite either, wouldn't you have to include the imported Buffer in your defineConfig?

import { Buffer } from 'buffer';

export default defineConfig({
  plugins: [ ... ],
  define: {
    "process.env": env,
    "Buffer": Buffer // <-- This line
  },
 ...
})

You mentioned that you imported it, but it didn't appear in your code sample 🤷‍♂️ .

jdtzmn avatar Jul 26 '21 21:07 jdtzmn

Knowing nothing about Vite either, wouldn't you have to include the imported Buffer in your defineConfig?

import { Buffer } from 'buffer';

export default defineConfig({
  plugins: [ ... ],
  define: {
    "process.env": env,
    "Buffer": Buffer // <-- This line
  },
 ...
})

You mentioned that you imported it, but it didn't appear in your code sample 🤷‍♂️ .

Cheers for the response; I omitted it since the snippets are entirely demonstrative: it was indeed included in the file, which changes the error from

'Buffer' is undefined

to

Uncaught TypeError: Cannot read property 'from' of undefined

Which means the Buffer import is recognized (using the "global" technique outlined above), but the failure persists around the use of Buffer as a global variable in the actual packages.

(@jdtzmn :: double edit: I corrected my original post; thank you!)

MrJackdaw avatar Jul 26 '21 21:07 MrJackdaw

@MrJackdaw two things to try:

  1. Try putting the Buffer override in the alias section (like with path-browserify) instead of the define section.
  2. Try adding a slash to the end of the buffer import, as described in the buffer package's usage section.

jdtzmn avatar Jul 27 '21 13:07 jdtzmn

@jdtzmn Thanks for the suggestion. I tried your suggestion (and a few variations) -- and they ultimately confirmed my suspicion: "buffer" is getting defined, but isn't getting consumed (possibly due to how vite bundles the dev environment).

What was done

  1. I removed my initial import and replaced it with
const Buffer = require("buffer/");

and left Buffer in the define global space: no change to error 2. I removed the Buffer from the global name space and added it to the resolve space:

   ...
   resolve: {
      alias: {
         ...
         Buffer: require("buffer/")
      }
   }

which changed the error back to Buffer is not defined 2. I updated the Buffer alias to

   ...
   resolve: {
      alias: {
         ...
         Buffer: require.resolve("buffer/")
      }
   }

which changed the error to a slightly more descriptive error (with the same stack trace in my original post):

Uncaught TypeError: define_Buffer_default.from is not a function

It would seem that both config.resolve.alias and config.define can be used for some types of global variables--or their effects are somewhat interchangeable in this case. Vite recommends the use of plugins for advanced custom resolution: I tried a few but never got past the "undefined from" error.

Hope this gives more insight; cheers again for the help

MrJackdaw avatar Jul 27 '21 16:07 MrJackdaw

Hey all,

Generally this would be fixed by using a polyfill for the node.js Buffer implementation, this can be done the following way:

npm i --save buffer or yarn add buffer and in your entry-point of vite doing:

import { Buffer } from 'buffer'
globalThis.Buffer = Buffer

This will ensure that even if you use SSR it won't try to assert window being present. To have this work this needs to be your entry-point before ever importing this library.

Alternatively you can use rollup-plugin-node-polyfills in your vite.config by inserting it into the inputOptions

JoviDeCroock avatar Jul 30 '21 08:07 JoviDeCroock

Hey all,

Generally this would be fixed by using a polyfill for the node.js Buffer implementation, this can be done the following way:

npm i --save buffer or yarn add buffer and in your entry-point of vite doing:

import { Buffer } from 'buffer'
globalThis.Buffer = Buffer

This will ensure that even if you use SSR it won't try to assert window being present. To have this work this needs to be your entry-point before ever importing this library.

Alternatively you can use rollup-plugin-node-polyfills in your vite.config by inserting it into the inputOptions

Hey @JoviDeCroock, thanks for taking the time. Unfortunately, rollup-plugin-node-polyfills was one of the first solutions I tried: there's no effect using the rollupInputOptions, since that is a subset of instructions for the build output.

Summary of (unsuccessful) attempts so far

  • Using nodePolyfill
    • This did trigger an environment change, since experimental Vue3 features stopped working.
    • Sadly, the Buffer issue was unresolved by the change
  • Replaced buffer with safe-buffer (made by the same person it seems)
    • This had the same effect as before:
      • Misconfiguration (i.e. failing to define, or properly define buffer) will cause a compile error related to buffer imports
      • Correct configuration (defining both buffer and its Buffer class) results in the Buffer.from is not a function error
  • Tried suggestions from this comment in a Vite github issue.
    • Tried "excluding" both algosdk (used under the hood by @reach-sh/stdlib) as well as the Reach library from dependency optimization
    • Tried including both (of the above) to force dependency optimization by Vite

Is there ... something I'm missing? Or is there any chance in hell that the sdk will switch over to an explicit polyfill (v.s. global reference) for its Buffer usage?

MrJackdaw avatar Aug 07 '21 19:08 MrJackdaw

This is what is working for me:

  1. yarn add buffer

  2. Replace window.Buffer when your point of entry mounts (e.g. __layour.svelte):

import { browser } from '$app/env';  
import { onMount } from 'svelte';

onMount(async () => {
  if (browser) {
    const { Buffer } = await import('buffer')
    window.Buffer = Buffer
  }
})
  1. Load the component importing algosdk dynamically. Example:
<script lang="ts">
import { onMount } from 'svelte';
let Wallet;

onMount(() => {
  if (Wallet) {
    return;
  }

  import('$lib/Wallet.svelte').then(module => {
    Wallet = module.default;
  )}
});
</script>

{#if Wallet}
  <svelte:component this={Wallet} />
{/if}

andsav avatar Dec 20 '21 08:12 andsav

@MrJackdaw

I finally found the fix for this issue, I'll keep it short and simple, install the following packages:

yarn add @esbuild-plugins/node-globals-polyfill path-browserify

Then in vite.config.js make sure you add:

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

import NodeGlobalsPolyfillPlugin from '@esbuild-plugins/node-globals-polyfill'

export default defineConfig({
  resolve: {
    alias: {
      path: 'path-browserify',
    },
  },
  optimizeDeps: {
    esbuildOptions: {
      // Node.js global to browser globalThis
      define: {
          global: 'globalThis'
      },
      // Enable esbuild polyfill plugins
      plugins: [
          NodeGlobalsPolyfillPlugin({
              buffer: true
          })
      ]
    }
  },
})

The key here was the package @esbuild-plugins/node-globals-polyfill, which later brought the path issue, which was resolved adding it in the alias, and installing path-browserify

After this, algosdk is now working fine. Hope it helps!

pnapoliJC avatar Feb 09 '22 19:02 pnapoliJC

Some installation instructions for Vite were added to the FAQ page if anyone new is looking https://github.com/algorand/js-algorand-sdk/blob/develop/FAQ.md#it-says-error-cant-resolve-in-the-sdk

Vite projects

With Vite, you would see:

Uncaught ReferenceError: Buffer is not defined

You will have to install buffer and path-browserify as dependencies.

In vite.config.js, specify:

resolve: {
  alias: {
    path: 'path-browserify';
  }
}

In index.html, add the following:

<script type="module">
  import { Buffer } from 'buffer';
  window.Buffer = Buffer;
</script>

To utilize the Buffer polyfill in production builds, in vite.config.js, add:

import inject from '@rollup/plugin-inject';
export default defineConfig({
  ...,
  build: {
    rollupOptions: {
      plugins: [inject({ Buffer: ['buffer', 'Buffer'] })],
    },
  },
  ...
});

fionnachan avatar Feb 10 '22 04:02 fionnachan

This solved the problem in my case.

closing here as the original issue can be solved with relative configFile option added in 1.0.0-next.8: svelte({configFile:"../svelte.config.js"})

vmanot avatar Jan 09 '23 20:01 vmanot

we use algosdk in vue + vite projects frequently!

in truth, it hasnt always been so straight forward getting algosdk to work in this setup so we made a helpful pkg algonautjs that is ESM native and wraps algosdk.


we also just open-sourced a wallet connection handler lib that supports most all algorand wallets, which you can find here: any-wallet.

spencercap avatar Sep 19 '23 21:09 spencercap

To my knowledge, over the past few months this SDK has eliminated the need for external configuration, so I'm closing this. If someone is still having issues, please comment. It's in our best interest to make using this SDK as easy as possible.

jasonpaulos avatar Sep 20 '23 14:09 jasonpaulos