diff --git a/.babelrc b/.babelrc deleted file mode 100644 index ad36157..0000000 --- a/.babelrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "presets": ["es2015", "react", "stage-0"], - "plugins": ["add-module-exports"] -} diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 40cf70c..0000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -dist -lib -example/build diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index dc663b4..0000000 --- a/.eslintrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "airbnb", - "env": { - "browser": true - } -} diff --git a/.travis.yml b/.travis.yml index 3163971..0acbf81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js node_js: - "6" -script: npm run test:production +script: npm test # Repository token must be provided in "coverage:production" npm script after_success: npm run coverage:production diff --git a/package.json b/package.json index a5101e9..b676e1c 100644 --- a/package.json +++ b/package.json @@ -2,23 +2,37 @@ "name": "redux-logger", "version": "3.0.1", "description": "Logger for Redux", - "main": "lib/index.js", + "main": "dist/redux-logger.js", + "module": "src/index.js", + "jsnext:main": "src/index.js", "scripts": { - "lint": "$(npm bin)/eslint src", - "test": "NODE_ENV=development npm run lint && npm run spec", - "test:production": "NODE_ENV=production npm run lint && npm run spec", - "spec": "NODE_PATH=src nyc --all --silent --require babel-core/register mocha --plugins transform-inline-environment-variables --recursive spec/*.spec.js", - "spec:watch": "NODE_ENV=development npm run spec -- --watch", + "lint": "eslint src", + "test": "npm run lint && npm run spec", + "spec": "nyc --all --silent --require babel-core/register mocha --plugins transform-inline-environment-variables --recursive spec/*.spec.js", + "spec:watch": "npm run spec -- --watch", "coverage": "nyc report", - "coverage:html": "nyc report --reporter=html && (http-server -p 8077 ./coverage & open-url http://localhost:8077/)", + "coverage:html": "nyc report --reporter=html && http-server -p 8077 ./coverage -o", "coverage:production": "nyc report --reporter=text-lcov > coverage.lcov && codecov", - "clean": "$(npm bin)/rimraf dist lib", - "build:lib": "$(npm bin)/babel src --out-dir lib", - "build:umd": "LIBRARY_NAME=reduxLogger NODE_ENV=development $(npm bin)/webpack src/index.js dist/index.js --config webpack.build.js", - "build:umd:min": "LIBRARY_NAME=reduxLogger NODE_ENV=production $(npm bin)/webpack -p src/index.js dist/index.min.js --config webpack.build.js", - "build": "npm run build:lib && npm run build:umd && npm run build:umd:min", - "precommit": "npm run test", - "prepublish": "npm run clean && npm run test:production && npm run build" + "clean": "rimraf dist", + "build": "rollup -c", + "precommit": "npm test", + "prepublish": "npm run clean && npm test && npm run build" + }, + "babel": { + "presets": [ + "es2015", + "stage-0" + ] + }, + "eslintConfig": { + "extends": "airbnb", + "rules": { + "no-console": "off" + }, + "env": { + "browser": true, + "mocha": true + } }, "nyc": { "exclude": [ @@ -27,12 +41,12 @@ "example", "lib", "dist", - "webpack.*.js" + "coverage", + "rollup.config.js" ] }, "files": [ "dist", - "lib", "src" ], "repository": { @@ -52,14 +66,10 @@ }, "homepage": "https://github.com/theaqua/redux-logger#readme", "devDependencies": { - "babel-cli": "^6.24.0", "babel-core": "^6.24.0", - "babel-loader": "^6.4.1", - "babel-plugin-add-module-exports": "0.2.1", - "babel-plugin-transform-es2015-modules-umd": "6.24.0", + "babel-plugin-external-helpers": "^6.22.0", "babel-plugin-transform-inline-environment-variables": "6.8.0", "babel-preset-es2015": "^6.24.0", - "babel-preset-react": "^6.23.0", "babel-preset-stage-0": "^6.22.0", "chai": "3.5.0", "codecov": "1.0.1", @@ -72,13 +82,16 @@ "husky": "^0.13.2", "mocha": "3.1.2", "nyc": "9.0.1", - "open-url": "2.0.2", "redux": "^3.6.0", "rimraf": "^2.6.1", - "sinon": "^1.17.7", - "webpack": "1.13.3" + "rollup": "^0.41.6", + "rollup-plugin-babel": "^2.7.1", + "rollup-plugin-commonjs": "^8.0.2", + "rollup-plugin-node-resolve": "^3.0.0", + "rollup-plugin-uglify": "^1.0.2", + "sinon": "^1.17.7" }, "dependencies": { - "deep-diff": "0.3.4" + "deep-diff": "^0.3.5" } } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..da52aa7 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,35 @@ +import babel from 'rollup-plugin-babel'; +import commonjs from 'rollup-plugin-commonjs'; +import nodeResolve from 'rollup-plugin-node-resolve'; +import uglify from 'rollup-plugin-uglify'; + +export default { + entry: 'src/index.js', + format: 'umd', + exports: 'named', + moduleName: 'reduxLogger', + dest: 'dist/redux-logger.js', + plugins: [ + babel({ + babelrc: false, + presets: [ + ['es2015', { + modules: false, + }], + 'stage-0' + ], + plugins: [ + 'external-helpers' + ], + }), + commonjs({ + include: 'node_modules/**', + }), + nodeResolve({ + jsnext: true, + main: true, + browser: true, + }), + uglify() + ] +}; diff --git a/spec/diff.spec.js b/spec/diff.spec.js new file mode 100644 index 0000000..a89b4bf --- /dev/null +++ b/spec/diff.spec.js @@ -0,0 +1,115 @@ +import sinon from 'sinon'; +import { expect } from 'chai'; +import { style, render, default as diffLogger } from '../src/diff'; + +context('Diff', () => { + describe('style', () => { + it('return css rules for the given kind of diff changes', () => { + expect(style('E')).to.equal('color: #2196F3; font-weight: bold'); + expect(style('N')).to.equal('color: #4CAF50; font-weight: bold'); + expect(style('D')).to.equal('color: #F44336; font-weight: bold'); + expect(style('A')).to.equal('color: #2196F3; font-weight: bold'); + }); + }); + + describe('render', () => { + it('should return an array indicating the changes', () => { + expect(render({ + kind: 'E', + path: ['capitain', 'name'], + lhs: 'kirk', + rhs: 'picard', + })).to.eql(['capitain.name', 'kirk', '→', 'picard']); + }); + + it('should return an array indicating an added property/element', () => { + expect(render({ + kind: 'N', + path: ['crew', 'engineer'], + rhs: 'geordi', + })).to.eql(['crew.engineer', 'geordi']); + }); + + it('should return an array indicating a removed property/element', () => { + expect(render({ + kind: 'D', + path: ['crew', 'security'], + })).to.eql(['crew.security']); + }); + + it('should return an array indicating a changed index', () => { + expect(render({ + kind: 'A', + path: ['crew'], + index: 2, + item: { + kind: 'N', + rhs: 'after', + }, + })).to.eql(['crew[2]', { + kind: 'N', + rhs: 'after', + }]); + }); + + it('should return an empty array', () => { + expect(render({})).to.eql([]); + }); + }); + + describe('diffLogger', () => { + let logger + + beforeEach(() => { + logger = { + log: sinon.spy(), + groupCollapsed: sinon.spy(), + groupEnd: sinon.spy(), + group: sinon.spy(), + }; + }); + + it('should show no diff with group collapsed', () => { + diffLogger({}, {}, logger, true); + + expect(logger.group.calledOnce).to.be.false; + expect(logger.groupCollapsed.calledOnce).to.be.true; + expect(logger.groupEnd.calledOnce).to.be.true; + expect(logger.log.calledOnce).to.be.true; + expect(logger.log.calledWith('—— no diff ——')).to.be.true; + }); + + it('should show no diff with group not collapsed', () => { + diffLogger({}, {}, logger, false); + + expect(logger.group.calledOnce).to.be.true; + expect(logger.groupCollapsed.calledOnce).to.be.false; + expect(logger.groupEnd.calledOnce).to.be.true; + expect(logger.log.calledOnce).to.be.true; + expect(logger.log.calledWith('—— no diff ——')).to.be.true; + }); + + it('should log no diff without group', () => { + const loggerWithNoGroupCollapsed = Object.assign({}, logger, { + groupCollapsed: () => { + throw new Error() + }, + groupEnd: () => { + throw new Error() + }, + }); + + diffLogger({}, {}, loggerWithNoGroupCollapsed, true); + + expect(loggerWithNoGroupCollapsed.log.calledWith('diff')).to.be.true; + expect(loggerWithNoGroupCollapsed.log.calledWith('—— no diff ——')).to.be.true; + expect(loggerWithNoGroupCollapsed.log.calledWith('—— diff end —— ')).to.be.true; + }); + + it('should log the diffs', () => { + diffLogger({name: 'kirk'}, {name: 'picard'}, logger, false); + + expect(logger.log.calledWithExactly('%c CHANGED:', 'color: #2196F3; font-weight: bold', 'name', 'kirk', '→', 'picard')).to.be.true; + }); + }); +}); diff --git a/spec/helpers.spec.js b/spec/helpers.spec.js new file mode 100644 index 0000000..7d8d4c2 --- /dev/null +++ b/spec/helpers.spec.js @@ -0,0 +1,23 @@ +import { expect } from 'chai'; +import { repeat, pad, formatTime } from '../src/helpers'; + +context('Helpers', () => { + describe('repeat', () => { + it('should repeat a string the number of indicated times', () => { + expect(repeat('teacher', 3)).to.equal('teacherteacherteacher'); + }); + }); + + describe('pad', () => { + it('should add leading zeros to a number given a maximun length', () => { + expect(pad(56, 4)).to.equal('0056'); + }); + }); + + describe('formatTime', () => { + it('should format a time given a Date object', () => { + const time = new Date('December 25, 1995 23:15:30'); + expect(formatTime(time)).to.equal('23:15:30.000'); + }); + }); +}); diff --git a/spec/index.spec.js b/spec/index.spec.js index 0e7d200..0373ef6 100644 --- a/spec/index.spec.js +++ b/spec/index.spec.js @@ -1,59 +1,47 @@ -import { expect } from 'chai'; import sinon from 'sinon'; - import { applyMiddleware, createStore } from 'redux'; +import { default as logger, createLogger } from '../src'; -import { repeat } from 'helpers'; -import logger, { createLogger } from '../src'; - -context(`Helpers`, () => { - describe(`repeat`, () => { - it(`should repeat a string the number of indicated times`, () => { - expect(repeat(`teacher`, 3)).to.equal(`teacherteacherteacher`); - }); - }); -}); - -context(`default logger`, () => { - describe(`init`, () => { +context('default logger', () => { + describe('init', () => { beforeEach(() => { - sinon.spy(console, `error`); + sinon.spy(console, 'error'); }); afterEach(() => { console.error.restore(); }); - it(`should be ok`, () => { + it('should be ok', () => { const store = createStore(() => ({}), applyMiddleware(logger)); - store.dispatch({ type: `foo` }); + store.dispatch({ type: 'foo' }); sinon.assert.notCalled(console.error); }); }); }); -context(`createLogger`, () => { - describe(`init`, () => { +context('createLogger', () => { + describe('init', () => { beforeEach(() => { - sinon.spy(console, `error`); + sinon.spy(console, 'error'); }); afterEach(() => { console.error.restore(); }); - it(`should throw error if passed direct to applyMiddleware`, () => { + it('should throw error if passed direct to applyMiddleware', () => { const store = createStore(() => ({}), applyMiddleware(createLogger)); - store.dispatch({ type: `foo` }); + store.dispatch({ type: 'foo' }); sinon.assert.calledOnce(console.error); }); - it(`should be ok`, () => { + it('should be ok', () => { const store = createStore(() => ({}), applyMiddleware(createLogger())); - store.dispatch({ type: `foo` }); + store.dispatch({ type: 'foo' }); sinon.assert.notCalled(console.error); }); }); diff --git a/src/diff.js b/src/diff.js index d2435c5..31d20d6 100644 --- a/src/diff.js +++ b/src/diff.js @@ -20,11 +20,11 @@ const dictionary = { }, }; -function style(kind) { +export function style(kind) { return `color: ${dictionary[kind].color}; font-weight: bold`; } -function render(diff) { +export function render(diff) { const { kind, path, lhs, rhs, index, item } = diff; switch (kind) { diff --git a/webpack.build.js b/webpack.build.js deleted file mode 100644 index dfc3d57..0000000 --- a/webpack.build.js +++ /dev/null @@ -1,4 +0,0 @@ -require(`babel-core/register`)(); -const config = require(`./webpack.config`); - -module.exports = config; diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 98ffab8..0000000 --- a/webpack.config.js +++ /dev/null @@ -1,23 +0,0 @@ -import webpack from 'webpack'; -import path from 'path'; - -export default { - entry: [ - path.join(__dirname, `src`, `index`), - ], - output: { - library: process.env.LIBRARY_NAME, - libraryTarget: `umd`, - }, - module: { - loaders: [ - { test: /\.js$/, exclude: /node_modules/, loaders: [`babel`] }, - ], - }, - plugins: [ - new webpack.optimize.OccurenceOrderPlugin(), - new webpack.DefinePlugin({ - NODE_ENV: JSON.stringify(process.env.NODE_ENV), - }), - ], -};