finally ESM working with tests!!

This commit is contained in:
Juanra Dikal 2022-08-01 19:10:00 +02:00
parent 16c2723ced
commit 1ed83623e7
25 changed files with 478 additions and 2385 deletions

View File

@ -1,11 +1,15 @@
#! /usr/bin/env node
const path = require('path')
const childProcess = require('child_process')
import { join, resolve } from 'path'
import { fork } from 'child_process'
import minimatch from 'minimatch'
import glob from 'glob'
import { fileURLToPath } from 'url'
const { sync } = glob
const __dirname = resolve(fileURLToPath(import.meta.url), '../')
const rootDir = join(__dirname, '../..')
const rootDir = path.join(__dirname, '../..')
const mochaTsRelativeDir = '.mocha-ts'
const minimatch = require('minimatch')
const glob = require('glob')
// First let us prepare the args to pass to mocha.
// ts.files will be replaced by their js-transpiled counterparts
@ -13,13 +17,13 @@ const glob = require('glob')
const processedArgs = processArgs(process.argv.slice(2))
// Now we can run a script and invoke a callback when complete, e.g.
runScript(path.join(rootDir, 'node_modules/mocha/bin/mocha'), processedArgs)
runScript(join(rootDir, 'node_modules/mocha/bin/mocha'), processedArgs)
function processArgs (args) {
args = process.argv.slice(2).map(arg => {
// Let us first remove surrounding quotes in string (it gives issues in windows)
arg = arg.replace(/^['"]/, '').replace(/['"]$/, '')
const filenames = glob.sync(arg, { cwd: rootDir, matchBase: true })
const filenames = sync(arg, { cwd: rootDir, matchBase: true })
if (filenames.length > 0) {
return filenames.map(file => {
const isTsTestFile = minimatch(file, '{test/**/*.ts,src/**/*.spec.ts}', { matchBase: true })
@ -58,7 +62,7 @@ function processArgs (args) {
}
function runScript (scriptPath, args) {
const mochaCmd = childProcess.fork(scriptPath, args, {
const mochaCmd = fork(scriptPath, args, {
cwd: rootDir
})

View File

@ -7,7 +7,7 @@ import typescriptPlugin from '@rollup/plugin-typescript'
import commonjs from '@rollup/plugin-commonjs'
import json from '@rollup/plugin-json'
import { join } from 'path'
import { dirname, join } from 'path'
import { existsSync } from 'fs-extra'
import { directories, name as _name, exports } from '../package.json'
import { compile } from './rollup-plugin-dts.js'
@ -145,7 +145,7 @@ export default [
typescriptPlugin(tsBundleOptions),
resolve({
browser: false,
exportConditions: ['node', 'module', 'import']
exportConditions: ['require', 'node', 'module', 'import']
}),
commonjs({ extensions: ['.js', '.cjs', '.ts', '.jsx', '.cjsx', '.tsx'] }), // the ".ts" extension is required
json()
@ -163,12 +163,14 @@ export default [
plugins: [
replace({
IS_BROWSER: false,
__filename: `'${join(rootDir, exports['.'].node.import)}'`,
__dirname: `'${dirname(join(rootDir, exports['.'].node.import))}'`,
preventAssignment: true
}),
typescriptPlugin(tsBundleOptions),
resolve({
browser: false,
exportConditions: ['node', 'module', 'import']
exportConditions: ['node']
}),
commonjs({ extensions: ['.js', '.cjs', '.ts', '.jsx', '.cjsx', '.tsx'] }), // the ".ts" extension is required
json()

View File

@ -1,13 +1,13 @@
const EventEmitter = require('events')
const fs = require('fs')
const path = require('path')
import EventEmitter from 'events'
import { mkdirSync, writeFileSync, rmSync } from 'fs'
import { dirname } from 'path'
module.exports = class Builder extends EventEmitter {
export default class Builder extends EventEmitter {
constructor (semaphoreFile, name = 'builder') {
super()
this.name = name
this.firstBuild = true
fs.mkdirSync(path.dirname(semaphoreFile), { recursive: true })
mkdirSync(dirname(semaphoreFile), { recursive: true })
this.semaphoreFile = semaphoreFile
this._ready = false
@ -26,7 +26,7 @@ module.exports = class Builder extends EventEmitter {
this.on('ready', () => {
if (this.firstBuild === false) {
fs.writeFileSync(this.semaphoreFile, '', 'utf-8')
writeFileSync(this.semaphoreFile, '', 'utf-8')
} else {
this.firstBuild = false
}
@ -54,6 +54,6 @@ module.exports = class Builder extends EventEmitter {
async close () {}
clean () {
fs.rmSync(this.semaphoreFile, { force: true })
rmSync(this.semaphoreFile, { force: true })
}
}

View File

@ -1,18 +1,25 @@
const EventEmitter = require('events')
const fs = require('fs')
const path = require('path')
import EventEmitter from 'events'
import { writeFileSync, existsSync } from 'fs'
import { join, resolve } from 'path'
import { fileURLToPath } from 'url'
const rollup = require('rollup')
const loadAndParseConfigFile = require('rollup/dist/loadConfigFile.js')
import { watch as _watch, rollup as _rollup } from 'rollup'
import loadAndParseConfigFile from 'rollup/dist/loadConfigFile.js'
const Builder = require('./Builder.cjs')
import Builder from './Builder.js'
const rootDir = path.join(__dirname, '../../../../')
const pkgJson = require(path.join(rootDir, 'package.json'))
const __dirname = resolve(fileURLToPath(import.meta.url), '../')
module.exports = class RollupBuilder extends Builder {
constructor ({ name = 'rollup', configPath = path.join(rootDir, 'rollup.config.js'), tempDir = path.join(rootDir, '.mocha-ts'), watch = false }) {
super(path.join(tempDir, 'semaphore'), name)
const rootDir = join(__dirname, '../../../../')
const pkgJson = (await import(join(rootDir, 'package.json'), {
assert: {
type: "json",
}
})).default
export default class RollupBuilder extends Builder {
constructor ({ name = 'rollup', configPath = join(rootDir, 'rollup.config.js'), tempDir = join(rootDir, '.mocha-ts'), watch = false }) {
super(join(tempDir, 'semaphore'), name)
this.configPath = configPath
this.watch = watch
}
@ -21,14 +28,16 @@ module.exports = class RollupBuilder extends Builder {
await super.start()
const { options } = await loadAndParseConfigFile(this.configPath)
// Watch only the Node CJS module, that is the one we are going to use with mocha
// Watch only the Node ESM module, that is the one we are going to use with mocha
const rollupOptions = options.filter(bundle => {
const file = (bundle.output[0].dir !== undefined)
? path.join(bundle.output[0].dir, bundle.output[0].entryFileNames)
? join(bundle.output[0].dir, bundle.output[0].entryFileNames)
: bundle.output[0].file
return file === path.join(rootDir, pkgJson.main)
return file === join(rootDir, pkgJson.exports['.'].node.import)
})[0]
delete rollupOptions.output.pop() // remove the second output
if (rollupOptions.output.length > 1) {
rollupOptions.output = rollupOptions.output[0]
}
this.builder = new RollupBundler(rollupOptions, this.watch)
@ -55,7 +64,7 @@ module.exports = class RollupBuilder extends Builder {
case 'ERROR':
if (event.result) event.result.close()
this.emit('error', event.error)
fs.writeFileSync(path.join(rootDir, pkgJson.main), '', 'utf8')
writeFileSync(join(rootDir, pkgJson.exports['.'].node.import), '', 'utf8')
this.emit('ready')
break
@ -85,13 +94,13 @@ class RollupBundler extends EventEmitter {
async start () {
if (this.watch === true) {
this.watcher = rollup.watch(this.rollupOptions)
this.watcher = _watch(this.rollupOptions)
this.watcher.on('event', event => {
this.emit('event', event)
})
} else {
if (fs.existsSync(path.join(rootDir, pkgJson.main)) === false) {
if (existsSync(join(rootDir, pkgJson.exports['.'].node.import)) === false) {
await this._bundle()
} else {
this.emit('event', { code: 'END', noBuild: true })
@ -103,7 +112,7 @@ class RollupBundler extends EventEmitter {
this.emit('event', { code: 'START' })
for (const optionsObj of [].concat(this.rollupOptions)) {
try {
const bundle = await rollup.rollup(optionsObj)
const bundle = await _rollup(optionsObj)
try {
await Promise.all(optionsObj.output.map(bundle.write))
this.emit('event', { code: 'BUNDLE_END' })

View File

@ -1,108 +0,0 @@
const path = require('path')
const fs = require('fs')
const ts = require('typescript')
const JSON5 = require('json5')
const Builder = require('./Builder.cjs')
const rootDir = path.join(__dirname, '../../../../')
const mochaTsRelativeDir = '.mocha-ts'
const mochaTsDir = path.join(rootDir, mochaTsRelativeDir)
const formatHost = {
getCanonicalFileName: path => path,
getCurrentDirectory: ts.sys.getCurrentDirectory,
getNewLine: () => ts.sys.newLine
}
module.exports = class TestsBuilder extends Builder {
constructor ({ name = 'tsc', configPath = path.join(rootDir, 'tsconfig.json'), tempDir = mochaTsDir }) {
super(path.join(tempDir, 'semaphore'), name)
if (fs.existsSync(configPath) !== true) throw new Error(`Couldn't find a tsconfig file at ${configPath}`)
this.tempDir = tempDir
const tsConfig = JSON5.parse(fs.readFileSync(configPath, 'utf8'))
tsConfig.file = undefined
// Exclude already transpiled files in src
tsConfig.exclude = ['src/ts/**/!(*.spec).ts']
// "noResolve": true
tsConfig.compilerOptions.noResolve = false
// we don't need declaration files
tsConfig.compilerOptions.declaration = false
// we need to emit files
tsConfig.compilerOptions.noEmit = false
// source mapping eases debuging
tsConfig.compilerOptions.sourceMap = true
// This prevents SyntaxError: Cannot use import statement outside a module
// tsConfig.compilerOptions.module = 'commonjs'
// Removed typeroots (it causes issues)
tsConfig.compilerOptions.typeRoots = undefined
tsConfig.compilerOptions.outDir = path.isAbsolute(tempDir) ? path.relative(rootDir, tempDir) : tempDir
this.tempTsConfigPath = path.join(rootDir, '.tsconfig.json')
fs.writeFileSync(this.tempTsConfigPath, JSON.stringify(tsConfig, undefined, 2))
const createProgram = ts.createSemanticDiagnosticsBuilderProgram
const reportDiagnostic = (diagnostic) => {
const filePath = path.relative(rootDir, diagnostic.file.fileName)
const tranpiledJsPath = `${path.join(tempDir, filePath).slice(0, -3)}.js`
const errorLine = diagnostic.file.text.slice(0, diagnostic.start).split(/\r\n|\r|\n/).length
if (fs.existsSync(tranpiledJsPath)) {
fs.writeFileSync(tranpiledJsPath, '', 'utf8')
}
this.emit('error', `[Error ${diagnostic.code}]`, `${filePath}:${errorLine}`, ':', ts.flattenDiagnosticMessageText(diagnostic.messageText, formatHost.getNewLine()))
}
const reportWatchStatusChanged = (diagnostic, newLine, options, errorCount) => {
if (errorCount !== undefined) {
this.emit('ready')
} else {
this.emit('busy')
if (diagnostic.code === 6031) {
this.emit('message', 'transpiling your tests...')
} else if (diagnostic.code === 6032) {
this.emit('message', 'file changes detected. Transpiling your tests...')
}
}
}
// Note that there is another overload for `createWatchCompilerHost` that takes
// a set of root files.
this.host = ts.createWatchCompilerHost(
this.tempTsConfigPath,
{},
ts.sys,
createProgram,
reportDiagnostic,
reportWatchStatusChanged
)
}
async start () {
await super.start()
// `createWatchProgram` creates an initial program, watches files, and updates
// the program over time.
this.watcher = ts.createWatchProgram(this.host)
return await this.ready()
}
async close () {
await super.close()
this.watcher.close()
fs.unlinkSync(this.tempTsConfigPath)
}
}

View File

@ -0,0 +1,127 @@
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'fs'
import json5 from 'json5'
import { isAbsolute, join, relative, resolve } from 'path'
import { fileURLToPath } from 'url'
import typescript from 'typescript'
import Builder from './Builder.js'
const { parse } = json5
const { sys, createSemanticDiagnosticsBuilderProgram, flattenDiagnosticMessageText, createWatchCompilerHost, createWatchProgram } = typescript
const __dirname = resolve(fileURLToPath(import.meta.url), '../')
const rootDir = join(__dirname, '../../../../')
const pkgJson = (await import(join(rootDir, 'package.json'), {
assert: {
type: "json",
}
})).default
const mochaTsRelativeDir = '.mocha-ts'
const mochaTsDir = join(rootDir, mochaTsRelativeDir)
const formatHost = {
getCanonicalFileName: path => path,
getCurrentDirectory: sys.getCurrentDirectory,
getNewLine: () => sys.newLine
}
export default class TestsBuilder extends Builder {
constructor ({ name = 'tsc', configPath = join(rootDir, 'tsconfig.json'), tempDir = mochaTsDir }) {
super(join(tempDir, 'semaphore'), name)
if (existsSync(configPath) !== true) throw new Error(`Couldn't find a tsconfig file at ${configPath}`)
this.tempDir = tempDir
this.tempPkgJsonPath = join(tempDir, 'package.json')
delete pkgJson.type
writeFileSync(this.tempPkgJsonPath, JSON.stringify(pkgJson, undefined, 2))
const tsConfig = parse(readFileSync(configPath, 'utf8'))
tsConfig.file = undefined
// Exclude already transpiled files in src
tsConfig.exclude = ['src/ts/**/!(*.spec).ts']
// "noResolve": true
tsConfig.compilerOptions.noResolve = false
// we don't need declaration files
tsConfig.compilerOptions.declaration = false
// we need to emit files
tsConfig.compilerOptions.noEmit = false
// source mapping eases debuging
tsConfig.compilerOptions.sourceMap = true
// This prevents SyntaxError: Cannot use import statement outside a module
// tsConfig.compilerOptions.module = 'commonjs'
// Removed typeroots (it causes issues)
tsConfig.compilerOptions.typeRoots = undefined
tsConfig.compilerOptions.outDir = isAbsolute(tempDir) ? relative(rootDir, tempDir) : tempDir
this.tempTsConfigPath = join(rootDir, '.tsconfig.json')
writeFileSync(this.tempTsConfigPath, JSON.stringify(tsConfig, undefined, 2))
const createProgram = createSemanticDiagnosticsBuilderProgram
const reportDiagnostic = (diagnostic) => {
const filePath = relative(rootDir, diagnostic.file.fileName)
const tranpiledJsPath = `${join(tempDir, filePath).slice(0, -3)}.js`
const errorLine = diagnostic.file.text.slice(0, diagnostic.start).split(/\r\n|\r|\n/).length
if (existsSync(tranpiledJsPath)) {
writeFileSync(tranpiledJsPath, '', 'utf8')
}
this.emit('error', `[Error ${diagnostic.code}]`, `${filePath}:${errorLine}`, ':', flattenDiagnosticMessageText(diagnostic.messageText, formatHost.getNewLine()))
}
const reportWatchStatusChanged = (diagnostic, newLine, options, errorCount) => {
if (errorCount !== undefined) {
this.emit('ready')
} else {
this.emit('busy')
if (diagnostic.code === 6031) {
this.emit('message', 'transpiling your tests...')
} else if (diagnostic.code === 6032) {
this.emit('message', 'file changes detected. Transpiling your tests...')
}
}
}
// Note that there is another overload for `createWatchCompilerHost` that takes
// a set of root files.
this.host = createWatchCompilerHost(
this.tempTsConfigPath,
{},
sys,
createProgram,
reportDiagnostic,
reportWatchStatusChanged
)
}
async start () {
await super.start()
// `createWatchProgram` creates an initial program, watches files, and updates
// the program over time.
this.watcher = createWatchProgram(this.host)
return await this.ready()
}
async close () {
await super.close()
this.watcher.close()
unlinkSync(this.tempTsConfigPath)
}
}

View File

@ -1,76 +0,0 @@
'use strict'
const path = require('path')
const chai = require('chai')
const rimraf = require('rimraf')
const RollupBuilder = require('./builders/RollupBuilder.cjs')
const TestsBuilder = require('./builders/TestsBuilder.cjs')
require('dotenv').config()
const rootDir = path.join(__dirname, '../../../')
global.chai = chai
loadPkgToGlobal()
global.IS_BROWSER = false
const watch = process.argv.includes('--watch') || process.argv.includes('-w')
const tempDir = path.join(rootDir, '.mocha-ts')
const rollupBuilder = new RollupBuilder({ name: 'rollup', configPath: path.join(rootDir, 'build/rollup.config.js'), tempDir, watch })
const testBuilder = new TestsBuilder({ name: 'tsc', tempDir })
rollupBuilder.start() // This should be in exports.mochaGlobalSetup but mocha fails when not in watch mode (DIRT...)
testBuilder.start() // This should be in exports.mochaGlobalSetup but mocha fails when not in watch mode (DIRT...)
exports.mochaHooks = {
beforeAll: [
async function () {
this.timeout('120000')
await Promise.all([rollupBuilder.ready(), testBuilder.ready()])
// Just in case our module had been modified. Reload it when the tests are repeated (for mocha watch mode).
delete require.cache[require.resolve(rootDir)]
loadPkgToGlobal()
// And now reset any other transpiled module (just delete the cache so it is fully reloaded)
for (const key in require.cache) {
const relativePath = path.relative(rootDir, key)
if (relativePath.startsWith(`.mocha-ts${path.sep}`)) {
delete require.cache[key]
}
}
}
]
}
// exports.mochaGlobalSetup = async function () {
// await rollupBuilder.start()
// await testBuilder.start()
// }
exports.mochaGlobalTeardown = async function () {
await testBuilder.close()
await rollupBuilder.close()
// I use the sync version of rimraf precisely because it blocks the
// main thread and thus the mocha watcher, which otherwise would complain
// about files being deleted
rimraf.sync(tempDir, { disableGlob: true })
}
function loadPkgToGlobal () {
const _pkg = require(rootDir)
if (typeof _pkg === 'function') { // If it is just a default export, load it as named (for compatibility)
global._pkg = {
default: _pkg
}
} else {
global._pkg = _pkg
}
}

View File

@ -0,0 +1,84 @@
'use strict'
import { join, relative, resolve, sep } from 'path'
import chai from 'chai'
import rimraf from 'rimraf'
import { fileURLToPath } from 'url'
import RollupBuilder from './builders/RollupBuilder.js'
import TestsBuilder from './builders/TestsBuilder.js'
import 'dotenv/config'
const __dirname = resolve(fileURLToPath(import.meta.url), '../')
const rootDir = join(__dirname, '../../../')
const pkgJson = (await import(join(rootDir, 'package.json'), {
assert: {
type: "json",
}
})).default
global.chai = chai
async function reloadModule () {
const _pkg = await import(join(rootDir, pkgJson.exports['.'].node.import + `?update=${Date.now()}`))
return _pkg
// if (typeof _pkg === 'function') { // If it is just a default export, load it as named (for compatibility)
// global._pkg = {
// default: _pkg
// }
// } else {
// global._pkg = _pkg
// }
}
global._pkg = await reloadModule()
global.IS_BROWSER = false
const watch = process.argv.includes('--watch') || process.argv.includes('-w')
const tempDir = join(rootDir, '.mocha-ts')
const rollupBuilder = new RollupBuilder({ name: 'rollup', configPath: join(rootDir, 'build/rollup.config.js'), tempDir, watch })
const testBuilder = new TestsBuilder({ name: 'tsc', tempDir })
rollupBuilder.start() // This should be in exports.mochaGlobalSetup but mocha fails when not in watch mode (DIRT...)
testBuilder.start() // This should be in exports.mochaGlobalSetup but mocha fails when not in watch mode (DIRT...)
export const mochaHooks = {
beforeAll: [
async function () {
this.timeout('120000')
await Promise.all([rollupBuilder.ready(), testBuilder.ready()])
// Just in case our module had been modified. Reload it when the tests are repeated (for mocha watch mode).
// delete require.cache[require.resolve(rootDir)]
global._pkg = await reloadModule()
// And now reset any other transpiled module (just delete the cache so it is fully reloaded)
// for (const key in require.cache) {
// const relativePath = relative(rootDir, key)
// if (relativePath.startsWith(`.mocha-ts${sep}`)) {
// delete require.cache[key]
// }
// }
}
]
}
// exports.mochaGlobalSetup = async function () {
// await rollupBuilder.start()
// await testBuilder.start()
// }
export const mochaGlobalTeardown = async function () {
await testBuilder.close()
await rollupBuilder.close()
// I use the sync version of rimraf precisely because it blocks the
// main thread and thus the mocha watcher, which otherwise would complain
// about files being deleted
rimraf.sync(tempDir, { disableGlob: true })
}

5
dist/bundles/esm.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -155,7 +155,7 @@ A promise that resolves to a boolean that is either true (a probably prime numbe
#### Defined in
[src/ts/isProbablyPrime.ts:21](https://github.com/juanelas/bigint-crypto-utils/blob/f25bb8c/src/ts/isProbablyPrime.ts#L21)
[src/ts/isProbablyPrime.ts:21](https://github.com/juanelas/bigint-crypto-utils/blob/16c2723/src/ts/isProbablyPrime.ts#L21)
___
@ -322,7 +322,7 @@ A promise that resolves to a bigint probable prime of bitLength bits.
#### Defined in
[src/ts/prime.ts:25](https://github.com/juanelas/bigint-crypto-utils/blob/f25bb8c/src/ts/prime.ts#L25)
[src/ts/prime.ts:25](https://github.com/juanelas/bigint-crypto-utils/blob/16c2723/src/ts/prime.ts#L25)
___
@ -352,7 +352,7 @@ A bigint probable prime of bitLength bits.
#### Defined in
[src/ts/prime.ts:102](https://github.com/juanelas/bigint-crypto-utils/blob/f25bb8c/src/ts/prime.ts#L102)
[src/ts/prime.ts:103](https://github.com/juanelas/bigint-crypto-utils/blob/16c2723/src/ts/prime.ts#L103)
___
@ -381,7 +381,7 @@ A cryptographically secure random bigint between [min,max]
#### Defined in
[src/ts/randBetween.ts:15](https://github.com/juanelas/bigint-crypto-utils/blob/f25bb8c/src/ts/randBetween.ts#L15)
[src/ts/randBetween.ts:15](https://github.com/juanelas/bigint-crypto-utils/blob/16c2723/src/ts/randBetween.ts#L15)
___
@ -410,7 +410,7 @@ A Promise that resolves to a UInt8Array/Buffer (Browser/Node.js) filled with cry
#### Defined in
[src/ts/randBits.ts:14](https://github.com/juanelas/bigint-crypto-utils/blob/f25bb8c/src/ts/randBits.ts#L14)
[src/ts/randBits.ts:14](https://github.com/juanelas/bigint-crypto-utils/blob/16c2723/src/ts/randBits.ts#L14)
___
@ -439,7 +439,7 @@ A Uint8Array/Buffer (Browser/Node.js) filled with cryptographically secure rando
#### Defined in
[src/ts/randBits.ts:45](https://github.com/juanelas/bigint-crypto-utils/blob/f25bb8c/src/ts/randBits.ts#L45)
[src/ts/randBits.ts:45](https://github.com/juanelas/bigint-crypto-utils/blob/16c2723/src/ts/randBits.ts#L45)
___
@ -468,7 +468,7 @@ A promise that resolves to a UInt8Array/Buffer (Browser/Node.js) filled with cry
#### Defined in
[src/ts/randBytes.ts:14](https://github.com/juanelas/bigint-crypto-utils/blob/f25bb8c/src/ts/randBytes.ts#L14)
[src/ts/randBytes.ts:14](https://github.com/juanelas/bigint-crypto-utils/blob/16c2723/src/ts/randBytes.ts#L14)
___
@ -497,7 +497,7 @@ A UInt8Array/Buffer (Browser/Node.js) filled with cryptographically secure rando
#### Defined in
[src/ts/randBytes.ts:47](https://github.com/juanelas/bigint-crypto-utils/blob/f25bb8c/src/ts/randBytes.ts#L47)
[src/ts/randBytes.ts:46](https://github.com/juanelas/bigint-crypto-utils/blob/16c2723/src/ts/randBytes.ts#L46)
___

View File

@ -1,4 +1,4 @@
const bigintCryptoUtils = require('../types')
const bigintCryptoUtils = require('..')
// const bigintCryptoUtils = require('bigint-crypto-utils')
/* A BigInt with value 666 can be declared calling the bigint constructor as

38
examples/node.esm.js Normal file
View File

@ -0,0 +1,38 @@
const bigintCryptoUtils = await import('../dist/esm/index.node.js')
// const bigintCryptoUtils = require('bigint-crypto-utils')
/* A BigInt with value 666 can be declared calling the bigint constructor as
BigInt('666') or with the shorter 666n.
Notice that you can also pass a number to the constructor, e.g. BigInt(666).
However, it is not recommended since values over 2**53 - 1 won't be safe but
no warning will be raised.
*/
const a = BigInt('5')
const b = BigInt('2')
const n = 19n
console.log(bigintCryptoUtils.modPow(a, b, n)) // prints 6
console.log(bigintCryptoUtils.modInv(2n, 5n)) // prints 3
console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))) // prints 2
console.log(bigintCryptoUtils.randBetween(2n ** 256n)) // Prints a cryptographically secure random number between 1 and 2**256 bits.
async function primeTesting () {
// Output of a probable prime of 2048 bits
console.log(await bigintCryptoUtils.prime(2048))
// Testing if a number is a probable prime (Miller-Rabin)
const number = 13139188972124309083000292697519085211422620620787723340749020496498012413131881656428777288953095338604061035790562501399090389032827482643578651715752317n
const isPrime = await bigintCryptoUtils.isProbablyPrime(number)
if (isPrime) {
console.log(`${number} is prime`)
} else {
console.log(`${number} is composite`)
}
}
primeTesting()
export {}

2200
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -53,12 +53,12 @@
"scripts": {
"build": "run-s lint build:js docs",
"build:js": "rollup -c build/rollup.config.js",
"clean": "rimraf .nyc_output .mocha-ts coverage dist types docs",
"coverage": "nyc --check-coverage --exclude build --exclude '{src/ts/**/*.spec.ts,test/**/*.ts,.mocha-ts/**/*}' --reporter=text --reporter=lcov node ./build/bin/mocha-ts.cjs --require build/testing/mocha/mocha-init.cjs '{src/ts/**/*.spec.ts,test/**/*.ts}'",
"clean": "rimraf .mocha-ts coverage dist types docs",
"coverage": "c8 --clean --check-coverage --exclude-after-remap --exclude '{build,node_modules,src/ts/**/*.spec.ts,test/**/*.ts,.mocha-ts/**/*}' --reporter=text --reporter=lcov node ./build/bin/mocha-ts.js --require build/testing/mocha/mocha-init.js '{src/ts/**/*.spec.ts,test/**/*.ts}'",
"docs": "node build/build.docs.cjs",
"git:add": "git add -A",
"lint": "ts-standard --fix",
"mocha-ts": "node ./build/bin/mocha-ts.cjs --require build/testing/mocha/mocha-init.cjs ",
"mocha-ts": "node ./build/bin/mocha-ts.js --require build/testing/mocha/mocha-init.js ",
"mocha-ts:browser": "node build/testing/browser/index.cjs ",
"mocha-ts:browser-headless": "node build/testing/browser/index.cjs headless ",
"preversion": "run-s clean lint build:js coverage test:browser-headless",
@ -68,7 +68,7 @@
"test:browser": "npm run mocha-ts:browser ",
"test:browser-headless": "npm run mocha-ts:browser-headless ",
"test:node": "npm run mocha-ts -- '{src/ts/**/*.spec.ts,test/**/*.ts}'",
"watch": "npm run mocha-ts:node -- --watch '{src/ts/**/*.spec.ts,test/**/*.ts}'"
"watch": "npm run mocha-ts -- --watch '{src/ts/**/*.spec.ts,test/**/*.ts}'"
},
"ts-standard": {
"env": [
@ -85,11 +85,12 @@
"_pkg",
"chai"
],
"project": "./tsconfig.json",
"project": "tsconfig.json",
"ignore": [
"dist/**/*",
"examples/**/*",
"types/**/*"
"types/**/*",
"build/testing/mocha/**/*"
]
},
"devDependencies": {
@ -101,6 +102,7 @@
"@rollup/plugin-typescript": "^8.2.0",
"@types/chai": "^4.2.22",
"@types/mocha": "^9.0.0",
"c8": "^7.12.0",
"chai": "^4.3.3",
"dotenv": "^16.0.0",
"fs-extra": "^10.0.0",
@ -109,7 +111,6 @@
"minimatch": "^5.0.1",
"mocha": "^10.0.0",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"pirates": "^4.0.1",
"puppeteer": "^15.5.0",
"rimraf": "^3.0.2",

View File

@ -25,7 +25,6 @@ export function isProbablyPrime (w: number|bigint, iterations: number = 16, disa
if (w < 0n) throw RangeError('w MUST be >= 0')
if (!IS_BROWSER) { // Node.js
/* istanbul ignore else */
if (!disableWorkers && _useWorkers) {
return new Promise((resolve, reject) => {
const worker = new workerThreads.Worker(__filename)
@ -400,7 +399,6 @@ export function _isProbablyPrimeWorkerUrl (): string {
if (!IS_BROWSER && _useWorkers) { // node.js with support for workers
var workerThreads = await import('worker_threads') // eslint-disable-line
const isWorker = !(workerThreads.isMainThread)
/* istanbul ignore if */
if (isWorker && workerThreads.parentPort !== null) { // worker
workerThreads.parentPort.on('message', function (data: MainToWorkerMsg) { // Let's start once we are called
const isPrime = _isProbablyPrime(data.rnd, data.iterations)

View File

@ -25,7 +25,7 @@ if (!IS_BROWSER) var workerThreads = await import('worker_threads') // eslint-di
export function prime (bitLength: number, iterations: number = 16): Promise<bigint> { // eslint-disable-line
if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
/* istanbul ignore if */
/* c8 ignore start */
if (!_useWorkers) { // If there is no support for workers
let rnd = 0n
do {
@ -33,6 +33,7 @@ export function prime (bitLength: number, iterations: number = 16): Promise<bigi
} while (!_isProbablyPrime(rnd, iterations))
return new Promise((resolve) => { resolve(rnd) })
}
/* c8 ignore stop */
return new Promise((resolve, reject) => {
const workerList: Array<NodeWorker | Worker> = []
const _onmessage = (msg: WorkerToMainMsg, newWorker: Worker | NodeWorker): void => {

View File

@ -17,7 +17,6 @@ export function randBytes (byteLength: number, forceLength = false): Promise<Uin
return new Promise(function (resolve, reject) {
if (!IS_BROWSER) {
crypto.randomBytes(byteLength, function (err, buf: Buffer) {
/* istanbul ignore if */
if (err !== null) reject(err)
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) buf[0] = buf[0] | 128

View File

@ -10,13 +10,13 @@ if (!IS_BROWSER) { // Node.js
try {
await import('worker_threads')
_useWorkers = true
} catch (e) {
/* istanbul ignore next */
} /* c8 ignore start */ catch (e) {
console.log(`[bigint-crypto-utils] WARNING:
This node version doesn't support worker_threads. You should enable them in order to greatly speedup the generation of big prime numbers.
· With Node >=11 it is enabled by default (consider upgrading).
· With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `)
}
/* c8 ignore stop */
} else { // Native JS
if (self.Worker !== undefined) _useWorkers = true
}

View File

@ -2,10 +2,10 @@
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "esnext",
"module": "ESNext",
// "lib": [ "es2020" ], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
// "outDir": ".dst", /* If not set we cannot import .js files without a warning that is going to be overwritten. outDir is not going to be used in any case */
"outDir": ".dst", /* If not set we cannot import .js files without a warning that is going to be overwritten. outDir is not going to be used in any case */
"checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'react', 'react-jsx', 'react-jsxdev', 'preserve' or 'react-native'. */
"strict": true, /* Enable all strict type-checking options. */
@ -22,12 +22,12 @@
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": [ "./build/typings", "./node_modules/@types" ], /* List of folders to include type definitions from. */
"typeRoots": [ "node_modules/@types", "build/typings" ], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
"allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
@ -35,7 +35,8 @@
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
"resolveJsonModule": true
},
"include": ["src/ts/**/*", "build/typings/**/*", "test/**/*"]
"include": ["src/ts/**/*", "test/**/*", "build/typings/**/*.d.ts"]
}

View File

@ -1 +1 @@
{"version":3,"file":"isProbablyPrime.d.ts","sourceRoot":"","sources":["../src/ts/isProbablyPrime.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAE,CAAC,EAAE,MAAM,GAAC,MAAM,EAAE,UAAU,GAAE,MAAW,EAAE,cAAc,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAoD7H;AAED,wBAAgB,gBAAgB,CAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CA0TxE;AAED,wBAAgB,yBAAyB,IAAK,MAAM,CAOnD"}
{"version":3,"file":"isProbablyPrime.d.ts","sourceRoot":"","sources":["../src/ts/isProbablyPrime.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAE,CAAC,EAAE,MAAM,GAAC,MAAM,EAAE,UAAU,GAAE,MAAW,EAAE,cAAc,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAmD7H;AAED,wBAAgB,gBAAgB,CAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CA0TxE;AAED,wBAAgB,yBAAyB,IAAK,MAAM,CAOnD"}

View File

@ -1 +1 @@
{"version":3,"file":"prime.d.ts","sourceRoot":"","sources":["../src/ts/prime.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CA+DlF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,MAAM,CAO7E"}
{"version":3,"file":"prime.d.ts","sourceRoot":"","sources":["../src/ts/prime.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAgElF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,MAAM,CAO7E"}

View File

@ -1 +1 @@
{"version":3,"file":"randBytes.d.ts","sourceRoot":"","sources":["../src/ts/randBytes.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAE,UAAU,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,UAAU,GAAC,MAAM,CAAC,CAoB9F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,UAAU,GAAC,MAAM,CAiBlG"}
{"version":3,"file":"randBytes.d.ts","sourceRoot":"","sources":["../src/ts/randBytes.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAE,UAAU,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,UAAU,GAAC,MAAM,CAAC,CAmB9F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,UAAU,GAAC,MAAM,CAiBlG"}