diff --git a/.gitignore b/.gitignore index 8b6c9ef..ecff5de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.map +build coverage node_modules bower_components diff --git a/.travis.yml b/.travis.yml index 5d47692..171bcfc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,12 @@ sudo: false language: node_js node_js: - - "0.10" + - 0.10 +env: + - TEST=1 + - TYPECHECK=1 + - LINT=1 + script: - - npm run verify + - ./config/travis/test.sh after_success: cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js diff --git a/Gulpfile.js b/Gulpfile.js index ea5c81f..b68784d 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -1,50 +1,46 @@ -var gulp = require('gulp'), - given = require('gulp-if'), - jsx = require('gulp-react'), - reactify = require('reactify'), - buffer = require('vinyl-buffer'), - minifyjs = require('gulp-uglify'), - browserify = require('browserify'), - sourcemaps = require('gulp-sourcemaps'), - sourcestream = require('vinyl-source-stream'); +var gulp = require('gulp'); +var babel = require('gulp-babel'); + +var browserifyCreator = require('./pipeline/browserify'); var args = require('yargs').alias('P', 'production') .alias('D', 'development') - .alias('E', 'example').argv, - production = args.production, - development = args.development, - example = args.example; - -gulp.task('build', function() { - // Build standalone bundle for the browser - var b = browserify({ - entries: './src/react-infinite.jsx', - standalone: 'Infinite' - }) - .transform(reactify, { - es6: true - }) - .exclude('react') - .bundle() - .pipe(sourcestream('react-infinite.' + (production ? 'min.' : '') + 'js')) - .pipe(buffer()) - .pipe(given(development, sourcemaps.init())) - .pipe(given(production, minifyjs())) - .pipe(given(development, sourcemaps.write('.'))) - .pipe(gulp.dest('dist')); + .alias('E', 'example').argv; + +var envObject = { + production: args.production, + development: args.development, + release: !(args.production || args.development) +}; +var INFINITE_SOURCE = './src/react-infinite.jsx'; +gulp.task('build-bundle', browserifyCreator(false, envObject, INFINITE_SOURCE)); +gulp.task('watch-develop-bundle', browserifyCreator(true, {development: true}, INFINITE_SOURCE)); + +// This task builds everything for release: the dist +// folder is populated with react-infinite.js and +// react-infinite.min.js, while the build folder is +// provided with a copy of the source transpiled to ES5. +gulp.task('release', ['build-bundle'], function() { // Transpile CommonJS files to ES5 with React's tools. gulp.src(['./src/**/*.js', './src/**/*.jsx']) - .pipe(jsx({ - harmony: true - })) - .pipe(gulp.dest('build')) - - if (example) { - gulp.src('./examples/*.jsx') - .pipe(jsx()) - .pipe(gulp.dest('examples')) - } + .pipe(babel()) + .pipe(gulp.dest('build')); +}); + +// This task is used to build the examples. It is used +// in the development watch function as well. +gulp.task('examples', function() { + gulp.src('./examples/*.jsx') + .pipe(babel()) + .pipe(gulp.dest('examples')); +}); + +// This task is used for development. When run, it sets up +// a watch on the source files +gulp.task('develop', ['watch-develop-bundle'], function() { + gulp.watch('Gulpfile.js', ['examples', 'build-bundle']); + gulp.watch('./examples/*.jsx', ['examples', 'build-bundle']); }); -gulp.task('default', ['build']); +gulp.task('default', ['release']); diff --git a/README.md b/README.md index 2cc4647..ad79c46 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,11 @@ I am seated in an office, surrounded by heads and bodies. There I've written som Tests are located in the `__tests__` directory2, and can be run with `npm test` after `npm install`. ## Developing -React Infinite is built with Browserify and Gulp. To get started, install the development dependencies with `npm install`. If you do not already have Gulp, you might wish to install it globally with `npm install -g gulp`. Then run `gulp build -D`, which builds the development version. To build the production version, run `gulp build -P`, and to build the non-minified release version, run `gulp`. +React Infinite is built with Browserify and Gulp. To get started, install the development dependencies with `npm install`. If you do not already have Gulp, you might wish to install it globally with `npm install -g gulp`. + +The newly revamped development environment is based on `gulp develop`. When run, this command sets up a watch on the `/src` folder with Browserify as well as the `examples` folder. + +The other major command, `gulp release`, prepares files for release. It produces normal and minified versions of `react-infinite.js` in the `dist` folder and also directly transpiles the source to ES5 in the `build` folder so it can be required by other asset pipelines. ### Infinite Computers diff --git a/build/computers/array_infinite_computer.js b/build/computers/array_infinite_computer.js deleted file mode 100644 index 5e86cb7..0000000 --- a/build/computers/array_infinite_computer.js +++ /dev/null @@ -1,56 +0,0 @@ -/* @flow */ - -var InfiniteComputer = require('./infinite_computer.js'), - bs = require('../utils/binary_index_search.js'); - -for(var InfiniteComputer____Key in InfiniteComputer){if(InfiniteComputer.hasOwnProperty(InfiniteComputer____Key)){ArrayInfiniteComputer[InfiniteComputer____Key]=InfiniteComputer[InfiniteComputer____Key];}}var ____SuperProtoOfInfiniteComputer=InfiniteComputer===null?null:InfiniteComputer.prototype;ArrayInfiniteComputer.prototype=Object.create(____SuperProtoOfInfiniteComputer);ArrayInfiniteComputer.prototype.constructor=ArrayInfiniteComputer;ArrayInfiniteComputer.__superConstructor__=InfiniteComputer; - - function ArrayInfiniteComputer(heightData/* : Array */, numberOfChildren )/* : void */ {"use strict"; - InfiniteComputer.call(this,heightData, numberOfChildren); - this.prefixHeightData = this.heightData.reduce(function(acc, next) { - if (acc.length === 0) { - return [next]; - } else { - acc.push(acc[acc.length - 1] + next); - return acc; - } - }, []); - } - - Object.defineProperty(ArrayInfiniteComputer.prototype,"maybeIndexToIndex",{writable:true,configurable:true,value:function(index )/* : number */ {"use strict"; - if (typeof index === 'undefined' || index === null) { - return this.prefixHeightData.length - 1; - } else { - return index; - } - }}); - - Object.defineProperty(ArrayInfiniteComputer.prototype,"getTotalScrollableHeight",{writable:true,configurable:true,value:function()/* : number */ {"use strict"; - var length = this.prefixHeightData.length; - return length === 0 ? 0 : this.prefixHeightData[length - 1]; - }}); - - Object.defineProperty(ArrayInfiniteComputer.prototype,"getDisplayIndexStart",{writable:true,configurable:true,value:function(windowTop )/* : number */ {"use strict"; - var foundIndex = bs.binaryIndexSearch(this.prefixHeightData, windowTop, bs.opts.CLOSEST_HIGHER); - return this.maybeIndexToIndex(foundIndex); - }}); - - Object.defineProperty(ArrayInfiniteComputer.prototype,"getDisplayIndexEnd",{writable:true,configurable:true,value:function(windowBottom )/* : number */ {"use strict"; - var foundIndex = bs.binaryIndexSearch(this.prefixHeightData, windowBottom, bs.opts.CLOSEST_HIGHER); - return this.maybeIndexToIndex(foundIndex); - }}); - - Object.defineProperty(ArrayInfiniteComputer.prototype,"getTopSpacerHeight",{writable:true,configurable:true,value:function(displayIndexStart )/* : number */ {"use strict"; - var previous = displayIndexStart - 1; - return previous < 0 ? 0 : this.prefixHeightData[previous]; - }}); - - Object.defineProperty(ArrayInfiniteComputer.prototype,"getBottomSpacerHeight",{writable:true,configurable:true,value:function(displayIndexEnd )/* : number */ {"use strict"; - if (displayIndexEnd === -1) { - return 0; - } - return this.getTotalScrollableHeight() - this.prefixHeightData[displayIndexEnd]; - }}); - - -module.exports = ArrayInfiniteComputer; diff --git a/build/computers/constant_infinite_computer.js b/build/computers/constant_infinite_computer.js deleted file mode 100644 index d9cca28..0000000 --- a/build/computers/constant_infinite_computer.js +++ /dev/null @@ -1,32 +0,0 @@ -/* @flow */ - -var InfiniteComputer = require('./infinite_computer.js'); - -for(var InfiniteComputer____Key in InfiniteComputer){if(InfiniteComputer.hasOwnProperty(InfiniteComputer____Key)){ConstantInfiniteComputer[InfiniteComputer____Key]=InfiniteComputer[InfiniteComputer____Key];}}var ____SuperProtoOfInfiniteComputer=InfiniteComputer===null?null:InfiniteComputer.prototype;ConstantInfiniteComputer.prototype=Object.create(____SuperProtoOfInfiniteComputer);ConstantInfiniteComputer.prototype.constructor=ConstantInfiniteComputer;ConstantInfiniteComputer.__superConstructor__=InfiniteComputer;function ConstantInfiniteComputer(){"use strict";if(InfiniteComputer!==null){InfiniteComputer.apply(this,arguments);}} - Object.defineProperty(ConstantInfiniteComputer.prototype,"getTotalScrollableHeight",{writable:true,configurable:true,value:function()/* : number */ {"use strict"; - return this.heightData * this.numberOfChildren; - }}); - - Object.defineProperty(ConstantInfiniteComputer.prototype,"getDisplayIndexStart",{writable:true,configurable:true,value:function(windowTop )/* : number */ {"use strict"; - return Math.floor(windowTop / this.heightData); - }}); - - Object.defineProperty(ConstantInfiniteComputer.prototype,"getDisplayIndexEnd",{writable:true,configurable:true,value:function(windowBottom )/* : number */ {"use strict"; - var nonZeroIndex = Math.ceil(windowBottom / this.heightData); - if (nonZeroIndex > 0) { - return nonZeroIndex - 1; - } - return nonZeroIndex; - }}); - - Object.defineProperty(ConstantInfiniteComputer.prototype,"getTopSpacerHeight",{writable:true,configurable:true,value:function(displayIndexStart )/* : number */ {"use strict"; - return displayIndexStart * this.heightData; - }}); - - Object.defineProperty(ConstantInfiniteComputer.prototype,"getBottomSpacerHeight",{writable:true,configurable:true,value:function(displayIndexEnd )/* : number */ {"use strict"; - var nonZeroIndex = displayIndexEnd + 1; - return Math.max(0, (this.numberOfChildren - nonZeroIndex) * this.heightData); - }}); - - -module.exports = ConstantInfiniteComputer; diff --git a/build/computers/infinite_computer.js b/build/computers/infinite_computer.js deleted file mode 100644 index 230ff77..0000000 --- a/build/computers/infinite_computer.js +++ /dev/null @@ -1,43 +0,0 @@ -// An infinite computer must be able to do the following things: -// 1. getTotalScrollableHeight() -// 2. getDisplayIndexStart() -// 3. getDisplayIndexEnd() - - - function InfiniteComputer(heightData, numberOfChildren) {"use strict"; - this.heightData = heightData; - this.numberOfChildren = numberOfChildren; - } - - Object.defineProperty(InfiniteComputer.prototype,"getTotalScrollableHeight",{writable:true,configurable:true,value:function() {"use strict"; - throw new Error('getTotalScrollableHeight not implemented.'); - }}); - - /* eslint-disable no-unused-vars */ - Object.defineProperty(InfiniteComputer.prototype,"getDisplayIndexStart",{writable:true,configurable:true,value:function(windowTop) {"use strict"; - /* eslint-enable no-unused-vars */ - throw new Error('getDisplayIndexStart not implemented.'); - }}); - - /* eslint-disable no-unused-vars */ - Object.defineProperty(InfiniteComputer.prototype,"getDisplayIndexEnd",{writable:true,configurable:true,value:function(windowBottom) {"use strict"; - /* eslint-enable no-unused-vars */ - throw new Error('getDisplayIndexEnd not implemented.'); - }}); - - // These are helper methods, and can be calculated from - // the above details. - /* eslint-disable no-unused-vars */ - Object.defineProperty(InfiniteComputer.prototype,"getTopSpacerHeight",{writable:true,configurable:true,value:function(displayIndexStart) {"use strict"; - /* eslint-enable no-unused-vars */ - throw new Error('getTopSpacerHeight not implemented.'); - }}); - - /* eslint-disable no-unused-vars */ - Object.defineProperty(InfiniteComputer.prototype,"getBottomSpacerHeight",{writable:true,configurable:true,value:function(displayIndexEnd) {"use strict"; - /* eslint-enable no-unused-vars */ - throw new Error('getBottomSpacerHeight not implemented.'); - }}); - - -module.exports = InfiniteComputer; diff --git a/build/react-infinite.js b/build/react-infinite.js deleted file mode 100644 index 28df716..0000000 --- a/build/react-infinite.js +++ /dev/null @@ -1,296 +0,0 @@ -var React = global.React || require('react'), - _isArray = require('lodash.isarray'), - _isFinite = require('lodash.isfinite'), - ConstantInfiniteComputer = require('./computers/constant_infinite_computer.js'), - ArrayInfiniteComputer = require('./computers/array_infinite_computer.js'); -var _assign = require('object-assign'); - -var Infinite = React.createClass({displayName: "Infinite", - - propTypes: { - handleScroll: React.PropTypes.func, - - // preloadBatchSize causes updates only to - // happen each preloadBatchSize pixels of scrolling. - // Set a larger number to cause fewer updates to the - // element list. - preloadBatchSize: React.PropTypes.number, - // preloadAdditionalHeight determines how much of the - // list above and below the container is preloaded even - // when it is not currently visible to the user. In the - // regular scroll implementation, preloadAdditionalHeight - // is equal to the entire height of the list. - preloadAdditionalHeight: React.PropTypes.number, // page to screen ratio - - // The provided elementHeight can be either - // 1. a constant: all elements are the same height - // 2. an array containing the height of each element - elementHeight: React.PropTypes.oneOfType([ - React.PropTypes.number, - React.PropTypes.arrayOf(React.PropTypes.number) - ]).isRequired, - // This is the total height of the visible window. - containerHeight: React.PropTypes.number.isRequired, - - infiniteLoadBeginBottomOffset: React.PropTypes.number, - onInfiniteLoad: React.PropTypes.func, - loadingSpinnerDelegate: React.PropTypes.node, - - isInfiniteLoading: React.PropTypes.bool, - timeScrollStateLastsForAfterUserScrolls: React.PropTypes.number, - useWindowAsScrollContainer: React.PropTypes.bool, - - className: React.PropTypes.string - }, - - getDefaultProps:function() { - return { - handleScroll: function() {}, - loadingSpinnerDelegate: React.createElement("div", null), - onInfiniteLoad: function() {}, - isInfiniteLoading: false, - timeScrollStateLastsForAfterUserScrolls: 150, - useWindowAsScrollContainer: false, - className: '' - }; - }, - - // automatic adjust to scroll direction - // give spinner a ReactCSSTransitionGroup - getInitialState:function() { - this.computedProps = this.generateComputedProps(this.props); - this.utils = this.generateComputedUtilityFunctions(this.props); - - var computer = this.createInfiniteComputer(this.computedProps.elementHeight, this.computedProps.children); - - var preloadBatchSize = this.computedProps.preloadBatchSize; - var preloadAdditionalHeight = this.computedProps.preloadAdditionalHeight; - - return { - infiniteComputer: computer, - - numberOfChildren: React.Children.count(this.computedProps.children), - displayIndexStart: 0, - displayIndexEnd: computer.getDisplayIndexEnd( - preloadBatchSize + preloadAdditionalHeight - ), - - isInfiniteLoading: false, - - preloadBatchSize: preloadBatchSize, - preloadAdditionalHeight: preloadAdditionalHeight, - - scrollTimeout: undefined, - isScrolling: false - }; - }, - - createInfiniteComputer:function(data, children) { - var computer; - var numberOfChildren = React.Children.count(children); - - if (_isFinite(data)) { - computer = new ConstantInfiniteComputer(data, numberOfChildren); - } else if (_isArray(data)) { - computer = new ArrayInfiniteComputer(data, numberOfChildren); - } else { - throw new Error('You must provide either a number or an array of numbers as the elementHeight prop.'); - } - - return computer; - }, - - generateComputedProps:function(props) { - var computedProps = _assign({}, props); - computedProps.containerHeight = props.useWindowAsScrollContainer - ? window.innerHeight : props.containerHeight; - computedProps.preloadBatchSize = typeof props.preloadBatchSize === 'number' - ? props.preloadBatchSize : computedProps.containerHeight / 2; - computedProps.preloadAdditionalHeight = typeof props.preloadAdditionalHeight === 'number' - ? props.preloadAdditionalHeight : computedProps.containerHeight; - return computedProps; - }, - - generateComputedUtilityFunctions:function(props) { - var utilities = {}; - if (props.useWindowAsScrollContainer) { - utilities.subscribeToScrollListener = function() { - window.onscroll = this.infiniteHandleScroll; - }.bind(this); - utilities.nodeScrollListener = function() {}; - utilities.getScrollTop = function() {return window.scrollY;}; - utilities.scrollShouldBeIgnored = function() {return false;}; - utilities.buildScrollableStyle = function() {return {};}; - } else { - utilities.subscribeToScrollListener = function() {}; - utilities.nodeScrollListener = this.infiniteHandleScroll; - utilities.getScrollTop = function() {return React.findDOMNode(this.refs.scrollable).scrollTop;}.bind(this); - utilities.scrollShouldBeIgnored = function(event) {return event.target !== React.findDOMNode(this.refs.scrollable);}.bind(this); - utilities.buildScrollableStyle = function() { - return { - height: this.computedProps.containerHeight, - overflowX: 'hidden', - overflowY: 'scroll' - }; - }.bind(this); - } - return utilities; - }, - - componentWillReceiveProps:function(nextProps) { - this.computedProps = this.generateComputedProps(nextProps); - this.utils = this.generateComputedUtilityFunctions(nextProps); - - var newStateObject = {}; - - // TODO: more efficient elementHeight change detection - newStateObject.infiniteComputer = this.createInfiniteComputer( - this.computedProps.elementHeight, - this.computedProps.children - ); - - if (this.computedProps.isInfiniteLoading !== undefined) { - newStateObject.isInfiniteLoading = this.computedProps.isInfiniteLoading; - } - - newStateObject.preloadBatchSize = this.computedProps.preloadBatchSize; - newStateObject.preloadAdditionalHeight = this.computedProps.preloadAdditionalHeight; - - this.setState(newStateObject, function() { - this.setStateFromScrollTop(this.utils.getScrollTop()); - }.bind(this)); - }, - - componentDidUpdate:function(prevProps) { - if (React.Children.count(this.computedProps.children) !== React.Children.count(prevProps.children)) { - this.setStateFromScrollTop(this.utils.getScrollTop()); - } - }, - - componentWillMount:function() { - if (_isArray(this.computedProps.elementHeight)) { - if (React.Children.count(this.computedProps.children) !== this.computedProps.elementHeight.length) { - throw new Error('There must be as many values provided in the elementHeight prop as there are children.'); - } - } - }, - - componentDidMount:function() { - this.utils.subscribeToScrollListener(); - }, - - // Given the scrollTop of the container, computes the state the - // component should be in. The goal is to abstract all of this - // from any actual representation in the DOM. - // The window is the block with any preloadAdditionalHeight - // added to it. - setStateFromScrollTop:function(scrollTop) { - var blockNumber = this.state.preloadBatchSize === 0 ? 0 : Math.floor(scrollTop / this.state.preloadBatchSize), - blockStart = this.state.preloadBatchSize * blockNumber, - blockEnd = blockStart + this.state.preloadBatchSize, - apertureTop = Math.max(0, blockStart - this.state.preloadAdditionalHeight), - apertureBottom = Math.min(this.state.infiniteComputer.getTotalScrollableHeight(), - blockEnd + this.state.preloadAdditionalHeight); - - this.setState({ - displayIndexStart: this.state.infiniteComputer.getDisplayIndexStart(apertureTop), - displayIndexEnd: this.state.infiniteComputer.getDisplayIndexEnd(apertureBottom) - }); - }, - - infiniteHandleScroll:function(e) { - if (this.utils.scrollShouldBeIgnored(e)) { - return; - } - this.computedProps.handleScroll(React.findDOMNode(this.refs.scrollable)); - this.handleScroll(this.utils.getScrollTop()); - }, - - manageScrollTimeouts:function() { - // Maintains a series of timeouts to set this.state.isScrolling - // to be true when the element is scrolling. - - if (this.state.scrollTimeout) { - clearTimeout(this.state.scrollTimeout); - } - - var that = this, - scrollTimeout = setTimeout(function() { - that.setState({ - isScrolling: false, - scrollTimeout: undefined - }); - }, this.computedProps.timeScrollStateLastsForAfterUserScrolls); - - this.setState({ - isScrolling: true, - scrollTimeout: scrollTimeout - }); - }, - - handleScroll:function(scrollTop) { - this.manageScrollTimeouts(); - this.setStateFromScrollTop(scrollTop); - - var infiniteScrollBottomLimit = scrollTop > - (this.state.infiniteComputer.getTotalScrollableHeight() - - this.computedProps.containerHeight - - this.computedProps.infiniteLoadBeginBottomOffset); - if (infiniteScrollBottomLimit && !this.state.isInfiniteLoading) { - this.setState({ - isInfiniteLoading: true - }); - this.computedProps.onInfiniteLoad(); - } - }, - - // Helpers for React styles. - buildScrollableStyle:function() { - return { - height: this.computedProps.containerHeight, - overflowX: 'hidden', - overflowY: 'scroll' - }; - }, - - buildHeightStyle:function(height) { - return { - width: '100%', - height: Math.ceil(height) - }; - }, - - render:function() { - var displayables = this.computedProps.children.slice(this.state.displayIndexStart, - this.state.displayIndexEnd + 1); - - var infiniteScrollStyles = {}; - if (this.state.isScrolling) { - infiniteScrollStyles.pointerEvents = 'none'; - } - - var topSpacerHeight = this.state.infiniteComputer.getTopSpacerHeight(this.state.displayIndexStart), - bottomSpacerHeight = this.state.infiniteComputer.getBottomSpacerHeight(this.state.displayIndexEnd); - - // topSpacer and bottomSpacer take up the amount of space that the - // rendered elements would have taken up otherwise - return React.createElement("div", {className: this.computedProps.className, - ref: "scrollable", - style: this.utils.buildScrollableStyle(), - onScroll: this.utils.nodeScrollListener}, - React.createElement("div", {ref: "smoothScrollingWrapper", style: infiniteScrollStyles}, - React.createElement("div", {ref: "topSpacer", - style: this.buildHeightStyle(topSpacerHeight)}), - displayables, - React.createElement("div", {ref: "bottomSpacer", - style: this.buildHeightStyle(bottomSpacerHeight)}), - React.createElement("div", {ref: "loadingSpinner"}, - this.state.isInfiniteLoading ? this.computedProps.loadingSpinnerDelegate : null - ) - ) - ); - } -}); - -module.exports = Infinite; -global.Infinite = Infinite; diff --git a/build/utils/binary_index_search.js b/build/utils/binary_index_search.js deleted file mode 100644 index 1f6b0a8..0000000 --- a/build/utils/binary_index_search.js +++ /dev/null @@ -1,43 +0,0 @@ -/* @flow */ - -var opts = { - CLOSEST_LOWER: 1, - CLOSEST_HIGHER: 2 -}; - -var binaryIndexSearch = function(array/* : Array */, - item/* : number */, - opt/* : number */)/* : ?number */ { - var index; - - var high = array.length - 1, - low = 0, - middle, - middleItem; - - while (low <= high) { - middle = low + Math.floor((high - low) / 2); - middleItem = array[middle]; - - if (middleItem === item) { - return middle; - } else if (middleItem < item) { - low = middle + 1; - } else if (middleItem > item) { - high = middle - 1; - } - } - - if (opt === opts.CLOSEST_LOWER && low > 0) { - index = low - 1; - } else if (opt === opts.CLOSEST_HIGHER && high < array.length - 1) { - index = high + 1; - } - - return index; -}; - -module.exports = { - binaryIndexSearch: binaryIndexSearch, - opts: opts -}; diff --git a/config/travis/test.sh b/config/travis/test.sh new file mode 100755 index 0000000..393930e --- /dev/null +++ b/config/travis/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +if [ "$TEST" = 1 ]; then + npm run test +fi; + +if [ "$TYPECHECK" = 1 ]; then + npm run typecheck +fi; + +if [ "$LINT" = 1 ]; then + npm run lint +fi; diff --git a/examples/borderless-window.html b/examples/borderless-window.html index 6f5e0e8..2d4d0cb 100644 --- a/examples/borderless-window.html +++ b/examples/borderless-window.html @@ -26,7 +26,7 @@
- + diff --git a/examples/example.html b/examples/example.html index 5e0f5ec..a51cfb5 100644 --- a/examples/example.html +++ b/examples/example.html @@ -33,7 +33,7 @@
- + diff --git a/examples/window.html b/examples/window.html index 08d0bdf..af3cb5c 100644 --- a/examples/window.html +++ b/examples/window.html @@ -26,7 +26,7 @@
- + diff --git a/package.json b/package.json index 09701ed..90145ee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-infinite", - "version": "0.5.1", + "version": "0.5.2", "description": "A browser-ready efficient scrolling container based on UITableView", "main": "build/react-infinite.js", "repository": { @@ -21,13 +21,12 @@ }, "browser": "build/react-infinite.js", "scripts": { - "test": "node ./node_modules/.bin/jest --verbose", + "test": "node ./node_modules/.bin/jest", "lint": "./node_modules/.bin/eslint ./src --ext .jsx --ext .js", "typecheck": "./node_modules/.bin/flow check", - "verify": "npm run lint && npm run typecheck && npm test", - "watch-test": "watch \"npm test\" ./__tests__ ./src", - "watch-build": "watch \"gulp build -E\" ./examples ./src", - "build": "gulp build && gulp build -E" + "verify": "npm test && npm run typecheck && npm run lint", + "preversion": "npm run verify", + "prepublish": "gulp release" }, "jest": { "scriptPreprocessor": "/node_modules/babel-jest", @@ -42,12 +41,14 @@ "unmockedModulePathPatterns": [ "/node_modules/react" ], - "collectCoverage": true + "collectCoverage": true, + "verbose": true }, "homepage": "https://github.com/seatgeek/react-infinite", "devDependencies": { "babel-eslint": "^4.0.7", "babel-jest": "^5.3.0", + "babelify": "^6.3.0", "browserify": "^9.0.3", "coveralls": "^2.11.2", "eslint": "~1.6.0", @@ -57,18 +58,18 @@ "eslint-plugin-standard": "^1.2.0", "flow-bin": "^0.17.0", "gulp": "^3.8.8", + "gulp-babel": "^5.2.1", "gulp-concat": "^2.4.3", - "gulp-if": "^1.2.5", - "gulp-react": "3.0.0", - "gulp-rename": "^1.2.0", "gulp-sourcemaps": "^1.2.4", "gulp-uglify": "^1.0.1", "jest-cli": "0.4.19", + "moment": "^2.10.6", "react-tools": "*", - "reactify": "^1.0.0", + "uglifyify": "^3.0.1", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.0.0", "watch": "^0.16.0", + "watchify": "^3.4.0", "yargs": "^1.3.2" }, "dependencies": { diff --git a/pipeline/browserify.js b/pipeline/browserify.js new file mode 100644 index 0000000..5ed6336 --- /dev/null +++ b/pipeline/browserify.js @@ -0,0 +1,58 @@ +var watchify = require('watchify'); +var browserify = require('browserify'); +var babelify = require('babelify'); +var moment = require('moment'); +var buffer = require('vinyl-buffer'); +var sourcestream = require('vinyl-source-stream'); +var sourcemaps = require('gulp-sourcemaps'); +var uglify = require('gulp-uglify'); +var gulp = require('gulp'); + +function transformBundle(root, envObject) { + root = root.bundle(); + + if (envObject.development) { + root.pipe(sourcestream('react-infinite.js')) + .pipe(buffer()) + .pipe(sourcemaps.init()) + .pipe(sourcemaps.write('.')) + .pipe(gulp.dest('dist')); + } + if (envObject.production || envObject.release) { + root.pipe(sourcestream('react-infinite.min.js')) + .pipe(buffer()) + .pipe(uglify()) + .pipe(gulp.dest('dist')); + } + if (envObject.release) { + root.pipe(sourcestream('react-infinite.js')) + .pipe(buffer()) + .pipe(gulp.dest('dist')); + } +} + +module.exports = function(shouldWatch, envObject, files) { + var watchFunction = shouldWatch ? watchify : function(x) { return x; }; + var watchArgs = shouldWatch ? watchify.args : undefined; + + return function() { + var root = watchFunction(browserify({ + entries: files, + standalone: 'Infinite' + }, watchArgs)) + .transform(babelify) + .exclude('react'); + + if (shouldWatch) { + root.on('update', function() { + transformBundle(root, envObject); + }); + + root.on('log', function() { + console.log('[' + moment().format() + '] Browserify bundle refreshed'); + }); + } + + transformBundle(root, envObject); + }; +};