├── .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 |
--------------------------------------------------------------------------------