js-build-tools 4.1.1 → 5.1.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.
Files changed (67) hide show
  1. package/README.md +274 -267
  2. package/babel.config.js +18 -1
  3. package/dist/build.js +58 -56
  4. package/dist/build.min.js +1 -1
  5. package/dist/bundle.js +16 -16
  6. package/dist/bundleLint.js +16 -20
  7. package/dist/bundleLint.min.js +1 -1
  8. package/dist/bundleMinify.js +13 -13
  9. package/dist/compileReadme.js +10 -10
  10. package/dist/copyFonts.js +13 -13
  11. package/dist/default.js +15 -15
  12. package/dist/dist.js +10 -10
  13. package/dist/distLint.js +16 -20
  14. package/dist/distLint.min.js +1 -1
  15. package/dist/distMinify.js +13 -13
  16. package/dist/images.js +10 -10
  17. package/dist/partials/addToReadme.js +17 -17
  18. package/dist/partials/beginWatcher.js +12 -12
  19. package/dist/partials/clean.js +14 -14
  20. package/dist/partials/copyFor.js +12 -9
  21. package/dist/partials/distFor.js +21 -21
  22. package/dist/partials/distForSrc.js +11 -11
  23. package/dist/partials/distSeries.js +19 -19
  24. package/dist/partials/imagesFor.js +56 -21
  25. package/dist/partials/minifyFor.js +16 -16
  26. package/dist/partials/readmeTemplate.js +14 -14
  27. package/dist/partials/removeDirectory.js +37 -13
  28. package/dist/partials/runOnChange.js +28 -28
  29. package/dist/partials/sassFor.js +28 -22
  30. package/dist/partials/standardLint.js +96 -0
  31. package/dist/partials.js +110 -103
  32. package/dist/partials.min.js +1 -1
  33. package/dist/sass.js +10 -10
  34. package/dist/test-helpers/setUp.js +89 -89
  35. package/dist/test-helpers/standardLintWithRealStandard.js +28 -0
  36. package/dist/testFull.js +3 -3
  37. package/dist/testHelpers.js +10 -10
  38. package/dist/testQuick.js +3 -3
  39. package/dist/typeScript.js +9 -9
  40. package/dist/utilities/dotGet.js +46 -46
  41. package/dist/utilities/dotNotate.js +46 -46
  42. package/dist/utilities/dotSet.js +37 -37
  43. package/dist/utilities/isObject.js +8 -8
  44. package/dist/utilities/strAfter.js +11 -11
  45. package/dist/utilities/strBefore.js +11 -11
  46. package/dist/watchFull.js +10 -10
  47. package/dist/watchTest.js +16 -16
  48. package/functions/build.mjs +6 -3
  49. package/functions/build.test.mjs +31 -0
  50. package/functions/bundleLint.mjs +2 -6
  51. package/functions/bundleLint.test.mjs +3 -0
  52. package/functions/distLint.mjs +2 -6
  53. package/functions/distLint.test.mjs +3 -0
  54. package/functions/partials/copyFor.mjs +2 -1
  55. package/functions/partials/copyFor.test.mjs +18 -0
  56. package/functions/partials/imagesFor.mjs +35 -3
  57. package/functions/partials/imagesFor.test.mjs +83 -1
  58. package/functions/partials/removeDirectory.mjs +28 -14
  59. package/functions/partials/removeDirectory.test.mjs +44 -0
  60. package/functions/partials/sassFor.mjs +5 -2
  61. package/functions/partials/sassFor.test.mjs +22 -4
  62. package/functions/partials/standardLint.mjs +80 -0
  63. package/functions/partials/standardLint.test.mjs +99 -0
  64. package/functions/partials.mjs +1 -0
  65. package/functions/partials.test.mjs +1 -1
  66. package/functions/test-helpers/standardLintWithRealStandard.mjs +21 -0
  67. package/package.json +15 -4
@@ -1,43 +1,43 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.dotNotate = void 0;
7
- require("core-js/modules/esnext.iterator.constructor.js");
8
- require("core-js/modules/esnext.iterator.map.js");
9
- var _isObject = require("./isObject.js");
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.dotNotate = void 0;
7
+ require("core-js/modules/esnext.iterator.constructor.js");
8
+ require("core-js/modules/esnext.iterator.map.js");
9
+ var _isObject = require("./isObject.js");
10
10
  /**
11
11
  * Convert an array of keys into a regex, return a function to test if incoming keys match.
12
12
  * @param {Array.<string>} [retainObjects=[]] - An array of keys to retain as objects
13
13
  * @returns {Function} The dot-notated array
14
- */
15
- const handleRetainObjects = (retainObjects = []) => {
16
- if (!retainObjects.length) {
14
+ */
15
+ const handleRetainObjects = (retainObjects = []) => {
16
+ if (!retainObjects.length) {
17
17
  /**
18
18
  * Bypass the test function if there are no retainObjects.
19
19
  * @returns {false}
20
- */
21
- return (currentKey, value, results) => false;
22
- }
23
- retainObjects = retainObjects.map(key => key.replace('\.', '\\.'));
24
- const retainRegex = new RegExp(`(${retainObjects.join('|')})$`);
20
+ */
21
+ return (currentKey, value, results) => false;
22
+ }
23
+ retainObjects = retainObjects.map(key => key.replace('\.', '\\.'));
24
+ const retainRegex = new RegExp(`(${retainObjects.join('|')})$`);
25
25
  /**
26
26
  * Test if a key should be retained as an object.
27
27
  * @param {string} currentKey - The key to test
28
28
  * @param {*} value - The value of the key
29
29
  * @param {Object} results - The result object to add to
30
30
  * @returns {boolean}
31
- */
32
- return (currentKey, value, results) => {
33
- if (!currentKey.match(retainRegex)) {
34
- return false;
35
- }
36
- results[currentKey] = value;
37
- return true;
38
- };
39
- };
40
-
31
+ */
32
+ return (currentKey, value, results) => {
33
+ if (!currentKey.match(retainRegex)) {
34
+ return false;
35
+ }
36
+ results[currentKey] = value;
37
+ return true;
38
+ };
39
+ };
40
+
41
41
  /**
42
42
  * The underlying logic function for converting arrays to dot-notation.
43
43
  * @param {Object} arrayObject - The array or object to dot-notate
@@ -45,29 +45,29 @@ const handleRetainObjects = (retainObjects = []) => {
45
45
  * @param {string} [prepend=''] - The path for the property being processed
46
46
  * @param {Object<string, *>} [results={}] - The final notated object to return
47
47
  * @returns {Object<string, *>} The dot-notated object
48
- */
49
- const performDotNotate = (arrayObject, didRetain, prepend = '', results = {}) => {
50
- for (let key in arrayObject) {
51
- const value = arrayObject[key];
52
- const currentKey = `${prepend}${key}`;
53
- if (didRetain(currentKey, value, results)) {
54
- continue;
55
- }
56
- if ((0, _isObject.isObject)(value)) {
57
- performDotNotate(value, didRetain, `${currentKey}.`, results);
58
- continue;
59
- }
60
- results[currentKey] = value;
61
- }
62
- return results;
63
- };
64
-
48
+ */
49
+ const performDotNotate = (arrayObject, didRetain, prepend = '', results = {}) => {
50
+ for (let key in arrayObject) {
51
+ const value = arrayObject[key];
52
+ const currentKey = `${prepend}${key}`;
53
+ if (didRetain(currentKey, value, results)) {
54
+ continue;
55
+ }
56
+ if ((0, _isObject.isObject)(value)) {
57
+ performDotNotate(value, didRetain, `${currentKey}.`, results);
58
+ continue;
59
+ }
60
+ results[currentKey] = value;
61
+ }
62
+ return results;
63
+ };
64
+
65
65
  /**
66
66
  * Convert an array or object to a single dimensional associative array with dot notation.
67
67
  * Original source concepts from {@link https://github.com/jheagle/si-funciona/blob/main/src/helpers/objects/dotNotate.ts Sí, funciona}
68
68
  * @param {Object} arrayObject - The array or object to dot-notate
69
69
  * @param {Array.<string>} [retainObjects=[]] - An array of keys to retain as objects
70
70
  * @returns {Object<string, *>} The dot-notated object
71
- */
72
- const dotNotate = (arrayObject, retainObjects = []) => performDotNotate(arrayObject, handleRetainObjects(retainObjects));
71
+ */
72
+ const dotNotate = (arrayObject, retainObjects = []) => performDotNotate(arrayObject, handleRetainObjects(retainObjects));
73
73
  exports.dotNotate = dotNotate;
@@ -1,12 +1,12 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.dotSet = void 0;
7
- var _isObject = require("./isObject.js");
8
- var _strAfter = require("./strAfter.js");
9
- var _strBefore = require("./strBefore.js");
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.dotSet = void 0;
7
+ var _isObject = require("./isObject.js");
8
+ var _strAfter = require("./strAfter.js");
9
+ var _strBefore = require("./strBefore.js");
10
10
  /**
11
11
  * Set a nested property value an object.
12
12
  * Original source concepts from {@link https://github.com/jheagle/si-funciona/blob/main/src/helpers/objects/dotSet.ts Sí, funciona}
@@ -14,32 +14,32 @@ var _strBefore = require("./strBefore.js");
14
14
  * @param {string} dotNotation - The path for the property
15
15
  * @param {*} value - The default value to return if the property is not found
16
16
  * @returns {Object} The modified object
17
- */
18
- const dotSet = (arrayObject, dotNotation, value = null) => {
19
- let key = (0, _strBefore.strBefore)(dotNotation, '.');
20
- const lastKey = !key;
21
- if (lastKey) {
22
- key = dotNotation;
23
- }
24
- if (key === '*') {
25
- for (let wildKey in arrayObject) {
26
- if (lastKey) {
27
- arrayObject[wildKey] = value;
28
- continue;
29
- }
30
- if (!(0, _isObject.isObject)(arrayObject[wildKey])) {
31
- continue;
32
- }
33
- dotSet(arrayObject[wildKey], (0, _strAfter.strAfter)(dotNotation, '.'), value);
34
- }
35
- return arrayObject;
36
- }
37
- if (lastKey) {
38
- arrayObject[dotNotation] = value;
39
- return arrayObject;
40
- }
41
- const next = arrayObject[key] ?? [];
42
- arrayObject[key] = dotSet(next, (0, _strAfter.strAfter)(dotNotation, '.'), value);
43
- return arrayObject;
44
- };
17
+ */
18
+ const dotSet = (arrayObject, dotNotation, value = null) => {
19
+ let key = (0, _strBefore.strBefore)(dotNotation, '.');
20
+ const lastKey = !key;
21
+ if (lastKey) {
22
+ key = dotNotation;
23
+ }
24
+ if (key === '*') {
25
+ for (let wildKey in arrayObject) {
26
+ if (lastKey) {
27
+ arrayObject[wildKey] = value;
28
+ continue;
29
+ }
30
+ if (!(0, _isObject.isObject)(arrayObject[wildKey])) {
31
+ continue;
32
+ }
33
+ dotSet(arrayObject[wildKey], (0, _strAfter.strAfter)(dotNotation, '.'), value);
34
+ }
35
+ return arrayObject;
36
+ }
37
+ if (lastKey) {
38
+ arrayObject[dotNotation] = value;
39
+ return arrayObject;
40
+ }
41
+ const next = arrayObject[key] ?? [];
42
+ arrayObject[key] = dotSet(next, (0, _strAfter.strAfter)(dotNotation, '.'), value);
43
+ return arrayObject;
44
+ };
45
45
  exports.dotSet = dotSet;
@@ -1,14 +1,14 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.isObject = void 0;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isObject = void 0;
7
7
  /**
8
8
  * Check if the provided thing is an object / array.
9
9
  * Original source concepts from {@link https://github.com/jheagle/si-funciona/blob/main/src/helpers/objects/isObject.ts Sí, funciona}
10
10
  * @param {*} object
11
11
  * @returns {boolean}
12
- */
13
- const isObject = object => typeof object === 'object' && object !== null;
12
+ */
13
+ const isObject = object => typeof object === 'object' && object !== null;
14
14
  exports.isObject = isObject;
@@ -1,18 +1,18 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.strAfter = void 0;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.strAfter = void 0;
7
7
  /**
8
8
  * Retrieve the string part after the search match.
9
9
  * Original source from {@link https://github.com/jheagle/si-funciona/blob/main/src/helpers/strings/strAfter.ts Sí, funciona}
10
10
  * @param {string} str
11
11
  * @param {string} search
12
12
  * @returns {string}
13
- */
14
- const strAfter = (str, search) => {
15
- const index = str.indexOf(search);
16
- return index === -1 ? '' : str.substring(index + search.length);
17
- };
13
+ */
14
+ const strAfter = (str, search) => {
15
+ const index = str.indexOf(search);
16
+ return index === -1 ? '' : str.substring(index + search.length);
17
+ };
18
18
  exports.strAfter = strAfter;
@@ -1,18 +1,18 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.strBefore = void 0;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.strBefore = void 0;
7
7
  /**
8
8
  * Retrieve the string part before the search match.
9
9
  * Original source from {@link https://github.com/jheagle/si-funciona/blob/main/src/helpers/strings/strBefore.ts Sí, funciona}
10
10
  * @param {string} str
11
11
  * @param {string} search
12
12
  * @returns {string}
13
- */
14
- const strBefore = (str, search) => {
15
- const index = str.indexOf(search);
16
- return index === -1 ? '' : str.slice(0, index);
17
- };
13
+ */
14
+ const strBefore = (str, search) => {
15
+ const index = str.indexOf(search);
16
+ return index === -1 ? '' : str.slice(0, index);
17
+ };
18
18
  exports.strBefore = strBefore;
package/dist/watchFull.js CHANGED
@@ -1,14 +1,14 @@
1
- 'use strict'
2
-
3
- Object.defineProperty(exports, '__esModule', {
4
- value: true
5
- })
6
- exports.watchFull = void 0
7
- var _partials = require('./partials.js')
1
+ 'use strict'
2
+
3
+ Object.defineProperty(exports, '__esModule', {
4
+ value: true
5
+ })
6
+ exports.watchFull = void 0
7
+ const _partials = require('./partials.js')
8
8
  /**
9
9
  * Watch for changes and run the distribution for the changed files, then bundle and test the changed files.
10
10
  * @memberOf module:js-build-tools
11
11
  * @returns {FSWatcher}
12
- */
13
- const watchFull = () => (0, _partials.beginWatcher)().on('change', _partials.runOnChange)
14
- exports.watchFull = watchFull
12
+ */
13
+ const watchFull = () => (0, _partials.beginWatcher)().on('change', _partials.runOnChange)
14
+ exports.watchFull = watchFull
package/dist/watchTest.js CHANGED
@@ -1,20 +1,20 @@
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.watchTest = void 0
8
- var gulpConfig = _interopRequireWildcard(require('../gulp.config.js'))
9
- var _gulp = require('gulp')
10
- var _testQuick = require('./testQuick.js')
11
- function _interopRequireWildcard (e, t) { if (typeof WeakMap === 'function') var r = new WeakMap(); var n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o; var i; var 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.watchTest = void 0
8
+ const gulpConfig = _interopRequireWildcard(require('../gulp.config.js'))
9
+ const _gulp = require('gulp')
10
+ const _testQuick = require('./testQuick.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) }
12
12
  /**
13
13
  * Watch for changes and run the tests.
14
14
  * @memberOf module:js-build-tools
15
15
  * @returns {*}
16
- */
17
- const watchTest = () => (0, _gulp.watch)(gulpConfig.get('test.watch'), {
18
- ignoreInitial: false
19
- }, _testQuick.testQuick)
20
- exports.watchTest = watchTest
16
+ */
17
+ const watchTest = () => (0, _gulp.watch)(gulpConfig.get('test.watch'), {
18
+ ignoreInitial: false
19
+ }, _testQuick.testQuick)
20
+ exports.watchTest = watchTest
@@ -21,7 +21,7 @@ import { testFull } from './testFull.mjs'
21
21
  export const build = (done = null) => {
22
22
  const distLintMinify = parallel(distLint, distMinify)
23
23
  const bundleLintMinify = parallel(bundleLint, bundleMinify)
24
- const buildActions = [clean, distSeries(), distLintMinify]
24
+ const buildActions = [distSeries(), distLintMinify]
25
25
  if (gulpConfig.get('typescript.enabled')) {
26
26
  // For ts usage, we need to run the readme on the dist directly since that is where the .js files are located
27
27
  buildActions.push(compileReadme)
@@ -51,7 +51,10 @@ export const build = (done = null) => {
51
51
  // Conditionally add SASS process
52
52
  runActions.push(sass)
53
53
  }
54
- return parallel(
55
- ...runActions
54
+ // clean has to finish before anything writes into the output folders - images, fonts and sass all write into
55
+ // browser/, so running clean in parallel with them races (ENOTEMPTY errors from clean, or output deleted mid-write).
56
+ return series(
57
+ clean,
58
+ parallel(...runActions)
56
59
  )(done)
57
60
  }
@@ -5,6 +5,9 @@ import { clean } from './partials/clean.mjs'
5
5
  import { testFull } from './testFull.mjs'
6
6
  import { countMatches } from 'test-filesystem'
7
7
 
8
+ // Run the real Standard (in a subprocess) - Jest's CommonJS transform can't load the ESM-only package itself.
9
+ jest.mock('./partials/standardLint.mjs', () => require('./test-helpers/standardLintWithRealStandard.mjs'))
10
+
8
11
  setUp.setDefaults('test-build')
9
12
  const gulpConfig = setUp.gulpConfig
10
13
 
@@ -621,4 +624,32 @@ describe('build', () => {
621
624
  done()
622
625
  })
623
626
  }, 30000)
627
+
628
+ test('finishes clean before starting the tasks that write into the output folders', done => {
629
+ const srcPath = gulpConfig.get('srcPath')
630
+ const sourcesPath = `${srcPath}/sources`
631
+ fs.writeFileSync(`${sourcesPath}/file1.js`, file1Contents)
632
+ fs.writeFileSync(`${sourcesPath}/file2.js`, file2Contents)
633
+ fs.writeFileSync(`${srcPath}/main.js`, mainFileContents)
634
+ expect.assertions(1)
635
+ const events = []
636
+ clean.mockImplementationOnce(() => new Promise(resolve => setTimeout(() => {
637
+ events.push('clean finished')
638
+ resolve(true)
639
+ }, 100)))
640
+ testFull.mockImplementationOnce(() => {
641
+ events.push('testFull started')
642
+ return Promise.resolve(true)
643
+ })
644
+ // Earlier tests leave these enabled, so state them explicitly to keep this test independent of test order.
645
+ gulpConfig.set('browser.enabled', false)
646
+ gulpConfig.set('fonts.enabled', false)
647
+ gulpConfig.set('images.enabled', false)
648
+ gulpConfig.set('sass.enabled', false)
649
+ gulpConfig.set('typescript.enabled', false)
650
+ build(() => {
651
+ expect(events).toEqual(['clean finished', 'testFull started'])
652
+ done()
653
+ })
654
+ }, 30000)
624
655
  })
@@ -1,6 +1,6 @@
1
1
  import { dest, src } from 'gulp'
2
2
  import * as gulpConfig from '../gulp.config.mjs'
3
- import standard from 'gulp-standard'
3
+ import { standardLint } from './partials/standardLint.mjs'
4
4
 
5
5
  /**
6
6
  * Applies Standard code style linting to bundled file.
@@ -8,9 +8,5 @@ import standard from 'gulp-standard'
8
8
  * @returns {stream.Stream}
9
9
  */
10
10
  export const bundleLint = () => src(`${gulpConfig.get('browser.to')}/${gulpConfig.get('browser.name')}.js`)
11
- .pipe(standard({ fix: true }))
12
- .pipe(standard.reporter('default', {
13
- fix: true,
14
- quiet: true
15
- }))
11
+ .pipe(standardLint({ fix: true }))
16
12
  .pipe(dest(gulpConfig.get('browser.to')))
@@ -3,6 +3,9 @@ import * as setUp from './test-helpers/setUp.mjs'
3
3
  import { bundleLint } from './bundleLint.mjs'
4
4
  import { countMatches } from 'test-filesystem'
5
5
 
6
+ // Run the real Standard (in a subprocess) - Jest's CommonJS transform can't load the ESM-only package itself.
7
+ jest.mock('./partials/standardLint.mjs', () => require('./test-helpers/standardLintWithRealStandard.mjs'))
8
+
6
9
  setUp.setDefaults('test-bundle-lint')
7
10
  const gulpConfig = setUp.gulpConfig
8
11
 
@@ -1,6 +1,6 @@
1
1
  import * as gulpConfig from '../gulp.config.mjs'
2
2
  import { dest, src } from 'gulp'
3
- import standard from 'gulp-standard'
3
+ import { standardLint } from './partials/standardLint.mjs'
4
4
 
5
5
  /**
6
6
  * Applies Standard code style linting to distribution files.
@@ -8,9 +8,5 @@ import standard from 'gulp-standard'
8
8
  * @returns {*}
9
9
  */
10
10
  export const distLint = () => src(gulpConfig.get('browser.from'))
11
- .pipe(standard({ fix: true }))
12
- .pipe(standard.reporter('default', {
13
- fix: true,
14
- quiet: true
15
- }))
11
+ .pipe(standardLint({ fix: true }))
16
12
  .pipe(dest(gulpConfig.get('dist.to')))
@@ -3,6 +3,9 @@ import * as setUp from './test-helpers/setUp.mjs'
3
3
  import { distLint } from './distLint.mjs'
4
4
  import { countMatches } from 'test-filesystem'
5
5
 
6
+ // Run the real Standard (in a subprocess) - Jest's CommonJS transform can't load the ESM-only package itself.
7
+ jest.mock('./partials/standardLint.mjs', () => require('./test-helpers/standardLintWithRealStandard.mjs'))
8
+
6
9
  setUp.setDefaults('test-dist-lint')
7
10
  const gulpConfig = setUp.gulpConfig
8
11
 
@@ -7,5 +7,6 @@ import { dest, src } from 'gulp'
7
7
  * @param {string} destPath
8
8
  * @returns {stream.Stream}
9
9
  */
10
- export const copyFor = (srcPath, destPath) => src(srcPath)
10
+ // encoding: false copies the bytes untouched - Gulp 5's default utf8 handling corrupts binary files such as fonts.
11
+ export const copyFor = (srcPath, destPath) => src(srcPath, { encoding: false })
11
12
  .pipe(dest(destPath))
@@ -32,4 +32,22 @@ describe('copyFor', () => {
32
32
  done()
33
33
  })
34
34
  })
35
+
36
+ test('copies binary files byte for byte, including a leading BOM and bytes above 0x7f', done => {
37
+ const srcPath = gulpConfig.get('srcPath')
38
+ const binaryFile = `${srcPath}/font.woff`
39
+ const original = Buffer.concat([Buffer.from([0xef, 0xbb, 0xbf]), Buffer.from(Array.from({ length: 512 }, (_, i) => i % 256))])
40
+ fs.writeFileSync(binaryFile, original)
41
+ expect.assertions(1)
42
+ const browserPath = gulpConfig.get('browser.to')
43
+ copyFor(binaryFile, browserPath)
44
+ .on('finish', () => {
45
+ expect(fs.readFileSync(`${browserPath}/font.woff`).equals(original)).toBeTruthy()
46
+ done()
47
+ })
48
+ .on('error', error => {
49
+ console.error('Encountered error', error)
50
+ done()
51
+ })
52
+ })
35
53
  })
@@ -1,7 +1,36 @@
1
1
  import cache from 'gulp-cache'
2
+ import { createRequire } from 'node:module'
2
3
  import { dest, src } from 'gulp'
4
+ import path from 'node:path'
3
5
  import * as gulpConfig from '../../gulp.config.mjs'
4
- import imagemin from 'gulp-imagemin'
6
+
7
+ const installHint = 'Add it to your project with: npm install --save-dev github:jheagle/gulp-imagemin#common-js-compatibility ' +
8
+ '(the CommonJS-compatible fork - upstream gulp-imagemin is ESM-only and cannot be require()d by this package\'s CommonJS build).'
9
+
10
+ /**
11
+ * Load `gulp-imagemin`, an optional peer dependency: image optimization is off by default and its binary-download
12
+ * dependency chain is heavy, so it is only required once the images task actually runs, and it is resolved from the
13
+ * consuming project's own directory rather than from this package.
14
+ * @memberOf module:partials
15
+ * @param {string} [projectPath=process.cwd()] - The project directory to resolve `gulp-imagemin` from.
16
+ * @returns {Function} The gulp-imagemin plugin factory.
17
+ * @throws {Error} With install instructions when gulp-imagemin is missing or is the ESM-only upstream release.
18
+ */
19
+ export const loadImagemin = (projectPath = process.cwd()) => {
20
+ let loaded
21
+ try {
22
+ loaded = createRequire(path.join(projectPath, 'package.json'))('gulp-imagemin')
23
+ } catch (error) {
24
+ if (error.code === 'MODULE_NOT_FOUND' && /Cannot find module 'gulp-imagemin'/.test(error.message)) {
25
+ throw new Error(`The images task needs the optional peer dependency gulp-imagemin, which is not installed. ${installHint}`)
26
+ }
27
+ if (error.code === 'ERR_REQUIRE_ESM') {
28
+ throw new Error(`gulp-imagemin could not be loaded because it is ESM-only. ${installHint}`)
29
+ }
30
+ throw error
31
+ }
32
+ return loaded && (loaded.__esModule || loaded.default) ? loaded.default : loaded
33
+ }
5
34
 
6
35
  /**
7
36
  * Move and optimize images into the browser directory.
@@ -10,8 +39,10 @@ import imagemin from 'gulp-imagemin'
10
39
  * @param {string} [imageDest=dest/image/folder]
11
40
  * @return {stream.Stream}
12
41
  */
13
- export const imagesFor = (imageSrc = gulpConfig.get('images.from'), imageDest = gulpConfig.get('images.to')) =>
14
- src(imageSrc)
42
+ export const imagesFor = (imageSrc = gulpConfig.get('images.from'), imageDest = gulpConfig.get('images.to')) => {
43
+ const imagemin = loadImagemin()
44
+ // Gulp 5 reads files as utf8 text unless told otherwise, which corrupts binary images (and makes gulp-cache drop them).
45
+ return src(imageSrc, { encoding: false })
15
46
  .pipe(
16
47
  // Caching images that ran through imagemin
17
48
  cache(
@@ -23,3 +54,4 @@ export const imagesFor = (imageSrc = gulpConfig.get('images.from'), imageDest =
23
54
  )
24
55
  )
25
56
  .pipe(dest(imageDest))
57
+ }