diff --git a/README.md b/README.md index acec985..6454900 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) -[![Node.js CI](https://github.com/juanelas/bigint-mod-arith/workflows/build/badge.svg)](https://github.com/juanelas/bigint-mod-arith/actions?query=workflow%3A%22build%22) +[![Node.js CI](https://github.com/juanelas/bigint-mod-arith/actions/workflows/nodejs.yml/badge.svg)](https://github.com/juanelas/bigint-mod-arith/actions/workflows/nodejs.yml) [![Coverage Status](https://coveralls.io/repos/github/juanelas/bigint-mod-arith/badge.svg?branch=master)](https://coveralls.io/github/juanelas/bigint-mod-arith?branch=master) # bigint-mod-arith @@ -24,7 +24,7 @@ Then either require (Node.js CJS): const bigintModArith = require('bigint-mod-arith') ``` -> **Node >=10.4 <11**. `bigint-mod-arith` uses workers to speed up some operations. Workers are enabled by default with Node.js from version 11. In order to use them with Node >=10.4 and <11, you need to execute node with the flag `--experimental-worker`, and require the .js file manually (otherwise .cjs is required by default and would not be supported by the workers) +> **Node >=10.4 <11**. `bigint-mod-arith` uses workers to speed up some operations. Workers are enabled by default with Node.js from version 11. In order to use them with Node >=10.4 and <11, you need to execute node with the flag `--experimental-worker`, and require the `.js` file manually (otherwise `.cjs` is required by default and would not be supported by the workers) > > ```javascript > const bigintCryptoUtils = require('bigint-crypto-utils/dist/cjs/index.node') // ONLY FOR node >=10.4 <11 ! diff --git a/build/bin/mocha-ts.cjs b/build/bin/mocha-ts.cjs index 1914e20..2a96f20 100644 --- a/build/bin/mocha-ts.cjs +++ b/build/bin/mocha-ts.cjs @@ -1,75 +1,166 @@ #! /usr/bin/env node +const fs = require('fs') const path = require('path') -const childProcess = require('child_process') +const glob = require('glob') +const minimatch = require('minimatch') +const rimraf = require('rimraf') +const runScript = require('../run-script.cjs') 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 -// a watch file to our semaphore will be added -const processedArgs = processArgs(process.argv.slice(2)) +const pkgJson = require(path.join(rootDir, 'package.json')) -// Now we can run a script and invoke a callback when complete, e.g. -runScript(path.join(rootDir, 'node_modules/mocha/bin/mocha'), processedArgs) +const mochaTsRelativeDir = pkgJson.directories['mocha-ts'] +const mochaTsDir = path.join(rootDir, mochaTsRelativeDir) -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 }) - if (filenames.length > 0) { - return filenames.map(file => { - const isTsTestFile = minimatch(file, '{test/**/*.ts,src/**/*.spec.ts}', { matchBase: true }) - if (isTsTestFile) { - return `${mochaTsRelativeDir}/${file.slice(0, -3)}.js` - } - return file - }) - } - return arg - }) +// clean .mocha-ts directory +rimraf.sync(mochaTsDir) - const processedArgs = [] +const semaphorePath = `${mochaTsRelativeDir}/semaphore` - let addSemaphore = false - let semaphoreAdded = false - for (const arg of args) { - if (Array.isArray(arg)) { - processedArgs.push(...arg) - } else { - processedArgs.push(arg) - if (arg === '--watch' || arg === '-w') { - addSemaphore = true - } else if (arg === '--watch-files') { - processedArgs.push(`${mochaTsRelativeDir}/semaphore`) - semaphoreAdded = true +const tempDir = mochaTsDir +fs.mkdirSync(tempDir, { recursive: true }) + +const usage = `Usage: mocha-ts [options] [spec] + +mocha against ts tests and modules + +Arguments: + spec One or more files, directories, or globs to test (default: + "{src/ts/**/*.spec.ts,test/**/*.ts}") + +Options: + -w, --watch run in watch mode. Since mocha only supports CJS in watch + mode. This option implies -cjs as well (default: false) + -cjs, --commonjs run tests against the CJS bundle instead of the ESM one + (default: false) + -h, --help display help for command + +` + +function parse () { + const args = process.argv.slice(2) + + const help = getBooleanOption(args, '--help', '-h') + if (help) { + console.log(usage) + process.exit() + } + + const requiredFile = getOption(args, '--require') + + const watch = getBooleanOption(args, '--watch', '-w') + + const commonjs = getBooleanOption(args, '--commonjs', '-cjs') + if (commonjs === false && watch === true) { + console.log('ERROR: mocha in watch mode only supports commonjs') + console.log(usage) + process.exit(1) + } + + let testsGlob = (args.pop() ?? '').replace(/^['"]/, '').replace(/['"]$/, '') // Let us remove surrounding quotes in string (it gives issues in windows) + if (testsGlob === '') { + testsGlob = '{src/ts/**/*.spec.ts,test/**/*.ts}' + } + + const mochaArgs = [] + + if (requiredFile !== '') { + mochaArgs.push('--require') + mochaArgs.push(requiredFile) + } + mochaArgs.push('--require') + mochaArgs.push('build/testing/mocha/mocha-init.cjs') + + if (watch) { + mochaArgs.push('-w') + mochaArgs.push('--watch-files') + mochaArgs.push(semaphorePath) + } + + if (testsGlob.substring(0, 1) === '-') { + console.log(usage) + process.exit(9) + } + let filenames = [] + try { + filenames = glob.sync(testsGlob, { cwd: rootDir, matchBase: true }) + } catch (error) {} + if (filenames.length === 0) { + console.error('invalid or empty glob pattern: ' + testsGlob) + console.log() + console.log(usage) + process.exit(9) + } + + const testFiles = [] + const jsTestFiles = [] + + if (filenames.length > 0) { + filenames.forEach(file => { + const isTsTestFile = minimatch(file, '{test/**/*.ts,src/**/*.spec.ts}', { matchBase: true }) + if (isTsTestFile) { + testFiles.push(file) + const extension = commonjs ? 'cjs' : 'js' + jsTestFiles.push(`${mochaTsRelativeDir}/${file.slice(0, -3)}.${extension}`) } - } - } - if (addSemaphore === true || semaphoreAdded === false) { - processedArgs.push('--watch-files') - processedArgs.push(`${mochaTsRelativeDir}/semaphore`) + }) } + mochaArgs.push(...jsTestFiles) - return processedArgs + return { + mochaArgs, + testFiles, + commonjs + } } -function runScript (scriptPath, args) { - const mochaCmd = childProcess.fork(scriptPath, args, { - cwd: rootDir - }) +const processedArgs = parse() +const commonjs = processedArgs.commonjs +const testFiles = processedArgs.testFiles +const mochaArgs = processedArgs.mochaArgs - mochaCmd.on('error', (error) => { - throw error - }) +// prepare setup for mocha (it should be written to a JSON file that will be loaded by the mocha-init.cjs) +const mochaSetup = { + testFiles, + commonjs +} +fs.writeFileSync(path.join(tempDir, 'testSetup.json'), JSON.stringify(mochaSetup, undefined, 2), { encoding: 'utf-8' }) - // execute the callback once the process has finished running - mochaCmd.on('exit', function (code) { - if (code !== 0) { - throw new Error('exit code ' + code) +if (commonjs) { + console.log('\x1b[33mℹ [mocha-ts] Running tests against the CommonJS module \x1b[0m\n') +} else { + console.log('\x1b[33mℹ [mocha-ts] Running tests against the ESM module \x1b[0m\n') +} + +const rollupBuilder = require('../testing/mocha/builders/RollupBuilder.cjs').rollupBuilder + +rollupBuilder.start({ commonjs, watch: false }).then(() => { + rollupBuilder.close() + const testsBuilder = require('../testing/mocha/builders/TestsBuilder.cjs').testBuilder + testsBuilder.start({ commonjs, testFiles }).then(() => { + testsBuilder.close() + // Now run mocha + runScript(path.join(rootDir, 'node_modules/mocha/bin/mocha'), mochaArgs) + }) +}) + +function getBooleanOption (args, ...optionNames) { + let found = false + optionNames.forEach((option) => { + const index = args.indexOf(option) + if (index > -1) { + found = true + args.splice(index, 1) } }) + return found +} + +function getOption (args, option) { + const index = args.indexOf(option) + if (index > -1 && index < args.length - 2) { + return args.splice(index, 2)[1] + } + return '' } diff --git a/build/build.docs.cjs b/build/build.docs.cjs index 197fde9..adaf354 100644 --- a/build/build.docs.cjs +++ b/build/build.docs.cjs @@ -3,7 +3,9 @@ const fs = require('fs') const TypeDoc = require('typedoc') const path = require('path') +const json5 = require('json5') const pkgJson = require('../package.json') +const rimraf = require('rimraf') const rootDir = path.join(__dirname, '..') @@ -14,15 +16,25 @@ function camelise (str) { }) } +const tsConfigPath = path.join(rootDir, 'tsconfig.json') +const tempTsConfigPath = path.join(rootDir, '.tsconfig.json') + async function typedoc () { const app = new TypeDoc.Application() + // prepare tsconfig + const tsConfig = json5.parse(fs.readFileSync(tsConfigPath, 'utf8')) + tsConfig.include = ['src/ts/**/*', 'build/typings/**/*.d.ts'] + tsConfig.exclude = ['src/**/*.spec.ts'] + fs.writeFileSync(tempTsConfigPath, JSON.stringify(tsConfig, undefined, 2)) + // If you want TypeDoc to load tsconfig.json / typedoc.json files app.options.addReader(new TypeDoc.TSConfigReader()) app.options.addReader(new TypeDoc.TypeDocReader()) app.bootstrap({ // typedoc options here + tsconfig: tempTsConfigPath, entryPoints: ['src/ts/index.ts'], plugin: ['typedoc-plugin-markdown'], includeVersion: true, @@ -84,7 +96,7 @@ if (repoProvider) { iifeBundle = `[IIFE bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/${iifeBundlePath})` esmBundle = `[ESM bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/${esmBundlePath})` umdBundle = `[UMD bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/${umdBundlePath})` - workflowBadget = `[![Node.js CI](https://github.com/${repoUsername}/${repoName}/workflows/build/badge.svg)](https://github.com/${repoUsername}/${repoName}/actions?query=workflow%3A%22build%22)` + workflowBadget = `[![Node.js CI](https://github.com/${repoUsername}/${repoName}/actions/workflows/nodejs.yml/badge.svg)](https://github.com/${repoUsername}/${repoName}/actions/workflows/nodejs.yml)` coverallsBadge = `[![Coverage Status](https://coveralls.io/repos/github/${repoUsername}/${repoName}/badge.svg?branch=master)](https://coveralls.io/github/${repoUsername}/${repoName}?branch=master)` break @@ -117,3 +129,5 @@ const readmeFile = path.join(rootDir, 'README.md') fs.writeFileSync(readmeFile, template) typedoc() + +rimraf.sync(tempTsConfigPath) diff --git a/build/rollup.config.js b/build/rollup.config.js index 7d00eea..f19f4cc 100644 --- a/build/rollup.config.js +++ b/build/rollup.config.js @@ -8,8 +8,8 @@ import commonjs from '@rollup/plugin-commonjs' import json from '@rollup/plugin-json' import { join } from 'path' -import { existsSync } from 'fs-extra' -import { directories, name as _name, dependencies, peerDependencies, exports } from '../package.json' +import { existsSync } from 'fs' +import { directories, name as _name, exports } from '../package.json' import { compile } from './rollup-plugin-dts.js' const rootDir = join(__dirname, '..') @@ -37,8 +37,6 @@ const tsBundleOptions = { exclude: ['src/**/*.spec.ts'] } -const external = [...Object.keys(dependencies || {}), ...Object.keys(peerDependencies || {})] - const sourcemapOutputOptions = { sourcemap: 'inline', sourcemapExcludeSources: true @@ -54,83 +52,9 @@ function compileDts () { } export default [ - { // ESM for browsers and declarations + { // Node ESM with declarations input: input, output: [ - { - file: join(rootDir, exports['.'].default), - ...sourcemapOutputOptions, - format: 'es' - } - ], - plugins: [ - replace({ - IS_BROWSER: true, - preventAssignment: true - }), - typescriptPlugin(tsBundleOptions), - compileDts(), - json() - ], - external - }, - { // Browser bundles - input: input, - output: [ - { - file: join(dstDir, 'bundles/iife.js'), - format: 'iife', - name: pkgCamelisedName, - plugins: [terser()] - }, - { - file: join(dstDir, 'bundles/esm.js'), - ...sourcemapOutputOptions, - format: 'es' - }, - { - file: join(dstDir, 'bundles/esm.min.js'), - format: 'es', - plugins: [terser()] - }, - { - file: join(dstDir, 'bundles/umd.js'), - format: 'umd', - name: pkgCamelisedName, - plugins: [terser()] - } - ], - plugins: [ - replace({ - IS_BROWSER: true, - preventAssignment: true - }), - typescriptPlugin(tsBundleOptions), - resolve({ - browser: true, - exportConditions: ['browser', 'default'], - mainFields: ['browser', 'main'] - }), - commonjs({ - // namedExports: { - // 'bn.js': ['BN'], - // 'hash.js': ['hmac', 'ripemd160', 'sha256', 'sha512'], - // elliptic: ['ec'], - // 'scrypt-js': ['scrypt', 'syncScrypt'] - // } - }), - json() - ] - }, - { // Node - input: input, - output: [ - { - file: join(rootDir, exports['.'].node.require), - ...sourcemapOutputOptions, - format: 'cjs', - exports: 'auto' - }, { file: join(rootDir, exports['.'].node.import), ...sourcemapOutputOptions, @@ -143,9 +67,111 @@ export default [ preventAssignment: true }), typescriptPlugin(tsBundleOptions), + compileDts(), + // resolve({ + // browser: false, + // exportConditions: ['node'] + // }), commonjs({ extensions: ['.js', '.cjs', '.ts', '.jsx', '.cjsx', '.tsx'] }), // the ".ts" extension is required json() + ] + }, + { // Node CJS + input: input, + output: [ + { + file: join(rootDir, exports['.'].node.require), + ...sourcemapOutputOptions, + format: 'cjs', + exports: 'auto' + } ], - external + plugins: [ + // replace({ + // 'await import(': 'require(', + // delimiters: ['', ''], + // preventAssignment: true + // }), + replace({ + IS_BROWSER: false, + preventAssignment: true + }), + typescriptPlugin(tsBundleOptions), + // resolve({ + // browser: false, + // exportConditions: ['node'] + // }), + commonjs({ extensions: ['.js', '.cjs', '.ts', '.jsx', '.cjsx', '.tsx'] }), // the ".ts" extension is required + json() + ] + }, + { // ESM for browsers + input: input, + output: [ + { + file: join(rootDir, exports['.'].default), + ...sourcemapOutputOptions, + format: 'es' + }, + { + file: join(dstDir, 'bundles/esm.js'), + ...sourcemapOutputOptions, + format: 'es' + }, + { + file: join(dstDir, 'bundles/esm.min.js'), + format: 'es', + plugins: [terser()] + } + ], + plugins: [ + replace({ + IS_BROWSER: true, + preventAssignment: true + }), + typescriptPlugin(tsBundleOptions), + resolve({ + browser: true, + exportConditions: ['browser', 'default'] + }), + commonjs({ extensions: ['.js', '.cjs', '.ts', '.jsx', '.cjsx', '.tsx'] }), // the ".ts" extension is required + json() + ] + }, + { // Browser bundles + input: input, + output: [ + { + file: join(dstDir, 'bundles/iife.js'), + format: 'iife', + name: pkgCamelisedName, + plugins: [terser()] + }, + { + file: join(dstDir, 'bundles/umd.js'), + format: 'umd', + name: pkgCamelisedName, + plugins: [terser()] + } + ], + plugins: [ + // replace({ + // 'await import(': 'require(', + // delimiters: ['', ''], + // preventAssignment: true + // }), + replace({ + IS_BROWSER: true, + preventAssignment: true + }), + typescriptPlugin(tsBundleOptions), + resolve({ + browser: true, + exportConditions: ['browser', 'default'], + mainFields: ['browser', 'module', 'main'] + }), + commonjs({ extensions: ['.js', '.cjs', '.ts', '.jsx', '.cjsx', '.tsx'] }), // the ".ts" extension is required + json() + ] } ] diff --git a/build/run-script.cjs b/build/run-script.cjs new file mode 100644 index 0000000..a09e482 --- /dev/null +++ b/build/run-script.cjs @@ -0,0 +1,26 @@ +const childProcess = require('child_process') + +const rootDir = require('path').join(__dirname, '../') + +function runScript (scriptPath, args) { + return new Promise((resolve, reject) => { + const cmd = childProcess.fork(scriptPath, args, { + cwd: rootDir + }) + + cmd.on('error', (error) => { + reject(error) + }) + + // execute the callback once the process has finished running + cmd.on('exit', function (code) { + if (code !== 0) { + const error = new Error('exit code ' + code) + reject(error) + } + resolve() + }) + }) +} + +module.exports = runScript diff --git a/build/testing/browser/index.cjs b/build/testing/browser/index.cjs index 3ad5a5f..6453247 100644 --- a/build/testing/browser/index.cjs +++ b/build/testing/browser/index.cjs @@ -29,11 +29,11 @@ const browserTests = async ( } if (ignore) return - let text = (message.args().length > 0) ? message.args()[0]._remoteObject.value : message.text() + let text = (message.args().length > 0) ? message.args()[0].remoteObject().value : message.text() const args = [] if (message.args() !== undefined && message.args().length > 1) { for (let i = 1; i < message.args().length; i++) { - args.push(message.args()[i]._remoteObject.value) + args.push(message.args()[i].remoteObject().value) } } diff --git a/build/testing/browser/server.cjs b/build/testing/browser/server.cjs index d777eeb..a0ca662 100644 --- a/build/testing/browser/server.cjs +++ b/build/testing/browser/server.cjs @@ -13,6 +13,7 @@ const multi = require('@rollup/plugin-multi-entry') const typescriptPlugin = require('@rollup/plugin-typescript') const commonjs = require('@rollup/plugin-commonjs') const json = require('@rollup/plugin-json') +const runScript = require('../../run-script.cjs') const rootDir = path.join(__dirname, '..', '..', '..') @@ -40,10 +41,6 @@ const indexHtml = ` timeout: 90000 }) -