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

View File

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

View File

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

View File

@ -1,18 +1,25 @@
const EventEmitter = require('events') import EventEmitter from 'events'
const fs = require('fs') import { writeFileSync, existsSync } from 'fs'
const path = require('path') import { join, resolve } from 'path'
import { fileURLToPath } from 'url'
const rollup = require('rollup') import { watch as _watch, rollup as _rollup } from 'rollup'
const loadAndParseConfigFile = require('rollup/dist/loadConfigFile.js') import loadAndParseConfigFile from 'rollup/dist/loadConfigFile.js'
const Builder = require('./Builder.cjs') import Builder from './Builder.js'
const rootDir = path.join(__dirname, '../../../../') const __dirname = resolve(fileURLToPath(import.meta.url), '../')
const pkgJson = require(path.join(rootDir, 'package.json'))
module.exports = class RollupBuilder extends Builder { const rootDir = join(__dirname, '../../../../')
constructor ({ name = 'rollup', configPath = path.join(rootDir, 'rollup.config.js'), tempDir = path.join(rootDir, '.mocha-ts'), watch = false }) { const pkgJson = (await import(join(rootDir, 'package.json'), {
super(path.join(tempDir, 'semaphore'), name) 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.configPath = configPath
this.watch = watch this.watch = watch
} }
@ -21,14 +28,16 @@ module.exports = class RollupBuilder extends Builder {
await super.start() await super.start()
const { options } = await loadAndParseConfigFile(this.configPath) 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 rollupOptions = options.filter(bundle => {
const file = (bundle.output[0].dir !== undefined) 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 : bundle.output[0].file
return file === path.join(rootDir, pkgJson.main) return file === join(rootDir, pkgJson.exports['.'].node.import)
})[0] })[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) this.builder = new RollupBundler(rollupOptions, this.watch)
@ -55,7 +64,7 @@ module.exports = class RollupBuilder extends Builder {
case 'ERROR': case 'ERROR':
if (event.result) event.result.close() if (event.result) event.result.close()
this.emit('error', event.error) this.emit('error', event.error)
fs.writeFileSync(path.join(rootDir, pkgJson.main), '', 'utf8') writeFileSync(join(rootDir, pkgJson.exports['.'].node.import), '', 'utf8')
this.emit('ready') this.emit('ready')
break break
@ -85,13 +94,13 @@ class RollupBundler extends EventEmitter {
async start () { async start () {
if (this.watch === true) { if (this.watch === true) {
this.watcher = rollup.watch(this.rollupOptions) this.watcher = _watch(this.rollupOptions)
this.watcher.on('event', event => { this.watcher.on('event', event => {
this.emit('event', event) this.emit('event', event)
}) })
} else { } else {
if (fs.existsSync(path.join(rootDir, pkgJson.main)) === false) { if (existsSync(join(rootDir, pkgJson.exports['.'].node.import)) === false) {
await this._bundle() await this._bundle()
} else { } else {
this.emit('event', { code: 'END', noBuild: true }) this.emit('event', { code: 'END', noBuild: true })
@ -103,7 +112,7 @@ class RollupBundler extends EventEmitter {
this.emit('event', { code: 'START' }) this.emit('event', { code: 'START' })
for (const optionsObj of [].concat(this.rollupOptions)) { for (const optionsObj of [].concat(this.rollupOptions)) {
try { try {
const bundle = await rollup.rollup(optionsObj) const bundle = await _rollup(optionsObj)
try { try {
await Promise.all(optionsObj.output.map(bundle.write)) await Promise.all(optionsObj.output.map(bundle.write))
this.emit('event', { code: 'BUNDLE_END' }) 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 #### 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 #### 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 #### 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 #### 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 #### 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 #### 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 #### 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 #### 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') // const bigintCryptoUtils = require('bigint-crypto-utils')
/* A BigInt with value 666 can be declared calling the bigint constructor as /* 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": { "scripts": {
"build": "run-s lint build:js docs", "build": "run-s lint build:js docs",
"build:js": "rollup -c build/rollup.config.js", "build:js": "rollup -c build/rollup.config.js",
"clean": "rimraf .nyc_output .mocha-ts coverage dist types docs", "clean": "rimraf .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}'", "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", "docs": "node build/build.docs.cjs",
"git:add": "git add -A", "git:add": "git add -A",
"lint": "ts-standard --fix", "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": "node build/testing/browser/index.cjs ",
"mocha-ts:browser-headless": "node build/testing/browser/index.cjs headless ", "mocha-ts:browser-headless": "node build/testing/browser/index.cjs headless ",
"preversion": "run-s clean lint build:js coverage test:browser-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": "npm run mocha-ts:browser ",
"test:browser-headless": "npm run mocha-ts:browser-headless ", "test:browser-headless": "npm run mocha-ts:browser-headless ",
"test:node": "npm run mocha-ts -- '{src/ts/**/*.spec.ts,test/**/*.ts}'", "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": { "ts-standard": {
"env": [ "env": [
@ -85,11 +85,12 @@
"_pkg", "_pkg",
"chai" "chai"
], ],
"project": "./tsconfig.json", "project": "tsconfig.json",
"ignore": [ "ignore": [
"dist/**/*", "dist/**/*",
"examples/**/*", "examples/**/*",
"types/**/*" "types/**/*",
"build/testing/mocha/**/*"
] ]
}, },
"devDependencies": { "devDependencies": {
@ -101,6 +102,7 @@
"@rollup/plugin-typescript": "^8.2.0", "@rollup/plugin-typescript": "^8.2.0",
"@types/chai": "^4.2.22", "@types/chai": "^4.2.22",
"@types/mocha": "^9.0.0", "@types/mocha": "^9.0.0",
"c8": "^7.12.0",
"chai": "^4.3.3", "chai": "^4.3.3",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
@ -109,7 +111,6 @@
"minimatch": "^5.0.1", "minimatch": "^5.0.1",
"mocha": "^10.0.0", "mocha": "^10.0.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"pirates": "^4.0.1", "pirates": "^4.0.1",
"puppeteer": "^15.5.0", "puppeteer": "^15.5.0",
"rimraf": "^3.0.2", "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 (w < 0n) throw RangeError('w MUST be >= 0')
if (!IS_BROWSER) { // Node.js if (!IS_BROWSER) { // Node.js
/* istanbul ignore else */
if (!disableWorkers && _useWorkers) { if (!disableWorkers && _useWorkers) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const worker = new workerThreads.Worker(__filename) const worker = new workerThreads.Worker(__filename)
@ -400,7 +399,6 @@ export function _isProbablyPrimeWorkerUrl (): string {
if (!IS_BROWSER && _useWorkers) { // node.js with support for workers if (!IS_BROWSER && _useWorkers) { // node.js with support for workers
var workerThreads = await import('worker_threads') // eslint-disable-line var workerThreads = await import('worker_threads') // eslint-disable-line
const isWorker = !(workerThreads.isMainThread) const isWorker = !(workerThreads.isMainThread)
/* istanbul ignore if */
if (isWorker && workerThreads.parentPort !== null) { // worker if (isWorker && workerThreads.parentPort !== null) { // worker
workerThreads.parentPort.on('message', function (data: MainToWorkerMsg) { // Let's start once we are called workerThreads.parentPort.on('message', function (data: MainToWorkerMsg) { // Let's start once we are called
const isPrime = _isProbablyPrime(data.rnd, data.iterations) 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 export function prime (bitLength: number, iterations: number = 16): Promise<bigint> { // eslint-disable-line
if (bitLength < 1) throw new RangeError('bitLength MUST be > 0') 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 if (!_useWorkers) { // If there is no support for workers
let rnd = 0n let rnd = 0n
do { do {
@ -33,6 +33,7 @@ export function prime (bitLength: number, iterations: number = 16): Promise<bigi
} while (!_isProbablyPrime(rnd, iterations)) } while (!_isProbablyPrime(rnd, iterations))
return new Promise((resolve) => { resolve(rnd) }) return new Promise((resolve) => { resolve(rnd) })
} }
/* c8 ignore stop */
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const workerList: Array<NodeWorker | Worker> = [] const workerList: Array<NodeWorker | Worker> = []
const _onmessage = (msg: WorkerToMainMsg, newWorker: Worker | NodeWorker): void => { 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) { return new Promise(function (resolve, reject) {
if (!IS_BROWSER) { if (!IS_BROWSER) {
crypto.randomBytes(byteLength, function (err, buf: Buffer) { crypto.randomBytes(byteLength, function (err, buf: Buffer) {
/* istanbul ignore if */
if (err !== null) reject(err) if (err !== null) reject(err)
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) buf[0] = buf[0] | 128 if (forceLength) buf[0] = buf[0] | 128

View File

@ -10,13 +10,13 @@ if (!IS_BROWSER) { // Node.js
try { try {
await import('worker_threads') await import('worker_threads')
_useWorkers = true _useWorkers = true
} catch (e) { } /* c8 ignore start */ catch (e) {
/* istanbul ignore next */
console.log(`[bigint-crypto-utils] WARNING: 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. 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 >=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 `) · 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 } else { // Native JS
if (self.Worker !== undefined) _useWorkers = true if (self.Worker !== undefined) _useWorkers = true
} }

View File

@ -2,10 +2,10 @@
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": { "compilerOptions": {
"target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "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. */ // "lib": [ "es2020" ], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */ "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. */ "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'react', 'react-jsx', 'react-jsxdev', 'preserve' or 'react-native'. */ // "jsx": "preserve", /* Specify JSX code generation: 'react', 'react-jsx', 'react-jsxdev', 'preserve' or 'react-native'. */
"strict": true, /* Enable all strict type-checking options. */ "strict": true, /* Enable all strict type-checking options. */
@ -22,12 +22,12 @@
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "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'. */ // "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. */ // "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. */ // "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. */ // "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'. */ "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. */ // "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 */ /* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
@ -35,7 +35,8 @@
/* Advanced Options */ /* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */ "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"}