Skip to content

Commit

Permalink
Progress-Bar refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
nknapp committed May 7, 2017
1 parent c68e77c commit be3a3e4
Show file tree
Hide file tree
Showing 11 changed files with 264 additions and 65 deletions.
2 changes: 1 addition & 1 deletion .thought/partials/usage.md.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
Run `analyze-module-size` in your project directory. The output will be something like this:
(Note that the displayed sizes are accumulated from the each module an its dependencies):

{{exec 'node src/index.js'}}
{{exec 'node bin/analyze-module-size.js'}}
54 changes: 53 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,59 @@ Run `analyze-module-size` in your project directory. The output will be somethin
(Note that the displayed sizes are accumulated from the each module an its dependencies):

```
size: 64k... with-dependencies: 1724k
├── bluebird@3.5.0, 716k, 0 deps
├─┬ globby@6.1.0, 484k, 20 deps
│ ├─┬ glob@7.1.1, 340k, 13 deps
│ │ ├─┬ minimatch@3.0.3, 132k, 3 deps
│ │ │ └─┬ brace-expansion@1.1.7, 84k, 2 deps
│ │ │ ├── concat-map@0.0.1, 40k, 0 deps
│ │ │ └── balanced-match@0.4.2, 24k, 0 deps
│ │ ├─┬ inflight@1.0.6, 60k, 3 deps
│ │ │ ├─┬ once@1.4.0, 40k, 1 deps
│ │ │ │ └── wrappy@1.0.2, 20k, 0 deps
│ │ │ └── wrappy@1.0.2, 20k, 0 deps
│ │ ├─┬ once@1.4.0, 40k, 1 deps
│ │ │ └── wrappy@1.0.2, 20k, 0 deps
│ │ ├── fs.realpath@1.0.0, 32k, 0 deps
│ │ ├── inherits@2.0.3, 24k, 0 deps
│ │ └── path-is-absolute@1.0.1, 20k, 0 deps
│ ├─┬ pinkie-promise@2.0.1, 44k, 1 deps
│ │ └── pinkie@2.0.4, 24k, 0 deps
│ ├─┬ array-union@1.0.2, 40k, 1 deps
│ │ └── array-uniq@1.0.3, 20k, 0 deps
│ ├── object-assign@4.1.1, 20k, 0 deps
│ └── pify@2.3.0, 20k, 0 deps
├─┬ glob@7.1.1, 340k, 13 deps
│ ├─┬ minimatch@3.0.3, 132k, 3 deps
│ │ └─┬ brace-expansion@1.1.7, 84k, 2 deps
│ │ ├── concat-map@0.0.1, 40k, 0 deps
│ │ └── balanced-match@0.4.2, 24k, 0 deps
│ ├─┬ inflight@1.0.6, 60k, 3 deps
│ │ ├─┬ once@1.4.0, 40k, 1 deps
│ │ │ └── wrappy@1.0.2, 20k, 0 deps
│ │ └── wrappy@1.0.2, 20k, 0 deps
│ ├─┬ once@1.4.0, 40k, 1 deps
│ │ └── wrappy@1.0.2, 20k, 0 deps
│ ├── fs.realpath@1.0.0, 32k, 0 deps
│ ├── inherits@2.0.3, 24k, 0 deps
│ └── path-is-absolute@1.0.1, 20k, 0 deps
├─┬ chalk@1.1.3, 144k, 7 deps
│ ├─┬ has-ansi@2.0.0, 40k, 1 deps
│ │ └── ansi-regex@2.1.1, 20k, 0 deps
│ ├─┬ strip-ansi@3.0.1, 40k, 1 deps
│ │ └── ansi-regex@2.1.1, 20k, 0 deps
│ ├── ansi-styles@2.2.1, 20k, 0 deps
│ ├── escape-string-regexp@1.0.5, 20k, 0 deps
│ └── supports-color@2.0.0, 20k, 0 deps
├─┬ debug@2.6.6, 128k, 1 deps
│ └── ms@0.7.3, 20k, 0 deps
├── archy@1.0.0, 52k, 0 deps
├── graceful-fs@4.1.11, 48k, 0 deps
├─┬ deep-aplus@1.0.4, 44k, 1 deps
│ └── lodash.isplainobject@4.0.6, 20k, 0 deps
├── progress@2.0.0, 44k, 0 deps
└── pify@2.3.0, 20k, 0 deps
```


Expand Down
30 changes: 8 additions & 22 deletions bin/analyze-module-size.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,14 @@ var {analyze} = require('../src/index')

var realFs = require('fs')
var gracefulFs = require('graceful-fs')
var tsProgress = require('ts-progress')
var {ProgressHandler} = require('../src/progress')

gracefulFs.gracefulify(realFs)

const {promise, progress} = analyze(process.cwd(), {depth: process.argv[2]})

var progressBar = null

progress
.on('dependencies', dep => {
progressBar = tsProgress.create({
title: 'Loading package info',
total: dep * 2
})
})
.on('dep-start-load', pkg => progressBar.update())
.on('dep-end-load', pkg => progressBar.update())

promise.then(
(output) => {
progressBar.done()
return process.stdout.write(output)
},
(err) => process.stderr.write(err.stack + '\n')
)
analyze(process.cwd(), {progress: new ProgressHandler(process.stderr), depth: process.argv[2]})
.then(
(output) => {
return process.stdout.write(output)
},
(err) => process.stderr.write(err.stack + '\n')
)
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@
"globby": "^6.1.0",
"graceful-fs": "^4.1.11",
"pify": "^2.3.0",
"ts-progress": "^0.1.3"
"progress": "^2.0.0"
},
"devDependencies": {
"chai": "^3.5.0",
"chai-as-promised": "^6.0.0",
"concat-stream": "^1.6.0",
"dirty-chai": "^1.2.2",
"eslint": "^3.17.1",
"eslint-config-standard": "^7.0.1",
Expand Down
48 changes: 20 additions & 28 deletions src/DependencyTree.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
const path = require('path')
const pify = require('pify')
const glob = pify(require('glob'))
const {findPackages} = require('./find-packages')
var {Package} = require('./Package')
var debug = require('debug')('analyze-module-size:DependencyTree')
var EventEmitter = require('events')
var Promise = require('bluebird')
const deep = require('deep-aplus')(Promise)
const {NullProgressHandler} = require('./progress')

class DependencyTree {
/**
Expand Down Expand Up @@ -34,50 +32,44 @@ class DependencyTree {
this.all = all
}

static loadFrom (packageJsonPath) {
static loadFrom (packageJsonPath, progressHandler = new NullProgressHandler()) {
// Compute "this" package
var rootPackage = Package.loadFrom(packageJsonPath)
const progress = new EventEmitter()

// Compute dependencies
var cwd = path.dirname(packageJsonPath)
var dependencies = glob('**/node_modules/*/package.json', {cwd})
.then((dependencies) => {
debug('dependencies resolved')
progress.emit('unfiltered-dependencies', dependencies.length)
// Only allow paths liks node_modules/pkg-name/node_modules/pkg-name/node_modules/pkg-name/package.json
const filteredDependencies = dependencies
.filter((packageJson) => {
progress.emit('dep-filtered', dependencies.length)
return packageJson.match(/^(node_modules\/[^/]*\/)*package.json/)
})

debug('dependencies filtered')
progress.emit('dependencies', filteredDependencies.length)
var dependencies = findPackages(cwd, progressHandler)
.then((dependencies) => {
progressHandler.allDependenciesFound(dependencies.length)

// Load all dependency with a concurrency level of 4
// Empiric tests show that it is not really slower than running fully parallel,
// but the progress bar behaves much more consistent.
return Promise.map(
filteredDependencies,
dependencies,
(packageJson) => {
progress.emit('dep-start-load', packageJson)
return Package.loadFrom(path.join(cwd, packageJson))
.then(pkg => {
progress.emit('dep-end-load', packageJson)
progressHandler.dependencyLoaded(pkg)
return pkg
})
},
{concurrency: 4}
)
})
return {
progress: progress,
promise: deep({rootPackage, dependencies}).then(function ({rootPackage, dependencies}) {
var {prod, dev, manual} = Package.connectAll(rootPackage, dependencies)
return new DependencyTree(rootPackage, prod.dependencies, dev.dependencies, manual.dependencies, dependencies)
})
}
return deep({rootPackage, dependencies}).then(function ({rootPackage, dependencies}) {
progressHandler.connectAll()
var {prod, dev, manual} = Package.connectAll(rootPackage, dependencies)
progressHandler.done()
return new DependencyTree(
rootPackage,
prod.dependencies,
dev.dependencies,
manual.dependencies,
dependencies
)
})
}
}

Expand Down
27 changes: 27 additions & 0 deletions src/find-packages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
var Glob = require('glob').Glob

/**
* @param {string} cwd
* @param {ProgressHandler} progressHandler
*/
function findPackages (cwd, progressHandler) {
return new Promise((resolve, reject) => {
const matcher = new Glob(
'**/node_modules/*/package.json',
{cwd},
(err, results) => err ? /* istanbul ignore next */ reject(err) : resolve(results)
)
matcher.on('match', function (file) {
progressHandler.dependencyFound(file)
})
})
.then(dependencies => {
// Only allow paths liks node_modules/pkg-name/node_modules/pkg-name/node_modules/pkg-name/package.json
return dependencies.filter((file) => {
const valid = file.match(/^(node_modules\/[^/]*\/)*package.json/)
return valid
})
})
}

module.exports = {findPackages}
13 changes: 6 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,23 @@ const sortby = require('lodash.sortby')
const archy = require('archy')
const chalk = require('chalk')
const path = require('path')
const {NullProgressHandler} = require('./progress')

/**
*
* @param cwd
* @param options
* @param {number} options.depth The number of levels to display in the tree-view
* @param {ProgressHandler} options.progress
*/
function analyze (cwd, options) {
const {promise, progress} = DependencyTree.loadFrom(path.join(cwd, 'package.json'))
return {
progress,
promise: promise.then(function (tree) {
function analyze (cwd, options = {}) {
return DependencyTree.loadFrom(path.join(cwd, 'package.json'), options.progress || new NullProgressHandler())
.then(function (tree) {
return archy({
label: `size: ${tree.rootPackage.stats.totalBlockSize() / 1024}k... with-dependencies: ${tree.rootPackage.totalStats().totalBlockSize() / 1024}k`,
nodes: toArchy(tree.prod, options && options.depth)
nodes: toArchy(tree.prod, options.depth)
})
})
}
}

/**
Expand Down
69 changes: 69 additions & 0 deletions src/progress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const debug = require('debug')('analyze-module-size:progress')
const ProgressBar = require('progress')

class ProgressHandler {
constructor (stream) {
this.stream = stream
this.foundDepsProgress = new ProgressBar('dependencies found: :current', {total: 1000, stream: this.stream})
this.loadedDepsProgress = null
}

/**
* Notify the user that a dependency (package.json in node_modules) has been found
* @param {string} file the path to the package.json file
*/
dependencyFound (file) {
this.foundDepsProgress.tick()
}

allDependenciesFound (dep) {
this.stream.write(' ...done\n')
this.loadedDepsProgress = new ProgressBar('Loading dependency details: :bar :locationxxxxxxxxxxxxxxxxxx', {
total: dep,
stream: this.stream
})
}

/**
* Update the progressbar after a dependency has been loaded.
* (Increase the tick-count)
*/
dependencyLoaded (pkg) {
this.loadedDepsProgress.tick({locationxxxxxxxxxxxxxxxxxx: pkg.location().substr(0, 'locationxxxxxxxxxxxxxxxxxx'.length)})
}

connectAll () {
this.stream.write('Connecting dependency graph\n')
}

done () {
this.stream.write('done\n')
}
}

/**
* No actual output (only if DEBUG=anaylze-module-size:progress)
*/
class NullProgressHandler {
dependencyFound (file) {
debug('dependencyFound', file)
}

allDependenciesFound (count) {
debug('allDependenciesFound', count)
}

dependencyLoaded (pkg) {
debug('dependencyLoaded', pkg.location())
}

connectAll () {
debug('connectAll')
}

done () {
debug('done')
}
}

module.exports = {ProgressHandler, NullProgressHandler}
3 changes: 0 additions & 3 deletions test/DependencyTree-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ describe('The DependencyTree-class:', function () {

it('should create a DependencyTree with production dependencies', function () {
return DependencyTree.loadFrom('test/fixtures/moduleWithDeps/package.json')
.promise
.then((tree) => expect(visit(tree.prod)).to.deep.equal([
{
'_id': 'dep1@1.0.0',
Expand Down Expand Up @@ -40,7 +39,6 @@ describe('The DependencyTree-class:', function () {

it('should create a DependencyTree with dev dependencies', function () {
return DependencyTree.loadFrom('test/fixtures/moduleWithDeps/package.json')
.promise
.then((tree) => expect(visit(tree.dev)).to.deep.equal([
{
'_id': 'devdep1@1.0.0',
Expand All @@ -58,7 +56,6 @@ describe('The DependencyTree-class:', function () {

it('should create a DependencyTree with manually installed dependencies', function () {
return DependencyTree.loadFrom('test/fixtures/moduleWithDeps/package.json')
.promise
.then((tree) => expect(visit(tree.manual)).to.deep.equal([
{
'_id': 'manualdep1@1.0.0',
Expand Down
2 changes: 0 additions & 2 deletions test/index-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ require('chalk').enabled = false
describe('The index-function (module main function):', function () {
it('should return an archy-tree for the dependencies of the referenced package directory', function () {
return analyze('test/fixtures/moduleWithDeps')
.promise
.then((result) => {
expect(result).to.equal(fs.readFileSync('test/fixtures/moduleWithDeps.txt', 'utf-8'))
})
})

it('should cut the display at a specific depth if specified', function () {
return analyze('test/fixtures/moduleWithDeps', {depth: 1})
.promise
.then((result) => {
expect(result).to.equal(fs.readFileSync('test/fixtures/moduleWithDeps-depth1.txt', 'utf-8'))
})
Expand Down
Loading

0 comments on commit be3a3e4

Please sign in to comment.