From dc5495216d092de6890c9245e99453666376461f Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Wed, 11 Dec 2019 23:44:43 +0100 Subject: [PATCH 1/8] chore: change eslint-rules for tasks/ - use es2017 rules as NodeJS supports it today - add "prefer-const" --- tasks/.eslintrc | 16 ---------------- tasks/.eslintrc.js | 12 ++++++++++++ tasks/metrics.js | 4 ++-- tasks/parser.js | 12 ++++++------ tasks/publish.js | 16 ++++++++-------- tasks/test.js | 30 +++++++++++++++--------------- tasks/util/git.js | 6 +++--- tasks/version.js | 6 +++--- 8 files changed, 49 insertions(+), 53 deletions(-) delete mode 100644 tasks/.eslintrc create mode 100644 tasks/.eslintrc.js diff --git a/tasks/.eslintrc b/tasks/.eslintrc deleted file mode 100644 index 34615029..00000000 --- a/tasks/.eslintrc +++ /dev/null @@ -1,16 +0,0 @@ -{ - "globals": { - "require": true - }, - "rules": { - // Disabling for tests, for now. - "no-path-concat": 0, - - "no-var": 0, - "no-shadow": 0, - "handle-callback-err": 0, - "no-console": 0, - "no-process-env": 0, - "dot-notation": [2, {"allowKeywords": true}] - } -} \ No newline at end of file diff --git a/tasks/.eslintrc.js b/tasks/.eslintrc.js new file mode 100644 index 00000000..7c985caa --- /dev/null +++ b/tasks/.eslintrc.js @@ -0,0 +1,12 @@ +module.exports = { + extends: ['../.eslintrc.js'], + parserOptions: { + sourceType: 'module', + ecmaVersion: 2017, + ecmaFeatures: {} + }, + rules: { + 'no-process-env': 'off', + 'prefer-const': 'warn' + } +}; diff --git a/tasks/metrics.js b/tasks/metrics.js index f903ceb7..bcb76318 100644 --- a/tasks/metrics.js +++ b/tasks/metrics.js @@ -1,10 +1,10 @@ -var _ = require('underscore'), +const _ = require('underscore'), async = require('neo-async'), metrics = require('../bench'); module.exports = function(grunt) { grunt.registerTask('metrics', function() { - var done = this.async(), + const done = this.async(), execName = grunt.option('name'), events = {}; diff --git a/tasks/parser.js b/tasks/parser.js index 0adba111..21e7442a 100644 --- a/tasks/parser.js +++ b/tasks/parser.js @@ -1,16 +1,16 @@ -var childProcess = require('child_process'); +const childProcess = require('child_process'); module.exports = function(grunt) { grunt.registerTask('parser', 'Generate jison parser.', function() { - var done = this.async(); + const done = this.async(); - var cmd = './node_modules/.bin/jison'; + let cmd = './node_modules/.bin/jison'; if (process.platform === 'win32') { cmd = 'node_modules\\.bin\\jison.cmd'; } - var child = childProcess.spawn( + const child = childProcess.spawn( cmd, ['-m', 'js', 'src/handlebars.yy', 'src/handlebars.l'], { stdio: 'inherit' } @@ -22,14 +22,14 @@ module.exports = function(grunt) { return; } - var src = [ + const src = [ 'src/parser-prefix.js', 'handlebars.js', 'src/parser-suffix.js' ] .map(grunt.file.read) .join(''); - grunt.file.delete('handlebars.js'); + grunt.file['delete']('handlebars.js'); grunt.file.write('lib/handlebars/compiler/parser.js', src); grunt.log.writeln('Parser "lib/handlebars/compiler/parser.js" created.'); diff --git a/tasks/publish.js b/tasks/publish.js index b22c9b8e..207e281f 100644 --- a/tasks/publish.js +++ b/tasks/publish.js @@ -1,4 +1,4 @@ -var _ = require('underscore'), +const _ = require('underscore'), async = require('neo-async'), AWS = require('aws-sdk'), git = require('./util/git'), @@ -6,7 +6,7 @@ var _ = require('underscore'), module.exports = function(grunt) { grunt.registerTask('publish:latest', function() { - var done = this.async(); + const done = this.async(); git.debug(function(remotes, branches) { grunt.log.writeln('remotes: ' + remotes); @@ -15,7 +15,7 @@ module.exports = function(grunt) { git.commitInfo(function(err, info) { grunt.log.writeln('tag: ' + info.tagName); - var files = []; + const files = []; // Publish the master as "latest" and with the commit-id if (info.isMaster) { @@ -40,7 +40,7 @@ module.exports = function(grunt) { }); }); grunt.registerTask('publish:version', function() { - var done = this.async(); + const done = this.async(); initSDK(); git.commitInfo(function(err, info) { @@ -52,7 +52,7 @@ module.exports = function(grunt) { }); function initSDK() { - var bucket = process.env.S3_BUCKET_NAME, + const bucket = process.env.S3_BUCKET_NAME, key = process.env.S3_ACCESS_KEY_ID, secret = process.env.S3_SECRET_ACCESS_KEY; @@ -63,13 +63,13 @@ module.exports = function(grunt) { AWS.config.update({ accessKeyId: key, secretAccessKey: secret }); } function publish(files, callback) { - var s3 = new AWS.S3(), + const s3 = new AWS.S3(), bucket = process.env.S3_BUCKET_NAME; async.each( _.keys(files), function(file, callback) { - var params = { + const params = { Bucket: bucket, Key: file, Body: grunt.file.read(files[file]) @@ -87,7 +87,7 @@ module.exports = function(grunt) { ); } function fileMap(suffixes) { - var map = {}; + const map = {}; _.each( [ 'handlebars.js', diff --git a/tasks/test.js b/tasks/test.js index 94fc8c25..c7279cf3 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -1,13 +1,13 @@ -var childProcess = require('child_process'), +const childProcess = require('child_process'), fs = require('fs'), os = require('os'); module.exports = function(grunt) { grunt.registerTask('test:bin', function() { - var done = this.async(); + const done = this.async(); - var cmd = './bin/handlebars'; - var args = ['-a', 'spec/artifacts/empty.handlebars']; + let cmd = './bin/handlebars'; + const args = ['-a', 'spec/artifacts/empty.handlebars']; // On Windows, the executable handlebars.js file cannot be run directly if (os.platform() === 'win32') { @@ -19,7 +19,7 @@ module.exports = function(grunt) { throw err; } - var expected = fs + const expected = fs .readFileSync('./spec/expected/empty.amd.js') .toString() .replace(/\r\n/g, '\n'); @@ -38,9 +38,9 @@ module.exports = function(grunt) { }); }); grunt.registerTask('test:mocha', function() { - var done = this.async(); + const done = this.async(); - var runner = childProcess.fork('./spec/env/runner', [], { + const runner = childProcess.fork('./spec/env/runner', [], { stdio: 'inherit' }); runner.on('close', function(code) { @@ -51,24 +51,24 @@ module.exports = function(grunt) { }); }); grunt.registerTask('test:cov', function() { - var done = this.async(); + const done = this.async(); - var runner = childProcess.fork( + const runner = childProcess.spawn( 'node_modules/istanbul/lib/cli.js', ['cover', '--source-map', '--', './spec/env/runner.js'], { stdio: 'inherit' } ); - runner.on('close', function(code) { - if (code != 0) { + runner.on('exit', function(code) { + if (code !== 0) { grunt.fatal(code + ' tests failed'); } done(); }); }); grunt.registerTask('test:min', function() { - var done = this.async(); + const done = this.async(); - var runner = childProcess.fork('./spec/env/runner', ['--min'], { + const runner = childProcess.fork('./spec/env/runner', ['--min'], { stdio: 'inherit' }); runner.on('close', function(code) { @@ -80,9 +80,9 @@ module.exports = function(grunt) { }); grunt.registerTask('test:check-cov', function() { - var done = this.async(); + const done = this.async(); - var runner = childProcess.fork( + const runner = childProcess.fork( 'node_modules/istanbul/lib/cli.js', [ 'check-coverage', diff --git a/tasks/util/git.js b/tasks/util/git.js index 5418bf03..4dc5cbde 100644 --- a/tasks/util/git.js +++ b/tasks/util/git.js @@ -1,4 +1,4 @@ -var childProcess = require('child_process'); +const childProcess = require('child_process'); module.exports = { debug: function(callback) { @@ -99,13 +99,13 @@ module.exports = { throw new Error('git.tagName: ' + err.message); } - var tags = stdout.trim().split(/\n/); + let tags = stdout.trim().split(/\n/); tags = tags.filter(function(info) { info = info.split('-'); return info.length == 1; }); - var versionTags = tags.filter(function(info) { + const versionTags = tags.filter(function(info) { return /^v/.test(info[0]); }); diff --git a/tasks/version.js b/tasks/version.js index 05c05fa0..f9d5c567 100644 --- a/tasks/version.js +++ b/tasks/version.js @@ -1,4 +1,4 @@ -var async = require('neo-async'), +const async = require('neo-async'), git = require('./util/git'), semver = require('semver'); @@ -7,7 +7,7 @@ module.exports = function(grunt) { 'version', 'Updates the current release version', function() { - var done = this.async(), + const done = this.async(), pkg = grunt.config('pkg'), version = grunt.option('ver'); @@ -61,7 +61,7 @@ module.exports = function(grunt) { ); function replace(path, regex, value) { - var content = grunt.file.read(path); + let content = grunt.file.read(path); content = content.replace(regex, value); grunt.file.write(path, content); } From dde108e2836928ec4c0509fdfb32da052022357f Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 12 Dec 2019 01:00:57 +0100 Subject: [PATCH 2/8] test/style: refactor test-task to make it more readable --- Gruntfile.js | 2 + tasks/.eslintrc.js | 4 +- tasks/parser.js | 2 +- tasks/test-bin.js | 55 +++++++++++++++++++++++ tasks/test-mocha.js | 66 +++++++++++++++++++++++++++ tasks/test.js | 107 -------------------------------------------- 6 files changed, 127 insertions(+), 109 deletions(-) create mode 100644 tasks/test-bin.js create mode 100644 tasks/test-mocha.js delete mode 100644 tasks/test.js diff --git a/Gruntfile.js b/Gruntfile.js index 6e32e162..84a36158 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -263,6 +263,8 @@ module.exports = function(grunt) { this.registerTask('amd', ['babel:amd', 'requirejs']); + this.registerTask('test', ['test:bin', 'test:cov', 'test:check-cov']); + grunt.registerTask('bench', ['metrics']); if (process.env.SAUCE_USERNAME) { diff --git a/tasks/.eslintrc.js b/tasks/.eslintrc.js index 7c985caa..642a9ab0 100644 --- a/tasks/.eslintrc.js +++ b/tasks/.eslintrc.js @@ -7,6 +7,8 @@ module.exports = { }, rules: { 'no-process-env': 'off', - 'prefer-const': 'warn' + 'prefer-const': 'warn', + 'compat/compat': 'off', + 'dot-notation': ['error', { allowKeywords: true }] } }; diff --git a/tasks/parser.js b/tasks/parser.js index 21e7442a..a0c19e47 100644 --- a/tasks/parser.js +++ b/tasks/parser.js @@ -29,7 +29,7 @@ module.exports = function(grunt) { ] .map(grunt.file.read) .join(''); - grunt.file['delete']('handlebars.js'); + grunt.file.delete('handlebars.js'); grunt.file.write('lib/handlebars/compiler/parser.js', src); grunt.log.writeln('Parser "lib/handlebars/compiler/parser.js" created.'); diff --git a/tasks/test-bin.js b/tasks/test-bin.js new file mode 100644 index 00000000..48c0ded8 --- /dev/null +++ b/tasks/test-bin.js @@ -0,0 +1,55 @@ +const childProcess = require('child_process'), + fs = require('fs'), + os = require('os'), + expect = require('chai').expect, + util = require('util'); + +const readFile = util.promisify(fs.readFile); +const execFile = util.promisify(childProcess.execFile); + +module.exports = function(grunt) { + grunt.registerTask( + 'test:bin', + wrapAsync(async function() { + const { stdout } = await execFileWithWin32Fallback('./bin/handlebars', [ + '-a', + 'spec/artifacts/empty.handlebars' + ]); + + const expectedOutput = await readFile( + './spec/expected/empty.amd.js', + 'utf-8' + ); + + const normalizedOutput = normalizeCrlf(stdout); + const normalizedExpectedOutput = normalizeCrlf(expectedOutput); + expect(normalizedOutput).to.equal(normalizedExpectedOutput); + }) + ); + + async function execFileWithWin32Fallback(command, args) { + // On Windows, the executable handlebars.js file cannot be run directly + if (os.platform() === 'win32') { + args.unshift(command); + command = process.argv[0]; + } + return execFile(command, args, { encoding: 'utf-8' }); + } + + function normalizeCrlf(string) { + if (string != null) { + return string.replace(/\r\n/g, '\n'); + } + return string; + } + + function wrapAsync(asyncFunction) { + return function() { + asyncFunction() + .catch(error => { + grunt.fatal(error); + }) + .finally(this.async()); + }; + } +}; diff --git a/tasks/test-mocha.js b/tasks/test-mocha.js new file mode 100644 index 00000000..9bed2c63 --- /dev/null +++ b/tasks/test-mocha.js @@ -0,0 +1,66 @@ +const childProcess = require('child_process'); + +module.exports = function(grunt) { + grunt.registerTask( + 'test:mocha', + promiseBasedTask(async () => forkAndWait('./spec/env/runner')) + ); + + grunt.registerTask( + 'test:cov', + promiseBasedTask(async () => + forkAndWait( + 'node_modules/istanbul/lib/cli.js', + 'cover', + '--source-map', + '--', + './spec/env/runner.js' + ) + ) + ); + + grunt.registerTask( + 'test:min', + promiseBasedTask(async () => forkAndWait('./spec/env/runner', '--min')) + ); + + grunt.registerTask( + 'test:check-cov', + promiseBasedTask(() => + forkAndWait( + 'node_modules/istanbul/lib/cli.js', + 'check-coverage', + '--statements', + '100', + '--functions', + '100', + '--branches', + '100', + '--lines 100' + ) + ) + ); + + function promiseBasedTask(asyncFunction) { + return function() { + asyncFunction() + .catch(error => { + grunt.fatal(error); + }) + .finally(this.async()); + }; + } + + async function forkAndWait(command, ...args) { + return new Promise((resolve, reject) => { + const child = childProcess.fork(command, args, { stdio: 'inherit' }); + child.on('close', code => { + if (code !== 0) { + reject(new Error(`Child process failed with exit-code ${code}`)); + } + }); + }); + } + + grunt.registerTask('test', ['test:bin', 'test:cov', 'test:check-cov']); +}; diff --git a/tasks/test.js b/tasks/test.js deleted file mode 100644 index c7279cf3..00000000 --- a/tasks/test.js +++ /dev/null @@ -1,107 +0,0 @@ -const childProcess = require('child_process'), - fs = require('fs'), - os = require('os'); - -module.exports = function(grunt) { - grunt.registerTask('test:bin', function() { - const done = this.async(); - - let cmd = './bin/handlebars'; - const args = ['-a', 'spec/artifacts/empty.handlebars']; - - // On Windows, the executable handlebars.js file cannot be run directly - if (os.platform() === 'win32') { - args.unshift(cmd); - cmd = process.argv[0]; - } - childProcess.execFile(cmd, args, function(err, stdout) { - if (err) { - throw err; - } - - const expected = fs - .readFileSync('./spec/expected/empty.amd.js') - .toString() - .replace(/\r\n/g, '\n'); - - if (stdout.toString() !== expected) { - throw new Error( - 'Expected binary output differed:\n\n"' + - stdout + - '"\n\n"' + - expected + - '"' - ); - } - - done(); - }); - }); - grunt.registerTask('test:mocha', function() { - const done = this.async(); - - const runner = childProcess.fork('./spec/env/runner', [], { - stdio: 'inherit' - }); - runner.on('close', function(code) { - if (code !== 0) { - grunt.fatal(code + ' tests failed'); - } - done(); - }); - }); - grunt.registerTask('test:cov', function() { - const done = this.async(); - - const runner = childProcess.spawn( - 'node_modules/istanbul/lib/cli.js', - ['cover', '--source-map', '--', './spec/env/runner.js'], - { stdio: 'inherit' } - ); - runner.on('exit', function(code) { - if (code !== 0) { - grunt.fatal(code + ' tests failed'); - } - done(); - }); - }); - grunt.registerTask('test:min', function() { - const done = this.async(); - - const runner = childProcess.fork('./spec/env/runner', ['--min'], { - stdio: 'inherit' - }); - runner.on('close', function(code) { - if (code !== 0) { - grunt.fatal(code + ' tests failed'); - } - done(); - }); - }); - - grunt.registerTask('test:check-cov', function() { - const done = this.async(); - - const runner = childProcess.fork( - 'node_modules/istanbul/lib/cli.js', - [ - 'check-coverage', - '--statements', - '100', - '--functions', - '100', - '--branches', - '100', - '--lines 100' - ], - { stdio: 'inherit' } - ); - runner.on('close', function(code) { - if (code != 0) { - grunt.fatal('Coverage check failed: ' + code); - } - done(); - }); - }); - grunt.registerTask('test', ['test:bin', 'test:cov', 'test:check-cov']); -}; From 3a5b65e02be8949e778cf2a93d71f6cc49a31232 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 12 Dec 2019 22:19:30 +0100 Subject: [PATCH 3/8] test/style: refactor parser task --- tasks/parser.js | 62 ++++++++++++------------- tasks/test-bin.js | 82 +++++++++++++++------------------- tasks/test-mocha.js | 82 +++++++++++----------------------- tasks/util/async-grunt-task.js | 13 ++++++ tasks/util/exec-file.js | 51 +++++++++++++++++++++ 5 files changed, 154 insertions(+), 136 deletions(-) create mode 100644 tasks/util/async-grunt-task.js create mode 100644 tasks/util/exec-file.js diff --git a/tasks/parser.js b/tasks/parser.js index a0c19e47..252b8c26 100644 --- a/tasks/parser.js +++ b/tasks/parser.js @@ -1,39 +1,33 @@ -const childProcess = require('child_process'); +const { execFileWithInheritedOutput } = require('./util/exec-file'); +const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task'); + +const OUTPUT_FILE = 'lib/handlebars/compiler/parser.js'; module.exports = function(grunt) { - grunt.registerTask('parser', 'Generate jison parser.', function() { - const done = this.async(); + const registerAsyncTask = createRegisterAsyncTaskFn(grunt); - let cmd = './node_modules/.bin/jison'; - - if (process.platform === 'win32') { - cmd = 'node_modules\\.bin\\jison.cmd'; - } - - const child = childProcess.spawn( - cmd, - ['-m', 'js', 'src/handlebars.yy', 'src/handlebars.l'], - { stdio: 'inherit' } - ); - child.on('exit', function(code) { - if (code != 0) { - grunt.fatal('Jison failure: ' + code); - done(); - return; - } - - const src = [ - 'src/parser-prefix.js', - 'handlebars.js', - 'src/parser-suffix.js' - ] - .map(grunt.file.read) - .join(''); - grunt.file.delete('handlebars.js'); - - grunt.file.write('lib/handlebars/compiler/parser.js', src); - grunt.log.writeln('Parser "lib/handlebars/compiler/parser.js" created.'); - done(); - }); + registerAsyncTask('parser', async () => { + await runJison(); + combineWithPrefixAndSuffix(); + grunt.log.writeln(`Parser "${OUTPUT_FILE}" created.`); }); + + async function runJison() { + await execFileWithInheritedOutput('jison', [ + '-m', + 'js', + 'src/handlebars.yy', + 'src/handlebars.l' + ]); + } + + function combineWithPrefixAndSuffix() { + const combinedParserSourceCode = + grunt.file.read('src/parser-prefix.js') + + grunt.file.read('handlebars.js') + + grunt.file.read('src/parser-suffix.js'); + + grunt.file.write(OUTPUT_FILE, combinedParserSourceCode); + grunt.file.delete('handlebars.js'); + } }; diff --git a/tasks/test-bin.js b/tasks/test-bin.js index 48c0ded8..8873c9f0 100644 --- a/tasks/test-bin.js +++ b/tasks/test-bin.js @@ -1,55 +1,45 @@ const childProcess = require('child_process'), fs = require('fs'), os = require('os'), - expect = require('chai').expect, - util = require('util'); - -const readFile = util.promisify(fs.readFile); -const execFile = util.promisify(childProcess.execFile); + expect = require('chai').expect; module.exports = function(grunt) { - grunt.registerTask( - 'test:bin', - wrapAsync(async function() { - const { stdout } = await execFileWithWin32Fallback('./bin/handlebars', [ - '-a', - 'spec/artifacts/empty.handlebars' - ]); + grunt.registerTask('test:bin', function() { + const stdout = executeBinHandlebars( + '-a', + 'spec/artifacts/empty.handlebars' + ); - const expectedOutput = await readFile( - './spec/expected/empty.amd.js', - 'utf-8' - ); + const expectedOutput = fs.readFileSync( + './spec/expected/empty.amd.js', + 'utf-8' + ); - const normalizedOutput = normalizeCrlf(stdout); - const normalizedExpectedOutput = normalizeCrlf(expectedOutput); - expect(normalizedOutput).to.equal(normalizedExpectedOutput); - }) - ); + const normalizedOutput = normalizeCrlf(stdout); + const normalizedExpectedOutput = normalizeCrlf(expectedOutput); - async function execFileWithWin32Fallback(command, args) { - // On Windows, the executable handlebars.js file cannot be run directly - if (os.platform() === 'win32') { - args.unshift(command); - command = process.argv[0]; - } - return execFile(command, args, { encoding: 'utf-8' }); - } - - function normalizeCrlf(string) { - if (string != null) { - return string.replace(/\r\n/g, '\n'); - } - return string; - } - - function wrapAsync(asyncFunction) { - return function() { - asyncFunction() - .catch(error => { - grunt.fatal(error); - }) - .finally(this.async()); - }; - } + expect(normalizedOutput).to.equal(normalizedExpectedOutput); + }); }; + +// helper functions + +function executeBinHandlebars(...args) { + if (os.platform() === 'win32') { + // On Windows, the executable handlebars.js file cannot be run directly + const nodeJs = process.argv[0]; + return execFilesSyncUtf8(nodeJs, ['./bin/handlebars'].concat(args)); + } + return execFilesSyncUtf8('./bin/handlebars', args); +} + +function execFilesSyncUtf8(command, args) { + return childProcess.execFileSync(command, args, { encoding: 'utf-8' }); +} + +function normalizeCrlf(string) { + if (typeof string === 'string') { + return string.replace(/\r\n/g, '\n'); + } + return string; +} diff --git a/tasks/test-mocha.js b/tasks/test-mocha.js index 9bed2c63..445a88cf 100644 --- a/tasks/test-mocha.js +++ b/tasks/test-mocha.js @@ -1,66 +1,36 @@ -const childProcess = require('child_process'); +const { execNodeJsScriptWithInheritedOutput } = require('./util/exec-file'); +const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task'); module.exports = function(grunt) { - grunt.registerTask( - 'test:mocha', - promiseBasedTask(async () => forkAndWait('./spec/env/runner')) + const registerAsyncTask = createRegisterAsyncTaskFn(grunt); + + registerAsyncTask('test:mocha', async () => + execNodeJsScriptWithInheritedOutput('./spec/env/runner') ); - grunt.registerTask( - 'test:cov', - promiseBasedTask(async () => - forkAndWait( - 'node_modules/istanbul/lib/cli.js', - 'cover', - '--source-map', - '--', - './spec/env/runner.js' - ) - ) + registerAsyncTask('test:cov', async () => + execNodeJsScriptWithInheritedOutput('node_modules/istanbul/lib/cli.js', [ + 'cover', + '--source-map', + '--', + './spec/env/runner.js' + ]) ); - grunt.registerTask( - 'test:min', - promiseBasedTask(async () => forkAndWait('./spec/env/runner', '--min')) + registerAsyncTask('test:min', async () => + execNodeJsScriptWithInheritedOutput('./spec/env/runner', ['--min']) ); - grunt.registerTask( - 'test:check-cov', - promiseBasedTask(() => - forkAndWait( - 'node_modules/istanbul/lib/cli.js', - 'check-coverage', - '--statements', - '100', - '--functions', - '100', - '--branches', - '100', - '--lines 100' - ) - ) + registerAsyncTask('test:check-cov', async () => + execNodeJsScriptWithInheritedOutput('node_modules/istanbul/lib/cli.js', [ + 'check-coverage', + '--statements', + '100', + '--functions', + '100', + '--branches', + '100', + '--lines 100' + ]) ); - - function promiseBasedTask(asyncFunction) { - return function() { - asyncFunction() - .catch(error => { - grunt.fatal(error); - }) - .finally(this.async()); - }; - } - - async function forkAndWait(command, ...args) { - return new Promise((resolve, reject) => { - const child = childProcess.fork(command, args, { stdio: 'inherit' }); - child.on('close', code => { - if (code !== 0) { - reject(new Error(`Child process failed with exit-code ${code}`)); - } - }); - }); - } - - grunt.registerTask('test', ['test:bin', 'test:cov', 'test:check-cov']); }; diff --git a/tasks/util/async-grunt-task.js b/tasks/util/async-grunt-task.js new file mode 100644 index 00000000..20360570 --- /dev/null +++ b/tasks/util/async-grunt-task.js @@ -0,0 +1,13 @@ +module.exports = { createRegisterAsyncTaskFn }; + +function createRegisterAsyncTaskFn(grunt) { + return function registerAsyncTask(name, asyncFunction) { + grunt.registerTask(name, function() { + asyncFunction() + .catch(error => { + grunt.fatal(error); + }) + .finally(this.async()); + }); + }; +} diff --git a/tasks/util/exec-file.js b/tasks/util/exec-file.js new file mode 100644 index 00000000..66fd8e92 --- /dev/null +++ b/tasks/util/exec-file.js @@ -0,0 +1,51 @@ +const childProcess = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +module.exports = { + execNodeJsScriptWithInheritedOutput, + execFileWithInheritedOutput +}; + +async function execNodeJsScriptWithInheritedOutput(command, args) { + return new Promise((resolve, reject) => { + const child = childProcess.fork(command, args, { stdio: 'inherit' }); + child.on('close', code => { + if (code !== 0) { + reject(new Error(`Child process failed with exit-code ${code}`)); + } + resolve(); + }); + }); +} + +async function execFileWithInheritedOutput(command, args) { + return new Promise((resolve, reject) => { + const resolvedCommand = preferLocalDependencies(command); + const child = childProcess.spawn(resolvedCommand, args, { + stdio: 'inherit' + }); + child.on('exit', code => { + if (code !== 0) { + reject(new Error(`Child process failed with exit-code ${code}`)); + } + resolve(); + }); + }); +} + +function preferLocalDependencies(command) { + const localCandidate = resolveLocalCandidate(command); + + if (fs.existsSync(localCandidate)) { + return localCandidate; + } + return command; +} + +function resolveLocalCandidate(command) { + if (process.platform === 'win32') { + return path.join('node_modules', '.bin', command + '.cmd'); + } + return path.join('node_modules', '.bin', command); +} From 1ebce2b53cb62d81954b64f48bc0bc7112395c8a Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 12 Dec 2019 23:04:35 +0100 Subject: [PATCH 4/8] test/style: use nyc instead of istanbul, npm audit fix --- .istanbul.yml | 2 - Gruntfile.js | 2 +- nyc.config.js | 9 + package-lock.json | 1110 +++++++++++++++++++++++++++++++++---------- package.json | 4 +- spec/builtins.js | 17 + tasks/test-mocha.js | 20 +- 7 files changed, 902 insertions(+), 262 deletions(-) delete mode 100644 .istanbul.yml create mode 100644 nyc.config.js diff --git a/.istanbul.yml b/.istanbul.yml deleted file mode 100644 index 960643ef..00000000 --- a/.istanbul.yml +++ /dev/null @@ -1,2 +0,0 @@ -instrumentation: - excludes: ['**/spec/**', '**/handlebars/compiler/parser.js'] diff --git a/Gruntfile.js b/Gruntfile.js index 84a36158..0326cfc8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -263,7 +263,7 @@ module.exports = function(grunt) { this.registerTask('amd', ['babel:amd', 'requirejs']); - this.registerTask('test', ['test:bin', 'test:cov', 'test:check-cov']); + this.registerTask('test', ['test:bin', 'test:cov']); grunt.registerTask('bench', ['metrics']); diff --git a/nyc.config.js b/nyc.config.js new file mode 100644 index 00000000..f516eb2b --- /dev/null +++ b/nyc.config.js @@ -0,0 +1,9 @@ +module.exports = { + 'check-coverage': true, + branches: 100, + lines: 100, + functions: 100, + statements: 100, + exclude: ['**/spec/**', '**/handlebars/compiler/parser.js'], + reporter: 'html' +}; diff --git a/package-lock.json b/package-lock.json index 79fc1b9c..9c79a3d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,67 @@ "@babel/highlight": "^7.0.0" } }, + "@babel/generator": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", + "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", + "dev": true, + "requires": { + "@babel/types": "^7.7.4", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", + "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/types": "^7.7.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", + "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", + "dev": true, + "requires": { + "@babel/types": "^7.7.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", + "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", + "dev": true, + "requires": { + "@babel/types": "^7.7.4" + } + }, "@babel/highlight": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", @@ -61,6 +122,12 @@ } } }, + "@babel/parser": { + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", + "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", + "dev": true + }, "@babel/runtime": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz", @@ -70,6 +137,88 @@ "regenerator-runtime": "^0.13.2" } }, + "@babel/template": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", + "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4" + } + }, + "@babel/traverse": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", + "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.7.4", + "@babel/helper-function-name": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", + "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, "@knappi/grunt-saucelabs": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/@knappi/grunt-saucelabs/-/grunt-saucelabs-9.0.2.tgz", @@ -393,6 +542,21 @@ "normalize-path": "^2.0.0" } }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "dev": true, + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -676,6 +840,15 @@ "try-resolve": "^1.0.0" }, "dependencies": { + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "dev": true, + "requires": { + "brace-expansion": "^1.0.0" + } + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -1120,6 +1293,18 @@ } } }, + "caching-transform": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", + "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", + "dev": true, + "requires": { + "hasha": "^3.0.0", + "make-dir": "^2.0.0", + "package-hash": "^3.0.0", + "write-file-atomic": "^2.4.2" + } + }, "caller-callsite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", @@ -1462,6 +1647,12 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, "commoner": { "version": "0.10.8", "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz", @@ -1884,6 +2075,27 @@ } } }, + "cp-file": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", + "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "make-dir": "^2.0.0", + "nested-error-stacks": "^2.0.0", + "pify": "^4.0.1", + "safe-buffer": "^5.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -1991,6 +2203,23 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "dev": true, + "requires": { + "strip-bom": "^3.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -2489,6 +2718,12 @@ "is-arrayish": "^0.2.1" } }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -2516,73 +2751,6 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "escodegen": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.7.1.tgz", - "integrity": "sha1-MOz89mypjcZ80v0WKr626vqM5vw=", - "dev": true, - "requires": { - "esprima": "^1.2.2", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.5.0", - "source-map": "~0.2.0" - }, - "dependencies": { - "esprima": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz", - "integrity": "sha1-CZNQL+r2aBODJXVvMPmlH+7sEek=", - "dev": true - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - }, - "fast-levenshtein": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz", - "integrity": "sha1-AXjc3uAjuSkFGTrwlZ6KdjnP3Lk=", - "dev": true - }, - "levn": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz", - "integrity": "sha1-uo0znQykphDjo/FFucr0iAcVUFQ=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.0", - "type-check": "~0.3.1" - } - }, - "optionator": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.5.0.tgz", - "integrity": "sha1-t1qJlaLUF98ltuTjhi9QqohlE2g=", - "dev": true, - "requires": { - "deep-is": "~0.1.2", - "fast-levenshtein": "~1.0.0", - "levn": "~0.2.5", - "prelude-ls": "~1.1.1", - "type-check": "~0.3.1", - "wordwrap": "~0.0.2" - } - }, - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, "eslint": { "version": "6.7.2", "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.7.2.tgz", @@ -3304,16 +3472,6 @@ "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", "dev": true }, - "fileset": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-0.2.1.tgz", - "integrity": "sha1-WI74lzxmI7KnbfRlEFaWuWqsgGc=", - "dev": true, - "requires": { - "glob": "5.x", - "minimatch": "2.x" - } - }, "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", @@ -3342,6 +3500,37 @@ "unpipe": "~1.0.0" } }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", @@ -3449,6 +3638,28 @@ "for-in": "^1.0.1" } }, + "foreground-child": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", + "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "dev": true, + "requires": { + "cross-spawn": "^4", + "signal-exit": "^3.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + } + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -4268,9 +4479,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, "minimatch": { @@ -4297,9 +4508,9 @@ "dev": true }, "grunt": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.3.tgz", - "integrity": "sha512-/JzmZNPfKorlCrrmxWqQO4JVodO+DVd5XX4DkocL/1WlLlKVLE9+SdEIempOAxDhWPysLle6afvn/hg7Ck2k9g==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.4.tgz", + "integrity": "sha512-PYsMOrOC+MsdGEkFVwMaMyc6Ob7pKmq+deg1Sjr+vvMWp35sztfwKE7qoN51V+UEtHsyNuMcGdgMLFkBHvMxHQ==", "dev": true, "requires": { "coffeescript": "~1.10.0", @@ -4313,7 +4524,7 @@ "grunt-legacy-log": "~2.0.0", "grunt-legacy-util": "~1.1.1", "iconv-lite": "~0.4.13", - "js-yaml": "~3.5.2", + "js-yaml": "~3.13.0", "minimatch": "~3.0.2", "mkdirp": "~0.5.1", "nopt": "~3.0.6", @@ -4353,15 +4564,6 @@ "resolve": "~1.1.0" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", @@ -4494,9 +4696,9 @@ "dev": true }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, "source-map": { @@ -4559,9 +4761,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true } } @@ -4585,9 +4787,9 @@ }, "dependencies": { "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true } } @@ -4623,9 +4825,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, "supports-color": { @@ -4655,9 +4857,9 @@ }, "dependencies": { "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true } } @@ -4672,9 +4874,9 @@ }, "dependencies": { "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true } } @@ -4689,35 +4891,6 @@ "concat-stream": "^1.4.1" } }, - "handlebars": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz", - "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==", - "dev": true, - "requires": { - "async": "^2.5.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - }, - "dependencies": { - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - } - } - }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -4835,6 +5008,15 @@ } } }, + "hasha": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", + "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "dev": true, + "requires": { + "is-stream": "^1.0.1" + } + }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -4937,9 +5119,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true } } @@ -4968,9 +5150,9 @@ "dev": true }, "https-proxy-agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", - "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { "agent-base": "^4.3.0", @@ -5647,69 +5829,119 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, - "istanbul": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.3.22.tgz", - "integrity": "sha1-PhZNhQIf4ZyYXR8OfvDD4i0BLrY=", + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", + "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", "dev": true, "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.7.x", - "esprima": "2.5.x", - "fileset": "0.2.x", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" }, "dependencies": { - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true - }, - "esprima": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.5.0.tgz", - "integrity": "sha1-84ekb9NEwbGjm6+MIL+0O20AWMw=", - "dev": true - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" } }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } }, + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "dev": true, + "requires": { + "handlebars": "^4.1.2" + }, + "dependencies": { + "handlebars": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + } + } + } + }, "jison": { "version": "0.3.12", "resolved": "https://registry.npmjs.org/jison/-/jison-0.3.12.tgz", @@ -5734,19 +5966,19 @@ "dev": true }, "js-yaml": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz", - "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { - "argparse": "^1.0.2", - "esprima": "^2.6.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, "dependencies": { "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true } } @@ -6608,6 +6840,12 @@ "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -6714,6 +6952,34 @@ "signal-exit": "^3.0.0" } }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", @@ -6861,6 +7127,15 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -6928,12 +7203,12 @@ "dev": true }, "minimatch": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", - "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.0.0" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -6942,9 +7217,9 @@ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -7147,6 +7422,12 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==" }, + "nested-error-stacks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", + "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", + "dev": true + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -7303,6 +7584,204 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "nyc": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", + "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "caching-transform": "^3.0.2", + "convert-source-map": "^1.6.0", + "cp-file": "^6.2.0", + "find-cache-dir": "^2.1.0", + "find-up": "^3.0.0", + "foreground-child": "^1.5.6", + "glob": "^7.1.3", + "istanbul-lib-coverage": "^2.0.5", + "istanbul-lib-hook": "^2.0.7", + "istanbul-lib-instrument": "^3.3.0", + "istanbul-lib-report": "^2.0.8", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^2.2.4", + "js-yaml": "^3.13.1", + "make-dir": "^2.1.0", + "merge-source-map": "^1.1.0", + "resolve-from": "^4.0.0", + "rimraf": "^2.6.3", + "signal-exit": "^3.0.2", + "spawn-wrap": "^1.4.2", + "test-exclude": "^5.2.3", + "uuid": "^3.3.2", + "yargs": "^13.2.2", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -7633,6 +8112,18 @@ "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=", "dev": true }, + "package-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", + "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^3.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, "pako": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", @@ -7948,6 +8439,12 @@ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", "dev": true }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", @@ -8510,6 +9007,15 @@ "jsesc": "~0.5.0" } }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -8858,9 +9364,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -9205,6 +9711,20 @@ "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", "dev": true }, + "spawn-wrap": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", + "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", + "dev": true, + "requires": { + "foreground-child": "^1.5.6", + "mkdirp": "^0.5.0", + "os-homedir": "^1.0.1", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.2", + "which": "^1.3.0" + } + }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", @@ -9532,6 +10052,122 @@ "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", "dev": true }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -9916,38 +10552,15 @@ } }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "universalify": { @@ -10453,6 +11066,17 @@ "mkdirp": "^0.5.1" } }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, "xml2js": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", @@ -10481,6 +11105,12 @@ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, "yargs": { "version": "3.27.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.27.0.tgz", diff --git a/package.json b/package.json index 9ce188bb..6c8ca555 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "eslint-config-prettier": "^6.7.0", "eslint-plugin-compat": "^3.3.0", "eslint-plugin-es5": "^1.4.1", - "grunt": "^1.0.3", + "grunt": "^1.0.4", "grunt-babel": "^5.0.0", "grunt-bg-shell": "^2.3.3", "grunt-cli": "^1", @@ -57,12 +57,12 @@ "grunt-contrib-watch": "^1.1.0", "grunt-webpack": "^1.0.8", "husky": "^3.1.0", - "istanbul": "^0.3.0", "jison": "~0.3.0", "lint-staged": "^9.5.0", "mocha": "^5", "mock-stdin": "^0.3.0", "mustache": "^2.1.3", + "nyc": "^14.1.1", "prettier": "^1.19.1", "semver": "^5.0.1", "sinon": "^7.5.0", diff --git a/spec/builtins.js b/spec/builtins.js index d6bc2986..c74092e1 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -710,6 +710,23 @@ describe('builtin helpers', function() { shouldCompileTo(string, hash, ''); equals(true, called); }); + + it('should pass zero log arguments', function() { + var string = '{{log}}'; + var hash = { blah: 'whee' }; + var called; + + console.info = console.log = function() { + expect(arguments.length).to.equal(0); + called = true; + console.log = $log; + }; + + expectTemplate(string) + .withInput(hash) + .toCompileTo(''); + expect(called).to.be.true(); + }); /* eslint-enable no-console */ }); diff --git a/tasks/test-mocha.js b/tasks/test-mocha.js index 445a88cf..b412c8c0 100644 --- a/tasks/test-mocha.js +++ b/tasks/test-mocha.js @@ -1,5 +1,6 @@ const { execNodeJsScriptWithInheritedOutput } = require('./util/exec-file'); const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task'); +const nodeJs = process.argv0; module.exports = function(grunt) { const registerAsyncTask = createRegisterAsyncTaskFn(grunt); @@ -9,10 +10,8 @@ module.exports = function(grunt) { ); registerAsyncTask('test:cov', async () => - execNodeJsScriptWithInheritedOutput('node_modules/istanbul/lib/cli.js', [ - 'cover', - '--source-map', - '--', + execNodeJsScriptWithInheritedOutput('node_modules/nyc/bin/nyc', [ + nodeJs, './spec/env/runner.js' ]) ); @@ -20,17 +19,4 @@ module.exports = function(grunt) { registerAsyncTask('test:min', async () => execNodeJsScriptWithInheritedOutput('./spec/env/runner', ['--min']) ); - - registerAsyncTask('test:check-cov', async () => - execNodeJsScriptWithInheritedOutput('node_modules/istanbul/lib/cli.js', [ - 'check-coverage', - '--statements', - '100', - '--functions', - '100', - '--branches', - '100', - '--lines 100' - ]) - ); }; From 1ec1737d24a1a37e39ca4c24b0fdb7efd877e628 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Sat, 14 Dec 2019 01:49:55 +0100 Subject: [PATCH 5/8] test/style: refactor remaining grunt tasks to use promises instead of callbacks --- tasks/metrics.js | 37 ++++++----- tasks/publish.js | 158 ++++++++++++++++++++++---------------------- tasks/util/git.js | 163 +++++++++++++++++----------------------------- tasks/version.js | 108 +++++++++++++++--------------- 4 files changed, 209 insertions(+), 257 deletions(-) diff --git a/tasks/metrics.js b/tasks/metrics.js index bcb76318..acc3cece 100644 --- a/tasks/metrics.js +++ b/tasks/metrics.js @@ -1,26 +1,29 @@ -const _ = require('underscore'), - async = require('neo-async'), - metrics = require('../bench'); +const metrics = require('../bench'); +const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task'); module.exports = function(grunt) { - grunt.registerTask('metrics', function() { - const done = this.async(), - execName = grunt.option('name'), - events = {}; + const registerAsyncTask = createRegisterAsyncTaskFn(grunt); - async.each( - _.keys(metrics), - function(name, complete) { - if (/^_/.test(name) || (execName && name !== execName)) { - return complete(); - } + registerAsyncTask('metrics', function() { + const onlyExecuteName = grunt.option('name'); + const events = {}; + const promises = Object.keys(metrics).map(async name => { + if (/^_/.test(name)) { + return; + } + if (onlyExecuteName != null && name !== onlyExecuteName) { + return; + } + + return new Promise(resolve => { metrics[name](grunt, function(data) { events[name] = data; - complete(); + resolve(); }); - }, - done - ); + }); + }); + + return Promise.all(promises); }); }; diff --git a/tasks/publish.js b/tasks/publish.js index 207e281f..54c99f5c 100644 --- a/tasks/publish.js +++ b/tasks/publish.js @@ -1,54 +1,38 @@ -const _ = require('underscore'), - async = require('neo-async'), - AWS = require('aws-sdk'), - git = require('./util/git'), - semver = require('semver'); +const AWS = require('aws-sdk'); +const git = require('./util/git'); +const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task'); +const semver = require('semver'); module.exports = function(grunt) { - grunt.registerTask('publish:latest', function() { - const done = this.async(); + const registerAsyncTask = createRegisterAsyncTaskFn(grunt); - git.debug(function(remotes, branches) { - grunt.log.writeln('remotes: ' + remotes); - grunt.log.writeln('branches: ' + branches); + registerAsyncTask('publish:latest', async () => { + grunt.log.writeln('remotes: ' + (await git.remotes())); + grunt.log.writeln('branches: ' + (await git.branches())); - git.commitInfo(function(err, info) { - grunt.log.writeln('tag: ' + info.tagName); + const commitInfo = await git.commitInfo(); + grunt.log.writeln('tag: ' + commitInfo.tagName); - const files = []; + const suffixes = []; - // Publish the master as "latest" and with the commit-id - if (info.isMaster) { - files.push('-latest'); - files.push('-' + info.head); - } + // Publish the master as "latest" and with the commit-id + if (commitInfo.isMaster) { + suffixes.push('-latest'); + suffixes.push('-' + commitInfo.headSha); + } - // Publish tags by their tag-name - if (info.tagName && semver.valid(info.tagName)) { - files.push('-' + info.tagName); - } + // Publish tags by their tag-name + if (commitInfo.tagName && semver.valid(commitInfo.tagName)) { + suffixes.push('-' + commitInfo.tagName); + } - if (files.length > 0) { - initSDK(); - grunt.log.writeln('publishing files: ' + JSON.stringify(files)); - publish(fileMap(files), done); - } else { - // Silently ignore for branches - done(); - } - }); - }); - }); - grunt.registerTask('publish:version', function() { - const done = this.async(); - initSDK(); - - git.commitInfo(function(err, info) { - if (!info.tagName) { - throw new Error('The current commit must be tagged'); - } - publish(fileMap(['-' + info.tagName]), done); - }); + if (suffixes.length > 0) { + initSDK(); + grunt.log.writeln( + 'publishing file-suffixes: ' + JSON.stringify(suffixes) + ); + await publish(suffixes); + } }); function initSDK() { @@ -62,45 +46,57 @@ module.exports = function(grunt) { AWS.config.update({ accessKeyId: key, secretAccessKey: secret }); } - function publish(files, callback) { - const s3 = new AWS.S3(), - bucket = process.env.S3_BUCKET_NAME; - async.each( - _.keys(files), - function(file, callback) { - const params = { - Bucket: bucket, - Key: file, - Body: grunt.file.read(files[file]) - }; - s3.putObject(params, function(err) { - if (err) { - throw err; - } else { - grunt.log.writeln('Published ' + file + ' to build server.'); - callback(); - } - }); - }, - callback - ); + async function publish(suffixes) { + const publishPromises = suffixes.map(suffix => publishSuffix(suffix)); + return Promise.all(publishPromises); } - function fileMap(suffixes) { - const map = {}; - _.each( - [ - 'handlebars.js', - 'handlebars.min.js', - 'handlebars.runtime.js', - 'handlebars.runtime.min.js' - ], - function(file) { - _.each(suffixes, function(suffix) { - map[file.replace(/\.js$/, suffix + '.js')] = 'dist/' + file; - }); - } - ); - return map; + + async function publishSuffix(suffix) { + const filenames = [ + 'handlebars.js', + 'handlebars.min.js', + 'handlebars.runtime.js', + 'handlebars.runtime.min.js' + ]; + const publishPromises = filenames.map(filename => { + const nameInBucket = getNameInBucket(filename, suffix); + const localFile = getLocalFile(filename); + uploadToBucket(localFile, nameInBucket); + grunt.log.writeln( + `Published ${localFile} to build server (${nameInBucket})` + ); + }); + return Promise.all(publishPromises); + } + + async function uploadToBucket(localFile, nameInBucket) { + const bucket = process.env.S3_BUCKET_NAME; + const uploadParams = { + Bucket: bucket, + Key: nameInBucket, + Body: grunt.file.read(localFile) + }; + return s3PutObject(uploadParams); } }; + +function s3PutObject(uploadParams) { + const s3 = new AWS.S3(); + return new Promise((resolve, reject) => { + s3.putObject(uploadParams, err => { + if (err != null) { + return reject(err); + } + resolve(); + }); + }); +} + +function getNameInBucket(filename, suffix) { + return filename.replace(/\.js$/, suffix + '.js'); +} + +function getLocalFile(filename) { + return 'dist/' + filename; +} diff --git a/tasks/util/git.js b/tasks/util/git.js index 4dc5cbde..a86fcbb6 100644 --- a/tasks/util/git.js +++ b/tasks/util/git.js @@ -1,115 +1,74 @@ const childProcess = require('child_process'); module.exports = { - debug: function(callback) { - childProcess.exec('git remote -v', {}, function(err, remotes) { - if (err) { - throw new Error('git.remote: ' + err.message); + async remotes() { + return git('remotes', '-v'); + }, + async branches() { + return git('branch', '-a'); + }, + async clean() { + const stdout = git('diff-index', '--name-only', 'HEAD', '--'); + return stdout === ''; + }, + async commitInfo() { + const headSha = await this.headSha(); + const masterSha = await this.masterSha(); + return { + headSha, + masterSha, + tagName: await this.tagName(), + isMaster: headSha === masterSha + }; + }, + async headSha() { + const stdout = await git(' rev-parse', '--short', 'HEAD'); + return stdout.trim(); + }, + async masterSha() { + try { + const stdout = await git('rev-parse', '--short', 'origin/master'); + return stdout.trim(); + } catch (error) { + if (/Needed a single revision/.test(error.message)) { + // Master was not checked out but in this case, so we know we are not master. We can ignore this + return ''; } - - childProcess.exec('git branch -a', {}, function(err, branches) { - if (err) { - throw new Error('git.branch: ' + err.message); - } - - callback(remotes, branches); - }); - }); - }, - clean: function(callback) { - childProcess.exec('git diff-index --name-only HEAD --', {}, function( - err, - stdout - ) { - callback(undefined, !err && !stdout); - }); + throw error; + } }, - commitInfo: function(callback) { - module.exports.head(function(err, headSha) { - module.exports.master(function(err, masterSha) { - module.exports.tagName(function(err, tagName) { - callback(undefined, { - head: headSha, - master: masterSha, - tagName: tagName, - isMaster: headSha === masterSha - }); - }); - }); - }); + async add(path) { + return git('add', '-f', path); }, - - head: function(callback) { - childProcess.exec('git rev-parse --short HEAD', {}, function(err, stdout) { - if (err) { - throw new Error('git.head: ' + err.message); - } - - callback(undefined, stdout.trim()); - }); + async commit(message) { + return git('commit', '--message', message); }, - master: function(callback) { - childProcess.exec('git rev-parse --short origin/master', {}, function( - err, - stdout - ) { - // This will error if master was not checked out but in this case we know we are not master - // so we can ignore. - if (err && !/Needed a single revision/.test(err.message)) { - throw new Error('git.master: ' + err.message); - } - - callback(undefined, stdout.trim()); - }); + async tag(name) { + return git('tag', '-a', `--message=${name}`, name); }, + async tagName() { + const stdout = await git('tag', '-l', '--points-at', 'HEAD'); - add: function(path, callback) { - childProcess.exec('git add -f ' + path, {}, function(err) { - if (err) { - throw new Error('git.add: ' + err.message); - } + const tags = stdout.trim().split(/\n|\r\n/); + const versionTags = tags.filter(tag => /^v/.test(tag)); - callback(); - }); - }, - commit: function(name, callback) { - childProcess.exec('git commit --message=' + name, {}, function(err) { - if (err) { - throw new Error('git.commit: ' + err.message); - } - - callback(); - }); - }, - tag: function(name, callback) { - childProcess.exec('git tag -a --message=' + name + ' ' + name, {}, function( - err - ) { - if (err) { - throw new Error('git.tag: ' + err.message); - } - - callback(); - }); - }, - tagName: function(callback) { - childProcess.exec('git describe --tags', {}, function(err, stdout) { - if (err) { - throw new Error('git.tagName: ' + err.message); - } - - let tags = stdout.trim().split(/\n/); - tags = tags.filter(function(info) { - info = info.split('-'); - return info.length == 1; - }); - - const versionTags = tags.filter(function(info) { - return /^v/.test(info[0]); - }); - - callback(undefined, versionTags[0] || tags[0]); - }); + if (versionTags[0] != null) { + return versionTags; + } + return tags[0]; } }; + +async function git(...args) { + return new Promise((resolve, reject) => + childProcess.execFile('git', args, (err, stdout) => { + if (err != null) { + return reject( + new Error(`"git ${args.join(' ')}" caused error: ${err.message}`) + ); + } + resolve(stdout); + }) + ); +} diff --git a/tasks/version.js b/tasks/version.js index f9d5c567..ba89d1fd 100644 --- a/tasks/version.js +++ b/tasks/version.js @@ -1,68 +1,62 @@ -const async = require('neo-async'), - git = require('./util/git'), - semver = require('semver'); +const git = require('./util/git'); +const semver = require('semver'); +const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task'); module.exports = function(grunt) { - grunt.registerTask( - 'version', - 'Updates the current release version', - function() { - const done = this.async(), - pkg = grunt.config('pkg'), - version = grunt.option('ver'); + const registerAsyncTask = createRegisterAsyncTaskFn(grunt); - if (!semver.valid(version)) { - throw new Error( - 'Must provide a version number (Ex: --ver=1.0.0):\n\t' + - version + - '\n\n' - ); - } - - pkg.version = version; - grunt.config('pkg', pkg); - - grunt.log.writeln('Updating to version ' + version); - - async.each( - [ - [ - 'lib/handlebars/base.js', - /const VERSION = ['"](.*)['"];/, - "const VERSION = '" + version + "';" - ], - [ - 'components/bower.json', - /"version":.*/, - '"version": "' + version + '",' - ], - [ - 'components/package.json', - /"version":.*/, - '"version": "' + version + '",' - ], - [ - 'components/handlebars.js.nuspec', - /.*<\/version>/, - '' + version + '' - ] - ], - function(args, callback) { - replace.apply(undefined, args); - grunt.log.writeln(' - ' + args[0]); - git.add(args[0], callback); - }, - function() { - grunt.task.run(['default']); - done(); - } + registerAsyncTask('version', async () => { + const pkg = grunt.config('pkg'); + const version = grunt.option('ver'); + if (!semver.valid(version)) { + throw new Error( + 'Must provide a version number (Ex: --ver=1.0.0):\n\t' + + version + + '\n\n' ); } - ); + pkg.version = version; + grunt.config('pkg', pkg); - function replace(path, regex, value) { + const replaceSpec = [ + { + path: 'lib/handlebars/base.js', + regex: /const VERSION = ['"](.*)['"];/, + replacement: `const VERSION = '${version}';` + }, + { + path: 'components/bower.json', + regex: /"version":.*/, + replacement: `"version": "${version}",` + }, + { + path: 'components/package.json', + regex: /"version":.*/, + replacement: `"version": "${version}",` + }, + { + path: 'components/handlebars.js.nuspec', + regex: /.*<\/version>/, + replacement: `${version}` + } + ]; + + await Promise.all( + replaceSpec.map(replaceSpec => + replaceAndAdd( + replaceSpec.path, + replaceSpec.regex, + replaceSpec.replacement + ) + ) + ); + grunt.task.run(['default']); + }); + + async function replaceAndAdd(path, regex, value) { let content = grunt.file.read(path); content = content.replace(regex, value); grunt.file.write(path, content); + await git.add(path); } }; From 14b621caf5bcdcbf7476b3286afbc30380d2cc3f Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Sat, 14 Dec 2019 17:39:39 +0100 Subject: [PATCH 6/8] test/style: remove or hide unused code in git.js, add tests --- .eslintrc.js | 2 +- Gruntfile.js | 2 +- package-lock.json | 31 +++++- package.json | 3 +- tasks/{publish.js => publish-to-aws.js} | 6 +- tasks/task-tests/.eslintrc.js | 9 ++ tasks/task-tests/README.md | 1 + tasks/task-tests/git.test.js | 121 ++++++++++++++++++++++++ tasks/task-tests/mocha.opts | 0 tasks/util/git.js | 77 ++++++++------- 10 files changed, 202 insertions(+), 50 deletions(-) rename tasks/{publish.js => publish-to-aws.js} (93%) create mode 100644 tasks/task-tests/.eslintrc.js create mode 100644 tasks/task-tests/README.md create mode 100644 tasks/task-tests/git.test.js create mode 100644 tasks/task-tests/mocha.opts diff --git a/.eslintrc.js b/.eslintrc.js index 0abcd580..65c24093 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,7 +17,7 @@ module.exports = { // Best Practices // //----------------// 'default-case': 'warn', - 'dot-notation': ['error', { allowKeywords: false }], + 'dot-notation': 'warn', 'guard-for-in': 'warn', 'no-alert': 'error', 'no-caller': 'error', diff --git a/Gruntfile.js b/Gruntfile.js index 0326cfc8..65e0b8f3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -279,7 +279,7 @@ module.exports = function(grunt) { 'bgShell:integrationTests', 'sauce', 'metrics', - 'publish:latest' + 'publish-to-aws' ]); grunt.registerTask('on-file-change', [ 'build', diff --git a/package-lock.json b/package-lock.json index 9c79a3d7..5d119122 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2594,6 +2594,19 @@ "strip-json-comments": "^2.0.1", "tslint": "^5.14.0", "typescript": "^3.4.3" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } } }, "dustjs-linkedin": { @@ -3699,14 +3712,22 @@ "dev": true }, "fs-extra": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", - "integrity": "sha1-irwSj3lG4xATXdyTuYvdtBDno0s=", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", + "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + } } }, "fs-readdir-recursive": { @@ -10566,7 +10587,7 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, "unpipe": { diff --git a/package.json b/package.json index 6c8ca555..19f096a5 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "eslint-config-prettier": "^6.7.0", "eslint-plugin-compat": "^3.3.0", "eslint-plugin-es5": "^1.4.1", + "fs-extra": "^8.1.0", "grunt": "^1.0.4", "grunt-babel": "^5.0.0", "grunt-bg-shell": "^2.3.3", @@ -86,7 +87,7 @@ "lint": "eslint --max-warnings 0 . ", "dtslint": "dtslint types", "test": "grunt", - "extensive-tests-and-publish-to-aws": "grunt --stack extensive-tests-and-publish-to-aws", + "extensive-tests-and-publish-to-aws": "npx mocha tasks/task-tests/ && grunt --stack extensive-tests-and-publish-to-aws", "integration-test": "grunt integration-tests", "--- combined tasks ---": "", "check-before-pull-request": "concurrently --kill-others-on-fail npm:lint npm:dtslint npm:check-format npm:test" diff --git a/tasks/publish.js b/tasks/publish-to-aws.js similarity index 93% rename from tasks/publish.js rename to tasks/publish-to-aws.js index 54c99f5c..b551e1c9 100644 --- a/tasks/publish.js +++ b/tasks/publish-to-aws.js @@ -6,12 +6,12 @@ const semver = require('semver'); module.exports = function(grunt) { const registerAsyncTask = createRegisterAsyncTaskFn(grunt); - registerAsyncTask('publish:latest', async () => { + registerAsyncTask('publish-to-aws', async () => { grunt.log.writeln('remotes: ' + (await git.remotes())); grunt.log.writeln('branches: ' + (await git.branches())); const commitInfo = await git.commitInfo(); - grunt.log.writeln('tag: ' + commitInfo.tagName); + grunt.log.writeln('tag: ', commitInfo.tagName); const suffixes = []; @@ -22,7 +22,7 @@ module.exports = function(grunt) { } // Publish tags by their tag-name - if (commitInfo.tagName && semver.valid(commitInfo.tagName)) { + if (commitInfo.tagName != null && semver.valid(commitInfo.tagName)) { suffixes.push('-' + commitInfo.tagName); } diff --git a/tasks/task-tests/.eslintrc.js b/tasks/task-tests/.eslintrc.js new file mode 100644 index 00000000..604aeb2c --- /dev/null +++ b/tasks/task-tests/.eslintrc.js @@ -0,0 +1,9 @@ +module.exports = { + extends: '../../.eslintrc.js', + env: { + mocha: true + }, + parserOptions: { + ecmaVersion: 2018 + } +}; diff --git a/tasks/task-tests/README.md b/tasks/task-tests/README.md new file mode 100644 index 00000000..f4a88806 --- /dev/null +++ b/tasks/task-tests/README.md @@ -0,0 +1 @@ +Use `mocha tasks/task-tests` to run these tests diff --git a/tasks/task-tests/git.test.js b/tasks/task-tests/git.test.js new file mode 100644 index 00000000..89546a8b --- /dev/null +++ b/tasks/task-tests/git.test.js @@ -0,0 +1,121 @@ +const os = require('os'); +const path = require('path'); +const fs = require('fs-extra'); +const chai = require('chai'); +chai.use(require('dirty-chai')); + +const git = require('../util/git'); + +const expect = chai.expect; + +const tmpBaseDir = path.join(os.tmpdir(), 'handlebars-task-tests'); +const tmpDir = path.join(tmpBaseDir, Date.now().toString(36)); +const remoteDir = path.join(tmpDir, 'remote-repo'); +const cloneDir = path.join(tmpDir, 'clone-repo'); +const oldCwd = process.cwd(); + +describe('utils/git', function() { + beforeEach(async function() { + await fs.remove(tmpDir); + await createRepositoryThatActsAsRemote(); + process.chdir(tmpDir); + await git.git('clone', 'remote-repo', 'clone-repo'); + process.chdir(cloneDir); + }); + + async function createRepositoryThatActsAsRemote() { + await fs.mkdirp(remoteDir); + process.chdir(remoteDir); + + await git.git('init'); + await fs.writeFile('testfile.txt', 'Testfile'); + await git.add('testfile.txt'); + await git.commit('commit message'); + } + + afterEach(function() { + process.chdir(oldCwd); + }); + + describe('the "remotes"-function', function() { + it('should list all remotes', async function() { + await git.git('remote', 'set-url', 'origin', 'https://test.org/test'); + await git.git('remote', 'add', 'second-remote', 'https://test.org/test2'); + + const result = await git.remotes(); + + expect(result.trim().split('\n')).to.deep.equal([ + 'origin\thttps://test.org/test (fetch)', + 'origin\thttps://test.org/test (push)', + 'second-remote\thttps://test.org/test2 (fetch)', + 'second-remote\thttps://test.org/test2 (push)' + ]); + }); + }); + + describe('the "branches"-function', function() { + it('should list all branches', async function() { + await git.git('branch', 'test'); + await git.git('branch', 'test2'); + + const result = await git.branches(); + expect(result.trim().split('\n')).to.deep.equal([ + '* master', + ' test', + ' test2', + ' remotes/origin/HEAD -> origin/master', + ' remotes/origin/master' + ]); + }); + }); + + describe('the "commitInfo"-function', function() { + it('should list head and master sha', async function() { + const result = await git.commitInfo(); + expect(result.masterSha).to.equal(result.headSha); + expect(result.masterSha).to.match(/^[0-9a-f]+$/); + expect(result.headSha).to.match(/^[0-9a-f]+$/); + }); + + it('should have "isMaster=true" if the master branch is checked out', async function() { + const result = await git.commitInfo(); + expect(result.isMaster).to.be.true(); + }); + + it('should have "isMaster=true" if the current commit is the last commit of the master branch', async function() { + await git.git('checkout', '-b', 'new-branch'); + + const result = await git.commitInfo(); + expect(result.isMaster).to.be.true(); + }); + + it('should have "isMaster=false" if the current commit is NOT the last commit of the master branch', async function() { + await git.git('checkout', '-b', 'new-branch'); + fs.writeFile('new-file.txt', 'new-file'); + await git.add('new-file.txt'); + await git.commit('added new file'); + + const result = await git.commitInfo(); + expect(result.isMaster).to.be.false(); + }); + + it('should show the current tag', async function() { + await git.git('tag', 'test-tag'); + const result = await git.commitInfo(); + expect(result.tagName).to.be.equal('test-tag'); + }); + + it('should show a version tag rather than standard tags', async function() { + await git.git('tag', 'test-tag'); + await git.git('tag', 'v1.2'); + await git.git('tag', 'test-tag2'); + const result = await git.commitInfo(); + expect(result.tagName).to.be.equal('v1.2'); + }); + + it('should show no tag if there is no tag', async function() { + const result = await git.commitInfo(); + expect(result.tagName).to.be.null(); + }); + }); +}); diff --git a/tasks/task-tests/mocha.opts b/tasks/task-tests/mocha.opts new file mode 100644 index 00000000..e69de29b diff --git a/tasks/util/git.js b/tasks/util/git.js index a86fcbb6..46c9cbe7 100644 --- a/tasks/util/git.js +++ b/tasks/util/git.js @@ -2,64 +2,63 @@ const childProcess = require('child_process'); module.exports = { async remotes() { - return git('remotes', '-v'); + return git('remote', '-v'); }, async branches() { return git('branch', '-a'); }, - async clean() { - const stdout = git('diff-index', '--name-only', 'HEAD', '--'); - return stdout === ''; - }, async commitInfo() { - const headSha = await this.headSha(); - const masterSha = await this.masterSha(); + const headSha = await getHeadSha(); + const masterSha = await getMasterSha(); return { headSha, masterSha, - tagName: await this.tagName(), + tagName: await getTagName(), isMaster: headSha === masterSha }; }, - async headSha() { - const stdout = await git(' rev-parse', '--short', 'HEAD'); - return stdout.trim(); - }, - async masterSha() { - try { - const stdout = await git('rev-parse', '--short', 'origin/master'); - return stdout.trim(); - } catch (error) { - if (/Needed a single revision/.test(error.message)) { - // Master was not checked out but in this case, so we know we are not master. We can ignore this - return ''; - } - throw error; - } - }, - async add(path) { return git('add', '-f', path); }, async commit(message) { return git('commit', '--message', message); }, - async tag(name) { - return git('tag', '-a', `--message=${name}`, name); - }, - async tagName() { - const stdout = await git('tag', '-l', '--points-at', 'HEAD'); - - const tags = stdout.trim().split(/\n|\r\n/); - const versionTags = tags.filter(tag => /^v/.test(tag)); - - if (versionTags[0] != null) { - return versionTags; - } - return tags[0]; - } + git // visible for testing }; +async function getHeadSha() { + const stdout = await git('rev-parse', '--short', 'HEAD'); + return stdout.trim(); +} + +async function getMasterSha() { + try { + const stdout = await git('rev-parse', '--short', 'origin/master'); + return stdout.trim(); + } catch (error) { + if (/Needed a single revision/.test(error.message)) { + // Master was not checked out but in this case, so we know we are not master. We can ignore this + return ''; + } + throw error; + } +} + +async function getTagName() { + const stdout = await git('tag', '-l', '--points-at', 'HEAD'); + const trimmedStdout = stdout.trim(); + if (trimmedStdout === '') { + return null; // there is no tag + } + + const tags = trimmedStdout.split(/\n|\r\n/); + const versionTags = tags.filter(tag => /^v/.test(tag)); + if (versionTags[0] != null) { + return versionTags[0]; + } + return tags[0]; +} + async function git(...args) { return new Promise((resolve, reject) => childProcess.execFile('git', args, (err, stdout) => { From ac4655ee9516d5fc1b29d4d52e17c1751ffc8324 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Sat, 14 Dec 2019 17:48:08 +0100 Subject: [PATCH 7/8] chore: disable "dot-notation" rule - I don't think it makes much sense. In some cases, it is more readable to wrap property access in quotes, some times not, but there is no universal rule for that. - a promises "catch"-function should not be wrapped, but it has to be, if "allow-keywords" is set to false - an "if"-helper should be wrapped, but it is not allowed to be if "allow-keywords" is set to true (default). --- .eslintrc.js | 1 - 1 file changed, 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 65c24093..2ac72935 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,7 +17,6 @@ module.exports = { // Best Practices // //----------------// 'default-case': 'warn', - 'dot-notation': 'warn', 'guard-for-in': 'warn', 'no-alert': 'error', 'no-caller': 'error', From 164b7ff0de3041532b0ba671b3eefb7c0c5b5338 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Sat, 14 Dec 2019 17:49:42 +0100 Subject: [PATCH 8/8] chore: ignore .nyc_output --- .eslintignore | 1 + .gitignore | 1 + .prettierignore | 1 + 3 files changed, 3 insertions(+) diff --git a/.eslintignore b/.eslintignore index a6467301..714d8401 100644 --- a/.eslintignore +++ b/.eslintignore @@ -9,6 +9,7 @@ sauce_connect.log* yarn-error.log node_modules /handlebars-release.tgz +.nyc_output # Generated files lib/handlebars/compiler/parser.js diff --git a/.gitignore b/.gitignore index 8922abaf..59019b37 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ sauce_connect.log* /yarn.lock node_modules /handlebars-release.tgz +.nyc_output # Generated files lib/handlebars/compiler/parser.js diff --git a/.prettierignore b/.prettierignore index b6bc4621..72462066 100644 --- a/.prettierignore +++ b/.prettierignore @@ -9,6 +9,7 @@ sauce_connect.log* yarn-error.log node_modules /handlebars-release.tgz +.nyc_output # Generated files lib/handlebars/compiler/parser.js