Skip to content

Commit

Permalink
statistician: Create and compare files stats, and webpack bundle stats
Browse files Browse the repository at this point in the history
  • Loading branch information
omrilotan committed Oct 16, 2018
0 parents commit 23f5789
Show file tree
Hide file tree
Showing 49 changed files with 1,372 additions and 0 deletions.
63 changes: 63 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
defaults: &defaults
working_directory: ~/app
docker:
- image: circleci/node:8

version: 2
jobs:
dependencies:
<<: *defaults
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package.json" }}
- run:
name: Install dependencies
command: npm i
- save_cache:
key: dependency-cache-{{ checksum "package.json" }}
paths:
- ./node_modules
test:
<<: *defaults
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package.json" }}
- run:
name: Run tests
command: npm t
- run:
name: Code linting
command: npm run lint
publish:
<<: *defaults
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package.json" }}
- run:
name: Set npmrc credentials
command: echo -e $NPMRC > ~/.npmrc
- run:
name: Publish to NPM
command: npx published

experimental:
notify:
branches:
only:
- master

workflows:
version: 2
delivery:
jobs:
- dependencies
- test:
requires:
- dependencies
- publish:
context: org-global
requires:
- test
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# EditorConfig is awesome: http://EditorConfig.org
root = true

[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
indent_style = tab
tab_width = 2

[*.json]
indent_style = space
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
example
35 changes: 35 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"parserOptions": {
"ecmaVersion": 2017,
"sourceType": "module",
"ecmaFeatures": {
"impliedStrict": true
}
},
"env": {
"node": true,
"es6": true
},
"globals": {},
"plugins": [],
"extends": [
"eslint:recommended"
],
"rules": {},
"overrides": [
{
"files": [ "**/spec.js" ],
"env": {
"mocha": true
},
"globals": {
"expect": true,
"assert": true
},
"rules": {
"no-empty-function": 0,
"no-console": 0
}
}
]
}
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text eol=lf
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @fiverr/SRE @fiverr/DevOps
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
*.log
node_modules
example
notes
5 changes: 5 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.*
*.log
spec.js
example
notes
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# statistician

## Create and compare files stats, and webpack bundle stats

Use webpack stats file and self created files stats file to compare between states of the package, and comment on GitHub pull requests

## CLI Examples

Create webpack stats
```sh
webpack --config <CONFIG_FILE> --profile --json > stats.json
```
See [webpack stats API for more details](https://webpack.js.org/api/stats/)

---

# `files`

Create file stats
```sh
npx statistician files --dir "./dist" --ignore "\.map$" --ignore "(^|\/)\." --ignore "(^|/)node_modules\/," > files.json
```

| Option | Meaning | Example
| - | - | -
| dir | Directory holding the files to measure | `--dir "./dist"`
| ignore | Pattern to exclude from summary | `--ignore "\.map$"`

Outputs JSON

## files output example
```json
{
"dist/arrows.svg": 645,
"dist/badges-sprite.svg": 948,
"dist/index.css": 6004,
"dist/index.js": 62304,
"dist/outline.svg": 531,
"dist/shields.svg": 345
}
```

---

# `github-pull-request`

Create pull request (Example using CircleCI env variables)
```sh
npx statistician github-pull-request --file "./files-before.json,./files-after.json" --bundle "./stats-before.json,./stats-after.json" --user $CIRCLE_PROJECT_USERNAME --repo $CIRCLE_PROJECT_REPONAME --pr $CIRCLE_PR_NUMBER --token $GITHUB_TOKEN
```

| Option | Meaning | Example
| - | - | -
| file | pair of file stats to compare (comma separated) | `--file "./files-before.json,./files-after.json"`
| bundle | pair of bundle stats to compare (comma separated) | `--file "./files-before.json,./files-after.json"`
| user | Repository owner / org (\* not necessarily commenter) | `--user fiverr`
| repo | Name of repository | `--repo some-package`
| pr | Pull request number | `--pr 24`
| token | [GitHub API Token](https://github.com/settings/tokens) | `--token s4f5ybwne84bodafzopayrtjrc2koa2k4qb3y1wp`

## Pull request comment example

![image](https://user-images.githubusercontent.com/516342/47003941-204c1c00-d139-11e8-86ac-fdec938448de.png)
40 changes: 40 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env node

const {argv} = require('yargs');

/**
* Program names which output is required in JSON
* @type {String[]}
*/
const JSON_OUTPUT = [
'files',
];

process.on('unhandledRejection', console.error); // eslint-disable-line no-console

/**
* Entry point of CLI application
*/
(async() => {
try {
const {
_: [first],
} = argv;

const program = require(`./programs/${first}`);
const result = await program(argv);

console.log( // eslint-disable-line no-console
JSON_OUTPUT.includes(first)
?
JSON.stringify(result, null, 2)
:
result
);
} catch (error) {
console.error(error); // eslint-disable-line no-console
process.exit(1);
}
})();


40 changes: 40 additions & 0 deletions lib/GitHub/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const {join} = require('path');
const fetch = require('node-fetch');
const BASE = 'api.github.com';

module.exports = class GitHub {
constructor({token} = {}) {
this.token = token;
this.request = this.request.bind(this);
this.defaultHeaders = {
'Accept': 'application/json',
'Authorization': `token ${this.token}`,
};
}

async request(url, options = {}) {
options.headers = Object.assign(
{},
this.defaultHeaders,
options.headers || {}
);
const response = await fetch(
`https://${join(BASE, url)}`,
options
);

if (response.ok) {
return await response.json();
}

console.error( // eslint-disable-line no-console
[
`Error in request ${url}`,
options.body ? `Body: ${options.body}` : '',
JSON.stringify(await response.json(), null, 2),
].join('\n')
);

return null;
}
}
18 changes: 18 additions & 0 deletions lib/afsync/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const {promisify} = require('util');
const fs = require('fs');
const FUNCTIONS = [
'readdir',
'readFile',
'stat',
];

/**
* "Promisified" versions of fs functions
*/
module.exports = FUNCTIONS.reduce(
(accumulator, fn) => Object.assign(
accumulator,
{[fn]: promisify(fs[fn])}
),
{}
);
24 changes: 24 additions & 0 deletions lib/afsync/spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const {expect} = require('chai');
const clearModule = require('clear-module');
describe('afsync', async() => {
let afsync;
let fs;

before(() => {
clearModule('fs')
fs = require('fs');
fs.readdir = (dir, options, callback = () => null) => callback(null, 'result');
fs.stat = (file, options, callback = () => null) => callback(null, new fs.Stats({some: 'stats'}));

afsync = require('.');
});
after(() => clearModule('fs'));

it('Should convert callback methods to promises', async() => {
const promise = afsync.readdir('dd', () => null);
expect(promise.then).to.be.a('function');

const result = await promise;
expect(result).to.equal('result');
});
});
7 changes: 7 additions & 0 deletions lib/badge/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Create a badge tag with text and colour
* @param {String} string
* @param {String} color
* @return {String}
*/
module.exports = (string, color) => `![${string}](https://img.shields.io/badge/-${encodeURIComponent(string)}-${color}.svg?style=flat-square)`;
26 changes: 26 additions & 0 deletions lib/diff/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const percent = require('../percent');
const badge = require('../badge');
const COLOUR_THRESHOLD = 5;

/**
* Create a textual representation of the diff percentage between two numbers
* @param {Number} before
* @param {Number} after
* @return {String}
*/
module.exports = (before, after) => {
if (!after || !before) {
return 'N/A';
}

const percentage = percent(after, before) - 100;
const string = `${percentage}%`;

if (percentage > COLOUR_THRESHOLD) {
return badge(string, 'red');
} else if (percentage < -COLOUR_THRESHOLD) {
return badge(string, 'green');
}

return string;
}
6 changes: 6 additions & 0 deletions lib/esc/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const characters = [
'<',
];
const pattern = new RegExp(`[${characters.join('|')}]`, 'gm');

module.exports = string => string.replace(pattern, match => `\\${match}`);
8 changes: 8 additions & 0 deletions lib/esc/spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const {expect} = require('chai');
const esc = require('.');

describe('esc', () => {
it('Should add a backslash to characters that need escaping in markdown', () => {
expect(esc('<root>something</root>')).to.equal('\\<root>something\\</root>');
});
});
14 changes: 14 additions & 0 deletions lib/getJSON/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const {readFile} = require('../afsync');

module.exports = async file => {
if (!file) {
return {};
}

try {
return JSON.parse((await readFile(file)).toString())
} catch (error) {
console.error(error); // eslint-disable-line no-console
return {};
}
}
Loading

0 comments on commit 23f5789

Please sign in to comment.