Permalink
Browse files

refactor(src): migrate `loglevelnext` into `webpack-log`

  • Loading branch information...
michael-ciniawsky committed Aug 22, 2018
1 parent 9630380 commit b5c2017e45b013a1b4c9e09694fc120afc3a307a
Showing with 345 additions and 20 deletions.
  1. +0 −5 package-lock.json
  2. +2 −3 package.json
  3. +12 −12 { → src}/index.js
  4. +83 −0 src/loglevel/LogLevel.js
  5. +136 −0 src/loglevel/MethodFactory.js
  6. +56 −0 src/loglevel/PrefixFactory.js
  7. +56 −0 src/loglevel/index.js
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View
@@ -2,9 +2,9 @@
"name": "webpack-log",
"version": "1.2.0",
"description": "A common logger for the webpack ecosystem",
"main": "index.js",
"main": "src/index.js",
"files": [
"index.js"
"src"
],
"engines": {
"node": ">= 6"
@@ -20,7 +20,6 @@
},
"dependencies": {
"ansi-colors": "^3.0.0",
"loglevelnext": "^1.0.1",
"uuid": "^3.1.0"
},
"devDependencies": {
View
@@ -1,8 +1,8 @@
'use strict';
const colors = require('ansi-colors');
const loglevel = require('loglevelnext'); //eslint-disable-line
const uuid = require('uuid/v4');
const colors = require('ansi-colors');
const loglevel = require('./loglevel');
const symbols = {
trace: colors.grey(''),
@@ -19,11 +19,13 @@ const defaults = {
};
const prefix = {
level: opts => symbols[opts.level],
level (options) {
return symbols[options.level]
},
template: `{{level}} ${colors.gray('「{{name}}」')}: `
};
module.exports = function webpackLog(options) {
function log (options) {
const opts = Object.assign({}, defaults, options);
const { id } = options;
@@ -57,16 +59,14 @@ module.exports = function webpackLog(options) {
return log;
};
module.exports = log
// NOTE: this is exported so that consumers of webpack-log can use the same
// version of ansi-colors to decorate log messages without incurring additional
// dependency overhead
// version of ansi-colors to decorate log messages without incurring additional
// dependency overhead
module.exports.colors = colors;
/**
* @NOTE: This is an undocumented function solely for the purpose of tests.
* Do not use this method in production code. Using in production code
* may result in strange behavior.
*/
// NOTE: This is an undocumented function solely for the purpose of tests.
// Do not use this method in production code. Using in production code
// may result in strange behavior.
module.exports.delLogger = function delLogger(name) {
delete loglevel.loggers[name];
};
View
@@ -0,0 +1,83 @@
'use strict';
/* global window: true */
const PrefixFactory = require('./PrefixFactory');
const MethodFactory = require('./MethodFactory');
const defaults = {
name: +new Date(),
level: 'warn',
prefix: null,
factory: null,
};
class LogLevel {
constructor(options) {
// implement for some _very_ loose type checking. avoids getting into a
// circular require between MethodFactory and LogLevel
this.type = 'LogLevel';
this.options = Object.assign({}, defaults, options);
this.methodFactory = options.factory;
if (!this.methodFactory) {
const factory = options.prefix
? new PrefixFactory(this, options.prefix)
: new MethodFactory(this);
this.methodFactory = factory;
}
if (!this.methodFactory.logger) {
this.methodFactory.logger = this;
}
this.name = options.name || '<unknown>';
// this.level is a setter, do this after setting up the factory
this.level = this.options.level;
}
get factory() {
return this.methodFactory;
}
set factory(factory) {
factory.logger = this;
this.methodFactory = factory;
this.methodFactory.replaceMethods(this.level);
}
disable() {
this.level = this.levels.SILENT;
}
enable() {
this.level = this.levels.TRACE;
}
get level() {
return this.currentLevel;
}
set level(logLevel) {
const level = this.methodFactory.distillLevel(logLevel);
if (level == null) {
throw new Error(`loglevelnext: setLevel() called with invalid level: ${logLevel}`);
}
this.currentLevel = level;
this.methodFactory.replaceMethods(level);
if (typeof console === 'undefined' && level < this.levels.SILENT) {
// eslint-disable-next-line no-console
console.warn('loglevelnext: console is undefined. The log will produce no output');
}
}
get levels() { // eslint-disable-line class-methods-use-this
return this.methodFactory.levels;
}
};
module.exports = LogLevel;
@@ -0,0 +1,136 @@
'use strict';
const noop = () => {};
const levels = Symbol('valid log levels');
const instance = Symbol('a log instance');
class MethodFactory {
constructor(logger) {
this[levels] = {
TRACE: 0,
DEBUG: 1,
INFO: 2,
WARN: 3,
ERROR: 4,
SILENT: 5
};
this[instance] = logger;
}
get levels() {
return this[levels];
}
get logger() {
return this[instance];
}
set logger(logger) {
this[instance] = logger;
}
get methods() {
return Object.keys(this.levels)
.map(key => key.toLowerCase())
.filter(key => key !== 'silent');
}
// eslint-disable-next-line class-methods-use-this
bindMethod(obj, methodName) {
const method = obj[methodName];
if (typeof method.bind === 'function') {
return method.bind(obj);
}
try {
return Function.prototype.bind.call(method, obj);
} catch (e) {
// Missing bind shim or IE8 + Modernizr, fallback to wrapping
return function result() {
// eslint-disable-next-line prefer-rest-params
return Function.prototype.apply.apply(method, [obj, arguments]);
};
}
}
distillLevel(level) {
let result = level;
if (
typeof result === 'string' &&
typeof this.levels[result.toUpperCase()] !== 'undefined'
) {
result = this.levels[result.toUpperCase()];
}
if (this.levelValid(result)) {
return result;
}
}
levelValid(level) {
if (
typeof level === 'number' && level >= 0 &&
level <= this.levels.SILENT
) {
return true;
}
return false;
}
/**
* Build the best logging method possible for this env
* Wherever possible we want to bind, not wrap, to preserve stack traces.
* Since we're targeting modern browsers, there's no need to wait for the
* console to become available.
*/
// eslint-disable-next-line class-methods-use-this
make(methodName) {
if (methodName === 'debug') {
methodName = 'log';
}
/* eslint-disable no-console */
if (typeof console[methodName] !== 'undefined') {
return this.bindMethod(console, methodName);
}
if (typeof console.log !== 'undefined') {
return this.bindMethod(console, 'log');
}
/* eslint-enable no-console */
return noop;
}
replaceMethods(logLevel) {
const level = this.distillLevel(logLevel);
if (level == null) {
throw new Error(
`loglevelnext: replaceMethods() called with invalid level: ${logLevel}`
);
}
if (!this.logger || this.logger.type !== 'LogLevel') {
throw new TypeError(
'loglevelnext: Logger is undefined or invalid. Please specify a valid Logger instance.'
);
}
this.methods.forEach((methodName) => {
const { [methodName.toUpperCase()]: methodLevel } = this.levels;
this.logger[methodName] = (methodLevel < level)
? noop
: this.make(methodName);
});
// Define log.log as an alias for log.debug
this.logger.log = this.logger.debug;
}
};
module.exports = MethodFactory
@@ -0,0 +1,56 @@
'use strict';
const MethodFactory = require('./MethodFactory');
const defaults = {
name (options) {
return options.logger.name
},
time () {
return new Date().toTimeString().split(' ')[0]
},
level (options) {
return `[${options.level}]`
},
template: '{{time}} {{level}} ',
};
class PrefixFactory extends MethodFactory {
constructor(logger, options) {
super(logger);
this.options = Object.assign({}, defaults, options);
}
interpolate(level) {
return this.options.template.replace(/{{([^{}]*)}}/g, (stache, prop) => {
const fn = this.options[prop];
if (fn) {
return fn({ level, logger: this.logger });
}
return stache;
});
}
make(methodName) {
const og = super.make(methodName);
return (...args) => {
const [first] = args;
const output = this.interpolate(methodName);
if (typeof first === 'string') {
args[0] = output + first;
} else {
args.unshift(output);
}
og(...args);
};
}
};
module.exports = PrefixFactory
Oops, something went wrong.

0 comments on commit b5c2017

Please sign in to comment.