├── .gitignore ├── .npmignore ├── Makefile ├── README.md ├── index.js ├── package.json └── test-project ├── build.js ├── buildStatic.js ├── config.js ├── index.html ├── indexSfx.html ├── main.hbs ├── main.js ├── not-bundled.js ├── package.json └── test-module.js /.gitignore: -------------------------------------------------------------------------------- 1 | test-project/jspm_packages 2 | test-project/bundle.js 3 | test-project/bundle.js.map 4 | test-project/bundleSfx.js 5 | test-project/bundleSfx.js.map 6 | node_modules 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test-project/ 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test-build: 2 | node test-project/build.js 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jspm-dev-builder 2 | 3 | A small module for running [jspm](http://www.jspm.io) development builds. 4 | 5 | __Note__: if you're running jspm 0.17-beta12 or higher, this feature is now built in! See [this GitHub issue](https://github.com/jspm/jspm-cli/issues/1572) for more information. 6 | 7 | ## Motivations 8 | 9 | When you're working on small applications, one of the best features of jspm is that you don't need to run any form of watch task. jspm loads and transpiles everything in the browser, and in development this workflow works really well. However, when your app reaches a certain size the overhead of running everything in the browser leads to your app taking multiple seconds to refresh. 10 | 11 | At this point, you can switch to bundling your application whenever you save a file to generate a large, unminified bundle file which you can give to your browser. This is made possible thanks to recent additions to jspm that enable builds to make use of a build cache. On an application with ~800 modules, we've seen the initial jspm build hit 6-7 seconds, but after a file change we see the app rebuild in ~0.5 seconds. 12 | 13 | ## Usage 14 | 15 | ```js 16 | var DevBuilder = require('jspm-dev-builder'); 17 | 18 | var appDevBuilder = new DevBuilder({ 19 | jspm: require('jspm'), // so you can use your local version of jspm 20 | expression: path.join('app', 'bootstrap.js'), // path to your app's entry point 21 | outLoc: path.join('client', 'app-bundle.js'), // where you want the output file 22 | logPrefix: 'jspm-app', // put at the beginning of log messages from dev builder 23 | buildOptions: { 24 | sfx: false, // if the build should be self executing (please see note below on Self Executing Builds) 25 | // below options are passed straight through to the builder 26 | // the values shown are the defaults 27 | minify: false, 28 | mangle: false, 29 | sourceMaps: false, 30 | lowResSourceMaps: false, 31 | } 32 | }); 33 | ``` 34 | 35 | You can then call `appDevBuilder.build()` to generate a new build. If a file has changed and you need to rebuild, call `appDevBuilder.build('file-that-changed.js')`. This will cause the builder to invalidate the cache for the file that changed, and hence when a new build is generated it will have the new version of that file within it. 36 | 37 | If you don't pass `DevBuilder` a version of jspm to use, it will use its own version, which is currently set at 0.16.12. Note that in order for the cache invalidation to work as expected, you **must be using jspm 0.16.12 or newer**. If you are stuck on an older version, you'll have to override `DevBuilder.prototype.removeFromTrace`. 38 | 39 | ## Self Executing Builds 40 | 41 | There is currently a bug in jspm that causes cache invalidation to break with self executing builds. [Issue #9 is tracking this problem](https://github.com/jackfranklin/jspm-dev-builder/issues/9). Until then this plugin will not work well with self executing builds; you are advised to only use this with regular builds. 42 | 43 | ## Sample Output 44 | 45 | Below is a CLI output from a watch task that is using `jspm-dev-builder` to generate a bundle. 46 | 47 | ``` 48 | jspm-app jspm build starting app/bootstrap.js 49 | jspm-app jspm build finished 4911 50 | Watching files! 51 | File changed: app/routes/home/index/home-index.controller.js 52 | jspm-app Deleting app-compiled/routes/home/index/home-index.controller.js from trace cache 53 | jspm-app jspm build starting app/bootstrap.js 54 | jspm-app jspm build finished 429 55 | ``` 56 | 57 | ## Changelog 58 | 59 | #### 0.4.0 60 | - add logging of `jspm.version` 61 | - update to latest stable jspm (0.16.32) 62 | 63 | #### 0.3.2 64 | - add message about SFX cache invalidation being buggy that links to https://github.com/jackfranklin/jspm-dev-builder/issues/9. 65 | 66 | #### 0.3.1 67 | - updated deprecated calls to the builder. Added more messaging during cache invalidation. More examples - for SFX as well. 68 | 69 | #### 0.3.0 70 | - fixed the build function creating a new instance each time, and hence ignoring the cache - thanks @OrKoN for the PR 71 | 72 | #### 0.2.0 73 | - renamed `inLoc` to `expression` 74 | - added `buildOptions` which are passed through to SystemJS-builder 75 | - added `buildOptions.sfx` to turn on self executing bundling 76 | - upgraded to jspm 0.16.12, and use the new SystemJS-Builder cache invalidation 77 | 78 | #### 0.1.0 79 | - initial release 80 | 81 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var chalk = require('chalk'); 2 | var path = require('path'); 3 | var _ = require('lodash'); 4 | 5 | function DevBuilder(options) { 6 | this.expression = options.expression; 7 | this.outLoc = options.outLoc; 8 | this.logPrefix = options.logPrefix || 'jspm-dev'; 9 | this.jspm = options.jspm || require('jspm'); 10 | this.builder = new this.jspm.Builder(); 11 | this.buildOptions = _.extend({ 12 | sfx: false, 13 | minify: false, 14 | mangle: false, 15 | sourceMaps: false, 16 | lowResSourceMaps: false, 17 | }, options.buildOptions || {}); 18 | 19 | // older versions of jspm don't have a version property 20 | if (this.jspm.version) { 21 | this.logInfo('Using jspm version ', this.jspm.version); 22 | } 23 | 24 | if (this.buildOptions.sfx) { 25 | this.logInfo('Warning! SFX Cache invalidation is buggy. See jackfranklin/jspm-dev-builder/issues/9.'); 26 | } 27 | } 28 | 29 | DevBuilder.prototype.removeFromTrace = function(filename) { 30 | if (!this.builder) return false; 31 | var invalidated = this.builder.invalidate(filename); 32 | return invalidated.length > 0; 33 | }; 34 | 35 | DevBuilder.prototype.bundle = function() { 36 | var buildOptions = _.omit(this.buildOptions, 'sfx'); 37 | if (this.buildOptions.sfx) { 38 | this.logInfo('Building SFX bundle'); 39 | return this.builder.buildStatic(this.expression, this.outLoc, buildOptions); 40 | } else { 41 | this.logInfo('Building non-SFX bundle'); 42 | return this.builder.bundle(this.expression, this.outLoc, buildOptions); 43 | } 44 | }; 45 | 46 | DevBuilder.prototype.build = function(filename) { 47 | if (filename) { 48 | if (!this.removeFromTrace(filename)) { 49 | this.logError('jspm invalidation error', ['Nothing has been invalidated for', 50 | filename + '. It\'s likely that the module does not exist.', 51 | 'If the module is loaded using a plugin, you need to append !', 52 | 'to its name in order to invalidate it properly'].join(' ')); 53 | } else { 54 | this.logInfo('Invalidated', chalk.blue(filename), 'from cache'); 55 | } 56 | } 57 | 58 | var buildStart = Date.now(); 59 | this.logInfo('jspm build starting', chalk.blue(this.expression)); 60 | 61 | return this.bundle().then(function() { 62 | var buildEnd = Date.now(); 63 | this.logInfo('jspm build finished', chalk.red(buildEnd - buildStart)); 64 | return this; 65 | }.bind(this)).catch(function(err) { 66 | // Do any errors actually contain a message property? 67 | if (err.message) { 68 | this.logError('jspm build error:', err.message, err.stack); 69 | } 70 | else { 71 | this.logError('jspm build error:', err); 72 | } 73 | }.bind(this)); 74 | }; 75 | 76 | DevBuilder.prototype.logInfo = function() { 77 | console.log.bind(console, chalk.red(this.logPrefix)).apply(null, arguments); 78 | }; 79 | 80 | DevBuilder.prototype.logError = function() { 81 | console.log.bind(console, chalk.red(this.logPrefix)).apply(null, arguments); 82 | }; 83 | 84 | module.exports = DevBuilder; 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jspm-dev-builder", 3 | "version": "0.4.0", 4 | "description": "A tiny library for building jspm bundles in development", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/jackfranklin/jspm-dev-builder.git" 8 | }, 9 | "main": "index.js", 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "author": "Jack Franklin", 14 | "contributors": [ 15 | { 16 | "name": "Oleksii Rudenko", 17 | "email": "oleksii.rudenko@n-fuse.de" 18 | } 19 | ], 20 | "license": "ISC", 21 | "dependencies": { 22 | "chalk": "^1.1.1", 23 | "jspm": "^0.16.32", 24 | "lodash": "^3.10.1" 25 | }, 26 | "devDependencies": { 27 | "chokidar": "^1.2.0" 28 | }, 29 | "jspm": { 30 | "directories": { 31 | "baseURL": "test-project" 32 | }, 33 | "dependencies": { 34 | "jquery": "github:components/jquery@^2.1.4" 35 | }, 36 | "devDependencies": { 37 | "babel": "npm:babel-core@^5.8.24", 38 | "babel-runtime": "npm:babel-runtime@^5.8.24", 39 | "core-js": "npm:core-js@^1.1.4" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test-project/build.js: -------------------------------------------------------------------------------- 1 | var DevBuilder = require('../index'); 2 | var path = require('path'); 3 | var chokidar = require('chokidar'); 4 | 5 | var appBuilder = new DevBuilder({ 6 | expression: path.join(__dirname, 'main'), 7 | outLoc: path.join(__dirname, 'bundle.js'), 8 | logPrefix: 'my-app', 9 | buildOptions: { 10 | minify: false, 11 | mangle: false, 12 | sourceMaps: true 13 | } 14 | }); 15 | 16 | appBuilder.build(); 17 | 18 | chokidar.watch('main.js').on('change', function() { 19 | appBuilder.build('main.js'); 20 | }); 21 | 22 | chokidar.watch('not-bundled.js').on('change', function() { 23 | appBuilder.build('not-bundled.js'); 24 | }); 25 | 26 | chokidar.watch('main.hbs').on('change', function() { 27 | appBuilder.build('main.hbs!'); // note ! 28 | }); 29 | -------------------------------------------------------------------------------- /test-project/buildStatic.js: -------------------------------------------------------------------------------- 1 | var DevBuilder = require('../index'); 2 | var path = require('path'); 3 | var chokidar = require('chokidar'); 4 | 5 | var appBuilder = new DevBuilder({ 6 | expression: path.join(__dirname, 'main'), 7 | outLoc: path.join(__dirname, 'bundleSfx.js'), 8 | logPrefix: 'my-app', 9 | buildOptions: { 10 | sfx: true 11 | } 12 | }); 13 | 14 | appBuilder.build(); 15 | 16 | chokidar.watch('main.js').on('change', function() { 17 | appBuilder.build('main.js'); 18 | }); 19 | 20 | chokidar.watch('not-bundled.js').on('change', function() { 21 | appBuilder.build('not-bundled.js'); 22 | }); 23 | 24 | chokidar.watch('main.hbs').on('change', function() { 25 | appBuilder.build('main.hbs!'); // note ! 26 | }); 27 | -------------------------------------------------------------------------------- /test-project/config.js: -------------------------------------------------------------------------------- 1 | System.config({ 2 | baseURL: "/", 3 | defaultJSExtensions: true, 4 | transpiler: "babel", 5 | babelOptions: { 6 | "optional": [ 7 | "runtime", 8 | "optimisation.modules.system" 9 | ] 10 | }, 11 | paths: { 12 | "github:*": "jspm_packages/github/*", 13 | "npm:*": "jspm_packages/npm/*" 14 | }, 15 | 16 | map: { 17 | "babel": "npm:babel-core@5.8.25", 18 | "babel-runtime": "npm:babel-runtime@5.8.25", 19 | "core-js": "npm:core-js@1.2.1", 20 | "ember": "github:components/ember@2.0.2", 21 | "hbs": "github:n-fuse/plugin-ember-hbs@2.0.2", 22 | "jquery": "github:components/jquery@2.1.4", 23 | "github:components/ember@2.0.2": { 24 | "jquery": "github:components/jquery@2.1.4" 25 | }, 26 | "github:jspm/nodelibs-assert@0.1.0": { 27 | "assert": "npm:assert@1.3.0" 28 | }, 29 | "github:jspm/nodelibs-process@0.1.2": { 30 | "process": "npm:process@0.11.2" 31 | }, 32 | "github:jspm/nodelibs-util@0.1.0": { 33 | "util": "npm:util@0.10.3" 34 | }, 35 | "github:n-fuse/plugin-ember-hbs@2.0.2": { 36 | "ember-template-compiler": "github:n-fuse/ember-template-compiler@2.0.2" 37 | }, 38 | "npm:assert@1.3.0": { 39 | "util": "npm:util@0.10.3" 40 | }, 41 | "npm:babel-runtime@5.8.25": { 42 | "process": "github:jspm/nodelibs-process@0.1.2" 43 | }, 44 | "npm:core-js@1.2.1": { 45 | "fs": "github:jspm/nodelibs-fs@0.1.2", 46 | "process": "github:jspm/nodelibs-process@0.1.2", 47 | "systemjs-json": "github:systemjs/plugin-json@0.1.0" 48 | }, 49 | "npm:inherits@2.0.1": { 50 | "util": "github:jspm/nodelibs-util@0.1.0" 51 | }, 52 | "npm:process@0.11.2": { 53 | "assert": "github:jspm/nodelibs-assert@0.1.0" 54 | }, 55 | "npm:util@0.10.3": { 56 | "inherits": "npm:inherits@2.0.1", 57 | "process": "github:jspm/nodelibs-process@0.1.2" 58 | } 59 | } 60 | }); 61 | -------------------------------------------------------------------------------- /test-project/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |