js-build-tools 6.4.0 → 6.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/README.md CHANGED
@@ -561,6 +561,9 @@ Add the following to the exports in your `build-tools.config.json`:
561
561
  // The directory holding the source, each folder in it becomes a module (TypeDoc) or is scanned recursively (jsdoc).
562
562
  // jsdoc also takes a list of directories and files, for example ["functions", "gulpfile.base.mjs"]
563
563
  "from": "src",
564
+ // Generate the documentation as part of build (the default). Set it to false to only generate it when you run
565
+ // the docs task yourself (see below)
566
+ "inBuild": true,
564
567
  // The markdown file used as the front page of the documentation (a short overview of your project)
565
568
  "index": "MAIN.md",
566
569
  // 'typedoc' or 'jsdoc'. Left out (or false), it's inferred from typescript.enabled - 'typedoc' when your
@@ -582,6 +585,20 @@ While `docs.enabled` is on, `gulp build` generates the documentation instead of
582
585
  hand-written (a short overview which links to the documentation). If you also export `docs` from your gulpfile you can
583
586
  run it on its own with `gulp docs`, and can then add the script `"docs": "gulp docs"`.
584
587
 
588
+ To make generating the documentation a deliberate step instead, set `docs.inBuild` to `false`: `gulp build` then
589
+ generates neither the documentation nor the readme, and `gulp docs` is the only thing that writes to `docs.to`. This
590
+ suits a `docs.to` which points into another repository (a website, say): a build on a work-in-progress branch no
591
+ longer rewrites the published documentation, which you regenerate when you release instead. For example with
592
+ release-it, in `.release-it.json`:
593
+
594
+ ```json
595
+ {
596
+ "hooks": {
597
+ "after:bump": "npm run docs"
598
+ }
599
+ }
600
+ ```
601
+
585
602
  ### Browser automation (optional)
586
603
 
587
604
  Unlike the rest of this file, this isn't a `build-tools.config.json`-driven feature with its own task - it's a pair
package/dist/build.js CHANGED
@@ -1,73 +1,76 @@
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.build = void 0;
8
- var _bundle = require("./bundle.js");
9
- var _bundleMinify = require("./bundleMinify.js");
10
- var _partials = require("./partials.js");
11
- var _compileReadme = require("./compileReadme.js");
12
- var _typeDocs = require("./typeDocs.js");
13
- var _jsdocDocs = require("./jsdocDocs.js");
14
- var _distMinify = require("./distMinify.js");
15
- var _sourceLint = require("./sourceLint.js");
16
- var _gulp = require("gulp");
17
- var gulpConfig = _interopRequireWildcard(require("../gulp.config.js"));
18
- var _copyFonts = require("./copyFonts.js");
19
- var _images = require("./images.js");
20
- var _resolveDocsMethod = require("./partials/resolveDocsMethod.js");
21
- var _sass = require("./sass.js");
22
- var _testFull = require("./testFull.js");
23
- var cache;
24
- function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var r, c, a = Object.defineProperty, n = { __proto__: null, default: e }; if (Object(e) !== e) return n; if (cache || "function" != typeof WeakMap || (cache = new WeakMap()), cache) { if (cache.has(e)) return cache.get(e); cache.set(e, n); } for (c in e) "default" !== c && {}.hasOwnProperty.call(e, c) && ((r = a && Object.getOwnPropertyDescriptor(e, c)) && (r.get || r.set) ? a(n, c, r) : n[c] = e[c]); return n; }
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.build = void 0;
8
+ var _bundle = require("./bundle.js");
9
+ var _bundleMinify = require("./bundleMinify.js");
10
+ var _partials = require("./partials.js");
11
+ var _compileReadme = require("./compileReadme.js");
12
+ var _typeDocs = require("./typeDocs.js");
13
+ var _jsdocDocs = require("./jsdocDocs.js");
14
+ var _distMinify = require("./distMinify.js");
15
+ var _sourceLint = require("./sourceLint.js");
16
+ var _gulp = require("gulp");
17
+ var gulpConfig = _interopRequireWildcard(require("../gulp.config.js"));
18
+ var _copyFonts = require("./copyFonts.js");
19
+ var _images = require("./images.js");
20
+ var _resolveDocsMethod = require("./partials/resolveDocsMethod.js");
21
+ var _sass = require("./sass.js");
22
+ var _testFull = require("./testFull.js");
23
+ var cache;
24
+ function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var r, c, a = Object.defineProperty, n = { __proto__: null, default: e }; if (Object(e) !== e) return n; if (cache || "function" != typeof WeakMap || (cache = new WeakMap()), cache) { if (cache.has(e)) return cache.get(e); cache.set(e, n); } for (c in e) "default" !== c && {}.hasOwnProperty.call(e, c) && ((r = a && Object.getOwnPropertyDescriptor(e, c)) && (r.get || r.set) ? a(n, c, r) : n[c] = e[c]); return n; }
25
25
  /**
26
26
  * Runs several processes to build and validate the project.
27
27
  * Cleans and lints the source, distributes and minifies, bundles and minifies, creates the readme, then runs the
28
28
  * tests.
29
29
  * @memberOf module:js-build-tools
30
30
  * @returns {stream.Stream}
31
- */
32
- const build = (done = null) => {
33
- const buildActions = [(0, _partials.distSeries)(), _distMinify.distMinify];
34
- const generateDocs = gulpConfig.get('docs.enabled');
35
- const docsMethod = (0, _resolveDocsMethod.resolveDocsMethod)();
36
- if (generateDocs) {
37
- // The documentation is generated from the source itself, the readme is written by hand
38
- buildActions.push(docsMethod === 'jsdoc' ? _jsdocDocs.jsdocDocs : _typeDocs.typeDocs);
39
- } else if (gulpConfig.get('typescript.enabled')) {
40
- // For ts usage, we need to run the readme on the dist directly since that is where the .js files are located
41
- buildActions.push(_compileReadme.compileReadme);
42
- }
43
- if (gulpConfig.get('browser.enabled')) {
44
- // If not 'nodeOnly' then we also want to bundle for browser after we complete the dist directory
45
- buildActions.push(_bundle.bundle);
46
- buildActions.push(_bundleMinify.bundleMinify);
47
- }
48
- const runActions = [(0, _gulp.series)(...buildActions)];
49
- if (!generateDocs && !gulpConfig.get('typescript.enabled')) {
50
- // Since we didn't run this in series after dist because of typescript, we need to run it now. Potentially faster here.
51
- runActions.push(_compileReadme.compileReadme);
52
- }
53
- runActions.push(_testFull.testFull);
54
- if (gulpConfig.get('fonts.enabled')) {
55
- // Conditionally add Fonts process
56
- runActions.push(_copyFonts.copyFonts);
57
- }
58
- if (gulpConfig.get('images.enabled')) {
59
- // Conditionally add Images process
60
- runActions.push(_images.images);
61
- }
62
- if (gulpConfig.get('sass.enabled')) {
63
- // Conditionally add SASS process
64
- runActions.push(_sass.sass);
65
- }
66
- // clean has to finish before anything writes into the output folders - images, fonts and sass all write into
67
- // browser/, so running clean in parallel with them races (ENOTEMPTY errors from clean, or output deleted mid-write).
68
- // sourceLint fixes the real source in place before anything compiles from it (distSeries, tsFor inside it), so a
69
- // build never reads a mix of pre- and post-fix content; clean only touches dist/browser output, so the two can
70
- // run alongside each other.
71
- return (0, _gulp.series)((0, _gulp.parallel)(_partials.clean, _sourceLint.sourceLint), (0, _gulp.parallel)(...runActions))(done);
72
- };
31
+ */
32
+ const build = (done = null) => {
33
+ const buildActions = [(0, _partials.distSeries)(), _distMinify.distMinify];
34
+ const generateDocs = gulpConfig.get('docs.enabled');
35
+ const docsMethod = (0, _resolveDocsMethod.resolveDocsMethod)();
36
+ if (generateDocs) {
37
+ // The documentation is generated from the source itself, the readme is written by hand. With docs.inBuild off,
38
+ // generating it is left to the standalone docs task (gulp docs), so a build never rewrites it.
39
+ if (gulpConfig.get('docs.inBuild') !== false) {
40
+ buildActions.push(docsMethod === 'jsdoc' ? _jsdocDocs.jsdocDocs : _typeDocs.typeDocs);
41
+ }
42
+ } else if (gulpConfig.get('typescript.enabled')) {
43
+ // For ts usage, we need to run the readme on the dist directly since that is where the .js files are located
44
+ buildActions.push(_compileReadme.compileReadme);
45
+ }
46
+ if (gulpConfig.get('browser.enabled')) {
47
+ // If not 'nodeOnly' then we also want to bundle for browser after we complete the dist directory
48
+ buildActions.push(_bundle.bundle);
49
+ buildActions.push(_bundleMinify.bundleMinify);
50
+ }
51
+ const runActions = [(0, _gulp.series)(...buildActions)];
52
+ if (!generateDocs && !gulpConfig.get('typescript.enabled')) {
53
+ // Since we didn't run this in series after dist because of typescript, we need to run it now. Potentially faster here.
54
+ runActions.push(_compileReadme.compileReadme);
55
+ }
56
+ runActions.push(_testFull.testFull);
57
+ if (gulpConfig.get('fonts.enabled')) {
58
+ // Conditionally add Fonts process
59
+ runActions.push(_copyFonts.copyFonts);
60
+ }
61
+ if (gulpConfig.get('images.enabled')) {
62
+ // Conditionally add Images process
63
+ runActions.push(_images.images);
64
+ }
65
+ if (gulpConfig.get('sass.enabled')) {
66
+ // Conditionally add SASS process
67
+ runActions.push(_sass.sass);
68
+ }
69
+ // clean has to finish before anything writes into the output folders - images, fonts and sass all write into
70
+ // browser/, so running clean in parallel with them races (ENOTEMPTY errors from clean, or output deleted mid-write).
71
+ // sourceLint fixes the real source in place before anything compiles from it (distSeries, tsFor inside it), so a
72
+ // build never reads a mix of pre- and post-fix content; clean only touches dist/browser output, so the two can
73
+ // run alongside each other.
74
+ return (0, _gulp.series)((0, _gulp.parallel)(_partials.clean, _sourceLint.sourceLint), (0, _gulp.parallel)(...runActions))(done);
75
+ };
73
76
  exports.build = build;
package/dist/build.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.build=void 0;var cache,_bundle=require("./bundle.js"),_bundleMinify=require("./bundleMinify.js"),_partials=require("./partials.js"),_compileReadme=require("./compileReadme.js"),_typeDocs=require("./typeDocs.js"),_jsdocDocs=require("./jsdocDocs.js"),_distMinify=require("./distMinify.js"),_sourceLint=require("./sourceLint.js"),_gulp=require("gulp"),gulpConfig=_interopRequireWildcard(require("../gulp.config.js")),_copyFonts=require("./copyFonts.js"),_images=require("./images.js"),_resolveDocsMethod=require("./partials/resolveDocsMethod.js"),_sass=require("./sass.js"),_testFull=require("./testFull.js");function _interopRequireWildcard(e,s){if(!s&&e&&e.__esModule)return e;var i,r,t=Object.defineProperty,l={__proto__:null,default:e};if(Object(e)!==e)return l;if(cache||"function"!=typeof WeakMap||(cache=new WeakMap),cache){if(cache.has(e))return cache.get(e);cache.set(e,l)}for(r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((i=t&&Object.getOwnPropertyDescriptor(e,r))&&(i.get||i.set)?t(l,r,i):l[r]=e[r]);return l}const build=(e=null)=>{const s=[(0,_partials.distSeries)(),_distMinify.distMinify],i=gulpConfig.get("docs.enabled"),r=(0,_resolveDocsMethod.resolveDocsMethod)();i?s.push("jsdoc"===r?_jsdocDocs.jsdocDocs:_typeDocs.typeDocs):gulpConfig.get("typescript.enabled")&&s.push(_compileReadme.compileReadme),gulpConfig.get("browser.enabled")&&(s.push(_bundle.bundle),s.push(_bundleMinify.bundleMinify));const t=[(0,_gulp.series)(...s)];return i||gulpConfig.get("typescript.enabled")||t.push(_compileReadme.compileReadme),t.push(_testFull.testFull),gulpConfig.get("fonts.enabled")&&t.push(_copyFonts.copyFonts),gulpConfig.get("images.enabled")&&t.push(_images.images),gulpConfig.get("sass.enabled")&&t.push(_sass.sass),(0,_gulp.series)((0,_gulp.parallel)(_partials.clean,_sourceLint.sourceLint),(0,_gulp.parallel)(...t))(e)};exports.build=build;
1
+ "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.build=void 0;var cache,_bundle=require("./bundle.js"),_bundleMinify=require("./bundleMinify.js"),_partials=require("./partials.js"),_compileReadme=require("./compileReadme.js"),_typeDocs=require("./typeDocs.js"),_jsdocDocs=require("./jsdocDocs.js"),_distMinify=require("./distMinify.js"),_sourceLint=require("./sourceLint.js"),_gulp=require("gulp"),gulpConfig=_interopRequireWildcard(require("../gulp.config.js")),_copyFonts=require("./copyFonts.js"),_images=require("./images.js"),_resolveDocsMethod=require("./partials/resolveDocsMethod.js"),_sass=require("./sass.js"),_testFull=require("./testFull.js");function _interopRequireWildcard(e,s){if(!s&&e&&e.__esModule)return e;var i,r,t=Object.defineProperty,l={__proto__:null,default:e};if(Object(e)!==e)return l;if(cache||"function"!=typeof WeakMap||(cache=new WeakMap),cache){if(cache.has(e))return cache.get(e);cache.set(e,l)}for(r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((i=t&&Object.getOwnPropertyDescriptor(e,r))&&(i.get||i.set)?t(l,r,i):l[r]=e[r]);return l}const build=(e=null)=>{const s=[(0,_partials.distSeries)(),_distMinify.distMinify],i=gulpConfig.get("docs.enabled"),r=(0,_resolveDocsMethod.resolveDocsMethod)();i?!1!==gulpConfig.get("docs.inBuild")&&s.push("jsdoc"===r?_jsdocDocs.jsdocDocs:_typeDocs.typeDocs):gulpConfig.get("typescript.enabled")&&s.push(_compileReadme.compileReadme),gulpConfig.get("browser.enabled")&&(s.push(_bundle.bundle),s.push(_bundleMinify.bundleMinify));const t=[(0,_gulp.series)(...s)];return i||gulpConfig.get("typescript.enabled")||t.push(_compileReadme.compileReadme),t.push(_testFull.testFull),gulpConfig.get("fonts.enabled")&&t.push(_copyFonts.copyFonts),gulpConfig.get("images.enabled")&&t.push(_images.images),gulpConfig.get("sass.enabled")&&t.push(_sass.sass),(0,_gulp.series)((0,_gulp.parallel)(_partials.clean,_sourceLint.sourceLint),(0,_gulp.parallel)(...t))(e)};exports.build=build;
@@ -8,6 +8,7 @@ exports.lintSource = void 0;
8
8
  var gulpConfig = _interopRequireWildcard(require("../../gulp.config.js"));
9
9
  var _gulp = require("gulp");
10
10
  var _neostandardLint = require("./neostandardLint.js");
11
+ var _nodeStream = require("node:stream");
11
12
  var cache;
12
13
  function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var r, c, a = Object.defineProperty, n = { __proto__: null, default: e }; if (Object(e) !== e) return n; if (cache || "function" != typeof WeakMap || (cache = new WeakMap()), cache) { if (cache.has(e)) return cache.get(e); cache.set(e, n); } for (c in e) "default" !== c && {}.hasOwnProperty.call(e, c) && ((r = a && Object.getOwnPropertyDescriptor(e, c)) && (r.get || r.set) ? a(n, c, r) : n[c] = e[c]); return n; }
13
14
  /**
@@ -30,12 +31,22 @@ const lintSource = ({
30
31
  const linter = (0, _neostandardLint.neostandardLint)({
31
32
  fix: true,
32
33
  ts,
33
- rules: gulpConfig.get('lint.rules'),
34
- failOnError
34
+ rules: gulpConfig.get('lint.rules')
35
35
  });
36
- const written = (0, _gulp.src)(patterns).pipe(linter).pipe((0, _gulp.dest)(srcPath));
37
- // pipe() does not forward errors, and gulp only watches the returned stream, so pass the linter's failure along.
38
- linter.on('error', error => written.emit('error', error));
36
+ // Fail only once dest() has written every fixed file: gulp exits as soon as a task fails, so failing from the
37
+ // linter itself (before dest() finished) could leave a file that dest() had opened, but not yet written, empty.
38
+ // dest() passes each file on only after writing it, so this last step's flush runs after the final write. Files
39
+ // are consumed here rather than passed on, so nothing downstream has to read them.
40
+ const written = new _nodeStream.Transform({
41
+ objectMode: true,
42
+ transform: (file, enc, callback) => callback(),
43
+ flush: callback => failOnError && linter.totals.errors ? callback(new Error(`neostandard found ${linter.totals.errors} lint error(s) that could not be fixed automatically.`)) : callback()
44
+ });
45
+ const saved = (0, _gulp.src)(patterns).pipe(linter).pipe((0, _gulp.dest)(srcPath));
46
+ saved.pipe(written);
47
+ // pipe() does not forward errors, and gulp only watches the returned stream, so pass any failure along.
48
+ linter.on('error', error => written.destroy(error));
49
+ saved.on('error', error => written.destroy(error));
39
50
  return written;
40
51
  };
41
52
  exports.lintSource = lintSource;
@@ -113,7 +113,7 @@ const printFileReport = (file, result) => {
113
113
  * @param {function(Object): Promise<{lintText: Function}>} [loadEslint] - Resolves an ESLint-shaped linter. Real
114
114
  * callers should never need to pass this - it exists so tests can supply a stand-in, since this project's
115
115
  * CommonJS-transform-based Jest setup cannot execute a real dynamic import() of neostandard (ESM-only).
116
- * @returns {stream.Transform}
116
+ * @returns {stream.Transform} With a `totals` property ({errors, warnings}), complete once the stream ends.
117
117
  */
118
118
  const neostandardLint = ({
119
119
  fix = true,
@@ -126,7 +126,7 @@ const neostandardLint = ({
126
126
  warnings: 0
127
127
  };
128
128
  let eslintPromise = null;
129
- return _through.default.obj(function (file, enc, callback) {
129
+ const linter = _through.default.obj(function (file, enc, callback) {
130
130
  if (file.isNull()) {
131
131
  callback(null, file);
132
132
  return;
@@ -169,5 +169,8 @@ const neostandardLint = ({
169
169
  }
170
170
  callback();
171
171
  });
172
+ // Read by lintSource, which can only fail once the fixed files have been written, after this stream has ended.
173
+ linter.totals = totals;
174
+ return linter;
172
175
  };
173
176
  exports.neostandardLint = neostandardLint;
@@ -26,8 +26,11 @@ export const build = (done = null) => {
26
26
  const generateDocs = gulpConfig.get('docs.enabled')
27
27
  const docsMethod = resolveDocsMethod()
28
28
  if (generateDocs) {
29
- // The documentation is generated from the source itself, the readme is written by hand
30
- buildActions.push(docsMethod === 'jsdoc' ? jsdocDocs : typeDocs)
29
+ // The documentation is generated from the source itself, the readme is written by hand. With docs.inBuild off,
30
+ // generating it is left to the standalone docs task (gulp docs), so a build never rewrites it.
31
+ if (gulpConfig.get('docs.inBuild') !== false) {
32
+ buildActions.push(docsMethod === 'jsdoc' ? jsdocDocs : typeDocs)
33
+ }
31
34
  } else if (gulpConfig.get('typescript.enabled')) {
32
35
  // For ts usage, we need to run the readme on the dist directly since that is where the .js files are located
33
36
  buildActions.push(compileReadme)
@@ -676,6 +676,29 @@ describe('build', () => {
676
676
  })
677
677
  }, 30000)
678
678
 
679
+ test('with docs.inBuild off, build generates neither the documentation nor the readme', done => {
680
+ const readmePath = gulpConfig.get('readme.to')
681
+ const readmeFile = gulpConfig.get('readme.file')
682
+ fs.rmSync(`${readmePath}/${readmeFile}`, { force: true })
683
+ typeDocs.mockClear()
684
+ jsdocDocs.mockClear()
685
+ gulpConfig.set('browser.enabled', false)
686
+ gulpConfig.set('typescript.enabled', false)
687
+ gulpConfig.set('docs.enabled', true)
688
+ gulpConfig.set('docs.inBuild', false)
689
+ expect.assertions(4)
690
+ build(() => {
691
+ gulpConfig.set('docs.enabled', false)
692
+ gulpConfig.set('docs.inBuild', true)
693
+ expect(jsdocDocs).not.toHaveBeenCalled()
694
+ expect(typeDocs).not.toHaveBeenCalled()
695
+ // The readme is still written by hand while docs.enabled is on
696
+ expect(fs.existsSync(`${readmePath}/${readmeFile}`)).toBe(false)
697
+ expect(testFull).toHaveBeenCalled()
698
+ done()
699
+ })
700
+ }, 30000)
701
+
679
702
  test('docs.method overrides the typescript.enabled inference either way', done => {
680
703
  const srcPath = gulpConfig.get('srcPath')
681
704
  const rootPath = gulpConfig.get('rootPath')
@@ -1,6 +1,7 @@
1
1
  import * as gulpConfig from '../../gulp.config.mjs'
2
2
  import { dest, src } from 'gulp'
3
3
  import { neostandardLint } from './neostandardLint.mjs'
4
+ import { Transform } from 'node:stream'
4
5
 
5
6
  /**
6
7
  * Lint the project's source (srcPath: .js, .mjs and .cjs, plus .ts when typescript.enabled) with neostandard and
@@ -17,11 +18,24 @@ export const lintSource = ({ failOnError = false } = {}) => {
17
18
  if (ts) {
18
19
  patterns.push(`${srcPath}/**/*.ts`)
19
20
  }
20
- const linter = neostandardLint({ fix: true, ts, rules: gulpConfig.get('lint.rules'), failOnError })
21
- const written = src(patterns)
21
+ const linter = neostandardLint({ fix: true, ts, rules: gulpConfig.get('lint.rules') })
22
+ // Fail only once dest() has written every fixed file: gulp exits as soon as a task fails, so failing from the
23
+ // linter itself (before dest() finished) could leave a file that dest() had opened, but not yet written, empty.
24
+ // dest() passes each file on only after writing it, so this last step's flush runs after the final write. Files
25
+ // are consumed here rather than passed on, so nothing downstream has to read them.
26
+ const written = new Transform({
27
+ objectMode: true,
28
+ transform: (file, enc, callback) => callback(),
29
+ flush: callback => failOnError && linter.totals.errors
30
+ ? callback(new Error(`neostandard found ${linter.totals.errors} lint error(s) that could not be fixed automatically.`))
31
+ : callback()
32
+ })
33
+ const saved = src(patterns)
22
34
  .pipe(linter)
23
35
  .pipe(dest(srcPath))
24
- // pipe() does not forward errors, and gulp only watches the returned stream, so pass the linter's failure along.
25
- linter.on('error', error => written.emit('error', error))
36
+ saved.pipe(written)
37
+ // pipe() does not forward errors, and gulp only watches the returned stream, so pass any failure along.
38
+ linter.on('error', error => written.destroy(error))
39
+ saved.on('error', error => written.destroy(error))
26
40
  return written
27
41
  }
@@ -87,7 +87,7 @@ const printFileReport = (file, result) => {
87
87
  * @param {function(Object): Promise<{lintText: Function}>} [loadEslint] - Resolves an ESLint-shaped linter. Real
88
88
  * callers should never need to pass this - it exists so tests can supply a stand-in, since this project's
89
89
  * CommonJS-transform-based Jest setup cannot execute a real dynamic import() of neostandard (ESM-only).
90
- * @returns {stream.Transform}
90
+ * @returns {stream.Transform} With a `totals` property ({errors, warnings}), complete once the stream ends.
91
91
  */
92
92
  export const neostandardLint = (
93
93
  { fix = true, ts = false, rules = {}, failOnError = false } = {},
@@ -95,7 +95,7 @@ export const neostandardLint = (
95
95
  ) => {
96
96
  const totals = { errors: 0, warnings: 0 }
97
97
  let eslintPromise = null
98
- return through.obj(
98
+ const linter = through.obj(
99
99
  function (file, enc, callback) {
100
100
  if (file.isNull()) {
101
101
  callback(null, file)
@@ -136,4 +136,7 @@ export const neostandardLint = (
136
136
  callback()
137
137
  }
138
138
  )
139
+ // Read by lintSource, which can only fail once the fixed files have been written, after this stream has ended.
140
+ linter.totals = totals
141
+ return linter
139
142
  }
@@ -143,4 +143,24 @@ describe('lint', () => {
143
143
  done(new Error('lint should have failed on the unused variable'))
144
144
  })
145
145
  }, 20000)
146
+
147
+ test('only fails once every fixed file has been written back', done => {
148
+ // gulp exits as soon as a task fails, so failing while a file is still being written can leave that file empty.
149
+ const srcPath = gulpConfig.get('srcPath')
150
+ const fixable = ['a', 'b', 'c'].map(name => `${srcPath}/fixable-${name}.js`)
151
+ fixable.forEach(file => fs.writeFileSync(file, 'const x = "y";\n\nmodule.exports = x;\n'))
152
+ fs.writeFileSync(`${srcPath}/unfixable.js`, 'const unused = 1\n')
153
+ const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {})
154
+ expect.assertions(fixable.length)
155
+ lint()
156
+ .on('error', () => {
157
+ logSpy.mockRestore()
158
+ fixable.forEach(file => expect(fs.readFileSync(file).toString()).toEqual('const x = \'y\'\n\nmodule.exports = x\n'))
159
+ done()
160
+ })
161
+ .on('finish', () => {
162
+ logSpy.mockRestore()
163
+ done(new Error('lint should have failed on the unused variable'))
164
+ })
165
+ }, 30000)
146
166
  })
package/gulp.config.mjs CHANGED
@@ -135,6 +135,9 @@ import { fileExists } from 'test-filesystem'
135
135
  * build does this instead of generating the readme from the jsdoc comments of the compiled files.
136
136
  * @property {ArrayableSetting} from - The directory holding the source, each folder in it becomes a module (TypeDoc) or
137
137
  * is scanned recursively (jsdoc). jsdoc also takes a list of directories and files.
138
+ * @property {BooleanSetting} inBuild - Generate the documentation as part of build (the default). Turn it off to only
139
+ * generate it when you run the docs task yourself (gulp docs), for example when docs.to points into another
140
+ * repository and you only want to publish the documentation of a release.
138
141
  * @property {StringSetting} index - The markdown file used as the front page of the documentation.
139
142
  * @property {StringSetting} logLevel - How much TypeDoc reports: Verbose, Info, Warn, Error or None. Not used by jsdoc.
140
143
  * @property {FlagStringSetting} method - 'typedoc' or 'jsdoc'. False (the default) infers it from typescript.enabled -
@@ -216,6 +219,7 @@ const setDefaults = {
216
219
  docs: {
217
220
  enabled: false,
218
221
  from: 'src',
222
+ inBuild: true,
219
223
  index: 'MAIN.md',
220
224
  logLevel: 'Warn',
221
225
  method: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-build-tools",
3
- "version": "6.4.0",
3
+ "version": "6.5.0",
4
4
  "description": "Standardised build process with gulp.",
5
5
  "main": "gulpfile.base.js",
6
6
  "exports": {