js-build-tools 5.4.0 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.js CHANGED
@@ -5,19 +5,28 @@ Object.defineProperty(exports, '__esModule', {
5
5
  value: true
6
6
  })
7
7
  exports.bundle = void 0
8
+ require('core-js/modules/esnext.iterator.constructor.js')
9
+ require('core-js/modules/esnext.iterator.map.js')
8
10
  const _browserify = _interopRequireDefault(require('browserify'))
9
11
  const _gulp = require('gulp')
12
+ const _mergeStream = _interopRequireDefault(require('merge-stream'))
10
13
  const gulpConfig = _interopRequireWildcard(require('../gulp.config.js'))
11
14
  const _vinylSourceStream = _interopRequireDefault(require('vinyl-source-stream'))
15
+ const _getBundleConfigs = require('./utilities/getBundleConfigs.js')
12
16
  function _interopRequireWildcard (e, t) { if (typeof WeakMap === 'function') var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; let o; let i; const f = { __proto__: null, default: e }; if (e === null || typeof e !== 'object' && typeof e !== 'function') return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f) } for (const t in e) t !== 'default' && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f })(e, t) }
13
17
  function _interopRequireDefault (e) { return e && e.__esModule ? e : { default: e } }
14
18
  /**
15
- * Starting at the distribution entry point, bundle all the files into a single file and store them in the specified output directory.
19
+ * Starting at each bundle's own entry point (dist.main for the primary bundle, or its own
20
+ * override from browser.bundles), bundle all the reachable files into a single file per bundle and
21
+ * store them in the specified output directory. A project with no browser.bundles entries gets
22
+ * exactly the one bundle it always did; each additional entry produces its own separate bundle
23
+ * (for example, a render-only variant built from a different entry point than the full bundle).
16
24
  *
17
- * browser.name does double duty: it names the output file (browser.name.js) and, via browserify's `standalone`
18
- * option, is also the name the bundle's exports are exposed under - as a global when loaded with a bare <script>
19
- * tag, or as the module when required via CommonJS/AMD. Without this, a bundle built from a real entry point (one
20
- * that actually exports something) builds successfully but exposes nothing a consumer can reach.
25
+ * A bundle's name does double duty: it names the output file (name.js) and, via browserify's
26
+ * `standalone` option, is also the name its exports are exposed under - as a global when loaded
27
+ * with a bare <script> tag, or as the module when required via CommonJS/AMD. Without this, a
28
+ * bundle built from a real entry point (one that actually exports something) builds successfully
29
+ * but exposes nothing a consumer can reach.
21
30
  *
22
31
  * Two escape hatches from browser.config.json keep an unwanted dependency (or a dependency's dependency) out of the
23
32
  * bundle: 'ignore' replaces it with an empty object (safe when your code never actually calls into it - for example
@@ -26,18 +35,25 @@ function _interopRequireDefault (e) { return e && e.__esModule ? e : { default:
26
35
  * @memberOf module:js-build-tools
27
36
  * @returns {stream.Stream}
28
37
  */
29
- const bundle = () => {
30
- const bundler = (0, _browserify.default)(gulpConfig.get('dist.main'), {
31
- standalone: gulpConfig.get('browser.name')
38
+ const bundle = () => (0, _mergeStream.default)((0, _getBundleConfigs.getBundleConfigs)().map(({
39
+ name,
40
+ main,
41
+ ignore,
42
+ exclude
43
+ }) => {
44
+ const bundler = (0, _browserify.default)(main, {
45
+ standalone: name
32
46
  })
33
- const ignore = gulpConfig.get('browser.ignore')
34
- const exclude = gulpConfig.get('browser.exclude')
35
47
  if (ignore && ignore.length) {
36
48
  bundler.ignore(ignore)
37
49
  }
38
50
  if (exclude && exclude.length) {
39
51
  bundler.exclude(exclude)
40
52
  }
41
- return bundler.bundle().pipe((0, _vinylSourceStream.default)(`${gulpConfig.get('browser.name')}.js`)).pipe((0, _gulp.dest)(gulpConfig.get('browser.to')))
42
- }
53
+ return bundler.bundle().pipe((0, _vinylSourceStream.default)(`${name}.js`))
54
+ })
55
+ // One shared dest() for every bundle, not one per bundle - vinyl-fs's dest() races against
56
+ // itself (a real "write after end" failure) when two separate dest() streams target the same
57
+ // directory concurrently, which every additional bundle's own dest() call otherwise would.
58
+ ).pipe((0, _gulp.dest)(gulpConfig.get('browser.to')))
43
59
  exports.bundle = bundle
@@ -1 +1 @@
1
- "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.bundle=void 0;var _browserify=_interopRequireDefault(require("browserify")),_gulp=require("gulp"),gulpConfig=_interopRequireWildcard(require("../gulp.config.js")),_vinylSourceStream=_interopRequireDefault(require("vinyl-source-stream"));function _interopRequireWildcard(e,r){if("function"==typeof WeakMap)var t=new WeakMap,n=new WeakMap;return(_interopRequireWildcard=function(e,r){if(!r&&e&&e.__esModule)return e;var u,o,i={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return i;if(u=r?n:t){if(u.has(e))return u.get(e);u.set(e,i)}for(const r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((o=(u=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,r))&&(o.get||o.set)?u(i,r,o):i[r]=e[r]);return i})(e,r)}function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const bundle=()=>{const e=(0,_browserify.default)(gulpConfig.get("dist.main"),{standalone:gulpConfig.get("browser.name")}),r=gulpConfig.get("browser.ignore"),t=gulpConfig.get("browser.exclude");return r&&r.length&&e.ignore(r),t&&t.length&&e.exclude(t),e.bundle().pipe((0,_vinylSourceStream.default)(`${gulpConfig.get("browser.name")}.js`)).pipe((0,_gulp.dest)(gulpConfig.get("browser.to")))};exports.bundle=bundle;
1
+ "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.bundle=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js");var _browserify=_interopRequireDefault(require("browserify")),_gulp=require("gulp"),_mergeStream=_interopRequireDefault(require("merge-stream")),gulpConfig=_interopRequireWildcard(require("../gulp.config.js")),_vinylSourceStream=_interopRequireDefault(require("vinyl-source-stream")),_getBundleConfigs=require("./utilities/getBundleConfigs.js");function _interopRequireWildcard(e,r){if("function"==typeof WeakMap)var t=new WeakMap,u=new WeakMap;return(_interopRequireWildcard=function(e,r){if(!r&&e&&e.__esModule)return e;var n,i,o={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return o;if(n=r?u:t){if(n.has(e))return n.get(e);n.set(e,o)}for(const r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((i=(n=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,r))&&(i.get||i.set)?n(o,r,i):o[r]=e[r]);return o})(e,r)}function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const bundle=()=>(0,_mergeStream.default)((0,_getBundleConfigs.getBundleConfigs)().map(({name:e,main:r,ignore:t,exclude:u})=>{const n=(0,_browserify.default)(r,{standalone:e});return t&&t.length&&n.ignore(t),u&&u.length&&n.exclude(u),n.bundle().pipe((0,_vinylSourceStream.default)(`${e}.js`))})).pipe((0,_gulp.dest)(gulpConfig.get("browser.to")));exports.bundle=bundle;
@@ -1,20 +1,28 @@
1
- 'use strict'
2
-
3
- require('core-js/modules/esnext.weak-map.delete-all.js')
4
- Object.defineProperty(exports, '__esModule', {
5
- value: true
6
- })
7
- exports.bundleLint = void 0
8
- const _gulp = require('gulp')
9
- const gulpConfig = _interopRequireWildcard(require('../gulp.config.js'))
10
- const _standardLint = require('./partials/standardLint.js')
11
- function _interopRequireWildcard (e, t) { if (typeof WeakMap === 'function') var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; let o; let i; const f = { __proto__: null, default: e }; if (e === null || typeof e !== 'object' && typeof e !== 'function') return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f) } for (const t in e) t !== 'default' && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f })(e, t) }
1
+ 'use strict'
2
+
3
+ require('core-js/modules/esnext.weak-map.delete-all.js')
4
+ Object.defineProperty(exports, '__esModule', {
5
+ value: true
6
+ })
7
+ exports.bundleLint = void 0
8
+ require('core-js/modules/esnext.iterator.constructor.js')
9
+ require('core-js/modules/esnext.iterator.map.js')
10
+ const _gulp = require('gulp')
11
+ const gulpConfig = _interopRequireWildcard(require('../gulp.config.js'))
12
+ const _standardLint = require('./partials/standardLint.js')
13
+ const _getBundleConfigs = require('./utilities/getBundleConfigs.js')
14
+ function _interopRequireWildcard (e, t) { if (typeof WeakMap === 'function') var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; let o; let i; const f = { __proto__: null, default: e }; if (e === null || typeof e !== 'object' && typeof e !== 'function') return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f) } for (const t in e) t !== 'default' && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f })(e, t) }
12
15
  /**
13
- * Applies Standard code style linting to bundled file.
16
+ * Applies Standard code style linting to every bundled file (the primary bundle, plus any
17
+ * additional ones from browser.bundles). The lint step itself never varies per bundle, so this is
18
+ * one shared src()/dest() pipeline fed every bundle's filename at once, rather than one pipeline
19
+ * per bundle - gulp's src()/dest() both accept an array of paths natively.
14
20
  * @memberOf module:js-build-tools
15
21
  * @returns {stream.Stream}
16
- */
17
- const bundleLint = () => (0, _gulp.src)(`${gulpConfig.get('browser.to')}/${gulpConfig.get('browser.name')}.js`).pipe((0, _standardLint.standardLint)({
18
- fix: true
19
- })).pipe((0, _gulp.dest)(gulpConfig.get('browser.to')))
20
- exports.bundleLint = bundleLint
22
+ */
23
+ const bundleLint = () => (0, _gulp.src)((0, _getBundleConfigs.getBundleConfigs)().map(({
24
+ name
25
+ }) => `${gulpConfig.get('browser.to')}/${name}.js`)).pipe((0, _standardLint.standardLint)({
26
+ fix: true
27
+ })).pipe((0, _gulp.dest)(gulpConfig.get('browser.to')))
28
+ exports.bundleLint = bundleLint
@@ -1 +1 @@
1
- "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.bundleLint=void 0;var _gulp=require("gulp"),gulpConfig=_interopRequireWildcard(require("../gulp.config.js")),_standardLint=require("./partials/standardLint.js");function _interopRequireWildcard(e,t){if("function"==typeof WeakMap)var r=new WeakMap,n=new WeakMap;return(_interopRequireWildcard=function(e,t){if(!t&&e&&e.__esModule)return e;var i,o,u={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return u;if(i=t?n:r){if(i.has(e))return i.get(e);i.set(e,u)}for(const t in e)"default"!==t&&{}.hasOwnProperty.call(e,t)&&((o=(i=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,t))&&(o.get||o.set)?i(u,t,o):u[t]=e[t]);return u})(e,t)}const bundleLint=()=>(0,_gulp.src)(`${gulpConfig.get("browser.to")}/${gulpConfig.get("browser.name")}.js`).pipe((0,_standardLint.standardLint)({fix:!0})).pipe((0,_gulp.dest)(gulpConfig.get("browser.to")));exports.bundleLint=bundleLint;
1
+ "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.bundleLint=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js");var _gulp=require("gulp"),gulpConfig=_interopRequireWildcard(require("../gulp.config.js")),_standardLint=require("./partials/standardLint.js"),_getBundleConfigs=require("./utilities/getBundleConfigs.js");function _interopRequireWildcard(e,t){if("function"==typeof WeakMap)var r=new WeakMap,n=new WeakMap;return(_interopRequireWildcard=function(e,t){if(!t&&e&&e.__esModule)return e;var i,o,u={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return u;if(i=t?n:r){if(i.has(e))return i.get(e);i.set(e,u)}for(const t in e)"default"!==t&&{}.hasOwnProperty.call(e,t)&&((o=(i=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,t))&&(o.get||o.set)?i(u,t,o):u[t]=e[t]);return u})(e,t)}const bundleLint=()=>(0,_gulp.src)((0,_getBundleConfigs.getBundleConfigs)().map(({name:e})=>`${gulpConfig.get("browser.to")}/${e}.js`)).pipe((0,_standardLint.standardLint)({fix:!0})).pipe((0,_gulp.dest)(gulpConfig.get("browser.to")));exports.bundleLint=bundleLint;
@@ -1,17 +1,25 @@
1
- 'use strict'
2
-
3
- require('core-js/modules/esnext.weak-map.delete-all.js')
4
- Object.defineProperty(exports, '__esModule', {
5
- value: true
6
- })
7
- exports.bundleMinify = void 0
8
- const gulpConfig = _interopRequireWildcard(require('../gulp.config.js'))
9
- const _minifyFor = require('./partials/minifyFor.js')
10
- function _interopRequireWildcard (e, t) { if (typeof WeakMap === 'function') var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; let o; let i; const f = { __proto__: null, default: e }; if (e === null || typeof e !== 'object' && typeof e !== 'function') return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f) } for (const t in e) t !== 'default' && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f })(e, t) }
1
+ 'use strict'
2
+
3
+ require('core-js/modules/esnext.weak-map.delete-all.js')
4
+ Object.defineProperty(exports, '__esModule', {
5
+ value: true
6
+ })
7
+ exports.bundleMinify = void 0
8
+ require('core-js/modules/esnext.iterator.constructor.js')
9
+ require('core-js/modules/esnext.iterator.map.js')
10
+ const gulpConfig = _interopRequireWildcard(require('../gulp.config.js'))
11
+ const _minifyFor = require('./partials/minifyFor.js')
12
+ const _getBundleConfigs = require('./utilities/getBundleConfigs.js')
13
+ function _interopRequireWildcard (e, t) { if (typeof WeakMap === 'function') var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; let o; let i; const f = { __proto__: null, default: e }; if (e === null || typeof e !== 'object' && typeof e !== 'function') return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f) } for (const t in e) t !== 'default' && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f })(e, t) }
11
14
  /**
12
- * Creates the minified bundle file.
15
+ * Creates the minified file for every bundle (the primary bundle, plus any additional ones from
16
+ * browser.bundles). The minify step itself never varies per bundle, so this is one shared
17
+ * src()/dest() pipeline (inside minifyFor) fed every bundle's filename at once, rather than one
18
+ * pipeline per bundle - gulp's src() accepts an array of paths natively.
13
19
  * @memberOf module:js-build-tools
14
20
  * @returns {*}
15
- */
16
- const bundleMinify = () => (0, _minifyFor.minifyFor)(`${gulpConfig.get('browser.to')}/${gulpConfig.get('browser.name')}.js`, gulpConfig.get('browser.to'))
17
- exports.bundleMinify = bundleMinify
21
+ */
22
+ const bundleMinify = () => (0, _minifyFor.minifyFor)((0, _getBundleConfigs.getBundleConfigs)().map(({
23
+ name
24
+ }) => `${gulpConfig.get('browser.to')}/${name}.js`), gulpConfig.get('browser.to'))
25
+ exports.bundleMinify = bundleMinify
@@ -1 +1 @@
1
- "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.bundleMinify=void 0;var gulpConfig=_interopRequireWildcard(require("../gulp.config.js")),_minifyFor=require("./partials/minifyFor.js");function _interopRequireWildcard(e,r){if("function"==typeof WeakMap)var i=new WeakMap,t=new WeakMap;return(_interopRequireWildcard=function(e,r){if(!r&&e&&e.__esModule)return e;var n,o,u={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return u;if(n=r?t:i){if(n.has(e))return n.get(e);n.set(e,u)}for(const r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((o=(n=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,r))&&(o.get||o.set)?n(u,r,o):u[r]=e[r]);return u})(e,r)}const bundleMinify=()=>(0,_minifyFor.minifyFor)(`${gulpConfig.get("browser.to")}/${gulpConfig.get("browser.name")}.js`,gulpConfig.get("browser.to"));exports.bundleMinify=bundleMinify;
1
+ "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.bundleMinify=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js");var gulpConfig=_interopRequireWildcard(require("../gulp.config.js")),_minifyFor=require("./partials/minifyFor.js"),_getBundleConfigs=require("./utilities/getBundleConfigs.js");function _interopRequireWildcard(e,r){if("function"==typeof WeakMap)var i=new WeakMap,t=new WeakMap;return(_interopRequireWildcard=function(e,r){if(!r&&e&&e.__esModule)return e;var n,o,u={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return u;if(n=r?t:i){if(n.has(e))return n.get(e);n.set(e,u)}for(const r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((o=(n=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,r))&&(o.get||o.set)?n(u,r,o):u[r]=e[r]);return u})(e,r)}const bundleMinify=()=>(0,_minifyFor.minifyFor)((0,_getBundleConfigs.getBundleConfigs)().map(({name:e})=>`${gulpConfig.get("browser.to")}/${e}.js`),gulpConfig.get("browser.to"));exports.bundleMinify=bundleMinify;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ require("core-js/modules/esnext.weak-map.delete-all.js");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.getBundleConfigs = void 0;
8
+ require("core-js/modules/esnext.iterator.constructor.js");
9
+ require("core-js/modules/esnext.iterator.map.js");
10
+ var gulpConfig = _interopRequireWildcard(require("../../gulp.config.js"));
11
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
12
+ /**
13
+ * Build the full list of bundles to produce for this project: the primary bundle (browser.name /
14
+ * dist.main / browser.ignore / browser.exclude, exactly as before browser.bundles existed) plus
15
+ * any additional bundles from browser.bundles - each falling back to the primary's main / ignore /
16
+ * exclude whenever its own isn't given, so a render-only (or any other variant) bundle only has to
17
+ * specify what's actually different about it.
18
+ * @memberOf module:utilities
19
+ * @returns {Array<{name: string, main: string, ignore: Array, exclude: Array}>}
20
+ */
21
+ const getBundleConfigs = () => {
22
+ const primary = {
23
+ name: gulpConfig.get('browser.name'),
24
+ main: gulpConfig.get('dist.main'),
25
+ ignore: gulpConfig.get('browser.ignore'),
26
+ exclude: gulpConfig.get('browser.exclude')
27
+ };
28
+ const additional = gulpConfig.get('browser.bundles') || [];
29
+ return [primary, ...additional.map(bundleConfig => ({
30
+ name: bundleConfig.name,
31
+ main: bundleConfig.main || primary.main,
32
+ ignore: bundleConfig.ignore || primary.ignore,
33
+ exclude: bundleConfig.exclude || primary.exclude
34
+ }))];
35
+ };
36
+ exports.getBundleConfigs = getBundleConfigs;
@@ -1,15 +1,22 @@
1
1
  import browserify from 'browserify'
2
2
  import { dest } from 'gulp'
3
+ import mergeStream from 'merge-stream'
3
4
  import * as gulpConfig from '../gulp.config.mjs'
4
5
  import source from 'vinyl-source-stream'
6
+ import { getBundleConfigs } from './utilities/getBundleConfigs.mjs'
5
7
 
6
8
  /**
7
- * Starting at the distribution entry point, bundle all the files into a single file and store them in the specified output directory.
9
+ * Starting at each bundle's own entry point (dist.main for the primary bundle, or its own
10
+ * override from browser.bundles), bundle all the reachable files into a single file per bundle and
11
+ * store them in the specified output directory. A project with no browser.bundles entries gets
12
+ * exactly the one bundle it always did; each additional entry produces its own separate bundle
13
+ * (for example, a render-only variant built from a different entry point than the full bundle).
8
14
  *
9
- * browser.name does double duty: it names the output file (browser.name.js) and, via browserify's `standalone`
10
- * option, is also the name the bundle's exports are exposed under - as a global when loaded with a bare <script>
11
- * tag, or as the module when required via CommonJS/AMD. Without this, a bundle built from a real entry point (one
12
- * that actually exports something) builds successfully but exposes nothing a consumer can reach.
15
+ * A bundle's name does double duty: it names the output file (name.js) and, via browserify's
16
+ * `standalone` option, is also the name its exports are exposed under - as a global when loaded
17
+ * with a bare <script> tag, or as the module when required via CommonJS/AMD. Without this, a
18
+ * bundle built from a real entry point (one that actually exports something) builds successfully
19
+ * but exposes nothing a consumer can reach.
13
20
  *
14
21
  * Two escape hatches from browser.config.json keep an unwanted dependency (or a dependency's dependency) out of the
15
22
  * bundle: 'ignore' replaces it with an empty object (safe when your code never actually calls into it - for example
@@ -18,18 +25,20 @@ import source from 'vinyl-source-stream'
18
25
  * @memberOf module:js-build-tools
19
26
  * @returns {stream.Stream}
20
27
  */
21
- export const bundle = () => {
22
- const bundler = browserify(gulpConfig.get('dist.main'), { standalone: gulpConfig.get('browser.name') })
23
- const ignore = gulpConfig.get('browser.ignore')
24
- const exclude = gulpConfig.get('browser.exclude')
25
- if (ignore && ignore.length) {
26
- bundler.ignore(ignore)
27
- }
28
- if (exclude && exclude.length) {
29
- bundler.exclude(exclude)
30
- }
31
- return bundler
32
- .bundle()
33
- .pipe(source(`${gulpConfig.get('browser.name')}.js`))
34
- .pipe(dest(gulpConfig.get('browser.to')))
35
- }
28
+ export const bundle = () => mergeStream(
29
+ getBundleConfigs().map(({ name, main, ignore, exclude }) => {
30
+ const bundler = browserify(main, { standalone: name })
31
+ if (ignore && ignore.length) {
32
+ bundler.ignore(ignore)
33
+ }
34
+ if (exclude && exclude.length) {
35
+ bundler.exclude(exclude)
36
+ }
37
+ return bundler
38
+ .bundle()
39
+ .pipe(source(`${name}.js`))
40
+ })
41
+ // One shared dest() for every bundle, not one per bundle - vinyl-fs's dest() races against
42
+ // itself (a real "write after end" failure) when two separate dest() streams target the same
43
+ // directory concurrently, which every additional bundle's own dest() call otherwise would.
44
+ ).pipe(dest(gulpConfig.get('browser.to')))
@@ -88,6 +88,63 @@ describe('bundle', () => {
88
88
  })
89
89
  })
90
90
 
91
+ describe('bundle with additional bundles', () => {
92
+ const renderOnlyFileContents = 'const file1 = require(\'./sources/file1\')\n' +
93
+ '\n' +
94
+ 'module.exports = {\n' +
95
+ ' file1,\n' +
96
+ '}\n'
97
+
98
+ afterEach(() => gulpConfig.set('browser.bundles', []))
99
+
100
+ test('produces one file per additional bundle, each from its own entry point', done => {
101
+ const distPath = gulpConfig.get('dist.to')
102
+ const renderOnlyMain = `${distPath}/renderOnly`
103
+ fs.writeFileSync(`${renderOnlyMain}.js`, renderOnlyFileContents)
104
+ gulpConfig.set('browser.bundles', [{ name: 'renderOnly', main: renderOnlyMain }])
105
+
106
+ const destPath = gulpConfig.get('browser.to')
107
+ const primaryFile = `${destPath}/${gulpConfig.get('browser.name')}.js`
108
+ const renderOnlyFile = `${destPath}/renderOnly.js`
109
+ expect.assertions(4)
110
+ bundle()
111
+ .on('finish', () => {
112
+ // The primary bundle is unaffected - still built from dist.main, still has both files.
113
+ const primaryContents = fs.readFileSync(primaryFile).toString()
114
+ expect(primaryContents).toContain('Hello from file1')
115
+ expect(primaryContents).toContain('Good-bye from file2')
116
+
117
+ // The additional bundle came from its own entry point - file1 only, never file2.
118
+ const renderOnlyContents = fs.readFileSync(renderOnlyFile).toString()
119
+ expect(renderOnlyContents).toContain('Hello from file1')
120
+ expect(renderOnlyContents).not.toContain('Good-bye from file2')
121
+ done()
122
+ })
123
+ .on('error', error => done(error))
124
+ })
125
+
126
+ test('an additional bundle exposes its own name as its standalone export', done => {
127
+ const distPath = gulpConfig.get('dist.to')
128
+ const renderOnlyMain = `${distPath}/renderOnly`
129
+ fs.writeFileSync(`${renderOnlyMain}.js`, renderOnlyFileContents)
130
+ gulpConfig.set('browser.bundles', [{ name: 'renderOnly', main: renderOnlyMain }])
131
+
132
+ const destPath = gulpConfig.get('browser.to')
133
+ const renderOnlyFile = `${destPath}/renderOnly.js`
134
+ expect.assertions(2)
135
+ bundle()
136
+ .on('finish', () => {
137
+ const resolved = path.resolve(renderOnlyFile)
138
+ delete require.cache[resolved]
139
+ const loaded = require(resolved)
140
+ expect(typeof loaded.file1).toBe('function')
141
+ expect(loaded.file2).toBeUndefined()
142
+ done()
143
+ })
144
+ .on('error', error => done(error))
145
+ })
146
+ })
147
+
91
148
  describe('bundle ignore / exclude', () => {
92
149
  const optionalDepMarker = 'MARKER_FROM_THE_OPTIONAL_DEPENDENCY_CODE'
93
150
 
@@ -1,12 +1,18 @@
1
1
  import { dest, src } from 'gulp'
2
2
  import * as gulpConfig from '../gulp.config.mjs'
3
3
  import { standardLint } from './partials/standardLint.mjs'
4
+ import { getBundleConfigs } from './utilities/getBundleConfigs.mjs'
4
5
 
5
6
  /**
6
- * Applies Standard code style linting to bundled file.
7
+ * Applies Standard code style linting to every bundled file (the primary bundle, plus any
8
+ * additional ones from browser.bundles). The lint step itself never varies per bundle, so this is
9
+ * one shared src()/dest() pipeline fed every bundle's filename at once, rather than one pipeline
10
+ * per bundle - gulp's src()/dest() both accept an array of paths natively.
7
11
  * @memberOf module:js-build-tools
8
12
  * @returns {stream.Stream}
9
13
  */
10
- export const bundleLint = () => src(`${gulpConfig.get('browser.to')}/${gulpConfig.get('browser.name')}.js`)
14
+ export const bundleLint = () => src(
15
+ getBundleConfigs().map(({ name }) => `${gulpConfig.get('browser.to')}/${name}.js`)
16
+ )
11
17
  .pipe(standardLint({ fix: true }))
12
18
  .pipe(dest(gulpConfig.get('browser.to')))
@@ -62,4 +62,24 @@ describe('bundleLint', () => {
62
62
  done()
63
63
  })
64
64
  }, 10000)
65
+
66
+ test('lints every additional bundle too, not just the primary one', done => {
67
+ const browserPath = gulpConfig.get('browser.to')
68
+ const renderOnlyFile = `${browserPath}/renderOnly.js`
69
+ fs.writeFileSync(renderOnlyFile, lintableContents)
70
+ gulpConfig.set('browser.bundles', [{ name: 'renderOnly' }])
71
+ expect.assertions(2)
72
+ bundleLint()
73
+ .on('finish', () => {
74
+ const lintedContents = fs.readFileSync(renderOnlyFile).toString()
75
+ expect(countMatches(lintedContents, '"')).toEqual(0)
76
+ expect(countMatches(lintedContents, ';')).toEqual(0)
77
+ gulpConfig.set('browser.bundles', [])
78
+ done()
79
+ })
80
+ .on('error', error => {
81
+ console.error('Encountered error', error)
82
+ done(error)
83
+ })
84
+ }, 10000)
65
85
  })
@@ -1,9 +1,16 @@
1
1
  import * as gulpConfig from '../gulp.config.mjs'
2
2
  import { minifyFor } from './partials/minifyFor.mjs'
3
+ import { getBundleConfigs } from './utilities/getBundleConfigs.mjs'
3
4
 
4
5
  /**
5
- * Creates the minified bundle file.
6
+ * Creates the minified file for every bundle (the primary bundle, plus any additional ones from
7
+ * browser.bundles). The minify step itself never varies per bundle, so this is one shared
8
+ * src()/dest() pipeline (inside minifyFor) fed every bundle's filename at once, rather than one
9
+ * pipeline per bundle - gulp's src() accepts an array of paths natively.
6
10
  * @memberOf module:js-build-tools
7
11
  * @returns {*}
8
12
  */
9
- export const bundleMinify = () => minifyFor(`${gulpConfig.get('browser.to')}/${gulpConfig.get('browser.name')}.js`, gulpConfig.get('browser.to'))
13
+ export const bundleMinify = () => minifyFor(
14
+ getBundleConfigs().map(({ name }) => `${gulpConfig.get('browser.to')}/${name}.js`),
15
+ gulpConfig.get('browser.to')
16
+ )
@@ -7,9 +7,31 @@ const gulpConfig = testHelpers.gulpConfig
7
7
  jest.mock('./partials/minifyFor.mjs', () => ({ minifyFor: jest.fn(() => Promise.resolve(true)) }))
8
8
 
9
9
  describe('bundleMinify', () => {
10
- test('calls minifyFor with configurations', () => {
10
+ afterEach(() => {
11
+ gulpConfig.set('browser.bundles', [])
12
+ minifyFor.mockClear()
13
+ })
14
+
15
+ test('calls minifyFor with the primary bundle', () => {
11
16
  expect.assertions(1)
12
17
  bundleMinify()
13
- expect(minifyFor).toHaveBeenCalledWith(`${gulpConfig.get('browser.to')}/${gulpConfig.get('browser.name')}.js`, gulpConfig.get('browser.to'))
18
+ expect(minifyFor).toHaveBeenCalledWith(
19
+ [`${gulpConfig.get('browser.to')}/${gulpConfig.get('browser.name')}.js`],
20
+ gulpConfig.get('browser.to')
21
+ )
22
+ })
23
+
24
+ test('calls minifyFor once, with every additional bundle included in the same array', () => {
25
+ gulpConfig.set('browser.bundles', [{ name: 'renderOnly' }])
26
+ expect.assertions(2)
27
+ bundleMinify()
28
+ expect(minifyFor).toHaveBeenCalledTimes(1)
29
+ expect(minifyFor).toHaveBeenCalledWith(
30
+ [
31
+ `${gulpConfig.get('browser.to')}/${gulpConfig.get('browser.name')}.js`,
32
+ `${gulpConfig.get('browser.to')}/renderOnly.js`
33
+ ],
34
+ gulpConfig.get('browser.to')
35
+ )
14
36
  })
15
37
  })
@@ -0,0 +1,29 @@
1
+ import * as gulpConfig from '../../gulp.config.mjs'
2
+
3
+ /**
4
+ * Build the full list of bundles to produce for this project: the primary bundle (browser.name /
5
+ * dist.main / browser.ignore / browser.exclude, exactly as before browser.bundles existed) plus
6
+ * any additional bundles from browser.bundles - each falling back to the primary's main / ignore /
7
+ * exclude whenever its own isn't given, so a render-only (or any other variant) bundle only has to
8
+ * specify what's actually different about it.
9
+ * @memberOf module:utilities
10
+ * @returns {Array<{name: string, main: string, ignore: Array, exclude: Array}>}
11
+ */
12
+ export const getBundleConfigs = () => {
13
+ const primary = {
14
+ name: gulpConfig.get('browser.name'),
15
+ main: gulpConfig.get('dist.main'),
16
+ ignore: gulpConfig.get('browser.ignore'),
17
+ exclude: gulpConfig.get('browser.exclude')
18
+ }
19
+ const additional = gulpConfig.get('browser.bundles') || []
20
+ return [
21
+ primary,
22
+ ...additional.map((bundleConfig) => ({
23
+ name: bundleConfig.name,
24
+ main: bundleConfig.main || primary.main,
25
+ ignore: bundleConfig.ignore || primary.ignore,
26
+ exclude: bundleConfig.exclude || primary.exclude
27
+ }))
28
+ ]
29
+ }
package/gulp.config.mjs CHANGED
@@ -60,6 +60,20 @@ import { fileExists } from 'test-filesystem'
60
60
  * @property {ArrayableSetting} exclude - Module names or file paths left out of the bundle entirely; requiring one at
61
61
  * runtime throws unless something else provides it (for example a script tag loading it separately). See
62
62
  * browserify's b.exclude(file).
63
+ * @property {Array<module:gulpConfig~AdditionalBundleConfig>} bundles - Additional bundles to produce alongside the
64
+ * primary one (name/dist.main/ignore/exclude above) - for example a render-only variant built from a different
65
+ * entry point than the full bundle. Empty by default: a project that never sets this gets exactly the one bundle it
66
+ * always did.
67
+ */
68
+ /**
69
+ * One additional bundle - everything but `name` falls back to the primary bundle's own value when omitted, so an
70
+ * additional bundle only has to specify what's actually different about it.
71
+ * @typedef {Object<string, Setting>} module:gulpConfig~AdditionalBundleConfig
72
+ * @property {StringSetting} name - The name for this bundle's output file and standalone export - must be unique
73
+ * among all of a project's bundles.
74
+ * @property {StringSetting} [main] - This bundle's own entry point. Falls back to dist.main when omitted.
75
+ * @property {ArrayableSetting} [ignore] - This bundle's own ignore list. Falls back to browser.ignore when omitted.
76
+ * @property {ArrayableSetting} [exclude] - This bundle's own exclude list. Falls back to browser.exclude when omitted.
63
77
  */
64
78
  /**
65
79
  * Configurations for building the node distribution files.
@@ -150,6 +164,7 @@ import { readFileSync } from 'fs'
150
164
 
151
165
  const setDefaults = {
152
166
  browser: {
167
+ bundles: [],
153
168
  enabled: true,
154
169
  exclude: [],
155
170
  from: 'dist/**/*.js',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-build-tools",
3
- "version": "5.4.0",
3
+ "version": "5.5.0",
4
4
  "description": "Standardised build process with gulp.",
5
5
  "main": "gulpfile.base.js",
6
6
  "exports": {
@@ -68,6 +68,7 @@
68
68
  "jest": "^30.5.2",
69
69
  "jest-environment-jsdom": "^30.5.2",
70
70
  "jsdoc-to-markdown": "^9.1.3",
71
+ "merge-stream": "^2.0.0",
71
72
  "postcss": "^8.5.28",
72
73
  "regenerator-runtime": "^0.14.1",
73
74
  "sass": "^1.105.0",