algorand / js-algorand-sdk

The official JavaScript SDK for Algorand.
https://algorand.github.io/js-algorand-sdk/
MIT License
285 stars 204 forks source link

Error while importing in Vite (SvelteKit) #398

Closed NoelJacob closed 1 year ago

jasonpaulos commented 3 years ago

@NoelJacob please give more context for this issue

NoelJacob commented 3 years ago

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.

jasonpaulos commented 3 years ago

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.

MrJackdaw commented 3 years ago

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


<img width="579" alt="Screen Shot 2021-07-24 at 5 08 21 PM" src="https://user-images.githubusercontent.com/6107109/126883730-b36629cf-39a4-4748-adc9-a599990b5f02.png">

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

```json
   "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)

jdtzmn commented 3 years ago

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 🤷‍♂️ .

MrJackdaw commented 3 years ago

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!)

jdtzmn commented 3 years ago

@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.
MrJackdaw commented 3 years ago

@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

  3. 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

JoviDeCroock commented 3 years ago

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

MrJackdaw commented 3 years ago

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

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?

andsav commented 2 years ago

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}
pnapoliJC commented 2 years ago

@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!

fionnachan commented 2 years ago

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'] })],
    },
  },
  ...
});
vmanot commented 1 year ago

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"})

spencercap commented 1 year ago

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.

jasonpaulos commented 1 year ago

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.