js-build-tools 6.5.0 → 6.7.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 (169) hide show
  1. package/LICENSE +674 -674
  2. package/README.md +762 -692
  3. package/babel.config.js +28 -28
  4. package/dist/build.js +75 -75
  5. package/dist/bundle.js +19 -19
  6. package/dist/bundleMinify.js +7 -7
  7. package/dist/compileReadme.js +4 -4
  8. package/dist/copyFiles.js +17 -17
  9. package/dist/copyFonts.js +4 -4
  10. package/dist/default.js +5 -5
  11. package/dist/dist.js +4 -4
  12. package/dist/distMinify.js +4 -4
  13. package/dist/docs.js +7 -7
  14. package/dist/images.js +4 -4
  15. package/dist/jsdocDocs.js +6 -6
  16. package/dist/lint.js +16 -16
  17. package/dist/partials/addToReadme.js +9 -9
  18. package/dist/partials/beginWatcher.js +4 -4
  19. package/dist/partials/clean.js +5 -5
  20. package/dist/partials/copyFor.js +9 -9
  21. package/dist/partials/distFor.js +6 -6
  22. package/dist/partials/distForSrc.js +5 -5
  23. package/dist/partials/distSeries.js +7 -7
  24. package/dist/partials/imagesFor.js +93 -56
  25. package/dist/partials/jsdocDocsFor.js +46 -46
  26. package/dist/partials/lintSource.js +51 -51
  27. package/dist/partials/loadPeer.js +99 -99
  28. package/dist/partials/loadPlaywright.js +14 -14
  29. package/dist/partials/minifyFor.js +20 -20
  30. package/dist/partials/neostandardLint.js +175 -175
  31. package/dist/partials/readmeTemplate.js +4 -4
  32. package/dist/partials/removeDirectory.js +11 -11
  33. package/dist/partials/resolveDocsMethod.js +6 -6
  34. package/dist/partials/runOnChange.js +31 -31
  35. package/dist/partials/sassFor.js +6 -6
  36. package/dist/partials/serveStatic.js +9 -9
  37. package/dist/partials/tsFor.js +32 -32
  38. package/dist/partials/typeDocsFor.js +57 -51
  39. package/dist/partials.js +176 -176
  40. package/dist/sourceLint.js +18 -18
  41. package/dist/test-helpers/isolateGulpCache.js +24 -24
  42. package/dist/test-helpers/setUp.js +4 -4
  43. package/dist/testFull.js +5 -5
  44. package/dist/testQuick.js +5 -5
  45. package/dist/typeDocs.js +5 -5
  46. package/dist/typeScript.js +4 -4
  47. package/dist/utilities/convertMjsFilesForDocs.js +12 -12
  48. package/dist/utilities/dotGet.js +54 -54
  49. package/dist/utilities/dotNotate.js +79 -79
  50. package/dist/utilities/dotSet.js +45 -45
  51. package/dist/utilities/esmNamedExportsToCjs.js +15 -15
  52. package/dist/utilities/getBundleConfigs.js +9 -9
  53. package/dist/utilities/isObject.js +6 -6
  54. package/dist/utilities/strAfter.js +7 -7
  55. package/dist/utilities/strBefore.js +7 -7
  56. package/dist/watchFull.js +4 -4
  57. package/dist/watchTest.js +4 -4
  58. package/functions/build.mjs +72 -72
  59. package/functions/build.test.mjs +763 -759
  60. package/functions/bundle.mjs +44 -44
  61. package/functions/bundle.test.mjs +219 -219
  62. package/functions/bundleMinify.mjs +16 -16
  63. package/functions/bundleMinify.test.mjs +37 -37
  64. package/functions/compileReadme.mjs +9 -9
  65. package/functions/compileReadme.test.mjs +18 -18
  66. package/functions/copyFiles.mjs +32 -32
  67. package/functions/copyFiles.test.mjs +78 -78
  68. package/functions/copyFonts.mjs +9 -9
  69. package/functions/copyFonts.test.mjs +32 -32
  70. package/functions/default.mjs +12 -12
  71. package/functions/default.test.mjs +33 -33
  72. package/functions/dist.mjs +8 -8
  73. package/functions/dist.test.mjs +44 -44
  74. package/functions/distMinify.mjs +9 -9
  75. package/functions/distMinify.test.mjs +15 -15
  76. package/functions/docs.mjs +13 -13
  77. package/functions/docs.test.mjs +45 -45
  78. package/functions/gulpConfigEntries.test.mjs +23 -23
  79. package/functions/images.mjs +8 -8
  80. package/functions/images.test.mjs +142 -142
  81. package/functions/jsdocDocs.mjs +10 -10
  82. package/functions/jsdocEsmExports.test.mjs +24 -24
  83. package/functions/lint.mjs +9 -9
  84. package/functions/partials/addToReadme.mjs +25 -25
  85. package/functions/partials/addToReadme.test.mjs +34 -34
  86. package/functions/partials/beginWatcher.mjs +9 -9
  87. package/functions/partials/beginWatcher.test.mjs +19 -19
  88. package/functions/partials/clean.mjs +13 -13
  89. package/functions/partials/clean.test.mjs +22 -22
  90. package/functions/partials/copyFor.mjs +15 -15
  91. package/functions/partials/copyFor.test.mjs +53 -53
  92. package/functions/partials/distFor.mjs +22 -22
  93. package/functions/partials/distFor.test.mjs +195 -195
  94. package/functions/partials/distForSrc.mjs +11 -11
  95. package/functions/partials/distForSrc.test.mjs +18 -18
  96. package/functions/partials/distSeries.mjs +21 -21
  97. package/functions/partials/distSeries.test.mjs +34 -34
  98. package/functions/partials/imagesFor.mjs +83 -50
  99. package/functions/partials/imagesFor.test.mjs +287 -229
  100. package/functions/partials/jsdocDocsFor.mjs +163 -152
  101. package/functions/partials/jsdocDocsFor.test.mjs +182 -152
  102. package/functions/partials/lintSource.mjs +41 -41
  103. package/functions/partials/loadPeer.mjs +76 -76
  104. package/functions/partials/loadPeer.test.mjs +58 -58
  105. package/functions/partials/loadPlaywright.mjs +23 -23
  106. package/functions/partials/loadPlaywright.test.mjs +70 -70
  107. package/functions/partials/minifyFor.mjs +15 -15
  108. package/functions/partials/minifyFor.test.mjs +49 -49
  109. package/functions/partials/neostandardLint.mjs +142 -142
  110. package/functions/partials/neostandardLint.test.mjs +131 -131
  111. package/functions/partials/readmeTemplate.mjs +12 -12
  112. package/functions/partials/readmeTemplate.test.mjs +34 -34
  113. package/functions/partials/removeDirectory.mjs +34 -34
  114. package/functions/partials/removeDirectory.test.mjs +72 -72
  115. package/functions/partials/resolveDocsMethod.mjs +10 -10
  116. package/functions/partials/resolveDocsMethod.test.mjs +33 -33
  117. package/functions/partials/runOnChange.mjs +54 -54
  118. package/functions/partials/runOnChange.test.mjs +95 -95
  119. package/functions/partials/sassFor.mjs +25 -25
  120. package/functions/partials/sassFor.test.mjs +81 -81
  121. package/functions/partials/serveStatic.mjs +47 -47
  122. package/functions/partials/serveStatic.test.mjs +71 -71
  123. package/functions/partials/tsFor.mjs +92 -92
  124. package/functions/partials/tsFor.test.mjs +88 -88
  125. package/functions/partials/typeDocsFor.mjs +231 -227
  126. package/functions/partials/typeDocsFor.test.mjs +263 -252
  127. package/functions/partials.mjs +31 -31
  128. package/functions/partials.test.mjs +7 -7
  129. package/functions/sass.mjs +4 -4
  130. package/functions/sass.test.mjs +31 -31
  131. package/functions/sourceLint.mjs +13 -13
  132. package/functions/sourceLint.test.mjs +166 -166
  133. package/functions/test-helpers/isolateGulpCache.mjs +16 -16
  134. package/functions/test-helpers/neostandardLintWithRealNeostandard.mjs +40 -40
  135. package/functions/test-helpers/setUp.mjs +100 -100
  136. package/functions/test-helpers/setUp.test.mjs +19 -19
  137. package/functions/testFull.mjs +17 -17
  138. package/functions/testFull.test.mjs +17 -17
  139. package/functions/testHelpers.mjs +10 -10
  140. package/functions/testHelpers.test.mjs +7 -7
  141. package/functions/testQuick.mjs +17 -17
  142. package/functions/testQuick.test.mjs +17 -17
  143. package/functions/typeDocs.mjs +9 -9
  144. package/functions/typeDocs.test.mjs +11 -11
  145. package/functions/typeScript.mjs +8 -8
  146. package/functions/typeScript.test.mjs +11 -11
  147. package/functions/utilities/convertMjsFilesForDocs.mjs +34 -34
  148. package/functions/utilities/convertMjsFilesForDocs.test.mjs +58 -58
  149. package/functions/utilities/dotGet.mjs +49 -49
  150. package/functions/utilities/dotNotate.mjs +73 -73
  151. package/functions/utilities/dotNotate.test.mjs +41 -41
  152. package/functions/utilities/dotSet.mjs +40 -40
  153. package/functions/utilities/esmNamedExportsToCjs.mjs +30 -30
  154. package/functions/utilities/esmNamedExportsToCjs.test.mjs +47 -47
  155. package/functions/utilities/getBundleConfigs.mjs +30 -30
  156. package/functions/utilities/isObject.mjs +8 -8
  157. package/functions/utilities/strAfter.mjs +12 -12
  158. package/functions/utilities/strBefore.mjs +12 -12
  159. package/functions/watchFull.mjs +9 -9
  160. package/functions/watchFull.test.mjs +41 -41
  161. package/functions/watchTest.mjs +10 -10
  162. package/functions/watchTest.test.mjs +49 -49
  163. package/gulp.config.js +4 -4
  164. package/gulp.config.mjs +344 -327
  165. package/gulpfile.base.js +51 -51
  166. package/gulpfile.base.mjs +29 -29
  167. package/jsdoc.base.js +16 -16
  168. package/jsdoc.esmExports.js +17 -17
  169. package/package.json +146 -145
@@ -1,142 +1,142 @@
1
- import fs from 'node:fs'
2
- import path from 'node:path'
3
- import through from 'through2'
4
- import { ESLint } from 'eslint'
5
- import { importModule } from './loadPeer.mjs'
6
-
7
- // neostandard (js-build-tools' chosen style - a maintained, TypeScript-aware successor to `standard`, see
8
- // https://github.com/neostandard/neostandard) ships ESM-only, see importModule for how it is imported.
9
- const importNeostandard = () => importModule('neostandard')
10
-
11
- /**
12
- * Read the `env` a project already declared for Standard (the `standard` field in its package.json), so a project
13
- * migrating from `standard` keeps its globals (jest, browser, ...) without touching package.json.
14
- * @param {string} cwd
15
- * @returns {Array<string>}
16
- * @private
17
- */
18
- const readEnv = cwd => {
19
- try {
20
- return JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8')).standard?.env ?? []
21
- } catch {
22
- return []
23
- }
24
- }
25
-
26
- /**
27
- * Build an ESLint instance configured with neostandard, honoring the project's own package.json `standard.env`,
28
- * plus an optional layer of rule overrides (see gulp.config.mjs's `lint.rules`) for a rule that is wrong for this
29
- * project specifically rather than something to fix in its code - for example a test fixture whose content
30
- * neostandard mistakes for a real syntax problem. Scoped to the same files neostandard itself lints (its own
31
- * `defaultFilePatterns.all`), per neostandard's own guidance: an unscoped rule-tweak layer would otherwise apply
32
- * to every lintable file, including ones from an unrelated plugin this project never defined.
33
- * @param {Object} options
34
- * @param {string} options.cwd
35
- * @param {boolean} options.ts
36
- * @param {boolean} options.fix
37
- * @param {Object<string, *>} [options.rules={}]
38
- * @returns {Promise<ESLint>} eslint's ESLint instance
39
- * @private
40
- */
41
- const buildEslint = async ({ cwd, ts, fix, rules = {} }) => {
42
- const { neostandard, defaultFilePatterns } = await importNeostandard()
43
- const baseConfig = neostandard({ ts, env: readEnv(cwd) })
44
- if (Object.keys(rules).length) {
45
- baseConfig.push({ files: defaultFilePatterns.all, rules })
46
- }
47
- return new ESLint({
48
- cwd,
49
- fix,
50
- overrideConfigFile: true,
51
- baseConfig
52
- })
53
- }
54
-
55
- /**
56
- * Print a file's lint messages, if it has any.
57
- * @memberOf module:partials
58
- * @param {Vinyl} file
59
- * @param {{messages: Array<{line: number, column: number, message: string}>, errorCount: number, warningCount: number}} result
60
- * @returns {undefined}
61
- */
62
- const printFileReport = (file, result) => {
63
- if (!result.messages.length) {
64
- return
65
- }
66
- console.log(path.relative(file.cwd, file.path))
67
- result.messages.forEach(({ line, column, message }) => console.log(`line ${line}:${column}\t${message}`))
68
- console.log(`✖ ${result.errorCount} error\t⚠ ${result.warningCount} warning\n`)
69
- }
70
-
71
- /**
72
- * Lint every file streaming through with neostandard and, unless `fix` is false, apply the automatic fixes to the
73
- * file's contents so a following `dest()` writes the fixed version. Remaining problems are printed along with a
74
- * summary once the stream ends; they only fail the stream when `failOnError` is set (the standalone `lint` task),
75
- * never during `build`.
76
- *
77
- * One ESLint instance is built from the first file's own cwd and reused for the rest of the stream, since every
78
- * file in a given lint pass shares the same project (and so the same config).
79
- * @memberOf module:partials
80
- * @param {Object} [options={}]
81
- * @param {boolean} [options.fix=true] - Apply neostandard's automatic fixes to the file contents.
82
- * @param {boolean} [options.ts=false] - Lint `.ts` files too, with type-aware rules (see neostandard's `ts` option).
83
- * @param {Object<string, *>} [options.rules={}] - ESLint rule overrides layered on top of neostandard's own rules
84
- * (see gulp.config.mjs's `lint.rules`).
85
- * @param {boolean} [options.failOnError=false] - Emit an error once the stream ends if any lint errors remain after
86
- * fixing, so a command-line run exits non-zero.
87
- * @param {function(Object): Promise<{lintText: Function}>} [loadEslint] - Resolves an ESLint-shaped linter. Real
88
- * callers should never need to pass this - it exists so tests can supply a stand-in, since this project's
89
- * CommonJS-transform-based Jest setup cannot execute a real dynamic import() of neostandard (ESM-only).
90
- * @returns {stream.Transform} With a `totals` property ({errors, warnings}), complete once the stream ends.
91
- */
92
- export const neostandardLint = (
93
- { fix = true, ts = false, rules = {}, failOnError = false } = {},
94
- loadEslint = buildEslint
95
- ) => {
96
- const totals = { errors: 0, warnings: 0 }
97
- let eslintPromise = null
98
- const linter = through.obj(
99
- function (file, enc, callback) {
100
- if (file.isNull()) {
101
- callback(null, file)
102
- return
103
- }
104
- if (file.isStream()) {
105
- callback(new Error('neostandardLint: streaming file contents are not supported.'))
106
- return
107
- }
108
- if (!eslintPromise) {
109
- eslintPromise = loadEslint({ cwd: file.cwd, ts, fix, rules })
110
- }
111
- eslintPromise
112
- .then(eslint => eslint.lintText(file.contents.toString(), { filePath: file.path }))
113
- .then(results => {
114
- // No result at all means the file is ignored by the project's ESLint configuration.
115
- const [result] = results
116
- if (result) {
117
- if (typeof result.output === 'string') {
118
- file.contents = Buffer.from(result.output)
119
- }
120
- totals.errors += result.errorCount
121
- totals.warnings += result.warningCount
122
- printFileReport(file, result)
123
- }
124
- callback(null, file)
125
- }, callback)
126
- },
127
- function (callback) {
128
- console.log('neostandard linter results')
129
- console.log('======================================')
130
- console.log(`✖ Errors total: ${totals.errors}`)
131
- console.log(`⚠ Warnings total: ${totals.warnings}\n`)
132
- if (failOnError && totals.errors) {
133
- callback(new Error(`neostandard found ${totals.errors} lint error(s) that could not be fixed automatically.`))
134
- return
135
- }
136
- callback()
137
- }
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
142
- }
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import through from 'through2'
4
+ import { ESLint } from 'eslint'
5
+ import { importModule } from './loadPeer.mjs'
6
+
7
+ // neostandard (js-build-tools' chosen style - a maintained, TypeScript-aware successor to `standard`, see
8
+ // https://github.com/neostandard/neostandard) ships ESM-only, see importModule for how it is imported.
9
+ const importNeostandard = () => importModule('neostandard')
10
+
11
+ /**
12
+ * Read the `env` a project already declared for Standard (the `standard` field in its package.json), so a project
13
+ * migrating from `standard` keeps its globals (jest, browser, ...) without touching package.json.
14
+ * @param {string} cwd
15
+ * @returns {Array<string>}
16
+ * @private
17
+ */
18
+ const readEnv = cwd => {
19
+ try {
20
+ return JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8')).standard?.env ?? []
21
+ } catch {
22
+ return []
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Build an ESLint instance configured with neostandard, honoring the project's own package.json `standard.env`,
28
+ * plus an optional layer of rule overrides (see gulp.config.mjs's `lint.rules`) for a rule that is wrong for this
29
+ * project specifically rather than something to fix in its code - for example a test fixture whose content
30
+ * neostandard mistakes for a real syntax problem. Scoped to the same files neostandard itself lints (its own
31
+ * `defaultFilePatterns.all`), per neostandard's own guidance: an unscoped rule-tweak layer would otherwise apply
32
+ * to every lintable file, including ones from an unrelated plugin this project never defined.
33
+ * @param {Object} options
34
+ * @param {string} options.cwd
35
+ * @param {boolean} options.ts
36
+ * @param {boolean} options.fix
37
+ * @param {Object<string, *>} [options.rules={}]
38
+ * @returns {Promise<ESLint>} eslint's ESLint instance
39
+ * @private
40
+ */
41
+ const buildEslint = async ({ cwd, ts, fix, rules = {} }) => {
42
+ const { neostandard, defaultFilePatterns } = await importNeostandard()
43
+ const baseConfig = neostandard({ ts, env: readEnv(cwd) })
44
+ if (Object.keys(rules).length) {
45
+ baseConfig.push({ files: defaultFilePatterns.all, rules })
46
+ }
47
+ return new ESLint({
48
+ cwd,
49
+ fix,
50
+ overrideConfigFile: true,
51
+ baseConfig
52
+ })
53
+ }
54
+
55
+ /**
56
+ * Print a file's lint messages, if it has any.
57
+ * @memberOf module:partials
58
+ * @param {Vinyl} file
59
+ * @param {{messages: Array<{line: number, column: number, message: string}>, errorCount: number, warningCount: number}} result
60
+ * @returns {undefined}
61
+ */
62
+ const printFileReport = (file, result) => {
63
+ if (!result.messages.length) {
64
+ return
65
+ }
66
+ console.log(path.relative(file.cwd, file.path))
67
+ result.messages.forEach(({ line, column, message }) => console.log(`line ${line}:${column}\t${message}`))
68
+ console.log(`✖ ${result.errorCount} error\t⚠ ${result.warningCount} warning\n`)
69
+ }
70
+
71
+ /**
72
+ * Lint every file streaming through with neostandard and, unless `fix` is false, apply the automatic fixes to the
73
+ * file's contents so a following `dest()` writes the fixed version. Remaining problems are printed along with a
74
+ * summary once the stream ends; they only fail the stream when `failOnError` is set (the standalone `lint` task),
75
+ * never during `build`.
76
+ *
77
+ * One ESLint instance is built from the first file's own cwd and reused for the rest of the stream, since every
78
+ * file in a given lint pass shares the same project (and so the same config).
79
+ * @memberOf module:partials
80
+ * @param {Object} [options={}]
81
+ * @param {boolean} [options.fix=true] - Apply neostandard's automatic fixes to the file contents.
82
+ * @param {boolean} [options.ts=false] - Lint `.ts` files too, with type-aware rules (see neostandard's `ts` option).
83
+ * @param {Object<string, *>} [options.rules={}] - ESLint rule overrides layered on top of neostandard's own rules
84
+ * (see gulp.config.mjs's `lint.rules`).
85
+ * @param {boolean} [options.failOnError=false] - Emit an error once the stream ends if any lint errors remain after
86
+ * fixing, so a command-line run exits non-zero.
87
+ * @param {function(Object): Promise<{lintText: Function}>} [loadEslint] - Resolves an ESLint-shaped linter. Real
88
+ * callers should never need to pass this - it exists so tests can supply a stand-in, since this project's
89
+ * CommonJS-transform-based Jest setup cannot execute a real dynamic import() of neostandard (ESM-only).
90
+ * @returns {stream.Transform} With a `totals` property ({errors, warnings}), complete once the stream ends.
91
+ */
92
+ export const neostandardLint = (
93
+ { fix = true, ts = false, rules = {}, failOnError = false } = {},
94
+ loadEslint = buildEslint
95
+ ) => {
96
+ const totals = { errors: 0, warnings: 0 }
97
+ let eslintPromise = null
98
+ const linter = through.obj(
99
+ function (file, enc, callback) {
100
+ if (file.isNull()) {
101
+ callback(null, file)
102
+ return
103
+ }
104
+ if (file.isStream()) {
105
+ callback(new Error('neostandardLint: streaming file contents are not supported.'))
106
+ return
107
+ }
108
+ if (!eslintPromise) {
109
+ eslintPromise = loadEslint({ cwd: file.cwd, ts, fix, rules })
110
+ }
111
+ eslintPromise
112
+ .then(eslint => eslint.lintText(file.contents.toString(), { filePath: file.path }))
113
+ .then(results => {
114
+ // No result at all means the file is ignored by the project's ESLint configuration.
115
+ const [result] = results
116
+ if (result) {
117
+ if (typeof result.output === 'string') {
118
+ file.contents = Buffer.from(result.output)
119
+ }
120
+ totals.errors += result.errorCount
121
+ totals.warnings += result.warningCount
122
+ printFileReport(file, result)
123
+ }
124
+ callback(null, file)
125
+ }, callback)
126
+ },
127
+ function (callback) {
128
+ console.log('neostandard linter results')
129
+ console.log('======================================')
130
+ console.log(`✖ Errors total: ${totals.errors}`)
131
+ console.log(`⚠ Warnings total: ${totals.warnings}\n`)
132
+ if (failOnError && totals.errors) {
133
+ callback(new Error(`neostandard found ${totals.errors} lint error(s) that could not be fixed automatically.`))
134
+ return
135
+ }
136
+ callback()
137
+ }
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
142
+ }
@@ -1,131 +1,131 @@
1
- import Vinyl from 'vinyl'
2
- import { Readable } from 'node:stream'
3
- import { neostandardLint } from './neostandardLint.mjs'
4
-
5
- const makeFile = (contents, filePath = '/project/src/example.js') => new Vinyl({
6
- cwd: '/project',
7
- base: '/project/src',
8
- path: filePath,
9
- contents
10
- })
11
-
12
- const run = (transform, files) => new Promise((resolve, reject) => {
13
- const out = []
14
- transform.on('data', file => out.push(file)).on('end', () => resolve(out)).on('error', reject)
15
- files.forEach(file => transform.write(file))
16
- transform.end()
17
- })
18
-
19
- const fakeEslint = (resultFor) => ({
20
- lintText: jest.fn((text, options) => Promise.resolve(resultFor(text, options)))
21
- })
22
-
23
- let logSpy
24
-
25
- beforeEach(() => {
26
- logSpy = jest.spyOn(console, 'log').mockImplementation(() => {})
27
- })
28
-
29
- afterEach(() => {
30
- logSpy.mockRestore()
31
- })
32
-
33
- describe('neostandardLint', () => {
34
- test('replaces file contents with the fixed output and hands ESLint the file path', async () => {
35
- expect.assertions(3)
36
- const eslint = fakeEslint((text) => [{ messages: [], errorCount: 0, warningCount: 0, output: text.replace(/"/g, '\'') }])
37
- const [linted] = await run(neostandardLint({ fix: true }, () => Promise.resolve(eslint)), [makeFile(Buffer.from('const a = "b"'))])
38
- expect(linted.contents.toString()).toEqual('const a = \'b\'')
39
- expect(eslint.lintText).toHaveBeenCalledWith('const a = "b"', { filePath: '/project/src/example.js' })
40
- expect(linted.path).toEqual('/project/src/example.js')
41
- })
42
-
43
- test('leaves the contents alone when ESLint has no fixed output (nothing needed fixing)', async () => {
44
- expect.assertions(1)
45
- const eslint = fakeEslint(() => [{ messages: [], errorCount: 0, warningCount: 0 }])
46
- const [linted] = await run(neostandardLint({}, () => Promise.resolve(eslint)), [makeFile(Buffer.from('const a = 1'))])
47
- expect(linted.contents.toString()).toEqual('const a = 1')
48
- })
49
-
50
- test('builds the linter once per stream, from the first file, and reuses it for the rest', async () => {
51
- expect.assertions(1)
52
- const loadEslint = jest.fn(() => Promise.resolve(fakeEslint(() => [{ messages: [], errorCount: 0, warningCount: 0 }])))
53
- await run(
54
- neostandardLint({}, loadEslint),
55
- [makeFile(Buffer.from('a'), '/project/src/a.js'), makeFile(Buffer.from('b'), '/project/src/b.js')]
56
- )
57
- expect(loadEslint).toHaveBeenCalledTimes(1)
58
- })
59
-
60
- test('passes fix and ts through to the loader', async () => {
61
- expect.assertions(1)
62
- const loadEslint = jest.fn(() => Promise.resolve(fakeEslint(() => [{ messages: [], errorCount: 0, warningCount: 0 }])))
63
- await run(neostandardLint({ fix: false, ts: true }, loadEslint), [makeFile(Buffer.from('x'))])
64
- expect(loadEslint).toHaveBeenCalledWith({ cwd: '/project', ts: true, fix: false, rules: {} })
65
- })
66
-
67
- test('passes rule overrides through to the loader', async () => {
68
- expect.assertions(1)
69
- const loadEslint = jest.fn(() => Promise.resolve(fakeEslint(() => [{ messages: [], errorCount: 0, warningCount: 0 }])))
70
- const rules = { 'no-template-curly-in-string': 'off' }
71
- await run(neostandardLint({ rules }, loadEslint), [makeFile(Buffer.from('x'))])
72
- expect(loadEslint).toHaveBeenCalledWith({ cwd: '/project', ts: false, fix: true, rules })
73
- })
74
-
75
- test('passes a file through untouched when the project ignores it (ESLint returns no results)', async () => {
76
- expect.assertions(2)
77
- const eslint = fakeEslint(() => [])
78
- const [linted] = await run(neostandardLint({}, () => Promise.resolve(eslint)), [makeFile(Buffer.from('var x=1'))])
79
- expect(linted.contents.toString()).toEqual('var x=1')
80
- expect(logSpy).toHaveBeenCalledWith('✖ Errors total: 0')
81
- })
82
-
83
- test('skips null-content files without linting them', async () => {
84
- expect.assertions(2)
85
- const eslint = fakeEslint(() => [])
86
- const [passed] = await run(neostandardLint({}, () => Promise.resolve(eslint)), [makeFile(null)])
87
- expect(passed.isNull()).toBeTruthy()
88
- expect(eslint.lintText).not.toHaveBeenCalled()
89
- })
90
-
91
- test('errors on streaming file contents, which cannot be linted', async () => {
92
- expect.assertions(1)
93
- const streamFile = makeFile(Readable.from(['x']))
94
- await expect(run(neostandardLint({}, () => Promise.resolve(fakeEslint(() => []))), [streamFile])).rejects.toThrow(/streaming/)
95
- })
96
-
97
- test('prints per-file problems and a summary of totals across all files, without failing', async () => {
98
- expect.assertions(5)
99
- const eslint = fakeEslint((text, { filePath }) => filePath.endsWith('a.js')
100
- ? [{ messages: [{ line: 2, column: 7, message: 'unused b' }, { line: 3, column: 1, message: 'bad thing' }], errorCount: 2, warningCount: 0 }]
101
- : [{ messages: [{ line: 1, column: 1, message: 'just a warning' }], errorCount: 0, warningCount: 1 }])
102
- const out = await run(
103
- neostandardLint({}, () => Promise.resolve(eslint)),
104
- [makeFile(Buffer.from('a'), '/project/src/a.js'), makeFile(Buffer.from('b'), '/project/src/b.js')]
105
- )
106
- expect(out).toHaveLength(2)
107
- expect(logSpy).toHaveBeenCalledWith('src/a.js')
108
- expect(logSpy).toHaveBeenCalledWith('line 2:7\tunused b')
109
- expect(logSpy).toHaveBeenCalledWith('✖ Errors total: 2')
110
- expect(logSpy).toHaveBeenCalledWith('⚠ Warnings total: 1\n')
111
- })
112
-
113
- test('with failOnError, fails the stream once it ends if any errors remain', async () => {
114
- expect.assertions(2)
115
- const eslint = fakeEslint(() => [{ messages: [{ line: 1, column: 1, message: 'bad thing' }], errorCount: 1, warningCount: 0 }])
116
- await expect(run(neostandardLint({ failOnError: true }, () => Promise.resolve(eslint)), [makeFile(Buffer.from('a'))]))
117
- .rejects.toThrow('neostandard found 1 lint error(s)')
118
- expect(logSpy).toHaveBeenCalledWith('✖ Errors total: 1')
119
- })
120
-
121
- test('with failOnError, still passes when there are only warnings', async () => {
122
- expect.assertions(1)
123
- const eslint = fakeEslint(() => [{ messages: [{ line: 1, column: 1, message: 'just a warning' }], errorCount: 0, warningCount: 1 }])
124
- await expect(run(neostandardLint({ failOnError: true }, () => Promise.resolve(eslint)), [makeFile(Buffer.from('a'))])).resolves.toHaveLength(1)
125
- })
126
-
127
- test('surfaces a failure to load or run the linter as a stream error', async () => {
128
- expect.assertions(1)
129
- await expect(run(neostandardLint({}, () => Promise.reject(new Error('cannot load'))), [makeFile(Buffer.from('x'))])).rejects.toThrow('cannot load')
130
- })
131
- })
1
+ import Vinyl from 'vinyl'
2
+ import { Readable } from 'node:stream'
3
+ import { neostandardLint } from './neostandardLint.mjs'
4
+
5
+ const makeFile = (contents, filePath = '/project/src/example.js') => new Vinyl({
6
+ cwd: '/project',
7
+ base: '/project/src',
8
+ path: filePath,
9
+ contents
10
+ })
11
+
12
+ const run = (transform, files) => new Promise((resolve, reject) => {
13
+ const out = []
14
+ transform.on('data', file => out.push(file)).on('end', () => resolve(out)).on('error', reject)
15
+ files.forEach(file => transform.write(file))
16
+ transform.end()
17
+ })
18
+
19
+ const fakeEslint = (resultFor) => ({
20
+ lintText: jest.fn((text, options) => Promise.resolve(resultFor(text, options)))
21
+ })
22
+
23
+ let logSpy
24
+
25
+ beforeEach(() => {
26
+ logSpy = jest.spyOn(console, 'log').mockImplementation(() => {})
27
+ })
28
+
29
+ afterEach(() => {
30
+ logSpy.mockRestore()
31
+ })
32
+
33
+ describe('neostandardLint', () => {
34
+ test('replaces file contents with the fixed output and hands ESLint the file path', async () => {
35
+ expect.assertions(3)
36
+ const eslint = fakeEslint((text) => [{ messages: [], errorCount: 0, warningCount: 0, output: text.replace(/"/g, '\'') }])
37
+ const [linted] = await run(neostandardLint({ fix: true }, () => Promise.resolve(eslint)), [makeFile(Buffer.from('const a = "b"'))])
38
+ expect(linted.contents.toString()).toEqual('const a = \'b\'')
39
+ expect(eslint.lintText).toHaveBeenCalledWith('const a = "b"', { filePath: '/project/src/example.js' })
40
+ expect(linted.path).toEqual('/project/src/example.js')
41
+ })
42
+
43
+ test('leaves the contents alone when ESLint has no fixed output (nothing needed fixing)', async () => {
44
+ expect.assertions(1)
45
+ const eslint = fakeEslint(() => [{ messages: [], errorCount: 0, warningCount: 0 }])
46
+ const [linted] = await run(neostandardLint({}, () => Promise.resolve(eslint)), [makeFile(Buffer.from('const a = 1'))])
47
+ expect(linted.contents.toString()).toEqual('const a = 1')
48
+ })
49
+
50
+ test('builds the linter once per stream, from the first file, and reuses it for the rest', async () => {
51
+ expect.assertions(1)
52
+ const loadEslint = jest.fn(() => Promise.resolve(fakeEslint(() => [{ messages: [], errorCount: 0, warningCount: 0 }])))
53
+ await run(
54
+ neostandardLint({}, loadEslint),
55
+ [makeFile(Buffer.from('a'), '/project/src/a.js'), makeFile(Buffer.from('b'), '/project/src/b.js')]
56
+ )
57
+ expect(loadEslint).toHaveBeenCalledTimes(1)
58
+ })
59
+
60
+ test('passes fix and ts through to the loader', async () => {
61
+ expect.assertions(1)
62
+ const loadEslint = jest.fn(() => Promise.resolve(fakeEslint(() => [{ messages: [], errorCount: 0, warningCount: 0 }])))
63
+ await run(neostandardLint({ fix: false, ts: true }, loadEslint), [makeFile(Buffer.from('x'))])
64
+ expect(loadEslint).toHaveBeenCalledWith({ cwd: '/project', ts: true, fix: false, rules: {} })
65
+ })
66
+
67
+ test('passes rule overrides through to the loader', async () => {
68
+ expect.assertions(1)
69
+ const loadEslint = jest.fn(() => Promise.resolve(fakeEslint(() => [{ messages: [], errorCount: 0, warningCount: 0 }])))
70
+ const rules = { 'no-template-curly-in-string': 'off' }
71
+ await run(neostandardLint({ rules }, loadEslint), [makeFile(Buffer.from('x'))])
72
+ expect(loadEslint).toHaveBeenCalledWith({ cwd: '/project', ts: false, fix: true, rules })
73
+ })
74
+
75
+ test('passes a file through untouched when the project ignores it (ESLint returns no results)', async () => {
76
+ expect.assertions(2)
77
+ const eslint = fakeEslint(() => [])
78
+ const [linted] = await run(neostandardLint({}, () => Promise.resolve(eslint)), [makeFile(Buffer.from('var x=1'))])
79
+ expect(linted.contents.toString()).toEqual('var x=1')
80
+ expect(logSpy).toHaveBeenCalledWith('✖ Errors total: 0')
81
+ })
82
+
83
+ test('skips null-content files without linting them', async () => {
84
+ expect.assertions(2)
85
+ const eslint = fakeEslint(() => [])
86
+ const [passed] = await run(neostandardLint({}, () => Promise.resolve(eslint)), [makeFile(null)])
87
+ expect(passed.isNull()).toBeTruthy()
88
+ expect(eslint.lintText).not.toHaveBeenCalled()
89
+ })
90
+
91
+ test('errors on streaming file contents, which cannot be linted', async () => {
92
+ expect.assertions(1)
93
+ const streamFile = makeFile(Readable.from(['x']))
94
+ await expect(run(neostandardLint({}, () => Promise.resolve(fakeEslint(() => []))), [streamFile])).rejects.toThrow(/streaming/)
95
+ })
96
+
97
+ test('prints per-file problems and a summary of totals across all files, without failing', async () => {
98
+ expect.assertions(5)
99
+ const eslint = fakeEslint((text, { filePath }) => filePath.endsWith('a.js')
100
+ ? [{ messages: [{ line: 2, column: 7, message: 'unused b' }, { line: 3, column: 1, message: 'bad thing' }], errorCount: 2, warningCount: 0 }]
101
+ : [{ messages: [{ line: 1, column: 1, message: 'just a warning' }], errorCount: 0, warningCount: 1 }])
102
+ const out = await run(
103
+ neostandardLint({}, () => Promise.resolve(eslint)),
104
+ [makeFile(Buffer.from('a'), '/project/src/a.js'), makeFile(Buffer.from('b'), '/project/src/b.js')]
105
+ )
106
+ expect(out).toHaveLength(2)
107
+ expect(logSpy).toHaveBeenCalledWith('src/a.js')
108
+ expect(logSpy).toHaveBeenCalledWith('line 2:7\tunused b')
109
+ expect(logSpy).toHaveBeenCalledWith('✖ Errors total: 2')
110
+ expect(logSpy).toHaveBeenCalledWith('⚠ Warnings total: 1\n')
111
+ })
112
+
113
+ test('with failOnError, fails the stream once it ends if any errors remain', async () => {
114
+ expect.assertions(2)
115
+ const eslint = fakeEslint(() => [{ messages: [{ line: 1, column: 1, message: 'bad thing' }], errorCount: 1, warningCount: 0 }])
116
+ await expect(run(neostandardLint({ failOnError: true }, () => Promise.resolve(eslint)), [makeFile(Buffer.from('a'))]))
117
+ .rejects.toThrow('neostandard found 1 lint error(s)')
118
+ expect(logSpy).toHaveBeenCalledWith('✖ Errors total: 1')
119
+ })
120
+
121
+ test('with failOnError, still passes when there are only warnings', async () => {
122
+ expect.assertions(1)
123
+ const eslint = fakeEslint(() => [{ messages: [{ line: 1, column: 1, message: 'just a warning' }], errorCount: 0, warningCount: 1 }])
124
+ await expect(run(neostandardLint({ failOnError: true }, () => Promise.resolve(eslint)), [makeFile(Buffer.from('a'))])).resolves.toHaveLength(1)
125
+ })
126
+
127
+ test('surfaces a failure to load or run the linter as a stream error', async () => {
128
+ expect.assertions(1)
129
+ await expect(run(neostandardLint({}, () => Promise.reject(new Error('cannot load'))), [makeFile(Buffer.from('x'))])).rejects.toThrow('cannot load')
130
+ })
131
+ })
@@ -1,12 +1,12 @@
1
- import { dest, src } from 'gulp'
2
- import * as gulpConfig from '../../gulp.config.mjs'
3
- import rename from 'gulp-rename'
4
-
5
- /**
6
- * Copy a readme template into the README.md file.
7
- * @memberOf module:partials
8
- * @returns {*}
9
- */
10
- export const readmeTemplate = () => src(gulpConfig.get('readme.template'))
11
- .pipe(rename(gulpConfig.get('readme.file')))
12
- .pipe(dest(gulpConfig.get('readme.to')))
1
+ import { dest, src } from 'gulp'
2
+ import * as gulpConfig from '../../gulp.config.mjs'
3
+ import rename from 'gulp-rename'
4
+
5
+ /**
6
+ * Copy a readme template into the README.md file.
7
+ * @memberOf module:partials
8
+ * @returns {*}
9
+ */
10
+ export const readmeTemplate = () => src(gulpConfig.get('readme.template'))
11
+ .pipe(rename(gulpConfig.get('readme.file')))
12
+ .pipe(dest(gulpConfig.get('readme.to')))
@@ -1,34 +1,34 @@
1
- import fs from 'fs'
2
- import * as setUp from '../test-helpers/setUp.mjs'
3
- import { readmeTemplate } from './readmeTemplate.mjs'
4
-
5
- setUp.setDefaults('test-readme-template')
6
- const gulpConfig = setUp.gulpConfig
7
-
8
- const mainMd = 'MAIN.md'
9
- const mainTestPath = gulpConfig.get('readme.template')
10
-
11
- beforeEach(() => setUp.beforeEach()
12
- .then(() => fs.copyFileSync(mainMd, mainTestPath)))
13
-
14
- afterEach(setUp.afterEach)
15
-
16
- describe('readmeTemplate', () => {
17
- test('copies the main contents into the new README.md', done => {
18
- const readmeFilePath = gulpConfig.get('readme.to') + gulpConfig.get('readme.file')
19
- const mainContents = fs.readFileSync(mainTestPath)
20
- expect.assertions(2)
21
- readmeTemplate()
22
- .on('finish', async () => {
23
- await expect(fs.existsSync(readmeFilePath)).toBeTruthy()
24
- const readme = await fs.readFileSync(readmeFilePath)
25
- // Confirm that the contents for MAIN.md are copied to README.md
26
- await expect(readme).toEqual(mainContents)
27
- done()
28
- })
29
- .on('error', error => {
30
- console.error('Encountered error', error)
31
- done()
32
- })
33
- })
34
- })
1
+ import fs from 'fs'
2
+ import * as setUp from '../test-helpers/setUp.mjs'
3
+ import { readmeTemplate } from './readmeTemplate.mjs'
4
+
5
+ setUp.setDefaults('test-readme-template')
6
+ const gulpConfig = setUp.gulpConfig
7
+
8
+ const mainMd = 'MAIN.md'
9
+ const mainTestPath = gulpConfig.get('readme.template')
10
+
11
+ beforeEach(() => setUp.beforeEach()
12
+ .then(() => fs.copyFileSync(mainMd, mainTestPath)))
13
+
14
+ afterEach(setUp.afterEach)
15
+
16
+ describe('readmeTemplate', () => {
17
+ test('copies the main contents into the new README.md', done => {
18
+ const readmeFilePath = gulpConfig.get('readme.to') + gulpConfig.get('readme.file')
19
+ const mainContents = fs.readFileSync(mainTestPath)
20
+ expect.assertions(2)
21
+ readmeTemplate()
22
+ .on('finish', async () => {
23
+ await expect(fs.existsSync(readmeFilePath)).toBeTruthy()
24
+ const readme = await fs.readFileSync(readmeFilePath)
25
+ // Confirm that the contents for MAIN.md are copied to README.md
26
+ await expect(readme).toEqual(mainContents)
27
+ done()
28
+ })
29
+ .on('error', error => {
30
+ console.error('Encountered error', error)
31
+ done()
32
+ })
33
+ })
34
+ })