diff --git a/.bowerrc b/.bowerrc deleted file mode 100644 index 44491d3..0000000 --- a/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory": "bower_components" -} diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..ad81109 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,18 @@ +{ + "parser": "babel-eslint", + "extends": [ + "standard" + ], + "env": { + "jasmine": true + }, + "rules": { + // overrides of the standard style + "curly": [2, "all"], + "indent": [2, 4], + "max-len": [2, 100, 4], + "semi": [2, "always"], + "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}], + "wrap-iife": [2, "outside"] + } +} diff --git a/.gitignore b/.gitignore index 7bf6eb1..44d646d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -bower_components node_modules +dist/ diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index cc2daa4..0000000 --- a/.jshintrc +++ /dev/null @@ -1,36 +0,0 @@ -{ - "node": true, - "browser": true, - "esnext": true, - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "immed": true, - "indent": 2, - "latedef": false, - "newcap": true, - "noarg": true, - "quotmark": "single", - "regexp": true, - "smarttabs": true, - "strict": true, - "trailing": true, - "undef": true, - "validthis": true, - "predef": [ - "$", - "jQuery", - "before", - "beforeEach", - "define", - "describe", - "describeComponent", - "describeMixin", - "expect", - "it", - "requirejs", - "setupComponent", - "spyOnEvent" - ] -} diff --git a/.travis.yml b/.travis.yml index 29582fa..9ce799e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,20 @@ +sudo: false language: node_js +cache: + directories: + - node_modules +notifications: + email: false node_js: - - "0.10" -before_script: - - npm install -g bower - - bower install + - '4' before_install: - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" + - npm i -g npm@^2.0.0 +before_script: + - npm prune + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start +after_success: + - npm run semantic-release +branches: + except: + - "/^v\\d+\\.\\d+\\.\\d+$/" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 23d7907..f9f72cd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,8 @@ the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features. +By contributing to this repository, including using the issue tracker, you agree to adhere to Twitter's [Open Source Code of Conduct][coc]. + ## Using the issue tracker diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 0c48074..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,24 +0,0 @@ -module.exports = function (grunt) { - - require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); - - grunt.initConfig({ - bump: { - options: { - files: [ - 'package.json', - 'bower.json' - ], - commit: true, - commitMessage: 'v%VERSION%', - commitFiles: ['-a'], - createTag: true, - tagName: 'v%VERSION%', - tagMessage: 'v%VERSION%', - push: true, - pushTo: 'origin', - gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d' - } - } - }); -}; diff --git a/README.md b/README.md index b0a07ff..d53c714 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,11 @@ A [Flight](https://github.com/flightjs/flight) mixin which extends the [flight-w ## Installation ```bash -bower install --save flight-with-observable-state +npm install --save flight-with-observable-state ``` +This module requires `flightjs`, `rx` and `flight-with-state` as a peer dependencies. + ## Example Here's an example component that uses `withObservableState`. @@ -66,25 +68,13 @@ var ToggleButton = flight.component( ## Development -Development of this component requires [Bower](http://bower.io) to be globally -installed: +To develop this module, clone the repository and run: -```bash -npm install -g bower ``` - -Then install the Node.js and client-side dependencies by running the following -commands in the repo's root directory. - -```bash -npm install & bower install +$ npm install && npm test ``` -To continuously run the tests in Chrome during development, just run: - -```bash -npm run watch-test -``` +If the tests pass, you have a working environment. You shouldn't need any external dependencies. ## Contributing to this project diff --git a/bower.json b/bower.json deleted file mode 100644 index 8e7e48d..0000000 --- a/bower.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "flight-with-observable-state", - "main": "lib/with-observable-state.js", - "dependencies": { - "flight": "^1.2.0", - "flight-with-state": "^2.1.0", - "rxjs": "^2.3.0" - }, - "devDependencies": { - "jasmine-flight": "latest", - "jasmine-jquery": "~2.1.0" - }, - "ignore": [ - ".gitignore", - ".gitattributes", - ".travis.yml", - "CONTRIBUTING.md", - "CHANGELOG.md", - "test", - "package.json", - "karma.conf.js" - ] -} diff --git a/config/constants.js b/config/constants.js new file mode 100644 index 0000000..f4125af --- /dev/null +++ b/config/constants.js @@ -0,0 +1,9 @@ +var path = require('path'); + +var ROOT_DIRECTORY = path.resolve(__dirname, '..'); +var BUILD_DIRECTORY = path.resolve(ROOT_DIRECTORY, 'dist'); + +module.exports = { + ROOT_DIRECTORY: ROOT_DIRECTORY, + BUILD_DIRECTORY: BUILD_DIRECTORY +}; diff --git a/config/karma.config.js b/config/karma.config.js new file mode 100644 index 0000000..cc76bc8 --- /dev/null +++ b/config/karma.config.js @@ -0,0 +1,47 @@ +'use strict'; + +var constants = require('./constants'); +var webpackConfig = require('./webpack.config.test'); +// entry is determined by karma config 'files' array +webpackConfig.entry = {}; + +module.exports = function (config) { + config.set({ + basePath: constants.ROOT_DIRECTORY, + browsers: [ process.env.TRAVIS ? 'Firefox' : 'Chrome' ], + browserNoActivityTimeout: 60000, + client: { + captureConsole: true, + useIframe: true + }, + files: [ + 'node_modules/jquery/dist/jquery.min.js', + 'src/specs.context.js' + ], + frameworks: [ + 'jasmine' + ], + plugins: [ + 'karma-chrome-launcher', + 'karma-firefox-launcher', + 'karma-jasmine', + 'karma-sourcemap-loader', + 'karma-webpack' + ], + preprocessors: { + 'src/specs.context.js': [ 'webpack', 'sourcemap' ] + }, + reporters: [ 'dots' ], + singleRun: true, + webpack: webpackConfig, + webpackMiddleware: { + stats: { + assetsSort: 'name', + colors: true, + children: false, + chunks: false, + modules: false + } + } + }); +}; diff --git a/config/webpack.config.js b/config/webpack.config.js new file mode 100644 index 0000000..7ce479a --- /dev/null +++ b/config/webpack.config.js @@ -0,0 +1,46 @@ +var webpack = require('webpack'); + +var DedupePlugin = webpack.optimize.DedupePlugin; +var OccurenceOrderPlugin = webpack.optimize.OccurenceOrderPlugin; +var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; + +var plugins = [ + new DedupePlugin(), + new OccurenceOrderPlugin() +]; + +if (process.env.NODE_ENV === 'publish') { + plugins.push( + new UglifyJsPlugin({ + compress: { + dead_code: true, + drop_console: true, + screw_ie8: true, + warnings: true + } + }) + ); +} + +module.exports = { + entry: './src', + module: { + loaders: [ + { + test: /\.jsx?$/, + exclude: /node_modules/, + loader: 'babel-loader' + } + ] + }, + resolve: { + alias: { + flight: 'flightjs' + } + }, + output: { + path: './dist', + filename: 'flight-with-observable-state.js' + }, + plugins: plugins +}; diff --git a/config/webpack.config.publish.js b/config/webpack.config.publish.js new file mode 100644 index 0000000..107b158 --- /dev/null +++ b/config/webpack.config.publish.js @@ -0,0 +1,16 @@ +var constants = require('./constants'); +var baseConfig = require('./webpack.config'); + +module.exports = Object.assign(baseConfig, { + output: { + library: 'withObservableState', + filename: 'flight-with-observable-state.js', + libraryTarget: 'umd', + path: constants.BUILD_DIRECTORY + }, + externals: [ + 'rx', + 'flight', + 'flight-with-state' + ] +}); diff --git a/config/webpack.config.test.js b/config/webpack.config.test.js new file mode 100644 index 0000000..fd13dad --- /dev/null +++ b/config/webpack.config.test.js @@ -0,0 +1,5 @@ +var baseConfig = require('./webpack.config'); + +module.exports = Object.assign(baseConfig, { + devtool: 'inline-source-map' +}); diff --git a/karma.conf.js b/karma.conf.js deleted file mode 100644 index 194fecd..0000000 --- a/karma.conf.js +++ /dev/null @@ -1,59 +0,0 @@ -// Karma configuration file -// -// For all available config options and default values, see: -// https://github.com/karma-runner/karma/blob/stable/lib/config.js#L54 - -module.exports = function (config) { - 'use strict'; - - config.set({ - // base path, that will be used to resolve files and exclude - basePath: '', - - frameworks: [ - 'jasmine', - 'requirejs' - ], - - // list of files / patterns to load in the browser - files: [ - // loaded without require - 'bower_components/jquery/dist/jquery.min.js', - 'bower_components/jasmine-jquery/lib/jasmine-jquery.js', - 'bower_components/jasmine-flight/lib/jasmine-flight.js', - - // loaded with require - {pattern: 'bower_components/flight/**/*.js', included: false}, - {pattern: 'bower_components/flight-with-state/lib/with-state.js', included: false}, - {pattern: 'bower_components/rxjs/dist/rx.lite.min.js', included: false}, - {pattern: 'lib/**/*.js', included: false}, - {pattern: 'test/spec/**/*.spec.js', included: false}, - - 'test/test-main.js' - ], - - // enable / disable watching file and executing tests whenever any file changes - // CLI --auto-watch --no-auto-watch - autoWatch: true, - - // Start these browsers - // CLI --browsers Chrome, Firefox, Safari - browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'], - - // If browser does not capture in given timeout [ms], kill it - // CLI --capture-timeout 5000 - captureTimeout: 20000, - - // list of files to exclude - exclude: [], - - // use dots reporter, as travis terminal does not support escaping sequences - // possible values: 'dots', 'progress' - // CLI --reporters progress - reporters: [process.env.TRAVIS ? 'dots' : 'progress'], - - // Auto run tests on start (when browsers are captured) and exit - // CLI --single-run --no-single-run - singleRun: false - }); -}; diff --git a/lib/with-observable-state.js b/lib/with-observable-state.js deleted file mode 100644 index 1a499be..0000000 --- a/lib/with-observable-state.js +++ /dev/null @@ -1,31 +0,0 @@ -define(function (require) { - 'use strict'; - - var compose = require('flight/lib/compose'); - var withState = require('flight-with-state'); - - /** - * with-observable-state uses RXJS to implement its observer/observable - * patterns. - * https://github.com/Reactive-Extensions/RxJS - */ - var Rx = require('rxjs'); - - return withObservableState; - function withObservableState() { - /* jshint validthis: true */ - - compose.mixin(this, [withState]); - - this.after('initialize', function () { - // Construct stream with an initial value of the components state. - var stateSubject = new Rx.BehaviorSubject(this.state); - - // Expose only the observable, so nothing else can push values on to the stream. - this.observableState = stateSubject.asObservable(); - - // When component state changes, push values on to the stream. - this.after('stateChanged', stateSubject.onNext.bind(stateSubject)); - }); - } -}); \ No newline at end of file diff --git a/package.json b/package.json index aff97f2..ea41a9f 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,55 @@ { "name": "flight-with-observable-state", - "version": "1.1.0", - "devDependencies": { - "bower": "^1.4.1", - "grunt": "~0.4.5", - "grunt-bump": "latest", - "karma": "~0.12.6", - "karma-chrome-launcher": "*", - "karma-cli": "0.0.4", - "karma-firefox-launcher": "*", - "karma-jasmine": "~0.2.0", - "karma-requirejs": "~0.2.2", - "matchdep": "latest", - "requirejs": "^2.1.15" - }, + "description": "A Flight mixin for exposing component state via an observable stream.", + "main": "dist/flight-with-state.js", + "files": [ + "dist" + ], "scripts": { - "install": "bower install", - "test": "karma start --single-run", - "watch-test": "karma start" + "build": "rm -rf ./dist && NODE_ENV=publish webpack --config config/webpack.config.publish.js --sort-assets-by --progress", + "lint": "eslint config src", + "lint:fix": "eslint --fix config src", + "prepublish": "npm run build", + "specs": "NODE_ENV=test karma start config/karma.config.js", + "specs:watch": "npm run specs -- --no-single-run", + "test": "npm run specs && npm run lint", + "semantic-release": "semantic-release pre && npm publish && semantic-release post" }, - "description": "A Flight mixin for exposing component state via an observable stream.", - "main": "lib/with-observable-state.js", - "directories": { - "test": "test" + "devDependencies": { + "babel-core": "^5.8.24", + "babel-eslint": "^4.1.1", + "babel-loader": "^5.3.2", + "babel-plugin-typecheck": "^1.2.0", + "babel-runtime": "^5.8.20", + "chai": "^3.2.0", + "eslint": "^1.3.1", + "eslint-config-standard": "^4.3.1", + "eslint-config-standard-react": "^1.0.4", + "eslint-plugin-react": "^3.3.1", + "eslint-plugin-standard": "^1.3.0", + "flightjs": "^1.5.1", + "flight-with-state": "^6", + "immutable": "^3.7.5", + "jasmine-core": "^2.3.4", + "jquery": "^2.1.4", + "karma": "^0.13.9", + "karma-chrome-launcher": "^0.2.0", + "karma-cli": "^0.1.0", + "karma-firefox-launcher": "^0.1.6", + "karma-jasmine": "^0.3.6", + "karma-mocha": "^0.2.0", + "karma-sourcemap-loader": "^0.3.5", + "karma-webpack": "^1.7.0", + "mocha": "^2.3.2", + "object-assign": "^4.0.1", + "rx": "^4", + "semantic-release": "^4.3.5", + "webpack": "^1.12.1" + }, + "peerDependencies": { + "flight-with-state": "^6", + "flightjs": "^1.5.1", + "rx": "^4" }, "repository": { "type": "git", diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..81b7f0c --- /dev/null +++ b/src/index.js @@ -0,0 +1,23 @@ +/** + * with-observable-state uses RXJS to implement its observer/observable + * patterns. + * https://github.com/Reactive-Extensions/RxJS + */ +import { compose } from 'flight'; +import withState from 'flight-with-state'; +import Rx from 'rx'; + +export default function withObservableState() { + compose.mixin(this, [withState]); + + this.after('initialize', function () { + // Construct stream with an initial value of the components state. + var stateSubject = new Rx.BehaviorSubject(this.state); + + // Expose only the observable, so nothing else can push values on to the stream. + this.observableState = stateSubject.asObservable(); + + // When component state changes, push values on to the stream. + this.after('stateChanged', stateSubject.onNext.bind(stateSubject)); + }); +} diff --git a/src/specs.context.js b/src/specs.context.js new file mode 100644 index 0000000..0c285e5 --- /dev/null +++ b/src/specs.context.js @@ -0,0 +1,14 @@ +/** + * Since we use webpack-specific features in our modules (e.g., loaders, + * plugins, adding CSS to the dependency graph), we must use webpack to build a + * test bundle. + * + * This module creates a context of all the unit test files (as per the unit + * test naming convention). It's used as the webpack entry file for unit tests. + * + * See: https://github.com/webpack/docs/wiki/context + */ + +const specsContext = require.context('.', true, /.+\.spec\.js$/); +specsContext.keys().forEach(specsContext); +module.exports = specsContext; diff --git a/test/spec/with-observable-state.spec.js b/src/with-observable-state.spec.js similarity index 65% rename from test/spec/with-observable-state.spec.js rename to src/with-observable-state.spec.js index 99ea3c1..16dfa33 100644 --- a/test/spec/with-observable-state.spec.js +++ b/src/with-observable-state.spec.js @@ -1,8 +1,12 @@ -describeMixin('lib/with-observable-state', function () { - 'use strict'; +import { component } from 'flight'; +import withObservableState from '.'; + +describe('withObservableState', function () { + var Component = component(withObservableState, function Base() {}); beforeEach(function () { - this.setupComponent(); + Component.teardownAll(); + this.component = (new Component()).initialize(document.body); }); it('should turn state into observableState', function () { @@ -20,4 +24,4 @@ describeMixin('lib/with-observable-state', function () { active: true }); }); -}); \ No newline at end of file +}); diff --git a/test/test-main.js b/test/test-main.js deleted file mode 100644 index e9c6768..0000000 --- a/test/test-main.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -var tests = Object.keys(window.__karma__.files).filter(function (file) { - return (/\.spec\.js$/.test(file)); -}); - -requirejs.config({ - // Karma serves files from '/base' - baseUrl: '/base', - - paths: { - 'flight': 'bower_components/flight', - 'flight-with-state': 'bower_components/flight-with-state/lib/with-state', - 'rxjs': 'bower_components/rxjs/dist/rx.lite.min' - }, - - // ask Require.js to load these files (all our tests) - deps: tests, - - // start test run, once Require.js is done - callback: window.__karma__.start -});