@@ -11,9 +11,13 @@ const log = require('debug')('cypress:browserify')
1111
1212const bundles = { }
1313
14+ // by default, we transform JavaScript (up to anything at stage-4), JSX,
15+ // CoffeeScript, and CJSX (CoffeeScript + JSX)
1416const defaults = {
1517 extensions : [ '.js' , '.jsx' , '.coffee' , '.cjsx' ] ,
1618 watchOptions : {
19+ // ignore watching the following or the user's system can get bogged down
20+ // by watchers
1721 ignoreWatch : [
1822 '**/.git/**' ,
1923 '**/.nyc_output/**' ,
@@ -33,31 +37,59 @@ const defaults = {
3337 options : {
3438 ast : false ,
3539 babelrc : false ,
40+ // irons out differents between ES6 modules and node exports
3641 plugins : [ 'babel-plugin-add-module-exports' ] . map ( require . resolve ) ,
42+ // babel-preset-env supports any JS that's stage-4, meaning it's
43+ // completely finalized in the ECMA standard
3744 presets : [ 'babel-preset-env' , 'babel-preset-react' ] . map ( require . resolve ) ,
3845 } ,
3946 } ,
4047 ] ,
4148}
4249
50+ // export a function that returns another function, making it easy for users
51+ // to configure like so:
52+ //
53+ // register('on:spec:file:preprocessor', browserify(config, userOptions))
54+ //
4355module . exports = ( config , userOptions = { } ) => {
4456 log ( 'received user options' , userOptions )
4557
4658 if ( ! config || typeof config . isTextTerminal !== 'boolean' ) {
4759 throw new Error ( `Cypress Browserify Preprocessor must be called with the Cypress config as its first argument. You passed: ${ JSON . stringify ( config , null , 2 ) } ` )
4860 }
4961
62+ // allow user to override default options
5063 const options = Object . assign ( { } , defaults , userOptions )
5164
65+ // we return function that accepts the arguments provided by
66+ // the event 'on:spec:file:preprocessor'
67+ //
68+ // this function will get called for the support file when a project is loaded
69+ // (if the support file is not disabled)
70+ // it will also get calledfor a spec file when that spec is requested by
71+ // the Cypress runner
72+ //
73+ // when running in the GUI, it will likely get called multiple times
74+ // with the same filePath, as the user could re-run the tests, causing
75+ // the supported file and spec file to be requested again
5276 return ( filePath , util ) => {
5377 log ( 'get' , filePath )
5478
79+ // since this function can get called multiple times with the same
80+ // filePath, we return the cached bundle promise if we already have one
81+ // since we don't want or need to re-initiate browserify/watchify for it
5582 if ( bundles [ filePath ] ) {
5683 log ( `already have bundle for ${ filePath } ` )
5784 return bundles [ filePath ]
5885 }
5986
87+ // if we're in a text terminal, this is a one-time run, probably in CI
88+ // so we don't need to watch
6089 const shouldWatch = ! config . isTextTerminal
90+ // util.getOutputPath returns a path alongside Cypress's other app data
91+ // files so we don't have to worry about where to put the bundled
92+ // file on disk
6193 const outputPath = util . getOutputPath ( filePath )
6294
6395 log ( `input: ${ filePath } ` )
@@ -75,26 +107,34 @@ module.exports = (config, userOptions = {}) => {
75107 bundler . plugin ( watchify , options . watchOptions || { } )
76108 }
77109
110+ // yield the bundle if onBundle is specified so the user can modify it
111+ // as need via `bundle.external()`, `bundle.plugin()`, etc
78112 const onBundle = options . onBundle
79113 if ( typeof onBundle === 'function' ) {
80114 onBundle ( bundler )
81115 }
82116
117+ // transforms are part of the options so that users can easily override
118+ // the options of the default cjsxify and babelify tranforms
83119 const transforms = options . transforms
84120 if ( Object . prototype . toString . call ( transforms ) === '[object Array]' ) {
85121 transforms . forEach ( ( transform ) => {
86122 bundler . transform ( transform . transform , transform . options )
87123 } )
88124 }
89125
126+ // this kicks off the bundling and wraps it up in a promise. the promise
127+ // is what is ultimately returned from this function
128+ // it resolves with the outputPath so Cypress knows where to serve
129+ // the file from
90130 const bundle = ( ) => {
91131 return new Promise ( ( resolve , reject ) => {
92132 log ( `making bundle ${ outputPath } ` )
93133
94134 const onError = ( err ) => {
95135 err . filePath = filePath
96- //// backup the original stack before its
97- //// potentially modified from bluebird
136+ // backup the original stack before its
137+ // potentially modified from bluebird
98138 err . originalStack = err . stack
99139 log ( `errored bundling ${ outputPath } ` , err )
100140 reject ( err )
@@ -114,8 +154,12 @@ module.exports = (config, userOptions = {}) => {
114154 } )
115155 }
116156
157+ // when we're notified of an update via watchify, we call `util.fileUpdated`
158+ // to let Cypress know to re-run the spec
117159 bundler . on ( 'update' , ( ) => {
118160 log ( `update ${ filePath } ` )
161+ // we overwrite the cached bundle promise, so on subsequent invocations
162+ // it gets the latest bundle
119163 bundles [ filePath ] = bundle ( ) . tap ( ( ) => {
120164 log ( `- update finished for ${ filePath } ` )
121165 util . fileUpdated ( filePath )
@@ -124,8 +168,12 @@ module.exports = (config, userOptions = {}) => {
124168
125169 const bundlePromise = fs . ensureDirAsync ( path . dirname ( outputPath ) ) . then ( bundle )
126170
171+ // cache the bundle promise, so it can be returned if this function
172+ // is invoked again with the same filePath
127173 bundles [ filePath ] = bundlePromise
128174
175+ // when the spec or project is closed, we need to clean up the cached
176+ // bundle promise and stop the watcher via `bundler.close()`
129177 util . onClose ( ( ) => {
130178 log ( `close ${ filePath } ` )
131179 delete bundles [ filePath ]
@@ -134,6 +182,8 @@ module.exports = (config, userOptions = {}) => {
134182 }
135183 } )
136184
185+ // return the promise, which will resolve with the outputPath or reject
186+ // with any error encountered
137187 return bundlePromise
138188 }
139189}
0 commit comments