Skip to content

SHA1 digest (Webpack)

Chung Leong edited this page Apr 25, 2024 · 5 revisions

In this example we're going to create an app that calculates SHA-1 digests of files, using a built-in function of Zig's standard library.

Creating the app

First, we'll create the basic skeleton:

mkdir sha1
cd sha1
npm init -y
npm install --save-dev react react-dom\
    webpack webpack-cli webpack-dev-server css-loader style-loader html-webpack-plugin\
    @babel/core @babel/preset-env @babel/preset-react babel-loader http-server\
    zigar-loader
mkdir src zig

Create sha1.zig:

const std = @import("std");

pub fn sha1(bytes: []const u8) [std.crypto.hash.Sha1.digest_length * 2]u8 {
    var digest: [std.crypto.hash.Sha1.digest_length]u8 = undefined;
    std.crypto.hash.Sha1.hash(bytes, &digest, .{});
    return std.fmt.bytesToHex(digest, .lower);
}

Then App.jsx:

import { useState, useCallback } from 'react';
import { sha1 } from '../zig/sha1.zig';

function App() {
  const [ digest, setDigest ] = useState('-');
  const onChange = useCallback(async (evt) => {
    const [ file ] = evt.target.files;
    if (file) {
      const buffer = await file.arrayBuffer();
      const { string } = sha1(buffer);
      setDigest(string); 
    } else {
      setDigest('-'); 
    }
  });
  return (
    <div className="App">
      <input type="file" onChange={onChange} />
      <h2>{digest}</h2>
    </div>
  );
}

export default App

Then index.js:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

And finally index.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>WebPack + React + Zigar</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

We then create webpack.config.js, identical to the one used in the previous example:

const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.join(__dirname, '/dist'),
    filename: 'bundle.js',
  },
  plugins: [
    new htmlWebpackPlugin({
      template: 'src/index.html',
    }),
  ],
  devServer: {
    port: 3030,
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: 'babel-loader',
      },
      {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader' ],
      },
      {
        test: /\.zig$/,
        exclude: /node_modules/,
        use: 'zigar-loader',
      },
    ],
  },
};

Plus .babelrc, which is also the same:

{
  "presets": [
    "@babel/preset-env",
    [ "@babel/preset-react", { "runtime": "automatic" } ]
  ]
}

In package.json we have the same three commands as previously:

  "scripts": {
    "dev": "webpack serve --mode development",
    "build": "webpack --mode production",
    "preview": "http-server ./dist"
  },

We use the first to verify that our app works:

npm run dev

Creating production build

Just run the following commands:

npm run build
npm run preview

You should notice that the app runs faster now than in dev mode. zigar-loader by default sets optimize to ReleaseSmall when building for production. That removes overhead from Zig's runtime safety system.

Conclusion

This example is still relatively simple. All we're doing is calling a function. It accepts an uncomplicated argument and returns an uncomplicated value. In the next example, the function involved will take more complicated arguments and return something complicated as well.


Image filter sample