├── .gitignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── Gruntfile.js ├── LICENSE-MIT ├── README.md ├── examples ├── basic │ ├── Gruntfile.js │ ├── client │ │ ├── basic.js │ │ └── test.js │ ├── lib │ │ └── moments.js │ ├── package.json │ └── public │ │ ├── app.js │ │ ├── index.html │ │ ├── main.js │ │ └── vendor.js ├── browserify-shim │ ├── .gitignore │ ├── Gruntfile.js │ ├── lib │ │ ├── anotherCustomLib.js │ │ └── customLib.js │ ├── package.json │ └── src │ │ └── app.js ├── factor-bundle │ ├── Gruntfile.js │ ├── client │ │ ├── w.js │ │ ├── x.js │ │ ├── y.js │ │ └── z.js │ ├── package.json │ └── public │ │ ├── common.js │ │ ├── x.js │ │ └── y.js ├── multi-dest │ ├── .gitignore │ ├── Gruntfile.js │ ├── common.js │ ├── first.app.js │ ├── package.json │ └── second.app.js └── watch │ ├── Gruntfile.js │ ├── client │ ├── basic.js │ └── test.js │ ├── lib │ └── moments.js │ ├── package.json │ └── public │ ├── app.js │ ├── index.html │ ├── main.js │ └── vendor.js ├── lib └── runner.js ├── package.json ├── tasks └── browserify.js └── test ├── browserify.test.js └── fixtures ├── alias ├── entry.js └── toBeAliased.js ├── aliasMappings ├── entry.js ├── foo │ ├── bar │ │ └── foobar.js │ └── foo.js └── root.js ├── basic ├── a.js ├── b.js └── basic.js ├── extension ├── a.js ├── b.fjs └── extension.js ├── external-dir ├── a.js ├── b │ └── index.js └── entry.js ├── external ├── a.js ├── alias.js ├── b.js └── entry.js ├── externalize ├── a.js ├── b.js └── entry.js ├── ignore ├── a.js ├── b.js ├── entry.js ├── ignore.js └── os.js ├── noParse ├── a.js ├── jquery.js └── noParse.js ├── paths └── paths.js ├── shim ├── a.js ├── jquery.js └── shim.js └── watch ├── a.js ├── b.js └── basic.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /examples/**/node_modules 3 | *.DS_Store 4 | .idea/ 5 | tmp/ 6 | npm-debug.log 7 | package-lock.json 8 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "indent": 2, 6 | "latedef": "nofunc", 7 | "newcap": true, 8 | "noarg": true, 9 | "node": true, 10 | "nonew": true, 11 | "sub": true, 12 | "undef": true 13 | } 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "14" 5 | - "12" 6 | - "10" 7 | - "8" 8 | before_install: 9 | - npm install -g grunt-cli 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 6.0.0 2 | - Update watchify to 4.0.0 3 | - Update browserify to 17.0.0 4 | - Bump minimum node version to 8.10 to keep up with with watchify and chokidar 5 | 6 | ### 5.3.0 7 | - Update browserify to 16.0.0 8 | 9 | ### 5.2.0 10 | - New: Added `cacheFile` option for [browserify-incremental](https://github.com/jsdf/browserify-incremental) support 11 | (contributed by Greg Slepak) 12 | - Update dependencies. 13 | 14 | ### 5.1.0 15 | - Update dependencies. Browserify 14.0 16 | 17 | ### 5.0.0 18 | - Update dependencies. Browserify 13.0 19 | - Fix Watchify on MacOS [#358](https://github.com/jmreidy/grunt-browserify/issues/358) 20 | - BC: the order of transform arguments is now consistent with the browserify API [#319](https://github.com/jmreidy/grunt-browserify/issues/319) 21 | 22 | ### 3.8.0 23 | - Update dependencies. Browserify 10.0 24 | - New: Users can specify the alias with {alias: path} [#316](https://github.com/jmreidy/grunt-browserify/issues/316) 25 | 26 | ### 3.7.0 27 | - Update dependencies. 28 | - Update to Watchify 3.0 (#314 via @jonbretman) 29 | - New: browserify-shim example 30 | - Fix: [#289](https://github.com/jmreidy/grunt-browserify/issues/289) with #317 by adding more details in readme for watchify. 31 | 32 | ### 3.6.0 33 | - New: List only the required files in package.json 34 | - New: Added node 0.12 for Travis. 35 | - Fix: Run tasks in parallel instead of in series to fix [#199](https://github.com/jmreidy/grunt-browserify/issues/199). 36 | 37 | ### 3.5.1 38 | - Update dependencies. 39 | - Update watchify to fix an issue with *.json files ([watchify#160](https://github.com/substack/watchify/pull/160) with #308 (@Pleochism)) 40 | 41 | ### 3.5.0 42 | - New: Support for passing options to watchify (watchifyOptions) (#299 via @nfvs) 43 | - New: JSHint in Travis (#300 via @jonbretman) 44 | - New: require option can now takes options hash (#302 via @jonbretman) 45 | - New: configure option to be able to configure the bundle using the browserify api. (#303 via @oliverwoodings) 46 | 47 | ### 3.4.0 48 | - Update dependencies. Browserify to v9. 49 | - Fix: Update require to expose as per browserify (#292 via @justinjmoses) 50 | - New: Add example with factor-bundle 51 | 52 | ### 3.3.0 53 | - Update dependencies. Browserify to v8. 54 | 55 | ### 3.2.1 56 | - Fix: Remove errant console.log (#257 via @tleunen) 57 | - Fix: Deep clone browserify options to prevent dupes (#261 via @wgcrouch) 58 | 59 | ### 3.2.0 60 | - New: Add support for browserify entries option (@JoshuaToenyes) 61 | - New: Add Banner option (@tleunen) 62 | - Fix: Merge options.alias with options.require (@tleunen) 63 | 64 | ### 3.1.0 65 | - Update dependencies. Browserify to v6. 66 | 67 | ### 3.0.1 68 | - Fix regression #227: keep failed process alive 69 | 70 | ### 3.0 71 | - Release of 2.2-beta 72 | - Actually moving to semver from this point forward 73 | 74 | ### 2.2-beta 75 | - Update browserify to v5 and watchify to v1 76 | 77 | ### v2.1.4 78 | - Update browserify to deal with security vulnerability 79 | 80 | ### v2.1.3 81 | - Fix ignore/exclude behavior 82 | 83 | ### v2.1.2 84 | - Fix onBundleComplete regression 85 | 86 | ### v2.1.1 87 | - Update dependencies layout 88 | - Only write bundle if src exists 89 | - Properly append semicolons to bundle output 90 | 91 | ### v2.1.0 92 | - Update to Browserify 4.x 93 | 94 | ### v2.0.8 95 | - Exclude should only resolve filenames for glob patterns. 96 | 97 | ### v2.0.7 98 | - Allow watchify bundle updates to fail without killing grunt 99 | 100 | ### v2.0.6 101 | - Add support for globbing patterns for ignore, exclude, and external 102 | 103 | ### v2.0.5 104 | - Update deps 105 | 106 | ### v2.0.4 107 | - Allow `alias` to work with modules. (via @daviwil) 108 | 109 | ### v2.0.3 110 | - Restore keepAlive and watch support. 111 | 112 | ### v2.0.2 113 | - Remove browserify-shim dependency, since it's now an optional transform 114 | 115 | ### v2.0.1 116 | - Complete rewrite of grunt-browserify internals, and update of the API. 117 | (2.0.0 was mis-published to NPM and removed). 118 | 119 | ### v1.3.2 120 | - Adding `require` and global `transform` options. 121 | 122 | ### v1.3.1 123 | - Adding support for Browserify 3.2 paths (via @trevordixon) 124 | 125 | ### v1.3.0 126 | - Bump to Browserify v3 127 | 128 | ### v1.2.12 129 | - Add `preBundleCB` option (via @alexstrat) 130 | 131 | ### v1.2.11 132 | - Move to browserify 2.35 for upstream dedupe fix 133 | 134 | ### v1.2.10 135 | - Fix #106 136 | 137 | ### v1.2.9 138 | - Fix peerDependency version requirements 139 | 140 | ### v1.2.8 141 | - Add postBundle callback support (via @Bockit) 142 | 143 | ### v1.2.7 144 | - Fix bug in sharing shimmed files across bundles (#89) 145 | 146 | ### v1.2.6 147 | - Move browserify to a peer dependency, to allow custom versions (via @nrn) 148 | - Add support for browserify extension flag (from browserify v2.31) 149 | 150 | ### v1.2.5 151 | - Documentation fix (via @alanshaw) 152 | - Allow aliasing inner modules (via @bananushka) 153 | - Fix multitask shim bug (via @byronmwong) 154 | 155 | ### v1.2.4 156 | - Flatten options arrays, to prevent any weird behavior (via @joeybaker) 157 | 158 | ### v1.2.3 159 | - Allow aliasing with arbitrary ids. For example, you could alias `./vendor/client/jquery/jquery.js` to `/vendor/jquery` 160 | for consumption by other bundles. See the updated `complex` and `externals` examples 161 | 162 | ### v1.2.2 163 | - Change `alias` destination behavior to only treat the destination as a 164 | filepath if it exists 165 | 166 | ### v1.2.1 167 | - Bumping dependency versions 168 | 169 | ### v1.2 170 | - `Externalize` has been deprecated in favor of `alias` (#69) 171 | - Allow `external` to use module names, in addition to file paths (#68). Waiting on Browserify changes for this to actually work. 172 | - Much improved docs (#67) 173 | - Allow non-files to be ignored (#50), via @joshuarubin 174 | 175 | ### v1.1.1 176 | - Fix regression where shimmed modules not being parsed 177 | 178 | ### v1.1.0 179 | - Added support for noParse option 180 | - Change browserify() call to pass files as opts.entries 181 | 182 | ### v1.0.5 183 | - Bumping to latest Browserify (2.18.x) 184 | 185 | ### v1.0.4 186 | - Adding directory support for `external` parameter 187 | 188 | ### v1.0.3 189 | - Add new aliasMappings functionality 190 | 191 | ### v1.0.2 192 | - Move away from browserify-stream to callback approach 193 | 194 | ### v1.0.0 195 | - Really should've been released at v0.2, but better late than never! 196 | 197 | ### v0.2.5 198 | - Update externalize to expose npm modules to external bundles 199 | 200 | ### v0.2.4 201 | - Add externalize option, to expose modules to external bundles 202 | - Add browserify-shim support 203 | - Completely rewrote and significantly improved tests 204 | - Various fixes 205 | 206 | ### v0.2.0 207 | - Add support for Browserify 2 208 | 209 | ### v0.1.1 210 | - Properly support compact and full grunt task syntax 211 | 212 | ### v0.1.0 213 | - Initial release 214 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 'use strict'; 3 | // Project configuration. 4 | grunt.initConfig({ 5 | 6 | clean: { 7 | tests: ['tmp'] 8 | }, 9 | 10 | jshint: { 11 | all: ['Gruntfile.js', 'tasks/**/*.js', 'lib/**/*.js', 'test/*.js'], 12 | options: { 13 | jshintrc: ".jshintrc" 14 | } 15 | } 16 | }); 17 | 18 | // Load local tasks. 19 | grunt.loadTasks('tasks'); 20 | grunt.loadNpmTasks('grunt-contrib-jshint'); 21 | grunt.loadNpmTasks('grunt-contrib-clean'); 22 | 23 | // Default task. 24 | grunt.registerTask('default', ['jshint']); 25 | }; 26 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2017 `grunt-browserify` contributors 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![build status](https://secure.travis-ci.org/jmreidy/grunt-browserify.png)](http://travis-ci.org/jmreidy/grunt-browserify) 2 | [![NPM version](https://badge.fury.io/js/grunt-browserify.png)](http://badge.fury.io/js/grunt-browserify) 3 | # grunt-browserify 4 | 5 | Grunt task for node-browserify. 6 | 7 | ## Getting Started 8 | This plugin requires [Grunt](https://gruntjs.com) `~0.4.0`. 9 | 10 | Install this grunt plugin with: 11 | ```shell 12 | npm install grunt-browserify --save-dev 13 | ``` 14 | 15 | Then add this line to your project's `grunt.js` Gruntfile: 16 | 17 | ```javascript 18 | grunt.loadNpmTasks('grunt-browserify'); 19 | ``` 20 | 21 | ## 3.0 Release 22 | An important note for those running the latest release of grunt-browserify: 23 | the newest version (>3.0) incorporates breaking changes from Browserify 24 | which REMOVED BUNDLE OPTIONS. All options to browserify must now be passed 25 | in the `browserifyOptions` hash. 26 | 27 | ## In the Wild 28 | Most simply, Browserify is a tool for taking your CommonJS-style Javascript 29 | code and packaging it for use in the browser. Grunt-Browserify provides the 30 | glue to better integrate Browserify into your Grunt-based development workflow. 31 | 32 | For JavaScripters unfamiliar with CJS-style code and the Node ecosystem, moving 33 | to Browserify can be a bit confusing. Writing your client-side code as CJS 34 | modules allows for smaller, easier to understand files that perform one task 35 | well. These modules, because of their simplicity, will be significantly easier 36 | to use across projects. CJS modules also help to expose the dependency graph 37 | inherent in your code, allowing you to write cleaner, more-maintainable 38 | modules. As [Alex MacCaw writes](http://spinejs.com/docs/commonjs): 39 | >CommonJS modules are one of the best solutions to JavaScript dependency 40 | >management. 41 | 42 | >CommonJS modules solve JavaScript scope issues by making sure each module is 43 | >executed in its own namespace. Modules have to explicitly export variables 44 | >they want to expose to other modules, and explicitly import other modules; in 45 | >other words, there's no global namespace. 46 | 47 | (A note to AMD fans that the benefits above are not unique to the CJS 48 | style of writing JavaScript modules, but the ease-of-interoperability with 49 | Node.JS code is a plus of CJS.) 50 | 51 | As you begin to write your client-side code in small, reusable modules, you 52 | start to have a lot more files to manage. At the same time, you need to 53 | integrate these files with other client-side libraries, some of which do not 54 | play particularly nicely with a CJS module system. The simplicity provided by 55 | CJS modules can be lost as build complexity is increased and Browserify 56 | compilation time gets out of control. 57 | 58 | 59 | ## Documentation 60 | Run this task with the `grunt browserify` command. As with other Grunt plugins, the `src` and `dest` properties are most important: `src` will use the Grunt glob pattern to specify files for inclusion in the browserified package, and `dest` will specify the outfile for the compiled module. 61 | 62 | The current version of grunt-browserify sticks as close to the core browserify API as possible. Additional functionality can be added via the rich ecosystem of browserify transforms and plugins. 63 | 64 | The following task options are supported: 65 | 66 | #### alias 67 | Type: `Object{alias:path}` 68 | 69 | Browserify can alias files or modules to a certain name. For example, `require('./foo')` can be aliased to be used as `require('foo')`. 70 | ```js 71 | options: { 72 | alias: { 73 | 'foo': './foo.js' 74 | } 75 | } 76 | ``` 77 | 78 | The `alias` option is just a shortcut to require a file and expose a different name for it. You could do exactly the same thing using `require` instead of `alias`. It's equivalent to `require: [ ['./foo.js', {expose: 'foo'} ] ]` 79 | 80 | If you need alias mappings, you can use @joeybaker's [remapify plugin](https://github.com/joeybaker/remapify), as demonstrated in the code below: 81 | 82 | ```js 83 | options: { 84 | plugin: [ 85 | [ 86 | 'remapify', [{ 87 | src: './client/views/**/*.js', // glob for the files to remap 88 | expose: 'views', // this will expose `__dirname + /client/views/home.js` as `views/home.js` 89 | cwd: __dirname // defaults to process.cwd() 90 | } 91 | ] 92 | ] 93 | ] 94 | } 95 | ``` 96 | 97 | #### banner 98 | Type: `String` 99 | Default: empty string 100 | 101 | The string will be prepended to the output. Template strings (e.g. `<%= config.value %>` will be expanded automatically. 102 | 103 | #### require 104 | Type: `[String]` or `[String:String]` or `[[String, Object]]` 105 | 106 | Specifies files to be required in the browserify bundle. String filenames are parsed into their full paths with `path.resolve`. Aliases can be provided by using the `filePathString:aliasName` format. 107 | 108 | Each require can also be provided with an options hash; in this case, the require should be specified as an array of `[filePathString, optionsHash]`. 109 | 110 | #### ignore 111 | Type: `[String]` 112 | 113 | Specifies files to be ignored in the browserify bundle. 114 | String filenames are parsed into their full paths with `path.resolve`. 115 | Globbing patterns are supported. 116 | 117 | #### exclude 118 | Type: `[String]` 119 | 120 | Specifies files or modules to be excluded in the browserify bundle. 121 | Globbing patterns are supported; globbed filenames are parsed into their full paths. 122 | 123 | #### external 124 | Type: `[String]` or `Object{alias:path}`. 125 | 126 | Specifies id strings which will be loaded from a previously loaded, “common” bundle. 127 | That is to say, files in the bundle that require the target String will assume 128 | that the target is provided externally. 129 | 130 | The secondary form of this option 131 | follows the format of `alias` above, and will externalise the ids specified in 132 | the alias object. This second form allows for the declaration of a single alias 133 | object which can be supplied to one bundle's `alias` option and another option's 134 | `external` option. 135 | 136 | In either case, globbing patterns are supported. 137 | 138 | 139 | #### transform 140 | Type: `[String || Function]` or `[[String || Function, Object]]` 141 | 142 | Specifies a pipeline of functions (or modules) through which the browserified bundle will be run. The transform can either be a literal function, or a string referring to a NPM module. The [browserify docs themselves](https://github.com/substack/node-browserify#btransformtr) explain transform well, but below is an example of transform used with `grunt-browserify` to automatically compile coffeescript files for use in a bundle: 143 | 144 | ```javascript 145 | browserify: { 146 | dist: { 147 | files: { 148 | 'build/module.js': ['client/scripts/**/*.js', 'client/scripts/**/*.coffee'] 149 | }, 150 | options: { 151 | transform: ['coffeeify'] 152 | } 153 | } 154 | } 155 | ``` 156 | 157 | Transforms can also be provided with an options hash; in this case, the transform should be specified as an array of `[transformStringOrFn, optionsHash]`. 158 | 159 | Note for [browserify-shim](https://github.com/thlorenz/browserify-shim), the configuration of this transformation has to be inside `package.json`. Please see documentation of `browserify-shim` and [our example](/examples/browserify-shim). 160 | 161 | #### plugin 162 | Type: `[String || Function]` 163 | Register a browserify plugin with the bundle. As with transforms, plugins are identified with either their NPM name (String) or a function literal. 164 | ```javascript 165 | browserify: { 166 | dev: { 167 | options: { 168 | plugin: [ 169 | 'coffeeify', // register plugin by name 170 | ['browserify-hmr', { noServe : true }] // register plugin with name and options 171 | ] 172 | } 173 | } 174 | } 175 | ``` 176 | 177 | #### browserifyOptions 178 | Type: Object 179 | 180 | A hash of options that are passed to browserify during instantiation. Task-level `browserifyOptions` are not merged into target-level options. 181 | If a target overrides task-level `browserifyOptions`, it overrides all of it. 182 | [Browserify Github README](https://github.com/substack/node-browserify#var-b--browserifyfiles-or-opts) 183 | 184 | #### watch 185 | Type: Boolean 186 | If true, invoke [watchify](https://github.com/substack/watchify) instead of browserify. 187 | 188 | For watchify to work properly, you have to keep the process running. The option `keepAlive` can help you do that, or you can use another `grunt-watch` task. 189 | 190 | #### cacheFile 191 | 192 | Type: String 193 | 194 | Set to location of [browserify-incremental](https://github.com/jsdf/browserify-incremental) cache file and enable incremental builds via `browserify-incremental`. Mutually exclusive with `watchify`. 195 | 196 | Note that unlike `watchify`, this setting is fully compatible with `grunt-contrib-watch` and should be used together with it. 197 | 198 | #### keepAlive 199 | Type: Boolean 200 | If true and if `watch` above is true, keep the Grunt process alive (simulates grunt-watch functionality). 201 | 202 | #### watchifyOptions 203 | Type: Object 204 | A hash of options that are passed to watchify during instantiation. 205 | [Watchify Github README](https://github.com/substack/watchify#var-w--watchifyb-opts) 206 | 207 | #### configure 208 | Type: `Function (b)` 209 | 210 | An optional callback function that is invoked once before the bundle runs. This can be used for programatically configuring browserify using it's API. 211 | `b` is the `browserify` instance for the bundle. 212 | 213 | #### preBundleCB 214 | Type: `Function (b)` 215 | 216 | An optional callback function, that will be called before bundle completion. 217 | `b` is the `browerify` instance that will output the bundle. 218 | 219 | __NB:__ This callback will be invoked every time the bundle is built so when used with the `watch` option set to true it will be called multiple times. Do not register transforms in this callback or they will end up being registered multiple times. 220 | 221 | #### postBundleCB 222 | Type: `Function (err, src, next)` 223 | 224 | An optional callback function, which will be called after bundle completion and 225 | before writing of the bundle. The `err` and `src` arguments are provided 226 | directly from browserify. The `next` callback should be called with `(err, 227 | modifiedSrc)`; the `modifiedSrc` is what will be written to the output file. 228 | 229 | __NB:__ This callback will be invoked every time the bundle is built so when used with the `watch` option set to true it will be called multiple times. 230 | 231 | 232 | ## Contributing 233 | In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code. 234 | 235 | ## Release History 236 | 237 | See the [CHANGELOG](https://github.com/jmreidy/grunt-browserify/blob/master/CHANGELOG.md). 238 | 239 | ## License 240 | Copyright (c) 2013-2017 `grunt-browserify` contributors 241 | Licensed under the MIT license. 242 | -------------------------------------------------------------------------------- /examples/basic/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | grunt.initConfig({ 3 | browserify: { 4 | vendor: { 5 | src: [], 6 | dest: 'public/vendor.js', 7 | options: { 8 | require: ['jquery'], 9 | alias: { 10 | momentWrapper: './lib/moments.js' 11 | } 12 | } 13 | }, 14 | client: { 15 | src: ['client/**/*.js'], 16 | dest: 'public/app.js', 17 | options: { 18 | external: ['jquery', 'momentWrapper'], 19 | } 20 | } 21 | }, 22 | 23 | concat: { 24 | 'public/main.js': ['public/vendor.js', 'public/app.js'] 25 | } 26 | }); 27 | 28 | grunt.loadTasks('../../tasks'); 29 | grunt.loadNpmTasks('grunt-contrib-concat'); 30 | grunt.registerTask('default', ['browserify', 'concat']); 31 | 32 | }; 33 | -------------------------------------------------------------------------------- /examples/basic/client/basic.js: -------------------------------------------------------------------------------- 1 | var $ = require('jquery'); 2 | var test = require('./test'); 3 | 4 | $().ready(function () { 5 | test(); 6 | }); 7 | -------------------------------------------------------------------------------- /examples/basic/client/test.js: -------------------------------------------------------------------------------- 1 | var moments = require('momentWrapper'); 2 | var evt = require('events'); 3 | 4 | module.exports = function () { 5 | console.log(evt); 6 | console.log(moments.createMoment()); 7 | }; 8 | -------------------------------------------------------------------------------- /examples/basic/lib/moments.js: -------------------------------------------------------------------------------- 1 | var moment = require('moment'); 2 | 3 | module.exports = { 4 | createMoment: function () { 5 | return moment(new Date()); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /examples/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "grunt": "~0.4.1", 4 | "grunt-contrib-concat": "~0.3.0", 5 | "jquery": "~2.1.0", 6 | "moment": "~2.5.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/basic/public/app.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0 && this._events[type].length > m) { 159 | this._events[type].warned = true; 160 | console.error('(node) warning: possible EventEmitter memory ' + 161 | 'leak detected. %d listeners added. ' + 162 | 'Use emitter.setMaxListeners() to increase limit.', 163 | this._events[type].length); 164 | if (typeof console.trace === 'function') { 165 | // not supported in IE 10 166 | console.trace(); 167 | } 168 | } 169 | } 170 | 171 | return this; 172 | }; 173 | 174 | EventEmitter.prototype.on = EventEmitter.prototype.addListener; 175 | 176 | EventEmitter.prototype.once = function(type, listener) { 177 | if (!isFunction(listener)) 178 | throw TypeError('listener must be a function'); 179 | 180 | var fired = false; 181 | 182 | function g() { 183 | this.removeListener(type, g); 184 | 185 | if (!fired) { 186 | fired = true; 187 | listener.apply(this, arguments); 188 | } 189 | } 190 | 191 | g.listener = listener; 192 | this.on(type, g); 193 | 194 | return this; 195 | }; 196 | 197 | // emits a 'removeListener' event iff the listener was removed 198 | EventEmitter.prototype.removeListener = function(type, listener) { 199 | var list, position, length, i; 200 | 201 | if (!isFunction(listener)) 202 | throw TypeError('listener must be a function'); 203 | 204 | if (!this._events || !this._events[type]) 205 | return this; 206 | 207 | list = this._events[type]; 208 | length = list.length; 209 | position = -1; 210 | 211 | if (list === listener || 212 | (isFunction(list.listener) && list.listener === listener)) { 213 | delete this._events[type]; 214 | if (this._events.removeListener) 215 | this.emit('removeListener', type, listener); 216 | 217 | } else if (isObject(list)) { 218 | for (i = length; i-- > 0;) { 219 | if (list[i] === listener || 220 | (list[i].listener && list[i].listener === listener)) { 221 | position = i; 222 | break; 223 | } 224 | } 225 | 226 | if (position < 0) 227 | return this; 228 | 229 | if (list.length === 1) { 230 | list.length = 0; 231 | delete this._events[type]; 232 | } else { 233 | list.splice(position, 1); 234 | } 235 | 236 | if (this._events.removeListener) 237 | this.emit('removeListener', type, listener); 238 | } 239 | 240 | return this; 241 | }; 242 | 243 | EventEmitter.prototype.removeAllListeners = function(type) { 244 | var key, listeners; 245 | 246 | if (!this._events) 247 | return this; 248 | 249 | // not listening for removeListener, no need to emit 250 | if (!this._events.removeListener) { 251 | if (arguments.length === 0) 252 | this._events = {}; 253 | else if (this._events[type]) 254 | delete this._events[type]; 255 | return this; 256 | } 257 | 258 | // emit removeListener for all listeners on all events 259 | if (arguments.length === 0) { 260 | for (key in this._events) { 261 | if (key === 'removeListener') continue; 262 | this.removeAllListeners(key); 263 | } 264 | this.removeAllListeners('removeListener'); 265 | this._events = {}; 266 | return this; 267 | } 268 | 269 | listeners = this._events[type]; 270 | 271 | if (isFunction(listeners)) { 272 | this.removeListener(type, listeners); 273 | } else { 274 | // LIFO order 275 | while (listeners.length) 276 | this.removeListener(type, listeners[listeners.length - 1]); 277 | } 278 | delete this._events[type]; 279 | 280 | return this; 281 | }; 282 | 283 | EventEmitter.prototype.listeners = function(type) { 284 | var ret; 285 | if (!this._events || !this._events[type]) 286 | ret = []; 287 | else if (isFunction(this._events[type])) 288 | ret = [this._events[type]]; 289 | else 290 | ret = this._events[type].slice(); 291 | return ret; 292 | }; 293 | 294 | EventEmitter.listenerCount = function(emitter, type) { 295 | var ret; 296 | if (!emitter._events || !emitter._events[type]) 297 | ret = 0; 298 | else if (isFunction(emitter._events[type])) 299 | ret = 1; 300 | else 301 | ret = emitter._events[type].length; 302 | return ret; 303 | }; 304 | 305 | function isFunction(arg) { 306 | return typeof arg === 'function'; 307 | } 308 | 309 | function isNumber(arg) { 310 | return typeof arg === 'number'; 311 | } 312 | 313 | function isObject(arg) { 314 | return typeof arg === 'object' && arg !== null; 315 | } 316 | 317 | function isUndefined(arg) { 318 | return arg === void 0; 319 | } 320 | 321 | },{}]},{},[1,2]); 322 | -------------------------------------------------------------------------------- /examples/basic/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple Browserify Test 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/browserify-shim/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /examples/browserify-shim/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | browserify: { 4 | dist: { 5 | src: ['src/app.js'], 6 | dest: 'build/target.js' 7 | // Note: The entire `browserify-shim` config is inside `package.json`. 8 | } 9 | } 10 | }); 11 | 12 | grunt.loadTasks('../../tasks'); 13 | }; -------------------------------------------------------------------------------- /examples/browserify-shim/lib/anotherCustomLib.js: -------------------------------------------------------------------------------- 1 | window.AnotherCustomLib = function(a, b) { 2 | return a+b; 3 | }; 4 | -------------------------------------------------------------------------------- /examples/browserify-shim/lib/customLib.js: -------------------------------------------------------------------------------- 1 | window.CustomLib = function(a, b) { 2 | return a*b; 3 | }; -------------------------------------------------------------------------------- /examples/browserify-shim/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "grunt": "~0.4.5" 4 | }, 5 | "dependencies": { 6 | "browserify": "^9.0.3", 7 | "browserify-shim": "^3.8.3" 8 | }, 9 | "browserify": { 10 | "transform": [ 11 | "browserify-shim" 12 | ] 13 | }, 14 | "browser": { 15 | "MyAliasLib": "./lib/customLib.js" 16 | }, 17 | "browserify-shim": { 18 | "MyAliasLib": "CustomLib", 19 | "./lib/anotherCustomLib.js": "AnotherCustomLib" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/browserify-shim/src/app.js: -------------------------------------------------------------------------------- 1 | var MyAliasLib = require('MyAliasLib'); 2 | var anotherCustomLib = require('../lib/anotherCustomLib') 3 | 4 | console.log(MyAliasLib, anotherCustomLib); -------------------------------------------------------------------------------- /examples/factor-bundle/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | grunt.initConfig({ 3 | browserify: { 4 | client: { 5 | src: [ 6 | './client/x.js', 7 | './client/y.js' 8 | ], 9 | dest: 'public/common.js', 10 | options: { 11 | plugin: [ 12 | ['factor-bundle', { outputs: [ 'public/x.js', 'public/y.js'] }] 13 | ], 14 | } 15 | } 16 | } 17 | }); 18 | 19 | grunt.loadTasks('../../tasks'); 20 | grunt.registerTask('default', ['browserify']); 21 | }; -------------------------------------------------------------------------------- /examples/factor-bundle/client/w.js: -------------------------------------------------------------------------------- 1 | module.exports = function (n) { return n * 50 } -------------------------------------------------------------------------------- /examples/factor-bundle/client/x.js: -------------------------------------------------------------------------------- 1 | var z = require('./z.js'); 2 | var w = require('./w.js'); 3 | console.log(z(5) * w(2)); -------------------------------------------------------------------------------- /examples/factor-bundle/client/y.js: -------------------------------------------------------------------------------- 1 | var z = require('./z.js'); 2 | console.log(z(2) + 111); -------------------------------------------------------------------------------- /examples/factor-bundle/client/z.js: -------------------------------------------------------------------------------- 1 | module.exports = function (n) { return n * 111 } -------------------------------------------------------------------------------- /examples/factor-bundle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "factor-bundle": "^2.3.3", 4 | "grunt": "~0.4.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/factor-bundle/public/common.js: -------------------------------------------------------------------------------- 1 | require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 2 | 3 | 4 | Simple Browserify Test 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/runner.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var path = require('path'); 3 | var resolve = require('resolve'); 4 | var glob = require('glob'); 5 | var browserifyInc = require('browserify-incremental'); 6 | 7 | module.exports = GruntBrowserifyRunner; 8 | 9 | function GruntBrowserifyRunner(options) { 10 | this.browserify = options.browserify; 11 | this.watchify = options.watchify; 12 | this.logger = options.logger; 13 | this.writer = options.writer; 14 | this.firstBuild = true; 15 | } 16 | 17 | // persist the created browserify instances between calls from grunt-contrib-watch 18 | // this is specifically for `options.cacheFile`, `browserify-incremental` 19 | var destinations = {} 20 | 21 | GruntBrowserifyRunner.prototype = _.create(GruntBrowserifyRunner.prototype, { 22 | run: function (files, destination, options, next) { 23 | var self = this; 24 | 25 | //set constructor options and instantiate 26 | var bOpts = _.cloneDeep(options.browserifyOptions) || {}; 27 | bOpts.entries = bOpts.entries || files; 28 | 29 | // watchify options 30 | var wOpts = options.watchifyOptions || {}; 31 | 32 | //determine watchify or browserify-incremental or browserify 33 | var b 34 | if (options.watch) { 35 | bOpts = _.extend({ cache: {}, packageCache: {} }, bOpts); 36 | b = this.watchify(this.browserify(bOpts), wOpts) 37 | } else if (options.cacheFile) { 38 | if (destinations[destination]) { 39 | return destinations[destination].call(this, next) 40 | } 41 | bOpts = Object.assign({}, bOpts, browserifyInc.args) 42 | b = this.browserify(bOpts) 43 | browserifyInc(b, {cacheFile: options.cacheFile}) 44 | 45 | b.on('log', function (msg) { 46 | self.logger.log.ok(msg.cyan) 47 | }) 48 | 49 | destinations[destination] = function (done) { 50 | doBundle(b, options, this.onBundleComplete(destination, options, done)) 51 | } 52 | } else { 53 | b = this.browserify(bOpts) 54 | } 55 | 56 | b.on('error', function (err) { 57 | self.logger.fail.warn(err); 58 | }); 59 | 60 | if(options.bundleOptions) { 61 | throw new Error('bundleOptions is no longer used. Move all option in browserifyOptions.'); 62 | } 63 | 64 | if(options.alias) { 65 | if(_.isPlainObject(options.alias)) { 66 | for(var alias in options.alias) { 67 | b.require(options.alias[alias], {expose: alias}); 68 | } 69 | } 70 | else { 71 | requireFiles(b, options.alias); 72 | } 73 | } 74 | 75 | if(options.require) { 76 | requireFiles(b, options.require); 77 | } 78 | 79 | if (options.exclude) { 80 | _.forEach(options.exclude, function (file) { 81 | runOptionForGlob(b, 'exclude', file); 82 | }); 83 | } 84 | 85 | if (options.ignore) { 86 | _.forEach(options.ignore, function (file) { 87 | runOptionForGlob(b, 'ignore', file); 88 | }); 89 | } 90 | 91 | if (options.external) { 92 | // allow externalizing of alias object 93 | if(_.isPlainObject(options.external)) { 94 | for(var id in options.external) { 95 | if (testForGlob(id)) { 96 | runOptionForGlob(b, 'external', id); 97 | } 98 | else { 99 | b.external(id); 100 | } 101 | } 102 | } 103 | else { 104 | _.forEach(options.external, function (id) { 105 | //allow externalizing of require lists 106 | if (id.match(':')) { 107 | id = id.split(':')[1]; 108 | } 109 | 110 | if (testForGlob(id)) { 111 | runOptionForGlob(b, 'external', id); 112 | } 113 | else { 114 | b.external(id); 115 | } 116 | }); 117 | } 118 | } 119 | 120 | if (options.transform) { 121 | _.forEach(options.transform, function (transformer) { 122 | b.transform(transformer); 123 | }); 124 | } 125 | 126 | if (options.plugin) { 127 | _.forEach(options.plugin, function (plugin) { 128 | b.plugin(plugin); 129 | }); 130 | } 131 | 132 | 133 | var destPath = this.createDestDir(destination); 134 | var keepAlive = this.keepAliveFn.bind(this, destination); 135 | var done = options.keepAlive? keepAlive : next; 136 | var bundleComplete = this.onBundleComplete(destination, options, done); 137 | 138 | if (options.watch) { 139 | var bundleUpdate = this.onBundleComplete(destination, options, keepAlive); 140 | b.on('update', function (ids) { 141 | ids.forEach(function (id) { 142 | self.logger.log.ok(id.cyan + ' changed, updating bundle.'); 143 | }); 144 | doBundle(b, options, bundleUpdate); 145 | }); 146 | } 147 | 148 | if (options.configure) { 149 | options.configure(b); 150 | } 151 | 152 | doBundle(b, options, bundleComplete); 153 | }, 154 | 155 | createDestDir: function (destination) { 156 | var destPath = path.dirname(path.resolve(destination)); 157 | if (!this.writer.exists(destPath)) { 158 | this.writer.mkdir(destPath); 159 | } 160 | return destPath; 161 | }, 162 | 163 | keepAliveFn: function (destination) { 164 | //this.logger.log.ok('Watchifying...'); 165 | }, 166 | 167 | onBundleComplete: function (destination, options, next) { 168 | var self = this; 169 | return function (err, buf) { 170 | if (err) { 171 | self.logger.log.error(err); 172 | if (self.firstBuild || !options.keepAlive) { 173 | self.logger.fail.warn('Error running grunt-browserify.'); 174 | } 175 | } 176 | else if (buf) { 177 | // prepend the banner 178 | if(options.banner) { 179 | buf = Buffer.concat([new Buffer(options.banner + '\n', 'utf8'), buf]); 180 | } 181 | 182 | self.logger.log.ok('Bundle ' + destination.cyan + ' created. ' + (options.keepAlive ? 'Watchifying...' : '')); 183 | self.writer.write(destination, buf); 184 | } 185 | 186 | self.firstBuild = false; 187 | next(); 188 | }; 189 | } 190 | }); 191 | 192 | function doBundle(browserifyInstance, opts, bundleComplete) { 193 | if (opts.preBundleCB) { 194 | opts.preBundleCB(browserifyInstance); 195 | } 196 | 197 | browserifyInstance.bundle(function (err, buf) { 198 | if (opts.postBundleCB) { 199 | opts.postBundleCB(err, buf, bundleComplete); 200 | } 201 | else { 202 | bundleComplete(err, buf); 203 | } 204 | }) 205 | // Need this to ensure it works on MacOS 206 | // See 'important' note here: https://github.com/substack/watchify#var-w--watchifyb-opts 207 | .on('data', function() {}); 208 | } 209 | 210 | function testForGlob(id) { 211 | return (/\*/.test(id)); 212 | } 213 | 214 | function runOptionForGlob(browserifyInstance, method, pattern) { 215 | var files = glob.sync(pattern); 216 | if (!files || files.length < 1) { 217 | //it's not a glob, it's a file / module path 218 | files = [pattern]; 219 | } 220 | files.forEach(function (f) { 221 | browserifyInstance[method].call(browserifyInstance, f); 222 | }); 223 | } 224 | 225 | function requireFiles(b, requiredFiles) { 226 | _.forEach(requiredFiles, function (file) { 227 | var filePath, opts; 228 | if (Array.isArray(file)) { 229 | filePath = file[0]; 230 | opts = file[1]; 231 | } 232 | else { 233 | var filePair = file.split(':'); 234 | filePath = filePair[0]; 235 | opts = { 236 | expose: filePair.length === 1 ? filePair[0] : filePair[1] 237 | }; 238 | } 239 | b.require(filePath, opts); 240 | }); 241 | } 242 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-browserify", 3 | "description": "Grunt task for node-browserify", 4 | "version": "6.0.0", 5 | "homepage": "https://github.com/jmreidy/grunt-browserify", 6 | "contributors": [ 7 | { 8 | "name": "Justin Reidy", 9 | "email": "jmreidy@rzrsharp.net" 10 | }, 11 | { 12 | "name": "Tommy Leunen", 13 | "email": "tommy.leunen@gmail.com" 14 | } 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/jmreidy/grunt-browserify.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/jmreidy/grunt-browserify/issues" 22 | }, 23 | "files": [ 24 | "lib/", 25 | "tasks/" 26 | ], 27 | "license": "MIT", 28 | "main": "tasks/browserify.js", 29 | "engines": { 30 | "node": ">= 8.10.x" 31 | }, 32 | "scripts": { 33 | "test": "mocha -R spec --timeout 5000" 34 | }, 35 | "dependencies": { 36 | "async": "^2.5.0", 37 | "browserify": "^17.0.0", 38 | "browserify-incremental": "^3.1.1", 39 | "glob": "^7.1.2", 40 | "lodash": "^4.17.4", 41 | "resolve": "^1.1.6", 42 | "watchify": "^4.0.0" 43 | }, 44 | "devDependencies": { 45 | "grunt": "^1.0.1", 46 | "grunt-cli": "^1.2.0", 47 | "grunt-contrib-clean": "^1.1.0", 48 | "grunt-contrib-jshint": "^1.1.0", 49 | "mocha": "^3.5.0", 50 | "sinon": "^3.2.0" 51 | }, 52 | "keywords": [ 53 | "gruntplugin", 54 | "browserify", 55 | "grunt" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /tasks/browserify.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-browserify 3 | * https://github.com/jmreidy/grunt-browserify 4 | * 5 | * Copyright (c) 2013 Justin Reidy 6 | * Licensed under the MIT license. 7 | */ 8 | var Runner = require('../lib/runner'); 9 | var path = require('path'); 10 | var async = require('async'); 11 | var browserify = require('browserify'); 12 | var watchify = require('watchify'); 13 | 14 | module.exports = Task; 15 | 16 | function Task (grunt) { 17 | grunt.registerMultiTask('browserify', 'Grunt task for browserify.', function () { 18 | 19 | // set default options 20 | var options = this.options({ 21 | banner: '' 22 | }); 23 | 24 | async.each(this.files, function (file, next) { 25 | Task.runTask(grunt, options, file, next); 26 | }, this.async()); 27 | }); 28 | } 29 | 30 | Task.runTask = function (grunt, options, file, next) { 31 | var runner = new Runner({ 32 | writer: grunt.file, 33 | logger: grunt, 34 | browserify: browserify, 35 | watchify: watchify 36 | }); 37 | var files = grunt.file.expand({filter: 'isFile'}, file.src).map(function (f) { 38 | return path.resolve(f); 39 | }); 40 | runner.run(files, file.dest, options, next); 41 | }; 42 | -------------------------------------------------------------------------------- /test/browserify.test.js: -------------------------------------------------------------------------------- 1 | /* global beforeEach, describe, context, it */ 2 | var Sinon = require('sinon'); 3 | var assert = require('assert'); 4 | var path = require('path'); 5 | var browserify = require('browserify'); 6 | var Runner = require('../lib/runner'); 7 | var _ = require('lodash'); 8 | 9 | describe('grunt-browserify-runner', function () { 10 | var dest = 'path/To/Dest'; 11 | 12 | it('instatiates browserify', function (done) { 13 | var b = spyBrowserify(); 14 | var runner = createRunner(b); 15 | runner.run([], dest, {}, function () { 16 | assert.equal(b.callCount, 1); 17 | done(); 18 | }); 19 | }); 20 | 21 | it('assigns source files as bundle entries', function (done) { 22 | var b = spyBrowserify(); 23 | var files = [path.resolve('./package.json')]; 24 | var runner = createRunner(b); 25 | runner.run(files, dest, {}, function () { 26 | assert.ok(b.calledWith({entries: files})); 27 | done(); 28 | }); 29 | }); 30 | 31 | it('invokes the bundler', function (done) { 32 | var b = stubBrowserify('bundle'); 33 | var runner = createRunner(b); 34 | runner.run([], dest, {}, function () { 35 | assert.equal(b().bundle.callCount, 1); 36 | done(); 37 | }); 38 | }); 39 | 40 | it('writes the bundle to the provided dest', function (done) { 41 | var b = spyBrowserify(); 42 | var runner = createRunner(b); 43 | runner.run([], dest, {}, function () { 44 | assert.ok(runner.writer.write.calledWith(dest)); 45 | done(); 46 | }); 47 | }); 48 | 49 | 50 | context('when passing option of watch:true', function () { 51 | var baseOpts = {watch:true}; 52 | it('invokes watchify instead of browserify', function (done) { 53 | var watchify = spyWatchify(); 54 | var runner = createRunner(spyBrowserify(), watchify); 55 | runner.run([], dest, baseOpts, function () { 56 | assert.equal(watchify.callCount, 1); 57 | done(); 58 | }); 59 | }); 60 | }); 61 | 62 | context('when passing option of watch:true with hash of watchifyOptions', function () { 63 | var browserify = spyBrowserify(); 64 | var watchifyOpts = {chokidar: {usePolling: true}}; 65 | var baseOpts = {watch:true, watchifyOptions: watchifyOpts}; 66 | it('invokes watchify instead of browserify', function (done) { 67 | var watchify = spyWatchify(); 68 | var runner = createRunner(browserify, watchify); 69 | runner.run([], dest, baseOpts, function () { 70 | assert.equal(watchify.callCount, 1); 71 | assert.ok(watchify.calledWithMatch(browserify, watchifyOpts)); 72 | done(); 73 | }); 74 | }); 75 | }); 76 | 77 | describe('when passing hash of browserifyOptions', function () { 78 | it('instantiates browserify with those options', function (done) { 79 | var browserify = spyBrowserify(); 80 | var bOpts = {foo: 'bar'}; 81 | var runner = createRunner(browserify); 82 | runner.run([], dest, {browserifyOptions: bOpts}, function () { 83 | assert.ok(browserify.calledWithMatch(bOpts)); 84 | done(); 85 | }); 86 | }); 87 | }); 88 | 89 | describe('when passing option of require', function () { 90 | it('requires each item in the array', function (done) { 91 | var b = stubBrowserify('require'); 92 | var requireList = ['./package.json']; 93 | var runner = createRunner(b); 94 | runner.run([], dest, {require: requireList}, function () { 95 | assert.ok(b().require.calledWith(requireList[0])); 96 | done(); 97 | }); 98 | }); 99 | 100 | context('if an options hash is provided', function () { 101 | it('passes the options directly to require()', function (done) { 102 | var b = stubBrowserify('require'); 103 | var requireList = [['./package.json', {entry: true, expose: 'expose-name'}]]; 104 | var runner = createRunner(b); 105 | runner.run([], dest, {require: requireList}, function () { 106 | var args = b().require.getCall(0).args; 107 | assert.deepEqual(args, requireList[0]); 108 | done(); 109 | }); 110 | }); 111 | }); 112 | 113 | }); 114 | 115 | describe('when passing option of exclude', function () { 116 | it('excludes each item in the array', function (done) { 117 | var b = stubBrowserify('exclude'); 118 | var excludeList = ['./package.json', 'lodash']; 119 | var runner = createRunner(b); 120 | runner.run([], dest, {exclude: excludeList}, function () { 121 | assert.ok(b().exclude.calledWith(excludeList[0])); 122 | assert.ok(b().exclude.calledWith(excludeList[1])); 123 | done(); 124 | }); 125 | }); 126 | it('excludes globbed file results', function (done) { 127 | var b = stubBrowserify('exclude'); 128 | var excludeList = ['./*.json']; 129 | var files = ['./package.json']; 130 | var runner = createRunner(b); 131 | runner.run([], dest, {exclude: excludeList}, function () { 132 | assert.ok(b().exclude.calledWith(files[0])); 133 | done(); 134 | }); 135 | }); 136 | }); 137 | 138 | describe('when passing option of ignore', function () { 139 | it('ignores the resolved filename of each item in the array', function (done) { 140 | var b = stubBrowserify('ignore'); 141 | var ignoreList = ['./package.json', 'os']; 142 | var runner = createRunner(b); 143 | runner.run([], dest, {ignore: ignoreList}, function () { 144 | assert.ok(b().ignore.calledWith(ignoreList[0])); 145 | assert.ok(b().ignore.calledWith(ignoreList[1])); 146 | done(); 147 | }); 148 | }); 149 | it('ignores globbed file results', function (done) { 150 | var b = stubBrowserify('ignore'); 151 | var ignoreList = ['./*.json']; 152 | var files = ['./package.json']; 153 | var runner = createRunner(b); 154 | runner.run([], dest, {ignore: ignoreList}, function () { 155 | assert.ok(b().ignore.calledWith(files[0])); 156 | done(); 157 | }); 158 | }); 159 | }); 160 | 161 | describe('when passing option of alias', function () { 162 | var b, runner; 163 | var pathAliasList = ['./package.json:alias']; 164 | var files = _.map(pathAliasList, function (file) { 165 | return file.split(':')[0]; 166 | }); 167 | var moduleAliasList = ['path:pathAlias']; 168 | var modules = _.map(moduleAliasList, function(module) { 169 | return module.split(':')[0]; 170 | }); 171 | var aliasList = pathAliasList.concat(moduleAliasList); 172 | var aliases = files.concat(modules); 173 | 174 | var aliasObj = { 175 | 'alias': './package.json', 176 | 'pathAlias': 'path' 177 | }; 178 | 179 | beforeEach(function () { 180 | b = stubBrowserify('require'); 181 | runner = createRunner(b); 182 | }); 183 | 184 | it('tries to require the filename of each item in the array', function (done) { 185 | runner.run([], dest, {alias: pathAliasList}, function () { 186 | assert.ok(b().require.calledWith(files[0])); 187 | done(); 188 | }); 189 | }); 190 | 191 | it('tries to require the module name of each item in the array', function (done) { 192 | runner.run([], dest, {alias: moduleAliasList}, function () { 193 | assert.ok(b().require.calledWith(modules[0])); 194 | done(); 195 | }); 196 | }); 197 | 198 | it('specifies the provided alias for each item in the array', function (done) { 199 | runner.run([], dest, {alias: aliasList}, function () { 200 | assert.ok(b().require.calledWith(aliases[0], {expose: 'alias'})); 201 | assert.ok(b().require.calledWith(aliases[1], {expose: 'pathAlias'})); 202 | done(); 203 | }); 204 | }); 205 | 206 | it('specifies the provided alias for each item in the object', function (done) { 207 | console.log(Object.keys(aliasObj)); 208 | runner.run([], dest, {alias: aliasObj}, function () { 209 | assert.ok(b().require.calledWith(aliasObj['alias'], {expose: 'alias'})); 210 | assert.ok(b().require.calledWith(aliasObj['pathAlias'], {expose: 'pathAlias'})); 211 | done(); 212 | }); 213 | }); 214 | }); 215 | 216 | describe('when passing option of external', function () { 217 | it('marks each array element as external', function (done) { 218 | var b = stubBrowserify('external'); 219 | var externalList = ['./package.json', 'foobar']; 220 | var runner = createRunner(b); 221 | runner.run([], dest, {external: externalList}, function () { 222 | assert.ok(b().external.calledWith(externalList[0])); 223 | assert.ok(b().external.calledWith(externalList[1])); 224 | done(); 225 | }); 226 | }); 227 | 228 | context('when provided with an alias array', function () { 229 | it('marks the aliases as external', function (done) { 230 | var b = stubBrowserify('external'); 231 | var runner = createRunner(b); 232 | var aliasList = ['./package.json:alias']; 233 | runner.run([], dest, {external: aliasList}, function () { 234 | assert.ok(b().external.calledWith('alias')); 235 | done(); 236 | }); 237 | }); 238 | }); 239 | 240 | context('when provided with a glob', function () { 241 | it('marks each glob result as external', function (done) { 242 | var b = stubBrowserify('external'); 243 | var externalList = ['./*.json', 'foobar']; 244 | var runner = createRunner(b); 245 | runner.run([], dest, {external: externalList}, function () { 246 | assert.ok(b().external.calledWith('./package.json')); 247 | assert.ok(b().external.calledWith(externalList[1])); 248 | done(); 249 | }); 250 | }); 251 | }); 252 | }); 253 | 254 | describe('when passing option of transform', function () { 255 | var b, runner; 256 | 257 | beforeEach(function () { 258 | b = stubBrowserify('transform'); 259 | runner = createRunner(b); 260 | }); 261 | 262 | it('invokes each array element as a transform', function (done) { 263 | var transforms = [function () {}]; 264 | runner.run([], dest, {transform: transforms}, function () { 265 | assert.ok(b().transform.calledWith(transforms[0])); 266 | done(); 267 | }); 268 | }); 269 | context('if an options hash is provided', function () { 270 | it('passes the options hash along with the transform fn', function (done) { 271 | var transforms = [[function () {}, {}]]; 272 | runner.run([], dest, {transform: transforms}, function () { 273 | assert.ok(b().transform.calledWith(transforms[0])); 274 | done(); 275 | }); 276 | }); 277 | }); 278 | }); 279 | 280 | describe('when passing option of configure', function () { 281 | it('calls the provided callback before bundling', function (done) { 282 | var cb = Sinon.stub(); 283 | var b = stubBrowserify('bundle'); 284 | var runner = createRunner(b); 285 | runner.run([], dest, {configure: cb}, function () { 286 | assert.ok(cb.calledOnce); 287 | assert.ok(cb.calledBefore(b().bundle)); 288 | done(); 289 | }); 290 | }); 291 | }); 292 | 293 | describe('when passing option of preBundleCB', function () { 294 | it('calls the provided callback before bundling', function (done) { 295 | var cb = Sinon.stub(); 296 | var b = stubBrowserify('bundle'); 297 | var runner = createRunner(b); 298 | runner.run([], dest, {preBundleCB: cb}, function () { 299 | assert.ok(cb.calledOnce); 300 | assert.ok(cb.calledBefore(b().bundle)); 301 | done(); 302 | }); 303 | }); 304 | }); 305 | 306 | describe('when passing option of postBundleCB', function () { 307 | it('calls the provided callback after bundling', function (done) { 308 | var cb = Sinon.stub().yields(); 309 | var b = stubBrowserify('bundle'); 310 | var runner = createRunner(b); 311 | runner.run([], dest, {postBundleCB: cb}, function () { 312 | assert.ok(cb.calledOnce); 313 | assert.ok(cb.calledAfter(b().bundle)); 314 | done(); 315 | }); 316 | }); 317 | }); 318 | 319 | describe('when passing option of plugin', function () { 320 | it('registers the plugin', function (done) { 321 | var b = stubBrowserify('plugin'); 322 | var runner = createRunner(b); 323 | var plugin = [function () {}]; 324 | runner.run([], dest, {plugin: plugin}, function () { 325 | assert.ok(b().plugin.calledWith(plugin[0])); 326 | done(); 327 | }); 328 | }); 329 | context('and when passing plugin options', function () { 330 | it('registers the plugin options', function (done) { 331 | var b = stubBrowserify('plugin'); 332 | var runner = createRunner(b); 333 | var plugin = [[function () {}, {}]]; 334 | runner.run([], dest, {plugin: plugin}, function () { 335 | assert.ok(b().plugin.calledWith(plugin[0])); 336 | done(); 337 | }); 338 | }); 339 | }); 340 | }); 341 | 342 | }); 343 | 344 | function createRunner(browserify, watchify) { 345 | return new Runner({ 346 | browserify: browserify, 347 | watchify: watchify, 348 | logger: stubLogger(), 349 | writer: stubGruntFile() 350 | }); 351 | } 352 | 353 | function stubGruntFile() { 354 | return Sinon.stub({ 355 | exists: function () { 356 | 357 | }, 358 | write: function () { 359 | 360 | }, 361 | mkdir: function () { 362 | 363 | } 364 | }); 365 | } 366 | 367 | function stubLogger() { 368 | return Sinon.stub({ 369 | log: { 370 | ok: function (msg) { 371 | //console.log(msg); 372 | }, 373 | error: function (msg) { 374 | //console.log(msg); 375 | } 376 | }, 377 | fail: { 378 | warn: function (msg) { 379 | //console.log(msg); 380 | } 381 | }, 382 | error: function (msg) { 383 | //console.log(msg); 384 | } 385 | }); 386 | } 387 | 388 | function spyBrowserify() { 389 | return Sinon.spy(browserify); 390 | } 391 | 392 | function stubBrowserify(spyMethod) { 393 | var b = browserify(); 394 | if (spyMethod) { 395 | Sinon.spy(b, spyMethod); 396 | } 397 | return Sinon.stub().returns(b); 398 | } 399 | 400 | function spyWatchify() { 401 | return Sinon.spy(require('watchify')); 402 | } 403 | -------------------------------------------------------------------------------- /test/fixtures/alias/entry.js: -------------------------------------------------------------------------------- 1 | required({ 2 | alias: require('alias'), 3 | }); -------------------------------------------------------------------------------- /test/fixtures/alias/toBeAliased.js: -------------------------------------------------------------------------------- 1 | module.exports = function (test) { 2 | return 'this file will be aliased'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/aliasMappings/entry.js: -------------------------------------------------------------------------------- 1 | required({ 2 | root: require('tmp/shortcut/root'), 3 | foo: require('tmp/shortcut/foo'), 4 | foobar: require('tmp/shortcut/foobar'), 5 | otherbar: require('tmp/other/bar/foobar') 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixtures/aliasMappings/foo/bar/foobar.js: -------------------------------------------------------------------------------- 1 | module.exports = 'file in foo/bar dir in aliased directory'; 2 | -------------------------------------------------------------------------------- /test/fixtures/aliasMappings/foo/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = 'file in foo dir in aliased directory'; 2 | -------------------------------------------------------------------------------- /test/fixtures/aliasMappings/root.js: -------------------------------------------------------------------------------- 1 | module.exports = 'file at root of aliased directory'; 2 | -------------------------------------------------------------------------------- /test/fixtures/basic/a.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return 'test'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/basic/b.js: -------------------------------------------------------------------------------- 1 | var a = require('./a'); 2 | 3 | module.exports = function (test) { 4 | return 'b.js'; 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/basic/basic.js: -------------------------------------------------------------------------------- 1 | required({ 2 | a: require('./a'), 3 | b: require('./b') 4 | }); -------------------------------------------------------------------------------- /test/fixtures/extension/a.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return 'test'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/extension/b.fjs: -------------------------------------------------------------------------------- 1 | var a = require('./a.js'); 2 | 3 | module.exports = function (test) { 4 | return 'b.js'; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /test/fixtures/extension/extension.js: -------------------------------------------------------------------------------- 1 | required({ 2 | a: require('./a'), 3 | b: require('./b') 4 | }); 5 | -------------------------------------------------------------------------------- /test/fixtures/external-dir/a.js: -------------------------------------------------------------------------------- 1 | var b = require('./b'); 2 | 3 | module.exports = function (test) { 4 | return 'a.js'; 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/external-dir/b/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return 'this should be a common module require'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/external-dir/entry.js: -------------------------------------------------------------------------------- 1 | required({ 2 | a: require('./a') 3 | }); -------------------------------------------------------------------------------- /test/fixtures/external/a.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return 'this should be a common require'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/external/alias.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return 'vendor/alias should be a common require'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/external/b.js: -------------------------------------------------------------------------------- 1 | var a = require('./a'); 2 | 3 | module.exports = function (test) { 4 | return 'b.js'; 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/external/entry.js: -------------------------------------------------------------------------------- 1 | required({ 2 | a: require('./a'), 3 | b: require('./b'), 4 | events: require('events'), 5 | alias: require('vendor/alias') 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixtures/externalize/a.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return 'this should be a common require'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/externalize/b.js: -------------------------------------------------------------------------------- 1 | var a = require('./a'); 2 | 3 | module.exports = function (test) { 4 | return 'b.js'; 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/externalize/entry.js: -------------------------------------------------------------------------------- 1 | required({ 2 | a: require('./a'), 3 | b: require('./b') 4 | }); -------------------------------------------------------------------------------- /test/fixtures/ignore/a.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return 'test'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/ignore/b.js: -------------------------------------------------------------------------------- 1 | var a = require('./a'); 2 | 3 | module.exports = function (test) { 4 | return 'b.js'; 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/ignore/entry.js: -------------------------------------------------------------------------------- 1 | required({ 2 | a: require('./a'), 3 | b: require('./b'), 4 | ignore: require('./ignore') 5 | }); -------------------------------------------------------------------------------- /test/fixtures/ignore/ignore.js: -------------------------------------------------------------------------------- 1 | var a = require('./a'); 2 | var b = require('./b'); 3 | 4 | module.exports = function () { 5 | return 'this should not be included'; 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/ignore/os.js: -------------------------------------------------------------------------------- 1 | var os = require('os'); 2 | 3 | module.exports = function () { 4 | return 'requiring this module should not fail if "os" is ignored'; 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/noParse/a.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return 'test'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/noParse/noParse.js: -------------------------------------------------------------------------------- 1 | required({ 2 | a: require('./a') 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/paths/paths.js: -------------------------------------------------------------------------------- 1 | required({ 2 | a: require('basic/a'), 3 | b: require('b') 4 | }); -------------------------------------------------------------------------------- /test/fixtures/shim/a.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return 'test'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/shim/shim.js: -------------------------------------------------------------------------------- 1 | required({ 2 | a: require('./a'), 3 | jquery: require('shimmedJQ') 4 | }); 5 | -------------------------------------------------------------------------------- /test/fixtures/watch/a.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return 'test'; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/watch/b.js: -------------------------------------------------------------------------------- 1 | var a = require('./a'); 2 | 3 | module.exports = function (test) { 4 | return 'b.js'; 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/watch/basic.js: -------------------------------------------------------------------------------- 1 | required({ 2 | a: require('./a'), 3 | b: require('./b') 4 | }); --------------------------------------------------------------------------------