Skip to content

Commit

Permalink
Add PackageStats class...
Browse files Browse the repository at this point in the history
... for computing statistics about a single npm-package
  • Loading branch information
nknapp committed Apr 28, 2017
1 parent ad6ddae commit 14ce7d4
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 1 deletion.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@
"preversion": "thought --version || npm -g install thought",
"version": "thoughtful changelog -o -a && npm run thought"
},
"dependencies": {},
"dependencies": {
"deep-aplus": "^1.0.4",
"glob": "^7.1.1",
"pify": "^2.3.0"
},
"devDependencies": {
"chai": "^3.5.0",
"dirty-chai": "^1.2.2",
Expand Down
82 changes: 82 additions & 0 deletions src/PackageStats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const path = require('path')
const deep = require('deep-aplus')(Promise)
const pify = require('pify')
const fs = require('fs')
const stat = pify(fs.stat)
const glob = pify(require('glob'))

/**
* Computes and aggregates a number of statistics from a package directory
* in the node_modules folder
*
*/
class PackageStats {
/**
* Create a new PackageStats object
* @param {string} directory path to the package-json files
* @param {{file: string, stat: fs.Stats}} files files and stats in this package
*/
constructor (directory, files) {
/**
* @type {string}
*/
this.directory = directory
/**
* @type {{file: string, stat: fs.Stats}}
*/
this.files = files
}

totalByteSize () {
if (!this._totalByteSize) {
this._totalByteSize = this.files.reduce((result, file) => result + file.stat.size, 0)
}
return this._totalByteSize
}

totalBlockSize () {
if (!this._totalBlocksize) {
this._totalBlockSize = this.files.reduce((result, file) => {
return result + Math.ceil(file.stat.size / file.stat.blksize) * file.stat.blksize
}, 0)
}
return this._totalBlockSize
}

/**
* Returns a new PackageStats-object with the same directory
* and the union of all files in this package and the other packages
* @param {PackageStats[]} packages
*/
merge (packages) {
var uniq = {}
// Merge files
this.files.forEach(file => { uniq[file.file] = file })
packages.forEach(pkg => {
pkg.files.forEach(file => { uniq[file.file] = file })
})
// Recreate file array
var files = Object.keys(uniq).map((key) => uniq[key])
return new PackageStats(this.directory, files)
}

/**
*
* @param packageJsonPath
* @return {Promise<PackageStats>}
*/
static loadFrom (packageJsonPath) {
var directory = path.dirname(packageJsonPath)
return glob('**', {dot: true, cwd: directory, mark: true, ignore: 'node_modules/**'})
// Add the directory itself
.then(files => ['./'].concat(files))
// Gather stats
.then(files => files.map(file => ({file: path.join(directory, file), stat: stat(path.join(directory, file))})))
// Wait for promises
.then(deep)
// creaate PackageStats-object
.then((files) => new PackageStats(directory, files))
}
}

module.exports = {PackageStats}
65 changes: 65 additions & 0 deletions test/PackageStats-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-env mocha */

const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect
const {PackageStats} = require('../src/PackageStats')

describe('The PackageStats-class', function () {
it('should load the package-stats from a directory', function () {
return PackageStats.loadFrom('test/fixtures/project1/package.json')
.then((packageStats) => {
expect(packageStats.directory).to.equal('test/fixtures/project1')
expect(sortedNameAndSize(packageStats.files)).to.deep.equal([
'test/fixtures/project1/ - 4096',
'test/fixtures/project1/dir/ - 4096',
'test/fixtures/project1/dir/file2.txt - 2',
'test/fixtures/project1/file3.txt - 3',
'test/fixtures/project1/file5000.txt - 5000',
'test/fixtures/project1/file6.txt - 6'
])
})
})

it('should compute the byte-size of all the files in a directory', function () {
return PackageStats.loadFrom('test/fixtures/project1/package.json')
.then((packageStats) => {
expect(packageStats.totalByteSize()).to.equal(4096 + 4096 + 2 + 3 + 5000 + 6)
})
})

it('should compute the disk usage (by whole blocks) of the directory', function () {
return PackageStats.loadFrom('test/fixtures/project1/package.json')
.then((packageStats) => {
expect(packageStats.totalBlockSize()).to.equal(7 * 4096)
})
})

it('should merge two stats-object by unioning the files', function () {
return Promise.all([
PackageStats.loadFrom('test/fixtures/project1/package.json'),
PackageStats.loadFrom('test/fixtures/project2/package.json'),
PackageStats.loadFrom('test/fixtures/project1/package.json')
])
.then((packages) => {
// Merge package with the first package being the main package
const merged = packages[0].merge(packages.slice(1))
expect(merged, 'Must return a copy').not.to.equal(packages[0])
expect(merged.directory).to.equal('test/fixtures/project1')
expect(sortedNameAndSize(merged.files)).to.deep.equal([
'test/fixtures/project1/ - 4096',
'test/fixtures/project1/dir/ - 4096',
'test/fixtures/project1/dir/file2.txt - 2',
'test/fixtures/project1/file3.txt - 3',
'test/fixtures/project1/file5000.txt - 5000',
'test/fixtures/project1/file6.txt - 6',
'test/fixtures/project2/ - 4096',
'test/fixtures/project2/file10.txt - 10'
])
})
})
})

function sortedNameAndSize (files) {
return files.map((file) => file.file + ' - ' + file.stat.size).sort()
}
1 change: 1 addition & 0 deletions test/fixtures/project1/file3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ab
100 changes: 100 additions & 0 deletions test/fixtures/project1/file5000.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
0123456789012345678901234567890123456789012345678
1 change: 1 addition & 0 deletions test/fixtures/project1/file6.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
abcef
52 changes: 52 additions & 0 deletions test/fixtures/project1/node_modules/abc/moduleFile.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions test/fixtures/project2/file10.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
012345678

0 comments on commit 14ce7d4

Please sign in to comment.