code is now typescript. Some minor fixes
This commit is contained in:
parent
ff9f8afd61
commit
fd780cb3ec
|
@ -15,7 +15,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [11, 12, 13]
|
||||
node-version: [10, 12, 14]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
|
@ -49,7 +49,7 @@ jobs:
|
|||
- uses: actions/setup-node@v1
|
||||
if: steps.check.outputs.changed == 'true'
|
||||
with:
|
||||
node-version: 12
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: install
|
||||
|
|
|
@ -12,9 +12,9 @@ node_modules
|
|||
# Visual Studio Code
|
||||
.vscode
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
|
||||
# MYC output
|
||||
.nyc_output
|
||||
coverage/*
|
||||
coverage
|
||||
|
||||
# Test temporal files
|
||||
.mocha-ts
|
||||
|
|
23
.npmignore
23
.npmignore
|
@ -1,8 +1,9 @@
|
|||
# Build scripts
|
||||
build/*
|
||||
build
|
||||
|
||||
# Source files
|
||||
src/*
|
||||
# Test files
|
||||
test
|
||||
**/*.spec.ts
|
||||
|
||||
# Logs
|
||||
logs
|
||||
|
@ -20,10 +21,16 @@ npm-debug.log*
|
|||
|
||||
# MYC output
|
||||
.nyc_output
|
||||
coverage/*
|
||||
|
||||
# Travis
|
||||
.travis.yml
|
||||
coverage
|
||||
|
||||
# GitHub
|
||||
.github
|
||||
.github
|
||||
|
||||
# tsconfig
|
||||
tsconfig.json
|
||||
|
||||
# SRC docs
|
||||
src/docs
|
||||
|
||||
# Test temporal files
|
||||
.mocha-ts
|
221
README.md
221
README.md
|
@ -1,6 +1,6 @@
|
|||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
||||
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
|
||||
![Node CI](https://github.com/juanelas/bigint-mod-arith/workflows/Node%20CI/badge.svg)
|
||||
[![Node CI](https://github.com/juanelas/bigint-mod-arith/workflows/Node%20CI/badge.svg)](https://github.com/juanelas/bigint-mod-arith/actions?query=workflow%3A%22Node+CI%22)
|
||||
[![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
|
||||
|
@ -9,57 +9,33 @@ Some extra functions to work with modular arithmetic using native JS ([ES-2020](
|
|||
|
||||
> The operations supported on BigInts are not constant time. BigInt can be therefore **[unsuitable for use in cryptography](https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html).** Many platforms provide native support for cryptography, such as [Web Cryptography API](https://w3c.github.io/webcrypto/) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html).
|
||||
|
||||
## Installation
|
||||
## Usage
|
||||
|
||||
bigint-mod-arith is distributed for [web browsers and/or webviews supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) as an ES6 module or an IIFE file; and for Node.js (>=10.4.0), as a CJS module.
|
||||
`bigint-mod-arith` can be imported to your project with `npm`:
|
||||
|
||||
bigint-mod-arith can be imported to your project with `npm`:
|
||||
|
||||
```bash
|
||||
```console
|
||||
npm install bigint-mod-arith
|
||||
```
|
||||
|
||||
NPM installation defaults to the ES6 module for browsers and the CJS one for Node.js. For web browsers, you can also directly download the [IIFE bundle](https://raw.githubusercontent.com/juanelas/bigint-mod-arith/master/lib/index.browser.bundle.iife.js) or the [ESM bundle](https://raw.githubusercontent.com/juanelas/bigint-mod-arith/master/lib/index.browser.bundle.mod.js) from the repository.
|
||||
Then either require (Node.js CJS):
|
||||
|
||||
```javascript
|
||||
const bigintModArith = require('bigint-mod-arith')
|
||||
```
|
||||
|
||||
## Usage example
|
||||
or import (JavaScript ES module):
|
||||
|
||||
Import your module as :
|
||||
```javascript
|
||||
import * as bigintModArith from 'bigint-mod-arith'
|
||||
```
|
||||
|
||||
- Node.js
|
||||
```javascript
|
||||
const bigintModArith = require('bigint-mod-arith')
|
||||
... // your code here
|
||||
```
|
||||
- JavaScript native or TypeScript project (including React and Angular)
|
||||
```javascript
|
||||
import * as bigintModArith from 'bigint-mod-arith'
|
||||
... // your code here
|
||||
```
|
||||
- JavaScript native browser ES module
|
||||
```html
|
||||
<script type="module">
|
||||
import * as bigintModArith from 'lib/index.browser.bundle.mod.js' // Use you actual path to the broser mod bundle
|
||||
... // your code here
|
||||
</script>
|
||||
```
|
||||
- JavaScript native browser IIFE
|
||||
```html
|
||||
<head>
|
||||
...
|
||||
<script src="../../lib/index.browser.bundle.iife.js"></script> <!-- Use you actual path to the browser bundle -->
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
<script>
|
||||
... // your code here
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
The appropriate version for browser or node is automatically exported.
|
||||
|
||||
You can also download the [IIFE bundle](https://raw.githubusercontent.com/juanelas/bigint-mod-arith/master/dist/bundles/bigint-mod-arith.iife.js), the [ESM bundle](https://raw.githubusercontent.com/juanelas/bigint-mod-arith/master/dist/bundles/bigint-mod-arith.esm.js) or the [UMD bundle](https://raw.githubusercontent.com/juanelas/bigint-mod-arith/master/dist/bundles/bigint-mod-arith.umd.js) and manually add it to your project, or, if you have already imported `bigint-mod-arith` to your project, just get the bundles from `node_modules/bigint-mod-arith/dist/bundles/`.
|
||||
|
||||
An example of usage could be:
|
||||
|
||||
```javascript
|
||||
```typescript
|
||||
/* Stage 3 BigInts with value 666 can be declared as BigInt('666')
|
||||
or the shorter syntax 666n.
|
||||
Notice that you can also pass a number, e.g. BigInt(666), but it is not
|
||||
|
@ -75,171 +51,8 @@ console.log(bigintModArith.modPow(a, b, n)) // prints 6
|
|||
console.log(bigintModArith.modInv(2n, 5n)) // prints 3
|
||||
|
||||
console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))) // prints 2
|
||||
|
||||
```
|
||||
|
||||
## API reference documentation
|
||||
|
||||
<a name="module_bigint-mod-arith"></a>
|
||||
|
||||
### bigint-mod-arith
|
||||
Some common functions for modular arithmetic using native JS implementation of BigInt
|
||||
|
||||
|
||||
* [bigint-mod-arith](#module_bigint-mod-arith)
|
||||
* [~abs(a)](#module_bigint-mod-arith..abs) ⇒ <code>bigint</code>
|
||||
* [~bitLength(a)](#module_bigint-mod-arith..bitLength) ⇒ <code>number</code>
|
||||
* [~eGcd(a, b)](#module_bigint-mod-arith..eGcd) ⇒ <code>egcdReturn</code>
|
||||
* [~gcd(a, b)](#module_bigint-mod-arith..gcd) ⇒ <code>bigint</code>
|
||||
* [~lcm(a, b)](#module_bigint-mod-arith..lcm) ⇒ <code>bigint</code>
|
||||
* [~max(a, b)](#module_bigint-mod-arith..max) ⇒ <code>bigint</code>
|
||||
* [~min(a, b)](#module_bigint-mod-arith..min) ⇒ <code>bigint</code>
|
||||
* [~modInv(a, n)](#module_bigint-mod-arith..modInv) ⇒ <code>bigint</code> \| <code>NaN</code>
|
||||
* [~modPow(b, e, n)](#module_bigint-mod-arith..modPow) ⇒ <code>bigint</code>
|
||||
* [~toZn(a, n)](#module_bigint-mod-arith..toZn) ⇒ <code>bigint</code>
|
||||
* [~egcdReturn](#module_bigint-mod-arith..egcdReturn) : <code>Object</code>
|
||||
|
||||
<a name="module_bigint-mod-arith..abs"></a>
|
||||
|
||||
#### bigint-mod-arith~abs(a) ⇒ <code>bigint</code>
|
||||
Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0
|
||||
|
||||
**Kind**: inner method of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Returns**: <code>bigint</code> - the absolute value of a
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| a | <code>number</code> \| <code>bigint</code> |
|
||||
|
||||
<a name="module_bigint-mod-arith..bitLength"></a>
|
||||
|
||||
#### bigint-mod-arith~bitLength(a) ⇒ <code>number</code>
|
||||
Returns the bitlength of a number
|
||||
|
||||
**Kind**: inner method of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Returns**: <code>number</code> - - the bit length
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| a | <code>number</code> \| <code>bigint</code> |
|
||||
|
||||
<a name="module_bigint-mod-arith..eGcd"></a>
|
||||
|
||||
#### bigint-mod-arith~eGcd(a, b) ⇒ <code>egcdReturn</code>
|
||||
An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm.
|
||||
Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b).
|
||||
|
||||
**Kind**: inner method of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Returns**: <code>egcdReturn</code> - A triple (g, x, y), such that ax + by = g = gcd(a, b).
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| a | <code>number</code> \| <code>bigint</code> |
|
||||
| b | <code>number</code> \| <code>bigint</code> |
|
||||
|
||||
<a name="module_bigint-mod-arith..gcd"></a>
|
||||
|
||||
#### bigint-mod-arith~gcd(a, b) ⇒ <code>bigint</code>
|
||||
Greatest-common divisor of two integers based on the iterative binary algorithm.
|
||||
|
||||
**Kind**: inner method of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Returns**: <code>bigint</code> - The greatest common divisor of a and b
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| a | <code>number</code> \| <code>bigint</code> |
|
||||
| b | <code>number</code> \| <code>bigint</code> |
|
||||
|
||||
<a name="module_bigint-mod-arith..lcm"></a>
|
||||
|
||||
#### bigint-mod-arith~lcm(a, b) ⇒ <code>bigint</code>
|
||||
The least common multiple computed as abs(a*b)/gcd(a,b)
|
||||
|
||||
**Kind**: inner method of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Returns**: <code>bigint</code> - The least common multiple of a and b
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| a | <code>number</code> \| <code>bigint</code> |
|
||||
| b | <code>number</code> \| <code>bigint</code> |
|
||||
|
||||
<a name="module_bigint-mod-arith..max"></a>
|
||||
|
||||
#### bigint-mod-arith~max(a, b) ⇒ <code>bigint</code>
|
||||
Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<=b
|
||||
|
||||
**Kind**: inner method of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Returns**: <code>bigint</code> - maximum of numbers a and b
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| a | <code>number</code> \| <code>bigint</code> |
|
||||
| b | <code>number</code> \| <code>bigint</code> |
|
||||
|
||||
<a name="module_bigint-mod-arith..min"></a>
|
||||
|
||||
#### bigint-mod-arith~min(a, b) ⇒ <code>bigint</code>
|
||||
Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b
|
||||
|
||||
**Kind**: inner method of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Returns**: <code>bigint</code> - minimum of numbers a and b
|
||||
|
||||
| Param | Type |
|
||||
| --- | --- |
|
||||
| a | <code>number</code> \| <code>bigint</code> |
|
||||
| b | <code>number</code> \| <code>bigint</code> |
|
||||
|
||||
<a name="module_bigint-mod-arith..modInv"></a>
|
||||
|
||||
#### bigint-mod-arith~modInv(a, n) ⇒ <code>bigint</code> \| <code>NaN</code>
|
||||
Modular inverse.
|
||||
|
||||
**Kind**: inner method of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Returns**: <code>bigint</code> \| <code>NaN</code> - the inverse modulo n or NaN if it does not exist
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| a | <code>number</code> \| <code>bigint</code> | The number to find an inverse for |
|
||||
| n | <code>number</code> \| <code>bigint</code> | The modulo |
|
||||
|
||||
<a name="module_bigint-mod-arith..modPow"></a>
|
||||
|
||||
#### bigint-mod-arith~modPow(b, e, n) ⇒ <code>bigint</code>
|
||||
Modular exponentiation b**e mod n. Currently using the right-to-left binary method
|
||||
|
||||
**Kind**: inner method of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Returns**: <code>bigint</code> - b**e mod n
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| b | <code>number</code> \| <code>bigint</code> | base |
|
||||
| e | <code>number</code> \| <code>bigint</code> | exponent |
|
||||
| n | <code>number</code> \| <code>bigint</code> | modulo |
|
||||
|
||||
<a name="module_bigint-mod-arith..toZn"></a>
|
||||
|
||||
#### bigint-mod-arith~toZn(a, n) ⇒ <code>bigint</code>
|
||||
Finds the smallest positive element that is congruent to a in modulo n
|
||||
|
||||
**Kind**: inner method of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Returns**: <code>bigint</code> - The smallest positive representation of a in modulo n
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| a | <code>number</code> \| <code>bigint</code> | An integer |
|
||||
| n | <code>number</code> \| <code>bigint</code> | The modulo |
|
||||
|
||||
<a name="module_bigint-mod-arith..egcdReturn"></a>
|
||||
|
||||
#### bigint-mod-arith~egcdReturn : <code>Object</code>
|
||||
A triple (g, x, y), such that ax + by = g = gcd(a, b).
|
||||
|
||||
**Kind**: inner typedef of [<code>bigint-mod-arith</code>](#module_bigint-mod-arith)
|
||||
**Properties**
|
||||
|
||||
| Name | Type |
|
||||
| --- | --- |
|
||||
| g | <code>bigint</code> |
|
||||
| x | <code>bigint</code> |
|
||||
| y | <code>bigint</code> |
|
||||
|
||||
[Check the API](./docs/API.md)
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
#! /usr/bin/env node
|
||||
const path = require('path')
|
||||
const childProcess = require('child_process')
|
||||
|
||||
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))
|
||||
|
||||
// Now we can run a script and invoke a callback when complete, e.g.
|
||||
runScript(path.join(rootDir, 'node_modules/.bin/mocha'), processArgs(processedArgs))
|
||||
|
||||
function processArgs (args) {
|
||||
args = process.argv.slice(2).map(arg => {
|
||||
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
|
||||
})
|
||||
|
||||
const processedArgs = []
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
if (addSemaphore === true || semaphoreAdded === false) {
|
||||
processedArgs.push('--watch-files')
|
||||
processedArgs.push(`${mochaTsRelativeDir}/semaphore`)
|
||||
}
|
||||
|
||||
return processedArgs
|
||||
}
|
||||
|
||||
function runScript (scriptPath, args) {
|
||||
const mochaCmd = childProcess.fork(path.join(rootDir, 'node_modules/.bin/mocha'), processedArgs, {
|
||||
cwd: rootDir
|
||||
})
|
||||
|
||||
mochaCmd.on('error', (error) => {
|
||||
throw error
|
||||
})
|
||||
|
||||
// execute the callback once the process has finished running
|
||||
mochaCmd.on('exit', function (code) {
|
||||
if (code !== 0) {
|
||||
throw new Error('exit code ' + code)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const jsdoc2md = require('jsdoc-to-markdown')
|
||||
const TypeDoc = require('typedoc')
|
||||
const path = require('path')
|
||||
const pkgJson = require('../package.json')
|
||||
|
||||
|
@ -14,6 +14,33 @@ function camelise (str) {
|
|||
})
|
||||
}
|
||||
|
||||
async function typedoc () {
|
||||
const app = new TypeDoc.Application()
|
||||
|
||||
// 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
|
||||
entryPoints: ['src/index.ts'],
|
||||
plugin: ['typedoc-plugin-markdown'],
|
||||
includeVersion: true,
|
||||
entryDocument: 'API.md',
|
||||
readme: 'none'
|
||||
})
|
||||
|
||||
const project = app.convert()
|
||||
|
||||
if (project) {
|
||||
// Project may not have converted correctly
|
||||
const output = path.join(rootDir, './docs')
|
||||
|
||||
// Rendered docs
|
||||
await app.generateDocs(project, output)
|
||||
}
|
||||
}
|
||||
|
||||
function getRepositoryData () {
|
||||
if (typeof pkgJson.repository === 'string') {
|
||||
const repodata = pkgJson.repository.split(/[:/]/)
|
||||
|
@ -30,39 +57,45 @@ function getRepositoryData () {
|
|||
|
||||
const { repoProvider, repoUsername, repoName } = getRepositoryData() || { repoProvider: null, repoUsername: null, repoName: null }
|
||||
|
||||
let iifeBundle, esmBundle, workflowBadget, coverallsBadge
|
||||
if (repoProvider && repoProvider === 'github') {
|
||||
iifeBundle = `[IIFE bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/lib/index.browser.bundle.iife.js)`
|
||||
esmBundle = `[ESM bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/lib/index.browser.bundle.mod.js)`
|
||||
workflowBadget = `![Node CI](https://github.com/${repoUsername}/${repoName}/workflows/Node%20CI/badge.svg)`
|
||||
coverallsBadge = `[![Coverage Status](https://coveralls.io/repos/github/${repoUsername}/${repoName}/badge.svg?branch=master)](https://coveralls.io/github/${repoUsername}/${repoName}?branch=master)`
|
||||
const regex = /^(?:(?<scope>@.*?)\/)?(?<name>.*)/ // We are going to take only the package name part if there is a scope, e.g. @my-org/package-name
|
||||
const { name } = pkgJson.name.match(regex).groups
|
||||
const camelCaseName = camelise(name)
|
||||
|
||||
let iifeBundle, esmBundle, umdBundle, workflowBadget, coverallsBadge
|
||||
if (repoProvider) {
|
||||
switch (repoProvider) {
|
||||
case 'github':
|
||||
iifeBundle = `[IIFE bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/dist/bundles/${name}.iife.js)`
|
||||
esmBundle = `[ESM bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/dist/bundles/${name}.esm.js)`
|
||||
umdBundle = `[UMD bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/dist/bundles/${name}.umd.js)`
|
||||
workflowBadget = `[![Node CI](https://github.com/${repoUsername}/${repoName}/workflows/Node%20CI/badge.svg)](https://github.com/${repoUsername}/${repoName}/actions?query=workflow%3A%22Node+CI%22)`
|
||||
coverallsBadge = `[![Coverage Status](https://coveralls.io/repos/github/${repoUsername}/${repoName}/badge.svg?branch=master)](https://coveralls.io/github/${repoUsername}/${repoName}?branch=master)`
|
||||
break
|
||||
|
||||
case 'gitlab':
|
||||
iifeBundle = `[IIFE bundle](https://gitlab.com/${repoUsername}/${repoName}/-/raw/master/dist/bundles/${name}.iife.js?inline=false)`
|
||||
esmBundle = `[ESM bundle](https://gitlab.com/${repoUsername}/${repoName}/-/raw/master/dist/bundles/${name}.esm.js?inline=false)`
|
||||
umdBundle = `[IIFE bundle](https://gitlab.com/${repoUsername}/${repoName}/-/raw/master/dist/bundles/${name}.umd.js?inline=false)`
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const templateFile = path.join(rootDir, pkgJson.directories.src, 'doc', 'readme-template.md')
|
||||
const templateFile = path.join(rootDir, pkgJson.directories.src, 'docs/index.md')
|
||||
let template = fs.readFileSync(templateFile, { encoding: 'UTF-8' })
|
||||
.replace(/\{\{PKG_NAME\}\}/g, pkgJson.name)
|
||||
.replace(/\{\{PKG_CAMELCASE\}\}/g, camelise(pkgJson.name))
|
||||
.replace(/\{\{PKG_CAMELCASE\}\}/g, camelCaseName)
|
||||
.replace(/\{\{IIFE_BUNDLE\}\}/g, iifeBundle || 'IIFE bundle')
|
||||
.replace(/\{\{ESM_BUNDLE\}\}/g, esmBundle || 'ESM bundle')
|
||||
.replace(/\{\{UMD_BUNDLE\}\}/g, umdBundle || 'UMD bundle')
|
||||
|
||||
if (repoProvider && repoProvider === 'github') {
|
||||
template = template.replace(/\{\{GITHUB_ACTIONS_BADGES\}\}/g, workflowBadget + '\n' + coverallsBadge)
|
||||
}
|
||||
|
||||
const input = path.join(rootDir, pkgJson.browser)
|
||||
// Let us replace bigint literals by standard numbers to avoid issues with bigint
|
||||
const source = fs.readFileSync(input, { encoding: 'UTF-8' }).replace(/([0-9]+)n([,\s\n)])/g, '$1$2')
|
||||
const readmeFile = path.join(rootDir, 'README.md')
|
||||
fs.writeFileSync(readmeFile, template)
|
||||
|
||||
const options = {
|
||||
source,
|
||||
template,
|
||||
'heading-depth': 3, // The initial heading depth. For example, with a value of 2 the top-level markdown headings look like "## The heading"
|
||||
'global-index-format': 'none' // none, grouped, table, dl.
|
||||
}
|
||||
|
||||
jsdoc2md.clear().then(() => {
|
||||
const readmeContents = jsdoc2md.renderSync(options)
|
||||
|
||||
const readmeFile = path.join(rootDir, 'README.md')
|
||||
fs.writeFileSync(readmeFile, readmeContents)
|
||||
})
|
||||
typedoc()
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
const fs = require('fs')
|
||||
const ts = require('typescript')
|
||||
const path = require('path')
|
||||
const pkgJson = require('../package.json')
|
||||
|
||||
const rootDir = path.join(__dirname, '..')
|
||||
const jsFile = path.join(rootDir, pkgJson.browser)
|
||||
const dtsFile = path.join(rootDir, pkgJson.types)
|
||||
|
||||
const compilerOptions = {
|
||||
declaration: true,
|
||||
noEmit: false,
|
||||
emitDeclarationOnly: true,
|
||||
allowJs: true
|
||||
}
|
||||
|
||||
const host = ts.createCompilerHost(compilerOptions)
|
||||
|
||||
host.writeFile = (fileName, contents) => {
|
||||
fs.writeFileSync(dtsFile, contents)
|
||||
}
|
||||
|
||||
// Prepare and emit the d.ts files
|
||||
const program = ts.createProgram([jsFile], compilerOptions, host)
|
||||
program.emit()
|
|
@ -1,15 +1,18 @@
|
|||
'use strict'
|
||||
|
||||
const resolve = require('@rollup/plugin-node-resolve')
|
||||
const resolve = require('@rollup/plugin-node-resolve').nodeResolve
|
||||
const replace = require('@rollup/plugin-replace')
|
||||
const { terser } = require('rollup-plugin-terser')
|
||||
const typescriptPlugin = require('@rollup/plugin-typescript')
|
||||
const commonjs = require('@rollup/plugin-commonjs')
|
||||
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const pkgJson = require('../package.json')
|
||||
|
||||
const rootDir = path.join(__dirname, '..')
|
||||
const srcDir = path.join(rootDir, pkgJson.directories.src)
|
||||
const dstDir = path.join(rootDir, pkgJson.directories.lib)
|
||||
const dstDir = path.join(rootDir, pkgJson.directories.dist)
|
||||
const srcDir = path.join(rootDir, 'src')
|
||||
|
||||
function camelise (str) {
|
||||
return str.replace(/-([a-z])/g,
|
||||
|
@ -18,61 +21,113 @@ function camelise (str) {
|
|||
})
|
||||
}
|
||||
|
||||
const pkgName = pkgJson.name
|
||||
const pkgCamelisedName = camelise(pkgName)
|
||||
const regex = /^(?:(?<scope>@.*?)\/)?(?<name>.*)/ // We are going to take only the package name part if there is a scope, e.g. @my-org/package-name
|
||||
const { name } = pkgJson.name.match(regex).groups
|
||||
const pkgCamelisedName = camelise(name)
|
||||
|
||||
const input = path.join(srcDir, 'js', 'index.js')
|
||||
const input = path.join(srcDir, 'index.ts')
|
||||
if (fs.existsSync(input) !== true) throw new Error('The entry point should be index.ts')
|
||||
|
||||
const tsBundleOptions = {
|
||||
exclude: ['test/**/*', 'src/**/*.spec.ts', './build/typings/global-this-pkg.d.ts']
|
||||
}
|
||||
|
||||
const external = [...Object.keys(pkgJson.dependencies || {}), ...Object.keys(pkgJson.peerDependencies || {})]
|
||||
|
||||
const sourcemapOutputOptions = {
|
||||
sourcemap: 'inline',
|
||||
sourcemapExcludeSources: true
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
{ // Native JS
|
||||
{ // ESM for browsers
|
||||
input: input,
|
||||
output: [
|
||||
{
|
||||
file: path.join(rootDir, pkgJson.browser),
|
||||
file: path.join(rootDir, pkgJson.exports['.'].default),
|
||||
...sourcemapOutputOptions,
|
||||
format: 'es'
|
||||
}
|
||||
],
|
||||
plugins: [
|
||||
replace({
|
||||
'process.browser': true
|
||||
})
|
||||
]
|
||||
// external: ['bigint-crypto-utils']
|
||||
IS_BROWSER: true,
|
||||
preventAssignment: true
|
||||
}),
|
||||
typescriptPlugin(tsBundleOptions)
|
||||
],
|
||||
external
|
||||
},
|
||||
{ // Browser bundles
|
||||
input: input,
|
||||
output: [
|
||||
{
|
||||
file: path.join(dstDir, 'index.browser.bundle.iife.js'),
|
||||
file: path.join(dstDir, `bundles/${name}.iife.js`),
|
||||
format: 'iife',
|
||||
name: pkgCamelisedName
|
||||
},
|
||||
{
|
||||
file: path.join(dstDir, 'index.browser.bundle.mod.js'),
|
||||
file: path.join(dstDir, `bundles/${name}.esm.js`),
|
||||
format: 'es'
|
||||
},
|
||||
{
|
||||
file: path.join(dstDir, `bundles/${name}.umd.js`),
|
||||
format: 'umd',
|
||||
name: pkgCamelisedName
|
||||
}
|
||||
],
|
||||
plugins: [
|
||||
replace({
|
||||
'process.browser': true
|
||||
IS_BROWSER: true,
|
||||
preventAssignment: true
|
||||
}),
|
||||
typescriptPlugin(tsBundleOptions),
|
||||
resolve({
|
||||
browser: true
|
||||
browser: true,
|
||||
exportConditions: ['browser', 'module', 'import', 'default']
|
||||
}),
|
||||
terser()
|
||||
]
|
||||
},
|
||||
{ // Node
|
||||
{ // Node ESM
|
||||
input: input,
|
||||
output: {
|
||||
file: path.join(rootDir, pkgJson.main),
|
||||
dir: path.join(rootDir, path.dirname(pkgJson.exports['.'].node.import)),
|
||||
entryFileNames: path.basename(pkgJson.exports['.'].node.import),
|
||||
...sourcemapOutputOptions,
|
||||
format: 'es'
|
||||
},
|
||||
plugins: [
|
||||
replace({
|
||||
IS_BROWSER: false,
|
||||
preventAssignment: true
|
||||
}),
|
||||
typescriptPlugin({
|
||||
...tsBundleOptions,
|
||||
declaration: true,
|
||||
outDir: path.join(rootDir, path.dirname(pkgJson.exports['.'].node.import)),
|
||||
declarationDir: path.join(rootDir, path.dirname(pkgJson.exports['.'].node.import), 'types'),
|
||||
declarationMap: true
|
||||
}),
|
||||
commonjs({ extensions: ['.js', '.ts'] }) // the ".ts" extension is required
|
||||
],
|
||||
external
|
||||
},
|
||||
{ // Node CJS with declaration files
|
||||
input: input,
|
||||
output: {
|
||||
dir: path.join(rootDir, path.dirname(pkgJson.exports['.'].node.require)),
|
||||
entryFileNames: path.basename(pkgJson.exports['.'].node.require),
|
||||
...sourcemapOutputOptions,
|
||||
format: 'cjs'
|
||||
},
|
||||
plugins: [
|
||||
replace({
|
||||
'process.browser': false
|
||||
})
|
||||
IS_BROWSER: false,
|
||||
preventAssignment: true
|
||||
}),
|
||||
typescriptPlugin(tsBundleOptions),
|
||||
commonjs({ extensions: ['.js', '.ts'] }) // the ".ts" extension is required
|
||||
]
|
||||
// external: ['bigint-crypto-utils']
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const resolve = require('@rollup/plugin-node-resolve')
|
||||
const replace = require('@rollup/plugin-replace')
|
||||
const commonjs = require('@rollup/plugin-commonjs')
|
||||
const multi = require('@rollup/plugin-multi-entry')
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const pkgJson = require('../package.json')
|
||||
const pkgJsonLock = require('../package-lock.json')
|
||||
const mochaVersion = pkgJsonLock.dependencies.mocha.version
|
||||
const chaiVersion = pkgJsonLock.dependencies.chai.version
|
||||
const pkgName = pkgJson.name
|
||||
|
||||
const rootDir = path.join(__dirname, '..')
|
||||
|
||||
// Let's first create the appropriate html file loading mocha, chai and a bundle of the tests
|
||||
const templatePath = path.join(rootDir, pkgJson.directories.src, 'browser', 'tests-template.html')
|
||||
const dstDir = path.join(rootDir, pkgJson.directories.test, 'browser')
|
||||
const dstFileName = path.join(dstDir, 'index.html')
|
||||
|
||||
const template = fs.readFileSync(templatePath, 'utf-8')
|
||||
const bundleFile = path.join(rootDir, pkgJson.directories.lib, 'index.browser.bundle.mod.js')
|
||||
const testsJs = `
|
||||
<script type="module">
|
||||
import * as _pkg from '${path.relative(templatePath, bundleFile)}'
|
||||
window._pkg = _pkg;
|
||||
import './tests.js';
|
||||
mocha.run();
|
||||
</script>`
|
||||
|
||||
fs.writeFileSync(dstFileName,
|
||||
template.replace(/{{TESTS}}/g, testsJs).replace(/{{PKG_NAME}}/g, pkgName).replace(/{{MOCHA_VERSION}}/g, mochaVersion).replace(/{{CHAI_VERSION}}/g, chaiVersion)
|
||||
)
|
||||
|
||||
const input = path.join(rootDir, pkgJson.directories.test, '*.js')
|
||||
console.log(input)
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
input: input,
|
||||
plugins: [
|
||||
multi({ exports: false }),
|
||||
replace({
|
||||
'const _pkg = require(\'../lib/index.node\')': '',
|
||||
'const chai = require(\'chai\')': '',
|
||||
delimiters: ['', ''],
|
||||
'process.browser': true
|
||||
}),
|
||||
resolve({
|
||||
browser: true
|
||||
}),
|
||||
commonjs()
|
||||
],
|
||||
output: {
|
||||
file: path.join(rootDir, pkgJson.directories.test, 'browser', 'tests.js'),
|
||||
format: 'esm'
|
||||
},
|
||||
external: [pkgName]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,66 @@
|
|||
const puppeteer = require('puppeteer')
|
||||
|
||||
const browserTests = async ({ logWarnings = false, serverPort = 38000, keepServerRunning = false, puppeteerOptions = {} }) => {
|
||||
const server = require('./server.js').server
|
||||
await server.init()
|
||||
await server.listen(serverPort)
|
||||
const browser = await puppeteer.launch(puppeteerOptions)
|
||||
const page = await browser.newPage()
|
||||
page.on('console', function (message) {
|
||||
let ignore = message.type() === 'warning' && !logWarnings
|
||||
if (message.type() === 'error' && message.location()) {
|
||||
if (message.location().url.includes('favicon.ico')) {
|
||||
ignore = true
|
||||
}
|
||||
}
|
||||
if (ignore) return
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if (message.type() === 'error' && message.location()) {
|
||||
text = `${message.location().url} : ${text}`
|
||||
}
|
||||
let consoleType = 'log'
|
||||
switch (message.type()) {
|
||||
case 'error':
|
||||
consoleType = 'error'
|
||||
break
|
||||
case 'warning':
|
||||
consoleType = 'warn'
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
console[consoleType](text, ...args)
|
||||
})
|
||||
page.on('error', function (err) { page.emit(new Error(err)) })
|
||||
|
||||
await page.goto('http://localhost:38000/')
|
||||
const watchDog = page.waitForFunction('_mocha.state === \'stopped\'')
|
||||
await watchDog
|
||||
|
||||
if (keepServerRunning === false) {
|
||||
await page.close()
|
||||
await browser.close()
|
||||
await server.close()
|
||||
}
|
||||
}
|
||||
|
||||
const opts = {
|
||||
// puppeteer options
|
||||
puppeteerOptions: {
|
||||
headless: true
|
||||
// slowMo: 100,
|
||||
// timeout: 10000
|
||||
},
|
||||
doNotLogWarnings: true,
|
||||
keepServerRunning: false, // keep server running until manually closed with ctrl-c. In combination with puppeteerOptions.headless (or just connecting any browser to the test page) allows debugging in browser
|
||||
serverPort: 38000
|
||||
}
|
||||
browserTests(opts)
|
|
@ -0,0 +1,141 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const http = require('http')
|
||||
const path = require('path')
|
||||
const pkgJson = require('../../../package.json')
|
||||
|
||||
const rollup = require('rollup')
|
||||
const resolve = require('@rollup/plugin-node-resolve').nodeResolve
|
||||
const replace = require('@rollup/plugin-replace')
|
||||
const multi = require('@rollup/plugin-multi-entry')
|
||||
const typescript = require('@rollup/plugin-typescript')
|
||||
const commonjs = require('@rollup/plugin-commonjs')
|
||||
|
||||
const rootDir = path.join(__dirname, '..', '..', '..')
|
||||
|
||||
const regex = /^(?:(?<scope>@.*?)\/)?(?<name>.*)/ // We are going to take only the package name part if there is a scope, e.g. @my-org/package-name
|
||||
const { name } = pkgJson.name.match(regex).groups
|
||||
|
||||
const indexHtml = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>${name}</title>
|
||||
<script src="/mocha.js"></script>
|
||||
<script src="/chai.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
<div id="mocha"></div>
|
||||
<script>
|
||||
mocha.setup({
|
||||
ui: 'bdd',
|
||||
reporter: 'spec',
|
||||
color: 'true',
|
||||
timeout: 90000
|
||||
})
|
||||
</script>
|
||||
<script type="module">
|
||||
import * as _pkg from './${name}.esm.js'
|
||||
self._pkg = _pkg
|
||||
import './tests.js'
|
||||
window._mocha = mocha.run()
|
||||
</script>
|
||||
</html>`
|
||||
|
||||
async function buildTests () {
|
||||
// create a bundle
|
||||
const inputOptions = {
|
||||
input: [path.join(rootDir, pkgJson.directories.test, '**/*.ts'), path.join(rootDir, pkgJson.directories.src, '**/*.spec.ts')],
|
||||
plugins: [
|
||||
multi({ exports: true }),
|
||||
replace({
|
||||
IS_BROWSER: true,
|
||||
preventAssignment: true
|
||||
}),
|
||||
typescript(),
|
||||
resolve({
|
||||
browser: true,
|
||||
exportConditions: ['browser', 'module', 'import', 'default']
|
||||
}),
|
||||
commonjs()
|
||||
]
|
||||
}
|
||||
const bundle = await rollup.rollup(inputOptions)
|
||||
const { output } = await bundle.generate({ format: 'esm' })
|
||||
await bundle.close()
|
||||
return output[0].code
|
||||
}
|
||||
|
||||
class TestServer {
|
||||
constructor () {
|
||||
this.server = http.createServer()
|
||||
}
|
||||
|
||||
async init () {
|
||||
const tests = await buildTests()
|
||||
this.server.on('request', function (req, res) {
|
||||
if (req.url === `/${name}.esm.js`) {
|
||||
fs.readFile(path.join(rootDir, pkgJson.directories.dist, `bundles/${name}.esm.js`), function (err, data) {
|
||||
if (err) {
|
||||
res.writeHead(404)
|
||||
res.end(JSON.stringify(err))
|
||||
return
|
||||
}
|
||||
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
||||
res.end(data)
|
||||
})
|
||||
} else if (req.url === '/index.html' || req.url === '/') {
|
||||
res.writeHead(200)
|
||||
res.end(indexHtml)
|
||||
} else if (req.url === '/tests.js') {
|
||||
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
||||
res.end(tests)
|
||||
} else if (req.url === '/mocha.js') {
|
||||
fs.readFile(path.join(rootDir, 'node_modules/mocha/mocha.js'), function (err, data) {
|
||||
if (err) {
|
||||
res.writeHead(404)
|
||||
res.end(JSON.stringify(err))
|
||||
return
|
||||
}
|
||||
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
||||
res.end(data)
|
||||
})
|
||||
} else if (req.url === '/chai.js' || req.url === '/chai') {
|
||||
fs.readFile(path.join(rootDir, 'node_modules/chai/chai.js'), function (err, data) {
|
||||
if (err) {
|
||||
res.writeHead(404)
|
||||
res.end(JSON.stringify(err))
|
||||
return
|
||||
}
|
||||
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
||||
res.end(data)
|
||||
})
|
||||
} else {
|
||||
res.writeHead(404)
|
||||
res.end()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
listen (port = 38080) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.server.listen(port, error => {
|
||||
if (error) return reject(error)
|
||||
console.log(`Testing server listenning at http://localhost:${port}`)
|
||||
return resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
close () {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.server.close(error => (error) ? reject(error) : resolve())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.server = new TestServer()
|
|
@ -0,0 +1,59 @@
|
|||
const EventEmitter = require('events')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = class Builder extends EventEmitter {
|
||||
constructor (semaphoreFile, name = 'builder') {
|
||||
super()
|
||||
this.name = name
|
||||
this.firstBuild = true
|
||||
fs.mkdirSync(path.dirname(semaphoreFile), { recursive: true })
|
||||
|
||||
this.semaphoreFile = semaphoreFile
|
||||
this._ready = false
|
||||
|
||||
this.on('message', (...message) => {
|
||||
if (message !== undefined) {
|
||||
console.log(`\x1b[33mℹ [${this.name}]`, ...message, '\x1b[0m')
|
||||
}
|
||||
})
|
||||
|
||||
this.on('error', (...error) => {
|
||||
if (error !== undefined) {
|
||||
console.error(`\x1b[31m❗ [${this.name}]`, ...error, '\x1b[0m')
|
||||
}
|
||||
})
|
||||
|
||||
this.on('ready', () => {
|
||||
if (this.firstBuild === false) {
|
||||
fs.writeFileSync(this.semaphoreFile, '', 'utf-8')
|
||||
} else {
|
||||
this.firstBuild = false
|
||||
}
|
||||
this._ready = true
|
||||
})
|
||||
|
||||
this.on('busy', () => {
|
||||
this._ready = false
|
||||
})
|
||||
}
|
||||
|
||||
ready () {
|
||||
return new Promise(resolve => {
|
||||
if (this._ready === true) return resolve()
|
||||
this.once('ready', () => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async start () {
|
||||
|
||||
}
|
||||
|
||||
async close () {}
|
||||
|
||||
clean () {
|
||||
fs.rmSync(this.semaphoreFile, { force: true })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
const EventEmitter = require('events')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const rollup = require('rollup')
|
||||
const loadAndParseConfigFile = require('rollup/dist/loadConfigFile')
|
||||
|
||||
const Builder = require('./Builder.js')
|
||||
|
||||
const rootDir = path.join(__dirname, '../../../../')
|
||||
const pkgJson = require(path.join(rootDir, 'package.json'))
|
||||
|
||||
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)
|
||||
this.configPath = configPath
|
||||
this.watch = watch
|
||||
}
|
||||
|
||||
async start () {
|
||||
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
|
||||
const rollupOptions = options.filter(bundle => {
|
||||
const file = (bundle.output[0].dir !== undefined)
|
||||
? path.join(bundle.output[0].dir, bundle.output[0].entryFileNames)
|
||||
: bundle.output[0].file
|
||||
return file === path.join(rootDir, pkgJson.main)
|
||||
})[0]
|
||||
|
||||
this.builder = new RollupBundler(rollupOptions, this.watch)
|
||||
|
||||
this.builder.on('event', event => {
|
||||
switch (event.code) {
|
||||
case 'START':
|
||||
this.emit('busy')
|
||||
if (this.firstBuild === true) {
|
||||
this.emit('message', 'building your module...')
|
||||
} else {
|
||||
this.emit('message', 'file changes detected. Rebuilding module files...')
|
||||
}
|
||||
break
|
||||
|
||||
case 'BUNDLE_END':
|
||||
if (event.result) event.result.close()
|
||||
break
|
||||
|
||||
case 'END':
|
||||
if (event.result) event.result.close()
|
||||
this.emit('ready')
|
||||
break
|
||||
|
||||
case 'ERROR':
|
||||
if (event.result) event.result.close()
|
||||
this.emit('error', event.error)
|
||||
fs.writeFileSync(path.join(rootDir, pkgJson.main), '', 'utf8')
|
||||
this.emit('ready')
|
||||
break
|
||||
|
||||
default:
|
||||
this.emit('busy')
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
this.builder.start()
|
||||
|
||||
return await this.ready()
|
||||
}
|
||||
|
||||
async close () {
|
||||
await super.close()
|
||||
this.builder.close()
|
||||
}
|
||||
}
|
||||
|
||||
class RollupBundler extends EventEmitter {
|
||||
constructor (rollupOptions, watch = false) {
|
||||
super()
|
||||
this.rollupOptions = rollupOptions
|
||||
this.watch = watch
|
||||
}
|
||||
|
||||
async start () {
|
||||
if (this.watch === true) {
|
||||
this.watcher = rollup.watch(this.rollupOptions)
|
||||
|
||||
this.watcher.on('event', event => {
|
||||
this.emit('event', event)
|
||||
})
|
||||
} else {
|
||||
if (fs.existsSync(path.join(rootDir, pkgJson.main)) === false) {
|
||||
await this._bundle()
|
||||
} else {
|
||||
this.emit('event', { code: 'END', noBuild: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _bundle () {
|
||||
this.emit('event', { code: 'START' })
|
||||
for (const optionsObj of [].concat(this.rollupOptions)) {
|
||||
try {
|
||||
const bundle = await rollup.rollup(optionsObj)
|
||||
try {
|
||||
await Promise.all(optionsObj.output.map(bundle.write))
|
||||
this.emit('event', { code: 'BUNDLE_END' })
|
||||
} catch (error) {
|
||||
this.emit('event', { code: 'ERROR', error })
|
||||
}
|
||||
} catch (error) {
|
||||
this.emit('event', { code: 'ERROR', error })
|
||||
}
|
||||
}
|
||||
this.emit('event', { code: 'END' })
|
||||
}
|
||||
|
||||
close () {
|
||||
if (this.watcher !== undefined) this.watcher.close()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
const ts = require('typescript')
|
||||
const JSON5 = require('json5')
|
||||
|
||||
const Builder = require('./Builder.js')
|
||||
|
||||
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 readFileAndMangle = (path) => { // We need to change the include or file in the original file to only compile the tests
|
||||
const fileStr = fs.readFileSync(path, 'utf8')
|
||||
const config = JSON5.parse(fileStr)
|
||||
if (config.file) delete config.file
|
||||
config.include = ['build/typings/**/*.ts', 'test/**/*.ts', 'src/**/*.spec.ts']
|
||||
return JSON.stringify(config)
|
||||
}
|
||||
const configFile = ts.readJsonConfigFile(configPath, readFileAndMangle)
|
||||
|
||||
const parsedTsConfig = ts.parseJsonSourceFileConfigFileContent(configFile, ts.sys, path.dirname(configPath))
|
||||
|
||||
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(
|
||||
parsedTsConfig.fileNames,
|
||||
{
|
||||
...parsedTsConfig.options,
|
||||
rootDir,
|
||||
outDir: this.tempDir,
|
||||
module: 'commonjs',
|
||||
noEmit: false,
|
||||
noResolve: true,
|
||||
sourceMap: true
|
||||
},
|
||||
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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
|
||||
const chai = require('chai')
|
||||
const rimraf = require('rimraf')
|
||||
|
||||
const RollupBuilder = require('./builders/RollupBuilder.js')
|
||||
const TestsBuilder = require('./builders/TestsBuilder.js')
|
||||
|
||||
const rootDir = path.join(__dirname, '../../../')
|
||||
|
||||
global.chai = chai
|
||||
|
||||
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 () {
|
||||
// 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 = require(rootDir)
|
||||
},
|
||||
async function () {
|
||||
this.timeout('120000')
|
||||
await Promise.all([rollupBuilder.ready(), testBuilder.ready()])
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 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 })
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import * as _pkgModule from '../..'
|
||||
|
||||
export as namespace _pkg
|
||||
|
||||
export = _pkgModule
|
|
@ -0,0 +1 @@
|
|||
declare const IS_BROWSER: boolean
|
|
@ -0,0 +1 @@
|
|||
function n(n){return n>=0?n:-n}function t(n){if(1n===(n=BigInt(n)))return 1;let t=1;do{t++}while((n>>=1n)>1n);return t}function r(n,t){let r=BigInt(n),i=BigInt(t);if(r<=0n||i<=0n)throw new RangeError("a and b MUST be > 0");let e=0n,u=1n,o=1n,f=0n;for(;0n!==r;){const n=i/r,t=i%r,c=e-o*n,g=u-f*n;i=r,r=t,e=o,u=f,o=c,f=g}return{g:i,x:e,y:u}}function i(t,r){let i=BigInt(n(t)),e=BigInt(n(r));if(0n===i)return e;if(0n===e)return i;let u=0n;for(;0n===(1n&(i|e));)i>>=1n,e>>=1n,u++;for(;0n===(1n&i);)i>>=1n;do{for(;0n===(1n&e);)e>>=1n;if(i>e){const n=i;i=e,e=n}e-=i}while(0n!==e);return i<<u}function e(t,r){const e=BigInt(t),u=BigInt(r);return 0n===e&&0n===u?BigInt(0):n(e*u)/i(e,u)}function u(n,t){return n>=t?n:t}function o(n,t){return n>=t?t:n}function f(n,t){const r=BigInt(t);if(t<=0)return NaN;const i=BigInt(n)%r;return i<0n?i+r:i}function c(n,t){try{const i=r(f(n,t),t);return 1n!==i.g?NaN:f(i.x,t)}catch(n){return NaN}}function g(t,r,i){const e=BigInt(i);if(e<=0n)return NaN;if(1n===e)return BigInt(0);let u=f(t,e);if((r=BigInt(r))<0n)return c(g(u,n(r),e),e);let o=1n;for(;r>0;)r%2n===1n&&(o=o*u%e),r/=2n,u=u**2n%e;return o}export{n as abs,t as bitLength,r as eGcd,i as gcd,e as lcm,u as max,o as min,c as modInv,g as modPow,f as toZn};
|
|
@ -0,0 +1 @@
|
|||
var bigintModArith=function(n){"use strict";function t(n){return n>=0?n:-n}function r(n,t){let r=BigInt(n),i=BigInt(t);if(r<=0n||i<=0n)throw new RangeError("a and b MUST be > 0");let e=0n,o=1n,u=1n,f=0n;for(;0n!==r;){const n=i/r,t=i%r,c=e-u*n,g=o-f*n;i=r,r=t,e=u,o=f,u=c,f=g}return{g:i,x:e,y:o}}function i(n,r){let i=BigInt(t(n)),e=BigInt(t(r));if(0n===i)return e;if(0n===e)return i;let o=0n;for(;0n===(1n&(i|e));)i>>=1n,e>>=1n,o++;for(;0n===(1n&i);)i>>=1n;do{for(;0n===(1n&e);)e>>=1n;if(i>e){const n=i;i=e,e=n}e-=i}while(0n!==e);return i<<o}function e(n,t){const r=BigInt(t);if(t<=0)return NaN;const i=BigInt(n)%r;return i<0n?i+r:i}function o(n,t){try{const i=r(e(n,t),t);return 1n!==i.g?NaN:e(i.x,t)}catch(n){return NaN}}return n.abs=t,n.bitLength=function(n){if(1n===(n=BigInt(n)))return 1;let t=1;do{t++}while((n>>=1n)>1n);return t},n.eGcd=r,n.gcd=i,n.lcm=function(n,r){const e=BigInt(n),o=BigInt(r);return 0n===e&&0n===o?BigInt(0):t(e*o)/i(e,o)},n.max=function(n,t){return n>=t?n:t},n.min=function(n,t){return n>=t?t:n},n.modInv=o,n.modPow=function n(r,i,u){const f=BigInt(u);if(f<=0n)return NaN;if(1n===f)return BigInt(0);let c=e(r,f);if((i=BigInt(i))<0n)return o(n(c,t(i),f),f);let g=1n;for(;i>0;)i%2n===1n&&(g=g*c%f),i/=2n,c=c**2n%f;return g},n.toZn=e,Object.defineProperty(n,"__esModule",{value:!0}),n}({});
|
|
@ -0,0 +1 @@
|
|||
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((n="undefined"!=typeof globalThis?globalThis:n||self).bigintModArith={})}(this,(function(n){"use strict";function t(n){return n>=0?n:-n}function e(n,t){let e=BigInt(n),r=BigInt(t);if(e<=0n||r<=0n)throw new RangeError("a and b MUST be > 0");let i=0n,o=1n,f=1n,u=0n;for(;0n!==e;){const n=r/e,t=r%e,c=i-f*n,g=o-u*n;r=e,e=t,i=f,o=u,f=c,u=g}return{g:r,x:i,y:o}}function r(n,e){let r=BigInt(t(n)),i=BigInt(t(e));if(0n===r)return i;if(0n===i)return r;let o=0n;for(;0n===(1n&(r|i));)r>>=1n,i>>=1n,o++;for(;0n===(1n&r);)r>>=1n;do{for(;0n===(1n&i);)i>>=1n;if(r>i){const n=r;r=i,i=n}i-=r}while(0n!==i);return r<<o}function i(n,t){const e=BigInt(t);if(t<=0)return NaN;const r=BigInt(n)%e;return r<0n?r+e:r}function o(n,t){try{const r=e(i(n,t),t);return 1n!==r.g?NaN:i(r.x,t)}catch(n){return NaN}}n.abs=t,n.bitLength=function(n){if(1n===(n=BigInt(n)))return 1;let t=1;do{t++}while((n>>=1n)>1n);return t},n.eGcd=e,n.gcd=r,n.lcm=function(n,e){const i=BigInt(n),o=BigInt(e);return 0n===i&&0n===o?BigInt(0):t(i*o)/r(i,o)},n.max=function(n,t){return n>=t?n:t},n.min=function(n,t){return n>=t?t:n},n.modInv=o,n.modPow=function n(e,r,f){const u=BigInt(f);if(u<=0n)return NaN;if(1n===u)return BigInt(0);let c=i(e,u);if((r=BigInt(r))<0n)return o(n(c,t(r),u),u);let g=1n;for(;r>0;)r%2n===1n&&(g=g*c%u),r/=2n,c=c**2n%u;return g},n.toZn=i,Object.defineProperty(n,"__esModule",{value:!0})}));
|
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