Skip to content

Commit

Permalink
feat: Add progress bar
Browse files Browse the repository at this point in the history
  • Loading branch information
d4rkr00t committed Mar 31, 2018
1 parent af742d5 commit 7f47cba
Show file tree
Hide file tree
Showing 13 changed files with 261 additions and 19 deletions.
7 changes: 5 additions & 2 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

const meow = require("meow");
const chalk = require("chalk");
const createProgressBar = require("./lib/console/progress-bar");
const defaultCommand = require("./commands/default");
const byCommand = require("./commands/by");
const helpCommand = require("./commands/help");
Expand Down Expand Up @@ -52,10 +53,12 @@ if (!input || !input.length || !input[0].match(".json") || flags.help) {
showHelp(0);
}

const updateProgressBar = createProgressBar();

if (flags.by) {
byCommand(input[0], flags, input[1]);
byCommand(input[0], flags, input[1], updateProgressBar);
} else {
defaultCommand(input[0], flags, input[1]);
defaultCommand(input[0], flags, input[1], updateProgressBar);
}

const timing = (Date.now() - start) / 1000;
Expand Down
17 changes: 11 additions & 6 deletions commands/by.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
/* @flow */

/*::
import type { UpdateProgressBar } from '../lib/console/progress-bar';
*/

const { analyze, print, getStats } = require("../lib");
const validate = require("../lib/validate");
const { log, invalidStatsJson } = require("../lib/messages");
const { log, invalidStatsJson } = require("../lib/console/messages");

module.exports = function byCommand(
statsFilePath /*: string */,
flags /*: { limit: number, by: string, ignore?: string } */,
pattern /*: string */
pattern /*: string */,
updateProgressBar /*: UpdateProgressBar */ = () => {}
) {
const stats = getStats(statsFilePath);

Expand All @@ -17,14 +22,14 @@ module.exports = function byCommand(
}

const ignore = flags.ignore ? flags.ignore.split(",") : [];
const report = analyze(stats, ignore).filter(mod => {
return (
const report = analyze(stats, ignore, updateProgressBar).filter(
mod =>
mod.reasons.some(
reason =>
reason.moduleName === flags.by || reason.clearName === flags.by
) || (mod.depsChains || []).some(deps => deps.indexOf(flags.by) !== -1)
);
});
);

const limit /*: number */ = pattern ? 0 : flags.limit >= 0 ? flags.limit : 20;
print(report, { by: flags.by }, limit);
};
9 changes: 6 additions & 3 deletions commands/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
const mm = require("micromatch");
const { analyze, print, getStats } = require("../lib");
const validate = require("../lib/validate");
const { log, invalidStatsJson } = require("../lib/messages");
const { log, invalidStatsJson } = require("../lib/console/messages");

/*::
import type { UpdateProgressBar } from '../lib/console/progress-bar';
type Flags = {
limit: number,
filesOnly?: boolean,
Expand All @@ -21,7 +23,8 @@ type Flags = {
module.exports = function defaultCommand(
statsFilePath /*: string */,
flags /*: Flags */,
pattern /*: string*/
pattern /*: string*/,
updateProgressBar /*: UpdateProgressBar */ = () => {}
) {
const stats = getStats(statsFilePath);

Expand All @@ -31,7 +34,7 @@ module.exports = function defaultCommand(
}

const ignore = flags.ignore ? flags.ignore.split(",") : [];
const report = analyze(stats, ignore).filter(module => {
const report = analyze(stats, ignore, updateProgressBar).filter(module => {
if (pattern && mm.isMatch(module.name, pattern)) {
return true;
} else if (pattern) {
Expand Down
20 changes: 20 additions & 0 deletions fixtures/valid-with-multiple-modules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"chunks": [
{
"modules": [
{
"id": "module1",
"name": "module-name",
"size": 500,
"reasons": []
},
{
"id": "module2",
"name": "module-name2",
"size": 300,
"reasons": []
}
]
}
]
}
4 changes: 3 additions & 1 deletion flow-typed/npm/micromatch_vx.x.x.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
*/

declare module "micromatch" {
declare module.exports: any;
declare module.exports: {
isMatch: (what: string, patterns: Array<string> | string) => boolean
};
}

/**
Expand Down
18 changes: 18 additions & 0 deletions lib/__tests__/analyze.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const test = require("ava");
const fixtures = require("fixturez");
const f = fixtures(__dirname);
const getStats = require("../get-stats");
const analyze = require("../analyze");

test("should call updateProgressBar correct number of times", t => {
let calls = 0;
const updateProgressBar = ({ progress }) => {
calls++;
};
const stats = analyze(
getStats(f.find("valid-with-multiple-modules.json")),
[],
updateProgressBar
);
t.is(calls, 2);
});
33 changes: 28 additions & 5 deletions lib/analyze.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/* @flow */
/*::
import type { UpdateProgressBar } from "./console/progress-bar";
export type WebpackStats = {
chunks?: Array<WebpackChunk>,
modules: Array<WebpackModule>
Expand Down Expand Up @@ -60,7 +63,6 @@ export type SubReason = {
loc: string,
importType: string,
}
*/

const path = require("path");
Expand Down Expand Up @@ -233,8 +235,11 @@ const buildModuleDepsChains = (modules, name) => {
}, []);
};

const postProcessModules = (modules, ignore) => {
return Object.keys(modules).reduce((acc, name) => {
const postProcessModules = (modules, ignore, updateProgressBar) => {
const total = Object.keys(modules).length;
return Object.keys(modules).reduce((acc, name, index) => {
updateProgressBar(total, index, "processing", name);

if (mm.isMatch(name, ignore)) {
return acc;
}
Expand Down Expand Up @@ -265,10 +270,28 @@ const postProcessModules = (modules, ignore) => {

module.exports = function analyze(
stats /*: WebpackStats */,
ignore /*: Array<string> */ = []
ignore /*: Array<string> */ = [],
updateProgressBar /*: UpdateProgressBar */ = () => {}
) {
const rawModules = flattenChunks(stats);
const ignorePatterns = [].concat(DEFAULT_IGNORE).concat(ignore);
const modules = pickFromModules(rawModules);
return toArray(postProcessModules(joinModules(modules), ignorePatterns));
const updateProgressBarWithTotal = (
total /*: number */,
cur /*: number */,
title /*: string */,
name /*: string */
) =>
updateProgressBar({
title,
text: name,
progress: Math.ceil(cur / total * 100)
});
return toArray(
postProcessModules(
joinModules(modules),
ignorePatterns,
updateProgressBarWithTotal
)
);
};
69 changes: 69 additions & 0 deletions lib/console/__tests__/__snapshots__/progress-bar.js.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Snapshot report for `lib/console/__tests__/progress-bar.js`

The actual snapshot is saved in `progress-bar.js.snap`.

Generated by [AVA](https://ava.li).

## should output correct progress for {"progress":0}

> Snapshot 1
[
`␍
`,
`␍
`,
'⸨░░░░░░░░░░░░░░░░░░░░⸩ 0% • info ',
]

## should output correct progress for {"progress":100}

> Snapshot 1
[
`␍
`,
`␍
`,
'⸨ ⸩ 100% • info ',
`␍
`,
`␍
`,
]

## should output correct progress for {"progress":30,"title":"title","text":"text"}

> Snapshot 1
[
`␍
`,
`␍
`,
'⸨ ░░░░░░░░░░░░░░⸩ 30% • info title: text',
]

## should output correct progress for {"progress":50,"text":"text"}

> Snapshot 1
[
`␍
`,
`␍
`,
'⸨ ░░░░░░░░░░⸩ 50% • info text',
]

## should output correct progress for {"progress":75,"title":"title"}

> Snapshot 1
[
`␍
`,
`␍
`,
'⸨ ░░░░░⸩ 75% • info title: ',
]
Binary file not shown.
26 changes: 26 additions & 0 deletions lib/console/__tests__/progress-bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const stripAnsi = require("strip-ansi");
const test = require("ava");
const fixtures = require("fixturez");
const f = fixtures(__dirname);
const createProgressBar = require("../progress-bar");

[
{ progress: 0 },
{ progress: 30, title: "title", text: "text" },
{ progress: 50, text: "text" },
{ progress: 75, title: "title" },
{ progress: 100 }
].forEach(testCase => {
test(`should output correct progress for ${JSON.stringify(testCase)}`, t => {
const writes = [];
const stdout = {
columns: 100,
write(str) {
writes.push(stripAnsi(str || ""));
}
};
const updateProgressBar = createProgressBar(stdout);
updateProgressBar(testCase);
t.snapshot(writes);
});
});
File renamed without changes.
73 changes: 73 additions & 0 deletions lib/console/progress-bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* @flow */

/*::
export type UpdateProgressBar = (param: { progress: number, title?: string, text?: string }) => void;
*/

const chalk = require("chalk");

function toStartOfLine(stdout /*: stream$Writable */) {
stdout.write("\r");
}

function clearLine(stdout /*: stream$Writable */) {
// $FlowFixMe: investigate process.stderr.columns flow error
stdout.write(`\r${" ".repeat(stdout.columns - 1)}`);
}

function selectPrevLine(stdout /*: stream$Writable */) {
stdout.write("\033[1A");
}

module.exports = function createProgressBar(
stdout /*: stream$Writable */ = process.stdout
) /*: UpdateProgressBar */ {
return function updateProgress({
progress,
title: maybeTitle,
text: maybeText
}) {
// $FlowFixMe: investigate process.stderr.columns flow error
const columns /*: number */ = stdout.columns;
const ratio = progress / 100;
const title = maybeTitle || "";
const text = maybeText || "";

const decorationsSize = "⸨⸩ • info: % …".length;
const progressNumberSize = ("" + progress).length;
const progressBarSize = 20;
const availableSpace = Math.max(
0,
columns -
decorationsSize -
progressNumberSize -
progressNumberSize -
title.length
);
const completeLength = Math.round(progressBarSize * ratio);
const incompleteLength = progressBarSize - completeLength;

const completed = chalk.inverse(" ").repeat(completeLength);
const incompleted = "░".repeat(progressBarSize - completeLength);
const infoLine =
text.length > availableSpace
? text.substr(0, availableSpace) + "…"
: text;

clearLine(stdout);
toStartOfLine(stdout);

stdout.write(
`⸨${completed}${incompleted}${chalk.dim(
`${progress}%`
)}${chalk.green("info")} ${
title ? chalk.magenta(`${title}: `) : ""
}${infoLine}`
);

if (progress === 100) {
toStartOfLine(stdout);
clearLine(stdout);
}
};
};
4 changes: 2 additions & 2 deletions lib/get-stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ const getAbsoultePath = (filePath /*: string */) =>

module.exports = function getStats(
statsFilePath /*: string */
) /*?: WebpackStats */ {
) /*: WebpackStats */ {
try {
// $FlowFixMe
const fileContent = fs.readFileSync(getAbsoultePath(statsFilePath), "utf8");
const indexOfTheFirstBrace = fileContent.indexOf("{");
const cleanContent = fileContent.substr(indexOfTheFirstBrace);
try {
return JSON.parse(cleanContent);
return JSON.parse(cleanContent) /* as WebpackStats */;
} catch (e) {
throw new Error(`Stats file "${statsFilePath}" is not a valid json...`);
}
Expand Down

0 comments on commit 7f47cba

Please sign in to comment.