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,252 +1,263 @@
1
- import fs from 'fs'
2
- import path from 'node:path'
3
- import ts from 'typescript'
4
- import * as setUp from '../test-helpers/setUp.mjs'
5
- import { describeExports, describeFile, describeFolder, loadTypeDoc, typeDocsFor, writeDocEntries } from './typeDocsFor.mjs'
6
-
7
- setUp.setDefaults('test-type-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}/main.ts`, "import a from './point/add'\nexport default { a }\n")
19
- write(`${srcPath}/point/add.ts`, '/** Add two numbers. */\nconst add = (a: number, b: number): number => a + b\n\nexport default add\n')
20
- write(`${srcPath}/point/get-name.ts`, 'export default function getName (): string { return "name" }\n')
21
- write(`${srcPath}/point/types.ts`, 'export interface Point { x: number, y: number }\nexport type Axis = keyof Point\n')
22
- write(`${srcPath}/point/index.ts`, "/**\n * Points of a matrix and the maths on them.\n * A second line.\n * @file Not part of the description\n * @module point\n */\nimport add from './add'\nexport default { add }\n")
23
- write(`${srcPath}/point/add.test.ts`, "test('adds', () => {})\n")
24
- write(`${srcPath}/point/add.d.ts`, 'export {}\n')
25
- write(`${srcPath}/line/length.ts`, 'export const length = 5\nexport default length\n')
26
- write(`${srcPath}/empty/README.md`, 'nothing to document\n')
27
- })
28
- )
29
-
30
- afterEach(setUp.afterEach)
31
-
32
- describe('describeExports', () => {
33
- const inSrc = name => `${gulpConfig.get('srcPath')}/${name}`
34
-
35
- test('finds a default export', () => {
36
- expect(describeExports(ts, inSrc('point/add.ts'))).toEqual({ hasDefault: true, hasNamed: false })
37
- })
38
-
39
- test('finds a default function declaration', () => {
40
- expect(describeExports(ts, inSrc('point/get-name.ts'))).toEqual({ hasDefault: true, hasNamed: false })
41
- })
42
-
43
- test('finds named exports (types)', () => {
44
- expect(describeExports(ts, inSrc('point/types.ts'))).toEqual({ hasDefault: false, hasNamed: true })
45
- })
46
-
47
- test('finds both when a file has both', () => {
48
- expect(describeExports(ts, inSrc('line/length.ts'))).toEqual({ hasDefault: true, hasNamed: true })
49
- })
50
-
51
- test('understands export lists and re-exports', () => {
52
- write(inSrc('lists.ts'), "const a = 1\nconst b = 2\nexport { a as default, b }\nexport * from './point/types'\n")
53
- expect(describeExports(ts, inSrc('lists.ts'))).toEqual({ hasDefault: true, hasNamed: true })
54
- })
55
- })
56
-
57
- describe('describeFolder', () => {
58
- test('is the text of the first comment of the index file, without its tags', () => {
59
- expect(describeFolder(`${gulpConfig.get('srcPath')}/point`)).toBe('Points of a matrix and the maths on them.\nA second line.')
60
- })
61
-
62
- test('is empty without an index file, or a comment in it', () => {
63
- expect(describeFolder(`${gulpConfig.get('srcPath')}/line`)).toBe('')
64
- write(`${gulpConfig.get('srcPath')}/bare/index.ts`, "import a from './a'\nexport default { a }\n")
65
- expect(describeFolder(`${gulpConfig.get('srcPath')}/bare`)).toBe('')
66
- })
67
- })
68
-
69
- describe('describeFile', () => {
70
- test('is the header comment of a file, without its tags', () => {
71
- write(`${gulpConfig.get('srcPath')}/header.ts`, "/**\n * Simulate what a user does.\n * @module simulate\n */\nimport a from './a'\nexport default a\n")
72
- expect(describeFile(`${gulpConfig.get('srcPath')}/header.ts`)).toBe('Simulate what a user does.')
73
- })
74
-
75
- test('is empty when the first comment documents a declaration', () => {
76
- write(`${gulpConfig.get('srcPath')}/declaration.ts`, '/**\n * Add two numbers.\n */\nconst add = (a: number, b: number) => a + b\nexport default add\n')
77
- expect(describeFile(`${gulpConfig.get('srcPath')}/declaration.ts`)).toBe('')
78
- })
79
- })
80
-
81
- describe('writeDocEntries', () => {
82
- test('writes one entry file for each folder which has source', () => {
83
- const entryDir = `${gulpConfig.get('srcPath')}/../entries`
84
- const entries = writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
85
- expect(entries.map(entry => path.basename(entry)).sort()).toEqual(['line.ts', 'point.ts'])
86
- })
87
-
88
- test('re-exports the default of each file by its name and the named exports as they are', () => {
89
- const entryDir = `${gulpConfig.get('srcPath')}/../entries`
90
- writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
91
- const absoluteSrc = path.resolve(gulpConfig.get('srcPath')).split(path.sep).join('/')
92
- const exportLines = file => fs.readFileSync(file, 'utf8').trim().split('\n').filter(line => line.startsWith('export'))
93
- expect(exportLines(`${entryDir}/point.ts`)).toEqual([
94
- `export { default as add } from '${absoluteSrc}/point/add'`,
95
- `export { default as getName } from '${absoluteSrc}/point/get-name'`,
96
- `export * from '${absoluteSrc}/point/types'`
97
- ])
98
- expect(exportLines(`${entryDir}/line.ts`)).toEqual([
99
- `export { default as length } from '${absoluteSrc}/line/length'`,
100
- `export * from '${absoluteSrc}/line/length'`
101
- ])
102
- })
103
-
104
- test('leaves out index files, tests and declaration files', () => {
105
- const entryDir = `${gulpConfig.get('srcPath')}/../entries`
106
- writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
107
- const entry = fs.readFileSync(`${entryDir}/point.ts`, 'utf8')
108
- expect(entry).not.toMatch(/from '.*(index|\.test|add\.d)/)
109
- })
110
-
111
- test('describes the module with the first comment of the index file of the folder', () => {
112
- const entryDir = `${gulpConfig.get('srcPath')}/../entries`
113
- writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
114
- expect(fs.readFileSync(`${entryDir}/point.ts`, 'utf8')).toMatch(/^\/\*\*\n \* Points of a matrix and the maths on them\.\n \* A second line\.\n \* @module\n \*\/\n\nexport/)
115
- // A folder without an index file has no comment
116
- expect(fs.readFileSync(`${entryDir}/line.ts`, 'utf8').startsWith('export')).toBe(true)
117
- })
118
-
119
- test('makes a module of a file which sits beside the folders, but not of the barrel files', () => {
120
- const srcPath = gulpConfig.get('srcPath')
121
- write(`${srcPath}/simulate.ts`, "/**\n * Simulate what a user does.\n */\nimport add from './point/add'\nexport const click = () => add(1, 2)\nexport default { click }\n")
122
- write(`${srcPath}/simulate.test.ts`, "test('x', () => {})\n")
123
- write(`${srcPath}/browser.ts`, "export * from './main'\n")
124
- const entryDir = `${srcPath}/../entries`
125
- const entries = writeDocEntries(ts, srcPath, entryDir)
126
- expect(entries.map(entry => path.basename(entry)).sort()).toEqual(['line.ts', 'point.ts', 'simulate.ts'])
127
- const absoluteSrc = path.resolve(srcPath).split(path.sep).join('/')
128
- expect(fs.readFileSync(`${entryDir}/simulate.ts`, 'utf8')).toBe(
129
- `/**\n * Simulate what a user does.\n * @module\n */\n\nexport { default as simulate } from '${absoluteSrc}/simulate'\nexport * from '${absoluteSrc}/simulate'\n`
130
- )
131
- })
132
-
133
- test('makes a module of each nested folder, named by its path, holding only the files directly in it', () => {
134
- const srcPath = gulpConfig.get('srcPath')
135
- write(`${srcPath}/point/polar/to-polar.ts`, 'export default (x: number): number => x\n')
136
- write(`${srcPath}/point/polar/index.ts`, "/**\n * Polar coordinates.\n */\nexport * from './to-polar'\n")
137
- write(`${srcPath}/point/deep/index.ts`, "export * from './deeper/far'\n")
138
- write(`${srcPath}/point/deep/deeper/far.ts`, 'export const far = 1\n')
139
- const entryDir = `${srcPath}/../entries`
140
- const entries = writeDocEntries(ts, srcPath, entryDir)
141
- const absoluteEntries = path.resolve(entryDir)
142
- expect(entries.map(entry => path.relative(absoluteEntries, entry).split(path.sep).join('/')).sort())
143
- .toEqual(['line.ts', 'point.ts', 'point/deep/deeper.ts', 'point/polar.ts'])
144
- const absoluteSrc = path.resolve(srcPath).split(path.sep).join('/')
145
- expect(fs.readFileSync(`${entryDir}/point.ts`, 'utf8')).not.toMatch(/polar|deep/)
146
- expect(fs.readFileSync(`${entryDir}/point/polar.ts`, 'utf8')).toBe(
147
- `/**\n * Polar coordinates.\n * @module point/polar\n */\n\nexport { default as toPolar } from '${absoluteSrc}/point/polar/to-polar'\n`
148
- )
149
- // A folder with only an index file (point/deep) has no module, and one without a description is still named
150
- expect(fs.readFileSync(`${entryDir}/point/deep/deeper.ts`, 'utf8')).toBe(
151
- `/**\n * @module point/deep/deeper\n */\n\nexport * from '${absoluteSrc}/point/deep/deeper/far'\n`
152
- )
153
- })
154
-
155
- test('makes one entry called index when the source has no folders', () => {
156
- const flat = `${gulpConfig.get('srcPath')}/../flat`
157
- write(`${flat}/one.ts`, 'export default 1\n')
158
- write(`${flat}/main.ts`, 'export default 2\n')
159
- const entries = writeDocEntries(ts, flat, `${flat}/../flat-entries`)
160
- expect(entries.map(entry => path.basename(entry))).toEqual(['index.ts'])
161
- expect(fs.readFileSync(entries[0], 'utf8')).toMatch(/default as one/)
162
- expect(fs.readFileSync(entries[0], 'utf8')).not.toMatch(/main/)
163
- })
164
- })
165
-
166
- describe('typeDocsFor', () => {
167
- const fakeTypeDoc = calls => () => Promise.resolve({
168
- Application: {
169
- bootstrap: async options => {
170
- calls.options = options
171
- calls.entryContents = options.entryPoints.map(entry => fs.readFileSync(entry, 'utf8'))
172
- calls.tsconfig = JSON.parse(fs.readFileSync(options.tsconfig, 'utf8'))
173
- return {
174
- convert: async () => ({ name: 'project' }),
175
- generateDocs: async (project, out) => {
176
- calls.generated = { project, out }
177
- fs.mkdirSync(out, { recursive: true })
178
- fs.writeFileSync(`${out}/index.html`, '<html></html>')
179
- }
180
- }
181
- }
182
- }
183
- })
184
-
185
- test('generates the documentation into the output directory, clearing what was there', done => {
186
- gulpConfig.set('docs.enabled', true)
187
- const outPath = `${gulpConfig.get('srcPath')}/../docs`
188
- write(`${outPath}/stale.html`, 'old')
189
- const calls = {}
190
- typeDocsFor(gulpConfig.get('srcPath'), outPath, fakeTypeDoc(calls))(() => {
191
- expect(fs.existsSync(`${outPath}/stale.html`)).toBe(false)
192
- expect(fs.existsSync(`${outPath}/index.html`)).toBe(true)
193
- expect(calls.generated.out).toBe(outPath)
194
- expect(calls.options.entryPoints.map(entry => path.basename(entry)).sort()).toEqual(['line.ts', 'point.ts'])
195
- expect(calls.options.skipErrorChecking).toBe(true)
196
- done()
197
- })
198
- })
199
-
200
- test('uses the front page and the title from the config, and none when the front page is missing', async () => {
201
- gulpConfig.set('docs.enabled', true)
202
- gulpConfig.set('docs.title', 'my-title')
203
- gulpConfig.set('docs.index', `${gulpConfig.get('srcPath')}/../MAIN.md`)
204
- write(`${gulpConfig.get('srcPath')}/../MAIN.md`, '# hello\n')
205
- const calls = {}
206
- await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
207
- expect(calls.options.name).toBe('my-title')
208
- expect(calls.options.readme).toBe(`${gulpConfig.get('srcPath')}/../MAIN.md`)
209
- gulpConfig.set('docs.index', 'does-not-exist.md')
210
- await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
211
- expect(calls.options.readme).toBe('none')
212
- })
213
-
214
- test('reads the source with the tsconfig of the project, adding the entries to it', async () => {
215
- gulpConfig.set('docs.enabled', true)
216
- gulpConfig.set('typescript.config', 'tsconfig.json')
217
- const calls = {}
218
- await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
219
- expect(calls.tsconfig.extends).toBe(path.resolve('tsconfig.json'))
220
- expect(calls.tsconfig.include).toHaveLength(2)
221
- expect(calls.tsconfig.exclude[0]).toMatch(/\*\*\/\*\.test\.\*$/)
222
- gulpConfig.set('docs.tsconfig', 'other.json')
223
- await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
224
- expect(calls.tsconfig.extends).toBe(path.resolve('other.json'))
225
- gulpConfig.set('docs.tsconfig', false)
226
- gulpConfig.set('typescript.config', false)
227
- })
228
-
229
- test('fails with a clear error when TypeDoc could not read the source', async () => {
230
- gulpConfig.set('docs.enabled', true)
231
- const failing = () => Promise.resolve({
232
- Application: { bootstrap: async () => ({ convert: async () => undefined }) }
233
- })
234
- await expect(typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, failing)()).rejects.toThrow(/could not read the TypeScript source/)
235
- })
236
-
237
- test('skips if docs.enabled is false', () => {
238
- gulpConfig.set('docs.enabled', false)
239
- const result = typeDocsFor()
240
- expect(result).toBeInstanceOf(Function)
241
- expect(result()).toBeUndefined()
242
- })
243
- })
244
-
245
- describe('loadTypeDoc', () => {
246
- // The real import() of the ESM-only TypeDoc cannot run in this project's CommonJS-transform-based Jest setup (see
247
- // tsFor.mjs), so loading it for real is checked by generating the documentation of a project, see the pull request.
248
-
249
- test('explains how to install typedoc when the project does not have it', async () => {
250
- await expect(loadTypeDoc('/')).rejects.toThrow(/optional peer dependency typedoc.*npm install --save-dev typedoc/)
251
- })
252
- })
1
+ import fs from 'fs'
2
+ import path from 'node:path'
3
+ import ts from 'typescript'
4
+ import * as setUp from '../test-helpers/setUp.mjs'
5
+ import { describeExports, describeFile, describeFolder, loadTypeDoc, typeDocsFor, writeDocEntries } from './typeDocsFor.mjs'
6
+
7
+ setUp.setDefaults('test-type-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}/main.ts`, "import a from './point/add'\nexport default { a }\n")
19
+ write(`${srcPath}/point/add.ts`, '/** Add two numbers. */\nconst add = (a: number, b: number): number => a + b\n\nexport default add\n')
20
+ write(`${srcPath}/point/get-name.ts`, 'export default function getName (): string { return "name" }\n')
21
+ write(`${srcPath}/point/types.ts`, 'export interface Point { x: number, y: number }\nexport type Axis = keyof Point\n')
22
+ write(`${srcPath}/point/index.ts`, "/**\n * Points of a matrix and the maths on them.\n * A second line.\n * @file Not part of the description\n * @module point\n */\nimport add from './add'\nexport default { add }\n")
23
+ write(`${srcPath}/point/add.test.ts`, "test('adds', () => {})\n")
24
+ write(`${srcPath}/point/add.d.ts`, 'export {}\n')
25
+ write(`${srcPath}/line/length.ts`, 'export const length = 5\nexport default length\n')
26
+ write(`${srcPath}/empty/README.md`, 'nothing to document\n')
27
+ })
28
+ )
29
+
30
+ afterEach(setUp.afterEach)
31
+
32
+ describe('describeExports', () => {
33
+ const inSrc = name => `${gulpConfig.get('srcPath')}/${name}`
34
+
35
+ test('finds a default export', () => {
36
+ expect(describeExports(ts, inSrc('point/add.ts'))).toEqual({ hasDefault: true, hasNamed: false })
37
+ })
38
+
39
+ test('finds a default function declaration', () => {
40
+ expect(describeExports(ts, inSrc('point/get-name.ts'))).toEqual({ hasDefault: true, hasNamed: false })
41
+ })
42
+
43
+ test('finds named exports (types)', () => {
44
+ expect(describeExports(ts, inSrc('point/types.ts'))).toEqual({ hasDefault: false, hasNamed: true })
45
+ })
46
+
47
+ test('finds both when a file has both', () => {
48
+ expect(describeExports(ts, inSrc('line/length.ts'))).toEqual({ hasDefault: true, hasNamed: true })
49
+ })
50
+
51
+ test('understands export lists and re-exports', () => {
52
+ write(inSrc('lists.ts'), "const a = 1\nconst b = 2\nexport { a as default, b }\nexport * from './point/types'\n")
53
+ expect(describeExports(ts, inSrc('lists.ts'))).toEqual({ hasDefault: true, hasNamed: true })
54
+ })
55
+ })
56
+
57
+ describe('describeFolder', () => {
58
+ test('is the text of the first comment of the index file, without its tags', () => {
59
+ expect(describeFolder(`${gulpConfig.get('srcPath')}/point`)).toBe('Points of a matrix and the maths on them.\nA second line.')
60
+ })
61
+
62
+ test('is empty without an index file, or a comment in it', () => {
63
+ expect(describeFolder(`${gulpConfig.get('srcPath')}/line`)).toBe('')
64
+ write(`${gulpConfig.get('srcPath')}/bare/index.ts`, "import a from './a'\nexport default { a }\n")
65
+ expect(describeFolder(`${gulpConfig.get('srcPath')}/bare`)).toBe('')
66
+ })
67
+ })
68
+
69
+ describe('describeFile', () => {
70
+ test('is the header comment of a file, without its tags', () => {
71
+ write(`${gulpConfig.get('srcPath')}/header.ts`, "/**\n * Simulate what a user does.\n * @module simulate\n */\nimport a from './a'\nexport default a\n")
72
+ expect(describeFile(`${gulpConfig.get('srcPath')}/header.ts`)).toBe('Simulate what a user does.')
73
+ })
74
+
75
+ test('is empty when the first comment documents a declaration', () => {
76
+ write(`${gulpConfig.get('srcPath')}/declaration.ts`, '/**\n * Add two numbers.\n */\nconst add = (a: number, b: number) => a + b\nexport default add\n')
77
+ expect(describeFile(`${gulpConfig.get('srcPath')}/declaration.ts`)).toBe('')
78
+ })
79
+ })
80
+
81
+ describe('writeDocEntries', () => {
82
+ test('writes one entry file for each folder which has source', () => {
83
+ const entryDir = `${gulpConfig.get('srcPath')}/../entries`
84
+ const entries = writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
85
+ expect(entries.map(entry => path.basename(entry)).sort()).toEqual(['line.ts', 'point.ts'])
86
+ })
87
+
88
+ test('re-exports the default of each file by its name and the named exports as they are', () => {
89
+ const entryDir = `${gulpConfig.get('srcPath')}/../entries`
90
+ writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
91
+ const absoluteSrc = path.resolve(gulpConfig.get('srcPath')).split(path.sep).join('/')
92
+ const exportLines = file => fs.readFileSync(file, 'utf8').trim().split('\n').filter(line => line.startsWith('export'))
93
+ expect(exportLines(`${entryDir}/point.ts`)).toEqual([
94
+ `export { default as add } from '${absoluteSrc}/point/add'`,
95
+ `export { default as getName } from '${absoluteSrc}/point/get-name'`,
96
+ `export * from '${absoluteSrc}/point/types'`
97
+ ])
98
+ expect(exportLines(`${entryDir}/line.ts`)).toEqual([
99
+ `export { default as length } from '${absoluteSrc}/line/length'`,
100
+ `export * from '${absoluteSrc}/line/length'`
101
+ ])
102
+ })
103
+
104
+ test('leaves out index files, tests and declaration files', () => {
105
+ const entryDir = `${gulpConfig.get('srcPath')}/../entries`
106
+ writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
107
+ const entry = fs.readFileSync(`${entryDir}/point.ts`, 'utf8')
108
+ expect(entry).not.toMatch(/from '.*(index|\.test|add\.d)/)
109
+ })
110
+
111
+ test('describes the module with the first comment of the index file of the folder', () => {
112
+ const entryDir = `${gulpConfig.get('srcPath')}/../entries`
113
+ writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
114
+ expect(fs.readFileSync(`${entryDir}/point.ts`, 'utf8')).toMatch(/^\/\*\*\n \* Points of a matrix and the maths on them\.\n \* A second line\.\n \* @module\n \*\/\n\nexport/)
115
+ // A folder without an index file has no comment
116
+ expect(fs.readFileSync(`${entryDir}/line.ts`, 'utf8').startsWith('export')).toBe(true)
117
+ })
118
+
119
+ test('makes a module of a file which sits beside the folders, but not of the barrel files', () => {
120
+ const srcPath = gulpConfig.get('srcPath')
121
+ write(`${srcPath}/simulate.ts`, "/**\n * Simulate what a user does.\n */\nimport add from './point/add'\nexport const click = () => add(1, 2)\nexport default { click }\n")
122
+ write(`${srcPath}/simulate.test.ts`, "test('x', () => {})\n")
123
+ write(`${srcPath}/browser.ts`, "export * from './main'\n")
124
+ const entryDir = `${srcPath}/../entries`
125
+ const entries = writeDocEntries(ts, srcPath, entryDir)
126
+ expect(entries.map(entry => path.basename(entry)).sort()).toEqual(['line.ts', 'point.ts', 'simulate.ts'])
127
+ const absoluteSrc = path.resolve(srcPath).split(path.sep).join('/')
128
+ expect(fs.readFileSync(`${entryDir}/simulate.ts`, 'utf8')).toBe(
129
+ `/**\n * Simulate what a user does.\n * @module\n */\n\nexport { default as simulate } from '${absoluteSrc}/simulate'\nexport * from '${absoluteSrc}/simulate'\n`
130
+ )
131
+ })
132
+
133
+ test('makes a module of each nested folder, named by its path, holding only the files directly in it', () => {
134
+ const srcPath = gulpConfig.get('srcPath')
135
+ write(`${srcPath}/point/polar/to-polar.ts`, 'export default (x: number): number => x\n')
136
+ write(`${srcPath}/point/polar/index.ts`, "/**\n * Polar coordinates.\n */\nexport * from './to-polar'\n")
137
+ write(`${srcPath}/point/deep/index.ts`, "export * from './deeper/far'\n")
138
+ write(`${srcPath}/point/deep/deeper/far.ts`, 'export const far = 1\n')
139
+ const entryDir = `${srcPath}/../entries`
140
+ const entries = writeDocEntries(ts, srcPath, entryDir)
141
+ const absoluteEntries = path.resolve(entryDir)
142
+ expect(entries.map(entry => path.relative(absoluteEntries, entry).split(path.sep).join('/')).sort())
143
+ .toEqual(['line.ts', 'point.ts', 'point/deep/deeper.ts', 'point/polar.ts'])
144
+ const absoluteSrc = path.resolve(srcPath).split(path.sep).join('/')
145
+ expect(fs.readFileSync(`${entryDir}/point.ts`, 'utf8')).not.toMatch(/polar|deep/)
146
+ expect(fs.readFileSync(`${entryDir}/point/polar.ts`, 'utf8')).toBe(
147
+ `/**\n * Polar coordinates.\n * @module point/polar\n */\n\nexport { default as toPolar } from '${absoluteSrc}/point/polar/to-polar'\n`
148
+ )
149
+ // A folder with only an index file (point/deep) has no module, and one without a description is still named
150
+ expect(fs.readFileSync(`${entryDir}/point/deep/deeper.ts`, 'utf8')).toBe(
151
+ `/**\n * @module point/deep/deeper\n */\n\nexport * from '${absoluteSrc}/point/deep/deeper/far'\n`
152
+ )
153
+ })
154
+
155
+ test('makes one entry called index when the source has no folders', () => {
156
+ const flat = `${gulpConfig.get('srcPath')}/../flat`
157
+ write(`${flat}/one.ts`, 'export default 1\n')
158
+ write(`${flat}/main.ts`, 'export default 2\n')
159
+ const entries = writeDocEntries(ts, flat, `${flat}/../flat-entries`)
160
+ expect(entries.map(entry => path.basename(entry))).toEqual(['index.ts'])
161
+ expect(fs.readFileSync(entries[0], 'utf8')).toMatch(/default as one/)
162
+ expect(fs.readFileSync(entries[0], 'utf8')).not.toMatch(/main/)
163
+ })
164
+ })
165
+
166
+ describe('typeDocsFor', () => {
167
+ const fakeTypeDoc = calls => () => Promise.resolve({
168
+ Application: {
169
+ bootstrap: async options => {
170
+ calls.options = options
171
+ calls.entryContents = options.entryPoints.map(entry => fs.readFileSync(entry, 'utf8'))
172
+ calls.tsconfig = JSON.parse(fs.readFileSync(options.tsconfig, 'utf8'))
173
+ return {
174
+ convert: async () => ({ name: 'project' }),
175
+ generateDocs: async (project, out) => {
176
+ calls.generated = { project, out }
177
+ fs.mkdirSync(out, { recursive: true })
178
+ fs.writeFileSync(`${out}/index.html`, '<html></html>')
179
+ }
180
+ }
181
+ }
182
+ }
183
+ })
184
+
185
+ test('generates the documentation into the output directory, clearing what was there', done => {
186
+ gulpConfig.set('docs.enabled', true)
187
+ const outPath = `${gulpConfig.get('srcPath')}/../docs`
188
+ write(`${outPath}/stale.html`, 'old')
189
+ const calls = {}
190
+ typeDocsFor(gulpConfig.get('srcPath'), outPath, fakeTypeDoc(calls))(() => {
191
+ expect(fs.existsSync(`${outPath}/stale.html`)).toBe(false)
192
+ expect(fs.existsSync(`${outPath}/index.html`)).toBe(true)
193
+ expect(calls.generated.out).toBe(outPath)
194
+ expect(calls.options.entryPoints.map(entry => path.basename(entry)).sort()).toEqual(['line.ts', 'point.ts'])
195
+ expect(calls.options.skipErrorChecking).toBe(true)
196
+ done()
197
+ })
198
+ })
199
+
200
+ test('uses the front page and the title from the config, and none when the front page is missing', async () => {
201
+ gulpConfig.set('docs.enabled', true)
202
+ gulpConfig.set('docs.title', 'my-title')
203
+ gulpConfig.set('docs.index', `${gulpConfig.get('srcPath')}/../MAIN.md`)
204
+ write(`${gulpConfig.get('srcPath')}/../MAIN.md`, '# hello\n')
205
+ const calls = {}
206
+ await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
207
+ expect(calls.options.name).toBe('my-title')
208
+ expect(calls.options.readme).toBe(`${gulpConfig.get('srcPath')}/../MAIN.md`)
209
+ gulpConfig.set('docs.index', 'does-not-exist.md')
210
+ await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
211
+ expect(calls.options.readme).toBe('none')
212
+ })
213
+
214
+ test('passes docs.gitRevision on to TypeDoc, and leaves TypeDoc\'s own default when it is false', async () => {
215
+ gulpConfig.set('docs.enabled', true)
216
+ const calls = {}
217
+ await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
218
+ expect('gitRevision' in calls.options).toBe(false)
219
+ gulpConfig.set('docs.gitRevision', 'main')
220
+ await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
221
+ expect(calls.options.gitRevision).toBe('main')
222
+ gulpConfig.set('docs.gitRevision', false)
223
+ })
224
+
225
+ test('reads the source with the tsconfig of the project, adding the entries to it', async () => {
226
+ gulpConfig.set('docs.enabled', true)
227
+ gulpConfig.set('typescript.config', 'tsconfig.json')
228
+ const calls = {}
229
+ await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
230
+ expect(calls.tsconfig.extends).toBe(path.resolve('tsconfig.json'))
231
+ expect(calls.tsconfig.include).toHaveLength(2)
232
+ expect(calls.tsconfig.exclude[0]).toMatch(/\*\*\/\*\.test\.\*$/)
233
+ gulpConfig.set('docs.tsconfig', 'other.json')
234
+ await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
235
+ expect(calls.tsconfig.extends).toBe(path.resolve('other.json'))
236
+ gulpConfig.set('docs.tsconfig', false)
237
+ gulpConfig.set('typescript.config', false)
238
+ })
239
+
240
+ test('fails with a clear error when TypeDoc could not read the source', async () => {
241
+ gulpConfig.set('docs.enabled', true)
242
+ const failing = () => Promise.resolve({
243
+ Application: { bootstrap: async () => ({ convert: async () => undefined }) }
244
+ })
245
+ await expect(typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, failing)()).rejects.toThrow(/could not read the TypeScript source/)
246
+ })
247
+
248
+ test('skips if docs.enabled is false', () => {
249
+ gulpConfig.set('docs.enabled', false)
250
+ const result = typeDocsFor()
251
+ expect(result).toBeInstanceOf(Function)
252
+ expect(result()).toBeUndefined()
253
+ })
254
+ })
255
+
256
+ describe('loadTypeDoc', () => {
257
+ // The real import() of the ESM-only TypeDoc cannot run in this project's CommonJS-transform-based Jest setup (see
258
+ // tsFor.mjs), so loading it for real is checked by generating the documentation of a project, see the pull request.
259
+
260
+ test('explains how to install typedoc when the project does not have it', async () => {
261
+ await expect(loadTypeDoc('/')).rejects.toThrow(/optional peer dependency typedoc.*npm install --save-dev typedoc/)
262
+ })
263
+ })
@@ -1,31 +1,31 @@
1
- /**
2
- * Micro-functions used as components for the main gulp functions.
3
- * @file
4
- * @author Joshua Heagle <joshuaheagle@gmail.com>
5
- * @version 3.0.0
6
- * @module partials
7
- * @memberOf module:js-build-tools
8
- */
9
-
10
- export { addToReadme } from './partials/addToReadme.mjs'
11
- export { beginWatcher } from './partials/beginWatcher.mjs'
12
- export { clean } from './partials/clean.mjs'
13
- export { copyFor } from './partials/copyFor.mjs'
14
- export { distFor } from './partials/distFor.mjs'
15
- export { distForSrc } from './partials/distForSrc.mjs'
16
- export { distSeries } from './partials/distSeries.mjs'
17
- export { imagesFor } from './partials/imagesFor.mjs'
18
- export { importModule, importPeer, requirePeer } from './partials/loadPeer.mjs'
19
- export { jsdocDocsFor } from './partials/jsdocDocsFor.mjs'
20
- export { loadPlaywright, launchBrowser } from './partials/loadPlaywright.mjs'
21
- export { lintSource } from './partials/lintSource.mjs'
22
- export { minifyFor } from './partials/minifyFor.mjs'
23
- export { neostandardLint } from './partials/neostandardLint.mjs'
24
- export { readmeTemplate } from './partials/readmeTemplate.mjs'
25
- export { removeDirectory } from './partials/removeDirectory.mjs'
26
- export { resolveDocsMethod } from './partials/resolveDocsMethod.mjs'
27
- export { runOnChange } from './partials/runOnChange.mjs'
28
- export { sassFor } from './partials/sassFor.mjs'
29
- export { serveStatic } from './partials/serveStatic.mjs'
30
- export { tsFor } from './partials/tsFor.mjs'
31
- export { typeDocsFor } from './partials/typeDocsFor.mjs'
1
+ /**
2
+ * Micro-functions used as components for the main gulp functions.
3
+ * @file
4
+ * @author Joshua Heagle <joshuaheagle@gmail.com>
5
+ * @version 3.0.0
6
+ * @module partials
7
+ * @memberOf module:js-build-tools
8
+ */
9
+
10
+ export { addToReadme } from './partials/addToReadme.mjs'
11
+ export { beginWatcher } from './partials/beginWatcher.mjs'
12
+ export { clean } from './partials/clean.mjs'
13
+ export { copyFor } from './partials/copyFor.mjs'
14
+ export { distFor } from './partials/distFor.mjs'
15
+ export { distForSrc } from './partials/distForSrc.mjs'
16
+ export { distSeries } from './partials/distSeries.mjs'
17
+ export { imagesFor } from './partials/imagesFor.mjs'
18
+ export { importModule, importPeer, requirePeer } from './partials/loadPeer.mjs'
19
+ export { jsdocDocsFor } from './partials/jsdocDocsFor.mjs'
20
+ export { loadPlaywright, launchBrowser } from './partials/loadPlaywright.mjs'
21
+ export { lintSource } from './partials/lintSource.mjs'
22
+ export { minifyFor } from './partials/minifyFor.mjs'
23
+ export { neostandardLint } from './partials/neostandardLint.mjs'
24
+ export { readmeTemplate } from './partials/readmeTemplate.mjs'
25
+ export { removeDirectory } from './partials/removeDirectory.mjs'
26
+ export { resolveDocsMethod } from './partials/resolveDocsMethod.mjs'
27
+ export { runOnChange } from './partials/runOnChange.mjs'
28
+ export { sassFor } from './partials/sassFor.mjs'
29
+ export { serveStatic } from './partials/serveStatic.mjs'
30
+ export { tsFor } from './partials/tsFor.mjs'
31
+ export { typeDocsFor } from './partials/typeDocsFor.mjs'
@@ -1,7 +1,7 @@
1
- import * as partials from './partials.mjs'
2
-
3
- describe('partials', () => {
4
- test('is object with all partial exports', () => {
5
- expect(Object.keys(partials).length).toBe(25)
6
- })
7
- })
1
+ import * as partials from './partials.mjs'
2
+
3
+ describe('partials', () => {
4
+ test('is object with all partial exports', () => {
5
+ expect(Object.keys(partials).length).toBe(25)
6
+ })
7
+ })
@@ -1,4 +1,4 @@
1
- import { sassFor } from './partials/sassFor.mjs'
2
-
3
- // Compile sass into CSS & auto-inject into browsers
4
- export const sass = () => sassFor()
1
+ import { sassFor } from './partials/sassFor.mjs'
2
+
3
+ // Compile sass into CSS & auto-inject into browsers
4
+ export const sass = () => sassFor()