Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue3/Vite: module has been externalized

Tags:

vue.js

vite

I'm trying to use crypto to hash strings in a Vue 3 app.

async function hash (token) {
    const data = new TextEncoder().encode(token)
    const byteHash = await crypto.subtle.digest("SHA-256", data)
    //                            ^ the below error is thrown here

    const arrayHash = Array.from(new Uint8Array(byteHash))
    const hexHash = arrayHash.map(b => b.toString(16).padStart(2, '0')).join('').toLocaleUpperCase()

    return hexHash
}

From my understanding, crypto is available in the browser nowadays, so it needs no browserify replacement.

Nevertheless, I'm getting the following error in my browser console:

Error: Module "crypto" has been externalized for browser compatibility. Cannot access "crypto.subtle" in client code.

I interpret this as "Vite is configured to externalize the crypto module in the build process". But I can see no such setting in my vite.config.js:

// Plugins:
import vue from '@vitejs/plugin-vue'
import vuetify from 'vite-plugin-vuetify'

// Utilies:
import { defineConfig } from 'vite'
import { fileURLToPath, URL } from 'node:url'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin
    vuetify({
      autoImport: true
    })
  ],
  define: { 'process.env': {} },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    },
    extensions: ['.js', '.json', '.jsx', '.mjs', '.ts', '.tsx', '.vue']
  },
  server: {
    port: 3000
  },
  test: {
    setupFiles: ['../vuetify.config.js'],
    deps: {
      inline: ['vuetify']
    },
    globals: true
  }
})

Are there any "baked in" Vite default settings, that could cause this? Is this configured somewere else? How can I fix this issue and use the crypto module in my app?

like image 547
Fred Avatar asked Sep 18 '25 08:09

Fred


1 Answers

The issue lies in the fact that NodeJS and the Browser both have a module called crypto that implements the webcrypto standard. Those modules are compatible, but need to be accessed differently. In NodeJS it can be used directly but in the browser it is provided by the window context that does not exist in NodeJS.

You don't see the difference if you are working directly in the browser, as window is the default context, but Vite is working in the NodeJS context, so it (correctly) decides that this module is not available as crypto in the browser and thus throws the error. It does not know/care that this module exists in the browser as well but as window.crypto.

Maybe this can be configured in vite.config.js somehow, but I am not very familiar with it.

I came up with the following solution instead, which works in both environments:

function getCrypto() {
  try {
    return window.crypto;
  } catch {
    return crypto;
  }
}
async function hash(token) {
  const compatibleCrypto = getCrypto();

  const data = new TextEncoder().encode(token);
  const byteHash = await compatibleCrypto.subtle.digest('SHA-256', data);

  const arrayHash = Array.from(new Uint8Array(byteHash));
  const hexHash = arrayHash
    .map(b => b.toString(16).padStart(2, '0'))
    .join('')
    .toLocaleUpperCase();

  return hexHash;
}

This function works in both environments now.

like image 160
Fred Avatar answered Sep 21 '25 10:09

Fred