Skip to content

Commit e81cb25

Browse files
author
Ovidiu Barabula
committed
feat(core): add a custom fancy logger
1 parent 2469e87 commit e81cb25

File tree

11 files changed

+383
-12
lines changed

11 files changed

+383
-12
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"deploy"
5252
],
5353
"dependencies": {
54+
"chalk": "^2.3.2",
5455
"commander": "^2.15.0"
5556
},
5657
"peerDependencies": {
@@ -61,6 +62,7 @@
6162
"@babel/preset-env": "^7.0.0-beta.40",
6263
"@babel/register": "^7.0.0-beta.40",
6364
"@types/chai": "^4.1.2",
65+
"@types/chalk": "^2.2.0",
6466
"@types/commander": "^2.12.2",
6567
"@types/gulp": "^4.0.5",
6668
"@types/lodash": "^4.14.104",
@@ -86,6 +88,7 @@
8688
"rimraf": "^2.6.2",
8789
"semantic-release": "^12.4.1",
8890
"source-map-support": "^0.5.3",
91+
"test-console": "^1.1.0",
8992
"travis-deploy-once": "^4.3.4",
9093
"ts-loader": "^4.0.1",
9194
"ts-node": "^5.0.1",

src/cli/cli.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ program
2020
program
2121
.command('init [name]')
2222
.action(async name => {
23-
console.log(`Creating a new project ./${name}`);
2423
const instance = await frontvue;
24+
const logger = instance.logger('cli');
25+
logger.info(`Creating a new project ./${name}`);
2526
instance.run('init');
2627
})
2728
.description('Initialize a new project');
@@ -38,6 +39,8 @@ program
3839
.description('Run tasks in a hook')
3940
.action(async hook => {
4041
const instance = await frontvue;
42+
const logger = instance.logger('cli');
43+
logger.info(`Starting ${hook}...`);
4144
instance.run(hook);
4245
});
4346

src/config-manager/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface IConfigManager {
1919
remove(...options: string[]): Promise<boolean>;
2020
}
2121

22+
2223
/**
2324
* Configuration manager constructor
2425
* @param namespace Configuration namespace, usually app name

src/core.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ import ConfigManager from './config-manager';
99
import PluginManager from './plugin-manager';
1010
import TaskManager from './task-manager';
1111
import taskInitProject from './tasks/task-init-project';
12+
import Logger, { ILogger } from './util/logger';
1213

1314

1415
/**
1516
* Main Frontvue constructor
1617
*/
1718
async function Frontvue() {
1819
const name = 'frontvue';
20+
const logger = Logger(name);
1921
const configManager = await ConfigManager(name);
2022
const taskManager = TaskManager({
2123
hooks: [
@@ -30,6 +32,7 @@ async function Frontvue() {
3032

3133
// Return public API
3234
return Object.freeze({
35+
logger,
3336
name,
3437
run,
3538
});

src/task-manager/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface TaskManager {
3131
getHooks?(): string[];
3232
}
3333

34-
interface TaskManagerOptions {
34+
export interface TaskManagerOptions {
3535
[key: string]: any;
3636
}
3737

src/tasks/task-init-project.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
*/
77

88
import * as gulp from 'gulp';
9+
import { Task } from '../task-manager';
910
import PlugableTask from '../task-manager/plugable-task';
11+
import Logger from '../util/logger';
1012
import { AnyFunction } from '../util/utility-functions';
1113

14+
const logger = Logger('frontvue')('init');
1215

1316
// Task meta data
1417
const hook = 'init';
@@ -21,7 +24,7 @@ const description = 'Task for initializing a new project';
2124
* @param done Gulp async callback
2225
*/
2326
function task(done?: AnyFunction): any {
24-
console.log(`>>> Running Task: ${name}`);
27+
logger.log(`Running Task: ${name}`);
2528
done && done();
2629
}
2730

src/util/logger.spec.ts

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { assert, expect } from 'chai';
2+
import 'mocha';
3+
import { stdout } from 'test-console';
4+
import Logger, { ERRORS, LogLevel } from './logger';
5+
6+
describe('Logger', () => {
7+
describe('Constructor', () => {
8+
it('returns a function when instantiated with a namespace', () => {
9+
expect(Logger('namespace')).to.be.a('function');
10+
});
11+
12+
13+
it('returns an object when returned function is called', () => {
14+
expect(Logger('namespace')()).to.be.an('object')
15+
.to.contain.keys('channel', 'debug', 'error', 'fatal', 'info', 'log', 'success', 'warn');
16+
});
17+
18+
19+
it('creates a prefix', () => {
20+
const logger = Logger('namespace')('channel');
21+
expect(logger.prefix(LogLevel.debug, 'channel')).to.include('namespace');
22+
expect(logger.prefix(LogLevel.debug, 'channel')).to.include('DEBUG');
23+
expect(logger.prefix(LogLevel.debug, 'channel')).to.include('@channel');
24+
});
25+
26+
27+
it('creates a prefix without a channel', () => {
28+
const logger = Logger('namespace')();
29+
expect(logger.prefix(LogLevel.debug)).to.not.include('@');
30+
});
31+
32+
33+
it('throws when namespace is not supplied', () => {
34+
assert.throws(() => Logger(undefined), ERRORS.NO_NAMESPACE);
35+
});
36+
});
37+
38+
39+
describe('Instance', () => {
40+
const logger = Logger('namespace')('channel');
41+
let inspect: any;
42+
43+
const CheckForAll = bits => string => bits.every(bit => string.includes(bit));
44+
const CheckForSome = bits => string => bits.some(bit => string.includes(bit));
45+
46+
beforeEach(() => {
47+
inspect = stdout.inspect();
48+
});
49+
50+
afterEach(() => {
51+
inspect.restore();
52+
});
53+
54+
55+
it('logs out in debug level', () => {
56+
const containsAll = CheckForAll(['channel', 'debug message']);
57+
logger.debug('debug message');
58+
expect(inspect.output.join(' ')).to.satisfy(containsAll);
59+
inspect.restore();
60+
});
61+
62+
63+
it('doesn\'t logs out in debug level if env is not \'development\' or \'test\'', () => {
64+
// Set node env to 'production' to prevent debug method to console log
65+
const NODE_ENV = process.env.NODE_ENV;
66+
process.env.NODE_ENV = 'production';
67+
68+
const silencedLogger = Logger('namespace')('channel').debug('debug message');
69+
expect(inspect.output.join(' ')).to.equal('');
70+
inspect.restore();
71+
72+
// Restore node env
73+
process.env.NODE_ENV = NODE_ENV;
74+
});
75+
76+
77+
it('logs out in error level', () => {
78+
const containsAll = CheckForAll(['namespace', 'ERROR', 'error message']);
79+
logger.error('error message');
80+
expect(inspect.output.join(' ')).to.satisfy(containsAll);
81+
inspect.restore();
82+
});
83+
84+
85+
it('logs out in fatal level', () => {
86+
const containsAll = CheckForAll(['namespace', 'FATAL', 'fatal message']);
87+
logger.fatal('fatal message');
88+
expect(inspect.output.join(' ')).to.satisfy(containsAll);
89+
inspect.restore();
90+
});
91+
92+
93+
it('logs out in info level', () => {
94+
const containsAll = CheckForAll(['namespace', 'INFO', 'info message']);
95+
logger.info('info message');
96+
expect(inspect.output.join(' ')).to.satisfy(containsAll);
97+
inspect.restore();
98+
});
99+
100+
101+
it('logs out in log level', () => {
102+
const containsAll = CheckForAll(['channel', 'log message']);
103+
logger.log('log message');
104+
expect(inspect.output.join(' ')).to.satisfy(containsAll);
105+
inspect.restore();
106+
});
107+
108+
109+
it('logs out in success level', () => {
110+
const containsAll = CheckForAll(['namespace', 'SUCCESS', 'success message']);
111+
logger.success('success message');
112+
expect(inspect.output.join(' ')).to.satisfy(containsAll);
113+
inspect.restore();
114+
});
115+
116+
117+
it('logs out in warn level', () => {
118+
const containsAll = CheckForAll(['namespace', 'WARN', 'warn message']);
119+
logger.warn('warn message');
120+
expect(inspect.output.join(' ')).to.satisfy(containsAll);
121+
inspect.restore();
122+
});
123+
});
124+
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy