Skip to content

paulmillr/jsbt

Repository files navigation

@paulmillr/jsbt

JS Build Tools: helpers for building, benchmarking & testing secure JS apps.

  • 🤖 workflows: Secure GitHub CI actions for testing & token-less publishing of JS packages using OIDC.
  • 🏋🏻 bench: Benchmark JS projects with nanosecond resolution.
  • 📝 test: Multi-env testing framework with familiar syntax & parallel execution
  • 🏗️ jsbt: Single CLI for release builds and shared doc/tree-shaking audits
  • ⚙️ tsconfig: Strict typescript configs, friendly to type stripping
  • 🪶 No dependencies

Used by noble cryptography and others.

Usage

npm install @paulmillr/jsbt

jsr add jsr:@paulmillr/jsbt

CI workflows

Secure GitHub CI configs for testing & publishing JS packages.

The files reside in .github/workflows:

  • test-js.yml: runs tests on LTS node.js, bun, deno, linter, and calculates coverage
  • test-ts.yml: the same, but runs typescript instead of js on supported node.js (v22+) On node.js v26, it executes test:tsc to compile files instead.
  • release.yml publishes package on NPM, JSR and creates single-file output if it exists
    • Uses brand new token-less GitHub OIDC connector to NPM, ensure to link package in npm settings first
    • The Trusted Publishing also provides provenance statements by default
    • It happens after GitHub release is created

You can copy them, or depend on them directly:

name: Publish release
on:
  release:
    types: [created]
jobs:
  release-js:
    name: 'jsbt v0.4.5'
    uses: paulmillr/jsbt/.github/workflows/release.yml@570adcfe0ed96b477bb9b35400fb43fd9406fb47
    permissions:
      contents: read
      id-token: write

bench

Benchmark JS projects with nanosecond resolution

  • Precise: 1ns resolution using process.hrtime
  • Lightweight: ~200 lines of code
  • Readable: utilizes colors and nice units, shows rel. margin of error only if it's high
import bench from '@paulmillr/jsbt/bench.js';
(async () => {
  await bench('printing', () => Promise.resolve(0));
  await bench('base', () => Promise.resolve(1));
  await bench('sqrt', 10000, () => Math.sqrt(2));
})();

Example output:

getPublicKey x 6,072 ops/sec @ 164μs/op ± 8.22% [143μs..17ms]
sign x 4,980 ops/sec @ 200μs/op
verify x 969 ops/sec @ 1ms/op
recoverPublicKey x 890 ops/sec @ 1ms/op
getSharedSecret x 585 ops/sec @ 1ms/op

test

Multi-env testing framework with familiar syntax & parallel execution.

  • Familiar syntax: similar to Mocha / Jest / Vitest
  • Multi-env: runs on node.js, deno, bun, cloudflare, browsers and others
  • No "global" magic: it.run() in the end simplifies logic and browser runs
  • Parallel: easily run in node.js and bun
  • Great UI: beautiful tree reporter, optional "quiet" dot reporter

node a.test.js

MSHOULD_FAST=1 MSHOULD_QUIET=1 node a.test.js

import { should } from 'micro-should';
import { equal } from 'node:assert';
// Any assertion library can be used e.g. Chai or Expect.js
should('add', () => {
  equal(2 + 2, 4);
});
should('work in async env', async () => {
  equal(await Promise.resolve(123), 123);
});
describe('nested', () => {
  describe('nested 2', () => {
    should('multiply', () => {
      equal(2 * 2, 4);
    });
    should.skip('disable test by using skip', () => {
      equal(true, false); // skip
    });
    // should.only('execute only one test', () => {
    //   equal(true, true);
    // });
  });
});

should.runWhen(import.meta.url);
// or
// should.run();
// should.opts.STOP_AT_ERROR = false; // default=true
// should.opts.MSHOULD_QUIET = true; // same as env var

Usage:

  • should(title, case) or it(title, case) syntax to register a test function
  • should.only, should.skip allows to limit tests to one case / skip tests
  • beforeEach, afterEach execute code before / after function in describe block
  • should.runWhen(import.meta.url) must be executed in the end
    • The helper ensures tests are not ran when imported from other file
    • It compares import.meta.url to CLI argument
  • should.run() or it.run() must always be executed in the end

ENV variables, specifiable via command line or through code:

  • MSHOULD_FAST=1 enables parallel execution in node.js and Bun. Values >1 will set worker count.
  • MSHOULD_QUIET=1 enables "quiet" dot reporter

jsbt

jsbt dispatches the release build and shared audit helpers shipped by the package.

Current subcommands:

jsbt esbuild: bundle one repo's test/build input via esbuild

jsbt esbuild <build-dir> [--auto] [--no-prefix]
jsbt check <package.json>
jsbt check <package.json> bigint
jsbt check <package.json> bytes
jsbt check <package.json> comments
jsbt check <package.json> errors
jsbt check <package.json> importtime
jsbt check <package.json> jsdoc
jsbt check <package.json> jsr
jsbt check <package.json> jsrpublish
jsbt check <package.json> mutate
jsbt check <package.json> patterns
jsbt check <package.json> readme
jsbt check <package.json> tests
jsbt check <package.json> treeshake [out-dir]
jsbt check <package.json> tsdoc
jsbt check <package.json> typeimport
jsbt check-install <package.json>
jsbt bigint <package.json>
jsbt bytes <package.json>
jsbt comments <package.json>
jsbt errors <package.json>
jsbt importtime <package.json>
jsbt jsr <package.json>
jsbt jsrpublish <package.json>
jsbt mutate <package.json>
jsbt readme <package.json>
jsbt tests <package.json>
jsbt treeshake <package.json> <out-dir>
jsbt typeimport <package.json>
jsbt tsdoc <package.json>

With the standard package script:

npm run check bigint
npm run check bytes
npm run check comments
npm run check errors
npm run check importtime
npm run check jsdoc
npm run check jsr
npm run check jsrpublish
npm run check mutate
npm run check patterns
npm run check readme
npm run check tests
npm run check treeshake
npm run check tsdoc
npm run check typeimport

The published package exposes a single bin, so all of these work through:

npx --no @paulmillr/jsbt <subcommand> ...

The command would execute following subcommands and produce several files:

cd test/build
npm install
npx esbuild --bundle input.js --outfile=out/noble-hashes.js --global-name=nobleHashes
npx esbuild --bundle input.js --outfile=out/noble-hashes.min.js --global-name=nobleHashes --minify
# 11d1900e99f3aa945603bb5e7d82bdd9ec6ddf5d30e2fcab69b836840cff76d2 test/build/out/noble-hashes.js
# 0be3876ff0816c44d21a401e6572fdb76d06012c760a23a5cb771c6f612106f5 test/build/out/noble-hashes.min.js

3790 LOC noble-hashes.js
58.21 KB noble-hashes.min.js
21.10 KB +gzip
19.57 KB +zstd

tsconfig

Strict typescript v6+ configs, friendly to type stripping.

  • tsconfig.test.json is for typescript tests, with looser checks

Option descriptions:

  • isolatedDeclarations ensures types are "fast" and friendly to JSR.io
  • verbatimModuleSyntax - ensures files are friendly to "type erasure" / "type ignore" node.js and others

License

MIT License

About

JS Build Tools: helpers for building, benchmarking & testing secure JS apps.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors