├── .eslintrc ├── .gitignore ├── .jscsrc ├── README.md ├── build.js ├── config └── index.js ├── examples ├── async.html └── simple.html ├── gulpfile.js ├── package.json ├── src ├── index.js ├── moduleA.js └── moduleB.js └── tests ├── .eslintrc ├── config ├── karma.conf.js └── setup.js ├── integration └── index.js └── unit ├── index.js ├── moduleA.js └── moduleB.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "rules": { 4 | "strict": 0, 5 | "quotes": [2, "single"], 6 | "consistent-return": 0, 7 | "no-unused-expressions": 0, 8 | "no-underscore-dangle": 0, 9 | "camelcase": 0 10 | }, 11 | "env": { 12 | "browser": true, 13 | "node": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | dist 4 | node_modules 5 | bower_components 6 | coverage 7 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "requireCurlyBraces": [ 3 | "if", 4 | "else", 5 | "for", 6 | "while", 7 | "do", 8 | "try", 9 | "catch" 10 | ], 11 | "requireSpaceAfterKeywords": [ 12 | "if", 13 | "else", 14 | "for", 15 | "while", 16 | "do", 17 | "switch", 18 | "case", 19 | "return", 20 | "try", 21 | "catch", 22 | "function", 23 | "typeof" 24 | ], 25 | "requireSpaceBeforeBlockStatements": true, 26 | "requireParenthesesAroundIIFE": true, 27 | "requireSpacesInConditionalExpression": true, 28 | "disallowSpacesInNamedFunctionExpression": { 29 | "beforeOpeningRoundBrace": true 30 | }, 31 | "disallowSpacesInFunctionDeclaration": { 32 | "beforeOpeningRoundBrace": true 33 | }, 34 | "disallowMultipleVarDecl": "exceptUndefined", 35 | "requireBlocksOnNewline": 1, 36 | "disallowEmptyBlocks": true, 37 | "disallowSpacesInsideArrayBrackets": true, 38 | "disallowSpacesInsideParentheses": true, 39 | "disallowQuotedKeysInObjects": true, 40 | "disallowSpaceAfterObjectKeys": true, 41 | "requireCommaBeforeLineBreak": true, 42 | "disallowSpaceAfterPrefixUnaryOperators": true, 43 | "disallowSpaceBeforePostfixUnaryOperators": true, 44 | "disallowSpaceBeforeBinaryOperators": [ 45 | "," 46 | ], 47 | "requireSpaceBeforeBinaryOperators": true, 48 | "requireSpaceAfterBinaryOperators": true, 49 | "requireSpacesInsideObjectBrackets": "all", 50 | "requireCamelCaseOrUpperCaseIdentifiers": true, 51 | "disallowKeywords": [ "with" ], 52 | "validateQuoteMarks": "'", 53 | "validateIndentation": 4, 54 | "disallowMixedSpacesAndTabs": true, 55 | "disallowTrailingWhitespace": true, 56 | "disallowTrailingComma": true, 57 | "disallowKeywordsOnNewLine": [ "else" ], 58 | "requireCapitalizedConstructors": true, 59 | "disallowYodaConditions": true, 60 | "disallowNewlineBeforeBlockStatements": true, 61 | "maximumLineLength": null, 62 | "esnext": true 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ES6 SDK browser boilerplate 2 | 3 | This boilerplate is intended for the development of third-party SDK's and browser applications using modern ES6 JavaScript. 4 | This is a simplified and enhanced version of [babel-library-boilerplate](https://github.com/babel/babel-library-boilerplate) 5 | 6 | ### Features 7 | * ES6 syntax using [Babel compiler](https://babeljs.io/). 8 | * Code linting with [eslint](http://eslint.org/) and [jscs](http://jscs.info/). 9 | * Builds for browser using [browserify](http://browserify.org/) and [babelify](https://github.com/babel/babelify). 10 | * Code minification and optimization using [UglifyJS2](https://github.com/mishoo/UglifyJS2). 11 | * Unit tests with [mocha](http://mochajs.org/), [chai](http://chaijs.com/) and [sinon](http://sinonjs.org/). 12 | * Code coverage reports for unit tests using [istanbul](https://github.com/gotwarlost/istanbul) and [isparta](https://github.com/douglasduteil/isparta). 13 | * Integration tests with [karma](https://github.com/karma-runner/karma). 14 | * [Gulp](http://gulpjs.com/) task runner. 15 | 16 | ### Installation Prerequisites 17 | * [NodeJS](https://nodejs.org/download/) or [io.js](https://iojs.org/en/index.html) with npm. 18 | * [Gulp](https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md) 19 | 20 | ### Installation 21 | ```bash 22 | $ git clone git@github.com:DavidKlassen/es6-browser-boilerplate.git 23 | $ cd es6-browser-boilerplate 24 | $ npm run setup 25 | ``` 26 | 27 | ### Available gulp tasks 28 | * `gulp lint` - runs eslint and jscs 29 | * `gulp test:unit` - runs mocha unit tests 30 | * `gulp coverage` - runs unit tests and generates coverage report 31 | * `gulp test:integration` - runs karma tests 32 | * `gulp test` - runs unit and integration tests and generates code coverage report 33 | * `gulp browserify` - builds the script for browser 34 | * `gulp compile` - runs uglify and generates minified script 35 | * `gulp build` - runs browserify and compile 36 | * `gulp watch` - runs watchify and watches for changes and builds script in background 37 | * `gulp` - default task, runs lint, test, build and compile tasks 38 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | import config from './config'; 2 | import sdk from './src'; 3 | 4 | ((root) => { 5 | let sdkRef = root[config.globalName]; 6 | if (sdkRef && sdkRef._ready && sdkRef._config) { 7 | sdk.init(sdkRef._config).then(sdkRef._ready.bind(sdkRef, sdk)); 8 | } else { 9 | root[config.globalName] = sdk; 10 | } 11 | })(window); 12 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | globalName: 'MAIN' 3 | }; 4 | -------------------------------------------------------------------------------- /examples/async.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Async loading 6 | 7 | 8 | 15 | 16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple loading 6 | 7 | 8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const gulp = require('gulp'); 3 | const babelify = require('babelify'); 4 | const browserify = require('browserify'); 5 | const watchify = require('watchify'); 6 | const source = require('vinyl-source-stream'); 7 | const karma = require('karma').server; 8 | const isparta = require('isparta'); 9 | const babel = require('babel/register'); 10 | const plugins = require('gulp-load-plugins')(); 11 | 12 | function unitTests() { 13 | return gulp.src(['tests/config/setup.js', 'tests/unit/**/*.js']) 14 | .pipe(plugins.mocha({ 15 | reporter: 'spec', 16 | compilers: {js: babel} 17 | })); 18 | } 19 | 20 | gulp.task('lint', function () { 21 | return gulp.src(['src/**/*.js', 'tests/**/*.js']) 22 | .pipe(plugins.eslint()) 23 | .pipe(plugins.eslint.format()) 24 | .pipe(plugins.eslint.failAfterError()) 25 | .pipe(plugins.jscs()); 26 | }); 27 | 28 | gulp.task('coverage', function (done) { 29 | gulp.src(['src/**/*.js']) 30 | .pipe(plugins.istanbul({ 31 | instrumenter: isparta.Instrumenter, 32 | includeUntested: true 33 | })) 34 | .pipe(plugins.istanbul.hookRequire()) 35 | .on('finish', function () { 36 | unitTests() 37 | .pipe(plugins.istanbul.writeReports()) 38 | .pipe(plugins.istanbul.enforceThresholds({thresholds: {global: 100}})) 39 | .on('end', done); 40 | }); 41 | }); 42 | 43 | gulp.task('test:unit', function () { 44 | return unitTests(); 45 | }); 46 | 47 | gulp.task('test:integration', function (done) { 48 | karma.start({ 49 | configFile: path.join(__dirname, '/tests/config/karma.conf.js'), 50 | singleRun: true 51 | }, done); 52 | }); 53 | 54 | gulp.task('browserify', function () { 55 | return buildScript(false); 56 | }); 57 | 58 | gulp.task('compile', function () { 59 | return gulp.src('dist/lib-build.js') 60 | .pipe(plugins.uglify()) 61 | .pipe(plugins.rename({extname: '.min.js'})) 62 | .pipe(gulp.dest('dist')); 63 | }); 64 | 65 | gulp.task('build', plugins.sequence('browserify', 'compile')); 66 | gulp.task('test', plugins.sequence('coverage', 'build', 'test:integration')); 67 | gulp.task('default', plugins.sequence('lint', 'test')); 68 | 69 | gulp.task('watch', function () { 70 | buildScript(true); 71 | }); 72 | 73 | function handleErrors() { 74 | var args = Array.prototype.slice.call(arguments); 75 | console.log("Error: ", args[0].loc, args[0].filename); 76 | this.emit('end'); 77 | } 78 | 79 | function buildScript(watch) { 80 | var props = { 81 | entries: './build.js', 82 | debug: true, 83 | transform: [babelify] 84 | }; 85 | 86 | // watchify() if watch requested, otherwise run browserify() once 87 | var bundler = watch ? watchify(browserify(props), {poll: true}) : browserify(props); 88 | 89 | function rebundle() { 90 | var stream = bundler.bundle(); 91 | return stream 92 | .on('error', handleErrors) 93 | .pipe(source('lib-build.js')) 94 | .pipe(gulp.dest('dist')); 95 | } 96 | 97 | bundler.on('update', function () { 98 | rebundle(); 99 | plugins.util.log('Rebundle...'); 100 | }); 101 | 102 | return rebundle(); 103 | } 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es6-browser-boilerplate", 3 | "version": "1.0.1", 4 | "description": "Boilerplate project for the development of third-party SDK's and browser applications using modern ES6 JavaScript.", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git@github.com:DavidKlassen/es6-browser-boilerplate.git" 9 | }, 10 | "scripts": { 11 | "setup": "npm install && gulp" 12 | }, 13 | "devDependencies": { 14 | "babel": "^5.6.7", 15 | "babel-eslint": "^4.1.8", 16 | "babelify": "^6.1.2", 17 | "browserify": "^10.2.4", 18 | "chai": "^3.0.0", 19 | "gulp": "^3.9.0", 20 | "gulp-eslint": "^0.14.0", 21 | "gulp-istanbul": "^0.10.0", 22 | "gulp-jscs": "^1.6.0", 23 | "gulp-load-plugins": "^1.0.0-rc.1", 24 | "gulp-mocha": "^2.1.2", 25 | "gulp-rename": "^1.2.2", 26 | "gulp-sequence": "^0.3.2", 27 | "gulp-uglify": "^1.2.0", 28 | "isparta": "^3.0.3", 29 | "karma": "^0.12.37", 30 | "karma-browserify": "^4.2.1", 31 | "karma-mocha": "^0.2.0", 32 | "karma-phantomjs-launcher": "^0.2.0", 33 | "karma-spec-reporter": "0.0.19", 34 | "mocha": "^2.2.5", 35 | "phantomjs": "^1.9.17", 36 | "sinon": "^1.15.3", 37 | "sinon-chai": "^2.8.0", 38 | "vinyl-source-stream": "^1.1.0", 39 | "watchify": "^3.7.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import A from './moduleA.js'; 2 | import B from './moduleB.js'; 3 | 4 | export default { 5 | init(config) { 6 | this._config = config; 7 | 8 | return Promise.resolve(); // In case when init() is async 9 | }, 10 | A: A, 11 | B: B 12 | }; 13 | -------------------------------------------------------------------------------- /src/moduleA.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module A description. 3 | */ 4 | 5 | export default { 6 | /** 7 | * Foo method description. 8 | * 9 | * @return {string} 10 | */ 11 | foo() { 12 | return 'I am method foo() of module A'; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/moduleB.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module B description. 3 | */ 4 | 5 | export default { 6 | /** 7 | * Method bar() description. 8 | * 9 | * @return {string} 10 | */ 11 | bar() { 12 | return 'I am method bar() of module B'; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "rules": { 4 | "strict": 0, 5 | "quotes": [2, "single"], 6 | "consistent-return": 0, 7 | "no-unused-expressions": 0, 8 | "no-underscore-dangle": 0, 9 | "camelcase": 0 10 | }, 11 | "env": { 12 | "browser": true, 13 | "node": true, 14 | "mocha": true 15 | }, 16 | "globals": { 17 | "expect": true, 18 | "sinon": true, 19 | "MAIN": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/config/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | config.set({ 3 | basePath: '../../', 4 | frameworks: ['mocha', 'browserify'], 5 | files: [ 6 | 'dist/lib-build.min.js', 7 | 'tests/config/setup.js', 8 | 'tests/integration/**/*.js' 9 | ], 10 | preprocessors: { 11 | 'tests/{config/setup,integration/**/*}.js': ['browserify'] 12 | }, 13 | browserify: { 14 | debug: true, 15 | transform: ['babelify'] 16 | }, 17 | reporters: ['spec'], 18 | colors: true, 19 | browsers: ['PhantomJS'] 20 | }); 21 | }; 22 | -------------------------------------------------------------------------------- /tests/config/setup.js: -------------------------------------------------------------------------------- 1 | import chai, {expect} from 'chai'; 2 | import sinon from 'sinon'; 3 | import sinonChai from 'sinon-chai'; 4 | chai.use(sinonChai); 5 | 6 | if (typeof global !== 'undefined') { 7 | global.expect = expect; 8 | global.sinon = sinon; 9 | } else if (typeof window !== 'undefined') { 10 | window.expect = expect; 11 | window.sinon = sinon; 12 | } 13 | -------------------------------------------------------------------------------- /tests/integration/index.js: -------------------------------------------------------------------------------- 1 | describe('Global reference to SDK', () => { 2 | it('should be defined', () => { 3 | expect(MAIN).to.not.be.undefined; 4 | }); 5 | }); 6 | 7 | describe('MAIN', () => { 8 | describe('A module', () => { 9 | describe('foo() method', () => { 10 | it('should return a string', () => { 11 | expect(MAIN.A.foo()).to.be.a('string'); 12 | }); 13 | }); 14 | }); 15 | 16 | describe('B module', () => { 17 | describe('bar() method', () => { 18 | it('should return a string', () => { 19 | expect(MAIN.B.bar()).to.be.a('string'); 20 | }); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /tests/unit/index.js: -------------------------------------------------------------------------------- 1 | import sdk from '../../src'; 2 | 3 | describe('SDK', () => { 4 | describe('init(config) method', () => { 5 | it('should save config to "_config" property', () => { 6 | let config = { foo: 'bar' }; 7 | sdk.init(config); 8 | expect(config).to.deep.equal(sdk._config); 9 | }); 10 | }); 11 | 12 | it('should have A module', () => { 13 | expect(sdk.A).to.not.be.undefined; 14 | }); 15 | 16 | it('should have B module', () => { 17 | expect(sdk.B).to.not.be.undefined; 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/unit/moduleA.js: -------------------------------------------------------------------------------- 1 | import A from '../../src/moduleA.js'; 2 | 3 | describe('Module A', () => { 4 | describe('foo() method', () => { 5 | before(() => { 6 | sinon.spy(A, 'foo'); 7 | }); 8 | 9 | it('should return a string', () => { 10 | expect(A.foo()).to.be.a('string'); 11 | }); 12 | 13 | it('should have been run once', () => { 14 | expect(A.foo).to.have.been.calledOnce; 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/unit/moduleB.js: -------------------------------------------------------------------------------- 1 | import B from '../../src/moduleB.js'; 2 | 3 | describe('Module B', () => { 4 | describe('bar() method', () => { 5 | before(() => { 6 | sinon.spy(B, 'bar'); 7 | }); 8 | 9 | it('should return a string', () => { 10 | expect(B.bar()).to.be.a('string'); 11 | }); 12 | 13 | it('should have been run once', () => { 14 | expect(B.bar).to.have.been.calledOnce; 15 | }); 16 | }); 17 | }); 18 | --------------------------------------------------------------------------------