diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..eb1e6c8 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,69 @@ +{ + "rules": { + "no-console": 0, + "no-unused-vars": 0, + "indent": [ + 2, + 4 + ], + "semi": [ + 2, + "always" + ] + }, + "env": { + "node": true, + "browser": true, + "jquery": true, + "amd": true + }, + "globals": { + "Matter": false, + "window": true, + "document": false, + "Element": false, + "MatterTools": false, + "phantom": false, + "process": false, + "HTMLElement": false, + "require": false, + "PIXI": false, + "$": false, + "Example": false, + "Image": false, + "navigator": false, + "setTimeout": false, + "decomp": false, + "module": false, + "Body": false, + "Composite": false, + "World": false, + "Contact": false, + "Detector": false, + "Grid": false, + "Pairs": false, + "Pair": false, + "Resolver": false, + "SAT": false, + "Constraint": false, + "MouseConstraint": false, + "Common": false, + "Engine": false, + "Mouse": false, + "Sleeping": false, + "Bodies": false, + "Composites": false, + "Axes": false, + "Bounds": false, + "Vector": false, + "Vertices": false, + "Render": false, + "RenderPixi": false, + "Events": false, + "Query": false, + "Runner": false, + "Svg": false, + "Metrics": false + }, + "extends": "eslint:recommended" +} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index b3fcc9d..0000000 --- a/.jshintrc +++ /dev/null @@ -1,45 +0,0 @@ -{ - // settings - "passfail" : false, // Stop on first error. - "maxerr" : 100, // Maximum error before stopping. - - // dev - "debug" : false, // Allow debugger statements e.g. browser breakpoints. - "devel" : true, // Allow developments statements e.g. `console.log();`. - - // ECMAScript 5 - "es5" : false, // Allow ECMAScript 5 syntax. - "strict" : false, // Require `use strict` pragma in every file. - "globalstrict" : false, // Allow global "use strict" (also enables 'strict'). - - // options - "laxbreak" : true, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. - "loopfunc" : true, // Allow functions in loops (e.g. for async closures) - - // style - "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`. - "noempty" : true, // Prohibit use of empty blocks. - "nonew" : true, // Prohibit use of constructors for side-effects. - "onevar" : false, // Allow only one `var` statement per function. - "plusplus" : false, // Prohibit use of `++` & `--`. - "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. - "trailing" : false, // Prohibit trailing whitespaces. - "white" : false, // Check against strict whitespace and indentation rules. - "indent" : 4, // Specify indentation spacing - - // variables - "undef": true, - // "unused": true, - "-W079": true, // Silence redefinition errors (they are false positives). - "-W020": true, // Silence readonly error (needed to simplify support for node). - "predef": [ - "Matter", "window", "document", "Element", "MatterTools", - "phantom", "process", "HTMLElement", "require", "PIXI", "$", - "Example", "Image", "navigator", "setTimeout", "decomp", "module", - "Body", "Composite", "World", "Contact", "Detector", "Grid", - "Pairs", "Pair", "Resolver", "SAT", "Constraint", "MouseConstraint", - "Common", "Engine", "Mouse", "Sleeping", "Bodies", "Composites", - "Axes", "Bounds", "Vector", "Vertices", "Render", "RenderPixi", - "Events", "Query", "Runner", "Svg", "Metrics" - ] -} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index b8819c4..81402da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: false node_js: - "0.12" before_install: - - npm install -g grunt-cli + - npm install -g gulp - mkdir travis-phantomjs - wget https://s3.amazonaws.com/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 -O $PWD/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 - tar -xvf $PWD/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 -C $PWD/travis-phantomjs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f2ad92f..71346f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ To build you must first install [node.js](http://nodejs.org/) and [grunt](http:/ This will install the required build dependencies, then run - grunt dev + gulp dev which is a task that builds the `matter-dev.js` file, spawns a `connect` and `watch` server, then opens `demo/dev.html` in your browser. Any changes you make to the source will automatically rebuild `matter-dev.js` and reload your browser for quick and easy testing. diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 4d6c5f2..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,226 +0,0 @@ -module.exports = function(grunt) { - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - buildName: 'matter', - buildVersion: 'edge-master', - docVersion: 'v<%= pkg.version %>', - browserify: { - options: { - banner: '/**\n* <%= buildName %>.js <%= buildVersion %> <%= grunt.template.today("yyyy-mm-dd") %>\n* <%= pkg.homepage %>\n* License: <%= pkg.license %>\n*/\n\n' + grunt.file.read('src/module/license.js') + '\n\n', - browserifyOptions: { - standalone: 'Matter' - } - }, - 'build/<%= buildName %>.js': ['src/module/main.js'] - }, - uglify: { - min: { - options: { - banner: '/**\n* <%= buildName %>.min.js <%= buildVersion %> <%= grunt.template.today("yyyy-mm-dd") %>\n* <%= pkg.homepage %>\n* License: <%= pkg.license %>\n*/\n\n' - }, - src: 'build/<%= buildName %>.js', - dest: 'build/<%= buildName %>.min.js' - }, - dev: { - options: { - mangle: false, - compress: false, - preserveComments: false, - beautify: { - width: 32000, - indent_level: 2, - space_colon: false, - beautify: true - }, - banner: '/**\n* <%= buildName %>.min.js <%= buildVersion %> <%= grunt.template.today("yyyy-mm-dd") %>\n* <%= pkg.homepage %>\n* License: <%= pkg.license %>\n*/\n\n' - }, - src: 'build/<%= buildName %>.js', - dest: 'build/<%= buildName %>.js' - } - }, - concat: { - examples: { - src: 'examples/**/*.js', - dest: 'demo/js/Examples.js' - } - }, - copy: { - demo: { - src: 'build/<%= buildName %>.js', - dest: 'demo/js/lib/<%= buildName %>.js' - } - }, - jshint: { - options: { - jshintrc: '.jshintrc' - }, - all: ['src/**/*.js', 'demo/js/*.js', 'examples/*.js', 'test/browser/TestDemo.js', 'test/node/TestDemo.js', '!src/module/*', '!demo/js/Examples.js'] - }, - connect: { - watch: { - options: { - port: 9000, - open: 'http://localhost:9000/demo', - livereload: 9001 - } - }, - serve: { - options: { - port: 8000 - } - } - }, - watch: { - options: { - livereload: { - port: 9001 - } - }, - src: { - files: ['src/**/*.js'], - tasks: ['build:dev'] - }, - demo: { - files: ['build/matter.js', 'demo/js/**/*.html', 'demo/js/**/*.js', 'demo/css/**/*.css'] - }, - examples: { - files: ['examples/**/*.js'], - tasks: ['concat:examples'] - } - }, - yuidoc: { - compile: { - name: '<%= pkg.name %>.js Physics Engine API Documentation for <%= docVersion %>', - description: '<%= pkg.description %>', - version: '<%= docVersion %>', - url: '<%= pkg.homepage %>', - options: { - paths: 'src', - themedir: 'matter-doc-theme', - outdir: 'doc', - linkNatives: true - } - } - }, - preprocess: { - options: { - inline: true, - context : { - DEBUG: false - } - }, - js: { - src: 'build/<%= buildName %>.js', - dest: 'build/<%= buildName %>.js' - } - }, - shell: { - testDemoBrowser: { - command: function(arg) { - arg = arg ? ' --' + arg : ''; - return 'phantomjs test/browser/TestDemo.js' + arg; - }, - options: { - execOptions: { - timeout: 1000 * 60 - } - } - }, - testDemoNode: { - command: function(arg) { - arg = arg ? ' --' + arg : ''; - return 'node test/node/TestDemo.js' + arg; - }, - options: { - execOptions: { - timeout: 1000 * 60 - } - } - } - } - }); - - grunt.loadNpmTasks('grunt-browserify'); - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-connect'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-contrib-copy'); - grunt.loadNpmTasks('grunt-contrib-yuidoc'); - grunt.loadNpmTasks('grunt-preprocess'); - grunt.loadNpmTasks('grunt-shell'); - - grunt.registerTask('default', ['concat:examples', 'test', 'build']); - grunt.registerTask('test', ['concat:examples', 'build:dev', 'connect:serve', 'jshint', 'test:demo', 'test:demoNode']); - grunt.registerTask('dev', ['concat:examples', 'build:dev', 'connect:watch', 'watch']); - - grunt.registerTask('test:demo', function() { - var updateAll = grunt.option('updateAll'), - diff = grunt.option('diff'); - - if (updateAll) { - grunt.task.run('shell:testDemoBrowser:updateAll'); - } else if (diff) { - grunt.task.run('shell:testDemoBrowser:diff'); - } else { - grunt.task.run('shell:testDemoBrowser'); - } - }); - - grunt.registerTask('test:demoNode', function() { - var updateAll = grunt.option('updateAll'), - diff = grunt.option('diff'); - - if (updateAll) { - grunt.task.run('shell:testDemoNode:updateAll'); - } else if (diff) { - grunt.task.run('shell:testDemoNode:diff'); - } else { - grunt.task.run('shell:testDemoNode'); - } - }); - - grunt.registerTask('build', function(mode) { - var isDev = (mode === 'dev'), - isRelease = (mode === 'release'), - isEdge = (mode === 'edge'), - pkg = grunt.file.readJSON('package.json'), - uglifyTask; - - // development build mode - if (isDev) { - grunt.config.set('buildName', 'matter-dev'); - grunt.config.set('buildVersion', pkg.version + '-dev'); - grunt.task.run('browserify', 'uglify:dev', 'uglify:min', 'copy'); - } - - // release build mode - if (isRelease) { - grunt.config.set('buildName', 'matter-' + pkg.version); - grunt.config.set('buildVersion', pkg.version + '-alpha'); - grunt.task.run('browserify', 'uglify:min', 'copy'); - } - - // edge build mode (default) - if (isEdge || (!isDev && !isRelease)) { - grunt.config.set('buildVersion', 'edge-master'); - grunt.task.run('browserify', 'preprocess', 'uglify:min'); - } - }); - - grunt.registerTask('doc', function(mode) { - var isDev = (mode === 'dev'), - isRelease = (mode === 'release'), - isEdge = (mode === 'edge'); - - if (isEdge) - grunt.config.set('docVersion', 'edge version (master)'); - - grunt.task.run('yuidoc'); - }); - - grunt.registerTask('set_config', 'Set a config property.', function(name, val) { - grunt.config.set(name, val); - }); -}; diff --git a/Gulpfile.js b/Gulpfile.js new file mode 100644 index 0000000..177dbcd --- /dev/null +++ b/Gulpfile.js @@ -0,0 +1,236 @@ +var gulp = require('gulp'); +var uglify = require('gulp-uglify'); +var rename = require('gulp-rename'); +var header = require('gulp-header'); +var eslint = require('gulp-eslint'); +var bump = require('gulp-bump'); +var changelog = require('gulp-conventional-changelog'); +var tag = require('gulp-tag-version'); +var sequence = require('run-sequence'); +var gutil = require('gulp-util'); +var replace = require('gulp-replace'); +var webserver = require('gulp-webserver'); +var concat = require('gulp-concat'); +var preprocess = require('gulp-preprocess'); +var browserify = require('browserify'); +var transform = require('vinyl-transform'); +var through2 = require('through2'); +var pkg = require('./package.json'); +var clone = require('gulp-clone'); +var livereload = require('connect-livereload'); +var es = require('event-stream'); +var path = require('path'); +var fs = require('fs'); +var watchify = require('watchify'); +var extend = require('util')._extend; +var exec = require('child_process').exec; +var server; + +gulp.task('default', ['build:dev']); + +gulp.task('dev', ['watch', 'serve']); + +gulp.task('release', function(callback) { + sequence('build:dev', 'test', 'build:release', 'bump', 'doc', 'changelog', 'tag', callback); +}); + +gulp.task('build:dev', function() { + return build(extend(extend({}, pkg), { version: 'dev' })); +}); + +gulp.task('build:edge', function() { + return build(extend(extend({}, pkg), { version: 'master' })); +}); + +gulp.task('build:release', function() { + return build(extend(extend({}, pkg), { version: pkg.version })); +}); + +gulp.task('build:examples', function() { + return gulp.src('examples/**/*.js') + .pipe(concat('Examples.js')) + .pipe(gulp.dest('demo/js')); +}); + +gulp.task('watch', function() { + var b = browserify({ + entries: ['src/module/main.js'], + standalone: 'Matter', + plugin: [watchify] + }); + + var bundle = function() { + gutil.log('Updated bundle build/matter-dev.js'); + b.bundle().pipe(fs.createWriteStream('build/matter-dev.js')); + }; + + b.on('update', bundle); + bundle(); + + gulp.watch('examples/**/*.js', ['build:examples']); +}); + +gulp.task('bump', function() { + return gulp.src(['package.json', 'bower.json']) + .pipe(bump({ type: process.argv[4] || 'minor' })) + .pipe(gulp.dest('.')); +}); + +gulp.task('tag', function() { + return gulp.src('package.json') + .pipe(tag({ prefix: '' })); +}); + +gulp.task('changelog', function () { + return gulp.src('CHANGELOG.md') + .pipe(changelog()) + .pipe(gulp.dest('.')); +}); + +gulp.task('serve', function() { + serve(false); +}); + +gulp.task('serve:test', function() { + serve(true); +}); + +gulp.task('serve:stop', function() { + if (server) { + try { + server.emit('kill'); + } catch (e) {} // eslint-disable-line no-empty + gutil.log('Web server stopped'); + } +}); + +gulp.task('test', function(callback) { + sequence('serve:test', 'lint', 'test:browser', 'test:node', 'serve:stop', callback); +}); + +gulp.task('test:browser', function(callback) { + shell('phantomjs test/browser/TestDemo.js', callback); +}); + +gulp.task('test:node', function(callback) { + shell('node test/node/TestDemo.js', callback); +}); + +gulp.task('lint', function() { + return gulp.src([ + 'src/**/*.js', + 'demo/js/*.js', + 'examples/*.js', + 'test/browser/TestDemo.js', + 'test/node/TestDemo.js', + 'Gulpfile.js' + ]) + .pipe(eslint()) + .pipe(eslint.format()) + .pipe(eslint.failAfterError()); +}); + +gulp.task('doc', function(callback) { + var options = { + paths: ['src'], + themedir: 'matter-doc-theme', + outdir: 'doc/build', + linkNatives: true, + project: { + name: pkg.name + '.js Physics Engine API Documentation for ' + pkg.version, + description: pkg.description, + version: pkg.version, + url: pkg.homepage + } + }; + + var Y = require('yuidocjs'); + var json = new Y.YUIDoc(options).run(); + json.project = options.project; + + var builder = new Y.DocBuilder(options, json); + builder.compile(callback); +}); + +var serve = function(isTest) { + process.on('uncaughtException', function(err) { + if (err.errno === 'EADDRINUSE') { + gutil.log('Server already running (or port is otherwise in use)'); + } + }); + + server = gulp.src('.') + .pipe(webserver({ + host: '0.0.0.0', + livereload: { + enable: !isTest, + filter: function(filename) { + return filename.match(/build|demo/); + }, + }, + middleware: livereload(), + open: isTest ? false : 'http://localhost:8000/demo/index.html', + directoryListing: true + })); +}; + +var build = function(options) { + var filename = 'build/matter' + (options.version === 'master' ? '' : '-' + options.version), + dest = filename + '.js', + destMin = filename + '.min.js'; + + options.date = options.date || new Date().toISOString().slice(0, 10); + options.author = '@liabru'; + + gutil.log('Building', filename, options.date); + + var compiled = gulp.src(['src/module/main.js']) + .pipe(replace("version = 'master'", "version = '" + options.version + "'")) + .pipe(through2.obj(function(file, enc, next){ + browserify(file.path, { standalone: 'Matter' }) + .bundle(function(err, res){ + file.contents = res; + next(null, file); + }); + })) + .pipe(preprocess({ context: { DEBUG: false } })); + + var build = compiled.pipe(clone()) + .pipe(header(banner + '\n' + license + '\n\n', { context: options })) + .pipe(rename(dest)) + .pipe(gulp.dest('.')); + + var buildMin = compiled.pipe(clone()) + .pipe(uglify({ output: { max_line_len: 1000 } })) + .pipe(header(banner, { context: options })) + .pipe(rename(destMin)) + .pipe(gulp.dest('.')); + + return es.merge(build, buildMin); +}; + +var shell = function(command, callback) { + var args = process.argv.slice(3).join(' '), + proc = exec(command + args, function(err) { + callback(err); + }); + + proc.stdout.on('data', function(data) { + process.stdout.write(data); + }); + + proc.stderr.on('data', function(data) { + process.stderr.write(data); + }); +}; + +var license = fs.readFileSync('src/module/license.js'); + +var banner = [ + '/**', + '* <%= context.name %> <%= context.version %> by <%= context.author %> <%= context.date %>', + '* <%= context.homepage %>', + '* License <%= context.license %>', + '*/', + '' +].join('\n'); diff --git a/README.md b/README.md index 213b325..60e4b9f 100644 --- a/README.md +++ b/README.md @@ -113,17 +113,17 @@ The library is reasonably stable as-is, but it is not yet feature complete. ### Building and Contributing -To build you must first install [node.js](http://nodejs.org/) and [grunt](http://gruntjs.com/), then run +To build you must first install [node.js](http://nodejs.org/) and [gulp](http://gulpjs.com/), then run npm install This will install the required build dependencies, then run - grunt dev + gulp dev which is a task that builds the `matter-dev.js` file, spawns a `connect` and `watch` server, then opens `demo/dev.html` in your browser. Any changes you make to the source will automatically rebuild `matter-dev.js` and reload your browser for quick and easy testing. -Contributions are welcome, please ensure they follow the same style and architecture as the rest of the code. You should run `grunt test` to ensure `jshint` gives no errors. Please do not include any changes to the files in the `build` directory. +Contributions are welcome, please ensure they follow the same style and architecture as the rest of the code. You should run `gulp test` to ensure `jshint` gives no errors. Please do not include any changes to the files in the `build` directory. If you'd like to contribute but not sure what to work on, feel free to message me. Thanks! diff --git a/examples/compoundStack.js b/examples/compoundStack.js index edc126f..991a918 100644 --- a/examples/compoundStack.js +++ b/examples/compoundStack.js @@ -7,8 +7,8 @@ Example.compoundStack = function(demo) { var engine = demo.engine, - world = engine.world; - var size = 50; + world = engine.world, + size = 50; var stack = Composites.stack(100, 220, 12, 6, 0, 0, function(x, y) { var partA = Bodies.rectangle(x, y, size, size / 5), diff --git a/package.json b/package.json index 92e349f..af441bb 100644 --- a/package.json +++ b/package.json @@ -20,25 +20,37 @@ "rigid body physics" ], "devDependencies": { + "browserify": "^12.0.1", "cheerio": "^0.19.0", + "connect-livereload": "^0.5.4", + "event-stream": "^3.3.2", "fast-json-patch": "^0.5.4", - "grunt": "~0.4.2", - "grunt-browserify": "~3.7.0", - "grunt-contrib-concat": "^0.5.1", - "grunt-contrib-connect": "~0.6.0", - "grunt-contrib-copy": "~0.5.0", - "grunt-contrib-jshint": "~0.6.3", - "grunt-contrib-uglify": "~0.2.7", - "grunt-contrib-watch": "~0.5.3", - "grunt-contrib-yuidoc": "~0.5.1", - "grunt-preprocess": "^4.1.0", - "grunt-shell": "^1.1.2", + "gulp": "^3.9.0", + "gulp-bump": "^1.0.0", + "gulp-clone": "^1.0.0", + "gulp-concat": "^2.6.0", + "gulp-conventional-changelog": "^0.7.0", + "gulp-eslint": "^1.0.0", + "gulp-header": "^1.7.1", + "gulp-preprocess": "^2.0.0", + "gulp-rename": "^1.2.2", + "gulp-replace": "^0.5.4", + "gulp-tag-version": "^1.3.0", + "gulp-uglify": "^1.4.2", + "gulp-util": "^3.0.7", + "gulp-webdriver": "^1.0.1", + "gulp-webserver": "^0.9.1", "mkdirp": "^0.5.1", - "rimraf": "^2.4.2" + "rimraf": "^2.4.2", + "run-sequence": "^1.1.4", + "through2": "^2.0.0", + "vinyl-transform": "^1.0.0", + "watchify": "^3.6.1", + "yuidocjs": "^0.9.0" }, "scripts": { - "dev": "npm install && grunt dev", - "test": "grunt test" + "dev": "npm install && gulp", + "test": "gulp test" }, "dependencies": {} }