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.
- package/LICENSE +674 -674
- package/README.md +762 -692
- package/babel.config.js +28 -28
- package/dist/build.js +75 -75
- package/dist/bundle.js +19 -19
- package/dist/bundleMinify.js +7 -7
- package/dist/compileReadme.js +4 -4
- package/dist/copyFiles.js +17 -17
- package/dist/copyFonts.js +4 -4
- package/dist/default.js +5 -5
- package/dist/dist.js +4 -4
- package/dist/distMinify.js +4 -4
- package/dist/docs.js +7 -7
- package/dist/images.js +4 -4
- package/dist/jsdocDocs.js +6 -6
- package/dist/lint.js +16 -16
- package/dist/partials/addToReadme.js +9 -9
- package/dist/partials/beginWatcher.js +4 -4
- package/dist/partials/clean.js +5 -5
- package/dist/partials/copyFor.js +9 -9
- package/dist/partials/distFor.js +6 -6
- package/dist/partials/distForSrc.js +5 -5
- package/dist/partials/distSeries.js +7 -7
- package/dist/partials/imagesFor.js +93 -56
- package/dist/partials/jsdocDocsFor.js +46 -46
- package/dist/partials/lintSource.js +51 -51
- package/dist/partials/loadPeer.js +99 -99
- package/dist/partials/loadPlaywright.js +14 -14
- package/dist/partials/minifyFor.js +20 -20
- package/dist/partials/neostandardLint.js +175 -175
- package/dist/partials/readmeTemplate.js +4 -4
- package/dist/partials/removeDirectory.js +11 -11
- package/dist/partials/resolveDocsMethod.js +6 -6
- package/dist/partials/runOnChange.js +31 -31
- package/dist/partials/sassFor.js +6 -6
- package/dist/partials/serveStatic.js +9 -9
- package/dist/partials/tsFor.js +32 -32
- package/dist/partials/typeDocsFor.js +57 -51
- package/dist/partials.js +176 -176
- package/dist/sourceLint.js +18 -18
- package/dist/test-helpers/isolateGulpCache.js +24 -24
- package/dist/test-helpers/setUp.js +4 -4
- package/dist/testFull.js +5 -5
- package/dist/testQuick.js +5 -5
- package/dist/typeDocs.js +5 -5
- package/dist/typeScript.js +4 -4
- package/dist/utilities/convertMjsFilesForDocs.js +12 -12
- package/dist/utilities/dotGet.js +54 -54
- package/dist/utilities/dotNotate.js +79 -79
- package/dist/utilities/dotSet.js +45 -45
- package/dist/utilities/esmNamedExportsToCjs.js +15 -15
- package/dist/utilities/getBundleConfigs.js +9 -9
- package/dist/utilities/isObject.js +6 -6
- package/dist/utilities/strAfter.js +7 -7
- package/dist/utilities/strBefore.js +7 -7
- package/dist/watchFull.js +4 -4
- package/dist/watchTest.js +4 -4
- package/functions/build.mjs +72 -72
- package/functions/build.test.mjs +763 -759
- package/functions/bundle.mjs +44 -44
- package/functions/bundle.test.mjs +219 -219
- package/functions/bundleMinify.mjs +16 -16
- package/functions/bundleMinify.test.mjs +37 -37
- package/functions/compileReadme.mjs +9 -9
- package/functions/compileReadme.test.mjs +18 -18
- package/functions/copyFiles.mjs +32 -32
- package/functions/copyFiles.test.mjs +78 -78
- package/functions/copyFonts.mjs +9 -9
- package/functions/copyFonts.test.mjs +32 -32
- package/functions/default.mjs +12 -12
- package/functions/default.test.mjs +33 -33
- package/functions/dist.mjs +8 -8
- package/functions/dist.test.mjs +44 -44
- package/functions/distMinify.mjs +9 -9
- package/functions/distMinify.test.mjs +15 -15
- package/functions/docs.mjs +13 -13
- package/functions/docs.test.mjs +45 -45
- package/functions/gulpConfigEntries.test.mjs +23 -23
- package/functions/images.mjs +8 -8
- package/functions/images.test.mjs +142 -142
- package/functions/jsdocDocs.mjs +10 -10
- package/functions/jsdocEsmExports.test.mjs +24 -24
- package/functions/lint.mjs +9 -9
- package/functions/partials/addToReadme.mjs +25 -25
- package/functions/partials/addToReadme.test.mjs +34 -34
- package/functions/partials/beginWatcher.mjs +9 -9
- package/functions/partials/beginWatcher.test.mjs +19 -19
- package/functions/partials/clean.mjs +13 -13
- package/functions/partials/clean.test.mjs +22 -22
- package/functions/partials/copyFor.mjs +15 -15
- package/functions/partials/copyFor.test.mjs +53 -53
- package/functions/partials/distFor.mjs +22 -22
- package/functions/partials/distFor.test.mjs +195 -195
- package/functions/partials/distForSrc.mjs +11 -11
- package/functions/partials/distForSrc.test.mjs +18 -18
- package/functions/partials/distSeries.mjs +21 -21
- package/functions/partials/distSeries.test.mjs +34 -34
- package/functions/partials/imagesFor.mjs +83 -50
- package/functions/partials/imagesFor.test.mjs +287 -229
- package/functions/partials/jsdocDocsFor.mjs +163 -152
- package/functions/partials/jsdocDocsFor.test.mjs +182 -152
- package/functions/partials/lintSource.mjs +41 -41
- package/functions/partials/loadPeer.mjs +76 -76
- package/functions/partials/loadPeer.test.mjs +58 -58
- package/functions/partials/loadPlaywright.mjs +23 -23
- package/functions/partials/loadPlaywright.test.mjs +70 -70
- package/functions/partials/minifyFor.mjs +15 -15
- package/functions/partials/minifyFor.test.mjs +49 -49
- package/functions/partials/neostandardLint.mjs +142 -142
- package/functions/partials/neostandardLint.test.mjs +131 -131
- package/functions/partials/readmeTemplate.mjs +12 -12
- package/functions/partials/readmeTemplate.test.mjs +34 -34
- package/functions/partials/removeDirectory.mjs +34 -34
- package/functions/partials/removeDirectory.test.mjs +72 -72
- package/functions/partials/resolveDocsMethod.mjs +10 -10
- package/functions/partials/resolveDocsMethod.test.mjs +33 -33
- package/functions/partials/runOnChange.mjs +54 -54
- package/functions/partials/runOnChange.test.mjs +95 -95
- package/functions/partials/sassFor.mjs +25 -25
- package/functions/partials/sassFor.test.mjs +81 -81
- package/functions/partials/serveStatic.mjs +47 -47
- package/functions/partials/serveStatic.test.mjs +71 -71
- package/functions/partials/tsFor.mjs +92 -92
- package/functions/partials/tsFor.test.mjs +88 -88
- package/functions/partials/typeDocsFor.mjs +231 -227
- package/functions/partials/typeDocsFor.test.mjs +263 -252
- package/functions/partials.mjs +31 -31
- package/functions/partials.test.mjs +7 -7
- package/functions/sass.mjs +4 -4
- package/functions/sass.test.mjs +31 -31
- package/functions/sourceLint.mjs +13 -13
- package/functions/sourceLint.test.mjs +166 -166
- package/functions/test-helpers/isolateGulpCache.mjs +16 -16
- package/functions/test-helpers/neostandardLintWithRealNeostandard.mjs +40 -40
- package/functions/test-helpers/setUp.mjs +100 -100
- package/functions/test-helpers/setUp.test.mjs +19 -19
- package/functions/testFull.mjs +17 -17
- package/functions/testFull.test.mjs +17 -17
- package/functions/testHelpers.mjs +10 -10
- package/functions/testHelpers.test.mjs +7 -7
- package/functions/testQuick.mjs +17 -17
- package/functions/testQuick.test.mjs +17 -17
- package/functions/typeDocs.mjs +9 -9
- package/functions/typeDocs.test.mjs +11 -11
- package/functions/typeScript.mjs +8 -8
- package/functions/typeScript.test.mjs +11 -11
- package/functions/utilities/convertMjsFilesForDocs.mjs +34 -34
- package/functions/utilities/convertMjsFilesForDocs.test.mjs +58 -58
- package/functions/utilities/dotGet.mjs +49 -49
- package/functions/utilities/dotNotate.mjs +73 -73
- package/functions/utilities/dotNotate.test.mjs +41 -41
- package/functions/utilities/dotSet.mjs +40 -40
- package/functions/utilities/esmNamedExportsToCjs.mjs +30 -30
- package/functions/utilities/esmNamedExportsToCjs.test.mjs +47 -47
- package/functions/utilities/getBundleConfigs.mjs +30 -30
- package/functions/utilities/isObject.mjs +8 -8
- package/functions/utilities/strAfter.mjs +12 -12
- package/functions/utilities/strBefore.mjs +12 -12
- package/functions/watchFull.mjs +9 -9
- package/functions/watchFull.test.mjs +41 -41
- package/functions/watchTest.mjs +10 -10
- package/functions/watchTest.test.mjs +49 -49
- package/gulp.config.js +4 -4
- package/gulp.config.mjs +344 -327
- package/gulpfile.base.js +51 -51
- package/gulpfile.base.mjs +29 -29
- package/jsdoc.base.js +16 -16
- package/jsdoc.esmExports.js +17 -17
- package/package.json +146 -145
|
@@ -1,152 +1,182 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import * as setUp from '../test-helpers/setUp.mjs'
|
|
4
|
-
import { jsdocDocsFor, resolveJsdocBin, resolveJsdocTheme } from './jsdocDocsFor.mjs'
|
|
5
|
-
import jsdocBase from '../../jsdoc.base.js'
|
|
6
|
-
|
|
7
|
-
setUp.setDefaults('test-jsdoc-docs-for')
|
|
8
|
-
const gulpConfig = setUp.gulpConfig
|
|
9
|
-
|
|
10
|
-
const write = (file, contents) => {
|
|
11
|
-
fs.mkdirSync(path.dirname(file), { recursive: true })
|
|
12
|
-
fs.writeFileSync(file, contents)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
beforeEach(
|
|
16
|
-
() => setUp.beforeEach().then(() => {
|
|
17
|
-
const srcPath = gulpConfig.get('srcPath')
|
|
18
|
-
write(`${srcPath}/add.js`, '/**\n * Add two numbers.\n * @param {number} a\n * @param {number} b\n * @returns {number}\n */\nconst add = (a, b) => a + b\n\nmodule.exports = add\n')
|
|
19
|
-
})
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
afterEach(setUp.afterEach)
|
|
23
|
-
|
|
24
|
-
describe('resolveJsdocBin / resolveJsdocTheme', () => {
|
|
25
|
-
test('resolves the real installed jsdoc CLI entry', () => {
|
|
26
|
-
const bin = resolveJsdocBin()
|
|
27
|
-
expect(fs.existsSync(bin)).toBe(true)
|
|
28
|
-
expect(path.basename(bin)).toBe('jsdoc.js')
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
test('resolves the real installed theme directory', () => {
|
|
32
|
-
const themeDir = resolveJsdocTheme('clean-jsdoc-theme')
|
|
33
|
-
expect(fs.existsSync(themeDir)).toBe(true)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
test('throws a clear, install-hint error for a package that is not installed', () => {
|
|
37
|
-
expect(() => resolveJsdocTheme('not-a-real-jsdoc-theme-package')).toThrow(/npm install --save-dev/)
|
|
38
|
-
})
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
describe('jsdocDocsFor', () => {
|
|
42
|
-
const fakeRunJsdoc = calls => (bin, args) => {
|
|
43
|
-
calls.bin = bin
|
|
44
|
-
calls.args = args
|
|
45
|
-
calls.config = JSON.parse(fs.readFileSync(args[args.indexOf('-c') + 1], 'utf8'))
|
|
46
|
-
fs.
|
|
47
|
-
fs.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
expect(fs.existsSync(`${outPath}/
|
|
59
|
-
expect(
|
|
60
|
-
expect(calls.config.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
warn.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
warn.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
expect(['a.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
await jsdocDocsFor(
|
|
125
|
-
expect(calls.config.opts.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
gulpConfig.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
expect(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import * as setUp from '../test-helpers/setUp.mjs'
|
|
4
|
+
import { jsdocDocsFor, resolveJsdocBin, resolveJsdocTheme } from './jsdocDocsFor.mjs'
|
|
5
|
+
import jsdocBase from '../../jsdoc.base.js'
|
|
6
|
+
|
|
7
|
+
setUp.setDefaults('test-jsdoc-docs-for')
|
|
8
|
+
const gulpConfig = setUp.gulpConfig
|
|
9
|
+
|
|
10
|
+
const write = (file, contents) => {
|
|
11
|
+
fs.mkdirSync(path.dirname(file), { recursive: true })
|
|
12
|
+
fs.writeFileSync(file, contents)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
beforeEach(
|
|
16
|
+
() => setUp.beforeEach().then(() => {
|
|
17
|
+
const srcPath = gulpConfig.get('srcPath')
|
|
18
|
+
write(`${srcPath}/add.js`, '/**\n * Add two numbers.\n * @param {number} a\n * @param {number} b\n * @returns {number}\n */\nconst add = (a, b) => a + b\n\nmodule.exports = add\n')
|
|
19
|
+
})
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
afterEach(setUp.afterEach)
|
|
23
|
+
|
|
24
|
+
describe('resolveJsdocBin / resolveJsdocTheme', () => {
|
|
25
|
+
test('resolves the real installed jsdoc CLI entry', () => {
|
|
26
|
+
const bin = resolveJsdocBin()
|
|
27
|
+
expect(fs.existsSync(bin)).toBe(true)
|
|
28
|
+
expect(path.basename(bin)).toBe('jsdoc.js')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('resolves the real installed theme directory', () => {
|
|
32
|
+
const themeDir = resolveJsdocTheme('clean-jsdoc-theme')
|
|
33
|
+
expect(fs.existsSync(themeDir)).toBe(true)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('throws a clear, install-hint error for a package that is not installed', () => {
|
|
37
|
+
expect(() => resolveJsdocTheme('not-a-real-jsdoc-theme-package')).toThrow(/npm install --save-dev/)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe('jsdocDocsFor', () => {
|
|
42
|
+
const fakeRunJsdoc = calls => (bin, args) => {
|
|
43
|
+
calls.bin = bin
|
|
44
|
+
calls.args = args
|
|
45
|
+
calls.config = JSON.parse(fs.readFileSync(args[args.indexOf('-c') + 1], 'utf8'))
|
|
46
|
+
calls.readme = fs.readFileSync(calls.config.opts.readme, 'utf8')
|
|
47
|
+
fs.mkdirSync(calls.config.opts.destination, { recursive: true })
|
|
48
|
+
fs.writeFileSync(`${calls.config.opts.destination}/index.html`, '<html></html>')
|
|
49
|
+
return Promise.resolve()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
test('generates the documentation into the output directory, clearing what was there', async () => {
|
|
53
|
+
gulpConfig.set('docs.enabled', true)
|
|
54
|
+
const outPath = `${gulpConfig.get('srcPath')}/../docs`
|
|
55
|
+
write(`${outPath}/stale.html`, 'old')
|
|
56
|
+
const calls = {}
|
|
57
|
+
await jsdocDocsFor(gulpConfig.get('srcPath'), outPath, fakeRunJsdoc(calls))()
|
|
58
|
+
expect(fs.existsSync(`${outPath}/stale.html`)).toBe(false)
|
|
59
|
+
expect(fs.existsSync(`${outPath}/index.html`)).toBe(true)
|
|
60
|
+
expect(calls.config.opts.destination).toBe(path.resolve(outPath))
|
|
61
|
+
expect(calls.config.source.include).toEqual([path.resolve(gulpConfig.get('srcPath'))])
|
|
62
|
+
// Headings of the front page get ids, so the theme's table of contents can follow them
|
|
63
|
+
expect(calls.config.markdown).toEqual({ idInHeadings: true })
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('passes on what jsdoc reports', async () => {
|
|
67
|
+
gulpConfig.set('docs.enabled', true)
|
|
68
|
+
const srcPath = gulpConfig.get('srcPath')
|
|
69
|
+
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
70
|
+
const calls = {}
|
|
71
|
+
const reporting = (bin, args) => fakeRunJsdoc(calls)(bin, args).then(() => ({ stdout: '', stderr: 'WARNING: a page failed\n' }))
|
|
72
|
+
await jsdocDocsFor(srcPath, `${srcPath}/../docs`, reporting)()
|
|
73
|
+
expect(warn).toHaveBeenCalledWith('WARNING: a page failed')
|
|
74
|
+
warn.mockClear()
|
|
75
|
+
await jsdocDocsFor(srcPath, `${srcPath}/../docs`, fakeRunJsdoc(calls))()
|
|
76
|
+
expect(warn).not.toHaveBeenCalled()
|
|
77
|
+
warn.mockRestore()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('takes a list of directories and files to document', async () => {
|
|
81
|
+
gulpConfig.set('docs.enabled', true)
|
|
82
|
+
const srcPath = gulpConfig.get('srcPath')
|
|
83
|
+
const calls = {}
|
|
84
|
+
await jsdocDocsFor([srcPath, `${srcPath}/../gulp.config.mjs`], `${srcPath}/../docs`, fakeRunJsdoc(calls))()
|
|
85
|
+
expect(calls.config.source.include).toEqual([path.resolve(srcPath), path.resolve(`${srcPath}/../gulp.config.mjs`)])
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('documents .js, .mjs and .cjs files, but not tests or minified copies', () => {
|
|
89
|
+
const include = new RegExp(jsdocBase.source.includePattern)
|
|
90
|
+
const exclude = new RegExp(jsdocBase.source.excludePattern)
|
|
91
|
+
const documented = file => include.test(file) && !exclude.test(file)
|
|
92
|
+
expect(['a.js', 'a.mjs', 'a.cjs', 'dir/a.mjs'].filter(documented)).toEqual(['a.js', 'a.mjs', 'a.cjs', 'dir/a.mjs'])
|
|
93
|
+
expect(['a.test.mjs', 'a.min.js', 'a.min.mjs', 'dir/_private.mjs', 'a.ts'].filter(documented)).toEqual([])
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('passes docs.title to the theme as a link to the home page of the docs and as its page title, and leaves it out when unset', async () => {
|
|
97
|
+
gulpConfig.set('docs.enabled', true)
|
|
98
|
+
const srcPath = gulpConfig.get('srcPath')
|
|
99
|
+
const calls = {}
|
|
100
|
+
gulpConfig.set('docs.title', 'Tom & Jerry <Tools>')
|
|
101
|
+
await jsdocDocsFor(srcPath, `${srcPath}/../docs`, fakeRunJsdoc(calls))()
|
|
102
|
+
expect(calls.config.opts.theme_opts).toEqual({
|
|
103
|
+
title: '<a href="index.html" class="sidebar-title sidebar-title-anchor">Tom & Jerry <Tools></a>',
|
|
104
|
+
homepageTitle: 'Tom & Jerry <Tools>'
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
expect(calls.config.opts.siteName).toBe('Tom & Jerry <Tools>')
|
|
108
|
+
|
|
109
|
+
gulpConfig.set('docs.title', '')
|
|
110
|
+
await jsdocDocsFor(srcPath, `${srcPath}/../docs`, fakeRunJsdoc(calls))()
|
|
111
|
+
expect(calls.config.opts.theme_opts).toBeUndefined()
|
|
112
|
+
expect(calls.config.opts.siteName).toBeUndefined()
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('passes docs.basePath to the theme, and leaves it out when unset', async () => {
|
|
116
|
+
gulpConfig.set('docs.enabled', true)
|
|
117
|
+
const srcPath = gulpConfig.get('srcPath')
|
|
118
|
+
const calls = {}
|
|
119
|
+
gulpConfig.set('docs.basePath', '/projects/tools/docs/')
|
|
120
|
+
await jsdocDocsFor(srcPath, `${srcPath}/../docs`, fakeRunJsdoc(calls))()
|
|
121
|
+
expect(calls.config.opts.basePath).toBe('/projects/tools/docs/')
|
|
122
|
+
|
|
123
|
+
gulpConfig.set('docs.basePath', false)
|
|
124
|
+
await jsdocDocsFor(srcPath, `${srcPath}/../docs`, fakeRunJsdoc(calls))()
|
|
125
|
+
expect(calls.config.opts.basePath).toBeUndefined()
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
test('uses the configured theme as the template directory', async () => {
|
|
129
|
+
gulpConfig.set('docs.enabled', true)
|
|
130
|
+
gulpConfig.set('docs.theme', 'clean-jsdoc-theme')
|
|
131
|
+
const calls = {}
|
|
132
|
+
await jsdocDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeRunJsdoc(calls))()
|
|
133
|
+
expect(calls.config.opts.template).toBe(resolveJsdocTheme('clean-jsdoc-theme'))
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
test('uses the front page from docs.index when it exists, and falls back to one with the title when it does not', async () => {
|
|
137
|
+
gulpConfig.set('docs.enabled', true)
|
|
138
|
+
gulpConfig.set('docs.index', `${gulpConfig.get('srcPath')}/../MAIN.md`)
|
|
139
|
+
write(`${gulpConfig.get('srcPath')}/../MAIN.md`, '# hello\n')
|
|
140
|
+
const calls = {}
|
|
141
|
+
await jsdocDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeRunJsdoc(calls))()
|
|
142
|
+
expect(calls.config.opts.readme).toBe(path.resolve(`${gulpConfig.get('srcPath')}/../MAIN.md`))
|
|
143
|
+
|
|
144
|
+
gulpConfig.set('docs.index', 'does-not-exist.md')
|
|
145
|
+
gulpConfig.set('docs.title', 'Tom & Jerry')
|
|
146
|
+
await jsdocDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeRunJsdoc(calls))()
|
|
147
|
+
expect(calls.readme).toBe('# Tom & Jerry\n')
|
|
148
|
+
// The fallback is written to a temporary folder, which is removed afterwards
|
|
149
|
+
expect(fs.existsSync(calls.config.opts.readme)).toBe(false)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test('skips if docs.enabled is false', () => {
|
|
153
|
+
gulpConfig.set('docs.enabled', false)
|
|
154
|
+
const result = jsdocDocsFor()
|
|
155
|
+
expect(result).toBeInstanceOf(Function)
|
|
156
|
+
expect(result.length).toBe(0)
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
describe('jsdocDocsFor, end to end with the real jsdoc CLI and clean-jsdoc-theme', () => {
|
|
161
|
+
test('produces a real browsable HTML page from real JSDoc comments', async () => {
|
|
162
|
+
gulpConfig.set('docs.enabled', true)
|
|
163
|
+
gulpConfig.set('docs.title', 'Tom & Jerry')
|
|
164
|
+
const outPath = `${gulpConfig.get('srcPath')}/../docs`
|
|
165
|
+
await jsdocDocsFor(gulpConfig.get('srcPath'), outPath)()
|
|
166
|
+
// clean-jsdoc-theme nests its real output under its own route folders (global/, source/...), not flat at
|
|
167
|
+
// outPath's own root - confirmed by actually running it, not assumed from the classic jsdoc template shape.
|
|
168
|
+
const files = fs.readdirSync(outPath, { recursive: true })
|
|
169
|
+
expect(files.some(file => file.endsWith('.html'))).toBe(true)
|
|
170
|
+
expect(fs.readFileSync(`${outPath}/index.html`, 'utf8')).toMatch(/<title>[^<]*Tom & Jerry\s*<\/title>/)
|
|
171
|
+
}, 30000)
|
|
172
|
+
|
|
173
|
+
test('starts every link at docs.basePath, for documentation served below the site root', async () => {
|
|
174
|
+
gulpConfig.set('docs.enabled', true)
|
|
175
|
+
gulpConfig.set('docs.basePath', '/projects/tools/docs/')
|
|
176
|
+
const outPath = `${gulpConfig.get('srcPath')}/../docs`
|
|
177
|
+
await jsdocDocsFor(gulpConfig.get('srcPath'), outPath)()
|
|
178
|
+
const links = [...fs.readFileSync(`${outPath}/index.html`, 'utf8').matchAll(/(?:href|src)="(\/[^"]*)"/g)].map(match => match[1])
|
|
179
|
+
expect(links.length).toBeGreaterThan(0)
|
|
180
|
+
expect(links.filter(link => !link.startsWith('/projects/tools/docs'))).toEqual([])
|
|
181
|
+
}, 30000)
|
|
182
|
+
})
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import * as gulpConfig from '../../gulp.config.mjs'
|
|
2
|
-
import { dest, src } from 'gulp'
|
|
3
|
-
import { neostandardLint } from './neostandardLint.mjs'
|
|
4
|
-
import { Transform } from 'node:stream'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Lint the project's source (srcPath: .js, .mjs and .cjs, plus .ts when typescript.enabled) with neostandard and
|
|
8
|
-
* write the automatic fixes back in place. Shared by the sourceLint (build) and lint (standalone) tasks.
|
|
9
|
-
* @memberOf module:partials
|
|
10
|
-
* @param {Object} [options={}]
|
|
11
|
-
* @param {boolean} [options.failOnError=false] - Fail once linting finishes if any errors could not be fixed.
|
|
12
|
-
* @returns {stream.Stream}
|
|
13
|
-
*/
|
|
14
|
-
export const lintSource = ({ failOnError = false } = {}) => {
|
|
15
|
-
const srcPath = gulpConfig.get('srcPath')
|
|
16
|
-
const ts = gulpConfig.get('typescript.enabled')
|
|
17
|
-
const patterns = [`${srcPath}/**/*.{js,mjs,cjs}`]
|
|
18
|
-
if (ts) {
|
|
19
|
-
patterns.push(`${srcPath}/**/*.ts`)
|
|
20
|
-
}
|
|
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)
|
|
34
|
-
.pipe(linter)
|
|
35
|
-
.pipe(dest(srcPath))
|
|
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))
|
|
40
|
-
return written
|
|
41
|
-
}
|
|
1
|
+
import * as gulpConfig from '../../gulp.config.mjs'
|
|
2
|
+
import { dest, src } from 'gulp'
|
|
3
|
+
import { neostandardLint } from './neostandardLint.mjs'
|
|
4
|
+
import { Transform } from 'node:stream'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lint the project's source (srcPath: .js, .mjs and .cjs, plus .ts when typescript.enabled) with neostandard and
|
|
8
|
+
* write the automatic fixes back in place. Shared by the sourceLint (build) and lint (standalone) tasks.
|
|
9
|
+
* @memberOf module:partials
|
|
10
|
+
* @param {Object} [options={}]
|
|
11
|
+
* @param {boolean} [options.failOnError=false] - Fail once linting finishes if any errors could not be fixed.
|
|
12
|
+
* @returns {stream.Stream}
|
|
13
|
+
*/
|
|
14
|
+
export const lintSource = ({ failOnError = false } = {}) => {
|
|
15
|
+
const srcPath = gulpConfig.get('srcPath')
|
|
16
|
+
const ts = gulpConfig.get('typescript.enabled')
|
|
17
|
+
const patterns = [`${srcPath}/**/*.{js,mjs,cjs}`]
|
|
18
|
+
if (ts) {
|
|
19
|
+
patterns.push(`${srcPath}/**/*.ts`)
|
|
20
|
+
}
|
|
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)
|
|
34
|
+
.pipe(linter)
|
|
35
|
+
.pipe(dest(srcPath))
|
|
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))
|
|
40
|
+
return written
|
|
41
|
+
}
|
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
import { createRequire } from 'node:module'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { pathToFileURL } from 'node:url'
|
|
4
|
-
|
|
5
|
-
// Several dependencies ship ESM-only (standard, gulp-ts-compile, typedoc), but this package also publishes a CommonJS
|
|
6
|
-
// build (dist/*.js) whose babel transform would turn even a dynamic import() into a require() that cannot load an ES
|
|
7
|
-
// module. Hiding the import() inside a Function constructor keeps it a genuine dynamic import that works from either
|
|
8
|
-
// build, because babel cannot see (and so cannot rewrite) an import expression which only exists as a runtime string.
|
|
9
|
-
// eslint-disable-next-line no-new-func -- deliberate, see above
|
|
10
|
-
const dynamicImport = new Function('specifier', 'return import(specifier)')
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Import an ES module (or any module) with a real dynamic import(), whichever build (ESM or CommonJS) this runs from.
|
|
14
|
-
* @memberOf module:partials
|
|
15
|
-
* @param {string} specifier - The package name, or the URL of a file.
|
|
16
|
-
* @returns {Promise<Object>} The module namespace.
|
|
17
|
-
*/
|
|
18
|
-
export const importModule = specifier => dynamicImport(specifier)
|
|
19
|
-
|
|
20
|
-
const isMissing = (error, name) => error.code === 'MODULE_NOT_FOUND' && error.message.includes(`'${name}'`)
|
|
21
|
-
|
|
22
|
-
const notInstalled = (name, { task, installHint }) => new Error(
|
|
23
|
-
`The ${task} task needs the optional peer dependency ${name}, which is not installed. ${installHint}`
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Load an optional peer dependency (one which is only needed once a task runs, so projects which never use that task
|
|
28
|
-
* do not have to install it) with require(), from the project being built rather than from this package. When it is
|
|
29
|
-
* missing the error explains how to install it.
|
|
30
|
-
* @memberOf module:partials
|
|
31
|
-
* @param {string} name - The package name.
|
|
32
|
-
* @param {Object} options
|
|
33
|
-
* @param {string} options.task - The task which needs the package, for the error message.
|
|
34
|
-
* @param {string} options.installHint - How to install it, for the error message.
|
|
35
|
-
* @param {string} [options.esmOnlyMessage] - The error message when the package turns out to be ESM-only (it cannot be require()d).
|
|
36
|
-
* @param {string} [options.projectPath=process.cwd()] - The folder of the project (which has the package.json) being built.
|
|
37
|
-
* @returns {*} Whatever the package exports.
|
|
38
|
-
* @throws {Error} With install instructions when the package is not installed.
|
|
39
|
-
*/
|
|
40
|
-
export const requirePeer = (name, { task, installHint, esmOnlyMessage, projectPath = process.cwd() }) => {
|
|
41
|
-
try {
|
|
42
|
-
return createRequire(path.join(projectPath, 'package.json'))(name)
|
|
43
|
-
} catch (error) {
|
|
44
|
-
if (isMissing(error, name)) {
|
|
45
|
-
throw notInstalled(name, { task, installHint })
|
|
46
|
-
}
|
|
47
|
-
if (error.code === 'ERR_REQUIRE_ESM' && esmOnlyMessage) {
|
|
48
|
-
throw new Error(esmOnlyMessage)
|
|
49
|
-
}
|
|
50
|
-
throw error
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Import an optional peer dependency which ships ESM-only, from the project being built. See {@link requirePeer}.
|
|
56
|
-
* @memberOf module:partials
|
|
57
|
-
* @param {string} name - The package name.
|
|
58
|
-
* @param {Object} options
|
|
59
|
-
* @param {string} options.task - The task which needs the package, for the error message.
|
|
60
|
-
* @param {string} options.installHint - How to install it, for the error message.
|
|
61
|
-
* @param {string} [options.projectPath=process.cwd()] - The folder of the project (which has the package.json) being built.
|
|
62
|
-
* @returns {Promise<Object>} The module namespace.
|
|
63
|
-
* @throws {Error} With install instructions when the package is not installed.
|
|
64
|
-
*/
|
|
65
|
-
export const importPeer = async (name, { task, installHint, projectPath = process.cwd() }) => {
|
|
66
|
-
let resolved
|
|
67
|
-
try {
|
|
68
|
-
resolved = createRequire(path.join(projectPath, 'package.json')).resolve(name)
|
|
69
|
-
} catch (error) {
|
|
70
|
-
if (isMissing(error, name)) {
|
|
71
|
-
throw notInstalled(name, { task, installHint })
|
|
72
|
-
}
|
|
73
|
-
throw error
|
|
74
|
-
}
|
|
75
|
-
return importModule(pathToFileURL(resolved).href)
|
|
76
|
-
}
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { pathToFileURL } from 'node:url'
|
|
4
|
+
|
|
5
|
+
// Several dependencies ship ESM-only (standard, gulp-ts-compile, typedoc), but this package also publishes a CommonJS
|
|
6
|
+
// build (dist/*.js) whose babel transform would turn even a dynamic import() into a require() that cannot load an ES
|
|
7
|
+
// module. Hiding the import() inside a Function constructor keeps it a genuine dynamic import that works from either
|
|
8
|
+
// build, because babel cannot see (and so cannot rewrite) an import expression which only exists as a runtime string.
|
|
9
|
+
// eslint-disable-next-line no-new-func -- deliberate, see above
|
|
10
|
+
const dynamicImport = new Function('specifier', 'return import(specifier)')
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Import an ES module (or any module) with a real dynamic import(), whichever build (ESM or CommonJS) this runs from.
|
|
14
|
+
* @memberOf module:partials
|
|
15
|
+
* @param {string} specifier - The package name, or the URL of a file.
|
|
16
|
+
* @returns {Promise<Object>} The module namespace.
|
|
17
|
+
*/
|
|
18
|
+
export const importModule = specifier => dynamicImport(specifier)
|
|
19
|
+
|
|
20
|
+
const isMissing = (error, name) => error.code === 'MODULE_NOT_FOUND' && error.message.includes(`'${name}'`)
|
|
21
|
+
|
|
22
|
+
const notInstalled = (name, { task, installHint }) => new Error(
|
|
23
|
+
`The ${task} task needs the optional peer dependency ${name}, which is not installed. ${installHint}`
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Load an optional peer dependency (one which is only needed once a task runs, so projects which never use that task
|
|
28
|
+
* do not have to install it) with require(), from the project being built rather than from this package. When it is
|
|
29
|
+
* missing the error explains how to install it.
|
|
30
|
+
* @memberOf module:partials
|
|
31
|
+
* @param {string} name - The package name.
|
|
32
|
+
* @param {Object} options
|
|
33
|
+
* @param {string} options.task - The task which needs the package, for the error message.
|
|
34
|
+
* @param {string} options.installHint - How to install it, for the error message.
|
|
35
|
+
* @param {string} [options.esmOnlyMessage] - The error message when the package turns out to be ESM-only (it cannot be require()d).
|
|
36
|
+
* @param {string} [options.projectPath=process.cwd()] - The folder of the project (which has the package.json) being built.
|
|
37
|
+
* @returns {*} Whatever the package exports.
|
|
38
|
+
* @throws {Error} With install instructions when the package is not installed.
|
|
39
|
+
*/
|
|
40
|
+
export const requirePeer = (name, { task, installHint, esmOnlyMessage, projectPath = process.cwd() }) => {
|
|
41
|
+
try {
|
|
42
|
+
return createRequire(path.join(projectPath, 'package.json'))(name)
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (isMissing(error, name)) {
|
|
45
|
+
throw notInstalled(name, { task, installHint })
|
|
46
|
+
}
|
|
47
|
+
if (error.code === 'ERR_REQUIRE_ESM' && esmOnlyMessage) {
|
|
48
|
+
throw new Error(esmOnlyMessage)
|
|
49
|
+
}
|
|
50
|
+
throw error
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Import an optional peer dependency which ships ESM-only, from the project being built. See {@link requirePeer}.
|
|
56
|
+
* @memberOf module:partials
|
|
57
|
+
* @param {string} name - The package name.
|
|
58
|
+
* @param {Object} options
|
|
59
|
+
* @param {string} options.task - The task which needs the package, for the error message.
|
|
60
|
+
* @param {string} options.installHint - How to install it, for the error message.
|
|
61
|
+
* @param {string} [options.projectPath=process.cwd()] - The folder of the project (which has the package.json) being built.
|
|
62
|
+
* @returns {Promise<Object>} The module namespace.
|
|
63
|
+
* @throws {Error} With install instructions when the package is not installed.
|
|
64
|
+
*/
|
|
65
|
+
export const importPeer = async (name, { task, installHint, projectPath = process.cwd() }) => {
|
|
66
|
+
let resolved
|
|
67
|
+
try {
|
|
68
|
+
resolved = createRequire(path.join(projectPath, 'package.json')).resolve(name)
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (isMissing(error, name)) {
|
|
71
|
+
throw notInstalled(name, { task, installHint })
|
|
72
|
+
}
|
|
73
|
+
throw error
|
|
74
|
+
}
|
|
75
|
+
return importModule(pathToFileURL(resolved).href)
|
|
76
|
+
}
|