diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 156c3e382..000000000 --- a/.eslintrc +++ /dev/null @@ -1,11 +0,0 @@ -root: true - -env: - node: true - es6: true - -extends: - "eslint:recommended" - -rules: - "no-bitwise": error diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..5d2f7439f --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "root": true, + "env": { + "node": true, + "es6": true + }, + "plugins": [ + "prettier" + ], + "extends": "eslint:recommended", + "rules": { + "prettier/prettier": [ + "error", + { + "singleQuote": true + } + ], + "no-extra-semi": 0, + "no-mixed-spaces-and-tabs": 0 + } +} diff --git a/lib/plugins/analysisstorer/index.js b/lib/plugins/analysisstorer/index.js index 20b5ec432..f4054f15e 100644 --- a/lib/plugins/analysisstorer/index.js +++ b/lib/plugins/analysisstorer/index.js @@ -6,7 +6,16 @@ const fs = require('fs'); Promise.promisifyAll(fs); function shouldIgnoreMessage(message) { - return ['url', 'error', 'summarize','browsertime.screenshot', 'browsertime.har', 'webpagetest.har'].indexOf(message.type) >= 0; + return ( + [ + 'url', + 'error', + 'summarize', + 'browsertime.screenshot', + 'browsertime.har', + 'webpagetest.har' + ].indexOf(message.type) >= 0 + ); } module.exports = { @@ -26,7 +35,11 @@ module.exports = { } if (message.url) { - return this.storageManager.writeDataForUrl(jsonData, fileName, message.url); + return this.storageManager.writeDataForUrl( + jsonData, + fileName, + message.url + ); } else { return this.storageManager.writeData(fileName, jsonData); } diff --git a/lib/plugins/assets/aggregator.js b/lib/plugins/assets/aggregator.js index 46cb6c4bc..a6cdefec7 100644 --- a/lib/plugins/assets/aggregator.js +++ b/lib/plugins/assets/aggregator.js @@ -13,7 +13,6 @@ module.exports = { largestThirdPartyAssetsByGroup: {}, slowestThirdPartyAssetsByGroup: {}, addToAggregate(data, group, url, resultUrls, runIndex, options) { - const maxSize = get(options, 'html.topListSize', 10); let page = resultUrls.relativeSummaryPageUrl(url); let runPage = page + runIndex; @@ -36,13 +35,17 @@ module.exports = { if (this.largestAssets[asset.type]) { this.largestAssets[asset.type].add(asset, page, runPage); } else { - this.largestAssets[asset.type] = new AssetsBySize(maxSize , asset, page); + this.largestAssets[asset.type] = new AssetsBySize(maxSize, asset, page); } if (this.largestAssetsByGroup[group][asset.type]) { this.largestAssetsByGroup[group][asset.type].add(asset, page, runPage); } else { - this.largestAssetsByGroup[group][asset.type] = new AssetsBySize(maxSize, asset, page); + this.largestAssetsByGroup[group][asset.type] = new AssetsBySize( + maxSize, + asset, + page + ); } this.slowestAssets.add(asset, page, runPage); @@ -88,7 +91,6 @@ module.exports = { urlInfoGroup.requestCount++; this.groups[group][url] = urlInfoGroup; } - }, summarize() { @@ -111,13 +113,17 @@ module.exports = { summary.size[group] = {}; summary.timing[group] = {}; for (let assetTypes of Object.keys(this.largestAssetsByGroup[group])) { - summary.size[group][assetTypes] = this.largestAssetsByGroup[group][assetTypes].getItems(); + summary.size[group][assetTypes] = this.largestAssetsByGroup[group][ + assetTypes + ].getItems(); } summary.timing[group] = this.slowestAssetsByGroup[group].getItems(); } for (let assetTypes of Object.keys(this.largestAssets)) { - summary.size.total[assetTypes] = this.largestAssets[assetTypes].getItems(); + summary.size.total[assetTypes] = this.largestAssets[ + assetTypes + ].getItems(); } summary.timing.total = this.slowestAssets.getItems(); diff --git a/lib/plugins/assets/assetsBySpeed.js b/lib/plugins/assets/assetsBySpeed.js index 1194da804..809c2b324 100644 --- a/lib/plugins/assets/assetsBySpeed.js +++ b/lib/plugins/assets/assetsBySpeed.js @@ -1,6 +1,6 @@ 'use strict'; -class AssetsBySpeed{ +class AssetsBySpeed { constructor(maxSize) { this.maxSize = maxSize; this.items = []; diff --git a/lib/plugins/assets/index.js b/lib/plugins/assets/index.js index 41ec1185d..38bc2184c 100644 --- a/lib/plugins/assets/index.js +++ b/lib/plugins/assets/index.js @@ -7,15 +7,16 @@ const aggregator = require('./aggregator'); const make = messageMaker('assets').make; -const DEFAULT_METRICS_LARGEST_ASSETS = [ - 'image.0.transferSize' -]; +const DEFAULT_METRICS_LARGEST_ASSETS = ['image.0.transferSize']; module.exports = { open(context, options) { this.options = options; this.resultUrls = context.resultUrls; - filterRegistry.registerFilterForType(DEFAULT_METRICS_LARGEST_ASSETS, 'largestassets.summary'); + filterRegistry.registerFilterForType( + DEFAULT_METRICS_LARGEST_ASSETS, + 'largestassets.summary' + ); filterRegistry.registerFilterForType([], 'slowestassets.summary'); filterRegistry.registerFilterForType([], 'aggregateassets.summary'); filterRegistry.registerFilterForType([], 'slowestthirdpartyassets.summary'); @@ -23,37 +24,64 @@ module.exports = { }, processMessage(message, queue) { switch (message.type) { - case 'pagexray.run': - { - aggregator.addToAggregate(message.data, message.group, message.url, this.resultUrls, message.runIndex, this.options); + case 'pagexray.run': { + aggregator.addToAggregate( + message.data, + message.group, + message.url, + this.resultUrls, + message.runIndex, + this.options + ); break; } - case 'summarize': - { + case 'summarize': { const summary = aggregator.summarize(); if (!isEmpty(summary)) { for (let group of Object.keys(summary.groups)) { - queue.postMessage(make('aggregateassets.summary', summary.groups[group], {group})); + queue.postMessage( + make('aggregateassets.summary', summary.groups[group], { group }) + ); for (let contentType of Object.keys(summary.size[group])) { const d = {}; d[contentType] = summary.size[group][contentType]; - queue.postMessage(make('largestassets.summary', d, {group})); + queue.postMessage(make('largestassets.summary', d, { group })); } - queue.postMessage(make('slowestassets.summary', summary.timing[group], {group})); + queue.postMessage( + make('slowestassets.summary', summary.timing[group], { group }) + ); } for (let contentType of Object.keys(summary.size.total)) { - queue.postMessage(make('largestassets.summary', summary.size.total[contentType], {group: 'total'})); + queue.postMessage( + make('largestassets.summary', summary.size.total[contentType], { + group: 'total' + }) + ); } - queue.postMessage(make('slowestassets.summary', summary.timing.total, {group: 'total'})); + queue.postMessage( + make('slowestassets.summary', summary.timing.total, { + group: 'total' + }) + ); if (this.options.firstParty) { - queue.postMessage(make('slowestthirdpartyassets.summary', summary.timing.thirdParty, {group: 'total'})); - queue.postMessage(make('largestthirdpartyassets.summary', summary.size.thirdParty, {group: 'total'})); + queue.postMessage( + make( + 'slowestthirdpartyassets.summary', + summary.timing.thirdParty, + { group: 'total' } + ) + ); + queue.postMessage( + make('largestthirdpartyassets.summary', summary.size.thirdParty, { + group: 'total' + }) + ); } } break; diff --git a/lib/plugins/browsertime/aggregator.js b/lib/plugins/browsertime/aggregator.js index 23e936bce..9245d7b99 100644 --- a/lib/plugins/browsertime/aggregator.js +++ b/lib/plugins/browsertime/aggregator.js @@ -10,44 +10,77 @@ module.exports = { groups: {}, addToAggregate(browsertimeRunData, group) { - if (this.groups[group] === undefined) { this.groups[group] = {}; } - forEach(timings, (timing) => { + forEach(timings, timing => { if (browsertimeRunData.timings[timing]) { - statsHelpers.pushGroupStats(this.statsPerType, this.groups[group], timing, browsertimeRunData.timings[timing]); + statsHelpers.pushGroupStats( + this.statsPerType, + this.groups[group], + timing, + browsertimeRunData.timings[timing] + ); } }); forEach(browsertimeRunData.timings.navigationTiming, (value, name) => { - statsHelpers.pushGroupStats(this.statsPerType, this.groups[group], ['navigationTiming', name], value); + statsHelpers.pushGroupStats( + this.statsPerType, + this.groups[group], + ['navigationTiming', name], + value + ); }); // pick up one level of custom metrics forEach(browsertimeRunData.custom, (value, name) => { - statsHelpers.pushGroupStats(this.statsPerType, this.groups[group], ['custom', name], value); + statsHelpers.pushGroupStats( + this.statsPerType, + this.groups[group], + ['custom', name], + value + ); }); forEach(browsertimeRunData.timings.pageTimings, (value, name) => { - statsHelpers.pushGroupStats(this.statsPerType, this.groups[group], ['pageTimings', name], value); + statsHelpers.pushGroupStats( + this.statsPerType, + this.groups[group], + ['pageTimings', name], + value + ); }); - forEach(browsertimeRunData.timings.userTimings.marks, (timing) => { - statsHelpers.pushGroupStats(this.statsPerType, this.groups[group], ['userTimings', 'marks', timing.name], timing.startTime); + forEach(browsertimeRunData.timings.userTimings.marks, timing => { + statsHelpers.pushGroupStats( + this.statsPerType, + this.groups[group], + ['userTimings', 'marks', timing.name], + timing.startTime + ); }); - forEach(browsertimeRunData.timings.userTimings.measures, (timing) => { - statsHelpers.pushGroupStats(this.statsPerType, this.groups[group], ['userTimings', 'measures', timing.name], timing.duration); + forEach(browsertimeRunData.timings.userTimings.measures, timing => { + statsHelpers.pushGroupStats( + this.statsPerType, + this.groups[group], + ['userTimings', 'measures', timing.name], + timing.duration + ); }); forEach(browsertimeRunData.visualMetrics, (value, name) => { if (name !== 'VisualProgress') { - statsHelpers.pushGroupStats(this.statsPerType, this.groups[group], ['visualMetrics', name], value); + statsHelpers.pushGroupStats( + this.statsPerType, + this.groups[group], + ['visualMetrics', name], + value + ); } }); - }, summarize() { if (Object.keys(this.statsPerType).length === 0) { @@ -69,23 +102,23 @@ module.exports = { summarizePerObject(obj) { return Object.keys(obj).reduce((summary, name) => { if (timings.indexOf(name) > -1) { - statsHelpers.setStatsSummary(summary, name, obj[name]) + statsHelpers.setStatsSummary(summary, name, obj[name]); } else if ('userTimings'.indexOf(name) > -1) { summary.userTimings = {}; const marksData = {}, measuresData = {}; forEach(obj.userTimings.marks, (stats, timingName) => { - statsHelpers.setStatsSummary(marksData, timingName, stats) + statsHelpers.setStatsSummary(marksData, timingName, stats); }); forEach(obj.userTimings.measures, (stats, timingName) => { - statsHelpers.setStatsSummary(measuresData, timingName, stats) + statsHelpers.setStatsSummary(measuresData, timingName, stats); }); summary.userTimings.marks = marksData; summary.userTimings.measures = measuresData; } else { const categoryData = {}; forEach(obj[name], (stats, timingName) => { - statsHelpers.setStatsSummary(categoryData, timingName, stats) + statsHelpers.setStatsSummary(categoryData, timingName, stats); }); summary[name] = categoryData; } diff --git a/lib/plugins/browsertime/analyzer.js b/lib/plugins/browsertime/analyzer.js index 1cd1d9ade..41851b98e 100644 --- a/lib/plugins/browsertime/analyzer.js +++ b/lib/plugins/browsertime/analyzer.js @@ -19,51 +19,64 @@ const chromeIphoneEmulationOptions = { } }; -const iphone6UserAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_3 like Mac OS X) AppleWebKit/536.26 ' + +const iphone6UserAgent = + 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_3 like Mac OS X) AppleWebKit/536.26 ' + '(KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25'; function parseUserScripts(scripts) { - if (!Array.isArray(scripts)) - scripts = [scripts]; + if (!Array.isArray(scripts)) scripts = [scripts]; - return Promise.reduce(scripts, (results, script) => - browserScripts.findAndParseScripts(path.resolve(script), 'custom') - .then((scripts) => merge(results, scripts)), - {}); + return Promise.reduce( + scripts, + (results, script) => + browserScripts + .findAndParseScripts(path.resolve(script), 'custom') + .then(scripts => merge(results, scripts)), + {} + ); } function addCoachScripts(scripts) { - return Promise.join(scripts, coach.getDomAdvice(), - (scripts, advice) => { - scripts.coach = { - coachAdvice: advice - }; - return scripts; - }); + return Promise.join(scripts, coach.getDomAdvice(), (scripts, advice) => { + scripts.coach = { + coachAdvice: advice + }; + return scripts; + }); } module.exports = { analyzeUrl(url, options) { const btOptions = merge({}, defaultBrowsertimeOptions, options); - merge(btOptions, {verbose: options.verbose}); + merge(btOptions, { verbose: options.verbose }); // set mobile options if (options.mobile) { btOptions.viewPort = '360x640'; if (btOptions.browser === 'chrome') { - btOptions.chrome = merge({}, btOptions.chrome, chromeIphoneEmulationOptions); + btOptions.chrome = merge( + {}, + btOptions.chrome, + chromeIphoneEmulationOptions + ); } else { btOptions.userAgent = iphone6UserAgent; } } const scriptCategories = browserScripts.allScriptCategories; - let scriptsByCategory = browserScripts.getScriptsForCategories(scriptCategories); + let scriptsByCategory = browserScripts.getScriptsForCategories( + scriptCategories + ); if (btOptions.script) { const userScripts = parseUserScripts(btOptions.script); - scriptsByCategory = Promise.join(scriptsByCategory, userScripts, - (scriptsByCategory, userScripts) => merge(scriptsByCategory, userScripts)); + scriptsByCategory = Promise.join( + scriptsByCategory, + userScripts, + (scriptsByCategory, userScripts) => + merge(scriptsByCategory, userScripts) + ); } if (btOptions.coach) { @@ -71,9 +84,15 @@ module.exports = { } let engine = new browsertime.Engine(btOptions); - log.info('Starting %s for analysing %s %s time(s)', btOptions.browser, url, btOptions.iterations); - return engine.start() + log.info( + 'Starting %s for analysing %s %s time(s)', + btOptions.browser, + url, + btOptions.iterations + ); + return engine + .start() .then(() => engine.run(url, scriptsByCategory)) - .finally(() => engine.stop()) + .finally(() => engine.stop()); } }; diff --git a/lib/plugins/browsertime/index.js b/lib/plugins/browsertime/index.js index 5ad395c9b..54df46d7c 100644 --- a/lib/plugins/browsertime/index.js +++ b/lib/plugins/browsertime/index.js @@ -35,7 +35,7 @@ const defaultConfig = { }, viewPort: '1366x708', delay: 0 -} +}; const DEFAULT_METRICS_PAGE_SUMMARY = [ 'statistics.timings.pageTimings', @@ -66,15 +66,16 @@ const DEFAULT_METRICS_SUMMARY = [ 'custom.*' ]; - function jsonifyVisualProgress(visualProgress) { // Original data looks like // "0=0%, 1500=81%, 1516=81%, 1533=84%, 1550=84%, 1566=84%, 1600=95%, 1683=95%, 1833=100%" const progress = visualProgress.split(','); const visualProgressJSON = {}; - forEach(progress, (value) => { - const eachMetric = value.split('='); - visualProgressJSON[eachMetric[0].replace(' ','')] = Number(eachMetric[1].replace('%','')); + forEach(progress, value => { + const eachMetric = value.split('='); + visualProgressJSON[eachMetric[0].replace(' ', '')] = Number( + eachMetric[1].replace('%', '') + ); }); return visualProgressJSON; } @@ -90,193 +91,256 @@ module.exports = { browsertime.logging.configure(options); // hack for disabling viewport on Android that's not supported - if (this.options.chrome && this.options.chrome.android && this.options.chrome.android.package) { + if ( + this.options.chrome && + this.options.chrome.android && + this.options.chrome.android.package + ) { this.options.viewPort = undefined; } - filterRegistry.registerFilterForType(DEFAULT_METRICS_PAGE_SUMMARY, 'browsertime.pageSummary'); - filterRegistry.registerFilterForType(DEFAULT_METRICS_SUMMARY, 'browsertime.summary'); + filterRegistry.registerFilterForType( + DEFAULT_METRICS_PAGE_SUMMARY, + 'browsertime.pageSummary' + ); + filterRegistry.registerFilterForType( + DEFAULT_METRICS_SUMMARY, + 'browsertime.summary' + ); }, processMessage(message, queue) { function processCoachOutput(url, group, results) { - return Promise.resolve(results.browserScripts) - .each((run, runIndex) => { - const coachAdvice = run.coach.coachAdvice; + return Promise.resolve(results.browserScripts).each((run, runIndex) => { + const coachAdvice = run.coach.coachAdvice; - // check if the coach has error(s) - if (!isEmpty(coachAdvice.errors)) { - log.error('%s generated the following errors in the coach %:2j', url, coachAdvice.errors); - queue.postMessage(make('error', 'The coach got the following errors: ' + JSON.stringify(coachAdvice.errors), { - url, - runIndex - })); - } - - // if we miss the HAR from Firefox - if (results.har) { - // make sure to get the right run in the HAR - const myHar = api.pickAPage(results.har, runIndex); - - return api.runHarAdvice(myHar) - .then((harResult) => api.merge(coachAdvice, harResult)) - .then((total) => queue.postMessage(make('coach.run', total, { + // check if the coach has error(s) + if (!isEmpty(coachAdvice.errors)) { + log.error( + '%s generated the following errors in the coach %:2j', + url, + coachAdvice.errors + ); + queue.postMessage( + make( + 'error', + 'The coach got the following errors: ' + + JSON.stringify(coachAdvice.errors), + { url, - group, runIndex - }))); - } else { - return queue.postMessage(make('coach.run', coachAdvice, { - url, - group, - runIndex - })); - } - }); - } + } + ) + ); + } - switch (message.type) { - case 'url': - { - const url = message.url; - const group = message.group; + // if we miss the HAR from Firefox + if (results.har) { + // make sure to get the right run in the HAR + const myHar = api.pickAPage(results.har, runIndex); - visitedUrls.add(url); - // manually set the resultBaseDir - // it's used in BT when we record a video - return this.storageManager.createDirForUrl(message.url, 'data').then((dir) => { - this.options.resultDir = dir - }).then(() => analyzer.analyzeUrl(url, this.options)) - .tap((results) => { - log.verbose('Result from Browsertime for %s with %:2j', url, results); - }) - .tap((results) => { - - results.browserScripts.forEach((run, runIndex) => { - // take the HAR from this run and add it to the - // run data - // sometimes Firefox can't create the HAR + in the future - // we may wanna use Safari (without HAR) - if (results.har) { - - // Add meta data to be used when we compare multiple HARs - results.har.log.pages[runIndex]._meta = {}; - const _meta = results.har.log.pages[runIndex]._meta; - _meta._connectivity = this.options.connectivity.profile; - - if (this.resultUrls.hasBaseUrl()) { - const base = this.resultUrls.absoluteSummaryPageUrl(url); - _meta._screenshot = `${base}data/screenshots/${runIndex}.png`; - _meta._result = `${base}${runIndex}.html`; - if (this.options.video) { - _meta._video = `${base}data/video/${runIndex}.mp4`; - } - } - - // Setup visual metrics to use when we compare hars - results.har.log.pages[runIndex]._visualMetrics = {}; - const _visualMetrics = results.har.log.pages[runIndex]._visualMetrics; - - const pageTimings = results.har.log.pages[runIndex].pageTimings; - - // if we have first, complete85 and last visual change add it to the HAR file - // so we can see it in the waterfall graph (PerfCascade automatically picks it up) - if (results.visualMetrics && results.visualMetrics[runIndex]) { - pageTimings._firstVisualChange = results.visualMetrics[runIndex].FirstVisualChange; - pageTimings._lastVisualChange = results.visualMetrics[runIndex].LastVisualChange; - pageTimings._visualComplete85 = results.visualMetrics[runIndex].VisualComplete85; - _visualMetrics._FirstVisualChange = results.visualMetrics[runIndex].FirstVisualChange; - _visualMetrics._SpeedIndex = results.visualMetrics[runIndex].SpeedIndex; - _visualMetrics._VisualComplete85 = results.visualMetrics[runIndex].VisualComplete85; - _visualMetrics._LastVisualChange = results.visualMetrics[runIndex].LastVisualChange; - _visualMetrics._VisualProgress = jsonifyVisualProgress(results.visualMetrics[runIndex].VisualProgress); - } - // only add first paint if we don't have visual metrics - else if (run.timings.firstPaint) { - pageTimings._firstPaint = run.timings.firstPaint; - } - if (run.timings.pageTimings) { - pageTimings._domInteractiveTime = run.timings.pageTimings.domInteractiveTime; - pageTimings._domContentLoadedTime = run.timings.pageTimings.domContentLoadedTime; - } - - run.har = api.pickAPage(results.har, runIndex); - } - - // Kind of ugly way to add visualMetrics to a run - // it's outside of browserScripts today - // we could instead pass browsertime.visualMetrics maybe - if (results.visualMetrics) { - run.visualMetrics = results.visualMetrics[runIndex]; - } - - run.timestamp = moment(results.timestamps[runIndex]).format(TIME_FORMAT); - - queue.postMessage(make('browsertime.run', run, { + return api + .runHarAdvice(myHar) + .then(harResult => api.merge(coachAdvice, harResult)) + .then(total => + queue.postMessage( + make('coach.run', total, { url, group, runIndex - })); - aggregator.addToAggregate(run, group); - }); + }) + ) + ); + } else { + return queue.postMessage( + make('coach.run', coachAdvice, { + url, + group, + runIndex + }) + ); + } + }); + } - // Let take the first runs timestamp and use that as the summary timestamp - results.timestamp = moment(results.timestamps[0]).format(TIME_FORMAT); - queue.postMessage(make('browsertime.pageSummary', results, { + switch (message.type) { + case 'url': { + const url = message.url; + const group = message.group; + + visitedUrls.add(url); + // manually set the resultBaseDir + // it's used in BT when we record a video + return this.storageManager + .createDirForUrl(message.url, 'data') + .then(dir => { + this.options.resultDir = dir; + }) + .then(() => analyzer.analyzeUrl(url, this.options)) + .tap(results => { + log.verbose( + 'Result from Browsertime for %s with %:2j', + url, + results + ); + }) + .tap(results => { + results.browserScripts.forEach((run, runIndex) => { + // take the HAR from this run and add it to the + // run data + // sometimes Firefox can't create the HAR + in the future + // we may wanna use Safari (without HAR) + if (results.har) { + // Add meta data to be used when we compare multiple HARs + results.har.log.pages[runIndex]._meta = {}; + const _meta = results.har.log.pages[runIndex]._meta; + _meta._connectivity = this.options.connectivity.profile; + + if (this.resultUrls.hasBaseUrl()) { + const base = this.resultUrls.absoluteSummaryPageUrl(url); + _meta._screenshot = `${base}data/screenshots/${runIndex}.png`; + _meta._result = `${base}${runIndex}.html`; + if (this.options.video) { + _meta._video = `${base}data/video/${runIndex}.mp4`; + } + } + + // Setup visual metrics to use when we compare hars + results.har.log.pages[runIndex]._visualMetrics = {}; + const _visualMetrics = + results.har.log.pages[runIndex]._visualMetrics; + + const pageTimings = results.har.log.pages[runIndex].pageTimings; + + // if we have first, complete85 and last visual change add it to the HAR file + // so we can see it in the waterfall graph (PerfCascade automatically picks it up) + if (results.visualMetrics && results.visualMetrics[runIndex]) { + pageTimings._firstVisualChange = + results.visualMetrics[runIndex].FirstVisualChange; + pageTimings._lastVisualChange = + results.visualMetrics[runIndex].LastVisualChange; + pageTimings._visualComplete85 = + results.visualMetrics[runIndex].VisualComplete85; + _visualMetrics._FirstVisualChange = + results.visualMetrics[runIndex].FirstVisualChange; + _visualMetrics._SpeedIndex = + results.visualMetrics[runIndex].SpeedIndex; + _visualMetrics._VisualComplete85 = + results.visualMetrics[runIndex].VisualComplete85; + _visualMetrics._LastVisualChange = + results.visualMetrics[runIndex].LastVisualChange; + _visualMetrics._VisualProgress = jsonifyVisualProgress( + results.visualMetrics[runIndex].VisualProgress + ); + } else if (run.timings.firstPaint) { + // only add first paint if we don't have visual metrics + pageTimings._firstPaint = run.timings.firstPaint; + } + if (run.timings.pageTimings) { + pageTimings._domInteractiveTime = + run.timings.pageTimings.domInteractiveTime; + pageTimings._domContentLoadedTime = + run.timings.pageTimings.domContentLoadedTime; + } + + run.har = api.pickAPage(results.har, runIndex); + } + + // Kind of ugly way to add visualMetrics to a run + // it's outside of browserScripts today + // we could instead pass browsertime.visualMetrics maybe + if (results.visualMetrics) { + run.visualMetrics = results.visualMetrics[runIndex]; + } + + run.timestamp = moment(results.timestamps[runIndex]).format( + TIME_FORMAT + ); + + queue.postMessage( + make('browsertime.run', run, { + url, + group, + runIndex + }) + ); + aggregator.addToAggregate(run, group); + }); + + // Let take the first runs timestamp and use that as the summary timestamp + results.timestamp = moment(results.timestamps[0]).format( + TIME_FORMAT + ); + queue.postMessage( + make('browsertime.pageSummary', results, { url, group - })); - }) - .tap((results) => { - if (results.har) { - queue.postMessage(make('browsertime.har', results.har, { + }) + ); + }) + .tap(results => { + if (results.har) { + queue.postMessage( + make('browsertime.har', results.har, { url, group - })); - } - }) - .tap((results) => { - if (results.extraJson) { - forEach(results.extraJson, (value, key) => { - if (key.indexOf('trace' > -1)) { - queue.postMessage(make('browsertime.chrometrace', value, { + }) + ); + } + }) + .tap(results => { + if (results.extraJson) { + forEach(results.extraJson, (value, key) => { + if (key.indexOf('trace' > -1)) { + queue.postMessage( + make('browsertime.chrometrace', value, { url, group, name: key - })); - } - }) - } - }) - .tap((results) => { - if (results.screenshots) { - queue.postMessage(make('browsertime.screenshot', results.screenshots, { + }) + ); + } + }); + } + }) + .tap(results => { + if (results.screenshots) { + queue.postMessage( + make('browsertime.screenshot', results.screenshots, { url, group - })); - } - }) - .tap((results) => { - if (this.options.coach) { - return processCoachOutput(url, group, results); - } - }) - .catch(BrowsertimeError, (e) => { - log.error('%s generated the following error in Browsertime %s', url, e); - queue.postMessage(make('error', e.message, merge({url}, e.extra))); - }); - } - - case 'summarize': - { - const summary = aggregator.summarize(); - if (summary) { - for (let group of Object.keys(summary.groups)) { - queue.postMessage(make('browsertime.summary', summary.groups[group], {group})); + }) + ); } - } + }) + .tap(results => { + if (this.options.coach) { + return processCoachOutput(url, group, results); + } + }) + .catch(BrowsertimeError, e => { + log.error( + '%s generated the following error in Browsertime %s', + url, + e + ); + queue.postMessage( + make('error', e.message, merge({ url }, e.extra)) + ); + }); + } - break; + case 'summarize': { + const summary = aggregator.summarize(); + if (summary) { + for (let group of Object.keys(summary.groups)) { + queue.postMessage( + make('browsertime.summary', summary.groups[group], { group }) + ); + } } + + break; + } } }, config: defaultConfig diff --git a/lib/plugins/budget/index.js b/lib/plugins/budget/index.js index 59a72a280..dc75a932b 100644 --- a/lib/plugins/budget/index.js +++ b/lib/plugins/budget/index.js @@ -19,11 +19,10 @@ module.exports = { case 'browsertime.pageSummary': case 'webpagetest.pageSummary': case 'pagexray.pageSummary': - case 'coach.pageSummary': - { - verify(message, this.result, this.options.budget.config); - return; - } + case 'coach.pageSummary': { + verify(message, this.result, this.options.budget.config); + return; + } } }, close() { @@ -32,18 +31,25 @@ module.exports = { tap.writeTap(this.result, this.storageManager.getBaseDir()); } else if (this.options.budget.output === 'junit') { junit.writeJunit(this.result, this.storageManager.getBaseDir()); - } - else { + } else { let failing = 0; let working = 0; for (const url of Object.keys(this.result.failing)) { - for (const result of this.result.failing[url]) { - log.error('Failing budget %s.%s for %s with value %s %s limit %s', result.type, result.metric, url, result.value, result.limitType , result.limit); - failing = failing + 1; - } + for (const result of this.result.failing[url]) { + log.error( + 'Failing budget %s.%s for %s with value %s %s limit %s', + result.type, + result.metric, + url, + result.value, + result.limitType, + result.limit + ); + failing = failing + 1; + } } for (const url of Object.keys(this.result.working)) { - working = working + this.result.working[url].length; + working = working + this.result.working[url].length; } log.info('Budget: %d working and %d failing tests', working, failing); } diff --git a/lib/plugins/budget/junit.js b/lib/plugins/budget/junit.js index cb9ef2c02..4e8dea8f1 100644 --- a/lib/plugins/budget/junit.js +++ b/lib/plugins/budget/junit.js @@ -12,29 +12,53 @@ exports.writeJunit = function(results, dir) { for (const url of urls) { const parsedUrl = urlParser.parse(url); - const fineUrl = parsedUrl.hostname.replace(/\./g, '_') + '.' + parsedUrl.path.replace(/\./g, '_').replace(/\//g, '_'); + const fineUrl = + parsedUrl.hostname.replace(/\./g, '_') + + '.' + + parsedUrl.path.replace(/\./g, '_').replace(/\//g, '_'); const suite = builder.testSuite().name('sitespeed.io' + '.' + fineUrl); if (results.failing[url]) { for (const result of results.failing[url]) { - suite.testCase() + suite + .testCase() .className(fineUrl) .name(result.type + '.' + result.metric) - .failure(result.metric + ' is ' + result.value + ' and limit ' + result.limitType + ' ' + result.limit + ' ' + url); + .failure( + result.metric + + ' is ' + + result.value + + ' and limit ' + + result.limitType + + ' ' + + result.limit + + ' ' + + url + ); } } if (results.working[url]) { for (const result of results.working[url]) { - suite.testCase() + suite + .testCase() .className(fineUrl) .name(result.type + '.' + result.metric) - .standardOutput(result.metric + ' is ' + result.value + ' and limit ' + result.limitType + ' ' + result.limit + ' ' + url); + .standardOutput( + result.metric + + ' is ' + + result.value + + ' and limit ' + + result.limitType + + ' ' + + result.limit + + ' ' + + url + ); } } - } const file = path.join(dir, 'junit.xml'); log.info('Write junit budget to %s', path.resolve(file)); builder.writeTo(file); -} +}; diff --git a/lib/plugins/budget/tap.js b/lib/plugins/budget/tap.js index b3cb1c737..029658692 100644 --- a/lib/plugins/budget/tap.js +++ b/lib/plugins/budget/tap.js @@ -7,9 +7,8 @@ const tap = require('tape'), EOL = require('os').EOL; exports.writeTap = function(results, dir) { - const file = path.join(dir, 'budget.tap'); - log.info('Write budget to %s', path.resolve(file) ); + log.info('Write budget to %s', path.resolve(file)); const tapOutput = fs.createWriteStream(file); tap.createStream().pipe(tapOutput); @@ -18,15 +17,26 @@ exports.writeTap = function(results, dir) { for (const url of urls) { for (const result of results.failing[url]) { - tap(result.type + '.' + result.metric + ' ' + url, function(t) { + tap(result.type + '.' + result.metric + ' ' + url, function(t) { let extra = ''; if (resultType === 'failing') { extra = ' limit ' + result.limitType + ' ' + result.limit + EOL; } - t.ok(resultType === 'failing' ? false : true, result.type + '.' + result.metric + ' ' + result.value + ' ' + extra + ' ' + url); + t.ok( + resultType === 'failing' ? false : true, + result.type + + '.' + + result.metric + + ' ' + + result.value + + ' ' + + extra + + ' ' + + url + ); t.end(); - }) + }); } } } -} +}; diff --git a/lib/plugins/budget/verify.js b/lib/plugins/budget/verify.js index eebb7e172..0d6886e1b 100644 --- a/lib/plugins/budget/verify.js +++ b/lib/plugins/budget/verify.js @@ -17,12 +17,15 @@ function getItem(url, type, metric, value, limit, limitType) { } function getHelperFunction(metric) { - if (metric.indexOf('transferSize') > -1 || metric.indexOf('contentSize') > -1) { + if ( + metric.indexOf('transferSize') > -1 || + metric.indexOf('contentSize') > -1 + ) { return size; } else if (metric.indexOf('timings') > -1) { return function(time) { return time + ' ms'; - } + }; } else return noop; } @@ -33,13 +36,19 @@ module.exports = { // do we have an entry in the budget for this kind of message? if (budgets[message.type]) { for (var budget of budgets[message.type]) { - let value = get(message.data, budget.metric); if (value !== undefined) { const format = getHelperFunction(budget.metric); - const item = getItem(message.url, message.type, budget.metric, format(value), budget.max !== undefined ? format(budget.max) : format(budget.min), budget.max !== undefined ? 'max': 'min'); + const item = getItem( + message.url, + message.type, + budget.metric, + format(value), + budget.max !== undefined ? format(budget.max) : format(budget.min), + budget.max !== undefined ? 'max' : 'min' + ); if (budget.max !== undefined) { if (value <= budget.max) { @@ -64,11 +73,15 @@ module.exports = { // group working/failing per URL if (failing.length > 0) { - result.failing[message.url] = result.failing[message.url] ? result.failing[message.url].concat(failing) : failing; + result.failing[message.url] = result.failing[message.url] + ? result.failing[message.url].concat(failing) + : failing; } if (working.length > 0) { - result.working[message.url] = result.working[message.url] ? result.working[message.url].concat(working) : working; + result.working[message.url] = result.working[message.url] + ? result.working[message.url].concat(working) + : working; } } -} +}; diff --git a/lib/plugins/coach/aggregator.js b/lib/plugins/coach/aggregator.js index 0339d406b..726c509e7 100644 --- a/lib/plugins/coach/aggregator.js +++ b/lib/plugins/coach/aggregator.js @@ -8,23 +8,37 @@ module.exports = { groups: {}, addToAggregate(coachData, group) { - if (this.groups[group] === undefined) { this.groups[group] = {}; } // push the total score - statsHelpers.pushGroupStats(this.statsPerCategory, this.groups[group], 'score', coachData.advice.score); + statsHelpers.pushGroupStats( + this.statsPerCategory, + this.groups[group], + 'score', + coachData.advice.score + ); forEach(coachData.advice, (category, categoryName) => { if (category.score === undefined) { return; } // Push the score per category - statsHelpers.pushGroupStats(this.statsPerCategory, this.groups[group], [categoryName, 'score'], category.score); + statsHelpers.pushGroupStats( + this.statsPerCategory, + this.groups[group], + [categoryName, 'score'], + category.score + ); forEach(category.adviceList, (advice, adviceName) => { - statsHelpers.pushGroupStats(this.statsPerCategory, this.groups[group], [categoryName, adviceName], advice.score); + statsHelpers.pushGroupStats( + this.statsPerCategory, + this.groups[group], + [categoryName, adviceName], + advice.score + ); }); }); }, @@ -47,12 +61,12 @@ module.exports = { summarizePerObject(type) { return Object.keys(type).reduce((summary, categoryName) => { if (categoryName === 'score') { - statsHelpers.setStatsSummary(summary, 'score', type[categoryName]) + statsHelpers.setStatsSummary(summary, 'score', type[categoryName]); } else { const categoryData = {}; forEach(type[categoryName], (stats, name) => { - statsHelpers.setStatsSummary(categoryData, name, stats) + statsHelpers.setStatsSummary(categoryData, name, stats); }); summary[categoryName] = categoryData; diff --git a/lib/plugins/coach/index.js b/lib/plugins/coach/index.js index 3370c1c32..9b0f41b29 100644 --- a/lib/plugins/coach/index.js +++ b/lib/plugins/coach/index.js @@ -8,68 +8,129 @@ const filterRegistry = require('../../support/filterRegistry'); const make = messageMaker('coach').make; -const DEFAULT_METRICS_SUMMARY = ['score.*','performance.score.*', 'bestpractice.score.*', 'accessibility.score.*']; -const DEFAULT_METRICS_PAGESUMMARY = ['advice.score','advice.performance.score','advice.bestpractice.score', 'advice.accessibility.score','advice.info.documentHeight','advice.info.domElements','advice.info.domDepth', 'advice.info.iframes', 'advice.info.scripts','advice.info.localStorageSize']; -const DEFAULT_PAGEXRAY_PAGESUMMARY_METRICS = ['contentTypes','transferSize','contentSize','requests','firstParty', 'thirdParty','responseCodes','expireStats', 'totalDomains', 'lastModifiedStats', 'cookieStats']; -const DEFAULT_PAGEXRAY_SUMMARY_METRICS = ['contentTypes','transferSize','contentSize','requests','firstParty', 'thirdParty','responseCodes','expireStats', 'domains', 'lastModifiedStats', 'cookieStats']; +const DEFAULT_METRICS_SUMMARY = [ + 'score.*', + 'performance.score.*', + 'bestpractice.score.*', + 'accessibility.score.*' +]; +const DEFAULT_METRICS_PAGESUMMARY = [ + 'advice.score', + 'advice.performance.score', + 'advice.bestpractice.score', + 'advice.accessibility.score', + 'advice.info.documentHeight', + 'advice.info.domElements', + 'advice.info.domDepth', + 'advice.info.iframes', + 'advice.info.scripts', + 'advice.info.localStorageSize' +]; +const DEFAULT_PAGEXRAY_PAGESUMMARY_METRICS = [ + 'contentTypes', + 'transferSize', + 'contentSize', + 'requests', + 'firstParty', + 'thirdParty', + 'responseCodes', + 'expireStats', + 'totalDomains', + 'lastModifiedStats', + 'cookieStats' +]; +const DEFAULT_PAGEXRAY_SUMMARY_METRICS = [ + 'contentTypes', + 'transferSize', + 'contentSize', + 'requests', + 'firstParty', + 'thirdParty', + 'responseCodes', + 'expireStats', + 'domains', + 'lastModifiedStats', + 'cookieStats' +]; module.exports = { open(context, options) { this.options = options; - filterRegistry.registerFilterForType(DEFAULT_METRICS_SUMMARY, 'coach.summary'); - filterRegistry.registerFilterForType(DEFAULT_METRICS_PAGESUMMARY, 'coach.pageSummary'); - filterRegistry.registerFilterForType(DEFAULT_PAGEXRAY_PAGESUMMARY_METRICS, 'pagexray.pageSummary'); - filterRegistry.registerFilterForType(DEFAULT_PAGEXRAY_SUMMARY_METRICS, 'pagexray.summary'); + filterRegistry.registerFilterForType( + DEFAULT_METRICS_SUMMARY, + 'coach.summary' + ); + filterRegistry.registerFilterForType( + DEFAULT_METRICS_PAGESUMMARY, + 'coach.pageSummary' + ); + filterRegistry.registerFilterForType( + DEFAULT_PAGEXRAY_PAGESUMMARY_METRICS, + 'pagexray.pageSummary' + ); + filterRegistry.registerFilterForType( + DEFAULT_PAGEXRAY_SUMMARY_METRICS, + 'pagexray.summary' + ); }, processMessage(message, queue) { switch (message.type) { - case 'coach.run': - { + case 'coach.run': { if (message.runIndex === 0) { // For now, choose the first run to represent the whole page. // Later we might want to change the median run (based on some metric) similar to the WPT approach. const url = message.url; const group = message.group; - queue.postMessage(make('coach.pageSummary', message.data, {url, group})); + queue.postMessage( + make('coach.pageSummary', message.data, { url, group }) + ); } aggregator.addToAggregate(message.data, message.group); break; } - case 'browsertime.har': - { + case 'browsertime.har': { const url = message.url; const group = message.group; let config = { includeAssets: true, - firstParty: this.options.firstParty ? this.options.firstParty : undefined + firstParty: this.options.firstParty + ? this.options.firstParty + : undefined }; const pageSummary = pagexray.convert(message.data, config); pagexrayAggregator.addToAggregate(pageSummary, group); - queue.postMessage(make('pagexray.pageSummary', pageSummary[0], {url, group})); + queue.postMessage( + make('pagexray.pageSummary', pageSummary[0], { url, group }) + ); pageSummary.forEach((run, runIndex) => { - queue.postMessage(make('pagexray.run', run, {url, group, runIndex})); + queue.postMessage( + make('pagexray.run', run, { url, group, runIndex }) + ); }); break; } - case 'summarize': - { + case 'summarize': { let summary = aggregator.summarize(); if (summary) { for (let group of Object.keys(summary.groups)) { - queue.postMessage(make('coach.summary', summary.groups[group], {group})); + queue.postMessage( + make('coach.summary', summary.groups[group], { group }) + ); } } let pagexraySummary = pagexrayAggregator.summarize(); if (pagexraySummary) { for (let group of Object.keys(pagexraySummary.groups)) { - queue.postMessage(make('pagexray.summary', pagexraySummary.groups[group], {group})); + queue.postMessage( + make('pagexray.summary', pagexraySummary.groups[group], { group }) + ); } } break; diff --git a/lib/plugins/coach/pagexrayAggregator.js b/lib/plugins/coach/pagexrayAggregator.js index 6ea0e7239..f3aa99f5e 100644 --- a/lib/plugins/coach/pagexrayAggregator.js +++ b/lib/plugins/coach/pagexrayAggregator.js @@ -9,7 +9,6 @@ module.exports = { stats: {}, groups: {}, addToAggregate(pageSummary, group) { - if (this.groups[group] === undefined) { this.groups[group] = {}; } @@ -20,42 +19,85 @@ module.exports = { pageSummary.forEach(function(summary) { // stats for the whole page METRIC_NAMES.forEach(function(metric) { - statsHelpers.pushGroupStats(stats, groups[group], metric, summary[metric]); + statsHelpers.pushGroupStats( + stats, + groups[group], + metric, + summary[metric] + ); }); Object.keys(summary.contentTypes).forEach(function(contentType) { METRIC_NAMES.forEach(function(metric) { - statsHelpers.pushGroupStats(stats,groups[group], 'contentTypes.' + contentType + '.' + metric, summary.contentTypes[contentType][metric]); + statsHelpers.pushGroupStats( + stats, + groups[group], + 'contentTypes.' + contentType + '.' + metric, + summary.contentTypes[contentType][metric] + ); }); }); Object.keys(summary.responseCodes).forEach(function(responseCode) { - statsHelpers.pushGroupStats(stats, groups[group], 'responseCodes.' + responseCode, summary.responseCodes[responseCode]); + statsHelpers.pushGroupStats( + stats, + groups[group], + 'responseCodes.' + responseCode, + summary.responseCodes[responseCode] + ); }); // extras for firstParty vs third if (summary.firstParty.requests) { METRIC_NAMES.forEach(function(metric) { if (summary.firstParty[metric] !== undefined) { - statsHelpers.pushGroupStats(stats, groups[group], 'firstParty' + '.' + metric, summary.firstParty[metric]); + statsHelpers.pushGroupStats( + stats, + groups[group], + 'firstParty' + '.' + metric, + summary.firstParty[metric] + ); } if (summary.thirdParty[metric] !== undefined) { - statsHelpers.pushGroupStats(stats, groups[group], 'thirdParty' + '.' + metric, summary.thirdParty[metric]); + statsHelpers.pushGroupStats( + stats, + groups[group], + 'thirdParty' + '.' + metric, + summary.thirdParty[metric] + ); } - }) + }); } // Add the total amount of domains on this page - statsHelpers.pushGroupStats(stats, groups[group], 'domains', (Object.keys(summary.domains)).length); + statsHelpers.pushGroupStats( + stats, + groups[group], + 'domains', + Object.keys(summary.domains).length + ); - forEach(summary.assets, (asset) => { - statsHelpers.pushGroupStats(stats, groups[group], 'expireStats', asset.expires); - statsHelpers.pushGroupStats(stats, groups[group], 'lastModifiedStats', asset.timeSinceLastModified); - statsHelpers.pushGroupStats(stats, groups[group], 'cookiesStats', asset.cookies); + forEach(summary.assets, asset => { + statsHelpers.pushGroupStats( + stats, + groups[group], + 'expireStats', + asset.expires + ); + statsHelpers.pushGroupStats( + stats, + groups[group], + 'lastModifiedStats', + asset.timeSinceLastModified + ); + statsHelpers.pushGroupStats( + stats, + groups[group], + 'cookiesStats', + asset.cookies + ); }); - - }) - + }); }, summarize() { if (Object.keys(this.stats).length === 0) { @@ -71,31 +113,37 @@ module.exports = { summary.groups[group] = this.summarizePerObject(this.groups[group]); } return summary; - }, summarizePerObject(type) { return Object.keys(type).reduce((summary, name) => { - if (METRIC_NAMES.indexOf(name) > -1 || name.match(/(^domains|^expireStats|^lastModifiedStats|^cookiesStats)/)) { - statsHelpers.setStatsSummary(summary, name, type[name]) + if ( + METRIC_NAMES.indexOf(name) > -1 || + name.match(/(^domains|^expireStats|^lastModifiedStats|^cookiesStats)/) + ) { + statsHelpers.setStatsSummary(summary, name, type[name]); } else { if (name === 'contentTypes') { const contentTypeData = {}; - forEach(Object.keys(type[name]), (contentType) => { + forEach(Object.keys(type[name]), contentType => { forEach(type[name][contentType], (stats, metric) => { - statsHelpers.setStatsSummary(contentTypeData, [contentType, metric], stats) + statsHelpers.setStatsSummary( + contentTypeData, + [contentType, metric], + stats + ); }); }); summary[name] = contentTypeData; - } else if(name === 'responseCodes') { + } else if (name === 'responseCodes') { const responseCodeData = {}; - type.responseCodes.forEach((stats, metric) =>{ + type.responseCodes.forEach((stats, metric) => { statsHelpers.setStatsSummary(responseCodeData, metric, stats); }); summary[name] = responseCodeData; } else { const data = {}; forEach(type[name], (stats, metric) => { - statsHelpers.setStatsSummary(data, metric, stats) + statsHelpers.setStatsSummary(data, metric, stats); }); summary[name] = data; } diff --git a/lib/plugins/crawler/index.js b/lib/plugins/crawler/index.js index 7493f4e01..05bcde3db 100644 --- a/lib/plugins/crawler/index.js +++ b/lib/plugins/crawler/index.js @@ -9,9 +9,9 @@ const Crawler = require('simplecrawler'); const make = messageMaker('crawler').make; -const defaultOptions = ({ +const defaultOptions = { depth: 3 -}); +}; module.exports = { open(context, options) { @@ -25,7 +25,7 @@ module.exports = { return Promise.resolve(); } - return new Promise((resolve) => { + return new Promise(resolve => { const redirectedUrls = new Set(), crawler = new Crawler(message.url); @@ -45,7 +45,7 @@ module.exports = { redirectedUrls.add(response.headers.location); }); - crawler.on('fetchcomplete', (queueItem) => { + crawler.on('fetchcomplete', queueItem => { const pageMimeType = /^(text|application)\/x?html/i; const url = queueItem.url; @@ -55,7 +55,7 @@ module.exports = { log.verbose('Crawler skipping initial URL %s', url); } else if (pageMimeType.test(queueItem.stateData.contentType)) { log.verbose('Crawler found %s URL %s', pageCount, url); - queue.postMessage(make('url', {}, {url, group: message.group})); + queue.postMessage(make('url', {}, { url, group: message.group })); pageCount++; if (pageCount >= maxPages) { @@ -70,10 +70,16 @@ module.exports = { crawler.on('complete', resolve); - log.info('Starting to crawl from ' + message.url + ' with max depth ' + crawler.maxDepth + - ' and max count ' + maxPages); + log.info( + 'Starting to crawl from ' + + message.url + + ' with max depth ' + + crawler.maxDepth + + ' and max count ' + + maxPages + ); crawler.start(); - }) + }); } } }; diff --git a/lib/plugins/datacollector/dataCollector.js b/lib/plugins/datacollector/dataCollector.js index bca7240ec..4b246f586 100644 --- a/lib/plugins/datacollector/dataCollector.js +++ b/lib/plugins/datacollector/dataCollector.js @@ -39,9 +39,9 @@ class DataCollector { if (runIndex !== undefined) { let runData = this.urlRunPages[url][runIndex] || { - runIndex, - data: {} - }; + runIndex, + data: {} + }; set(runData.data, typePath, data); this.urlRunPages[url][runIndex] = runData; } else { @@ -56,7 +56,6 @@ class DataCollector { set(this.summaryPages, name, merge({}, data)); } } - } module.exports = DataCollector; diff --git a/lib/plugins/datacollector/index.js b/lib/plugins/datacollector/index.js index bc161fa5a..4a7893b66 100644 --- a/lib/plugins/datacollector/index.js +++ b/lib/plugins/datacollector/index.js @@ -15,10 +15,13 @@ module.exports = { const dataCollector = this.dataCollector; switch (message.type) { - case 'error': - { - return dataCollector.addErrorForUrl(message.url, message.source, message.data); - } + case 'error': { + return dataCollector.addErrorForUrl( + message.url, + message.source, + message.data + ); + } case 'browsertime.run': case 'browsertime.pageSummary': @@ -30,63 +33,66 @@ module.exports = { case 'pagexray.run': case 'pagexray.pageSummary': case 'coach.run': - case 'coach.pageSummary': - { - return dataCollector.addDataForUrl(message.url, message.type, message.data, message.runIndex); - } + case 'coach.pageSummary': { + return dataCollector.addDataForUrl( + message.url, + message.type, + message.data, + message.runIndex + ); + } - case 'aggregateassets.summary': - { - if (message.group === 'total') { - const assetList = reduce(message.data, (assetList, asset) => { + case 'aggregateassets.summary': { + if (message.group === 'total') { + const assetList = reduce( + message.data, + (assetList, asset) => { assetList.push(asset); return assetList; - }, []); + }, + [] + ); - const count = this.maxAssets, - fullCount = Object.keys(assetList).length, - topAssets = assetList + const count = this.maxAssets, + fullCount = Object.keys(assetList).length, + topAssets = assetList .sort((a, b) => b.requestCount - a.requestCount) .splice(0, count); - return dataCollector.addDataForSummaryPage('assets', { - topAssets, - count, - fullCount - }); - } else return; - } + return dataCollector.addDataForSummaryPage('assets', { + topAssets, + count, + fullCount + }); + } else return; + } - case 'largestassets.summary': - { - if (message.group === 'total') { - const assetsBySize = {}; - const contentType = Object.keys(message.data)[0]; - assetsBySize[contentType] = message.data[contentType]; - return dataCollector.addDataForSummaryPage('toplist', { - assetsBySize - }); - } else return; - } - case 'largestthirdpartyassets.summary': - { + case 'largestassets.summary': { + if (message.group === 'total') { + const assetsBySize = {}; + const contentType = Object.keys(message.data)[0]; + assetsBySize[contentType] = message.data[contentType]; + return dataCollector.addDataForSummaryPage('toplist', { + assetsBySize + }); + } else return; + } + case 'largestthirdpartyassets.summary': { if (message.group === 'total') { return dataCollector.addDataForSummaryPage('toplist', { thirdPartyAssetsBySize: message.data }); } else return; } - case 'slowestassets.summary': - { - if (message.group === 'total') { - const slowestAssets = message.data; - return dataCollector.addDataForSummaryPage('toplist', { - slowestAssets - }); - } else return; - } + case 'slowestassets.summary': { + if (message.group === 'total') { + const slowestAssets = message.data; + return dataCollector.addDataForSummaryPage('toplist', { + slowestAssets + }); + } else return; + } - case 'slowestthirdpartyassets.summary': - { + case 'slowestthirdpartyassets.summary': { if (message.group === 'total') { return dataCollector.addDataForSummaryPage('toplist', { thirdPartySlowestAssets: message.data @@ -94,39 +100,41 @@ module.exports = { } else return; } - case 'domains.summary': - { - if (message.group === 'total') { - const domainList = reduce(message.data, (domainList, domainStats) => { + case 'domains.summary': { + if (message.group === 'total') { + const domainList = reduce( + message.data, + (domainList, domainStats) => { domainList.push(domainStats); return domainList; - }, []); + }, + [] + ); - const count = 200, - fullCount = domainList.length, - topDomains = domainList + const count = 200, + fullCount = domainList.length, + topDomains = domainList .sort((a, b) => b.requestCount - a.requestCount) .splice(0, count); - return dataCollector.addDataForSummaryPage('domains', { - topDomains, - count, - fullCount - }); - } else { - return - } + return dataCollector.addDataForSummaryPage('domains', { + topDomains, + count, + fullCount + }); + } else { + return; } + } case 'webpagetest.summary': case 'coach.summary': case 'pagexray.summary': case 'browsertime.summary': - case 'gpsi.summary': - { - const data = {}; - set(data, message.type, message.data); - dataCollector.addDataForSummaryPage('index', data); - return dataCollector.addDataForSummaryPage('detailed', data); - } + case 'gpsi.summary': { + const data = {}; + set(data, message.type, message.data); + dataCollector.addDataForSummaryPage('index', data); + return dataCollector.addDataForSummaryPage('detailed', data); + } } }, close() {} diff --git a/lib/plugins/domains/aggregator.js b/lib/plugins/domains/aggregator.js index 6240b4abd..a883d2237 100644 --- a/lib/plugins/domains/aggregator.js +++ b/lib/plugins/domains/aggregator.js @@ -7,53 +7,65 @@ const Stats = require('fast-stats').Stats, isEmpty = require('lodash.isempty'), reduce = require('lodash.reduce'); -const timingNames = ['blocked', 'dns', 'connect', 'ssl', 'send', 'wait', 'receive']; +const timingNames = [ + 'blocked', + 'dns', + 'connect', + 'ssl', + 'send', + 'wait', + 'receive' +]; function parseDomainName(url) { return urlParser.parse(url).hostname; } function getDomain(domainName) { - return { - domainName, - requestCount: 0, - totalTime: new Stats(), - blocked: new Stats(), - dns: new Stats(), - connect: new Stats(), - ssl: new Stats(), - send: new Stats(), - wait: new Stats(), - receive: new Stats() - }; - } + return { + domainName, + requestCount: 0, + totalTime: new Stats(), + blocked: new Stats(), + dns: new Stats(), + connect: new Stats(), + ssl: new Stats(), + send: new Stats(), + wait: new Stats(), + receive: new Stats() + }; +} function calc(domains) { - return reduce(domains, (summary, domainStats, domainName) => { - const domainSummary = { - requestCount: domainStats.requestCount, - domainName - }; + return reduce( + domains, + (summary, domainStats, domainName) => { + const domainSummary = { + requestCount: domainStats.requestCount, + domainName + }; - const stats = statsHelpers.summarizeStats(domainStats.totalTime); - if (!isEmpty(stats)) { - domainSummary.totalTime = stats; - } - timingNames.forEach((name) => { - const stats = statsHelpers.summarizeStats(domainStats[name]); + const stats = statsHelpers.summarizeStats(domainStats.totalTime); if (!isEmpty(stats)) { - domainSummary[name] = stats; + domainSummary.totalTime = stats; } - }); + timingNames.forEach(name => { + const stats = statsHelpers.summarizeStats(domainStats[name]); + if (!isEmpty(stats)) { + domainSummary[name] = stats; + } + }); - summary[domainName] = domainSummary; - return summary; - }, {}); + summary[domainName] = domainSummary; + return summary; + }, + {} + ); } function isValidTiming(timing) { // The HAR format uses -1 to indicate invalid/missing timings - return (typeof timing === 'number' && timing !== -1); + return typeof timing === 'number' && timing !== -1; } module.exports = { @@ -62,11 +74,11 @@ module.exports = { addToAggregate(har, url) { const mainDomain = parseDomainName(url); if (this.groups[mainDomain] === undefined) { - this.groups[mainDomain] = {} + this.groups[mainDomain] = {}; } const firstPageId = har.log.pages[0].id; - har.log.entries.forEach((entry) => { + har.log.entries.forEach(entry => { if (entry.pageref !== firstPageId) { // Only pick the first request out of multiple runs. return; @@ -74,7 +86,8 @@ module.exports = { const domainName = parseDomainName(entry.request.url), domain = this.domains[domainName] || getDomain(domainName), - groupDomain = this.groups[mainDomain][domainName] || getDomain(domainName), + groupDomain = + this.groups[mainDomain][domainName] || getDomain(domainName), totalTime = entry.time; domain.requestCount++; @@ -87,7 +100,7 @@ module.exports = { log.debug('Missing time from har entry for url: ' + entry.request.url); } - timingNames.forEach((name) => { + timingNames.forEach(name => { const timing = entry.timings[name]; if (isValidTiming(timing)) { diff --git a/lib/plugins/domains/index.js b/lib/plugins/domains/index.js index 39b8c426a..3e9009625 100644 --- a/lib/plugins/domains/index.js +++ b/lib/plugins/domains/index.js @@ -14,22 +14,22 @@ module.exports = { }, processMessage(message, queue) { switch (message.type) { - case 'browsertime.har': - { - aggregator.addToAggregate(message.data, message.url); - break; - } + case 'browsertime.har': { + aggregator.addToAggregate(message.data, message.url); + break; + } - case 'summarize': - { - const summary = aggregator.summarize(); - if (!isEmpty(summary)) { - for (let group of Object.keys(summary.groups)) { - queue.postMessage(make('domains.summary', summary.groups[group], {group})); - } + case 'summarize': { + const summary = aggregator.summarize(); + if (!isEmpty(summary)) { + for (let group of Object.keys(summary.groups)) { + queue.postMessage( + make('domains.summary', summary.groups[group], { group }) + ); } - break; } + break; + } } } }; diff --git a/lib/plugins/gpsi/aggregator.js b/lib/plugins/gpsi/aggregator.js index f7e8877ad..c46de3cef 100644 --- a/lib/plugins/gpsi/aggregator.js +++ b/lib/plugins/gpsi/aggregator.js @@ -2,21 +2,34 @@ const statsHelpers = require('../../support/statsHelpers'); - module.exports = { - statsPerType: {}, - total: {}, - groups: {}, - addToAggregate(gpsiData, group) { - if (this.groups[group] === undefined) { - this.groups[group] = {}; - } - statsHelpers.pushGroupStats(this.statsPerType, this.groups[group], 'SPEED.score', gpsiData.ruleGroups['SPEED'].score); - statsHelpers.pushStats(this.total, 'SPEED.score', gpsiData.ruleGroups['SPEED'].score); - }, - summarize() { - const summary = {}; - statsHelpers.setStatsSummary(summary, 'groups.total.SPEED.score', this.total.SPEED.score); - // TODO add GPSI score per group - return summary; +module.exports = { + statsPerType: {}, + total: {}, + groups: {}, + addToAggregate(gpsiData, group) { + if (this.groups[group] === undefined) { + this.groups[group] = {}; } + statsHelpers.pushGroupStats( + this.statsPerType, + this.groups[group], + 'SPEED.score', + gpsiData.ruleGroups['SPEED'].score + ); + statsHelpers.pushStats( + this.total, + 'SPEED.score', + gpsiData.ruleGroups['SPEED'].score + ); + }, + summarize() { + const summary = {}; + statsHelpers.setStatsSummary( + summary, + 'groups.total.SPEED.score', + this.total.SPEED.score + ); + // TODO add GPSI score per group + return summary; } +}; diff --git a/lib/plugins/gpsi/analyzer.js b/lib/plugins/gpsi/analyzer.js index d4c3be5e6..c1a41dfc1 100644 --- a/lib/plugins/gpsi/analyzer.js +++ b/lib/plugins/gpsi/analyzer.js @@ -6,7 +6,7 @@ var log = require('intel').getLogger('sitespeedio.plugin.gpsi'), module.exports = { analyzeUrl: function(url, options) { log.info('Sending url ' + url + ' to test on Page Speed Insights'); - const args = {url}; + const args = { url }; if (options.gpsi.key) { args.key = options.gpsi.key; @@ -14,10 +14,10 @@ module.exports = { args.nokey = true; } - args.strategy = "desktop"; + args.strategy = 'desktop'; - if(options.mobile) { - args.strategy = "mobile"; + if (options.mobile) { + args.strategy = 'mobile'; } return gpagespeed(args); diff --git a/lib/plugins/gpsi/index.js b/lib/plugins/gpsi/index.js index 5fc8fa544..fceb24bd1 100644 --- a/lib/plugins/gpsi/index.js +++ b/lib/plugins/gpsi/index.js @@ -15,31 +15,35 @@ module.exports = { gpsi: options.gpsi, mobile: options.mobile }; - filterRegistry.registerFilterForType(DEFAULT_METRICS_PAGESUMMARY, 'gpsi.pageSummary'); + filterRegistry.registerFilterForType( + DEFAULT_METRICS_PAGESUMMARY, + 'gpsi.pageSummary' + ); }, processMessage(message, queue) { switch (message.type) { - case 'url': - { - const url = message.url; - const group = message.group; - return analyzer.analyzeUrl(url, this.options) - .then((result) => { - log.info('Got ' + url + ' analysed from Google Page Speed Insights'); - log.verbose('Result from Google Page Speed Insights:%:2j', result); - queue.postMessage(make('gpsi.pageSummary', result, { - url, - group - })); - aggregator.addToAggregate(result, group); - }); - } + case 'url': { + const url = message.url; + const group = message.group; + return analyzer.analyzeUrl(url, this.options).then(result => { + log.info('Got ' + url + ' analysed from Google Page Speed Insights'); + log.verbose('Result from Google Page Speed Insights:%:2j', result); + queue.postMessage( + make('gpsi.pageSummary', result, { + url, + group + }) + ); + aggregator.addToAggregate(result, group); + }); + } - case 'summarize': - { - const summary = aggregator.summarize(); - queue.postMessage(make('gpsi.summary', summary.groups.total , {group: 'total'})); - } + case 'summarize': { + const summary = aggregator.summarize(); + queue.postMessage( + make('gpsi.summary', summary.groups.total, { group: 'total' }) + ); + } } } }; diff --git a/lib/plugins/graphite/data-generator.js b/lib/plugins/graphite/data-generator.js index e6c75f99a..2ec691345 100644 --- a/lib/plugins/graphite/data-generator.js +++ b/lib/plugins/graphite/data-generator.js @@ -10,8 +10,11 @@ function keyPathFromMessage(message, options, includeQueryParams) { typeParts.push(typeParts.shift()); // always have browser and connectivity in Browsertime and related tools - if (message.type.match(/(^pagexray|^coach|^browsertime|^largestassets|^slowestassets|^aggregateassets|^domains)/)) { - + if ( + message.type.match( + /(^pagexray|^coach|^browsertime|^largestassets|^slowestassets|^aggregateassets|^domains)/ + ) + ) { // if we have a friendly name for your connectivity, use that! let connectivity = graphiteUtil.getConnectivity(options); @@ -27,7 +30,16 @@ function keyPathFromMessage(message, options, includeQueryParams) { } // if we get a URL type, add the URL if (message.url) { - typeParts.splice(1, 0, graphiteUtil.getURLAndGroup(options, message.group, message.url, includeQueryParams)); + typeParts.splice( + 1, + 0, + graphiteUtil.getURLAndGroup( + options, + message.group, + message.url, + includeQueryParams + ) + ); } else if (message.group) { // add the group of the summary message typeParts.splice(1, 0, graphiteUtil.toSafeKey(message.group)); @@ -46,13 +58,21 @@ class GraphiteDataGenerator { dataFromMessage(message, time) { const timestamp = Math.round(time.valueOf() / 1000); - const keypath = keyPathFromMessage(message, this.options, this.includeQueryParams); + const keypath = keyPathFromMessage( + message, + this.options, + this.includeQueryParams + ); - return reduce(flatten.flattenMessageData(message), (entries, value, key) => { - const fullKey = util.format('%s.%s.%s', this.namespace, keypath, key); - entries.push(util.format('%s %s %s', fullKey, value, timestamp)); - return entries; - }, []); + return reduce( + flatten.flattenMessageData(message), + (entries, value, key) => { + const fullKey = util.format('%s.%s.%s', this.namespace, keypath, key); + entries.push(util.format('%s %s %s', fullKey, value, timestamp)); + return entries; + }, + [] + ); } } diff --git a/lib/plugins/graphite/index.js b/lib/plugins/graphite/index.js index d7ee84573..3b17a54d4 100644 --- a/lib/plugins/graphite/index.js +++ b/lib/plugins/graphite/index.js @@ -21,13 +21,27 @@ module.exports = { const opts = merge({}, defaultConfig, options.graphite); this.options = options; this.sender = new Sender(opts.host, opts.port); - this.dataGenerator = new DataGenerator(opts.namespace, opts.includeQueryParams, options); - log.debug('Setting up Graphite %s:%s for namespace %s', opts.host, opts.port, opts.namespace); + this.dataGenerator = new DataGenerator( + opts.namespace, + opts.includeQueryParams, + options + ); + log.debug( + 'Setting up Graphite %s:%s for namespace %s', + opts.host, + opts.port, + opts.namespace + ); this.timestamp = context.timestamp; this.resultUrls = context.resultUrls; }, processMessage(message) { - if (!(message.type.endsWith('.summary') || message.type.endsWith('.pageSummary'))) + if ( + !( + message.type.endsWith('.summary') || + message.type.endsWith('.pageSummary') + ) + ) return; // we only sends individual groups to Graphite, not the @@ -37,27 +51,44 @@ module.exports = { } message = filterRegistry.filterMessage(message); - if (isEmpty(message.data)) - return; + if (isEmpty(message.data)) return; // TODO Here we could add logic to either create a new timestamp or // use the one that we have for that run. Now just use the one for the // run - const dataPoints = this.dataGenerator.dataFromMessage(message, this.timestamp); + const dataPoints = this.dataGenerator.dataFromMessage( + message, + this.timestamp + ); if (dataPoints.length > 0) { const data = dataPoints.join('\n') + '\n'; return this.sender.send(data).then(() => { // make sure we only send once per URL (and browsertime is the most important) // and you need to configure a base URL where you get the HTML result - if (message.type === 'browsertime.pageSummary' && this.resultUrls.hasBaseUrl()) { - const resultPageUrl = this.resultUrls.absoluteSummaryPageUrl(message.url); - return sendAnnotations.send(this.options, message.group, message.url, resultPageUrl, this.timestamp); + if ( + message.type === 'browsertime.pageSummary' && + this.resultUrls.hasBaseUrl() + ) { + const resultPageUrl = this.resultUrls.absoluteSummaryPageUrl( + message.url + ); + return sendAnnotations.send( + this.options, + message.group, + message.url, + resultPageUrl, + this.timestamp + ); } }); } else { - return Promise.reject(new Error('No data to send to graphite for message:\n' + - JSON.stringify(message, null, 2))); + return Promise.reject( + new Error( + 'No data to send to graphite for message:\n' + + JSON.stringify(message, null, 2) + ) + ); } }, config: defaultConfig diff --git a/lib/plugins/graphite/send-annotation.js b/lib/plugins/graphite/send-annotation.js index 186af3273..615f1eb93 100644 --- a/lib/plugins/graphite/send-annotation.js +++ b/lib/plugins/graphite/send-annotation.js @@ -7,7 +7,6 @@ const graphiteUtil = require('../../support/tsdbUtil'); module.exports = { send(options, group, url, resultPageUrl, time) { - // The tags make it possible for the dashboard to use the // templates to choose which annotations that will be showed. // That's why we need to send tags that matches the template @@ -15,14 +14,18 @@ module.exports = { const connectivity = graphiteUtil.getConnectivity(options); const browser = options.browser; const namespace = options.graphite.namespace.split('.'); - const urlAndGroup = graphiteUtil.getURLAndGroup(options, group, url, options.graphite.includeQueryParams).split('.'); - const tagsString = `"${connectivity},${browser},${namespace.join(',')},${urlAndGroup.join(',')}"`; + const urlAndGroup = graphiteUtil + .getURLAndGroup(options, group, url, options.graphite.includeQueryParams) + .split('.'); + const tagsString = `"${connectivity},${browser},${namespace.join( + ',' + )},${urlAndGroup.join(',')}"`; const tagsArray = `["${connectivity}","${browser}","${namespace[0]}","${namespace[1]}","${urlAndGroup[0]}", "${urlAndGroup[1]}"]`; - const message = `Result ${options.browsertime.iterations} run(s)`; + const message = `Result ${options + .browsertime.iterations} run(s)`; const timestamp = Math.round(time.valueOf() / 1000); const tags = options.graphite.arrayTags ? tagsArray : tagsString; - const postData = - `{"what": "Sitespeed.io", "tags": ${tags}, "data": "${message}", "when": ${timestamp}}`; + const postData = `{"what": "Sitespeed.io", "tags": ${tags}, "data": "${message}", "when": ${timestamp}}`; const postOptions = { hostname: options.graphite.webHost || options.graphite.host, port: options.graphite.httpPort || 8080, @@ -43,9 +46,12 @@ module.exports = { log.verbose('Send annotation to Graphite: %j', postData); // not perfect but maybe work for us const lib = options.graphite.httpPort === 443 ? https : http; - const req = lib.request(postOptions, (res) => { + const req = lib.request(postOptions, res => { if (res.statusCode !== 200) { - log.error('Got %s from Graphite when sending annotation', res.statusCode); + log.error( + 'Got %s from Graphite when sending annotation', + res.statusCode + ); reject(); } else { res.setEncoding('utf8'); @@ -53,12 +59,12 @@ module.exports = { resolve(); } }); - req.on('error', (err) => { + req.on('error', err => { log.error('Got error from Graphite when sending annotation', err); - reject(err) + reject(err); }); req.write(postData); req.end(); - }) + }); } }; diff --git a/lib/plugins/harstorer/index.js b/lib/plugins/harstorer/index.js index 52f8e0e0d..5cabe7340 100644 --- a/lib/plugins/harstorer/index.js +++ b/lib/plugins/harstorer/index.js @@ -13,15 +13,24 @@ module.exports = { processMessage(message) { switch (message.type) { case 'browsertime.har': - case 'webpagetest.har': - { - const jsonData = JSON.stringify(message.data); - if (this.gzipHAR) { - return this.storageManager.writeDataForUrl(jsonData, message.type, message.url, '', true); - } else { - return this.storageManager.writeDataForUrl(jsonData, message.type, message.url); - } + case 'webpagetest.har': { + const jsonData = JSON.stringify(message.data); + if (this.gzipHAR) { + return this.storageManager.writeDataForUrl( + jsonData, + message.type, + message.url, + '', + true + ); + } else { + return this.storageManager.writeDataForUrl( + jsonData, + message.type, + message.url + ); } + } } } }; diff --git a/lib/plugins/html/htmlBuilder.js b/lib/plugins/html/htmlBuilder.js index 9af272de0..4cabe151e 100644 --- a/lib/plugins/html/htmlBuilder.js +++ b/lib/plugins/html/htmlBuilder.js @@ -49,26 +49,38 @@ class HTMLBuilder { const detailedBoxes = dataCollection.getDetailedBoxes(); this.summary.pages = { - pageTitle: `Overview of ${helpers.plural(nTestedPages,'page')} for ${name} at ${timestamp}`, + pageTitle: `Overview of ${helpers.plural( + nTestedPages, + 'page' + )} for ${name} at ${timestamp}`, pageDescription: 'See all the tested pages on a high level.', pages: validPages }; this.summary.index = { - pageTitle: `Executive Summary for ${name} tested ${helpers.plural(nTestedPages,'page')} at ${timestamp}`, - pageDescription: 'Executive summary of the sitespeed.io result. Act on red/yellow/green.', + pageTitle: `Executive Summary for ${name} tested ${helpers.plural( + nTestedPages, + 'page' + )} at ${timestamp}`, + pageDescription: + 'Executive summary of the sitespeed.io result. Act on red/yellow/green.', boxes: chunk(summaryBoxes.filter(Boolean), 3) }; this.summary.detailed = { - pageTitle: `In details summary for ${name} tested ${helpers.plural(nTestedPages,'page')} at ${timestamp}`, - pageDescription: 'Get all the details you need to fast track things you need to change.', + pageTitle: `In details summary for ${name} tested ${helpers.plural( + nTestedPages, + 'page' + )} at ${timestamp}`, + pageDescription: + 'Get all the details you need to fast track things you need to change.', metrics: detailedBoxes }; this.summary.domains = { pageTitle: `The most used domains for ${name} tested at ${timestamp}`, - pageDescription: 'A list of the most used domains and the respective timings' + pageDescription: + 'A list of the most used domains and the respective timings' }; this.summary.assets = { @@ -84,10 +96,10 @@ class HTMLBuilder { if (options.budget) { let totalFailing = 0; let totalWorking = 0; - for (const url of Object.keys(this.budget.failing )) { + for (const url of Object.keys(this.budget.failing)) { totalFailing = totalFailing + this.budget.failing[url].length; } - for (const url of Object.keys(this.budget.working )) { + for (const url of Object.keys(this.budget.working)) { totalWorking = totalWorking + this.budget.working[url].length; } this.summary.budget = { @@ -108,120 +120,170 @@ class HTMLBuilder { coach: coachData }; + const summaryRenders = Object.keys(this.summary).map(name => + this._renderSummaryPage( + name, + merge( + { + options, + noPages: Object.keys(dataCollection.urlPages).length + }, + dataCollection.summaryPages[name], + this.summary[name] + ) + ) + ); - const summaryRenders = Object.keys(this.summary) - .map((name) => this._renderSummaryPage(name, merge({ + const urlPageRenders = Promise.resolve(Object.keys(validPages)).map(url => { + const pageInfo = validPages[url]; + const runPages = dataCollection.urlRunPages[url]; + const medianRun = metricHelper.pickMedianRun(runPages, pageInfo); + let summaryPageHAR = get(pageInfo, 'data.browsertime.har'); + let browser = { + name: summaryPageHAR.log.browser.name, + version: summaryPageHAR.log.browser.version + }; + // if we are on the summary page we inline the HAR and then make sure + // we only pick one HAR run (medianRun). But you can also choose to + // fetch the HAR in the HTML, then it isn't included. + if (!(isEmpty(runPages) || options.html.showAllWaterfallSummary)) { + // only if we have some browsertime metrics, take the HAR and pass it to the summary + const har = get(runPages[medianRun], 'data.browsertime.run.har'); + if (har) { + summaryPageHAR = har; + } + } + + let daurlAlias; + if ( + options.urlsMetaData && + options.urlsMetaData[url] && + options.urlsMetaData[url].alias + ) { + daurlAlias = options.urlsMetaData[url].alias; + } + + return this._renderUrlPage(url, 'index', { + daurl: url, + daurlAlias, + pageInfo, options, - noPages: Object.keys(dataCollection.urlPages).length - }, dataCollection.summaryPages[name], this.summary[name]))); - - const urlPageRenders = Promise.resolve(Object.keys(validPages)) - .map((url) => { - const pageInfo = validPages[url]; - const runPages = dataCollection.urlRunPages[url]; - const medianRun = metricHelper.pickMedianRun(runPages, pageInfo); - let summaryPageHAR = get(pageInfo, 'data.browsertime.har'); - let browser = { - name: summaryPageHAR.log.browser.name, - version: summaryPageHAR.log.browser.version - } - // if we are on the summary page we inline the HAR and then make sure - // we only pick one HAR run (medianRun). But you can also choose to - // fetch the HAR in the HTML, then it isn't included. - if (!(isEmpty(runPages) || options.html.showAllWaterfallSummary)) { - // only if we have some browsertime metrics, take the HAR and pass it to the summary - const har = get(runPages[medianRun], 'data.browsertime.run.har'); - if (har) { - summaryPageHAR = har; - } - } - - let daurlAlias; - if (options.urlsMetaData && options.urlsMetaData[url] && options.urlsMetaData[url].alias) { - daurlAlias = options.urlsMetaData[url].alias - } - - return this._renderUrlPage(url, 'index', { + runPages, + summaryPageHAR, + medianRun, + browser + }).tap(() => + Promise.resolve(Object.keys(runPages)).map(runIndex => + this._renderUrlRunPage(url, runIndex, { daurl: url, daurlAlias, - pageInfo, + runIndex, + pageInfo: runPages[runIndex], options, runPages, - summaryPageHAR, - medianRun, browser }) - .tap(() => Promise.resolve(Object.keys(runPages)) - .map((runIndex) => - this._renderUrlRunPage(url, runIndex, { - daurl: url, - daurlAlias, - runIndex, - pageInfo: runPages[runIndex], - options, - runPages, - browser - }))); - }); + ) + ); + }); // Aggregate/summarize data and write additional files return Promise.all(summaryRenders) .then(() => Promise.all(urlPageRenders)) - .then(() => this.storageManager.copyToResultDir(path.join(__dirname, 'assets'))) - .then(() => log.info('HTML stored in %s', this.storageManager.getBaseDir())); + .then(() => + this.storageManager.copyToResultDir(path.join(__dirname, 'assets')) + ) + .then(() => + log.info('HTML stored in %s', this.storageManager.getBaseDir()) + ); } _renderUrlPage(url, name, locals) { - const summaryTimestamp = get(locals, 'pageInfo.data.browsertime.pageSummary.timestamp', this.timestamp); - locals = merge({ - JSON: JSON, - rootPath: this.storageManager.rootPathFromUrl(url), - menu: 'pages', - pageTitle: `Summary for ${helpers.plural(this.options.browsertime.iterations,'run')} ${url} at ${summaryTimestamp}`, - pageDescription: `${metricHelper.getMetricsFromPageSummary(locals.pageInfo)} collected by sitespeed.io ${packageInfo.version}`, - headers: this.summary, - version: packageInfo.version, - timestamp: summaryTimestamp, - h: helpers, - context: this.context - }, locals); + const summaryTimestamp = get( + locals, + 'pageInfo.data.browsertime.pageSummary.timestamp', + this.timestamp + ); + locals = merge( + { + JSON: JSON, + rootPath: this.storageManager.rootPathFromUrl(url), + menu: 'pages', + pageTitle: `Summary for ${helpers.plural( + this.options.browsertime.iterations, + 'run' + )} ${url} at ${summaryTimestamp}`, + pageDescription: `${metricHelper.getMetricsFromPageSummary( + locals.pageInfo + )} collected by sitespeed.io ${packageInfo.version}`, + headers: this.summary, + version: packageInfo.version, + timestamp: summaryTimestamp, + h: helpers, + context: this.context + }, + locals + ); - return this.storageManager.writeHtmlForUrl(renderer.renderTemplate('url/' + name, locals), name + '.html', url); + return this.storageManager.writeHtmlForUrl( + renderer.renderTemplate('url/' + name, locals), + name + '.html', + url + ); } _renderUrlRunPage(url, name, locals) { - const runTimestamp = get(locals, 'pageInfo.data.browsertime.run.timestamp', this.timestamp); - locals = merge({ - urlLink: './index.html', - JSON: JSON, - rootPath: this.storageManager.rootPathFromUrl(url), - menu: 'pages', - pageTitle: `Run ${(parseInt(name) + 1)} for ${url} at ${runTimestamp}`, - pageDescription: `${metricHelper.getMetricsFromRun(locals.pageInfo)} collected by sitespeed.io ${packageInfo.version}`, - headers: this.summary, - version: packageInfo.version, - timestamp: runTimestamp, - h: helpers, - context: this.context - }, locals); + const runTimestamp = get( + locals, + 'pageInfo.data.browsertime.run.timestamp', + this.timestamp + ); + locals = merge( + { + urlLink: './index.html', + JSON: JSON, + rootPath: this.storageManager.rootPathFromUrl(url), + menu: 'pages', + pageTitle: `Run ${parseInt(name) + 1} for ${url} at ${runTimestamp}`, + pageDescription: `${metricHelper.getMetricsFromRun( + locals.pageInfo + )} collected by sitespeed.io ${packageInfo.version}`, + headers: this.summary, + version: packageInfo.version, + timestamp: runTimestamp, + h: helpers, + context: this.context + }, + locals + ); - return this.storageManager.writeHtmlForUrl(renderer.renderTemplate('url/run', locals), name + '.html', url); + return this.storageManager.writeHtmlForUrl( + renderer.renderTemplate('url/run', locals), + name + '.html', + url + ); } _renderSummaryPage(name, locals) { - locals = merge({ - rootPath: '', - menu: name, - pageTitle: name, - pageDescription: '', - headers: this.summary, - version: packageInfo.version, - timestamp: this.timestamp, - h: helpers, - context: this.context - }, locals); + locals = merge( + { + rootPath: '', + menu: name, + pageTitle: name, + pageDescription: '', + headers: this.summary, + version: packageInfo.version, + timestamp: this.timestamp, + h: helpers, + context: this.context + }, + locals + ); - return this.storageManager.writeHtml(name + '.html', renderer.renderTemplate(name, locals)); + return this.storageManager.writeHtml( + name + '.html', + renderer.renderTemplate(name, locals) + ); } } diff --git a/lib/plugins/html/metricHelper.js b/lib/plugins/html/metricHelper.js index 23b61709d..41079a429 100644 --- a/lib/plugins/html/metricHelper.js +++ b/lib/plugins/html/metricHelper.js @@ -6,11 +6,19 @@ module.exports = { // this configurable through the CLI // If we have SpeedIndex use that else backup with RUM SpeedIndex - const speedIndexMedian = get(pageInfo, 'data.browsertime.pageSummary.statistics.visualMetrics.SpeedIndex.median'); - const rumSpeedIndexMedian = get(pageInfo, 'data.browsertime.pageSummary.statistics.timings.rumSpeedIndex.median'); + const speedIndexMedian = get( + pageInfo, + 'data.browsertime.pageSummary.statistics.visualMetrics.SpeedIndex.median' + ); + const rumSpeedIndexMedian = get( + pageInfo, + 'data.browsertime.pageSummary.statistics.timings.rumSpeedIndex.median' + ); if (speedIndexMedian) { for (var run of runs) { - if (speedIndexMedian === run.data.browsertime.run.visualMetrics.SpeedIndex) { + if ( + speedIndexMedian === run.data.browsertime.run.visualMetrics.SpeedIndex + ) { return { name: 'SpeedIndex', runIndex: run.runIndex @@ -20,7 +28,11 @@ module.exports = { } else if (rumSpeedIndexMedian) { for (var rumRuns of runs) { // make sure we run Browsertime for that run = 3 runs WPT and 2 runs BT - if (rumRuns.data.browsertime && rumSpeedIndexMedian === rumRuns.data.browsertime.run.timings.rumSpeedIndex) { + if ( + rumRuns.data.browsertime && + rumSpeedIndexMedian === + rumRuns.data.browsertime.run.timings.rumSpeedIndex + ) { return { name: 'RUMSpeedIndex', runIndex: rumRuns.runIndex @@ -50,15 +62,23 @@ module.exports = { } }, getMetricsFromPageSummary(pageInfo) { - const visualMetrics = get(pageInfo, 'data.browsertime.pageSummary.statistics.visualMetrics'); - const timings = get(pageInfo, 'data.browsertime.pageSummary.statistics.timings'); + const visualMetrics = get( + pageInfo, + 'data.browsertime.pageSummary.statistics.visualMetrics' + ); + const timings = get( + pageInfo, + 'data.browsertime.pageSummary.statistics.timings' + ); if (visualMetrics) { - return `Median First Visual Change: ${visualMetrics.FirstVisualChange.median}, + return `Median First Visual Change: ${visualMetrics.FirstVisualChange + .median}, Median Speed Index: ${visualMetrics.SpeedIndex.median}, Median Visual Complete 85%: ${visualMetrics.VisualComplete85.median}, Median Last Visual Change: ${visualMetrics.LastVisualChange.median}`; } else if (timings) { - return `Median RUMSpeedIndex: ${timings.rumSpeedIndex.median}, Median Fully loaded: ${timings.fullyLoaded.median}`; + return `Median RUMSpeedIndex: ${timings.rumSpeedIndex + .median}, Median Fully loaded: ${timings.fullyLoaded.median}`; } else { return ''; } diff --git a/lib/plugins/html/renderer.js b/lib/plugins/html/renderer.js index a64e118f4..dfc1157f7 100644 --- a/lib/plugins/html/renderer.js +++ b/lib/plugins/html/renderer.js @@ -8,8 +8,7 @@ const basePath = path.resolve(__dirname, 'templates'); const templateCache = {}; function getTemplate(templateName) { - if (!templateName.endsWith('.pug')) - templateName = templateName + '.pug'; + if (!templateName.endsWith('.pug')) templateName = templateName + '.pug'; const template = templateCache[templateName]; if (template) { diff --git a/lib/plugins/influxdb/data-generator.js b/lib/plugins/influxdb/data-generator.js index b291a8387..55df71098 100644 --- a/lib/plugins/influxdb/data-generator.js +++ b/lib/plugins/influxdb/data-generator.js @@ -17,7 +17,12 @@ class InfluxDBDataGenerator { } dataFromMessage(message, time) { - function getTagsFromMessage(message, includeQueryParams, options, defaultTags) { + function getTagsFromMessage( + message, + includeQueryParams, + options, + defaultTags + ) { const tags = merge({}, defaultTags); let typeParts = message.type.split('.'); tags.origin = typeParts[0]; @@ -41,24 +46,41 @@ class InfluxDBDataGenerator { // if we get a URL type, add the URL if (message.url) { - const urlAndGroup = util.getURLAndGroup(options, message.group, message.url, includeQueryParams).split('.'); + const urlAndGroup = util + .getURLAndGroup( + options, + message.group, + message.url, + includeQueryParams + ) + .split('.'); tags.page = urlAndGroup[1]; tags.group = urlAndGroup[0]; } else if (message.group) { - // add the group of the summary message + // add the group of the summary message tags.group = util.toSafeKey(message.group); } return tags; } function getFieldAndSeriesName(key) { - const functions = ['min', 'p10', 'median', 'mean', 'avg', 'max', 'p90', 'p99', 'mdev']; + const functions = [ + 'min', + 'p10', + 'median', + 'mean', + 'avg', + 'max', + 'p90', + 'p99', + 'mdev' + ]; const keyArray = key.split('.'); const end = keyArray.pop(); if (functions.indexOf(end) > -1) { - return {field: end, seriesName: keyArray.pop()} + return { field: end, seriesName: keyArray.pop() }; } - return {field: 'value', seriesName: end}; + return { field: 'value', seriesName: end }; } function getAdditionalTags(key, type) { @@ -73,8 +95,7 @@ class InfluxDBDataGenerator { // pageTimings.serverResponseTime.max // visualMetrics.SpeedIndex.median tags.timings = keyArray[0]; - } - else if (type === 'browsertime.pageSummary') { + } else if (type === 'browsertime.pageSummary') { // statistics.timings.pageTimings.backEndTime.median // statistics.timings.rumSpeedIndex.median // statistics.timings.userTimings.marks.logoTime.median @@ -83,15 +104,13 @@ class InfluxDBDataGenerator { if (keyArray.length >= 5) { tags[keyArray[2]] = keyArray[3]; } - } - else if (type === 'browsertime.summary') { + } else if (type === 'browsertime.summary') { // firstPaint.median // userTimings.marks.logoTime.median if (key.indexOf('userTimings') > -1) { - tags[keyArray[0]] = keyArray[1]; + tags[keyArray[0]] = keyArray[1]; } - } - else if (type === 'coach.pageSummary') { + } else if (type === 'coach.pageSummary') { // advice.score // advice.performance.score if (keyArray.length > 2) { @@ -109,30 +128,28 @@ class InfluxDBDataGenerator { // data.median.firstView.breakdown.html.requests // data.median.firstView.breakdown.html.bytes if (key.indexOf('breakdown') > -1) { - tags.contentType = keyArray[4]; + tags.contentType = keyArray[4]; } } else if (type === 'webpagetest.summary') { // timing.firstView.SpeedIndex.median tags.view = keyArray[1]; // asset.firstView.breakdown.html.requests.median if (key.indexOf('breakdown') > -1) { - tags.contentType = keyArray[4]; + tags.contentType = keyArray[4]; } } else if (type === 'pagexray.summary') { // firstParty.requests.min pagexray.summary // requests.median // responseCodes.307.max pagexray.summary // requests.min pagexray.summary - if (key.indexOf('responseCodes') > -1 ) { + if (key.indexOf('responseCodes') > -1) { tags.responseCodes = 'response'; } if (key.indexOf('firstParty') > -1 || key.indexOf('thirdParty') > -1) { tags.party = keyArray[0]; } - - } - else if (type === 'pagexray.pageSummary') { + } else if (type === 'pagexray.pageSummary') { // thirdParty.contentTypes.json.requests pagexray.pageSummary // thirdParty.requests pagexray.pageSummary // firstParty.cookieStats.max pagexray.pageSummary @@ -142,34 +159,42 @@ class InfluxDBDataGenerator { if (key.indexOf('firstParty') > -1 || key.indexOf('thirdParty') > -1) { tags.party = keyArray[0]; } - if (key.indexOf('responseCodes') > -1 ) { + if (key.indexOf('responseCodes') > -1) { tags.responseCodes = 'response'; } - if (key.indexOf('contentTypes') > -1 ) { + if (key.indexOf('contentTypes') > -1) { tags.contentType = keyArray[2]; } - } - else { + } else { // console.log('Missed added tags to ' + key + ' ' + type); } return tags; } - return reduce(flatten.flattenMessageData(message), (entries, value, key) => { - const fieldAndSeriesName= getFieldAndSeriesName(key); - let tags = getTagsFromMessage(message, this.includeQueryParams, this.options, this.defaultTags); - tags = merge(getAdditionalTags(key, message.type), tags); - let point = { - time: time.valueOf() - } - point[fieldAndSeriesName.field] = value; - entries.push({ - tags, - seriesName: fieldAndSeriesName.seriesName, - point - }); - return entries; - }, []); + return reduce( + flatten.flattenMessageData(message), + (entries, value, key) => { + const fieldAndSeriesName = getFieldAndSeriesName(key); + let tags = getTagsFromMessage( + message, + this.includeQueryParams, + this.options, + this.defaultTags + ); + tags = merge(getAdditionalTags(key, message.type), tags); + let point = { + time: time.valueOf() + }; + point[fieldAndSeriesName.field] = value; + entries.push({ + tags, + seriesName: fieldAndSeriesName.seriesName, + point + }); + return entries; + }, + [] + ); } } diff --git a/lib/plugins/influxdb/index.js b/lib/plugins/influxdb/index.js index dbcb2f46a..c00fc88a4 100644 --- a/lib/plugins/influxdb/index.js +++ b/lib/plugins/influxdb/index.js @@ -19,7 +19,11 @@ const defaultConfig = { module.exports = { open(context, options) { throwIfMissing(options.influxdb, ['host', 'database'], 'influxdb'); - log.info('Setup InfluxDB host %s and database %s', options.influxdb.host, options.influxdb.database); + log.info( + 'Setup InfluxDB host %s and database %s', + options.influxdb.host, + options.influxdb.database + ); const opts = options.influxdb; this.options = options; @@ -29,11 +33,20 @@ module.exports = { this.dataGenerator = new DataGenerator(opts.includeQueryParams, options); }, processMessage(message) { - if (!(message.type.endsWith('.summary') || message.type.endsWith('.pageSummary'))) + if ( + !( + message.type.endsWith('.summary') || + message.type.endsWith('.pageSummary') + ) + ) return; // Let us skip this for a while and concentrate on the real deal - if (message.type.match(/(^largestassets|^slowestassets|^aggregateassets|^domains)/)) + if ( + message.type.match( + /(^largestassets|^slowestassets|^aggregateassets|^domains)/ + ) + ) return; // we only sends individual groups to Influx, not the @@ -43,8 +56,7 @@ module.exports = { } message = filterRegistry.filterMessage(message); - if (isEmpty(message.data)) - return; + if (isEmpty(message.data)) return; let data = this.dataGenerator.dataFromMessage(message, this.timestamp); @@ -52,13 +64,29 @@ module.exports = { return this.sender.send(data).then(() => { // make sure we only send once per URL (and browsertime is the most important) // and you need to configure a base URL where you get the HTML result - if (message.type === 'browsertime.pageSummary' && this.resultUrls.hasBaseUrl()) { - const resultPageUrl = this.resultUrls.absoluteSummaryPageUrl(message.url); - return sendAnnotations.send(this.options, message.group, message.url, resultPageUrl, this.timestamp); - }}) + if ( + message.type === 'browsertime.pageSummary' && + this.resultUrls.hasBaseUrl() + ) { + const resultPageUrl = this.resultUrls.absoluteSummaryPageUrl( + message.url + ); + return sendAnnotations.send( + this.options, + message.group, + message.url, + resultPageUrl, + this.timestamp + ); + } + }); } else { - return Promise.reject(new Error('No data to send to influxdb for message:\n' + - JSON.stringify(message, null, 2))); + return Promise.reject( + new Error( + 'No data to send to influxdb for message:\n' + + JSON.stringify(message, null, 2) + ) + ); } }, config: defaultConfig diff --git a/lib/plugins/influxdb/send-annotation.js b/lib/plugins/influxdb/send-annotation.js index 638d9abbf..944ac978d 100644 --- a/lib/plugins/influxdb/send-annotation.js +++ b/lib/plugins/influxdb/send-annotation.js @@ -14,20 +14,21 @@ module.exports = { // variables in Grafana. const connectivity = tsdbUtil.getConnectivity(options); const browser = options.browser; - const urlAndGroup = tsdbUtil.getURLAndGroup(options, group, url, options.influxdb.includeQueryParams).split('.'); + const urlAndGroup = tsdbUtil + .getURLAndGroup(options, group, url, options.influxdb.includeQueryParams) + .split('.'); let tags = `${connectivity},${browser},${urlAndGroup.join(',')}`; - const message = `Result ${options.browsertime.iterations} run(s)`; + const message = `Result ${options + .browsertime.iterations} run(s)`; const timestamp = Math.round(time.valueOf() / 1000); // if we have a category, let us send that category too if (options.influxdb.tags) { for (var row of options.influxdb.tags.split(',')) { const keyAndValue = row.split('='); - if (keyAndValue[0] === 'category') - tags += `,${keyAndValue[1]}`; + if (keyAndValue[0] === 'category') tags += `,${keyAndValue[1]}`; } } - const postData = - `events title="Sitespeed.io",text="${message}",tags="${tags}" ${timestamp}`; + const postData = `events title="Sitespeed.io",text="${message}",tags="${tags}" ${timestamp}`; const postOptions = { hostname: options.influxdb.host, port: options.influxdb.port, @@ -40,19 +41,26 @@ module.exports = { }; if (options.influxdb.username) { - postOptions.path = postOptions.path + '&' + querystring.stringify({ - u: options.influxdb.username, - p: options.influxdb.password - }); + postOptions.path = + postOptions.path + + '&' + + querystring.stringify({ + u: options.influxdb.username, + p: options.influxdb.password + }); } return new Promise((resolve, reject) => { log.verbose('Send annotation to Influx: %j', postData); // not perfect but maybe work for us const lib = options.influxdb.protocol === 'https' ? https : http; - const req = lib.request(postOptions, (res) => { + const req = lib.request(postOptions, res => { if (res.statusCode !== 204) { - log.error('Got %s from InfluxDB when sending annotation %s', res.statusCode, res.statusMessage); + log.error( + 'Got %s from InfluxDB when sending annotation %s', + res.statusCode, + res.statusMessage + ); reject(); } else { res.setEncoding('utf-8'); @@ -60,12 +68,12 @@ module.exports = { resolve(); } }); - req.on('error', (err) => { + req.on('error', err => { log.error('Got error from InfluxDB when sending annotation', err); - reject(err) + reject(err); }); req.write(postData); req.end(); - }) + }); } }; diff --git a/lib/plugins/influxdb/sender.js b/lib/plugins/influxdb/sender.js index 3fb720e56..d12f76be2 100644 --- a/lib/plugins/influxdb/sender.js +++ b/lib/plugins/influxdb/sender.js @@ -4,20 +4,27 @@ const Influx = require('influx'), Promise = require('bluebird'); class InfluxDBSender { - constructor({protocol, host, port, database, username, password}) { - this.client = new Influx.InfluxDB({protocol, host, port, database, username, password}); + constructor({ protocol, host, port, database, username, password }) { + this.client = new Influx.InfluxDB({ + protocol, + host, + port, + database, + username, + password + }); } send(data) { return Promise.resolve(data) - .map((point) => { + .map(point => { return { tags: point.tags, measurement: point.seriesName, fields: point.point - } + }; }) - .then((points) => this.client.writePoints(points)); + .then(points => this.client.writePoints(points)); } } diff --git a/lib/plugins/metrics/index.js b/lib/plugins/metrics/index.js index 9bb9af3db..53febe00d 100644 --- a/lib/plugins/metrics/index.js +++ b/lib/plugins/metrics/index.js @@ -17,8 +17,9 @@ module.exports = { }, postOpen() { if (this.options.filter) { - - const filters = Array.isArray(this.options.filter) ? this.options.filter : [this.options.filter]; + const filters = Array.isArray(this.options.filter) + ? this.options.filter + : [this.options.filter]; for (let metric of filters) { // for all filters @@ -26,18 +27,16 @@ module.exports = { // metrics are sent if (metric === '*+') { filterRegistry.clearAll(); - } - // all registered types will be set as unmatching, - // use it if you want to have a clean filter where - // all types are removed and then you can add your own - else if(metric === '*-') { + } else if (metric === '*-') { + // all registered types will be set as unmatching, + // use it if you want to have a clean filter where + // all types are removed and then you can add your own let types = filterRegistry.getTypes(); filterRegistry.clearAll(); for (let type of types) { filterRegistry.registerFilterForType('-', type); } - } - else { + } else { let parts = metric.split('.'); // the type is "always" the first two let type = parts.shift() + '.' + parts.shift(); @@ -52,11 +51,15 @@ module.exports = { } } } - }, processMessage(message) { if (this.options.list) { - if (!(message.type.endsWith('.summary') || message.type.endsWith('.pageSummary'))) + if ( + !( + message.type.endsWith('.summary') || + message.type.endsWith('.pageSummary') + ) + ) return; let flattenMess = flatten.flattenMessageData(message); for (let key of Object.keys(flattenMess)) { @@ -66,7 +69,10 @@ module.exports = { }, close() { if (this.options.list) { - this.storageManager.writeData('metrics.txt', Object.keys(this.metrics).join('\n')); + this.storageManager.writeData( + 'metrics.txt', + Object.keys(this.metrics).join('\n') + ); } if (this.options.filterList) { @@ -74,7 +80,7 @@ module.exports = { let filtersByType = filterRegistry.getFilters(); for (let type of Object.keys(filtersByType)) { for (let filters of filtersByType[type]) { - output+= type + '.' + filters + '\n'; + output += type + '.' + filters + '\n'; } } return this.storageManager.writeData('configuredMetrics.txt', output); diff --git a/lib/plugins/s3/index.js b/lib/plugins/s3/index.js index 2bb1274af..69add0d6e 100644 --- a/lib/plugins/s3/index.js +++ b/lib/plugins/s3/index.js @@ -10,8 +10,13 @@ const pick = require('lodash.pick'); Promise.promisifyAll(fs); function createS3Client(s3Options) { - const clientOptions = pick(s3Options, - ['maxAsyncS3', 's3RetryCount', 's3RetryDelay', 'multipartUploadThreshold', 'multipartUploadSize']); + const clientOptions = pick(s3Options, [ + 'maxAsyncS3', + 's3RetryCount', + 's3RetryDelay', + 'multipartUploadThreshold', + 'multipartUploadSize' + ]); clientOptions.s3Options = { accessKeyId: s3Options.key, @@ -46,8 +51,8 @@ module.exports = { Prefix: s3Options.path || this.storageManager.getStoragePrefix(), // Allow user to set default StorageClass on objects and default to STANDARD if not set - // Possible options [STANDARD | REDUCED_REDUNDANCY | STANDARD_IA] - StorageClass: s3Options.storageClass || 'STANDARD' + // Possible options [STANDARD | REDUCED_REDUNDANCY | STANDARD_IA] + StorageClass: s3Options.storageClass || 'STANDARD' // other options supported by putObject, except Body and ContentLength. // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property @@ -55,7 +60,9 @@ module.exports = { }; return new Promise((resolve, reject) => { - log.info(`Uploading ${baseDir} to S3 bucket ${s3Options.bucketname}, this can take a while ...`); + log.info( + `Uploading ${baseDir} to S3 bucket ${s3Options.bucketname}, this can take a while ...` + ); const uploader = client.uploadDir(params); @@ -65,12 +72,13 @@ module.exports = { }); uploader.on('end', () => { if (s3Options.removeLocalResult) { - fs.removeAsync(baseDir) + fs + .removeAsync(baseDir) .then(() => { log.info(`Removed local files and directory ${baseDir}`); resolve(); }) - .catch((e) => reject(e)); + .catch(e => reject(e)); } else { resolve(); } diff --git a/lib/plugins/screenshot/index.js b/lib/plugins/screenshot/index.js index a8292ab03..642822b33 100644 --- a/lib/plugins/screenshot/index.js +++ b/lib/plugins/screenshot/index.js @@ -7,13 +7,19 @@ function getImagesAndName(images) { return { data: image, name: index + '.png' - } + }; }); } function storeScreenshots(url, imagesAndName, storageManager) { - return Promise.map(imagesAndName, (screenshot) => - storageManager.writeDataForUrl(screenshot.data, screenshot.name, url, 'screenshots')); + return Promise.map(imagesAndName, screenshot => + storageManager.writeDataForUrl( + screenshot.data, + screenshot.name, + url, + 'screenshots' + ) + ); } module.exports = { @@ -24,7 +30,11 @@ module.exports = { processMessage(message) { switch (message.type) { case 'browsertime.screenshot': - return storeScreenshots(message.url, getImagesAndName(message.data), this.storageManager); + return storeScreenshots( + message.url, + getImagesAndName(message.data), + this.storageManager + ); } } }; diff --git a/lib/plugins/slack/attachements.js b/lib/plugins/slack/attachements.js index 3980f32b5..d6039f976 100644 --- a/lib/plugins/slack/attachements.js +++ b/lib/plugins/slack/attachements.js @@ -5,7 +5,7 @@ const h = require('../../support/helpers'); function getMetric(metric) { if (metric.median) { - return metric.median + ' ms' + ' (' + metric.max + ')' + return metric.median + ' ms' + ' (' + metric.max + ')'; } else { return metric; } @@ -22,35 +22,59 @@ module.exports = function(dataCollection, resultUrls, slackOptions) { const metrics = { firstPaint: { name: 'First paint', - metric: get(base.browsertime, 'pageSummary.statistics.timings.firstPaint') + metric: get( + base.browsertime, + 'pageSummary.statistics.timings.firstPaint' + ) }, speedIndex: { name: 'Speed Index', - metric: get(base.browsertime, 'pageSummary.statistics.visualMetrics.SpeedIndex') + metric: get( + base.browsertime, + 'pageSummary.statistics.visualMetrics.SpeedIndex' + ) }, firstVisualChange: { name: 'First Visual Change', - metric: get(base.browsertime, 'pageSummary.statistics.visualMetrics.FirstVisualChange') + metric: get( + base.browsertime, + 'pageSummary.statistics.visualMetrics.FirstVisualChange' + ) }, visualComplete85: { name: 'Visual Complete 85%', - metric: get(base.browsertime, 'pageSummary.statistics.visualMetrics.VisualComplete85') + metric: get( + base.browsertime, + 'pageSummary.statistics.visualMetrics.VisualComplete85' + ) }, lastVisualChange: { name: 'Last Visual Change', - metric: get(base.browsertime, 'pageSummary.statistics.visualMetrics.LastVisualChange') + metric: get( + base.browsertime, + 'pageSummary.statistics.visualMetrics.LastVisualChange' + ) }, fullyLoaded: { name: 'Fully Loaded', - metric: get(base.browsertime, 'pageSummary.statistics.timings.fullyLoaded') + metric: get( + base.browsertime, + 'pageSummary.statistics.timings.fullyLoaded' + ) }, domContentLoadedTime: { name: 'domContentLoadedTime', - metric: get(base.browsertime, 'pageSummary.statistics.timings.pageTimings.domContentLoadedTime') + metric: get( + base.browsertime, + 'pageSummary.statistics.timings.pageTimings.domContentLoadedTime' + ) }, rumSpeedIndex: { name: 'RUM Speed Index', - metric: get(base.browsertime, 'pageSummary.statistics.timings.rumSpeedIndex') + metric: get( + base.browsertime, + 'pageSummary.statistics.timings.rumSpeedIndex' + ) }, coachScore: { name: 'Coach score', @@ -74,7 +98,7 @@ module.exports = function(dataCollection, resultUrls, slackOptions) { title: metric.name, value: getMetric(metric.metric), short: true - }) + }); } } @@ -85,14 +109,14 @@ module.exports = function(dataCollection, resultUrls, slackOptions) { title: key + ' error', value: results.errors[key], short: false - }) + }); } } let color = 'good'; const limitMetric = metrics[slackOptions.limitMetric]; - // if we failed with a run, we don't have the metric and just make it dangerous + // if we failed with a run, we don't have the metric and just make it dangerous if (limitMetric === undefined) { color = 'danger'; } else { @@ -102,9 +126,8 @@ module.exports = function(dataCollection, resultUrls, slackOptions) { } else if (limitMetric.metric < slackOptions.limitWarning) { color = 'warning'; } - } - // SpeedIndex/firstVisualChange - else if (limitMetric.metric > slackOptions.limitError) { + } else if (limitMetric.metric > slackOptions.limitError) { + // SpeedIndex/firstVisualChange color = 'danger'; } else if (limitMetric.metric > slackOptions.limitWarning) { color = 'warning'; diff --git a/lib/plugins/slack/index.js b/lib/plugins/slack/index.js index d7f6fef79..b96c8b2bd 100644 --- a/lib/plugins/slack/index.js +++ b/lib/plugins/slack/index.js @@ -30,7 +30,7 @@ module.exports = { const slack = new Slack(slackOptions.hookUrl); const type = slackOptions.type; const pageErrors = []; - let logo = "https://www.sitespeed.io/img/slack/sitespeed-logo-slack.png"; + let logo = 'https://www.sitespeed.io/img/slack/sitespeed-logo-slack.png'; let channel = slackOptions.channel; if (channel && !channel.startsWith('#')) { @@ -46,7 +46,13 @@ module.exports = { let text = ''; if (['summary', 'all', 'error'].includes(type)) { - const sum = getSummary(this.data, pageErrors, this.resultUrls, this.context, options); + const sum = getSummary( + this.data, + pageErrors, + this.resultUrls, + this.context, + options + ); text += sum.summaryText + '\n' + sum.errorText; logo = sum.logo; } @@ -56,8 +62,12 @@ module.exports = { attachments = getAttachments(this.data, this.resultUrls, slackOptions); } - if (type === 'error' && pageErrors.length > 0 || type !== 'error') { - log.debug('Sending message to Slack channel %s and username %s', slackOptions.channel, slackOptions.userName); + if ((type === 'error' && pageErrors.length > 0) || type !== 'error') { + log.debug( + 'Sending message to Slack channel %s and username %s', + slackOptions.channel, + slackOptions.userName + ); return slack.sendAsync({ text, icon_url: logo, @@ -65,7 +75,7 @@ module.exports = { mrkdwn: true, username: slackOptions.userName, attachments - }) + }); } }, config: defaultConfig diff --git a/lib/plugins/slack/summary.js b/lib/plugins/slack/summary.js index 9206d1d62..3cec0e050 100644 --- a/lib/plugins/slack/summary.js +++ b/lib/plugins/slack/summary.js @@ -3,7 +3,13 @@ const util = require('util'); const get = require('lodash.get'); const h = require('../../support/helpers'); -module.exports = function(dataCollection, errors, resultUrls, context, options) { +module.exports = function( + dataCollection, + errors, + resultUrls, + context, + options +) { const base = dataCollection.summaryPages.index || {}; const metrics = { firstPaint: { @@ -12,7 +18,10 @@ module.exports = function(dataCollection, errors, resultUrls, context, options) }, domContentLoadedTime: { name: 'domContentLoadedTime', - metric: get(base.browsertime, 'summary.pageTimings.domContentLoadedTime.median') + metric: get( + base.browsertime, + 'summary.pageTimings.domContentLoadedTime.median' + ) }, speedIndex: { name: 'Speed Index', @@ -20,15 +29,24 @@ module.exports = function(dataCollection, errors, resultUrls, context, options) }, firstVisualChange: { name: 'First Visual Change', - metric: get(base.browsertime, 'summary.visualMetrics.FirstVisualChange.median') + metric: get( + base.browsertime, + 'summary.visualMetrics.FirstVisualChange.median' + ) }, visualComplete85: { name: 'Visual Complete 85%', - metric: get(base.browsertime, 'summary.visualMetrics.VisualComplete85.median') + metric: get( + base.browsertime, + 'summary.visualMetrics.VisualComplete85.median' + ) }, lastVisualChange: { name: 'Last Visual Change', - metric: get(base.browsertime, 'summary.visualMetrics.LastVisualChange.median') + metric: get( + base.browsertime, + 'summary.visualMetrics.LastVisualChange.median' + ) }, fullyLoaded: { name: 'Fully Loaded', @@ -44,9 +62,15 @@ module.exports = function(dataCollection, errors, resultUrls, context, options) } }; - let summaryText = `${h.plural(Object.keys(dataCollection.urlPages).length, 'page')} analyzed for ${h.short(context.name, 30)} ` + + let summaryText = + `${h.plural( + Object.keys(dataCollection.urlPages).length, + 'page' + )} analyzed for ${h.short(context.name, 30)} ` + `(${h.plural(options.browsertime.iterations, 'run')}, ` + - `${h.cap(options.browsertime.browser)}/${options.mobile ? 'mobile' : 'desktop'}/${options.connectivity})\n`; + `${h.cap(options.browsertime.browser)}/${options.mobile + ? 'mobile' + : 'desktop'}/${options.connectivity})\n`; let message = ''; if (resultUrls.hasBaseUrl()) { diff --git a/lib/plugins/text/index.js b/lib/plugins/text/index.js index 60c11bfe4..0f6bd5c64 100644 --- a/lib/plugins/text/index.js +++ b/lib/plugins/text/index.js @@ -11,7 +11,9 @@ module.exports = { close(options) { if (!options.summary) return; - const renderer = this.options.summaryDetail ? textBuilder.renderSummary : textBuilder.renderBriefSummary; + const renderer = this.options.summaryDetail + ? textBuilder.renderSummary + : textBuilder.renderBriefSummary; return renderer(this.dataCollection, this.context, options); } }; diff --git a/lib/plugins/text/textBuilder.js b/lib/plugins/text/textBuilder.js index 1e0b8eca7..c75741d3a 100644 --- a/lib/plugins/text/textBuilder.js +++ b/lib/plugins/text/textBuilder.js @@ -6,27 +6,32 @@ const table = require('text-table'), chunk = require('lodash.chunk'), h = require('../../support/helpers'), tableOpts = { - stringLength : color.getStrippedLength + stringLength: color.getStrippedLength }, drab = color.blackBright; - function getMarker(label) { - return {ok: '√', warning: '!', error: '✗', info: ''}[label] || ''; + return { ok: '√', warning: '!', error: '✗', info: '' }[label] || ''; } function getColor(label) { // ansi = {ok: 150, warning: 230, error: 217, info: ''}[b.label], - return {ok: 'green', warning: 'yellow', error: 'red', info: 'blackBright'}[label]; + return { ok: 'green', warning: 'yellow', error: 'red', info: 'blackBright' }[ + label + ]; } function getHeader(dataCollection, context, options) { const noPages = Object.keys(dataCollection.urlPages).length; - return drab([ - `${h.plural(noPages,'page')} analyzed for ${h.short(context.name, 30)} `, - `(${h.plural(options.browsertime.iterations, 'run')}, `, - `${h.cap(options.browsertime.browser)}/${options.mobile ? 'mobile' : 'desktop'}/${options.connectivity})` - ].join('')); + return drab( + [ + `${h.plural(noPages, 'page')} analyzed for ${h.short(context.name, 30)} `, + `(${h.plural(options.browsertime.iterations, 'run')}, `, + `${h.cap(options.browsertime.browser)}/${options.mobile + ? 'mobile' + : 'desktop'}/${options.connectivity})` + ].join('') + ); } function getBoxes(dataCollection) { @@ -36,7 +41,7 @@ function getBoxes(dataCollection) { // foo bar -> fb function abbr(str) { if (/total|overall/i.test(str)) return str.trim(); - return str.replace(/\w+\s?/g, (a) => a[0]); + return str.replace(/\w+\s?/g, a => a[0]); } function noop(a) { @@ -46,38 +51,40 @@ function noop(a) { module.exports = { renderSummary(dataCollection, context, options) { let out = getHeader(dataCollection, context, options); - let rows = getBoxes(dataCollection).map((b) => { + let rows = getBoxes(dataCollection).map(b => { var marker = getMarker(b.label), c = getColor(b.label); // color.xterm(ansi)(label), - return [ color[c](marker), color[c](b.name), color.bold(b.median) ]; + return [color[c](marker), color[c](b.name), color.bold(b.median)]; }); - rows.unshift(['', 'Score / Metric', 'Median'],['', '-------------', '------']); - options.summary = {out : `${out}\n` + table(rows, tableOpts)}; + rows.unshift( + ['', 'Score / Metric', 'Median'], + ['', '-------------', '------'] + ); + options.summary = { out: `${out}\n` + table(rows, tableOpts) }; }, renderBriefSummary(dataCollection, context, options) { let out = getHeader(dataCollection, context, options); var lines = [], scores = [], - size = '', reqs = '', rum = ''; - getBoxes(dataCollection).map((b) => { - var c = getColor(b.label), + size = '', + reqs = '', + rum = ''; + getBoxes(dataCollection).map(b => { + var c = getColor(b.label), val = b.median, name; c = color[c] || noop; if (/score$/i.test(b.url)) { - name = abbr(b.name.replace('score','')); + name = abbr(b.name.replace('score', '')); scores.push(c(`${name}:${val}`)); - } - else if ('pageSize' === b.url) { - val = val.replace(' ',''); // 10 KB -> 10KB + } else if ('pageSize' === b.url) { + val = val.replace(' ', ''); // 10 KB -> 10KB size = `${val}`; - } - else if (/total requests/i.test(b.name)) { + } else if (/total requests/i.test(b.name)) { reqs = `${val} reqs`; - } - else if (b.url === 'rumSpeedIndex') { + } else if (b.url === 'rumSpeedIndex') { name = abbr(b.name); rum = color.bold(`${name}: ${val}`); } diff --git a/lib/plugins/tracestorer/index.js b/lib/plugins/tracestorer/index.js index 14ece0c75..7cdf64e81 100644 --- a/lib/plugins/tracestorer/index.js +++ b/lib/plugins/tracestorer/index.js @@ -7,10 +7,15 @@ module.exports = { processMessage(message) { switch (message.type) { case 'browsertime.chrometrace': - case 'webpagetest.chrometrace': - { - return this.storageManager.writeDataForUrl(JSON.stringify(message.data, null, 0), message.name, message.url, '', true); - } + case 'webpagetest.chrometrace': { + return this.storageManager.writeDataForUrl( + JSON.stringify(message.data, null, 0), + message.name, + message.url, + '', + true + ); + } } } }; diff --git a/lib/plugins/webpagetest/aggregator.js b/lib/plugins/webpagetest/aggregator.js index 2496d6b1b..b6ba8ad6f 100644 --- a/lib/plugins/webpagetest/aggregator.js +++ b/lib/plugins/webpagetest/aggregator.js @@ -25,27 +25,47 @@ module.exports = { this.customGroups[group] = {}; } - forEach(wptData.data.runs, (run) => { + forEach(wptData.data.runs, run => { forEach(run, (viewData, viewName) => { - forEach(metrics, (metric) => - statsHelpers.pushGroupStats(this.timingStats, this.timingGroups[group], [viewName, metric], viewData[metric])); + forEach(metrics, metric => + statsHelpers.pushGroupStats( + this.timingStats, + this.timingGroups[group], + [viewName, metric], + viewData[metric] + ) + ); forEach(viewData.userTimes, (timingData, timingName) => - statsHelpers.pushGroupStats(this.timingStats, this.timingGroups[group], [viewName, timingName], timingData)); + statsHelpers.pushGroupStats( + this.timingStats, + this.timingGroups[group], + [viewName, timingName], + timingData + ) + ); forEach(viewData.breakdown, (contentType, typeName) => - forEach(['requests', 'bytes'], (property) => - statsHelpers.pushGroupStats(this.assetStats, this.assetGroups[group], [viewName, typeName, property], contentType[property]))); + forEach(['requests', 'bytes'], property => + statsHelpers.pushGroupStats( + this.assetStats, + this.assetGroups[group], + [viewName, typeName, property], + contentType[property] + ) + ) + ); - forEach(viewData.custom, (metricName) => { + forEach(viewData.custom, metricName => { if (!isNaN(viewData[metricName])) { - statsHelpers.pushGroupStats( + statsHelpers.pushGroupStats( this.customStats, this.customGroups[group], [viewName, 'custom', metricName], - viewData[metricName]) - } - }); + viewData[metricName] + ); + } + }); }); }); }, @@ -62,16 +82,22 @@ module.exports = { for (let group of Object.keys(this.timingGroups)) { if (!summary.groups[group]) summary.groups[group] = {}; - summary.groups[group].timing = this.summarizePerTimingType(this.timingGroups[group]); + summary.groups[group].timing = this.summarizePerTimingType( + this.timingGroups[group] + ); } for (let group of Object.keys(this.assetGroups)) { if (!summary.groups[group]) summary.groups[group] = {}; - summary.groups[group].asset = this.summarizePerAssetType(this.assetGroups[group]); + summary.groups[group].asset = this.summarizePerAssetType( + this.assetGroups[group] + ); } if (this.customGroups) { for (let group of Object.keys(this.customGroups)) { if (!summary.groups[group]) summary.groups[group] = {}; - summary.groups[group].custom = this.summarizePerCustomType(this.customGroups[group]); + summary.groups[group].custom = this.summarizePerCustomType( + this.customGroups[group] + ); } } return summary; @@ -81,24 +107,38 @@ module.exports = { forEach(type, (view, viewName) => forEach(view, (contentType, contentTypeName) => forEach(contentType, (stats, propertyName) => - statsHelpers.setStatsSummary(summary, [viewName, 'breakdown', contentTypeName, propertyName], stats)))); + statsHelpers.setStatsSummary( + summary, + [viewName, 'breakdown', contentTypeName, propertyName], + stats + ) + ) + ) + ); return summary; }, summarizePerTimingType(type) { const summary = {}; forEach(type, (view, viewName) => forEach(view, (stats, name) => - statsHelpers.setStatsSummary(summary, [viewName, name], stats))); + statsHelpers.setStatsSummary(summary, [viewName, name], stats) + ) + ); return summary; }, summarizePerCustomType(type) { const summary = {}; forEach(type, (view, viewName) => forEach(view, (metricName, name) => - forEach(metricName, (stats, propertyName) => { - statsHelpers.setStatsSummary(summary, [viewName, name, propertyName], stats) - } - ))); + forEach(metricName, (stats, propertyName) => { + statsHelpers.setStatsSummary( + summary, + [viewName, name, propertyName], + stats + ); + }) + ) + ); return summary; } }; diff --git a/lib/plugins/webpagetest/analyzer.js b/lib/plugins/webpagetest/analyzer.js index 28209da39..637ca5386 100644 --- a/lib/plugins/webpagetest/analyzer.js +++ b/lib/plugins/webpagetest/analyzer.js @@ -8,13 +8,11 @@ var fs = require('fs'), WebPageTest = require('webpagetest'), WPTAPIError = require('webpagetest/lib/helper').WPTAPIError; - Promise.promisifyAll(fs); Promise.promisifyAll(WebPageTest.prototype); module.exports = { analyzeUrl(url, storageManager, wptOptions) { - const wptClient = new WebPageTest(wptOptions.host, wptOptions.key); wptOptions.firstViewOnly = !wptOptions.includeRepeatView; let urlOrScript = url; @@ -26,90 +24,143 @@ module.exports = { // See https://github.com/sitespeedio/sitespeed.io/issues/1367 const options = clone(wptOptions); - return wptClient.runTestAsync(urlOrScript, options) - .then(function(data) { - const id = data.data.id; - log.info('Got %s analysed with id %s from %s', url, id, options.host); - log.verbose('Got JSON from WebPageTest :%:2j', data); + return wptClient.runTestAsync(urlOrScript, options).then(function(data) { + const id = data.data.id; + log.info('Got %s analysed with id %s from %s', url, id, options.host); + log.verbose('Got JSON from WebPageTest :%:2j', data); - // Something failed with WebPageTest but how should we handle that? - if (data.statusCode !== 200) { - log.error('The test got status code %s from WebPageTest with %s. Checkout %s to try to find the original reason.', data.statusCode, data.statusText, get(data, 'data.summary')); - } + // Something failed with WebPageTest but how should we handle that? + if (data.statusCode !== 200) { + log.error( + 'The test got status code %s from WebPageTest with %s. Checkout %s to try to find the original reason.', + data.statusCode, + data.statusText, + get(data, 'data.summary') + ); + } - const promises = []; - let har; - promises.push(wptClient.getHARDataAsync(id, {}).then((theHar => har = theHar)).catch(WPTAPIError, (error) => log.error('Couldnt get HAR data fir id %s %s', id, error))); + const promises = []; + let har; + promises.push( + wptClient + .getHARDataAsync(id, {}) + .then(theHar => (har = theHar)) + .catch(WPTAPIError, error => + log.error('Couldnt get HAR data fir id %s %s', id, error) + ) + ); - const traces = {}; - const views = ['firstView']; - if (!wptOptions.firstViewOnly) { - views.push('repeatView'); - } + const traces = {}; + const views = ['firstView']; + if (!wptOptions.firstViewOnly) { + views.push('repeatView'); + } - views.forEach(function(view) { - for (let j = 1; j < wptOptions.runs + 1; j++) { - // The WPT API wrapper mutates the options object, why ohh why?!?!?! - const screenShotOptions = { - run: j, - repeatView: view === 'repeatView' - }; - - const waterfallOptions = { - run: j, - repeatView: view === 'repeatView' - }; - - const connectionOptions = { - run: j, - chartType: 'connection', - repeatView: view === 'repeatView' - }; - - const timelineOptions = { - run: j, - repeatView: view === 'repeatView' - }; - - promises.push( - Promise.join(wptClient.getScreenshotImageAsync(id, screenShotOptions), j, view, - (result, index, view) => storageManager.writeDataForUrl(result, 'wpt-' + index + '-' + view + '.png', url, - 'screenshots')).catch(WPTAPIError, (error) => log.error('Couldnt get screenshot for id %s %s', id, error)) - ); - - promises.push( - Promise.join(wptClient.getWaterfallImageAsync(id, waterfallOptions), j, view, - (result, index, view) => { - return storageManager.writeDataForUrl(result, 'wpt-waterfall-' + index + '-' + view + '.png', url, - 'waterfall').catch(WPTAPIError, (error) => log.error('Couldnt get waterfall %s %s', id, error)) - }) - ); - - promises.push( - Promise.join(wptClient.getWaterfallImageAsync(id, connectionOptions), j, view, - (result, index, view) => storageManager.writeDataForUrl(result, 'wpt-waterfall-connection-' + index + '-' + view + - '.png', - url, 'waterfall')).catch(WPTAPIError, (error) => log.error('Couldnt get watetfall connection for id %s %s', id, error)) - ); - if (wptOptions.timeline) { - promises.push( - Promise.join(wptClient.getChromeTraceDataAsync(id, timelineOptions), j, view, - (result, index, view) => { - traces['trace-' + index + '-wpt-' + view] = result - }).catch(WPTAPIError, (error) => log.error('Couldnt get chrome trace for id %s %s',id, error)) - ); - } - - } - }); - return Promise.all(promises).then(() => { - const myResult = { - data: data.data, - har + views.forEach(function(view) { + for (let j = 1; j < wptOptions.runs + 1; j++) { + // The WPT API wrapper mutates the options object, why ohh why?!?!?! + const screenShotOptions = { + run: j, + repeatView: view === 'repeatView' }; - myResult.trace = traces; - return myResult; - }) + + const waterfallOptions = { + run: j, + repeatView: view === 'repeatView' + }; + + const connectionOptions = { + run: j, + chartType: 'connection', + repeatView: view === 'repeatView' + }; + + const timelineOptions = { + run: j, + repeatView: view === 'repeatView' + }; + + promises.push( + Promise.join( + wptClient.getScreenshotImageAsync(id, screenShotOptions), + j, + view, + (result, index, view) => + storageManager.writeDataForUrl( + result, + 'wpt-' + index + '-' + view + '.png', + url, + 'screenshots' + ) + ).catch(WPTAPIError, error => + log.error('Couldnt get screenshot for id %s %s', id, error) + ) + ); + + promises.push( + Promise.join( + wptClient.getWaterfallImageAsync(id, waterfallOptions), + j, + view, + (result, index, view) => { + return storageManager + .writeDataForUrl( + result, + 'wpt-waterfall-' + index + '-' + view + '.png', + url, + 'waterfall' + ) + .catch(WPTAPIError, error => + log.error('Couldnt get waterfall %s %s', id, error) + ); + } + ) + ); + + promises.push( + Promise.join( + wptClient.getWaterfallImageAsync(id, connectionOptions), + j, + view, + (result, index, view) => + storageManager.writeDataForUrl( + result, + 'wpt-waterfall-connection-' + index + '-' + view + '.png', + url, + 'waterfall' + ) + ).catch(WPTAPIError, error => + log.error( + 'Couldnt get watetfall connection for id %s %s', + id, + error + ) + ) + ); + if (wptOptions.timeline) { + promises.push( + Promise.join( + wptClient.getChromeTraceDataAsync(id, timelineOptions), + j, + view, + (result, index, view) => { + traces['trace-' + index + '-wpt-' + view] = result; + } + ).catch(WPTAPIError, error => + log.error('Couldnt get chrome trace for id %s %s', id, error) + ) + ); + } + } }); + return Promise.all(promises).then(() => { + const myResult = { + data: data.data, + har + }; + myResult.trace = traces; + return myResult; + }); + }); } }; diff --git a/lib/plugins/webpagetest/index.js b/lib/plugins/webpagetest/index.js index d303adede..7143f57cc 100644 --- a/lib/plugins/webpagetest/index.js +++ b/lib/plugins/webpagetest/index.js @@ -13,7 +13,7 @@ const WebPageTest = require('webpagetest'); const make = messageMaker('webpagetest').make; -const hostRegex = /^(https?:\/\/)?([^\/]*)/i; +const hostRegex = /^(https?:\/\/)?([^/]*)/i; const defaultWptHost = urlParser.parse(WebPageTest.defaultServer).host; const DEFAULT_PAGE_SUMMARY_METRICS = [ @@ -42,8 +42,11 @@ const DEFAULT_SUMMARY_METRICS = [ function addCustomMetric(result) { const customMetrics = get(result, 'data.median.firstView.custom'); if (customMetrics) { - for (const customMetric of customMetrics ) { - filterRegistry.addFilterForType('data.median.*.' + customMetric, 'webpagetest.pageSummary'); + for (const customMetric of customMetrics) { + filterRegistry.addFilterForType( + 'data.median.*.' + customMetric, + 'webpagetest.pageSummary' + ); } } } @@ -68,68 +71,97 @@ module.exports = { if (!options.key) { const host = hostRegex.exec(options.host); if (host && host[2] === defaultWptHost) { - throw new Error('webpagetest.key needs to be specified when using the public WebPageTest server.'); + throw new Error( + 'webpagetest.key needs to be specified when using the public WebPageTest server.' + ); } } - filterRegistry.registerFilterForType(DEFAULT_PAGE_SUMMARY_METRICS , 'webpagetest.pageSummary'); - filterRegistry.registerFilterForType(DEFAULT_SUMMARY_METRICS , 'webpagetest.summary'); + filterRegistry.registerFilterForType( + DEFAULT_PAGE_SUMMARY_METRICS, + 'webpagetest.pageSummary' + ); + filterRegistry.registerFilterForType( + DEFAULT_SUMMARY_METRICS, + 'webpagetest.summary' + ); }, processMessage(message, queue) { switch (message.type) { - case 'url': - - { + case 'url': { const url = message.url; const group = message.group; - return analyzer.analyzeUrl(url, this.storageManager, this.options) - .tap((result) => { + return analyzer + .analyzeUrl(url, this.storageManager, this.options) + .tap(result => { addCustomMetric(result); if (result.trace) { - forEach(result.trace, (value, key) => { - queue.postMessage(make('webpagetest.chrometrace', value, {url, group, name: key + '.json'})); - }); + forEach(result.trace, (value, key) => { + queue.postMessage( + make('webpagetest.chrometrace', value, { + url, + group, + name: key + '.json' + }) + ); + }); } - queue.postMessage(make('webpagetest.har', result.har, {url, group})); - forEach(result.data.runs, (run, runKey) => - queue.postMessage(make('webpagetest.run', run, { + queue.postMessage( + make('webpagetest.har', result.har, { url, group }) + ); + forEach(result.data.runs, (run, runKey) => + queue.postMessage( + make('webpagetest.run', run, { url, group, - runIndex: (parseInt(runKey) - 1) - })) - ); - const location = result.data.location.replace(':', '-').replace(' ', '-').toLowerCase(); - // There's no connectivity setup in the default config for WPT, make sure we catch that - const connectivity = get(result, 'data.connectivity', 'native').toLowerCase(); - queue.postMessage(make('webpagetest.pageSummary', result, { + runIndex: parseInt(runKey) - 1 + }) + ) + ); + const location = result.data.location + .replace(':', '-') + .replace(' ', '-') + .toLowerCase(); + // There's no connectivity setup in the default config for WPT, make sure we catch that + const connectivity = get( + result, + 'data.connectivity', + 'native' + ).toLowerCase(); + queue.postMessage( + make('webpagetest.pageSummary', result, { url, group, location, connectivity - })); - aggregator.addToAggregate(group, result, connectivity, location); - }) - .catch((err) => { - log.error('Error creating WebPageTest result ', err); - queue.postMessage(make('error', err, { + }) + ); + aggregator.addToAggregate(group, result, connectivity, location); + }) + .catch(err => { + log.error('Error creating WebPageTest result ', err); + queue.postMessage( + make('error', err, { url - })); - }) - } + }) + ); + }); + } - case 'summarize': - { - let summary = aggregator.summarize(); - if (summary && Object.keys(summary.groups).length > 0) { - for (let group of Object.keys(summary.groups)) { - queue.postMessage(make('webpagetest.summary', summary.groups[group], { + case 'summarize': { + let summary = aggregator.summarize(); + if (summary && Object.keys(summary.groups).length > 0) { + for (let group of Object.keys(summary.groups)) { + queue.postMessage( + make('webpagetest.summary', summary.groups[group], { connectivity: aggregator.connectivity, location: aggregator.location, group - })); - } + }) + ); } + } } } }, diff --git a/lib/sitespeed.js b/lib/sitespeed.js index 145337674..250bd429a 100644 --- a/lib/sitespeed.js +++ b/lib/sitespeed.js @@ -27,7 +27,7 @@ const budgetResult = { }; function hasFunctionFilter(functionName) { - return ((obj) => (typeof obj[functionName] === 'function')); + return obj => typeof obj[functionName] === 'function'; } function allInArray(sampleArray, referenceArray) { @@ -42,7 +42,7 @@ function runOptionalFunction(objects, fN) { } return Promise.resolve(objects) .filter(hasFunctionFilter(fN)) - .map((plugin) => Promise.resolve(plugin[fN].apply(plugin, args))); + .map(plugin => Promise.resolve(plugin[fN].apply(plugin, args))); } module.exports = { @@ -54,60 +54,88 @@ module.exports = { timestamp.utc(); } - const {storageManager, resultUrls} = resultsStorage(url, timestamp, options.outputFolder, options.resultBaseURL); + const { storageManager, resultUrls } = resultsStorage( + url, + timestamp, + options.outputFolder, + options.resultBaseURL + ); const dataCollection = new DataCollection(); - return storageManager.createDataDir('logs').then((logDir) => { - logging.configure(options, logDir); - }).then(() => { - if (log.isEnabledFor(log.VERBOSE)) { - Promise.longStackTraces(); - } - log.info('Versions OS: %s nodejs: %s sitespeed.io: %s browsertime: %s coach: %s', os.platform() + ' ' + os.release(), process.version, packageInfo.version, packageInfo.dependencies.browsertime, packageInfo.dependencies.webcoach); - }).then(() => { - return loader.parsePluginNames(options) - }).then((pluginNames) => { - const plugins = options.plugins; - if (plugins) { - pullAll(pluginNames, toArray(plugins.disable)); - pluginNames = union(pluginNames, toArray(plugins.load)); - - if (plugins.list) { - log.info('The following plugins are enabled: %s', pluginNames.join(', ')); + return storageManager + .createDataDir('logs') + .then(logDir => { + logging.configure(options, logDir); + }) + .then(() => { + if (log.isEnabledFor(log.VERBOSE)) { + Promise.longStackTraces(); } - } - // if we run without cli, we still want the default options - // in options to use it in output - if (allInArray(['browsertime'], pluginNames)) { - options.browsertime = merge({}, browsertimeConfig, options.browsertime); - } - if (allInArray(['webpagetest'], pluginNames)) { - options.webpagetest = merge({}, webpagetestConfig, options.webpagetest); - } - if (allInArray(['browsertime', 'coach'], pluginNames)) { - options.browsertime = merge({}, options.browsertime, { - coach: true - }); - } - if (allInArray(['browsertime', 'screenshot'], pluginNames)) { - options.browsertime = merge({}, options.browsertime, { - screenshot: true - }); - } - return pluginNames; - }) - .then((pluginNames) => { + log.info( + 'Versions OS: %s nodejs: %s sitespeed.io: %s browsertime: %s coach: %s', + os.platform() + ' ' + os.release(), + process.version, + packageInfo.version, + packageInfo.dependencies.browsertime, + packageInfo.dependencies.webcoach + ); + }) + .then(() => { + return loader.parsePluginNames(options); + }) + .then(pluginNames => { + const plugins = options.plugins; + if (plugins) { + pullAll(pluginNames, toArray(plugins.disable)); + pluginNames = union(pluginNames, toArray(plugins.load)); - return loader.loadPlugins(pluginNames) - .then((plugins) => { + if (plugins.list) { + log.info( + 'The following plugins are enabled: %s', + pluginNames.join(', ') + ); + } + } + // if we run without cli, we still want the default options + // in options to use it in output + if (allInArray(['browsertime'], pluginNames)) { + options.browsertime = merge( + {}, + browsertimeConfig, + options.browsertime + ); + } + if (allInArray(['webpagetest'], pluginNames)) { + options.webpagetest = merge( + {}, + webpagetestConfig, + options.webpagetest + ); + } + if (allInArray(['browsertime', 'coach'], pluginNames)) { + options.browsertime = merge({}, options.browsertime, { + coach: true + }); + } + if (allInArray(['browsertime', 'screenshot'], pluginNames)) { + options.browsertime = merge({}, options.browsertime, { + screenshot: true + }); + } + return pluginNames; + }) + .then(pluginNames => { + return loader.loadPlugins(pluginNames).then(plugins => { + let urlSources = [urlSource]; - let urlSources = [urlSource]; + const allPlugins = urlSources.concat(plugins), + queueHandler = new QueueHandler(plugins, options); - const allPlugins = urlSources.concat(plugins), - queueHandler = new QueueHandler(plugins, options); - - return runOptionalFunction(allPlugins, 'open', { + return runOptionalFunction( + allPlugins, + 'open', + { storageManager, resultUrls, dataCollection, @@ -115,14 +143,20 @@ module.exports = { budget: budgetResult, name: url, log - }, options) - .then(() => runOptionalFunction(allPlugins, 'postOpen', options)) - .then(() => queueHandler.run(urlSources)) - .tap((errors) => runOptionalFunction(allPlugins, 'close', options, errors)) - .tap((errors) => runOptionalFunction(allPlugins, 'postClose', options, errors)); - }) + }, + options + ) + .then(() => runOptionalFunction(allPlugins, 'postOpen', options)) + .then(() => queueHandler.run(urlSources)) + .tap(errors => + runOptionalFunction(allPlugins, 'close', options, errors) + ) + .tap(errors => + runOptionalFunction(allPlugins, 'postClose', options, errors) + ); + }); }) - .then((errors) => { + .then(errors => { log.info('Finished analysing %s', url); if (options.summary && options.summary.out) { console.log(options.summary.out); // eslint-disable-line no-console @@ -132,9 +166,9 @@ module.exports = { budgetResult }; }) - .catch((err) => { + .catch(err => { log.error(err); throw err; - }) + }); } }; diff --git a/lib/support/cli.js b/lib/support/cli.js index 0297368bb..e742cef04 100644 --- a/lib/support/cli.js +++ b/lib/support/cli.js @@ -28,7 +28,8 @@ module.exports.parseCommandLine = function parseCommandLine() { }) .option('verbose', { alias: 'v', - describe: 'Verbose mode prints progress messages to the console. Enter up to three times (-vvv)' + + describe: + 'Verbose mode prints progress messages to the console. Enter up to three times (-vvv)' + ' to increase the level of detail.', type: 'count' }) @@ -51,7 +52,16 @@ module.exports.parseCommandLine = function parseCommandLine() { .option('browsertime.connectivity.profile', { alias: ['c', 'connectivity'], default: browsertimeConfig.connectivity.profile, - choices: ['3g', '3gfast', '3gslow', '3gem', '2g', 'cable', 'native', 'custom'], + choices: [ + '3g', + '3gfast', + '3gslow', + '3gem', + '2g', + 'cable', + 'native', + 'custom' + ], describe: 'The connectivity profile.', group: 'Browser' }) @@ -81,20 +91,24 @@ module.exports.parseCommandLine = function parseCommandLine() { .option('browsertime.connectivity.engine', { default: browsertimeConfig.connectivity.engine, choices: ['tc', 'tsproxy', 'external'], - describe: 'The engine for connectivity. TC (Linux Traffic Control) needs tc work but will only setup upload and latency. Use external if you set the connectivity outside of Browsertime. The best way do to this is described in https://github.com/sitespeedio/browsertime#connectivity', + describe: + 'The engine for connectivity. TC (Linux Traffic Control) needs tc work but will only setup upload and latency. Use external if you set the connectivity outside of Browsertime. The best way do to this is described in https://github.com/sitespeedio/browsertime#connectivity', group: 'Browser' }) .option('browsertime.pageCompleteCheck', { - describe: 'Supply a Javascript that decides when the browser is finished loading the page and can start to collect metrics. The Javascript snippet is repeatedly queried to see if page has completed loading (indicated by the script returning true). Use it to fetch timings happening after the loadEventEnd.', + describe: + 'Supply a Javascript that decides when the browser is finished loading the page and can start to collect metrics. The Javascript snippet is repeatedly queried to see if page has completed loading (indicated by the script returning true). Use it to fetch timings happening after the loadEventEnd.', group: 'Browser' }) .option('browsertime.script', { - describe: 'Add custom Javascript that collect metrics and run after the page has finished loading. Note that --script can be passed multiple times if you want to collect multiple metrics. The metrics will automatically be pushed to the summary/detailed summary and each individual page + sent to Graphite/InfluxDB.', + describe: + 'Add custom Javascript that collect metrics and run after the page has finished loading. Note that --script can be passed multiple times if you want to collect multiple metrics. The metrics will automatically be pushed to the summary/detailed summary and each individual page + sent to Graphite/InfluxDB.', alias: ['script'], group: 'Browser' }) .option('browsertime.selenium.url', { - describe: 'Configure the path to the Selenium server when fetching timings using browsers. If not configured the supplied NodeJS/Selenium version is used.', + describe: + 'Configure the path to the Selenium server when fetching timings using browsers. If not configured the supplied NodeJS/Selenium version is used.', group: 'Browser' }) .option('browsertime.viewPort', { @@ -103,21 +117,25 @@ module.exports.parseCommandLine = function parseCommandLine() { group: 'Browser' }) .option('browsertime.userAgent', { - describe: 'The full User Agent string, defaults to the User Agent used by the browsertime.browser option.', + describe: + 'The full User Agent string, defaults to the User Agent used by the browsertime.browser option.', group: 'Browser' }) .option('browsertime.preScript', { alias: 'preScript', - describe: 'Selenium script(s) to run before you test your URL (use it for login, warm the cache, etc). Note that --preScript can be passed multiple times.', + describe: + 'Selenium script(s) to run before you test your URL (use it for login, warm the cache, etc). Note that --preScript can be passed multiple times.', group: 'Browser' }) .option('browsertime.postScript', { alias: 'postScript', - describe: 'Selenium script(s) to run after you test your URL (use it for logout etc). Note that --postScript can be passed multiple times.', + describe: + 'Selenium script(s) to run after you test your URL (use it for logout etc). Note that --postScript can be passed multiple times.', group: 'Browser' }) .option('browsertime.delay', { - describe: 'Delay between runs, in milliseconds. Use it if your web server needs to rest between runs :)', + describe: + 'Delay between runs, in milliseconds. Use it if your web server needs to rest between runs :)', group: 'Browser' }) .option('browsertime.speedIndex', { @@ -134,18 +152,21 @@ module.exports.parseCommandLine = function parseCommandLine() { }) .option('browsertime.preURL', { alias: 'preURL', - describe: 'A URL that will be accessed first by the browser before the URL that you wanna analyze. Use it to fill the cache.', + describe: + 'A URL that will be accessed first by the browser before the URL that you wanna analyze. Use it to fill the cache.', group: 'Browser' }) .option('browsertime.userTimingWhitelist', { alias: 'userTimingWhitelist', - describe: 'This option takes a regex that will whitelist which userTimings to capture in the results. All userTimings are captured by default. T', + describe: + 'This option takes a regex that will whitelist which userTimings to capture in the results. All userTimings are captured by default. T', group: 'Browser' }) .option('browsertime.firefox.preference', { - describe: 'Extra command line arguments to pass Firefox preferences by the format key:value ' + - 'To add multiple preferences, repeat --browsertime.firefox.preference once per argument.', - group: 'Browser' + describe: + 'Extra command line arguments to pass Firefox preferences by the format key:value ' + + 'To add multiple preferences, repeat --browsertime.firefox.preference once per argument.', + group: 'Browser' }) .option('browsertime.firefox.includeResponseBodies', { describe: 'Include response bodies in HAR when using Firefox.', @@ -153,8 +174,9 @@ module.exports.parseCommandLine = function parseCommandLine() { group: 'Browser' }) .option('browsertime.chrome.args', { - describe: 'Extra command line arguments to pass to the Chrome process (e.g. --no-sandbox). ' + - 'To add multiple arguments to Chrome, repeat --browsertime.chrome.args once per argument.', + describe: + 'Extra command line arguments to pass to the Chrome process (e.g. --no-sandbox). ' + + 'To add multiple arguments to Chrome, repeat --browsertime.chrome.args once per argument.', group: 'Browser' }) .option('browsertime.chrome.collectTracingEvents', { @@ -163,11 +185,13 @@ module.exports.parseCommandLine = function parseCommandLine() { group: 'Browser' }) .option('browsertime.chrome.android.package', { - describe: 'Run Chrome on your Android device. Set to com.android.chrome for default Chrome version. You need to run adb start-server before you start.', + describe: + 'Run Chrome on your Android device. Set to com.android.chrome for default Chrome version. You need to run adb start-server before you start.', group: 'Browser' }) .option('browsertime.chrome.android.deviceSerial', { - describe: 'Choose which device to use. If you do not set it, the first found device will be used.', + describe: + 'Choose which device to use. If you do not set it, the first found device will be used.', group: 'Browser' }) // legacy naming of collectTracingEvents @@ -187,15 +211,18 @@ module.exports.parseCommandLine = function parseCommandLine() { }) .option('browsertime.requestheader', { alias: 'r', - describe: 'Request header that will be added to the request. Add multiple instances to add multiple request headers. Only Chrome support for now.', + describe: + 'Request header that will be added to the request. Add multiple instances to add multiple request headers. Only Chrome support for now.', group: 'Browser' }) .option('browsertime.block', { - describe: 'Domain to block. Add multiple instances to add multiple domains that will be blocked. Only Chrome support for now.', + describe: + 'Domain to block. Add multiple instances to add multiple domains that will be blocked. Only Chrome support for now.', group: 'Browser' }) .option('browsertime.basicAuth', { - describe: 'Use it if your server is behind Basic Auth. Format: username@password (Only Chrome at the moment).', + describe: + 'Use it if your server is behind Basic Auth. Format: username@password (Only Chrome at the moment).', group: 'Browser', alias: 'basicAuth' }) @@ -209,12 +236,13 @@ module.exports.parseCommandLine = function parseCommandLine() { describe: 'Https proxy (host:port)', group: 'proxy' }) - /* + /* Crawler options */ - .option('crawler.depth', { + .option('crawler.depth', { alias: 'd', - describe: 'How deep to crawl (1=only one page, 2=include links from first page, etc.)', + describe: + 'How deep to crawl (1=only one page, 2=include links from first page, etc.)', group: 'Crawler' }) .option('crawler.maxPages', { @@ -235,17 +263,20 @@ module.exports.parseCommandLine = function parseCommandLine() { group: 'Graphite' }) .option('graphite.auth', { - describe: 'The Graphite user and password used for authentication. Format: user:password', - group: 'Graphite' + describe: + 'The Graphite user and password used for authentication. Format: user:password', + group: 'Graphite' }) .option('graphite.httpPort', { - describe: 'The Graphite port used to access the user interface and send annotations event', - default: 8080, - group: 'Graphite' + describe: + 'The Graphite port used to access the user interface and send annotations event', + default: 8080, + group: 'Graphite' }) .option('graphite.webHost', { - describe: 'The graphite-web host. If not specified graphite.host will be used.', - group: 'Graphite' + describe: + 'The graphite-web host. If not specified graphite.host will be used.', + group: 'Graphite' }) .option('graphite.namespace', { default: graphiteConfig.namespace, @@ -254,17 +285,18 @@ module.exports.parseCommandLine = function parseCommandLine() { }) .option('graphite.includeQueryParams', { default: graphiteConfig.includeQueryParams, - describe: 'Whether to include query parameters from the URL in the Graphite keys or not', + describe: + 'Whether to include query parameters from the URL in the Graphite keys or not', type: 'boolean', group: 'Graphite' }) .option('graphite.arrayTags', { default: false, type: 'boolean', - describe: 'Send the tags as array or a string. In Graphite 1.0 the tags is a array.', + describe: + 'Send the tags as array or a string. In Graphite 1.0 the tags is a array.', group: 'Graphite' }) - /** Plugins */ .option('plugins.list', { describe: 'List all configured plugins in the log.', @@ -273,12 +305,14 @@ module.exports.parseCommandLine = function parseCommandLine() { }) .option('plugins.disable', { type: 'array', - describe: 'Disable a plugin. Use it to disable generating html or screenshots.', + describe: + 'Disable a plugin. Use it to disable generating html or screenshots.', group: 'Plugins' }) .option('plugins.load', { type: 'array', - describe: 'Extra plugins that you want to run. Relative or absolute path to the plugin.', + describe: + 'Extra plugins that you want to run. Relative or absolute path to the plugin.', group: 'Plugins' }) /** Budget */ @@ -295,7 +329,6 @@ module.exports.parseCommandLine = function parseCommandLine() { describe: 'The output format of the budget.', group: 'Budget' }) - /** InfluxDB cli option */ @@ -328,12 +361,14 @@ module.exports.parseCommandLine = function parseCommandLine() { }) .option('influxdb.tags', { default: influxdbConfig.tags, - describe: 'A comma separated list of tags and values added to each metric', + describe: + 'A comma separated list of tags and values added to each metric', group: 'InfluxDB' }) .option('influxdb.includeQueryParams', { default: influxdbConfig.includeQueryParams, - describe: 'Whether to include query parameters from the URL in the InfluxDB keys or not', + describe: + 'Whether to include query parameters from the URL in the InfluxDB keys or not', type: 'boolean', group: 'InfluxDB' }) @@ -344,21 +379,22 @@ module.exports.parseCommandLine = function parseCommandLine() { group: 'Metrics' }) .option('metrics.filterList', { - describe: 'List all configured filters for metrics in the data folder (configuredMetrics.txt)', + describe: + 'List all configured filters for metrics in the data folder (configuredMetrics.txt)', type: 'boolean', default: metricsConfig.filterList, group: 'Metrics' }) .option('metrics.filter', { type: 'array', - describe: 'Add/change/remove filters for metrics. If you want to send all metrics, use: *+ . If you want to remove all current metrics and send only the coach score: *- coach.summary.score.*', + describe: + 'Add/change/remove filters for metrics. If you want to send all metrics, use: *+ . If you want to remove all current metrics and send only the coach score: *- coach.summary.score.*', group: 'Metrics' }) - - /* + /* WebPageTest cli options */ - .option('webpagetest.host', { + .option('webpagetest.host', { default: webPageTestConfig.host, describe: 'The domain of your WebPageTest instance.', group: 'WebPageTest' @@ -383,7 +419,8 @@ module.exports.parseCommandLine = function parseCommandLine() { group: 'WebPageTest' }) .option('webpagetest.custom', { - describe: 'Execute arbitrary Javascript at the end of a test to collect custom metrics.', + describe: + 'Execute arbitrary Javascript at the end of a test to collect custom metrics.', group: 'WebPageTest' }) .option('webpagetest.file', { @@ -415,7 +452,8 @@ module.exports.parseCommandLine = function parseCommandLine() { Slack options */ .option('slack.hookUrl', { - describe: 'WebHook url for the Slack team (check https://.slack.com/apps/manage/custom-integrations).', + describe: + 'WebHook url for the Slack team (check https://.slack.com/apps/manage/custom-integrations).', group: 'Slack' }) .option('slack.userName', { @@ -424,11 +462,13 @@ module.exports.parseCommandLine = function parseCommandLine() { group: 'Slack' }) .option('slack.channel', { - describe: 'The slack channel without the # (if something else than the default channel for your hook).', + describe: + 'The slack channel without the # (if something else than the default channel for your hook).', group: 'Slack' }) .option('slack.type', { - describe: 'Send summary for a run, metrics from all URLs, only on errors or all to Slack.', + describe: + 'Send summary for a run, metrics from all URLs, only on errors or all to Slack.', default: slackConfig.type, choices: ['summary', 'url', 'error', 'all'], group: 'Slack' @@ -465,8 +505,9 @@ module.exports.parseCommandLine = function parseCommandLine() { group: 's3' }) .option('s3.path', { - describe: 'Override the default folder path in the bucket where the results are uploaded. By default it\'s ' + - '"DOMAIN_OR_FILENAME/TIMESTAMP", or the name of the folder if --outputFolder is specified.', + describe: + "Override the default folder path in the bucket where the results are uploaded. By default it's " + + '"DOMAIN_OR_FILENAME/TIMESTAMP", or the name of the folder if --outputFolder is specified.', group: 's3' }) .option('s3.region', { @@ -474,7 +515,8 @@ module.exports.parseCommandLine = function parseCommandLine() { group: 's3' }) .option('s3.removeLocalResult', { - describe: 'Remove all the local result files after they have been uploaded to S3', + describe: + 'Remove all the local result files after they have been uploaded to S3', default: false, type: 'boolean', group: 's3' @@ -483,25 +525,29 @@ module.exports.parseCommandLine = function parseCommandLine() { Html options */ .option('html.showAllWaterfallSummary', { - describe: 'Set to true to show all waterfalls on page summary HTML report', + describe: + 'Set to true to show all waterfalls on page summary HTML report', default: false, type: 'boolean', group: 'HTML' }) .option('html.fetchHARFiles', { - describe: 'Set to true to load HAR files using fetch instead of including them in the HTML. Turn this on if serve your pages using a server.', + describe: + 'Set to true to load HAR files using fetch instead of including them in the HTML. Turn this on if serve your pages using a server.', default: false, type: 'boolean', group: 'HTML' }) .option('html.logDownloadLink', { - describe: 'Adds a link in the HTML so you easily can download the logs from the sitespeed.io run. If your server is public, be careful so you don\'t log passwords etc', + describe: + "Adds a link in the HTML so you easily can download the logs from the sitespeed.io run. If your server is public, be careful so you don't log passwords etc", default: false, type: 'boolean', group: 'HTML' }) .option('html.topListSize', { - describe: 'Maximum number of assets to include in each toplist in the toplist tab', + describe: + 'Maximum number of assets to include in each toplist in the toplist tab', default: 10, group: 'HTML' }) @@ -518,12 +564,14 @@ module.exports.parseCommandLine = function parseCommandLine() { group: 'text' }) .option('mobile', { - describe: 'Access pages as mobile a fake mobile device. Set UA and width/height. For Chrome it will use device Apple iPhone 6.', + describe: + 'Access pages as mobile a fake mobile device. Set UA and width/height. For Chrome it will use device Apple iPhone 6.', default: false, type: 'boolean' }) .option('resultBaseURL', { - describe: 'The base URL to the server serving the HTML result. In the format of https://result.sitespeed.io' + describe: + 'The base URL to the server serving the HTML result. In the format of https://result.sitespeed.io' }) .option('gzipHAR', { describe: 'Compress the HAR files with GZIP.', @@ -534,7 +582,8 @@ module.exports.parseCommandLine = function parseCommandLine() { describe: 'The folder where the result will be stored.' }) .option('firstParty', { - describe: 'A regex running against each request and categorize it as first vs third party URL. (ex: ".*sitespeed.*")' + describe: + 'A regex running against each request and categorize it as first vs third party URL. (ex: ".*sitespeed.*")' }) .option('utc', { describe: 'Use Coordinated Universal Time for timestamps', @@ -549,12 +598,14 @@ module.exports.parseCommandLine = function parseCommandLine() { if (typeof arg === 'object' && !Array.isArray(arg)) { if (arg.configPath) { arg.config = JSON.parse(fs.readFileSync(arg.configPath, 'utf8')); - } else if (arg.config){ + } else if (arg.config) { arg.config = JSON.parse(arg.config); } return arg; } else { - throw new Error('[ERROR] Something looks wrong with your budget configuration. Since sitespeed.io 4.4 you should pass the path to your budget file through the --budget.configPath flag instead of directly through the --budget flag.'); + throw new Error( + '[ERROR] Something looks wrong with your budget configuration. Since sitespeed.io 4.4 you should pass the path to your budget file through the --budget.configPath flag instead of directly through the --budget flag.' + ); } }) .coerce('webpagetest', function(arg) { @@ -562,7 +613,9 @@ module.exports.parseCommandLine = function parseCommandLine() { if (arg.script && fs.existsSync(arg.script)) { arg.script = fs.readFileSync(path.resolve(arg.script), 'utf8'); /* eslint no-console: off */ - console.log('[WARNING] Since sitespeed.io 4.4 you should pass the path to the script file through the --webpagetest.file flag (https://github.com/sitespeedio/sitespeed.io/pull/1445).'); + console.log( + '[WARNING] Since sitespeed.io 4.4 you should pass the path to the script file through the --webpagetest.file flag (https://github.com/sitespeedio/sitespeed.io/pull/1445).' + ); return arg; } @@ -578,27 +631,37 @@ module.exports.parseCommandLine = function parseCommandLine() { // .describe('browser', 'Specify browser') .wrap(yargs.terminalWidth()) // .check(validateInput) - .epilog('Read the docs at https://www.sitespeed.io/documentation/sitespeed.io/'); + .epilog( + 'Read the docs at https://www.sitespeed.io/documentation/sitespeed.io/' + ); const aliases = parsed.getOptions().alias, argv = parsed.argv; // aliases are long options -> short option - const aliasLookup = reduce(aliases, (lookup, value, key) => { - lookup.set(value[0], key); - return lookup; - }, new Map()); + const aliasLookup = reduce( + aliases, + (lookup, value, key) => { + lookup.set(value[0], key); + return lookup; + }, + new Map() + ); let explicitOptions = yargs.reset().argv; - explicitOptions = reduce(explicitOptions, (result, value, key) => { - if (aliasLookup.has(key)) { - const fullKey = aliasLookup.get(key); - result = set(result, fullKey, value); - } - result = set(result, key, value); - return result; - }, {}); + explicitOptions = reduce( + explicitOptions, + (result, value, key) => { + if (aliasLookup.has(key)) { + const fullKey = aliasLookup.get(key); + result = set(result, fullKey, value); + } + result = set(result, key, value); + return result; + }, + {} + ); if (argv.config) { const config = require(path.resolve(process.cwd(), argv.config)); @@ -606,9 +669,12 @@ module.exports.parseCommandLine = function parseCommandLine() { } if (argv.webpagetest.custom) { - argv.webpagetest.custom = fs.readFileSync(path.resolve(argv.webpagetest.custom), { - encoding: 'utf8' - }); + argv.webpagetest.custom = fs.readFileSync( + path.resolve(argv.webpagetest.custom), + { + encoding: 'utf8' + } + ); } if (argv.summaryDetail) argv.summary = true; diff --git a/lib/support/cliUtil.js b/lib/support/cliUtil.js index c5fd25876..f75a9cf11 100644 --- a/lib/support/cliUtil.js +++ b/lib/support/cliUtil.js @@ -6,7 +6,7 @@ const path = require('path'); module.exports = { getURLs(urls) { const allUrls = []; - urls = urls.map((url) => url.trim()); + urls = urls.map(url => url.trim()); for (let url of urls) { if (url.startsWith('http')) { @@ -17,7 +17,7 @@ module.exports = { const lines = fs.readFileSync(filePath).toString().split('\n'); for (let line of lines) { if (line.trim().length > 0) { - let lineArray = line.split(" ", 2); + let lineArray = line.split(' ', 2); let url = lineArray[0].trim(); if (url) { allUrls.push(url); @@ -34,9 +34,9 @@ module.exports = { } return allUrls; }, - getAliases(urls) { + getAliases(urls) { const urlMetaData = {}; - urls = urls.map((url) => url.trim()); + urls = urls.map(url => url.trim()); for (let url of urls) { if (url.startsWith('http')) { @@ -46,14 +46,15 @@ module.exports = { const lines = fs.readFileSync(filePath).toString().split('\n'); for (let line of lines) { if (line.trim().length > 0) { - let url, alias = null; - let lineArray = line.split(" ", 2); + let url, + alias = null; + let lineArray = line.split(' ', 2); url = lineArray[0].trim(); - if(lineArray[1]) { + if (lineArray[1]) { alias = lineArray[1].trim(); } - if(url && alias) { - urlMetaData[url] = {'alias' : alias}; + if (url && alias) { + urlMetaData[url] = { alias: alias }; } } } diff --git a/lib/support/dataCollection.js b/lib/support/dataCollection.js index a480fab5a..4ace86638 100644 --- a/lib/support/dataCollection.js +++ b/lib/support/dataCollection.js @@ -19,23 +19,30 @@ class DataCollection { } getValidPages() { - return reduce(this.urlPages, (validPages, urlInfo, url) => { - if (Object.keys(urlInfo.data).length > 0) { - validPages[url] = urlInfo; - } - return validPages; - }, {}); + return reduce( + this.urlPages, + (validPages, urlInfo, url) => { + if (Object.keys(urlInfo.data).length > 0) { + validPages[url] = urlInfo; + } + return validPages; + }, + {} + ); } getErrorPages() { - return reduce(this.urlPages, (errors, urlInfo, url) => { - if (urlInfo.errors) { - errors[url] = urlInfo.errors; - } - return errors; - }, {}); + return reduce( + this.urlPages, + (errors, urlInfo, url) => { + if (urlInfo.errors) { + errors[url] = urlInfo.errors; + } + return errors; + }, + {} + ); } - } module.exports = DataCollection; diff --git a/lib/support/filterRegistry.js b/lib/support/filterRegistry.js index 5650cb77e..a2a5f48b9 100644 --- a/lib/support/filterRegistry.js +++ b/lib/support/filterRegistry.js @@ -45,7 +45,10 @@ module.exports = { } const filteredMessage = clone(message); - filteredMessage.data = metricsFilter.filterMetrics(filteredMessage.data, filterConfig); + filteredMessage.data = metricsFilter.filterMetrics( + filteredMessage.data, + filterConfig + ); return filteredMessage; } }; diff --git a/lib/support/flattenMessage.js b/lib/support/flattenMessage.js index c3f3e0921..0efbb091a 100644 --- a/lib/support/flattenMessage.js +++ b/lib/support/flattenMessage.js @@ -11,12 +11,12 @@ function toSafeKey(key) { } module.exports = { - keypathFromUrl(url, includeQueryParams) - { + keypathFromUrl(url, includeQueryParams) { function flattenQueryParams(params) { - return Object.keys(params).reduce((result, key) => - joinNonEmpty([result, key, params[key]], '_'), - ''); + return Object.keys(params).reduce( + (result, key) => joinNonEmpty([result, key, params[key]], '_'), + '' + ); } url = urlParser.parse(url, !!includeQueryParams); @@ -24,18 +24,18 @@ module.exports = { let path = toSafeKey(url.pathname); if (includeQueryParams) { - path = joinNonEmpty([path, toSafeKey(flattenQueryParams(url.query))], '_'); + path = joinNonEmpty( + [path, toSafeKey(flattenQueryParams(url.query))], + '_' + ); } - const keys = [ - toSafeKey(url.hostname), - path - ]; + const keys = [toSafeKey(url.hostname), path]; return joinNonEmpty(keys, '.'); }, - flattenMessageData({data, type}) { + flattenMessageData({ data, type }) { function isNumeric(n) { return !isNaN(parseFloat(n)) && isFinite(n); } @@ -43,7 +43,7 @@ module.exports = { function recursiveFlatten(target, keyPrefix, value) { // super simple version to avoid flatten HAR and screenshot data if (keyPrefix.match(/(screenshots\.|har\.)/)) { - return; + return; } // Google is overloading User Timing marks @@ -55,58 +55,72 @@ module.exports = { // Google is overloading User Timing marks = the same using WebPageTest // See https://github.com/sitespeedio/browsertime/issues/257 if (keyPrefix.indexOf('userTimes.goog_') > -1) { - return; + return; } const valueType = typeof value; switch (valueType) { case 'number': - { - target[keyPrefix] = value; - } + { + target[keyPrefix] = value; + } break; case 'object': - { - if (value === null) { - break; + { + if (value === null) { + break; + } + + Object.keys(value).forEach(key => { + // Hey are you coming to the future from 1980s? Please don't + // look at this code, it's a ugly hack to make sure we can send assets + // to Graphite and don't send them with array position, instead + // use the url to generate the key + + if (type === 'pagexray.pageSummary' && keyPrefix === 'assets') { + recursiveFlatten( + target, + joinNonEmpty( + [keyPrefix, toSafeKey(value[key].url || key)], + '.' + ), + value[key] + ); + } else { + recursiveFlatten( + target, + joinNonEmpty([keyPrefix, toSafeKey(key)], '.'), + value[key] + ); + } + }); } - - Object.keys(value).forEach((key) => { - - // Hey are you coming to the future from 1980s? Please don't - // look at this code, it's a ugly hack to make sure we can send assets - // to Graphite and don't send them with array position, instead - // use the url to generate the key - - if (type === 'pagexray.pageSummary' && keyPrefix === 'assets') { - recursiveFlatten(target, joinNonEmpty([keyPrefix, toSafeKey(value[key].url || key)], '.'), value[key]); - } - else { - recursiveFlatten(target, joinNonEmpty([keyPrefix, toSafeKey(key)], '.'), value[key]); - } - }); - } break; case 'string': - { - if (isNumeric(value)) { - target[keyPrefix] = parseFloat(value); + { + if (isNumeric(value)) { + target[keyPrefix] = parseFloat(value); + } } - } break; case 'boolean': - { - target[keyPrefix] = value ? 1 : 0; - } + { + target[keyPrefix] = value ? 1 : 0; + } break; case 'undefined': - { - // take care of faulty values, add a log in the future - } + { + // take care of faulty values, add a log in the future + } break; default: - throw new Error('Unhandled value type ' + valueType + ' found when flattening data for prefix ' + keyPrefix); + throw new Error( + 'Unhandled value type ' + + valueType + + ' found when flattening data for prefix ' + + keyPrefix + ); } } diff --git a/lib/support/helpers/cap.js b/lib/support/helpers/cap.js index b709869a1..c616349b3 100644 --- a/lib/support/helpers/cap.js +++ b/lib/support/helpers/cap.js @@ -1,5 +1,5 @@ 'use strict'; -module.exports = function ( word ) { - return word.substr( 0, 1 ).toUpperCase() + word.substr( 1 ); +module.exports = function(word) { + return word.substr(0, 1).toUpperCase() + word.substr(1); }; diff --git a/lib/support/helpers/get.js b/lib/support/helpers/get.js index 347ae8806..f7d929561 100644 --- a/lib/support/helpers/get.js +++ b/lib/support/helpers/get.js @@ -8,5 +8,5 @@ module.exports = function(object, property, defaultValue) { if (!object) { return defaultValue; } - return get(object, property, defaultValue) + return get(object, property, defaultValue); }; diff --git a/lib/support/helpers/index.js b/lib/support/helpers/index.js index 708f06157..5f71cb993 100644 --- a/lib/support/helpers/index.js +++ b/lib/support/helpers/index.js @@ -1,14 +1,14 @@ 'use strict'; module.exports = { - size: require( './size' ), + size: require('./size'), cap: require('./cap'), - time: require( './time' ), - plural: require( './plural' ), - scoreLabel: require( './scoreLabel' ), - label: require( './label' ), - get: require( './get' ), - short: require( './short'), - shortAsset: require( './shortAsset'), - noop: require( './noop' ) + time: require('./time'), + plural: require('./plural'), + scoreLabel: require('./scoreLabel'), + label: require('./label'), + get: require('./get'), + short: require('./short'), + shortAsset: require('./shortAsset'), + noop: require('./noop') }; diff --git a/lib/support/helpers/label.js b/lib/support/helpers/label.js index 6b0716efd..0b54c1ecb 100644 --- a/lib/support/helpers/label.js +++ b/lib/support/helpers/label.js @@ -4,8 +4,7 @@ module.exports = function(value, ok, warning) { value = value || 0; if (value > ok) { return 'ok'; - } - else if (value > warning) { + } else if (value > warning) { return 'warning'; } return 'error'; diff --git a/lib/support/helpers/plural.js b/lib/support/helpers/plural.js index 21938ae5b..6d74ff964 100644 --- a/lib/support/helpers/plural.js +++ b/lib/support/helpers/plural.js @@ -1,8 +1,8 @@ 'use strict'; -module.exports = function ( number, text ) { - if ( number === 0 || number > 1 ) { +module.exports = function(number, text) { + if (number === 0 || number > 1) { text += 's'; - } - return '' + number + ' ' + text; + } + return '' + number + ' ' + text; }; diff --git a/lib/support/helpers/scoreLabel.js b/lib/support/helpers/scoreLabel.js index ec29b3ecc..8350695c7 100644 --- a/lib/support/helpers/scoreLabel.js +++ b/lib/support/helpers/scoreLabel.js @@ -4,8 +4,7 @@ module.exports = function(value) { value = value || 0; if (value > 90) { return 'ok'; - } - else if (value > 80) { + } else if (value > 80) { return 'warning'; } return 'error'; diff --git a/lib/support/helpers/short.js b/lib/support/helpers/short.js index 08827a44d..3929dfeb3 100644 --- a/lib/support/helpers/short.js +++ b/lib/support/helpers/short.js @@ -1,8 +1,8 @@ 'use strict'; -module.exports = function ( text, number ) { - if ( text.length > number ) { - return text.slice(0,number) + '...'; - } - return text; +module.exports = function(text, number) { + if (text.length > number) { + return text.slice(0, number) + '...'; + } + return text; }; diff --git a/lib/support/helpers/shortAsset.js b/lib/support/helpers/shortAsset.js index 3dd7b6f97..12a0106e3 100644 --- a/lib/support/helpers/shortAsset.js +++ b/lib/support/helpers/shortAsset.js @@ -3,7 +3,7 @@ module.exports = function(url) { if (url.length > 40) { let shortUrl = url.replace(/\?.*/, ''); - url = (shortUrl.substr(0, 20) + '...' + shortUrl.substr(-17)); + url = shortUrl.substr(0, 20) + '...' + shortUrl.substr(-17); } return url; }; diff --git a/lib/support/helpers/size.js b/lib/support/helpers/size.js index 907bd83aa..f5a7d9ae5 100644 --- a/lib/support/helpers/size.js +++ b/lib/support/helpers/size.js @@ -1,16 +1,15 @@ 'use strict'; -const KB = 1024, MB = 1024 * 1024; +const KB = 1024, + MB = 1024 * 1024; module.exports = { asKb(bytes) { - if (!bytes || bytes < 0) - return 0; + if (!bytes || bytes < 0) return 0; return Number(bytes / KB).toFixed(1); }, format(bytes) { - if (!bytes || bytes < 0) - return 'N/A'; + if (!bytes || bytes < 0) return 'N/A'; if (bytes < KB) { return Number(bytes) + ' B'; diff --git a/lib/support/helpers/time.js b/lib/support/helpers/time.js index e125aa76c..e66a2238a 100644 --- a/lib/support/helpers/time.js +++ b/lib/support/helpers/time.js @@ -13,7 +13,7 @@ module.exports = { secondsPerDay = 60 * 60 * 24, secondsPerHour = 60 * 60, secondsPerMinute = 60, - sign = (seconds < 0) ? '-' : ''; + sign = seconds < 0 ? '-' : ''; if (seconds < 0) { seconds = Math.abs(seconds); @@ -34,6 +34,6 @@ module.exports = { } }, ms(ms) { - return ms + ' ms'; + return ms + ' ms'; } }; diff --git a/lib/support/logging.js b/lib/support/logging.js index 516b82ec4..47f0bc47d 100644 --- a/lib/support/logging.js +++ b/lib/support/logging.js @@ -27,23 +27,23 @@ module.exports.configure = function configure(options, logDir) { if (level === log.INFO) { log.basicConfig({ - 'format': '[%(date)s] %(levelname)s: %(message)s', - 'level': level + format: '[%(date)s] %(levelname)s: %(message)s', + level: level }); } else { log.basicConfig({ - 'format': '[%(date)s] %(levelname)s: [%(name)s] %(message)s', - 'level': level + format: '[%(date)s] %(levelname)s: [%(name)s] %(message)s', + level: level }); } - log.addHandler(new log.handlers.File( - { + log.addHandler( + new log.handlers.File({ file: logDir + '/sitespeed.io.log', formatter: new log.Formatter({ - 'format': '[%(date)s] %(levelname)s: [%(name)s] %(message)s', - 'level': level + format: '[%(date)s] %(levelname)s: [%(name)s] %(message)s', + level: level }) - } - )); + }) + ); }; diff --git a/lib/support/messageMaker.js b/lib/support/messageMaker.js index 0a5c489cc..2cbe25cee 100644 --- a/lib/support/messageMaker.js +++ b/lib/support/messageMaker.js @@ -10,7 +10,7 @@ module.exports = function messageMaker(source) { const timestamp = moment().format(), uuid = makeUuid(); - return merge({uuid, type, timestamp, source, data}, extras); + return merge({ uuid, type, timestamp, source, data }, extras); } }; }; diff --git a/lib/support/metricsFilter.js b/lib/support/metricsFilter.js index 78c114109..57e076a60 100644 --- a/lib/support/metricsFilter.js +++ b/lib/support/metricsFilter.js @@ -8,8 +8,7 @@ const toArray = require('./util').toArray, reduce = require('lodash.reduce'); function normalizePath(path) { - if (path.endsWith('.*')) - return path.slice(0, -2); + if (path.endsWith('.*')) return path.slice(0, -2); return path; } @@ -31,8 +30,7 @@ module.exports = { */ filterMetrics(json, metricPaths) { metricPaths = toArray(metricPaths); - if (typeof json !== 'object') - return undefined; + if (typeof json !== 'object') return undefined; return metricPaths.reduce((result, path) => { path = normalizePath(path); @@ -44,21 +42,23 @@ module.exports = { } else if (firstWildcard === 0) { const leafPath = path.substring(2); - reduce((json), (result, value, key) => { - if (typeof value === 'object') { - const leaf = this.filterMetrics(value, leafPath); + reduce( + json, + (result, value, key) => { + if (typeof value === 'object') { + const leaf = this.filterMetrics(value, leafPath); - if (!isEmpty(leaf)) { - result[key] = leaf; + if (!isEmpty(leaf)) { + result[key] = leaf; + } } - } - return result; - }, result); + return result; + }, + result + ); } else { let branchPath = path.substring(0, firstWildcard); - if (branchPath.endsWith('.')) - branchPath = branchPath.slice(0, -1); - + if (branchPath.endsWith('.')) branchPath = branchPath.slice(0, -1); let branch = get(json, branchPath); const leafPath = path.substring(firstWildcard + 2); diff --git a/lib/support/pluginLoader.js b/lib/support/pluginLoader.js index a0503af00..3893795da 100644 --- a/lib/support/pluginLoader.js +++ b/lib/support/pluginLoader.js @@ -6,7 +6,20 @@ const Promise = require('bluebird'), Promise.promisifyAll(fs); -const defaultPlugins = new Set(['browsertime', 'coach', 'datacollector', 'domains', 'assets', 'html', 'screenshot','metrics', 'text', 'harstorer', 'budget', 'tracestorer']); +const defaultPlugins = new Set([ + 'browsertime', + 'coach', + 'datacollector', + 'domains', + 'assets', + 'html', + 'screenshot', + 'metrics', + 'text', + 'harstorer', + 'budget', + 'tracestorer' +]); const pluginsDir = path.join(__dirname, '..', 'plugins'); @@ -15,8 +28,10 @@ module.exports = { // if we don't use the cli, this will work out fine as long // we configure only what we need const possibleConfiguredPlugins = options.explicitOptions || options; - const isDefaultOrConfigured = (name) => (defaultPlugins.has(name) || typeof possibleConfiguredPlugins[name] === 'object'); - const addMessageLoggerIfDebug = (pluginNames) => { + const isDefaultOrConfigured = name => + defaultPlugins.has(name) || + typeof possibleConfiguredPlugins[name] === 'object'; + const addMessageLoggerIfDebug = pluginNames => { if (options.debug) { // Need to make sure logger is first, so message logs appear // before messages are handled by other plugins @@ -25,30 +40,31 @@ module.exports = { return pluginNames; }; - return fs.readdirAsync(pluginsDir) - .map((name) => path.basename(name, '.js')) - .then((builtins) => { + return fs + .readdirAsync(pluginsDir) + .map(name => path.basename(name, '.js')) + .then(builtins => { let plugins = builtins.filter(isDefaultOrConfigured); return addMessageLoggerIfDebug(plugins); }); }, loadPlugins(pluginNames) { - return Promise.resolve(pluginNames).map((name) => { - try { - const plugin = require(path.join(pluginsDir, name)); - if (!plugin.name) { - plugin.name = () => name; - } - return plugin; - } catch (err) { - try { - return require(path.resolve(process.cwd(), name)); - } catch(error) { - console.error('Couldn\'t load plugin %s: %s', name, err); // eslint-disable-line no-console - // if it fails here, let it fail hard - throw error; + return Promise.resolve(pluginNames).map(name => { + try { + const plugin = require(path.join(pluginsDir, name)); + if (!plugin.name) { + plugin.name = () => name; + } + return plugin; + } catch (err) { + try { + return require(path.resolve(process.cwd(), name)); + } catch (error) { + console.error("Couldn't load plugin %s: %s", name, err); // eslint-disable-line no-console + // if it fails here, let it fail hard + throw error; + } } - } - }); - } + }); + } }; diff --git a/lib/support/queueHandler.js b/lib/support/queueHandler.js index 972c97662..b814a3a8e 100644 --- a/lib/support/queueHandler.js +++ b/lib/support/queueHandler.js @@ -32,14 +32,23 @@ function validateMessageFormat(message) { typeDepth = typeParts.length; if (typeDepth > 2) - throw new Error('Message type has too many dot separated sections: ' + message.type); + throw new Error( + 'Message type has too many dot separated sections: ' + message.type + ); const previousDepth = messageTypeDepths[baseType]; if (previousDepth && previousDepth !== typeDepth) { - throw new Error(util.format('All messages of type %s must have the same structure. ' + - '%s has %d part(s), but earlier messages had %d part(s).', - baseType, message.type, typeDepth, previousDepth)); + throw new Error( + util.format( + 'All messages of type %s must have the same structure. ' + + '%s has %d part(s), but earlier messages had %d part(s).', + baseType, + message.type, + typeDepth, + previousDepth + ) + ); } messageTypeDepths[baseType] = typeDepth; @@ -48,11 +57,18 @@ function validateMessageFormat(message) { function validateSummaryMessages(message) { const type = message.type; if (type.endsWith('.summary') && message.url) { - throw new Error(util.format('Summary message (%s) shouldn\'t be url specific, use .pageSummary instead.', type)); + throw new Error( + util.format( + "Summary message (%s) shouldn't be url specific, use .pageSummary instead.", + type + ) + ); } if (type.endsWith('.pageSummary') && !message.url) { - throw new Error(util.format('Page summary message (%s) failed to specify a url', type)); + throw new Error( + util.format('Page summary message (%s) failed to specify a url', type) + ); } } @@ -70,26 +86,27 @@ class QueueHandler { createQueues(plugins) { this.queues = plugins - .filter((plugin) => plugin.processMessage) - .map((plugin) => { + .filter(plugin => plugin.processMessage) + .map(plugin => { const concurrency = plugin.concurrency || Infinity; - const queue = cq() - .limit({concurrency}); + const queue = cq().limit({ concurrency }); queue.plugin = plugin; const messageWaitingStart = {}, messageProcessingStart = {}; - queue.enqueued((obj) => { + queue.enqueued(obj => { const message = obj.item; messageWaitingStart[message.uuid] = process.hrtime(); }); - queue.processingStarted((obj) => { + queue.processingStarted(obj => { const message = obj.item; - const waitingDuration = process.hrtime(messageWaitingStart[message.uuid]), + const waitingDuration = process.hrtime( + messageWaitingStart[message.uuid] + ), waitingNanos = waitingDuration[0] * 1e9 + waitingDuration[1]; queueStats.registerQueueTime(message, queue.plugin, waitingNanos); @@ -97,14 +114,16 @@ class QueueHandler { messageProcessingStart[message.uuid] = process.hrtime(); }); - // FIXME handle rejections (i.e. failures while processing messages) properly - queue.processingEnded((obj) => { + queue.processingEnded(obj => { const message = obj.item; const err = obj.err; if (err) { - let rejectionMessage = 'Rejected ' + JSON.stringify(message, shortenData, 2) + - ' for plugin: ' + plugin.name(); + let rejectionMessage = + 'Rejected ' + + JSON.stringify(message, shortenData, 2) + + ' for plugin: ' + + plugin.name(); if (message && message.url) rejectionMessage += ', url: ' + message.url; @@ -115,18 +134,25 @@ class QueueHandler { this.errors.push(rejectionMessage + '\n' + JSON.stringify(err)); } - const processingDuration = process.hrtime(messageWaitingStart[message.uuid]); - const processingNanos = processingDuration[0] * 1e9 + processingDuration[1]; + const processingDuration = process.hrtime( + messageWaitingStart[message.uuid] + ); + const processingNanos = + processingDuration[0] * 1e9 + processingDuration[1]; - queueStats.registerProcessingTime(message, queue.plugin, processingNanos); + queueStats.registerProcessingTime( + message, + queue.plugin, + processingNanos + ); }); - return {plugin, queue}; + return { plugin, queue }; }); } run(sources) { - return Promise.map(sources, (source) => source.findUrls(this)) + return Promise.map(sources, source => source.findUrls(this)) .then(() => this.startProcessingQueues()) .then(() => this.drainAllQueues()) .then(() => this.postMessage(make('summarize'))) @@ -150,20 +176,25 @@ class QueueHandler { } startProcessingQueues() { - return Promise.each(this.queues, (item) => { - const queue = item.queue, plugin = item.plugin; - queue.process((message) => Promise.resolve(plugin.processMessage(message, this))); + return Promise.each(this.queues, item => { + const queue = item.queue, + plugin = item.plugin; + queue.process(message => + Promise.resolve(plugin.processMessage(message, this)) + ); }); } drainAllQueues() { const queues = this.queues; - return new Promise((resolve) => { - queues.forEach((item) => item.queue.drained(() => { - if (queues.every((item) => item.queue.isDrained)) { - resolve(); - } - })); + return new Promise(resolve => { + queues.forEach(item => + item.queue.drained(() => { + if (queues.every(item => item.queue.isDrained)) { + resolve(); + } + }) + ); }); } } diff --git a/lib/support/queueStatistics.js b/lib/support/queueStatistics.js index e01b6ecc3..0d7c61558 100644 --- a/lib/support/queueStatistics.js +++ b/lib/support/queueStatistics.js @@ -34,20 +34,48 @@ module.exports = { includeSum: true }; - const byPluginName = Array.from(pluginNames).reduce((summary, pluginName) => { - set(summary, ['queueTime', pluginName], - stats.summarizeStats(get(queueTimeByPluginName, pluginName), statOptions)); - set(summary, ['processingTime', pluginName], - stats.summarizeStats(get(processingTimeByPluginName, pluginName), statOptions)); + const byPluginName = Array.from( + pluginNames + ).reduce((summary, pluginName) => { + set( + summary, + ['queueTime', pluginName], + stats.summarizeStats( + get(queueTimeByPluginName, pluginName), + statOptions + ) + ); + set( + summary, + ['processingTime', pluginName], + stats.summarizeStats( + get(processingTimeByPluginName, pluginName), + statOptions + ) + ); return summary; }, {}); - const byMessageType = Array.from(messageTypes).reduce((summary, messageType) => { - set(summary, ['queueTime', messageType], - stats.summarizeStats(get(queueTimeByMessageType, messageType), statOptions)); - set(summary, ['processingTime', messageType], - stats.summarizeStats(get(processingTimeByMessageType, messageType), statOptions)); + const byMessageType = Array.from( + messageTypes + ).reduce((summary, messageType) => { + set( + summary, + ['queueTime', messageType], + stats.summarizeStats( + get(queueTimeByMessageType, messageType), + statOptions + ) + ); + set( + summary, + ['processingTime', messageType], + stats.summarizeStats( + get(processingTimeByMessageType, messageType), + statOptions + ) + ); return summary; }, {}); @@ -55,6 +83,6 @@ module.exports = { return { byPluginName, byMessageType - } + }; } }; diff --git a/lib/support/resultsStorage/index.js b/lib/support/resultsStorage/index.js index 8c78ecc81..0d7d69059 100644 --- a/lib/support/resultsStorage/index.js +++ b/lib/support/resultsStorage/index.js @@ -25,7 +25,10 @@ module.exports = function(input, timestamp, outputFolder, resultBaseURL) { resultsSubFolders.push(path.basename(outputFolder)); storageBasePath = path.resolve(outputFolder); } else { - resultsSubFolders.push(getDomainOrFileName(input), timestamp.format('YYYY-MM-DD-HH-mm-ss')); + resultsSubFolders.push( + getDomainOrFileName(input), + timestamp.format('YYYY-MM-DD-HH-mm-ss') + ); storageBasePath = path.resolve('sitespeed-result', ...resultsSubFolders); } diff --git a/lib/support/resultsStorage/pathToFolder.js b/lib/support/resultsStorage/pathToFolder.js index 8ff428fab..7acc1ee80 100644 --- a/lib/support/resultsStorage/pathToFolder.js +++ b/lib/support/resultsStorage/pathToFolder.js @@ -18,6 +18,5 @@ module.exports = function pathFromRootToPageDir(url) { pathSegments.push('query-' + hash); } - return pathSegments.join('/') - .concat('/'); + return pathSegments.join('/').concat('/'); }; diff --git a/lib/support/resultsStorage/storageManager.js b/lib/support/resultsStorage/storageManager.js index e22be5889..f1f37f491 100644 --- a/lib/support/resultsStorage/storageManager.js +++ b/lib/support/resultsStorage/storageManager.js @@ -13,20 +13,24 @@ Promise.promisifyAll(zlib); const mkdirp = Promise.promisify(require('mkdirp')); function write(dirPath, filename, data, gzip) { - return Promise.join(dirPath, filename, data, - (dirPath, filename, data) => { - if (gzip) { - const buff = new Buffer(data, 'utf8'); - return zlib.gzipAsync(buff, { + return Promise.join(dirPath, filename, data, (dirPath, filename, data) => { + if (gzip) { + const buff = new Buffer(data, 'utf8'); + return zlib + .gzipAsync(buff, { level: 1 - }).then((buffer) => - fs.writeFileAsync(path.join(dirPath, filename + '.gz'), buffer, 'utf8') + }) + .then(buffer => + fs.writeFileAsync( + path.join(dirPath, filename + '.gz'), + buffer, + 'utf8' + ) ); - } else { - return fs.writeFileAsync(path.join(dirPath, filename), data, 'utf8'); - } + } else { + return fs.writeFileAsync(path.join(dirPath, filename), data, 'utf8'); } - ); + }); } module.exports = function storageManager(baseDir, storagePathPrefix) { @@ -40,13 +44,11 @@ module.exports = function storageManager(baseDir, storagePathPrefix) { .concat('/'); }, createDataDir(subDir) { - const pathSegments = [ - baseDir, - subDir - ].filter(Boolean); + const pathSegments = [baseDir, subDir].filter(Boolean); - return Promise.resolve(path.join.apply(null, pathSegments)) - .tap((dirPath) => mkdirp(dirPath)); + return Promise.resolve(path.join.apply(null, pathSegments)).tap(dirPath => + mkdirp(dirPath) + ); }, writeData(filename, data) { return write(this.createDataDir('data'), filename, data); @@ -61,19 +63,16 @@ module.exports = function storageManager(baseDir, storagePathPrefix) { return storagePathPrefix; }, copyToResultDir(filename) { - return Promise.join(this.createDataDir(), filename, - (dirPath, filename) => - fs.copyAsync(filename, dirPath)); + return Promise.join(this.createDataDir(), filename, (dirPath, filename) => + fs.copyAsync(filename, dirPath) + ); }, createDirForUrl(url, subDir) { - const pathSegments = [ - baseDir, - pathToFolder(url), - subDir - ].filter(Boolean); + const pathSegments = [baseDir, pathToFolder(url), subDir].filter(Boolean); - return Promise.resolve(path.join.apply(null, pathSegments)) - .tap((dirPath) => mkdirp(dirPath)); + return Promise.resolve(path.join.apply(null, pathSegments)).tap(dirPath => + mkdirp(dirPath) + ); }, writeDataForUrl(data, filename, url, subDir, gzip) { const dirPath = ['data', subDir].filter(Boolean).join(path.sep); @@ -82,5 +81,5 @@ module.exports = function storageManager(baseDir, storagePathPrefix) { writeHtmlForUrl(html, filename, url, gzip) { return write(this.createDirForUrl(url), filename, html, gzip); } - } + }; }; diff --git a/lib/support/setup/detailed.js b/lib/support/setup/detailed.js index 34e51c32a..b23cc8661 100644 --- a/lib/support/setup/detailed.js +++ b/lib/support/setup/detailed.js @@ -13,7 +13,7 @@ function row(stat, name, metricName, formatter) { metricName, node: stat, h: formatter ? formatter : h.noop - } + }; } module.exports = function(data) { @@ -34,9 +34,21 @@ module.exports = function(data) { rows.push( row(summary.score, 'Coach score', 'overallScore'), - row(summary.performance.score, 'Coach performance score', 'performanceScore'), - row(summary.accessibility.score, 'Accessibility score', 'accessibilityScore'), - row(summary.bestpractice.score, 'Best Practice score', 'bestPracticeScore') + row( + summary.performance.score, + 'Coach performance score', + 'performanceScore' + ), + row( + summary.accessibility.score, + 'Accessibility score', + 'accessibilityScore' + ), + row( + summary.bestpractice.score, + 'Best Practice score', + 'bestPracticeScore' + ) ); } @@ -45,24 +57,58 @@ module.exports = function(data) { const contentTypes = summary.contentTypes; rows.push( - row(contentTypes.image.requests, 'Image requests', 'imageRequestsPerPage'), + row( + contentTypes.image.requests, + 'Image requests', + 'imageRequestsPerPage' + ), row(contentTypes.css.requests, 'CSS requests', 'cssRequestsPerPage'), - row(contentTypes.javascript.requests, 'Javascript requests', 'jsRequestsPerPage' ), + row( + contentTypes.javascript.requests, + 'Javascript requests', + 'jsRequestsPerPage' + ), row(contentTypes.font.requests, 'Font requests', 'fontRequestsPerPage'), row(summary.requests, 'Total requests', 'totalRequestsPerPage') ); rows.push( - row(contentTypes.image.transferSize, 'Image size', 'imageSizePerPage', h.size.format), - row(contentTypes.html.transferSize, 'HTML size','htmlSizePerPage', h.size.format), - row(contentTypes.css.transferSize, 'CSS size','cssSizePerPage', h.size.format), - row(contentTypes.javascript.transferSize, 'Javascript size', 'jsSizePerPage', h.size.format), - row(contentTypes.font.transferSize, 'Font size', 'fontSizePerPage', h.size.format), - row(summary.transferSize, 'Total size', 'totalSizePerPage', h.size.format)); + row( + contentTypes.image.transferSize, + 'Image size', + 'imageSizePerPage', + h.size.format + ), + row( + contentTypes.html.transferSize, + 'HTML size', + 'htmlSizePerPage', + h.size.format + ), + row( + contentTypes.css.transferSize, + 'CSS size', + 'cssSizePerPage', + h.size.format + ), + row( + contentTypes.javascript.transferSize, + 'Javascript size', + 'jsSizePerPage', + h.size.format + ), + row( + contentTypes.font.transferSize, + 'Font size', + 'fontSizePerPage', + h.size.format + ), + row(summary.transferSize, 'Total size', 'totalSizePerPage', h.size.format) + ); const responseCodes = Object.keys(summary.responseCodes); for (let code of responseCodes) { - rows.push(row(summary.responseCodes[code], code + ' responses')) + rows.push(row(summary.responseCodes[code], code + ' responses')); } } @@ -72,26 +118,47 @@ module.exports = function(data) { rows.push( row(summary.rumSpeedIndex, 'RUMSpeed Index', 'rumSpeedIndex'), row(summary.firstPaint, 'First Paint', 'firstPaint'), - row(summary.fullyLoaded, 'Fully loaded', 'fullyLoaded')); + row(summary.fullyLoaded, 'Fully loaded', 'fullyLoaded') + ); const timings = Object.keys(summary.pageTimings); for (let timing of timings) { - rows.push(row(summary.pageTimings[timing], timing, timing)) + rows.push(row(summary.pageTimings[timing], timing, timing)); } if (summary.custom) { for (var key of Object.keys(summary.custom)) { - rows.push(row(summary.custom[key],key)); + rows.push(row(summary.custom[key], key)); } } if (summary.visualMetrics) { rows.push( - row(summary.visualMetrics.FirstVisualChange, 'First Visual Change', 'FirstVisualChange', h.time.ms), + row( + summary.visualMetrics.FirstVisualChange, + 'First Visual Change', + 'FirstVisualChange', + h.time.ms + ), row(summary.visualMetrics.SpeedIndex, 'Speed Index', 'SpeedIndex'), - row(summary.visualMetrics.PerceptualSpeedIndex, 'Perceptual Speed Index', 'PerceptualSpeedIndex'), - row(summary.visualMetrics.VisualComplete85, 'Visual Complete 85%', 'VisualComplete85', h.time.ms), - row(summary.visualMetrics.LastVisualChange, 'Last Visual Change', 'LastVisualChange', h.time.ms)); + row( + summary.visualMetrics.PerceptualSpeedIndex, + 'Perceptual Speed Index', + 'PerceptualSpeedIndex' + ), + row( + summary.visualMetrics.VisualComplete85, + 'Visual Complete 85%', + 'VisualComplete85', + h.time.ms + ), + row( + summary.visualMetrics.LastVisualChange, + 'Last Visual Change', + 'LastVisualChange', + h.time.ms + ) + ); } } @@ -101,14 +168,19 @@ module.exports = function(data) { rows.push( row(firstView.render, 'WPT render (firstView)', 'render'), row(firstView.SpeedIndex, 'WPT SpeedIndex (firstView)', 'SpeedIndex'), - row(firstView.fullyLoaded, 'WPT Fully loaded (firstView)', 'fullyLoaded')); + row( + firstView.fullyLoaded, + 'WPT Fully loaded (firstView)', + 'fullyLoaded' + ) + ); } } if (gpsi) { rows.push( row(gpsi.summary.SPEED.score, 'GPSI Speed Score', 'gpsispeedscore') - ) + ); } return rows.filter(Boolean); diff --git a/lib/support/setup/summaryBoxes.js b/lib/support/setup/summaryBoxes.js index 477015092..3d9ebb461 100644 --- a/lib/support/setup/summaryBoxes.js +++ b/lib/support/setup/summaryBoxes.js @@ -8,7 +8,7 @@ function infoBox(stat, name, formatter, url) { return undefined; } - return _box(stat, name, 'info', formatter, url) + return _box(stat, name, 'info', formatter, url); } function scoreBox(stat, name, url) { @@ -16,7 +16,7 @@ function scoreBox(stat, name, url) { return undefined; } - return _box(stat, name, h.scoreLabel(stat.median), h.noop, url) + return _box(stat, name, h.scoreLabel(stat.median), h.noop, url); } function metricBox(stat, name, score, formatter, url) { @@ -24,7 +24,7 @@ function metricBox(stat, name, score, formatter, url) { return undefined; } - return _box(stat, name, h.scoreLabel(score.median), formatter, url) + return _box(stat, name, h.scoreLabel(score.median), formatter, url); } function _box(stat, name, label, formatter, url) { @@ -37,7 +37,7 @@ function _box(stat, name, label, formatter, url) { median, p90, url - } + }; } module.exports = function(data) { @@ -58,13 +58,42 @@ module.exports = function(data) { boxes.push( scoreBox(summary.score, 'Overall score', 'overallScore'), - scoreBox(summary.performance.score, 'Performance score', 'performanceScore'), - scoreBox(summary.accessibility.score, 'Accessibility score', 'accessibilityScore'), - scoreBox(summary.bestpractice.score, 'Best Practice score', 'bestPracticeScore'), - scoreBox(summary.performance.fastRender, 'Fast Render advice', 'fastRender'), - scoreBox(summary.performance.avoidScalingImages, 'Avoid scaling images advice', 'avoidScalingImages'), - scoreBox(summary.performance.compressAssets, 'Compress assets advice', 'compressAssets'), - scoreBox(summary.performance.optimalCssSize, 'Optimal CSS size advice', 'optimalCssSize')); + scoreBox( + summary.performance.score, + 'Performance score', + 'performanceScore' + ), + scoreBox( + summary.accessibility.score, + 'Accessibility score', + 'accessibilityScore' + ), + scoreBox( + summary.bestpractice.score, + 'Best Practice score', + 'bestPracticeScore' + ), + scoreBox( + summary.performance.fastRender, + 'Fast Render advice', + 'fastRender' + ), + scoreBox( + summary.performance.avoidScalingImages, + 'Avoid scaling images advice', + 'avoidScalingImages' + ), + scoreBox( + summary.performance.compressAssets, + 'Compress assets advice', + 'compressAssets' + ), + scoreBox( + summary.performance.optimalCssSize, + 'Optimal CSS size advice', + 'optimalCssSize' + ) + ); } if (pagexray && coach) { @@ -72,14 +101,35 @@ module.exports = function(data) { const pxSum = pagexray.summary; boxes.push( - metricBox(pxSum.transferSize, 'Total size (transfer)', - cSum.performance.pageSize, h.size.format, 'pageSize'), - metricBox(pxSum.contentTypes.image.transferSize, 'Image size (transfer)', - cSum.performance.imageSize, h.size.format, 'imageSize'), - metricBox(pxSum.contentTypes.javascript.transferSize, 'Javascript size (transfer)', - cSum.performance.javascriptSize, h.size.format, 'javascriptSize'), - metricBox(pxSum.contentTypes.css.transferSize, 'CSS size (transfer)', cSum.performance.cssSize, h.size.format, 'cssSize')); - + metricBox( + pxSum.transferSize, + 'Total size (transfer)', + cSum.performance.pageSize, + h.size.format, + 'pageSize' + ), + metricBox( + pxSum.contentTypes.image.transferSize, + 'Image size (transfer)', + cSum.performance.imageSize, + h.size.format, + 'imageSize' + ), + metricBox( + pxSum.contentTypes.javascript.transferSize, + 'Javascript size (transfer)', + cSum.performance.javascriptSize, + h.size.format, + 'javascriptSize' + ), + metricBox( + pxSum.contentTypes.css.transferSize, + 'CSS size (transfer)', + cSum.performance.cssSize, + h.size.format, + 'cssSize' + ) + ); } // no matching rules @@ -98,18 +148,33 @@ module.exports = function(data) { infoBox(summary.responseCodes['404'], '404 responses'), infoBox(summary.domains, 'Domains per page'), infoBox(summary.expireStats, 'Cache time', h.time.duration), - infoBox(summary.lastModifiedStats, 'Time since last modification', h.time.duration)); + infoBox( + summary.lastModifiedStats, + 'Time since last modification', + h.time.duration + ) + ); if (summary.firstParty) { boxes.push( infoBox(summary.firstParty.requests, '1st party requests'), - infoBox(summary.firstParty.transferSize, '1st party size', h.size.format)); + infoBox( + summary.firstParty.transferSize, + '1st party size', + h.size.format + ) + ); } if (summary.thirdParty) { boxes.push( infoBox(summary.thirdParty.requests, '3rd party requests'), - infoBox(summary.thirdParty.transferSize, '3rd party sizes', h.size.format)); + infoBox( + summary.thirdParty.transferSize, + '3rd party sizes', + h.size.format + ) + ); } } @@ -117,18 +182,52 @@ module.exports = function(data) { const summary = browsertime.summary; boxes.push( - infoBox(summary.rumSpeedIndex, 'RUM Speed Index', h.noop, 'rumSpeedIndex'), + infoBox( + summary.rumSpeedIndex, + 'RUM Speed Index', + h.noop, + 'rumSpeedIndex' + ), infoBox(summary.firstPaint, 'First Paint', h.time.ms, 'firstPaint'), - infoBox(summary.pageTimings.backEndTime, 'Backend Time', h.time.ms, 'backEndTime'), - infoBox(summary.pageTimings.frontEndTime, 'Frontend Time', h.time.ms, 'frontEndTime'), - infoBox(summary.pageTimings.fullyLoaded, 'Fully Loaded Time', h.time.ms, 'fullyLoaded')); + infoBox( + summary.pageTimings.backEndTime, + 'Backend Time', + h.time.ms, + 'backEndTime' + ), + infoBox( + summary.pageTimings.frontEndTime, + 'Frontend Time', + h.time.ms, + 'frontEndTime' + ), + infoBox( + summary.pageTimings.fullyLoaded, + 'Fully Loaded Time', + h.time.ms, + 'fullyLoaded' + ) + ); if (summary.visualMetrics) { boxes.push( - infoBox(summary.visualMetrics.FirstVisualChange, 'First Visual Change', h.time.ms), + infoBox( + summary.visualMetrics.FirstVisualChange, + 'First Visual Change', + h.time.ms + ), infoBox(summary.visualMetrics.SpeedIndex, 'Speed Index'), - infoBox(summary.visualMetrics.VisualComplete85, 'Visual Complete 85%', h.time.ms), - infoBox(summary.visualMetrics.LastVisualChange, 'Last Visual Change', h.time.ms)); + infoBox( + summary.visualMetrics.VisualComplete85, + 'Visual Complete 85%', + h.time.ms + ), + infoBox( + summary.visualMetrics.LastVisualChange, + 'Last Visual Change', + h.time.ms + ) + ); } if (summary.custom) { @@ -143,15 +242,19 @@ module.exports = function(data) { if (firstView) { boxes.push( infoBox(firstView.render, 'WPT render (firstView)'), - infoBox(firstView.SpeedIndex, 'WPT SpeedIndex (firstView)', h.noop, 'SpeedIndex'), - infoBox(firstView.fullyLoaded, 'WPT Fully loaded (firstView)')); + infoBox( + firstView.SpeedIndex, + 'WPT SpeedIndex (firstView)', + h.noop, + 'SpeedIndex' + ), + infoBox(firstView.fullyLoaded, 'WPT Fully loaded (firstView)') + ); } } if (gpsi) { - boxes.push( - infoBox(gpsi.summary.SPEED.score, 'GPSI Speed Score') - ) + boxes.push(infoBox(gpsi.summary.SPEED.score, 'GPSI Speed Score')); } return boxes; diff --git a/lib/support/statsHelpers.js b/lib/support/statsHelpers.js index faca6f682..70626a140 100644 --- a/lib/support/statsHelpers.js +++ b/lib/support/statsHelpers.js @@ -15,7 +15,6 @@ function percentileName(percentile) { } module.exports = { - /** * Create or update a fast-stats#Stats object in target at path. */ @@ -52,13 +51,18 @@ module.exports = { median: parseInt(stats.median().toFixed(decimals)), mean: parseInt(stats.amean().toFixed(decimals)) }; - percentiles.forEach((p) => { + percentiles.forEach(p => { let name = percentileName(p); const percentile = stats.percentile(p); if (Number.isFinite(percentile)) { data[name] = parseInt(percentile.toFixed(decimals)); } else { - throw new Error('Failed to calculate ' + name + ' for stats: ' + JSON.stringify(stats, null, 2)); + throw new Error( + 'Failed to calculate ' + + name + + ' for stats: ' + + JSON.stringify(stats, null, 2) + ); } }); if (options.includeSum) { diff --git a/lib/support/tsdbUtil.js b/lib/support/tsdbUtil.js index 1c71bd50f..e47659478 100644 --- a/lib/support/tsdbUtil.js +++ b/lib/support/tsdbUtil.js @@ -17,9 +17,14 @@ module.exports = { } }, getURLAndGroup(options, group, url, includeQueryParams) { - if (group && options.urlsMetaData && options.urlsMetaData[url] && options.urlsMetaData[url].alias) { + if ( + group && + options.urlsMetaData && + options.urlsMetaData[url] && + options.urlsMetaData[url].alias + ) { let alias = options.urlsMetaData[url].alias; - return this.toSafeKey(group) + "." + this.toSafeKey(alias); + return this.toSafeKey(group) + '.' + this.toSafeKey(alias); } else { return flatten.keypathFromUrl(url, includeQueryParams); } diff --git a/lib/support/url-source.js b/lib/support/url-source.js index 17c07eddd..e74465ad3 100644 --- a/lib/support/url-source.js +++ b/lib/support/url-source.js @@ -11,7 +11,9 @@ module.exports = { }, findUrls(queue) { for (const url of this.options.urls) { - queue.postMessage(make('url', {}, {url: url, group: urlParser.parse(url).hostname})); + queue.postMessage( + make('url', {}, { url: url, group: urlParser.parse(url).hostname }) + ); } } }; diff --git a/lib/support/util.js b/lib/support/util.js index 01ac54a50..e3ae9027b 100644 --- a/lib/support/util.js +++ b/lib/support/util.js @@ -13,10 +13,15 @@ module.exports = { return [arrayLike]; }, throwIfMissing(options, keys, namespace) { - let missingKeys = keys.filter((key) => !options[key]); + let missingKeys = keys.filter(key => !options[key]); if (missingKeys.length > 0) { - throw new Error(format('Required option(s) %s need to be specified in namespace "%s"', - missingKeys.map((s) => '"' + s + '"'), namespace)); + throw new Error( + format( + 'Required option(s) %s need to be specified in namespace "%s"', + missingKeys.map(s => '"' + s + '"'), + namespace + ) + ); } } }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..dacbb1c4c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6357 @@ +{ + "name": "sitespeed.io", + "version": "5.4.3", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", + "dev": true + }, + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" + }, + "acorn-globals": { + "version": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", + "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", + "requires": { + "acorn": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz" + }, + "dependencies": { + "acorn": { + "version": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + } + } + }, + "acorn-jsx": { + "version": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + } + }, + "adbkit": { + "version": "https://registry.npmjs.org/adbkit/-/adbkit-2.8.1.tgz", + "integrity": "sha1-GWdzDOqcgSaicrgdP0pmda/RI1I=", + "requires": { + "adbkit-logcat": "https://registry.npmjs.org/adbkit-logcat/-/adbkit-logcat-1.1.0.tgz", + "adbkit-monkey": "https://registry.npmjs.org/adbkit-monkey/-/adbkit-monkey-1.0.1.tgz", + "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.34.tgz", + "commander": "2.10.0", + "debug": "2.6.8", + "node-forge": "https://registry.npmjs.org/node-forge/-/node-forge-0.6.49.tgz", + "split": "0.3.3" + }, + "dependencies": { + "bluebird": { + "version": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.34.tgz", + "integrity": "sha1-L3tOyAIWMoqf3evfacjUlC/v99g=" + } + } + }, + "adbkit-logcat": { + "version": "https://registry.npmjs.org/adbkit-logcat/-/adbkit-logcat-1.1.0.tgz", + "integrity": "sha1-Adf5sM75CTowvLOwB+//MBUIli8=" + }, + "adbkit-monkey": { + "version": "https://registry.npmjs.org/adbkit-monkey/-/adbkit-monkey-1.0.1.tgz", + "integrity": "sha1-8pG+cBou/FZ6Y/x6pq/N7TFDC+E=", + "requires": { + "async": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + } + }, + "adm-zip": { + "version": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", + "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=" + }, + "afterward": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/afterward/-/afterward-2.0.0.tgz", + "integrity": "sha1-lmp1MdL9tBv/Z7Tqg6vZ68vvXrI=", + "requires": { + "define-error": "1.0.0" + } + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + }, + "dependencies": { + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + } + } + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "longest": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + } + }, + "alto-saxophone": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/alto-saxophone/-/alto-saxophone-2.30.0.tgz", + "integrity": "sha512-MfMJYy1yDnkOg7PNTM0VmMIlMec8TxWulIK5y3MYFJ4FtId+OKdTVFyubNBMgjbHQbxd242kifHBlpDgZkO2LA==", + "requires": { + "download": "https://registry.npmjs.org/download/-/download-4.4.3.tgz", + "download-status": "https://registry.npmjs.org/download-status/-/download-status-2.2.1.tgz" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-escapes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-2.0.0.tgz", + "integrity": "sha1-W65SvkJIeN2Xg+iRDj/Cki6DyBs=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "aproba": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz", + "integrity": "sha512-ZpYajIfO0j2cOFTO955KUMIKNmj6zhX8kVztMAxFsDaMwz+9Z9SV0uou2pC9HJqcfpffOsjnbrDMvkNy+9RXPw==", + "dev": true + }, + "archive-type": { + "version": "https://registry.npmjs.org/archive-type/-/archive-type-3.2.0.tgz", + "integrity": "sha1-nNnABpV+vpX62tW9YJiUKoE3N/Y=", + "requires": { + "file-type": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz" + } + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "dev": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.2" + } + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "arr-diff": { + "version": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "requires": { + "arr-flatten": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz" + } + }, + "arr-flatten": { + "version": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz", + "integrity": "sha1-onTthawIhJtr14R8RYB0XcUa37E=" + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asap": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz", + "integrity": "sha1-UidltQw1EEkOUtfc/ghe+bqWlY8=" + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "assertion-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", + "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", + "dev": true + }, + "async": { + "version": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sdk": { + "version": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.0.31.tgz", + "integrity": "sha1-5yzx/caQFb2f0r3z07iMFlB9Jo4=", + "requires": { + "xml2js": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.6.tgz", + "xmlbuilder": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-0.4.2.tgz" + }, + "dependencies": { + "sax": { + "version": "https://registry.npmjs.org/sax/-/sax-0.4.2.tgz", + "integrity": "sha1-OfO2AXM9a+yXEFskKipA/Wl4rDw=" + }, + "xml2js": { + "version": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.6.tgz", + "integrity": "sha1-0gnE5N2h/JxFIUHvQcB39a399sQ=", + "requires": { + "sax": "https://registry.npmjs.org/sax/-/sax-0.4.2.tgz" + } + }, + "xmlbuilder": { + "version": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-0.4.2.tgz", + "integrity": "sha1-F3bWXz/brUcKCNhgTN6xxOVA/4M=" + } + } + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "babel-code-frame": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64url": { + "version": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", + "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=" + }, + "bl": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz", + "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=", + "requires": { + "readable-stream": "2.3.2" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "bluebird": { + "version": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "requires": { + "expand-range": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "preserve": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "repeat-element": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz" + } + }, + "browser-stdout": { + "version": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" + }, + "browsertime": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/browsertime/-/browsertime-1.5.4.tgz", + "integrity": "sha1-wQ7nxhQPEmOp7a5qHiSnPemnBRU=", + "requires": { + "adbkit": "https://registry.npmjs.org/adbkit/-/adbkit-2.8.1.tgz", + "alto-saxophone": "2.30.0", + "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "chrome-har": "0.2.2", + "execa": "0.6.1", + "fast-stats": "0.0.3", + "hasbin": "1.2.3", + "intel": "https://registry.npmjs.org/intel/-/intel-1.1.2.tgz", + "lodash.foreach": "4.5.0", + "lodash.get": "4.4.2", + "lodash.groupby": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "lodash.isempty": "4.4.0", + "lodash.merge": "4.6.0", + "lodash.pick": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "lodash.remove": "https://registry.npmjs.org/lodash.remove/-/lodash.remove-4.7.0.tgz", + "lodash.set": "4.3.2", + "mkdirp": "0.5.1", + "moment": "https://registry.npmjs.org/moment/-/moment-2.17.1.tgz", + "selenium-webdriver": "3.4.0", + "sltc": "https://registry.npmjs.org/sltc/-/sltc-0.6.0.tgz", + "soprano-saxophone": "0.18.0", + "valid-url": "1.0.9", + "xvfb": "https://registry.npmjs.org/xvfb/-/xvfb-0.2.3.tgz", + "yargs": "7.1.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "5.0.0" + } + } + } + }, + "buffer-crc32": { + "version": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-equal-constant-time": { + "version": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "buffer-equals": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz", + "integrity": "sha1-A1O1T9B/2VZBcGca5vZrnPENJ/U=", + "dev": true + }, + "buffer-to-vinyl": { + "version": "https://registry.npmjs.org/buffer-to-vinyl/-/buffer-to-vinyl-1.1.0.tgz", + "integrity": "sha1-APFfruOreh3aLN5tkSG//dB7ImI=", + "requires": { + "file-type": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "readable-stream": "2.3.2", + "uuid": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "vinyl": "1.2.0" + }, + "dependencies": { + "uuid": { + "version": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" + } + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "2.1.1", + "map-obj": "1.0.1" + } + }, + "capture-stack-trace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", + "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "catharsis": { + "version": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.8.tgz", + "integrity": "sha1-aTR59DqsVJ2Aa9c+kkzQ2USVGgY=", + "dev": true, + "requires": { + "underscore-contrib": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz" + } + }, + "caw": { + "version": "https://registry.npmjs.org/caw/-/caw-1.2.0.tgz", + "integrity": "sha1-/7Im/n78VHKI3GLuPpcHPCEtEDQ=", + "requires": { + "get-proxy": "https://registry.npmjs.org/get-proxy/-/get-proxy-1.1.0.tgz", + "is-obj": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "tunnel-agent": "0.4.3" + }, + "dependencies": { + "object-assign": { + "version": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + } + } + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } + }, + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "dev": true, + "requires": { + "assertion-error": "1.0.2", + "deep-eql": "0.1.3", + "type-detect": "1.0.0" + } + }, + "chai-as-promised": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-6.0.0.tgz", + "integrity": "sha1-GgKkM6byTa+sY7nJb6FoTbGqjaY=", + "dev": true, + "requires": { + "check-error": "1.0.2" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", + "requires": { + "is-regex": "1.0.4" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chrome-har": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/chrome-har/-/chrome-har-0.2.2.tgz", + "integrity": "sha1-/I9wkEyYrN3T61wDCj0JD9qnbTE=", + "requires": { + "debug": "2.6.8", + "moment": "2.18.1", + "tough-cookie": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz" + }, + "dependencies": { + "moment": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", + "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" + } + } + }, + "circular-json": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz", + "integrity": "sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0=", + "dev": true + }, + "clean-css": { + "version": "3.4.27", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.27.tgz", + "integrity": "sha1-re91sxwWD/pdcvTeZ5ZuJmDBolU=", + "requires": { + "commander": "2.8.1", + "source-map": "0.4.4" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": "1.0.1" + } + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "clean-css-cli": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-4.1.6.tgz", + "integrity": "sha1-PvKidGsHT1XuBTkihxCnDbeng4k=", + "dev": true, + "requires": { + "clean-css": "4.1.5", + "commander": "2.10.0", + "glob": "7.1.2" + }, + "dependencies": { + "clean-css": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.5.tgz", + "integrity": "sha1-0JqHoCpTdRF1iXlq52oGPKzbVBo=", + "dev": true, + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } + } + }, + "cli-color": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.1.0.tgz", + "integrity": "sha1-3hiM3Ekp2DtnrqBBEPvtQP2/Z3U=", + "requires": { + "ansi-regex": "2.1.1", + "d": "0.1.1", + "es5-ext": "0.10.23", + "es6-iterator": "2.0.1", + "memoizee": "0.3.10", + "timers-ext": "0.1.2" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "2.0.0" + } + }, + "cli-table2": { + "version": "https://registry.npmjs.org/cli-table2/-/cli-table2-0.2.0.tgz", + "integrity": "sha1-LR738hig54biFFQFYtS9F3/jLZc=", + "requires": { + "colors": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "lodash": "3.10.1", + "string-width": "1.0.2" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, + "cli-width": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", + "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + } + }, + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=" + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=" + }, + "co": { + "version": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", + "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "color-convert": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "optional": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.10.0.tgz", + "integrity": "sha512-q/r9trjmuikWDRJNTBHAVnWhuU6w+z80KgBq7j9YDclik5E7X4xi0KnlZBNFA1zOQ+SH/vHMWd2mC9QTOz7GpA==", + "requires": { + "graceful-readlink": "1.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.2", + "typedarray": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + } + }, + "concurrent-queue": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/concurrent-queue/-/concurrent-queue-7.0.1.tgz", + "integrity": "sha1-yXIzW7R2EsLpJOsPlOy8rN7966Q=", + "requires": { + "afterward": "2.0.0", + "define-error": "1.0.0", + "eventuate": "4.0.0", + "object-assign": "4.0.1", + "on-error": "2.1.0", + "once": "1.3.3", + "promise-polyfill": "2.1.4" + }, + "dependencies": { + "object-assign": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz", + "integrity": "sha1-mVBEVsNZi1ytT8WcJuipuxB/4L0=" + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "requires": { + "wrappy": "1.0.2" + } + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "constantinople": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz", + "integrity": "sha1-dWnKqKo/jVk11i4fqW+fcCzYHHk=", + "requires": { + "acorn": "3.3.0", + "is-expression": "2.1.0" + } + }, + "convert-source-map": { + "version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "create-error-class": { + "version": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "requires": { + "capture-stack-trace": "1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "which": "1.2.14" + } + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.10.1" + } + }, + "css-selector-parser": { + "version": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.3.0.tgz", + "integrity": "sha1-XxrUPi2O77/cME/NOaUhZklD4+s=", + "dev": true + }, + "csv": { + "version": "https://registry.npmjs.org/csv/-/csv-1.1.1.tgz", + "integrity": "sha1-2ZUtWbH5ZKevvN2ATWgYpzGZpHc=", + "requires": { + "csv-generate": "https://registry.npmjs.org/csv-generate/-/csv-generate-1.0.0.tgz", + "csv-parse": "https://registry.npmjs.org/csv-parse/-/csv-parse-1.2.0.tgz", + "csv-stringify": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-1.0.4.tgz", + "stream-transform": "https://registry.npmjs.org/stream-transform/-/stream-transform-0.1.2.tgz" + } + }, + "csv-generate": { + "version": "https://registry.npmjs.org/csv-generate/-/csv-generate-1.0.0.tgz", + "integrity": "sha1-vVKIaFnQySXz5R9g86vtJi+hXK8=" + }, + "csv-parse": { + "version": "https://registry.npmjs.org/csv-parse/-/csv-parse-1.2.0.tgz", + "integrity": "sha1-BHtzhoq5qFdG6IX2N/ntD7ZFpCU=" + }, + "csv-stringify": { + "version": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-1.0.4.tgz", + "integrity": "sha1-vBi6ua1M7zGV/SV5gLWLR5xC0+U=", + "requires": { + "lodash.get": "4.4.2" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "1.0.2" + } + }, + "d": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", + "integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=", + "requires": { + "es5-ext": "0.10.23" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "date-format": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-0.0.2.tgz", + "integrity": "sha1-+v1Ej3IRXvHitzkVWukvK+bCjdE=" + }, + "dateformat": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.0.0.tgz", + "integrity": "sha1-J0Pjq7XD/CRi5SfcpEXgTp9N7hc=" + }, + "dbug": { + "version": "https://registry.npmjs.org/dbug/-/dbug-0.4.2.tgz", + "integrity": "sha1-MrSzEF6IYQQ6b5rHVdgOVC02WzE=" + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decompress": { + "version": "https://registry.npmjs.org/decompress/-/decompress-3.0.0.tgz", + "integrity": "sha1-rx3VDQbjv8QyRh033hGzjA2ZG+0=", + "requires": { + "buffer-to-vinyl": "https://registry.npmjs.org/buffer-to-vinyl/-/buffer-to-vinyl-1.1.0.tgz", + "concat-stream": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "decompress-tar": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-3.1.0.tgz", + "decompress-tarbz2": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-3.1.0.tgz", + "decompress-targz": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-3.1.0.tgz", + "decompress-unzip": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-3.4.0.tgz", + "stream-combiner2": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "vinyl-assign": "https://registry.npmjs.org/vinyl-assign/-/vinyl-assign-1.2.1.tgz", + "vinyl-fs": "2.4.4" + } + }, + "decompress-tar": { + "version": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-3.1.0.tgz", + "integrity": "sha1-IXx4n5uURQ76rcXF5TeXj8MzxGY=", + "requires": { + "is-tar": "https://registry.npmjs.org/is-tar/-/is-tar-1.0.0.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "strip-dirs": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "tar-stream": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz", + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=" + }, + "object-assign": { + "version": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=" + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "decompress-tarbz2": { + "version": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-3.1.0.tgz", + "integrity": "sha1-iyOTVoE1X58YnYclag+L3ZbZZm0=", + "requires": { + "is-bzip2": "https://registry.npmjs.org/is-bzip2/-/is-bzip2-1.0.0.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "seek-bzip": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "strip-dirs": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "tar-stream": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz", + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=" + }, + "object-assign": { + "version": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=" + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "decompress-targz": { + "version": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-3.1.0.tgz", + "integrity": "sha1-ssE9+YFmJomRtxXWRH9kLpaW9aA=", + "requires": { + "is-gzip": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "strip-dirs": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "tar-stream": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz", + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=" + }, + "object-assign": { + "version": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=" + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "decompress-unzip": { + "version": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-3.4.0.tgz", + "integrity": "sha1-YUdbQVIGa74/7hL51inRX+ZHjus=", + "requires": { + "is-zip": "https://registry.npmjs.org/is-zip/-/is-zip-1.0.0.tgz", + "read-all-stream": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", + "stat-mode": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz", + "strip-dirs": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "vinyl": "1.2.0", + "yauzl": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz" + }, + "dependencies": { + "through2": { + "version": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "2.3.2", + "xtend": "4.0.1" + } + } + } + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "requires": { + "type-detect": "0.1.1" + }, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + } + } + }, + "deep-equal": { + "version": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" + }, + "deep-extend": { + "version": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deferred": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/deferred/-/deferred-0.7.1.tgz", + "integrity": "sha1-9lzTwFaD899VS/XBU4UUlIEdAfQ=", + "requires": { + "d": "0.1.1", + "es5-ext": "0.10.23", + "event-emitter": "0.3.5", + "next-tick": "0.2.2" + } + }, + "define-error": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-error/-/define-error-1.0.0.tgz", + "integrity": "sha1-X7SKRd1fZfiPgrDJoiPAGTN50/4=", + "requires": { + "capture-stack-trace": "1.0.0" + } + }, + "define-properties": { + "version": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "requires": { + "foreach": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "object-keys": "1.0.11" + } + }, + "defined": { + "version": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", + "dev": true, + "requires": { + "asap": "2.0.5", + "wrappy": "1.0.2" + } + }, + "diff": { + "version": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=" + }, + "doctrine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", + "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", + "dev": true, + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + } + }, + "doctypes": { + "version": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" + }, + "download": { + "version": "https://registry.npmjs.org/download/-/download-4.4.3.tgz", + "integrity": "sha1-qlX9rTktldS2jowr4D4MKqIbqaw=", + "requires": { + "caw": "https://registry.npmjs.org/caw/-/caw-1.2.0.tgz", + "concat-stream": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "each-async": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz", + "filenamify": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz", + "got": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", + "gulp-decompress": "https://registry.npmjs.org/gulp-decompress/-/gulp-decompress-1.2.0.tgz", + "gulp-rename": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.2.tgz", + "is-url": "1.2.2", + "object-assign": "4.1.1", + "read-all-stream": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", + "readable-stream": "2.3.2", + "stream-combiner2": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "vinyl": "1.2.0", + "vinyl-fs": "2.4.4", + "ware": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz" + } + }, + "download-status": { + "version": "https://registry.npmjs.org/download-status/-/download-status-2.2.1.tgz", + "integrity": "sha1-KPPFvNsA10qwBgL1CIiqZrd8yvk=", + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "lpad-align": "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "progress": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=" + }, + "ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=" + }, + "chalk": { + "version": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "requires": { + "ansi-styles": "1.1.0", + "escape-string-regexp": "1.0.5", + "has-ansi": "0.1.0", + "strip-ansi": "0.3.0", + "supports-color": "0.2.0" + } + }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "requires": { + "ansi-regex": "0.2.1" + } + }, + "object-assign": { + "version": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=" + }, + "strip-ansi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "requires": { + "ansi-regex": "0.2.1" + } + }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=" + } + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "requires": { + "readable-stream": "2.3.2" + } + }, + "duplexify": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz", + "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=", + "requires": { + "end-of-stream": "1.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.2", + "stream-shift": "1.0.0" + }, + "dependencies": { + "end-of-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", + "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=", + "requires": { + "once": "1.3.3" + } + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "requires": { + "wrappy": "1.0.2" + } + } + } + }, + "each-async": { + "version": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz", + "integrity": "sha1-3uUim98KtrogEqOV4bhpq/iBNHM=", + "requires": { + "onetime": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "set-immediate-shim": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "ecdsa-sig-formatter": { + "version": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", + "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", + "requires": { + "base64url": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", + "safe-buffer": "5.1.1" + } + }, + "eclint": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/eclint/-/eclint-1.1.5.tgz", + "integrity": "sha1-i46nBB7olkcBQQVrQ3W80k5AkKA=", + "dev": true, + "requires": { + "cli-color": "0.3.3", + "editorconfig": "0.12.2", + "gitlike-cli": "0.1.0", + "gulp-tap": "0.1.3", + "gulp-util": "3.0.8", + "iconv-lite": "0.4.11", + "linez": "4.1.4", + "lodash": "3.10.1", + "through2": "0.6.5", + "vinyl": "0.4.6", + "vinyl-fs": "1.0.0" + }, + "dependencies": { + "cli-color": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-0.3.3.tgz", + "integrity": "sha1-EtW90Vj/igsNtAEZiRPAPfBp9vU=", + "dev": true, + "requires": { + "d": "0.1.1", + "es5-ext": "0.10.23", + "memoizee": "0.3.10", + "timers-ext": "0.1.2" + } + }, + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "glob": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "2.0.10", + "once": "1.4.0" + } + }, + "glob-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-4.1.1.tgz", + "integrity": "sha1-uELfENaIx+trz869hG84UilrMgA=", + "dev": true, + "requires": { + "glob": "4.5.3", + "glob2base": "0.0.12", + "minimatch": "2.0.10", + "ordered-read-streams": "0.1.0", + "through2": "0.6.5", + "unique-stream": "2.2.1" + } + }, + "graceful-fs": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "dev": true, + "requires": { + "natives": "1.1.0" + } + }, + "iconv-lite": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz", + "integrity": "sha1-LstC/SlHRJIiCaLnxATayHk9it4=", + "dev": true + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + }, + "merge-stream": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-0.1.8.tgz", + "integrity": "sha1-SKB7O0oSHXSj7b/c20sIrb8CQLE=", + "dev": true, + "requires": { + "through2": "0.6.5" + } + }, + "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.1.8" + } + }, + "object-assign": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", + "dev": true + }, + "ordered-read-streams": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", + "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=", + "dev": true + }, + "strip-bom": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", + "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "is-utf8": "0.2.1" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + }, + "vinyl-fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-1.0.0.tgz", + "integrity": "sha1-0VdS5owtrXQ2Tn6FNHNzU1RpLt8=", + "dev": true, + "requires": { + "duplexify": "3.5.0", + "glob-stream": "4.1.1", + "glob-watcher": "0.0.8", + "graceful-fs": "3.0.11", + "merge-stream": "0.1.8", + "mkdirp": "0.5.1", + "object-assign": "2.1.1", + "strip-bom": "1.0.0", + "through2": "0.6.5", + "vinyl": "0.4.6" + } + } + } + }, + "editorconfig": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.12.2.tgz", + "integrity": "sha1-9nvU5IL7rgwk8SeFcvQ/+F3Cro8=", + "dev": true, + "requires": { + "bluebird": "2.11.0", + "commander": "1.1.1", + "lru-cache": "2.0.4", + "sigmund": "1.0.1" + }, + "dependencies": { + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", + "dev": true + }, + "commander": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-1.1.1.tgz", + "integrity": "sha1-UNFlGGiuYOzP8KLZ80WVN2vGsEE=", + "dev": true, + "requires": { + "keypress": "0.1.0" + } + }, + "lru-cache": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.0.4.tgz", + "integrity": "sha1-uLYa4JhIOF7Gdodg45wSPn45Voo=", + "dev": true + } + } + }, + "end-of-stream": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", + "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", + "requires": { + "once": "1.4.0" + } + }, + "entities": { + "version": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=" + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "requires": { + "is-arrayish": "0.2.1" + } + }, + "es-abstract": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.7.0.tgz", + "integrity": "sha1-363ndOAb/Nl/lhgCmMRJyGI/uUw=", + "requires": { + "es-to-primitive": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "is-callable": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "requires": { + "is-callable": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "is-date-object": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "is-symbol": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz" + } + }, + "es5-ext": { + "version": "0.10.23", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.23.tgz", + "integrity": "sha1-dXi1G+l0IHpUh4IbVlOMIk5Oezg=", + "requires": { + "es6-iterator": "2.0.1", + "es6-symbol": "3.1.1" + } + }, + "es6-iterator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", + "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=", + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.23", + "es6-symbol": "3.1.1" + }, + "dependencies": { + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "0.10.23" + } + } + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.23" + }, + "dependencies": { + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "0.10.23" + } + } + } + }, + "es6-weak-map": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-0.1.4.tgz", + "integrity": "sha1-cGzvnpmqI2undmwjnIueKG6n0ig=", + "requires": { + "d": "0.1.1", + "es5-ext": "0.10.23", + "es6-iterator": "0.1.3", + "es6-symbol": "2.0.1" + }, + "dependencies": { + "es6-iterator": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz", + "integrity": "sha1-1vWLjE/EE8JJtLqhl2j45NfIlE4=", + "requires": { + "d": "0.1.1", + "es5-ext": "0.10.23", + "es6-symbol": "2.0.1" + } + }, + "es6-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-2.0.1.tgz", + "integrity": "sha1-dhtcZ8/U8dGK+yNPaR1nhoLLO/M=", + "requires": { + "d": "0.1.1", + "es5-ext": "0.10.23" + } + } + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eslint": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.2.0.tgz", + "integrity": "sha1-orMYQRGxmOAunH88ymJaXgHFaz0=", + "dev": true, + "requires": { + "ajv": "5.2.2", + "babel-code-frame": "6.22.0", + "chalk": "1.1.3", + "concat-stream": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "debug": "2.6.8", + "doctrine": "2.0.0", + "eslint-scope": "3.7.1", + "espree": "3.4.3", + "esquery": "1.0.0", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "glob": "7.1.2", + "globals": "9.18.0", + "ignore": "3.3.3", + "imurmurhash": "0.1.4", + "inquirer": "3.2.0", + "is-resolvable": "1.0.0", + "js-yaml": "3.9.0", + "json-stable-stringify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.4", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "4.0.0", + "progress": "2.0.0", + "require-uncached": "1.0.3", + "strip-json-comments": "2.0.1", + "table": "4.0.1", + "text-table": "0.2.0" + }, + "dependencies": { + "ajv": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz", + "integrity": "sha1-R8aNaehvXZUxA7AHSpQw3GPaXjk=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "json-schema-traverse": "0.3.1", + "json-stable-stringify": "1.0.1" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.3.0.tgz", + "integrity": "sha1-t1seq+oMi5ezRANkfuJds0m52KA=", + "dev": true, + "requires": { + "get-stdin": "5.0.1" + }, + "dependencies": { + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + } + } + }, + "eslint-plugin-prettier": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.1.2.tgz", + "integrity": "sha1-S5D07n+Sv74ukmAX4cpA62KJZeo=", + "dev": true, + "requires": { + "fast-diff": "1.1.1", + "jest-docblock": "20.0.3" + } + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + } + }, + "espree": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.4.3.tgz", + "integrity": "sha1-KRC1zNSc6JPC//+qtP2LOjG4I3Q=", + "dev": true, + "requires": { + "acorn": "5.1.1", + "acorn-jsx": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz" + }, + "dependencies": { + "acorn": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz", + "integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esquery": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, + "esrecurse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "dev": true, + "requires": { + "estraverse": "4.2.0", + "object-assign": "4.1.1" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.23" + }, + "dependencies": { + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "0.10.23" + } + } + } + }, + "event-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.1.7.tgz", + "integrity": "sha1-tMVAAS0P4UmEIPPYlGAI22OTw3o=", + "dev": true, + "requires": { + "duplexer": "0.1.1", + "from": "0.1.7", + "map-stream": "0.1.0", + "pause-stream": "0.0.11", + "split": "0.2.10", + "stream-combiner": "0.0.4", + "through": "2.3.8" + }, + "dependencies": { + "split": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", + "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", + "dev": true, + "requires": { + "through": "2.3.8" + } + } + } + }, + "eventuate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventuate/-/eventuate-4.0.0.tgz", + "integrity": "sha1-TiaQVTFv/0EJB4FB+SZgDHS1caA=", + "requires": { + "define-error": "1.0.0", + "object-assign": "3.0.0", + "shallow-copy": "0.0.1" + }, + "dependencies": { + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + } + } + }, + "execa": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.6.1.tgz", + "integrity": "sha1-ee2kKt54w4dxiwqtSOD1c7VSXN4=", + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "is-stream": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "npm-run-path": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "p-finally": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "signal-exit": "3.0.2", + "strip-eof": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz" + } + }, + "expand-brackets": { + "version": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "requires": { + "is-posix-bracket": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz" + } + }, + "expand-range": { + "version": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "requires": { + "fill-range": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extend-shallow": { + "version": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + } + }, + "external-editor": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.4.tgz", + "integrity": "sha1-HtkZnanL/i7y96MbL96LDRI2iXI=", + "dev": true, + "requires": { + "iconv-lite": "0.4.18", + "jschardet": "1.5.0", + "tmp": "0.0.31" + }, + "dependencies": { + "tmp": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "dev": true, + "requires": { + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } + } + } + }, + "extglob": { + "version": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "requires": { + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + }, + "dependencies": { + "is-extglob": { + "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + } + } + }, + "extsprintf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=" + }, + "fancy-log": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.0.tgz", + "integrity": "sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg=", + "requires": { + "chalk": "1.1.3", + "time-stamp": "1.1.0" + } + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, + "fast-diff": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.1.tgz", + "integrity": "sha1-CuoOTmBbaiGJ8Ok21Lf7rxt8/Zs=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fast-stats": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/fast-stats/-/fast-stats-0.0.3.tgz", + "integrity": "sha1-ZQr5Y8P/hcSWo2EPINQM1MFkWU0=" + }, + "fd-slicer": { + "version": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "requires": { + "pend": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "1.2.2", + "object-assign": "4.1.1" + } + }, + "file-type": { + "version": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" + }, + "filename-regex": { + "version": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" + }, + "filename-reserved-regex": { + "version": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz", + "integrity": "sha1-5hz4BfDeHJhFZ9A4bcXfUO5a9+Q=" + }, + "filenamify": { + "version": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz", + "integrity": "sha1-qfL/0RxQO+0wABUCknI3jx8TZaU=", + "requires": { + "filename-reserved-regex": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz", + "strip-outer": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.0.tgz", + "trim-repeated": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz" + } + }, + "fill-range": { + "version": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "requires": { + "is-number": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "isobject": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "randomatic": "1.1.7", + "repeat-element": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + } + }, + "filter-files": { + "version": "https://registry.npmjs.org/filter-files/-/filter-files-0.4.0.tgz", + "integrity": "sha1-uTS5eBoaL42qghGzS7iQlwauuRQ=", + "requires": { + "async": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "is-directory": "https://registry.npmjs.org/is-directory/-/is-directory-0.2.3.tgz" + }, + "dependencies": { + "async": { + "version": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + } + } + }, + "find-index": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", + "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=", + "dev": true + }, + "find-line-column": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/find-line-column/-/find-line-column-0.5.2.tgz", + "integrity": "sha1-2wAjj/hoVRoYLnShA0FtKVqYyMo=", + "dev": true + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "findit2": { + "version": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", + "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" + }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=" + }, + "flat-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", + "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", + "dev": true, + "requires": { + "circular-json": "0.3.1", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, + "for-each": { + "version": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz", + "integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=", + "requires": { + "is-function": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz" + } + }, + "for-in": { + "version": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "requires": { + "for-in": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" + } + }, + "foreach": { + "version": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, + "fs-extra": { + "version": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz", + "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" + } + }, + "function-bind": { + "version": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "1.1.2", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "gaze": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", + "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "dev": true, + "requires": { + "globule": "0.1.0" + } + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" + }, + "get-proxy": { + "version": "https://registry.npmjs.org/get-proxy/-/get-proxy-1.1.0.tgz", + "integrity": "sha1-iUhUSRvFkbDxR9euVw9cZ4tyVus=", + "requires": { + "rc": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "get-stream": { + "version": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "gitlike-cli": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/gitlike-cli/-/gitlike-cli-0.1.0.tgz", + "integrity": "sha1-RiBYcKZ0cSbxyFRvodR9X7BiyZ0=", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "glob-base": { + "version": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "requires": { + "glob-parent": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + }, + "dependencies": { + "glob-parent": { + "version": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "requires": { + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + } + }, + "is-extglob": { + "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + } + } + } + }, + "glob-parent": { + "version": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "path-dirname": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz" + } + }, + "glob-stream": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", + "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", + "requires": { + "extend": "3.0.1", + "glob": "5.0.15", + "glob-parent": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "ordered-read-streams": "0.3.0", + "through2": "0.6.5", + "to-absolute-glob": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", + "unique-stream": "2.2.1" + } + }, + "glob-watcher": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.8.tgz", + "integrity": "sha1-aK62Yefizo02NDgbLsQV8AxrwqQ=", + "dev": true, + "requires": { + "gaze": "0.5.2" + } + }, + "glob2base": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", + "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", + "dev": true, + "requires": { + "find-index": "0.1.1" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } + } + }, + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", + "dev": true, + "requires": { + "glob": "3.1.21", + "lodash": "1.0.2", + "minimatch": "0.2.14" + }, + "dependencies": { + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "dev": true, + "requires": { + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" + } + }, + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", + "dev": true + }, + "inherits": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", + "dev": true + }, + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", + "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=", + "dev": true + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "glogg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", + "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", + "requires": { + "sparkles": "1.0.0" + } + }, + "google-auth-library": { + "version": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-0.10.0.tgz", + "integrity": "sha1-bhW6vuhf0d0U2NEoopW2g41SE24=", + "requires": { + "gtoken": "https://registry.npmjs.org/gtoken/-/gtoken-1.2.2.tgz", + "jws": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", + "lodash.noop": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-3.0.1.tgz", + "request": "2.81.0" + } + }, + "google-p12-pem": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-0.1.2.tgz", + "integrity": "sha1-M8RqsCGqc0+gMys5YKmj/8svMXc=", + "requires": { + "node-forge": "0.7.1" + }, + "dependencies": { + "node-forge": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz", + "integrity": "sha1-naYR6giYL0uUIGs760zJZl8gwwA=" + } + } + }, + "googleapis": { + "version": "https://registry.npmjs.org/googleapis/-/googleapis-16.1.0.tgz", + "integrity": "sha1-Dxny1wVy2RiIGg9ibjsaL6hilXY=", + "requires": { + "async": "https://registry.npmjs.org/async/-/async-2.1.5.tgz", + "google-auth-library": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-0.10.0.tgz", + "string-template": "https://registry.npmjs.org/string-template/-/string-template-1.0.0.tgz" + }, + "dependencies": { + "async": { + "version": "https://registry.npmjs.org/async/-/async-2.1.5.tgz", + "integrity": "sha1-5YfGhYCZSsZ/xW/4bTrFa9voELw=", + "requires": { + "lodash": "4.17.4" + } + } + } + }, + "got": { + "version": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", + "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", + "requires": { + "create-error-class": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "duplexer2": "0.1.4", + "is-redirect": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "is-retry-allowed": "1.1.0", + "is-stream": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "lowercase-keys": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "node-status-codes": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", + "object-assign": "4.1.1", + "parse-json": "2.2.0", + "pinkie-promise": "2.0.1", + "read-all-stream": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", + "readable-stream": "2.3.2", + "timed-out": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", + "unzip-response": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", + "url-parse-lax": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz" + } + }, + "gpagespeed": { + "version": "https://registry.npmjs.org/gpagespeed/-/gpagespeed-4.0.2.tgz", + "integrity": "sha1-l0/6FYUdzDhssEtUK+yha/8xGqw=", + "requires": { + "googleapis": "https://registry.npmjs.org/googleapis/-/googleapis-16.1.0.tgz", + "valid-url": "1.0.9" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "growl": { + "version": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + }, + "gtoken": { + "version": "https://registry.npmjs.org/gtoken/-/gtoken-1.2.2.tgz", + "integrity": "sha1-Fyd2oanZasCfwioA9b6DzubeiCA=", + "requires": { + "google-p12-pem": "0.1.2", + "jws": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", + "mime": "1.3.6", + "request": "2.81.0" + } + }, + "gulp-decompress": { + "version": "https://registry.npmjs.org/gulp-decompress/-/gulp-decompress-1.2.0.tgz", + "integrity": "sha1-jutlpeAV+O2FMsr+KEVJYGJvDcc=", + "requires": { + "archive-type": "https://registry.npmjs.org/archive-type/-/archive-type-3.2.0.tgz", + "decompress": "https://registry.npmjs.org/decompress/-/decompress-3.0.0.tgz", + "gulp-util": "3.0.8", + "readable-stream": "2.3.2" + } + }, + "gulp-rename": { + "version": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.2.tgz", + "integrity": "sha1-OtRCh2PwXidk3sHGfYaNsnVoeBc=" + }, + "gulp-sourcemaps": { + "version": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", + "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", + "requires": { + "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "graceful-fs": "4.1.11", + "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "vinyl": "1.2.0" + }, + "dependencies": { + "through2": { + "version": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "2.3.2", + "xtend": "4.0.1" + } + } + } + }, + "gulp-tap": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/gulp-tap/-/gulp-tap-0.1.3.tgz", + "integrity": "sha1-Eg3KKQHnb7hNXLStXzfK0BVjYeQ=", + "dev": true, + "requires": { + "event-stream": "3.1.7" + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "requires": { + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.0.0", + "fancy-log": "1.3.0", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.0", + "multipipe": "0.1.2", + "object-assign": "3.0.0", + "replace-ext": "0.0.1", + "through2": "2.0.3", + "vinyl": "0.5.3" + }, + "dependencies": { + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "2.3.2", + "xtend": "4.0.1" + } + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "requires": { + "glogg": "1.0.0" + } + }, + "har-schema": { + "version": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "requires": { + "ajv": "4.11.8", + "har-schema": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + } + }, + "has": { + "version": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "requires": { + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "requires": { + "sparkles": "1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "hasbin": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/hasbin/-/hasbin-1.2.3.tgz", + "integrity": "sha1-eMWSaJPIAhXCtWiuH9P8q3omlrA=", + "requires": { + "async": "1.5.2" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "hosted-git-info": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz", + "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc=" + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.1" + } + }, + "iconv-lite": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" + }, + "ignore": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", + "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "2.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "influx": { + "version": "https://registry.npmjs.org/influx/-/influx-5.0.4.tgz", + "integrity": "sha1-aUqdDDBVGLmjRTzMAtrEfkMUFG8=" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=" + }, + "inquirer": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.2.0.tgz", + "integrity": "sha512-4CyUYMP7lOBkiUU1rR24WGrfRX6SucwbY2Mqb1PdApU24wnTIk4TsnkQwV72dDdIKZ2ycLP+fWCV+tA7wwgoew==", + "dev": true, + "requires": { + "ansi-escapes": "2.0.0", + "chalk": "2.0.1", + "cli-cursor": "2.1.0", + "cli-width": "2.1.0", + "external-editor": "2.0.4", + "figures": "2.0.0", + "lodash": "4.17.4", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.1.0.tgz", + "integrity": "sha1-CcIC1ckX7CMYjKpcnLkXnNlUd1A=", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz", + "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==", + "dev": true, + "requires": { + "ansi-styles": "3.1.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.2.0" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "supports-color": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.0.tgz", + "integrity": "sha512-Ts0Mu/A1S1aZxEJNG88I4Oc9rcZSBFNac5e27yh4j2mqbhZSSzR1Ah79EYwSn9Zuh7lrlGD2cVGzw1RKGzyLSg==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "intel": { + "version": "https://registry.npmjs.org/intel/-/intel-1.1.2.tgz", + "integrity": "sha1-Hp3kiA9Q+zt0k76d/EXhzOhrArA=", + "requires": { + "chalk": "1.1.3", + "dbug": "https://registry.npmjs.org/dbug/-/dbug-0.4.2.tgz", + "stack-trace": "0.0.10", + "strftime": "https://registry.npmjs.org/strftime/-/strftime-0.9.2.tgz", + "symbol": "0.3.1", + "utcstring": "https://registry.npmjs.org/utcstring/-/utcstring-0.1.0.tgz" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-absolute": { + "version": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "integrity": "sha1-hHSREZ/MtftDYhfMc39/qtUPYD8=", + "requires": { + "is-relative": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-buffer": { + "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-bzip2": { + "version": "https://registry.npmjs.org/is-bzip2/-/is-bzip2-1.0.0.tgz", + "integrity": "sha1-XuWOqlounIDiFAe+3yOuWsCRs/w=" + }, + "is-callable": { + "version": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" + }, + "is-date-object": { + "version": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-directory": { + "version": "https://registry.npmjs.org/is-directory/-/is-directory-0.2.3.tgz", + "integrity": "sha1-DBbs98rGahrib9ewyAfXUzZoPe0=" + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" + }, + "is-equal-shallow": { + "version": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "requires": { + "is-primitive": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" + } + }, + "is-expression": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-2.1.0.tgz", + "integrity": "sha1-kb6dR968/vB3l36XIr5tz7RGXvA=", + "requires": { + "acorn": "3.3.0", + "object-assign": "4.1.1" + } + }, + "is-extendable": { + "version": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-function": { + "version": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" + }, + "is-glob": { + "version": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + } + }, + "is-gzip": { + "version": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz", + "integrity": "sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM=" + }, + "is-natural-number": { + "version": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.1.1.tgz", + "integrity": "sha1-fUxXKDd+84bD4ZSpkRv1fG3DNec=" + }, + "is-number": { + "version": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "requires": { + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + } + }, + "is-obj": { + "version": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "dev": true, + "requires": { + "is-path-inside": "1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", + "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, + "is-posix-bracket": { + "version": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" + }, + "is-primitive": { + "version": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" + }, + "is-promise": { + "version": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "is-redirect": { + "version": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" + } + }, + "is-relative": { + "version": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz", + "integrity": "sha1-kF/uiuhvRbPsYUvDwVyGnfCHboI=" + }, + "is-resolvable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", + "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", + "dev": true, + "requires": { + "tryit": "1.0.3" + } + }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" + }, + "is-stream": { + "version": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" + }, + "is-tar": { + "version": "https://registry.npmjs.org/is-tar/-/is-tar-1.0.0.tgz", + "integrity": "sha1-L2suF5LB9bs2UZrKqdZcDSb+hT0=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-url": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.2.tgz", + "integrity": "sha1-SYkFpZO/R8wtnn9zg3K792lsfyY=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-valid-glob": { + "version": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", + "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=" + }, + "is-zip": { + "version": "https://registry.npmjs.org/is-zip/-/is-zip-1.0.0.tgz", + "integrity": "sha1-R7Co/004p2QxzP2ZqOFaTIa6IyU=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jest-docblock": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-20.0.3.tgz", + "integrity": "sha1-F76phDQswz2DxQ++FUXqDvqkRxI=", + "dev": true + }, + "jju": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.3.0.tgz", + "integrity": "sha1-2t2e8BkkvHKLA/L3l5vb1i96Kqo=", + "dev": true + }, + "js-base64": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "integrity": "sha1-8OgK4DmkvWVLXygfyT8EqRSn/M4=", + "dev": true + }, + "js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.0.tgz", + "integrity": "sha512-0LoUNELX4S+iofCT8f4uEHIiRBR+c2AINyC8qRWfC6QNruLtxVZRJaPcu/xwMgFIgDxF25tGHaDjvxzJCNE9yw==", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + }, + "js2xmlparser": { + "version": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-1.0.0.tgz", + "integrity": "sha1-WhcPLo1kds5FQF4EgjJCUTeC/jA=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "jschardet": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.5.0.tgz", + "integrity": "sha512-+Q8JsoEQbrdE+a/gg1F9XO92gcKXgpE5UACqr0sIubjDmBEkd+OOWPGzQeMrWSLxd73r4dHxBeRW7edHu5LmJQ==", + "dev": true + }, + "jsdoc": { + "version": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.4.3.tgz", + "integrity": "sha1-5XQNYUXGgfZnnmwXeDqI292XzNM=", + "dev": true, + "requires": { + "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "catharsis": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.8.tgz", + "escape-string-regexp": "1.0.5", + "espree": "https://registry.npmjs.org/espree/-/espree-3.1.7.tgz", + "js2xmlparser": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-1.0.0.tgz", + "klaw": "1.3.1", + "marked": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz", + "mkdirp": "0.5.1", + "requizzle": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", + "strip-json-comments": "2.0.1", + "taffydb": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "underscore": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz" + }, + "dependencies": { + "bluebird": { + "version": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", + "dev": true + }, + "espree": { + "version": "https://registry.npmjs.org/espree/-/espree-3.1.7.tgz", + "integrity": "sha1-/V3ux2qXpRIKnNOnyxF3oJI7EdI=", + "dev": true, + "requires": { + "acorn": "3.3.0", + "acorn-jsx": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz" + } + } + } + }, + "json-parse-helpfulerror": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", + "integrity": "sha1-E/FM4C7tTpgSl7ZOueO5MuLdE9w=", + "dev": true, + "requires": { + "jju": "1.3.0" + } + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" + }, + "jsonfile": { + "version": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "4.1.11" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "requires": { + "is-promise": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "promise": "7.3.1" + } + }, + "junit-report-builder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/junit-report-builder/-/junit-report-builder-1.1.1.tgz", + "integrity": "sha1-+IWufHECgorqAlwEXxHJBifAEpI=", + "requires": { + "date-format": "0.0.2", + "lodash": "3.10.1", + "mkdirp": "0.5.1", + "xmlbuilder": "2.6.5" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + }, + "xmlbuilder": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.5.tgz", + "integrity": "sha1-b/etYPty0idk8AehZLd/K/FABSY=", + "requires": { + "lodash": "3.10.1" + } + } + } + }, + "jwa": { + "version": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", + "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", + "requires": { + "base64url": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", + "buffer-equal-constant-time": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "ecdsa-sig-formatter": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", + "safe-buffer": "5.1.1" + } + }, + "jws": { + "version": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", + "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", + "requires": { + "base64url": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", + "jwa": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", + "safe-buffer": "5.1.1" + } + }, + "keypress": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", + "integrity": "sha1-SjGI1CkbZrT2XtuZ+AaqmuKTWSo=", + "dev": true + }, + "kind-of": { + "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lazystream": { + "version": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "requires": { + "readable-stream": "2.3.2" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "1.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, + "license-checker": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-5.1.2.tgz", + "integrity": "sha1-EHsCIRdHkMH+GZWHy22PoVVkybs=", + "dev": true, + "requires": { + "chalk": "0.5.1", + "debug": "2.6.8", + "mkdirp": "0.3.5", + "nopt": "2.2.1", + "read-installed": "4.0.3", + "treeify": "1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "dev": true + }, + "ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "dev": true + }, + "chalk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "dev": true, + "requires": { + "ansi-styles": "1.1.0", + "escape-string-regexp": "1.0.5", + "has-ansi": "0.1.0", + "strip-ansi": "0.3.0", + "supports-color": "0.2.0" + } + }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "dev": true, + "requires": { + "ansi-regex": "0.2.1" + } + }, + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", + "dev": true + }, + "strip-ansi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "dev": true, + "requires": { + "ansi-regex": "0.2.1" + } + }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "dev": true + } + } + }, + "linez": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/linez/-/linez-4.1.4.tgz", + "integrity": "sha1-Tx2xaWXDoZ45SikxMCPMnLKfAqc=", + "dev": true, + "requires": { + "buffer-equals": "1.0.4", + "iconv-lite": "0.4.18" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "lodash._baseassign": { + "version": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "requires": { + "lodash._basecopy": "3.0.1", + "lodash.keys": "3.1.2" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" + }, + "lodash._basecreate": { + "version": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=" + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=" + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=" + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=" + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=" + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true + }, + "lodash.chunk": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", + "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw=" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.create": { + "version": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "requires": { + "lodash._baseassign": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "lodash._basecreate": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "lodash._isiterateecall": "3.0.9" + } + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "requires": { + "lodash._root": "3.0.1" + } + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.groupby": { + "version": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E=" + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" + }, + "lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=" + }, + "lodash.isequal": { + "version": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "lodash.merge": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.0.tgz", + "integrity": "sha1-aYhLoUSsM/5plzemCG3v+t0PicU=" + }, + "lodash.mergewith": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", + "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=", + "dev": true + }, + "lodash.noop": { + "version": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-3.0.1.tgz", + "integrity": "sha1-OBiPTWUKOkdCWEObluxFsyYXEzw=" + }, + "lodash.pick": { + "version": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" + }, + "lodash.pullall": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.pullall/-/lodash.pullall-4.2.0.tgz", + "integrity": "sha1-nZi4UYt8llsPrkCZvZ+334u/OLo=" + }, + "lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + }, + "lodash.remove": { + "version": "https://registry.npmjs.org/lodash.remove/-/lodash.remove-4.7.0.tgz", + "integrity": "sha1-8x0x58OaBpDVB07A02JxYjNO5iY=" + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=" + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + }, + "lodash.sortby": { + "version": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "requires": { + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" + } + }, + "lodash.union": { + "version": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" + }, + "longest": { + "version": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "longjohn": { + "version": "https://registry.npmjs.org/longjohn/-/longjohn-0.2.12.tgz", + "integrity": "sha1-fKdEawg2VcN351EiE9x1TVKmSn4=", + "requires": { + "source-map-support": "0.4.15" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" + } + }, + "lowercase-keys": { + "version": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=" + }, + "lpad-align": { + "version": "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz", + "integrity": "sha1-IfYArBwwlcPG5JfuZyce4ISB/p4=", + "requires": { + "get-stdin": "4.0.1", + "indent-string": "2.1.0", + "longest": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "meow": "3.7.0" + } + }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "requires": { + "pseudomap": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "yallist": "2.1.2" + } + }, + "lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "requires": { + "es5-ext": "0.10.23" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, + "marked": { + "version": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz", + "integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=", + "dev": true + }, + "memoizee": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.3.10.tgz", + "integrity": "sha1-TsoNiu057J0Bf0xcLy9kMvQuXI8=", + "requires": { + "d": "0.1.1", + "es5-ext": "0.10.23", + "es6-weak-map": "0.1.4", + "event-emitter": "0.3.5", + "lru-queue": "0.1.0", + "next-tick": "0.2.2", + "timers-ext": "0.1.2" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.3.8", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" + } + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "requires": { + "readable-stream": "2.3.2" + } + }, + "micromatch": { + "version": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "requires": { + "arr-diff": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "array-unique": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "braces": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "expand-brackets": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "extglob": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "filename-regex": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "normalize-path": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "object.omit": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "parse-glob": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "regex-cache": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz" + }, + "dependencies": { + "is-extglob": { + "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + } + } + } + }, + "mime": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", + "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=" + }, + "mime-db": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=" + }, + "mime-types": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", + "requires": { + "mime-db": "1.27.0" + } + }, + "mimic-fn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", + "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "mocha": { + "version": "https://registry.npmjs.org/mocha/-/mocha-3.4.2.tgz", + "integrity": "sha1-0O9NMyEm2/GNDWQMmzgt1IvpdZQ=", + "requires": { + "browser-stdout": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "commander": "2.9.0", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.0.tgz", + "diff": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "escape-string-regexp": "1.0.5", + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "growl": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "json3": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "lodash.create": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "mkdirp": "0.5.1", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz" + }, + "dependencies": { + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "requires": { + "graceful-readlink": "1.0.1" + } + }, + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.6.0.tgz", + "integrity": "sha1-vFlryr52F/Edn6FTYe3tVgi4SZs=", + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + } + }, + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=" + }, + "supports-color": { + "version": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "requires": { + "has-flag": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" + } + } + } + }, + "moment": { + "version": "https://registry.npmjs.org/moment/-/moment-2.17.1.tgz", + "integrity": "sha1-/tlQYGPzaxDwZsi1mhRNf66+HYI=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "requires": { + "duplexer2": "0.0.2" + }, + "dependencies": { + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "requires": { + "readable-stream": "1.1.14" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", + "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=" + }, + "natives": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.0.tgz", + "integrity": "sha1-6f+EFBimsux6SV6TmYT3jxY+bjE=" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "next-tick": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-0.2.2.tgz", + "integrity": "sha1-ddpKkn7liH45BliABltzNkE7MQ0=" + }, + "node-forge": { + "version": "https://registry.npmjs.org/node-forge/-/node-forge-0.6.49.tgz", + "integrity": "sha1-8e6V1ddGI5OP4Z1piqWibVTS9g8=" + }, + "node-gyp": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz", + "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=", + "dev": true, + "requires": { + "fstream": "1.0.11", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "nopt": "2.2.1", + "npmlog": "4.1.2", + "osenv": "0.1.4", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "which": "1.2.14" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } + } + }, + "node-sass": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.5.3.tgz", + "integrity": "sha1-0JydEXlkEjnRuX/8YjH9zsU+FWg=", + "dev": true, + "requires": { + "async-foreach": "0.1.3", + "chalk": "1.1.3", + "cross-spawn": "3.0.1", + "gaze": "1.1.2", + "get-stdin": "4.0.1", + "glob": "7.1.2", + "in-publish": "2.0.0", + "lodash.assign": "4.2.0", + "lodash.clonedeep": "4.5.0", + "lodash.mergewith": "4.6.0", + "meow": "3.7.0", + "mkdirp": "0.5.1", + "nan": "2.6.2", + "node-gyp": "3.6.2", + "npmlog": "4.1.2", + "request": "2.81.0", + "sass-graph": "2.2.4", + "stdout-stream": "1.4.0" + }, + "dependencies": { + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "which": "1.2.14" + } + }, + "gaze": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", + "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", + "dev": true, + "requires": { + "globule": "1.2.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "globule": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", + "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", + "dev": true, + "requires": { + "glob": "7.1.2", + "lodash": "4.17.4", + "minimatch": "3.0.4" + } + } + } + }, + "node-slack": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/node-slack/-/node-slack-0.0.7.tgz", + "integrity": "sha1-KgItO1tH6qppP7Fn/L+8rXaXrOc=", + "requires": { + "deferred": "0.7.1", + "request": "2.81.0" + } + }, + "node-status-codes": { + "version": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", + "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=" + }, + "nopt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.2.1.tgz", + "integrity": "sha1-KqCbfRdoSHs7ianFqlIzW/8Lrqc=", + "dev": true, + "requires": { + "abbrev": "1.1.0" + } + }, + "normalize-package-data": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz", + "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=", + "requires": { + "hosted-git-info": "2.4.2", + "is-builtin-module": "1.0.0", + "semver": "5.3.0", + "validate-npm-package-license": "3.0.1" + } + }, + "normalize-path": { + "version": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "1.0.2" + } + }, + "npm-run-path": { + "version": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.2.2.tgz", + "integrity": "sha1-yCEV5PzIiK6hTWTCLk8X9qcNXlo=" + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" + }, + "object.omit": { + "version": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "requires": { + "for-own": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "is-extendable": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + } + }, + "on-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-error/-/on-error-2.1.0.tgz", + "integrity": "sha1-usVuS0mATEkWcVDa0nWbQmERfc4=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "onetime": { + "version": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + } + } + }, + "ordered-read-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", + "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", + "requires": { + "is-stream": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "readable-stream": "2.3.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "1.0.0" + } + }, + "os-tmpdir": { + "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "dev": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } + }, + "p-finally": { + "version": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "pagexray": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/pagexray/-/pagexray-0.14.3.tgz", + "integrity": "sha1-ewclVJb3pKnSU9be8v7P/i5BWfU=", + "requires": { + "fast-stats": "0.0.3", + "minimist": "1.2.0" + } + }, + "parse-glob": { + "version": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "requires": { + "glob-base": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "is-dotfile": "1.0.3", + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + }, + "dependencies": { + "is-extglob": { + "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + } + } + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "1.3.1" + } + }, + "path-dirname": { + "version": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, + "pend": { + "version": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "2.0.4" + } + }, + "pluralize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-4.0.0.tgz", + "integrity": "sha1-WbcIwcAZCi9pLxx2GMRGsFL9F2I=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "preserve": { + "version": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" + }, + "prettier": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.5.3.tgz", + "integrity": "sha1-WdrcaDNF7GuI+IuU7Urn4do5S/4=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "progress": { + "version": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "2.0.5" + } + }, + "promise-polyfill": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-2.1.4.tgz", + "integrity": "sha1-cxkiNTLCasPlVefpvMDPdiAbUa0=" + }, + "pseudomap": { + "version": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "pug": { + "version": "2.0.0-rc.2", + "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-rc.2.tgz", + "integrity": "sha1-B4RVJ3kKssa+Z9z16x8xgECB8Eo=", + "requires": { + "pug-code-gen": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-1.1.1.tgz", + "pug-filters": "2.1.3", + "pug-lexer": "3.1.0", + "pug-linker": "3.0.1", + "pug-load": "2.0.7", + "pug-parser": "3.0.0", + "pug-runtime": "2.0.3", + "pug-strip-comments": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.2.tgz" + } + }, + "pug-attrs": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.2.tgz", + "integrity": "sha1-i+KyIlVo/6ddG4Zpgr/59BEa/8s=", + "requires": { + "constantinople": "3.1.0", + "js-stringify": "1.0.2", + "pug-runtime": "2.0.3" + } + }, + "pug-code-gen": { + "version": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-1.1.1.tgz", + "integrity": "sha1-HPcnRO8qA56uajNAyqoRBYcSWOg=", + "requires": { + "constantinople": "3.1.0", + "doctypes": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "js-stringify": "1.0.2", + "pug-attrs": "2.0.2", + "pug-error": "1.3.2", + "pug-runtime": "2.0.3", + "void-elements": "2.0.1", + "with": "5.1.1" + } + }, + "pug-error": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz", + "integrity": "sha1-U659nSm7A89WRJOgJhCfVMR/XyY=" + }, + "pug-filters": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.3.tgz", + "integrity": "sha1-1ZdnoiDeeX3XVUifZoNM+aqDqlQ=", + "requires": { + "clean-css": "3.4.27", + "constantinople": "3.1.0", + "jstransformer": "1.0.0", + "pug-error": "1.3.2", + "pug-walk": "1.1.3", + "resolve": "1.3.3", + "uglify-js": "2.8.29" + } + }, + "pug-lexer": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-3.1.0.tgz", + "integrity": "sha1-/QhzdtSmdbT1n4/vQiiDQ06VgaI=", + "requires": { + "character-parser": "2.2.0", + "is-expression": "3.0.0", + "pug-error": "1.3.2" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + }, + "is-expression": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", + "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", + "requires": { + "acorn": "4.0.13", + "object-assign": "4.1.1" + } + } + } + }, + "pug-linker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.1.tgz", + "integrity": "sha1-uj+P8hPKjzowSFm0T+0Tynud+hk=", + "requires": { + "pug-error": "1.3.2", + "pug-walk": "1.1.3" + } + }, + "pug-lint": { + "version": "https://registry.npmjs.org/pug-lint/-/pug-lint-2.4.0.tgz", + "integrity": "sha1-H1hMFiT6xI5aDQPkCCPK73NnAuA=", + "dev": true, + "requires": { + "acorn": "4.0.13", + "commander": "2.10.0", + "css-selector-parser": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.3.0.tgz", + "find-line-column": "0.5.2", + "glob": "7.1.2", + "minimatch": "3.0.4", + "path-is-absolute": "1.0.1", + "pug-attrs": "2.0.2", + "pug-error": "1.3.2", + "pug-lexer": "2.3.2", + "resolve": "1.3.3", + "strip-json-comments": "2.0.1", + "void-elements": "2.0.1" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "is-expression": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", + "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", + "dev": true + }, + "pug-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-2.3.2.tgz", + "integrity": "sha1-aLGdlupdwOSoYUiwHLlmwXgVphQ=", + "dev": true, + "requires": { + "character-parser": "2.2.0", + "is-expression": "3.0.0", + "pug-error": "1.3.2" + } + } + } + }, + "pug-lint-config-clock": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-lint-config-clock/-/pug-lint-config-clock-2.0.0.tgz", + "integrity": "sha1-d9uRnNdsQNnc3CLgedICy2xQf1A=", + "dev": true + }, + "pug-load": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.7.tgz", + "integrity": "sha1-Ux0MbhFUYBDphGMNA99AY2fS3nc=", + "requires": { + "object-assign": "4.1.1", + "pug-walk": "1.1.3" + } + }, + "pug-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-3.0.0.tgz", + "integrity": "sha1-N8YZ3YAPZCGHzk1s4aFkzddUh6M=", + "requires": { + "pug-error": "1.3.2", + "token-stream": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz" + } + }, + "pug-runtime": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.3.tgz", + "integrity": "sha1-mBYmB7D86eJU1CfzOYelrucWi9o=" + }, + "pug-strip-comments": { + "version": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.2.tgz", + "integrity": "sha1-0xOvoBvMN0mA4TmeI+vy65vchRM=", + "requires": { + "pug-error": "1.3.2" + } + }, + "pug-walk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.3.tgz", + "integrity": "sha1-181bI9s8qHxjbIaglz+c2OAwQ2w=" + }, + "punycode": { + "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + } + } + } + }, + "rc": { + "version": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "requires": { + "deep-extend": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "ini": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + } + }, + "read-all-stream": { + "version": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", + "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", + "requires": { + "pinkie-promise": "2.0.1", + "readable-stream": "2.3.2" + } + }, + "read-installed": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", + "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=", + "dev": true, + "requires": { + "debuglog": "1.0.1", + "graceful-fs": "4.1.11", + "read-package-json": "2.0.5", + "readdir-scoped-modules": "1.0.2", + "semver": "5.3.0", + "slide": "1.1.6", + "util-extend": "1.0.3" + } + }, + "read-package-json": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.0.5.tgz", + "integrity": "sha1-+Tpk5kFSnfaKCMZN5GOJ6KP4iEU=", + "dev": true, + "requires": { + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "json-parse-helpfulerror": "1.0.3", + "normalize-package-data": "2.3.8" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.3.8", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "readable-stream": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", + "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "readdir-scoped-modules": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz", + "integrity": "sha1-n6+jfShr5dksuuve4DDcm19AZ0c=", + "dev": true, + "requires": { + "debuglog": "1.0.1", + "dezalgo": "1.0.3", + "graceful-fs": "4.1.11", + "once": "1.4.0" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "2.1.0", + "strip-indent": "1.0.1" + } + }, + "regex-cache": { + "version": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", + "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=", + "requires": { + "is-equal-shallow": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "is-primitive": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" + } + }, + "remove-trailing-separator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz", + "integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE=" + }, + "repeat-element": { + "version": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + }, + "repeat-string": { + "version": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "1.0.2" + } + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + }, + "dependencies": { + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "0.1.0", + "resolve-from": "1.0.1" + } + }, + "requizzle": { + "version": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", + "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", + "dev": true, + "requires": { + "underscore": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + }, + "dependencies": { + "underscore": { + "version": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + } + } + }, + "resolve": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz", + "integrity": "sha1-ZVkHw0aahoDcLeOidaj91paR8OU=", + "requires": { + "path-parse": "1.0.5" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "3.0.2" + }, + "dependencies": { + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.1.0" + } + } + } + }, + "resumer": { + "version": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "requires": { + "through": "2.3.8" + } + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "0.1.4" + } + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "requires": { + "glob": "7.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } + } + }, + "robots-parser": { + "version": "https://registry.npmjs.org/robots-parser/-/robots-parser-1.0.1.tgz", + "integrity": "sha1-eeMuPly0Bm3lAUBCB6jrEjKXACA=" + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "4.0.8" + } + }, + "s3": { + "version": "https://registry.npmjs.org/s3/-/s3-4.4.0.tgz", + "integrity": "sha1-VqT3dVFae2ucjlxrGrUfkDdmnx8=", + "requires": { + "aws-sdk": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.0.31.tgz", + "fd-slicer": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "findit2": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "mime": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "mkdirp": "0.5.1", + "pend": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "streamsink": "https://registry.npmjs.org/streamsink/-/streamsink-1.2.0.tgz" + }, + "dependencies": { + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "requires": { + "natives": "1.1.0" + } + }, + "mime": { + "version": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" + }, + "rimraf": { + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + } + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "requires": { + "glob": "7.1.2", + "lodash": "4.17.4", + "scss-tokenizer": "0.2.3", + "yargs": "7.1.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "5.0.0" + } + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "requires": { + "js-base64": "2.1.9", + "source-map": "0.4.4" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "seek-bzip": { + "version": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", + "requires": { + "commander": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz" + }, + "dependencies": { + "commander": { + "version": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": "1.0.1" + } + } + } + }, + "selenium-webdriver": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.4.0.tgz", + "integrity": "sha1-FR90RSlNpqZsScwwB0eioX5TxSo=", + "requires": { + "adm-zip": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", + "rimraf": "2.6.1", + "tmp": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "xml2js": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-immediate-shim": { + "version": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, + "shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" + }, + "shebang-command": { + "version": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" + } + }, + "shebang-regex": { + "version": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simplecrawler": { + "version": "https://registry.npmjs.org/simplecrawler/-/simplecrawler-1.1.0.tgz", + "integrity": "sha1-P6MdghUegrjqKeqg5TUaxMGA4ak=", + "requires": { + "async": "2.4.1", + "iconv-lite": "0.4.18", + "robots-parser": "https://registry.npmjs.org/robots-parser/-/robots-parser-1.0.1.tgz", + "urijs": "1.18.10" + }, + "dependencies": { + "async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.4.1.tgz", + "integrity": "sha1-YqVrJ5yYoR0JhwlqAcw+6463u9c=", + "requires": { + "lodash": "4.17.4" + } + } + } + }, + "sleep": { + "version": "https://registry.npmjs.org/sleep/-/sleep-3.0.1.tgz", + "integrity": "sha1-vk0XxXk2DgfgTtgXK6KxCmkFTfM=", + "optional": true, + "requires": { + "nan": "2.6.2" + } + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "sltc": { + "version": "https://registry.npmjs.org/sltc/-/sltc-0.6.0.tgz", + "integrity": "sha1-KJD3P3sg6qVF7sLwtWq1Ub18y+c=", + "requires": { + "hasbin": "1.2.2", + "intel": "https://registry.npmjs.org/intel/-/intel-1.1.2.tgz", + "lodash.merge": "4.6.0", + "minimist": "1.2.0" + }, + "dependencies": { + "async": { + "version": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "hasbin": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/hasbin/-/hasbin-1.2.2.tgz", + "integrity": "sha1-ESdAbedtLZ6F6ndPma5Ka/LcQY0=", + "requires": { + "async": "https://registry.npmjs.org/async/-/async-1.5.2.tgz" + } + } + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.16.3" + } + }, + "soprano-saxophone": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/soprano-saxophone/-/soprano-saxophone-0.18.0.tgz", + "integrity": "sha512-zsB3xYsbT5Z4uPxTQr9HtkyW8wzAjl+UiVz/4JAuAwggkI1IBotLk8zyrDeIdFhjUIjX87AjMcNvvLihl0xQGA==", + "requires": { + "download": "https://registry.npmjs.org/download/-/download-4.4.3.tgz", + "download-status": "https://registry.npmjs.org/download-status/-/download-status-2.2.1.tgz" + } + }, + "source-map": { + "version": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" + }, + "source-map-support": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", + "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=", + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } + }, + "sparkles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", + "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=" + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "requires": { + "spdx-license-ids": "1.2.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=" + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=" + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "requires": { + "through": "2.3.8" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "stat-mode": { + "version": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz", + "integrity": "sha1-5sgLYjEj19gM8TLOU480YokHJQI=" + }, + "stdout-stream": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", + "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", + "dev": true, + "requires": { + "readable-stream": "2.3.2" + } + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "requires": { + "duplexer": "0.1.1" + } + }, + "stream-combiner2": { + "version": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "requires": { + "duplexer2": "0.1.4", + "readable-stream": "2.3.2" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + }, + "stream-transform": { + "version": "https://registry.npmjs.org/stream-transform/-/stream-transform-0.1.2.tgz", + "integrity": "sha1-fY5rTgOsR4F3j4x5UXUBv7B2Kp8=" + }, + "streamsink": { + "version": "https://registry.npmjs.org/streamsink/-/streamsink-1.2.0.tgz", + "integrity": "sha1-76/unx4i01ke1949yqlcP1559zw=" + }, + "strftime": { + "version": "https://registry.npmjs.org/strftime/-/strftime-0.9.2.tgz", + "integrity": "sha1-vMooYfKUVtNyqvaheBHIvG859YM=" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "string-template": { + "version": "https://registry.npmjs.org/string-template/-/string-template-1.0.0.tgz", + "integrity": "sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y=" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string.prototype.trim": { + "version": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", + "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "requires": { + "define-properties": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "es-abstract": "1.7.0", + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", + "requires": { + "first-chunk-stream": "1.0.0", + "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + } + }, + "strip-dirs": { + "version": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "integrity": "sha1-lgu9EoeETzl1pFWKoQOoJV4kVqA=", + "requires": { + "chalk": "1.1.3", + "get-stdin": "4.0.1", + "is-absolute": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "is-natural-number": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.1.1.tgz", + "minimist": "1.2.0", + "sum-up": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz" + } + }, + "strip-eof": { + "version": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "strip-outer": { + "version": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.0.tgz", + "integrity": "sha1-qsC6YNLpDF1PJ1/Yhp/ZotMQ/7g=", + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "sum-up": { + "version": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz", + "integrity": "sha1-HGYfZnBX9jvLeHWqFDi8FiUlFW4=", + "requires": { + "chalk": "1.1.3" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "symbol": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/symbol/-/symbol-0.3.1.tgz", + "integrity": "sha1-tvmpANSWpX8CQI8iGYwQndoGMEE=" + }, + "table": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.1.tgz", + "integrity": "sha1-qBFsEz+sLGH0pCCrbN9cTWHw5DU=", + "dev": true, + "requires": { + "ajv": "4.11.8", + "ajv-keywords": "1.5.1", + "chalk": "1.1.3", + "lodash": "4.17.4", + "slice-ansi": "0.0.4", + "string-width": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "taffydb": { + "version": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "dev": true + }, + "tape": { + "version": "https://registry.npmjs.org/tape/-/tape-4.6.3.tgz", + "integrity": "sha1-Y353WB6ass4XV36b1M5PV1gG2LY=", + "requires": { + "deep-equal": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "defined": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "for-each": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz", + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "glob": "7.1.2", + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "inherits": "2.0.3", + "minimist": "1.2.0", + "object-inspect": "1.2.2", + "resolve": "1.1.7", + "resumer": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "string.prototype.trim": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", + "through": "2.3.8" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + } + } + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-stream": { + "version": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz", + "integrity": "sha1-NlSc8E7RrumyowwBQyUiONr5QBY=", + "requires": { + "bl": "1.2.1", + "end-of-stream": "1.4.0", + "readable-stream": "2.3.2", + "xtend": "4.0.1" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", + "requires": { + "through2": "2.0.3", + "xtend": "4.0.1" + }, + "dependencies": { + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "2.3.2", + "xtend": "4.0.1" + } + } + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" + }, + "timed-out": { + "version": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", + "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=" + }, + "timers-ext": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.2.tgz", + "integrity": "sha1-YcxHp2wavTGV8UUn+XjViulMUgQ=", + "requires": { + "es5-ext": "0.10.23", + "next-tick": "1.0.0" + }, + "dependencies": { + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + } + } + }, + "tmp": { + "version": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "requires": { + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } + }, + "to-absolute-glob": { + "version": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", + "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", + "requires": { + "extend-shallow": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" + } + }, + "token-stream": { + "version": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", + "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" + }, + "tough-cookie": { + "version": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "requires": { + "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + }, + "treeify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.0.1.tgz", + "integrity": "sha1-abPNAiAioWhCTnz6HO1EyTnT6y8=", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "trim-repeated": { + "version": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "tryit": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", + "dev": true + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=" + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + }, + "type-detect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", + "dev": true + }, + "typedarray": { + "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "yargs": "3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "underscore": { + "version": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", + "dev": true + }, + "underscore-contrib": { + "version": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", + "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", + "dev": true, + "requires": { + "underscore": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + }, + "dependencies": { + "underscore": { + "version": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + } + } + }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "requires": { + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" + } + }, + "unzip-response": { + "version": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", + "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=" + }, + "urijs": { + "version": "1.18.10", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.18.10.tgz", + "integrity": "sha1-uURj6rpZoaeWA2pGe7YzxmfyIas=" + }, + "url-parse-lax": { + "version": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz" + } + }, + "utcstring": { + "version": "https://registry.npmjs.org/utcstring/-/utcstring-0.1.0.tgz", + "integrity": "sha1-Qw/VEKt/yVtdWRDJAteYgMIIQ2s=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util-extend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", + "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=", + "dev": true + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "vali-date": { + "version": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", + "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=" + }, + "valid-url": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", + "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=" + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } + }, + "verror": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "requires": { + "extsprintf": "1.0.2" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-assign": { + "version": "https://registry.npmjs.org/vinyl-assign/-/vinyl-assign-1.2.1.tgz", + "integrity": "sha1-TRmIkbVRWRHXcajNnFSApGoHSkU=", + "requires": { + "object-assign": "4.1.1", + "readable-stream": "2.3.2" + } + }, + "vinyl-fs": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", + "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", + "requires": { + "duplexify": "3.5.0", + "glob-stream": "5.3.5", + "graceful-fs": "4.1.11", + "gulp-sourcemaps": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", + "is-valid-glob": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", + "lazystream": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "lodash.isequal": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "merge-stream": "1.0.1", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "readable-stream": "2.3.2", + "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "strip-bom-stream": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "through2": "2.0.3", + "through2-filter": "2.0.0", + "vali-date": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", + "vinyl": "1.2.0" + }, + "dependencies": { + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "2.3.2", + "xtend": "4.0.1" + } + } + } + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + }, + "ware": { + "version": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz", + "integrity": "sha1-0bFPOdLiy0q4xAmPdW/ksWTkc9Q=", + "requires": { + "wrap-fn": "https://registry.npmjs.org/wrap-fn/-/wrap-fn-0.1.5.tgz" + } + }, + "webcoach": { + "version": "https://registry.npmjs.org/webcoach/-/webcoach-0.34.1.tgz", + "integrity": "sha1-AdiuFpjL/E4jDu8Y3DN7hW9r8mQ=", + "requires": { + "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "browsertime": "https://registry.npmjs.org/browsertime/-/browsertime-1.2.2.tgz", + "chalk": "1.1.3", + "cli-table2": "https://registry.npmjs.org/cli-table2/-/cli-table2-0.2.0.tgz", + "filter-files": "https://registry.npmjs.org/filter-files/-/filter-files-0.4.0.tgz", + "json-stable-stringify": "1.0.1", + "lodash.clonedeep": "4.5.0", + "lodash.groupby": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "lodash.merge": "4.6.0", + "lodash.sortby": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "pagexray": "https://registry.npmjs.org/pagexray/-/pagexray-0.14.1.tgz", + "word-wrap": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.1.tgz", + "yargs": "https://registry.npmjs.org/yargs/-/yargs-7.0.2.tgz" + }, + "dependencies": { + "alto-saxophone": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/alto-saxophone/-/alto-saxophone-2.29.0.tgz", + "integrity": "sha1-w/zjRcY51LvP/QRLfKqJf+ZlrH4=", + "requires": { + "download": "https://registry.npmjs.org/download/-/download-4.4.3.tgz", + "download-status": "https://registry.npmjs.org/download-status/-/download-status-2.2.1.tgz" + } + }, + "browsertime": { + "version": "https://registry.npmjs.org/browsertime/-/browsertime-1.2.2.tgz", + "integrity": "sha1-f0k4w5eoUEelM6Sm3zp1lwxlXwA=", + "requires": { + "adbkit": "https://registry.npmjs.org/adbkit/-/adbkit-2.8.1.tgz", + "alto-saxophone": "2.29.0", + "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "chrome-har": "0.1.0", + "execa": "0.6.1", + "fast-stats": "0.0.3", + "hasbin": "1.2.3", + "intel": "https://registry.npmjs.org/intel/-/intel-1.1.2.tgz", + "lodash.foreach": "4.5.0", + "lodash.get": "4.4.2", + "lodash.groupby": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "lodash.isempty": "4.4.0", + "lodash.merge": "4.6.0", + "lodash.pick": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "lodash.remove": "https://registry.npmjs.org/lodash.remove/-/lodash.remove-4.7.0.tgz", + "lodash.set": "4.3.2", + "mkdirp": "0.5.1", + "moment": "https://registry.npmjs.org/moment/-/moment-2.17.1.tgz", + "selenium-webdriver": "3.4.0", + "sltc": "https://registry.npmjs.org/sltc/-/sltc-0.6.0.tgz", + "soprano-saxophone": "0.15.0", + "valid-url": "1.0.9", + "xvfb": "https://registry.npmjs.org/xvfb/-/xvfb-0.2.3.tgz", + "yargs": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz" + }, + "dependencies": { + "yargs": { + "version": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "5.0.0" + } + } + } + }, + "camelcase": { + "version": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "chrome-har": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chrome-har/-/chrome-har-0.1.0.tgz", + "integrity": "sha1-7T/vqbMwm1EWLi0w0N/YbC99odI=", + "requires": { + "debug": "2.6.8", + "moment": "https://registry.npmjs.org/moment/-/moment-2.17.1.tgz", + "tough-cookie": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz" + } + }, + "pagexray": { + "version": "https://registry.npmjs.org/pagexray/-/pagexray-0.14.1.tgz", + "integrity": "sha1-WsvELk085qvYgDmz0wKZ3bbwnr0=", + "requires": { + "fast-stats": "0.0.3", + "minimist": "1.2.0" + } + }, + "soprano-saxophone": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/soprano-saxophone/-/soprano-saxophone-0.15.0.tgz", + "integrity": "sha1-pRxy6jpVj48mVs6H/RtADJqAEzs=", + "requires": { + "download": "https://registry.npmjs.org/download/-/download-4.4.3.tgz", + "download-status": "https://registry.npmjs.org/download-status/-/download-status-2.2.1.tgz" + } + }, + "yargs": { + "version": "https://registry.npmjs.org/yargs/-/yargs-7.0.2.tgz", + "integrity": "sha1-EVuX3xMhgj6Lhkjolox4JSEiH2c=", + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "5.0.0" + } + } + } + }, + "webpagetest": { + "version": "https://registry.npmjs.org/webpagetest/-/webpagetest-0.3.5.tgz", + "integrity": "sha1-ySX0g45AiHW1t0CRmvOXDxwsMfI=", + "requires": { + "commander": "2.10.0", + "csv": "https://registry.npmjs.org/csv/-/csv-1.1.1.tgz", + "entities": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "mocha": "https://registry.npmjs.org/mocha/-/mocha-3.4.2.tgz", + "xml2js": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz" + } + }, + "which": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", + "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "requires": { + "isexe": "2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "dev": true, + "requires": { + "string-width": "1.0.2" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "with": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", + "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", + "requires": { + "acorn": "3.3.0", + "acorn-globals": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz" + } + }, + "word-wrap": { + "version": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.1.tgz", + "integrity": "sha1-JI9Fm0ZdF5oXvEB8hU0xUdB+Rdg=" + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + } + }, + "wrap-fn": { + "version": "https://registry.npmjs.org/wrap-fn/-/wrap-fn-0.1.5.tgz", + "integrity": "sha1-8htuQQFv9KfjFyDbxjoJAWvfmEU=", + "requires": { + "co": "https://registry.npmjs.org/co/-/co-3.1.0.tgz" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "0.5.1" + } + }, + "xml2js": { + "version": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", + "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", + "requires": { + "sax": "1.2.4", + "xmlbuilder": "4.2.1" + } + }, + "xmlbuilder": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", + "requires": { + "lodash": "4.17.4" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "xvfb": { + "version": "https://registry.npmjs.org/xvfb/-/xvfb-0.2.3.tgz", + "integrity": "sha1-VYipaHVFk5E/M8DA4su3N0EjWDI=", + "requires": { + "sleep": "https://registry.npmjs.org/sleep/-/sleep-3.0.1.tgz" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "4.2.1" + }, + "dependencies": { + "camelcase": { + "version": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz" + } + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "requires": { + "camelcase": "3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + } + } + }, + "yauzl": { + "version": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz", + "integrity": "sha1-eUUK/yKyqcWkHvVOAtuQfM+/nuI=", + "requires": { + "buffer-crc32": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "fd-slicer": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz" + } + } + } +} diff --git a/package.json b/package.json index 189438b95..b31e3aa84 100644 --- a/package.json +++ b/package.json @@ -35,14 +35,15 @@ "url": "https://github.com/sitespeedio/sitespeed.io/issues" }, "scripts": { - "lint": "npm run eslint && npm run pug-lint", - "eslint": "eslint .", + "lint": "eslint . && npm run pug-lint", + "lint:fix": "eslint . --fix", + "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check", "eclint": "eclint check * lib/**/* bin/**/* tools/**/* !*.iml", "eclint:fix": "eclint fix * lib/**/* bin/**/* tools/**/* !*.iml", "pug-lint": "pug-lint lib/plugins/html/templates", "test": "mocha", "check-licenses": "tools/check-licenses.js", - "travis": "npm run lint && npm run test", + "travis": "npm run eslint-check && npm run lint && npm run test", "build:css": "node-sass lib/plugins/html/src/sass/main.scss > lib/plugins/html/assets/css/index.css && cleancss -o lib/plugins/html/assets/css/index.min.css lib/plugins/html/assets/css/index.css" }, "engines": { @@ -53,11 +54,14 @@ "chai-as-promised": "^6.0.0", "clean-css-cli": "^4.0.7", "eclint": "^1.1.5", - "eslint": "^3.10.2", + "eslint": "^4.2.0", + "eslint-config-prettier": "^2.3.0", + "eslint-plugin-prettier": "^2.1.2", "jsdoc": "^3.3.3", "license-checker": "^5.1.2", "mocha": "^3.1.2", "node-sass": "^4.5.0", + "prettier": "^1.5.3", "pug-lint": "^2.3.0", "pug-lint-config-clock": "^2.0.0" }, diff --git a/test/cliUtilTests.js b/test/cliUtilTests.js index 1fb9e8c89..fa5105883 100644 --- a/test/cliUtilTests.js +++ b/test/cliUtilTests.js @@ -1,29 +1,39 @@ 'use strict'; const cliUtil = require('../lib/support/cliUtil'), - expect = require('chai').expect; + expect = require('chai').expect; describe('cliUtil', function() { describe('getURLs', function() { it('should extract urls', function() { - let urls = cliUtil.getURLs(["test/fixtures/sitespeed-urls.txt"]); + let urls = cliUtil.getURLs(['test/fixtures/sitespeed-urls.txt']); expect(urls[0] === 'https://www.sitespeed.io'); expect(urls[3] === 'https://www.sitespeed.io/documentation/faq'); - - urls = cliUtil.getURLs(["test/fixtures/sitespeed-urls-aliases.txt"]); + + urls = cliUtil.getURLs(['test/fixtures/sitespeed-urls-aliases.txt']); expect(urls[0] === 'https://www.sitespeed.io'); expect(urls[3] === 'https://www.sitespeed.io/documentation/faq'); }); }); describe('getAliases', function() { it('should extract aliases', function() { - let aliases = cliUtil.getAliases(["test/fixtures/sitespeed-urls.txt"]); + let aliases = cliUtil.getAliases(['test/fixtures/sitespeed-urls.txt']); expect(aliases['https://www.sitespeed.io']).to.be.empty; - expect(aliases['https://www.sitespeed.io/documentation/sitespeed.io/webpagetest/']).to.be.empty; + expect( + aliases[ + 'https://www.sitespeed.io/documentation/sitespeed.io/webpagetest/' + ] + ).to.be.empty; - aliases = cliUtil.getAliases(["test/fixtures/sitespeed-urls-aliases.txt"]); + aliases = cliUtil.getAliases([ + 'test/fixtures/sitespeed-urls-aliases.txt' + ]); expect(aliases['https://www.sitespeed.io'].alias === 'Home_Page'); - expect(aliases['https://www.sitespeed.io/documentation/sitespeed.io/webpagetest/']).to.be.empty; + expect( + aliases[ + 'https://www.sitespeed.io/documentation/sitespeed.io/webpagetest/' + ] + ).to.be.empty; }); }); }); diff --git a/test/coachTests.js b/test/coachTests.js index 7a9568c73..6ad9e7c92 100644 --- a/test/coachTests.js +++ b/test/coachTests.js @@ -11,7 +11,7 @@ const coachRun = JSON.parse(fs.readFileSync(coachRunPath, 'utf8')); describe('coach', function() { describe('aggregator', function() { it('should summarize data', function() { - aggregator.addToAggregate(coachRun,'www.sitespeed.io'); + aggregator.addToAggregate(coachRun, 'www.sitespeed.io'); expect(aggregator.summarize()).to.not.be.empty; }); diff --git a/test/domainTests.js b/test/domainTests.js index fc93435d9..2d9360544 100644 --- a/test/domainTests.js +++ b/test/domainTests.js @@ -13,10 +13,14 @@ describe('domains', function() { let har; beforeEach(function() { - return fs.readFileAsync(path.resolve(__dirname, 'fixtures', 'www-theverge-com.har'), 'utf8') + return fs + .readFileAsync( + path.resolve(__dirname, 'fixtures', 'www-theverge-com.har'), + 'utf8' + ) .then(JSON.parse) - .tap((data) => { - har = data + .tap(data => { + har = data; }); }); diff --git a/test/graphiteTests.js b/test/graphiteTests.js index 931006cd1..846dba07d 100644 --- a/test/graphiteTests.js +++ b/test/graphiteTests.js @@ -8,24 +8,28 @@ describe('graphite', function() { describe('dataGenerator', function() { it('should generate data for gpsi.pageSummary', function() { const message = { - "type": "gpsi.pageSummary", - "timestamp": "2016-01-08T12:59:06+01:00", - "source": "gpsi", - "data": { - "median": "13", - "mean": "14.42", - "min": "13", - "p10": "13", - "p70": "16", - "p80": "16", - "p90": "16", - "p99": "16", - "max": "16" + type: 'gpsi.pageSummary', + timestamp: '2016-01-08T12:59:06+01:00', + source: 'gpsi', + data: { + median: '13', + mean: '14.42', + min: '13', + p10: '13', + p70: '16', + p80: '16', + p90: '16', + p99: '16', + max: '16' }, - 'url': 'http://sub.domain.com/foo/bar' + url: 'http://sub.domain.com/foo/bar' }; - let generator = new DataGenerator('ns', false, {_:['filename'], browser:'chrome', connectivity: 'cable'}); + let generator = new DataGenerator('ns', false, { + _: ['filename'], + browser: 'chrome', + connectivity: 'cable' + }); var data = generator.dataFromMessage(message, moment()); expect(data).to.match(/ns.pageSummary.sub_domain_com/); @@ -35,29 +39,34 @@ describe('graphite', function() { it('should generate data for domains.summary', function() { const message = { - "type": "domains.summary", - "timestamp": "2016-01-08T12:59:06+01:00", - "source": "domains", - "data": { - "www.sitespeed.io": { - "dns": { - "median": "0", - "mean": "13", - "min": "0", - "p10": "0", - "p90": "40", - "p99": "40", - "max": "40" + type: 'domains.summary', + timestamp: '2016-01-08T12:59:06+01:00', + source: 'domains', + data: { + 'www.sitespeed.io': { + dns: { + median: '0', + mean: '13', + min: '0', + p10: '0', + p90: '40', + p99: '40', + max: '40' } } }, - 'group': 'sub_domain_com' + group: 'sub_domain_com' }; - let generator = new DataGenerator('ns', false, {_:['sub_domain_com'], browser:'chrome', connectivity: 'cable'}); + let generator = new DataGenerator('ns', false, { + _: ['sub_domain_com'], + browser: 'chrome', + connectivity: 'cable' + }); var data = generator.dataFromMessage(message, moment()); - expect(data).to.match(/ns.summary.sub_domain_com.chrome.cable.domains.www.sitespeed.io.dns.median/); + expect(data).to.match( + /ns.summary.sub_domain_com.chrome.cable.domains.www.sitespeed.io.dns.median/ + ); }); - }); }); diff --git a/test/influxdbTests.js b/test/influxdbTests.js index f83dc0353..4dc56bb3b 100644 --- a/test/influxdbTests.js +++ b/test/influxdbTests.js @@ -8,344 +8,343 @@ describe('influxdb', function() { describe('dataGenerator', function() { it('should generate data for coach.summary', function() { const message = { - "uuid": "33774328-e781-4152-babe-a367cee27153", - "type": "coach.summary", - "timestamp": "2017-04-04T09:55:59+02:00", - "source": "coach", - "data": { - "score": { - "median": "96", - "mean": "96", - "min": "96", - "p90": "96", - "max": "96" + uuid: '33774328-e781-4152-babe-a367cee27153', + type: 'coach.summary', + timestamp: '2017-04-04T09:55:59+02:00', + source: 'coach', + data: { + score: { + median: '96', + mean: '96', + min: '96', + p90: '96', + max: '96' }, - "accessibility": { - "score": { - "median": "95", - "mean": "95", - "min": "95", - "p90": "95", - "max": "95" + accessibility: { + score: { + median: '95', + mean: '95', + min: '95', + p90: '95', + max: '95' }, - "altImages": { - "median": "80", - "mean": "80", - "min": "80", - "p90": "80", - "max": "80" + altImages: { + median: '80', + mean: '80', + min: '80', + p90: '80', + max: '80' }, - "headings": { - "median": "90", - "mean": "90", - "min": "90", - "p90": "90", - "max": "90" + headings: { + median: '90', + mean: '90', + min: '90', + p90: '90', + max: '90' }, - "labelOnInput": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + labelOnInput: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "landmarks": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + landmarks: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "neverSuppressZoom": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + neverSuppressZoom: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "sections": { - "median": "0", - "mean": "0", - "min": "0", - "p90": "0", - "max": "0" + sections: { + median: '0', + mean: '0', + min: '0', + p90: '0', + max: '0' }, - "table": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + table: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' } }, - "bestpractice": { - "score": { - "median": "85", - "mean": "85", - "min": "85", - "p90": "85", - "max": "85" + bestpractice: { + score: { + median: '85', + mean: '85', + min: '85', + p90: '85', + max: '85' }, - "charset": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + charset: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "doctype": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + doctype: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "https": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + https: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "httpsH2": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + httpsH2: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "language": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + language: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "metaDescription": { - "median": "50", - "mean": "50", - "min": "50", - "p90": "50", - "max": "50" + metaDescription: { + median: '50', + mean: '50', + min: '50', + p90: '50', + max: '50' }, - "optimizely": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + optimizely: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "pageTitle": { - "median": "50", - "mean": "50", - "min": "50", - "p90": "50", - "max": "50" + pageTitle: { + median: '50', + mean: '50', + min: '50', + p90: '50', + max: '50' }, - "spdy": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + spdy: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "url": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + url: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' } }, - "performance": { - "score": { - "median": "98", - "mean": "98", - "min": "98", - "p90": "98", - "max": "98" + performance: { + score: { + median: '98', + mean: '98', + min: '98', + p90: '98', + max: '98' }, - "avoidScalingImages": { - "median": "50", - "mean": "50", - "min": "50", - "p90": "50", - "max": "50" + avoidScalingImages: { + median: '50', + mean: '50', + min: '50', + p90: '50', + max: '50' }, - "cssPrint": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + cssPrint: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "fastRender": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + fastRender: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "inlineCss": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + inlineCss: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "jquery": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + jquery: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "spof": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + spof: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "thirdPartyAsyncJs": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + thirdPartyAsyncJs: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "assetsRedirects": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + assetsRedirects: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "cacheHeaders": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + cacheHeaders: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "cacheHeadersLong": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + cacheHeadersLong: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "compressAssets": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + compressAssets: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "connectionKeepAlive": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + connectionKeepAlive: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "cssSize": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + cssSize: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "documentRedirect": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + documentRedirect: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "favicon": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + favicon: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "fewFonts": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + fewFonts: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "fewRequestsPerDomain": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + fewRequestsPerDomain: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "headerSize": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + headerSize: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "imageSize": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + imageSize: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "javascriptSize": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + javascriptSize: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "mimeTypes": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + mimeTypes: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "optimalCssSize": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + optimalCssSize: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "pageSize": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + pageSize: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "privateAssets": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + privateAssets: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' }, - "responseOk": { - "median": "100", - "mean": "100", - "min": "100", - "p90": "100", - "max": "100" + responseOk: { + median: '100', + mean: '100', + min: '100', + p90: '100', + max: '100' } } }, - "group": "www.sitespeed.io" + group: 'www.sitespeed.io' }; - let generator = new DataGenerator(false, { _: ['filename'], browser: 'chrome', diff --git a/test/metricsFilterTests.js b/test/metricsFilterTests.js index 415b93151..cca726dcd 100644 --- a/test/metricsFilterTests.js +++ b/test/metricsFilterTests.js @@ -5,52 +5,74 @@ let metricsFilter = require('../lib/support/metricsFilter'), path = require('path'), expect = require('chai').expect; -const wptResultPath = path.resolve(__dirname, '..', 'node_modules', - 'webpagetest', 'test', 'fixtures', 'responses', 'testResults.json'); +const wptResultPath = path.resolve( + __dirname, + '..', + 'node_modules', + 'webpagetest', + 'test', + 'fixtures', + 'responses', + 'testResults.json' +); const wptResult = JSON.parse(fs.readFileSync(wptResultPath, 'utf8')); describe('metricsFilter', () => { describe('#filterMetrics', () => { - it('should filter a single metric', () => { - const filtered = metricsFilter.filterMetrics(wptResult, 'data.median.firstView.TTFB'); + const filtered = metricsFilter.filterMetrics( + wptResult, + 'data.median.firstView.TTFB' + ); - expect(filtered).to.deep.equal({data: {median: {firstView: {TTFB: 503}}}}); + expect(filtered).to.deep.equal({ + data: { median: { firstView: { TTFB: 503 } } } + }); }); it('should skip missing metric', () => { - const filtered = metricsFilter.filterMetrics(wptResult, 'data.median.firstView.TTTTTTFB'); + const filtered = metricsFilter.filterMetrics( + wptResult, + 'data.median.firstView.TTTTTTFB' + ); expect(filtered).to.deep.equal({}); }); it('should filter multiple metrics', () => { - const filtered = metricsFilter.filterMetrics(wptResult, - ['data.median.firstView.TTFB', 'data.median.repeatView.TTFB']); + const filtered = metricsFilter.filterMetrics(wptResult, [ + 'data.median.firstView.TTFB', + 'data.median.repeatView.TTFB' + ]); expect(filtered).to.deep.equal({ data: { median: { - firstView: {TTFB: 503}, - repeatView: {TTFB: 362} + firstView: { TTFB: 503 }, + repeatView: { TTFB: 362 } } } }); }); it('should filter with ending wildcard', () => { - const filtered = metricsFilter.filterMetrics(wptResult, - ['data.median.firstView.rawData.*']); + const filtered = metricsFilter.filterMetrics(wptResult, [ + 'data.median.firstView.rawData.*' + ]); expect(filtered).to.deep.equal({ data: { median: { firstView: { rawData: { - 'headers': 'http:\/\/www.webpagetest.org\/results\/14\/11\/06\/8N\/ZRC\/1_report.txt', - 'pageData': 'http:\/\/www.webpagetest.org\/results\/14\/11\/06\/8N\/ZRC\/1_IEWPG.txt', - 'requestsData': 'http:\/\/www.webpagetest.org\/results\/14\/11\/06\/8N\/ZRC\/1_IEWTR.txt', - 'utilization': 'http:\/\/www.webpagetest.org\/results\/14\/11\/06\/8N\/ZRC\/1_progress.csv' + headers: + 'http://www.webpagetest.org/results/14/11/06/8N/ZRC/1_report.txt', + pageData: + 'http://www.webpagetest.org/results/14/11/06/8N/ZRC/1_IEWPG.txt', + requestsData: + 'http://www.webpagetest.org/results/14/11/06/8N/ZRC/1_IEWTR.txt', + utilization: + 'http://www.webpagetest.org/results/14/11/06/8N/ZRC/1_progress.csv' } } } @@ -59,13 +81,16 @@ describe('metricsFilter', () => { }); it('should filter with wildcard', () => { - const filtered = metricsFilter.filterMetrics(wptResult, 'data.median.*.TTFB'); + const filtered = metricsFilter.filterMetrics( + wptResult, + 'data.median.*.TTFB' + ); expect(filtered).to.deep.equal({ data: { median: { - firstView: {TTFB: 503}, - repeatView: {TTFB: 362} + firstView: { TTFB: 503 }, + repeatView: { TTFB: 362 } } } }); @@ -73,8 +98,10 @@ describe('metricsFilter', () => { // Skip test case for now, since it doesn't work yet it('should filter for multiple sibling properties using wildcard', () => { - const filtered = metricsFilter.filterMetrics(wptResult, ['data.median.*.TTFB', - 'data.median.*.loadTime']); + const filtered = metricsFilter.filterMetrics(wptResult, [ + 'data.median.*.TTFB', + 'data.median.*.loadTime' + ]); expect(filtered).to.deep.equal({ data: { @@ -98,29 +125,32 @@ describe('metricsFilter', () => { expect(filtered).to.deep.equal({ data: { average: { - firstView: {TTFB: 503}, - repeatView: {TTFB: 362} + firstView: { TTFB: 503 }, + repeatView: { TTFB: 362 } }, standardDeviation: { - firstView: {TTFB: 0}, - repeatView: {TTFB: 0} + firstView: { TTFB: 0 }, + repeatView: { TTFB: 0 } }, median: { - firstView: {TTFB: 503}, - repeatView: {TTFB: 362} + firstView: { TTFB: 503 }, + repeatView: { TTFB: 362 } } } }); }); it('should filter with starting wildcards', () => { - const filtered = metricsFilter.filterMetrics(wptResult, '*.average.*.TTFB'); + const filtered = metricsFilter.filterMetrics( + wptResult, + '*.average.*.TTFB' + ); expect(filtered).to.deep.equal({ data: { average: { - firstView: {TTFB: 503}, - repeatView: {TTFB: 362} + firstView: { TTFB: 503 }, + repeatView: { TTFB: 362 } } } }); @@ -132,16 +162,16 @@ describe('metricsFilter', () => { expect(filtered).to.deep.equal({ data: { average: { - firstView: {TTFB: 503}, - repeatView: {TTFB: 362} + firstView: { TTFB: 503 }, + repeatView: { TTFB: 362 } }, standardDeviation: { - firstView: {TTFB: 0}, - repeatView: {TTFB: 0} + firstView: { TTFB: 0 }, + repeatView: { TTFB: 0 } }, median: { - firstView: {TTFB: 503}, - repeatView: {TTFB: 362} + firstView: { TTFB: 503 }, + repeatView: { TTFB: 362 } } } }); @@ -175,6 +205,5 @@ describe('metricsFilter', () => { } }); }); - }); }); diff --git a/test/prepostscripts/postSample.js b/test/prepostscripts/postSample.js index 605ec1339..959417ac4 100644 --- a/test/prepostscripts/postSample.js +++ b/test/prepostscripts/postSample.js @@ -1,5 +1,5 @@ module.exports = { - run(context) { - context.log.info('In posttask!!!'); - } - }; + run(context) { + context.log.info('In posttask!!!'); + } +}; diff --git a/test/prepostscripts/preSample.js b/test/prepostscripts/preSample.js index c2e4fb0f0..173590f03 100644 --- a/test/prepostscripts/preSample.js +++ b/test/prepostscripts/preSample.js @@ -2,10 +2,12 @@ module.exports = { run(context) { context.log.info('In pretask!!!'); if (!context.taskData.loadedSitespeed) { - return context.runWithDriver((driver) => { - return driver.get('https://www.sitespeed.io') + return context + .runWithDriver(driver => { + return driver + .get('https://www.sitespeed.io') .then(() => driver.getTitle()) - .then((title) => { + .then(title => { context.log.info('Loaded page with title: ' + title); }); }) diff --git a/test/resultUrlTests.js b/test/resultUrlTests.js index a11d56c63..149e3ba85 100644 --- a/test/resultUrlTests.js +++ b/test/resultUrlTests.js @@ -14,65 +14,116 @@ function createResultUrls(url, outputFolder, resultBaseURL) { describe('resultUrls', function() { describe('#hasBaseUrl', function() { it('should be false if base url is missing', function() { - const resultUrls = createResultUrls('http://www.foo.bar', undefined, undefined); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + undefined, + undefined + ); expect(resultUrls.hasBaseUrl()).to.be.false; }); it('should be true if base url is present', function() { - const resultUrls = createResultUrls('http://www.foo.bar', undefined, 'http://results.com'); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + undefined, + 'http://results.com' + ); expect(resultUrls.hasBaseUrl()).to.be.true; }); }); describe('#reportSummaryUrl', function() { it('should create url with default output folder', function() { - const resultUrls = createResultUrls('http://www.foo.bar', undefined, 'http://results.com'); - expect(resultUrls.reportSummaryUrl()) - .to.equal(`http://results.com/www.foo.bar/${timestampString}`); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + undefined, + 'http://results.com' + ); + expect(resultUrls.reportSummaryUrl()).to.equal( + `http://results.com/www.foo.bar/${timestampString}` + ); }); it('should create url with absolute output folder', function() { - const resultUrls = createResultUrls('http://www.foo.bar', '/root/leaf', 'http://results.com'); - expect(resultUrls.reportSummaryUrl()) - .to.equal('http://results.com/leaf'); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + '/root/leaf', + 'http://results.com' + ); + expect(resultUrls.reportSummaryUrl()).to.equal('http://results.com/leaf'); }); it('should create url with relative output folder', function() { - const resultUrls = createResultUrls('http://www.foo.bar', '../leaf', 'http://results.com'); - expect(resultUrls.reportSummaryUrl()) - .to.equal('http://results.com/leaf'); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + '../leaf', + 'http://results.com' + ); + expect(resultUrls.reportSummaryUrl()).to.equal('http://results.com/leaf'); }); }); describe('#absoluteSummaryPageUrl', function() { it('should create url with default output folder', function() { - const resultUrls = createResultUrls('http://www.foo.bar', undefined, 'http://results.com'); - expect(resultUrls.absoluteSummaryPageUrl('http://www.foo.bar/xyz')) - .to.equal(`http://results.com/www.foo.bar/${timestampString}/pages/www.foo.bar/xyz/`); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + undefined, + 'http://results.com' + ); + expect( + resultUrls.absoluteSummaryPageUrl('http://www.foo.bar/xyz') + ).to.equal( + `http://results.com/www.foo.bar/${timestampString}/pages/www.foo.bar/xyz/` + ); }); it('should create url with absolute output folder', function() { - const resultUrls = createResultUrls('http://www.foo.bar', '/root/leaf', 'http://results.com'); - expect(resultUrls.absoluteSummaryPageUrl('http://www.foo.bar/xyz')) - .to.equal('http://results.com/leaf/pages/www.foo.bar/xyz/'); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + '/root/leaf', + 'http://results.com' + ); + expect( + resultUrls.absoluteSummaryPageUrl('http://www.foo.bar/xyz') + ).to.equal('http://results.com/leaf/pages/www.foo.bar/xyz/'); }); it('should create url with relative output folder', function() { - const resultUrls = createResultUrls('http://www.foo.bar', '../leaf', 'http://results.com'); - expect(resultUrls.absoluteSummaryPageUrl('http://www.foo.bar/xyz')) - .to.equal('http://results.com/leaf/pages/www.foo.bar/xyz/'); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + '../leaf', + 'http://results.com' + ); + expect( + resultUrls.absoluteSummaryPageUrl('http://www.foo.bar/xyz') + ).to.equal('http://results.com/leaf/pages/www.foo.bar/xyz/'); }); }); describe('#relativeSummaryPageUrl', function() { it('should create url with default output folder', function() { - const resultUrls = createResultUrls('http://www.foo.bar', undefined, 'http://results.com'); - expect(resultUrls.relativeSummaryPageUrl('http://www.foo.bar/xyz')) - .to.equal('pages/www.foo.bar/xyz/'); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + undefined, + 'http://results.com' + ); + expect( + resultUrls.relativeSummaryPageUrl('http://www.foo.bar/xyz') + ).to.equal('pages/www.foo.bar/xyz/'); }); it('should create url with absolute output folder', function() { - const resultUrls = createResultUrls('http://www.foo.bar', '/root/leaf', 'http://results.com'); - expect(resultUrls.relativeSummaryPageUrl('http://www.foo.bar/xyz')) - .to.equal('pages/www.foo.bar/xyz/'); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + '/root/leaf', + 'http://results.com' + ); + expect( + resultUrls.relativeSummaryPageUrl('http://www.foo.bar/xyz') + ).to.equal('pages/www.foo.bar/xyz/'); }); it('should create url with relative output folder', function() { - const resultUrls = createResultUrls('http://www.foo.bar', '../leaf', 'http://results.com'); - expect(resultUrls.relativeSummaryPageUrl('http://www.foo.bar/xyz')) - .to.equal('pages/www.foo.bar/xyz/'); + const resultUrls = createResultUrls( + 'http://www.foo.bar', + '../leaf', + 'http://results.com' + ); + expect( + resultUrls.relativeSummaryPageUrl('http://www.foo.bar/xyz') + ).to.equal('pages/www.foo.bar/xyz/'); }); }); }); diff --git a/test/runWithoutCli.js b/test/runWithoutCli.js index 59321f0ab..8c2088e79 100644 --- a/test/runWithoutCli.js +++ b/test/runWithoutCli.js @@ -2,19 +2,19 @@ const sitespeed = require('../lib/sitespeed'); const urls = ['https://www.sitespeed.io/']; function run() { - - sitespeed.run({ + sitespeed + .run({ urls, browsertime: { iterations: 1 } }) - .then((results) => { + .then(results => { if (results.error) { throw new Error(results.error); } }) - .catch((err) => { + .catch(err => { /* eslint-disable no-console */ console.error(err); /* eslint-enable no-console */ diff --git a/test/storageManagerTests.js b/test/storageManagerTests.js index 0b70a5dbd..c556dba86 100644 --- a/test/storageManagerTests.js +++ b/test/storageManagerTests.js @@ -16,13 +16,17 @@ describe('storageManager', function() { describe('#rootPathFromUrl', function() { it('should create path from url', function() { const storageManager = createManager('http://www.foo.bar'); - const path = storageManager.rootPathFromUrl('http://www.foo.bar/x/y/z.html'); + const path = storageManager.rootPathFromUrl( + 'http://www.foo.bar/x/y/z.html' + ); expect(path).to.equal('../../../../../'); }); it('should create path from url with query string', function() { const storageManager = createManager('http://www.foo.bar'); - const path = storageManager.rootPathFromUrl('http://www.foo.bar/x/y/z?foo=bar'); + const path = storageManager.rootPathFromUrl( + 'http://www.foo.bar/x/y/z?foo=bar' + ); expect(path).to.equal('../../../../../../'); }); }); @@ -30,10 +34,15 @@ describe('storageManager', function() { describe('#getBaseDir', function() { it('should create base dir with default output folder', function() { const storageManager = createManager('http://www.foo.bar'); - expect(storageManager.getBaseDir()).to.equal(path.resolve('sitespeed-result', 'www.foo.bar', timestampString)); + expect(storageManager.getBaseDir()).to.equal( + path.resolve('sitespeed-result', 'www.foo.bar', timestampString) + ); }); it('should create base dir with custom output folder', function() { - const storageManager = createManager('http://www.foo.bar', '/tmp/sitespeed.io/foo'); + const storageManager = createManager( + 'http://www.foo.bar', + '/tmp/sitespeed.io/foo' + ); expect(storageManager.getBaseDir()).to.equal('/tmp/sitespeed.io/foo'); }); }); @@ -41,10 +50,15 @@ describe('storageManager', function() { describe('#getStoragePrefix', function() { it('should create prefix with default output folder', function() { const storageManager = createManager('http://www.foo.bar'); - expect(storageManager.getStoragePrefix()).to.equal(path.join('www.foo.bar', timestampString)); + expect(storageManager.getStoragePrefix()).to.equal( + path.join('www.foo.bar', timestampString) + ); }); it('should create prefix with custom output folder', function() { - const storageManager = createManager('http://www.foo.bar', '/tmp/sitespeed.io/foo'); + const storageManager = createManager( + 'http://www.foo.bar', + '/tmp/sitespeed.io/foo' + ); expect(storageManager.getStoragePrefix()).to.equal('foo'); }); }); diff --git a/test/webpagetestTests.js b/test/webpagetestTests.js index f4bccf913..e6485c30d 100644 --- a/test/webpagetestTests.js +++ b/test/webpagetestTests.js @@ -5,7 +5,11 @@ const aggregator = require('../lib/plugins/webpagetest/aggregator'), path = require('path'), expect = require('chai').expect; -const wptResultPath = path.resolve(__dirname, 'fixtures', 'webpagetest.data.json'); +const wptResultPath = path.resolve( + __dirname, + 'fixtures', + 'webpagetest.data.json' +); const wptResult = JSON.parse(fs.readFileSync(wptResultPath, 'utf8')); describe('webpagetest', function() { diff --git a/tools/check-licenses.js b/tools/check-licenses.js index 679bfa034..b9a877cc0 100755 --- a/tools/check-licenses.js +++ b/tools/check-licenses.js @@ -7,28 +7,35 @@ const checker = require('license-checker'); const INCOMPATIBLE_LICENCE_REGEX = /GPL/; -checker.init({ - start: '.' -}, function(json, err) { - if (err) { - console.error(err.message); - process.exit(1); - } else { - const incompatibleDependencies = Object.keys(json).filter((packageName) => { - let licenses = json[packageName].licenses; - - if (!Array.isArray(licenses)) - licenses = [licenses]; - - if (licenses.find((license) => license.match(INCOMPATIBLE_LICENCE_REGEX))) - return packageName; - }); - - if (incompatibleDependencies.length > 0) { - console.error('Found packages with incompatible license: ' + JSON.stringify(incompatibleDependencies)); +checker.init( + { + start: '.' + }, + function(json, err) { + if (err) { + console.error(err.message); process.exit(1); } else { - console.log('All is well! No packages with an incompatible license found.'); + const incompatibleDependencies = Object.keys(json).filter(packageName => { + let licenses = json[packageName].licenses; + + if (!Array.isArray(licenses)) licenses = [licenses]; + + if (licenses.find(license => license.match(INCOMPATIBLE_LICENCE_REGEX))) + return packageName; + }); + + if (incompatibleDependencies.length > 0) { + console.error( + 'Found packages with incompatible license: ' + + JSON.stringify(incompatibleDependencies) + ); + process.exit(1); + } else { + console.log( + 'All is well! No packages with an incompatible license found.' + ); + } } } -}); +); diff --git a/tools/tcp-server.js b/tools/tcp-server.js index 0b17597f1..8d25713e5 100755 --- a/tools/tcp-server.js +++ b/tools/tcp-server.js @@ -3,12 +3,13 @@ var net = require('net'); -var server = net.createServer(function(sock) { - sock.on('data', function(data) { - console.log(data.toString()); +var server = net + .createServer(function(sock) { + sock.on('data', function(data) { + console.log(data.toString()); + }); + }) + .listen(process.argv[2] || 0, undefined, undefined, () => { + var address = server.address(); + console.log('Server listening on ' + address.address + ':' + address.port); }); - -}).listen(process.argv[2] || 0, undefined, undefined, () => { - var address = server.address(); - console.log('Server listening on ' + address.address +':'+ address.port); -});