Search code examples
node.jssvelterolluprollupjs

How do I import crypto in Svelte?


I created a basic svelte project (it uses rollup). I want to import a native library, crypto into the project like so.

// src/lib/encrypt.js

import { randomBytes } from "crypto";

const encrypt = () => {
  return randomBytes(4);
};

export { encrypt };

and import this into a svelte file like so,

// src/App.svelte
<script>
    import {encrypt} from './lib/encrypt';
</script>

<main>
    <p>{encrypt()}</p>
</main>

Doing so, yields,

Uncaught TypeError: crypto.randomBytes is not a function
    at encrypt (encrypt.js:4)
    at Object.create [as c] (App.svelte:9)
    at init (index.mjs:1496)
    at new App (App.svelte:3)
    at main.js:3

How do I enable crypto to work for my scenario?

Note: This probably has something to do with the way rollup works. Upon some researching, I found that rollup-plugin-node-builtins is used to load native modules. This didn't seem to work either. I tried configuring the rollup.config.js

// rollup.config.js
import svelte from "rollup-plugin-svelte";
import commonjs from "@rollup/plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";
import livereload from "rollup-plugin-livereload";
import { terser } from "rollup-plugin-terser";
import css from "rollup-plugin-css-only";
import preprocess from "svelte-preprocess";
import builtins from "rollup-plugin-node-builtins";

const production = !process.env.ROLLUP_WATCH;

function serve() {
  let server;

  function toExit() {
    if (server) server.kill(0);
  }

  return {
    writeBundle() {
      if (server) return;
      server = require("child_process").spawn(
        "npm",
        ["run", "start", "--", "--dev"],
        {
          stdio: ["ignore", "inherit", "inherit"],
          shell: true,
        }
      );

      process.on("SIGTERM", toExit);
      process.on("exit", toExit);
    },
  };
}

export default {
  input: "src/main.js",
  output: {
    sourcemap: true,
    format: "iife",
    name: "app",
    file: "public/build/bundle.js",
  },

  plugins: [
    svelte({
      preprocess: preprocess(),
      compilerOptions: {
        // enable run-time checks when not in production
        dev: !production,
      },
    }),
    css({ output: "bundle.css" }),
    resolve({
      browser: true,
      dedupe: ["svelte"],
    }),
    builtins({ crypto: true }),
    commonjs(),
    !production && serve(),
    !production && livereload("public"),
    production && terser(),
  ],
  watch: {
    clearScreen: false,
  },
};

This results in,

[!] Error: Unexpected token (Note that you need @rollup/plugin-json to import JSON files)
 

Solution

  • crypto is a Node package intended to be run on the server and is tricky to run in the browser. rollup-plugin-node-builtins seems to be deprecated. Its successor states in the README that shimming the crypto package likely won't work:

    Crypto is not shimmed and and we just provide the commonjs one from browserify and it will likely not work, if you really want it please pass {crypto: true} as an option.

    You may need to find an alternative package that is intended to be used in the browser. Depending on your use case, you could also look into the native web crypto API.