Skip to content

SHA1 digest (Rollup)

Chung Leong edited this page Jul 25, 2026 · 2 revisions

JavaScript | PHP


In this example we're going to export a function for calculating SHA-1 digests.

Creating the project

We first initialize the Node project and install the necessary modules:

mkdir sha1
cd sha1
npm init -y
npm install --save-dev rollup rollup-plugin-zigar @rollup/plugin-node-resolve
mkdir zig

Next we 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 rollup.config.js:

import nodeResolve from '@rollup/plugin-node-resolve';
import zigar from 'rollup-plugin-zigar';

const input = './zig/sha1.zig';

export default [
  {
    input,
    plugins: [
      zigar({
        optimize: 'ReleaseSmall',
        embedWASM: true,
      }),
      nodeResolve(),
    ],
    output: {
      file: './dist/index.js',
      format: 'esm',
    },
  },
];

Make adjustments to package.json:

  "type": "module",
  "scripts": {
    "build": "rollup -c rollup.config.js"
  },

We're ready to build:

npm run build

Time for some testing in Node:

Welcome to Node.js v22.16.0.
Type ".help" for more information.
> const { sha1 } = await import('./dist/index.js');
undefined
> sha1('hello world').string;
'2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'

We would get the same digest if we calculate it using sha1sum:

echo -n "hello world" | sha1sum
2aae6c35c94fcfb415dbe95f408b9ce91ee846ed  -

Making return value more JS-friendly

In the example, sha1() returns an array of u8. On the JavaScript side it's represented by a object. To get a string, you need to access its string property.

Zigar lets you to flag certain functions as returning strings. To do so, you declare a struct type with a particular name at the root level:

const module_ns = @This();
pub const @"meta(zigar)" = struct {
    pub fn isDeclString(comptime T: type, comptime name: std.meta.DeclEnum(T)) bool {
        return switch (T) {
            module_ns => switch (name) {
                .sha1 => true,
                else => false,
            },
            else => false,
        };
    }
};

During export, isDeclString() is invoked when a function's return value something that can be a interpreted as a text string (e.g. []const u8). With the above declaration in place, we can simplify our JavaScript:

Welcome to Node.js v22.16.0.
Type ".help" for more information.
> const { sha1 } = await import('./dist/index.js');
undefined
> sha1('hello world');
'2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'

You can use the following to threat all occurences of u8 and u16 as text:

pub const @"meta(zigar)" = struct {
    pub fn isDeclString(comptime T: type, comptime _: std.meta.DeclEnum(T)) bool {
        return true;
    }

    pub fn isFieldString(comptime T: type, comptime _: std.meta.FieldEnum(T)) bool {
        return true;
    }
};

Source code

You can find the complete source code for this example here.

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 processing sample

Clone this wiki locally