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,88 +1,88 @@
|
|
|
1
|
-
/* eslint-disable no-template-curly-in-string -- fixture source code, the ${...} is meant to stay literal */
|
|
2
|
-
import fs from 'fs'
|
|
3
|
-
import { Transform, PassThrough } from 'node:stream'
|
|
4
|
-
import ts from 'typescript'
|
|
5
|
-
import File from 'vinyl'
|
|
6
|
-
import * as setUp from '../test-helpers/setUp.mjs'
|
|
7
|
-
import { tsFor, loadTypescript } from './tsFor.mjs'
|
|
8
|
-
import { countMatches } from 'test-filesystem'
|
|
9
|
-
|
|
10
|
-
setUp.setDefaults('test-ts-for')
|
|
11
|
-
const gulpConfig = setUp.gulpConfig
|
|
12
|
-
|
|
13
|
-
const rawContents = 'export function sayHello(name: string) {\n' +
|
|
14
|
-
' return `Hello from ${name}`;\n' +
|
|
15
|
-
'}'
|
|
16
|
-
|
|
17
|
-
// tsFor's real implementation loads gulp-ts-compile (a genuine ESM-only package) via a dynamic import() hidden
|
|
18
|
-
// from babel's static analysis - necessary for production correctness (see tsFor.mjs's own comments), but not
|
|
19
|
-
// something this project's current CommonJS-transform-based Jest setup can execute without a much bigger
|
|
20
|
-
// migration to Jest's native ESM support. This fake stands in for it here, doing real (not mocked-away)
|
|
21
|
-
// TypeScript transpilation via `ts.transpileModule` so this test still exercises genuine compile behavior -
|
|
22
|
-
// gulp-ts-compile's own test suite separately covers its whole-program/cross-file-import compile behavior.
|
|
23
|
-
const fakeLoadTsCompile = () => Promise.resolve((options) => {
|
|
24
|
-
const transform = new Transform({
|
|
25
|
-
objectMode: true,
|
|
26
|
-
transform (file, enc, callback) {
|
|
27
|
-
const { outputText } = ts.transpileModule(file.contents.toString(), { compilerOptions: options })
|
|
28
|
-
jsStream.push(new File({ cwd: file.cwd, base: file.base, path: file.path.replace(/\.ts$/, '.js'), contents: Buffer.from(outputText) }))
|
|
29
|
-
callback()
|
|
30
|
-
},
|
|
31
|
-
flush (callback) {
|
|
32
|
-
jsStream.end()
|
|
33
|
-
dtsStream.end()
|
|
34
|
-
callback()
|
|
35
|
-
}
|
|
36
|
-
})
|
|
37
|
-
const jsStream = new PassThrough({ objectMode: true })
|
|
38
|
-
const dtsStream = new PassThrough({ objectMode: true })
|
|
39
|
-
transform.js = jsStream
|
|
40
|
-
transform.dts = dtsStream
|
|
41
|
-
return transform
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
beforeEach(
|
|
45
|
-
() => setUp.beforeEach()
|
|
46
|
-
.then(
|
|
47
|
-
async () => {
|
|
48
|
-
const srcPath = gulpConfig.get('srcPath')
|
|
49
|
-
return fs.writeFileSync(`${srcPath}/typeScript.ts`, rawContents)
|
|
50
|
-
}
|
|
51
|
-
)
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
afterEach(setUp.afterEach)
|
|
55
|
-
|
|
56
|
-
describe('tsFor', () => {
|
|
57
|
-
test('copies the src directory and compiles it into the dist directory', done => {
|
|
58
|
-
gulpConfig.set('typescript.enabled', true)
|
|
59
|
-
const distPath = gulpConfig.get('typescript.to')
|
|
60
|
-
const srcPath = gulpConfig.get('srcPath')
|
|
61
|
-
const srcFile = `${srcPath}/typeScript.ts`
|
|
62
|
-
expect.assertions(3)
|
|
63
|
-
const oldContents = fs.readFileSync(srcFile).toString()
|
|
64
|
-
expect(countMatches(oldContents, 'sayHello(name: string)')).toEqual(1)
|
|
65
|
-
tsFor(srcFile, distPath, fakeLoadTsCompile)(() => {
|
|
66
|
-
expect(fs.existsSync(distPath)).toBeTruthy()
|
|
67
|
-
const compiledContents = fs.readFileSync(`${distPath}/typeScript.js`).toString()
|
|
68
|
-
expect(countMatches(compiledContents, 'sayHello(name)')).toEqual(1)
|
|
69
|
-
done()
|
|
70
|
-
})
|
|
71
|
-
}, 60000)
|
|
72
|
-
|
|
73
|
-
test('skips if typescript.enabled is false', () => {
|
|
74
|
-
gulpConfig.set('typescript.enabled', false)
|
|
75
|
-
const result = tsFor()
|
|
76
|
-
expect(result).toBeInstanceOf(Function)
|
|
77
|
-
})
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
describe('loadTypescript', () => {
|
|
81
|
-
test('loads the typescript of the project being built', () => {
|
|
82
|
-
expect(loadTypescript().version).toBe(ts.version)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
test('explains how to install typescript when the project does not have it', () => {
|
|
86
|
-
expect(() => loadTypescript('/')).toThrow(/optional peer dependency typescript.*npm install --save-dev typescript/)
|
|
87
|
-
})
|
|
88
|
-
})
|
|
1
|
+
/* eslint-disable no-template-curly-in-string -- fixture source code, the ${...} is meant to stay literal */
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import { Transform, PassThrough } from 'node:stream'
|
|
4
|
+
import ts from 'typescript'
|
|
5
|
+
import File from 'vinyl'
|
|
6
|
+
import * as setUp from '../test-helpers/setUp.mjs'
|
|
7
|
+
import { tsFor, loadTypescript } from './tsFor.mjs'
|
|
8
|
+
import { countMatches } from 'test-filesystem'
|
|
9
|
+
|
|
10
|
+
setUp.setDefaults('test-ts-for')
|
|
11
|
+
const gulpConfig = setUp.gulpConfig
|
|
12
|
+
|
|
13
|
+
const rawContents = 'export function sayHello(name: string) {\n' +
|
|
14
|
+
' return `Hello from ${name}`;\n' +
|
|
15
|
+
'}'
|
|
16
|
+
|
|
17
|
+
// tsFor's real implementation loads gulp-ts-compile (a genuine ESM-only package) via a dynamic import() hidden
|
|
18
|
+
// from babel's static analysis - necessary for production correctness (see tsFor.mjs's own comments), but not
|
|
19
|
+
// something this project's current CommonJS-transform-based Jest setup can execute without a much bigger
|
|
20
|
+
// migration to Jest's native ESM support. This fake stands in for it here, doing real (not mocked-away)
|
|
21
|
+
// TypeScript transpilation via `ts.transpileModule` so this test still exercises genuine compile behavior -
|
|
22
|
+
// gulp-ts-compile's own test suite separately covers its whole-program/cross-file-import compile behavior.
|
|
23
|
+
const fakeLoadTsCompile = () => Promise.resolve((options) => {
|
|
24
|
+
const transform = new Transform({
|
|
25
|
+
objectMode: true,
|
|
26
|
+
transform (file, enc, callback) {
|
|
27
|
+
const { outputText } = ts.transpileModule(file.contents.toString(), { compilerOptions: options })
|
|
28
|
+
jsStream.push(new File({ cwd: file.cwd, base: file.base, path: file.path.replace(/\.ts$/, '.js'), contents: Buffer.from(outputText) }))
|
|
29
|
+
callback()
|
|
30
|
+
},
|
|
31
|
+
flush (callback) {
|
|
32
|
+
jsStream.end()
|
|
33
|
+
dtsStream.end()
|
|
34
|
+
callback()
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
const jsStream = new PassThrough({ objectMode: true })
|
|
38
|
+
const dtsStream = new PassThrough({ objectMode: true })
|
|
39
|
+
transform.js = jsStream
|
|
40
|
+
transform.dts = dtsStream
|
|
41
|
+
return transform
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
beforeEach(
|
|
45
|
+
() => setUp.beforeEach()
|
|
46
|
+
.then(
|
|
47
|
+
async () => {
|
|
48
|
+
const srcPath = gulpConfig.get('srcPath')
|
|
49
|
+
return fs.writeFileSync(`${srcPath}/typeScript.ts`, rawContents)
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
afterEach(setUp.afterEach)
|
|
55
|
+
|
|
56
|
+
describe('tsFor', () => {
|
|
57
|
+
test('copies the src directory and compiles it into the dist directory', done => {
|
|
58
|
+
gulpConfig.set('typescript.enabled', true)
|
|
59
|
+
const distPath = gulpConfig.get('typescript.to')
|
|
60
|
+
const srcPath = gulpConfig.get('srcPath')
|
|
61
|
+
const srcFile = `${srcPath}/typeScript.ts`
|
|
62
|
+
expect.assertions(3)
|
|
63
|
+
const oldContents = fs.readFileSync(srcFile).toString()
|
|
64
|
+
expect(countMatches(oldContents, 'sayHello(name: string)')).toEqual(1)
|
|
65
|
+
tsFor(srcFile, distPath, fakeLoadTsCompile)(() => {
|
|
66
|
+
expect(fs.existsSync(distPath)).toBeTruthy()
|
|
67
|
+
const compiledContents = fs.readFileSync(`${distPath}/typeScript.js`).toString()
|
|
68
|
+
expect(countMatches(compiledContents, 'sayHello(name)')).toEqual(1)
|
|
69
|
+
done()
|
|
70
|
+
})
|
|
71
|
+
}, 60000)
|
|
72
|
+
|
|
73
|
+
test('skips if typescript.enabled is false', () => {
|
|
74
|
+
gulpConfig.set('typescript.enabled', false)
|
|
75
|
+
const result = tsFor()
|
|
76
|
+
expect(result).toBeInstanceOf(Function)
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
describe('loadTypescript', () => {
|
|
81
|
+
test('loads the typescript of the project being built', () => {
|
|
82
|
+
expect(loadTypescript().version).toBe(ts.version)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('explains how to install typescript when the project does not have it', () => {
|
|
86
|
+
expect(() => loadTypescript('/')).toThrow(/optional peer dependency typescript.*npm install --save-dev typescript/)
|
|
87
|
+
})
|
|
88
|
+
})
|
|
@@ -1,227 +1,231 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import os from 'node:os'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import * as gulpConfig from '../../gulp.config.mjs'
|
|
5
|
-
import { importPeer } from './loadPeer.mjs'
|
|
6
|
-
import { loadTypescript } from './tsFor.mjs'
|
|
7
|
-
import { removeDirectory } from './removeDirectory.mjs'
|
|
8
|
-
|
|
9
|
-
const installHint = 'Install it in your project with: npm install --save-dev typedoc'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Find the TypeDoc of the project being built (an optional peer dependency, only needed when the docs task is
|
|
13
|
-
* enabled) and load it, so projects which do not document TypeScript never need it installed.
|
|
14
|
-
* @memberOf module:partials
|
|
15
|
-
* @param {string} [projectPath=process.cwd()] The folder of the project (which has the package.json) being built.
|
|
16
|
-
* @returns {Promise<Object>} The TypeDoc module.
|
|
17
|
-
* @throws {Error} With install instructions when typedoc is not installed.
|
|
18
|
-
*/
|
|
19
|
-
export const loadTypeDoc = (projectPath = process.cwd()) => importPeer('typedoc', { task: 'docs', installHint, projectPath })
|
|
20
|
-
|
|
21
|
-
const isSource = fileName => /\.(ts|tsx|mts|cts)$/.test(fileName) && !/\.d\.[cm]?ts$/.test(fileName) && !/\.(test|spec)\./.test(fileName)
|
|
22
|
-
|
|
23
|
-
const listSources = dirPath => fs.readdirSync(dirPath, { withFileTypes: true })
|
|
24
|
-
.filter(entry => entry.isFile() && isSource(entry.name))
|
|
25
|
-
.map(entry => path.join(dirPath, entry.name))
|
|
26
|
-
|
|
27
|
-
// Every folder below dirPath (at any depth), named by its path from dirPath, with the source files directly in it
|
|
28
|
-
const listFolders = (dirPath, prefix = '') => fs.readdirSync(dirPath, { withFileTypes: true })
|
|
29
|
-
.filter(entry => entry.isDirectory() && entry.name !== 'node_modules')
|
|
30
|
-
.flatMap(entry => {
|
|
31
|
-
const folderPath = path.join(dirPath, entry.name)
|
|
32
|
-
const name = `${prefix}${entry.name}`
|
|
33
|
-
return [{ name, path: folderPath, files: listSources(folderPath) }, ...listFolders(folderPath, `${name}/`)]
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
const toIdentifier = fileName => fileName.replace(/[^\w$]+(\w)?/g, (match, letter) => (letter || '').toUpperCase()).replace(/^(\d)/, '_$1')
|
|
37
|
-
|
|
38
|
-
const toModulePath = filePath => filePath.replace(/\.[cm]?tsx?$/, '').split(path.sep).join('/')
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Describe what a source file exports: whether it has a default export, and whether it has any named exports.
|
|
42
|
-
* @memberOf module:partials
|
|
43
|
-
* @param {Object} ts - The TypeScript compiler API.
|
|
44
|
-
* @param {string} filePath
|
|
45
|
-
* @returns {{hasDefault: boolean, hasNamed: boolean}}
|
|
46
|
-
*/
|
|
47
|
-
export const describeExports = (ts, filePath) => {
|
|
48
|
-
const source = ts.createSourceFile(filePath, fs.readFileSync(filePath, 'utf8'), ts.ScriptTarget.Latest)
|
|
49
|
-
let hasDefault = false
|
|
50
|
-
let hasNamed = false
|
|
51
|
-
source.statements.forEach(statement => {
|
|
52
|
-
if (ts.isExportAssignment(statement)) {
|
|
53
|
-
hasDefault = hasDefault || !statement.isExportEquals
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
if (ts.isExportDeclaration(statement)) {
|
|
57
|
-
const names = statement.exportClause && ts.isNamedExports(statement.exportClause) ? statement.exportClause.elements : null
|
|
58
|
-
if (names && names.some(element => element.name.text === 'default')) {
|
|
59
|
-
hasDefault = true
|
|
60
|
-
}
|
|
61
|
-
if (!names || names.some(element => element.name.text !== 'default')) {
|
|
62
|
-
hasNamed = true
|
|
63
|
-
}
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
const modifiers = ts.canHaveModifiers(statement) ? ts.getModifiers(statement) || [] : []
|
|
67
|
-
if (modifiers.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
68
|
-
if (modifiers.some(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword)) {
|
|
69
|
-
hasDefault = true
|
|
70
|
-
} else {
|
|
71
|
-
hasNamed = true
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
return { hasDefault, hasNamed }
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* The description of a folder for its module: the first doc comment of the folder's index file, without its tags
|
|
80
|
-
* (@module, @file, ...), or an empty string when there is none.
|
|
81
|
-
* @memberOf module:partials
|
|
82
|
-
* @param {string} folderPath
|
|
83
|
-
* @returns {string}
|
|
84
|
-
*/
|
|
85
|
-
export const describeFolder = folderPath => {
|
|
86
|
-
const indexFile = ['index.ts', 'index.tsx', 'index.mts', 'index.cts'].map(name => path.join(folderPath, name)).find(file => fs.existsSync(file))
|
|
87
|
-
return indexFile ? describeFile(indexFile) : ''
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* The description of a source file for its module: its first doc comment without the tags (@module, @file, ...), when
|
|
92
|
-
* that comment is a header (it is followed by an import or export, so it does not document a declaration itself), or
|
|
93
|
-
* an empty string.
|
|
94
|
-
* @memberOf module:partials
|
|
95
|
-
* @param {string} filePath
|
|
96
|
-
* @returns {string}
|
|
97
|
-
*/
|
|
98
|
-
export const describeFile = filePath => {
|
|
99
|
-
const comment = fs.readFileSync(filePath, 'utf8').match(/^\s*\/\*\*([\s\S]*?)\*\/\s*(import\b|export\s+(\*|\{[^}]*\}\s+from)|$)/)
|
|
100
|
-
if (!comment) {
|
|
101
|
-
return ''
|
|
102
|
-
}
|
|
103
|
-
const lines = comment[1].split('\n').map(line => line.replace(/^\s*\* ?/, '').trimEnd())
|
|
104
|
-
const tagAt = lines.findIndex(line => /^@\w+/.test(line))
|
|
105
|
-
return (tagAt === -1 ? lines : lines.slice(0, tagAt)).join('\n').trim()
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Write one entry file per folder of the source directory into the entry directory. TypeDoc makes a module of every
|
|
110
|
-
* entry file, so the modules of the documentation mirror the folders. Nested folders are modules of their own, named
|
|
111
|
-
* by their path (utilities/parsers), and a folder's module holds only the files directly in it. The default export of each source file is
|
|
112
|
-
* re-exported under the name of the file (the source files hold one function each), and anything else a file exports
|
|
113
|
-
* is re-exported as it is (that is where the types.ts files go). Index files (the barrels), tests and declaration
|
|
114
|
-
* files are left out, but the first doc comment of a folder's index file becomes the description of its module.
|
|
115
|
-
* A file which sits beside the folders (not an index, main or browser file) is a module of its own, described by its header
|
|
116
|
-
* comment. A source directory with no folders gets a single entry called index.
|
|
117
|
-
* @memberOf module:partials
|
|
118
|
-
* @param {Object} ts - The TypeScript compiler API.
|
|
119
|
-
* @param {string} srcDir - The directory holding the TypeScript source.
|
|
120
|
-
* @param {string} entryDir - The directory to write the entry files into.
|
|
121
|
-
* @returns {Array<string>} The paths of the entry files.
|
|
122
|
-
*/
|
|
123
|
-
export const writeDocEntries = (ts, srcDir, entryDir) => {
|
|
124
|
-
const absoluteSrc = path.resolve(srcDir)
|
|
125
|
-
fs.mkdirSync(entryDir, { recursive: true })
|
|
126
|
-
const folders = listFolders(absoluteSrc).filter(folder => folder.files.some(file => !/^index\./.test(path.basename(file))))
|
|
127
|
-
const rootFiles = listSources(absoluteSrc).filter(file => !/^(index|main|browser)\./.test(path.basename(file)))
|
|
128
|
-
// Folders make modules, and so do the files which sit beside them (except the barrels: index, main and browser, the
|
|
129
|
-
// browser bundle's entry). A source directory with no folders gets a single module called index which holds its files.
|
|
130
|
-
const groups = folders.length
|
|
131
|
-
? [
|
|
132
|
-
...folders,
|
|
133
|
-
...rootFiles
|
|
134
|
-
.map(file => ({ name: toIdentifier(path.basename(file).replace(/\.[cm]?tsx?$/, '')), files: [file], description: describeFile(file) }))
|
|
135
|
-
.filter(group => !folders.some(folder => folder.name === group.name))
|
|
136
|
-
]
|
|
137
|
-
: [{ name: 'index', path: absoluteSrc, files: rootFiles }]
|
|
138
|
-
return groups.map(group => {
|
|
139
|
-
const lines = group.files
|
|
140
|
-
.filter(file => !/^index\./.test(path.basename(file)))
|
|
141
|
-
.sort()
|
|
142
|
-
.flatMap(file => {
|
|
143
|
-
const modulePath = toModulePath(file)
|
|
144
|
-
const { hasDefault, hasNamed } = describeExports(ts, file)
|
|
145
|
-
const fileName = path.basename(file).replace(/\.[cm]?tsx?$/, '')
|
|
146
|
-
return [
|
|
147
|
-
...(hasDefault ? [`export { default as ${toIdentifier(fileName)} } from '${modulePath}'`] : []),
|
|
148
|
-
...(hasNamed ? [`export * from '${modulePath}'`] : [])
|
|
149
|
-
]
|
|
150
|
-
})
|
|
151
|
-
const description = group.description ?? (group.path ? describeFolder(group.path) : '')
|
|
152
|
-
// TypeDoc takes the first comment of an entry file which has the @module tag as the description of the module
|
|
153
|
-
// A nested folder's module is named by its path (utilities/parsers), so it is not mistaken for its parent's
|
|
154
|
-
const moduleName = group.name.includes('/') ? ` ${group.name}` : ''
|
|
155
|
-
const moduleComment = description || moduleName ? `/**\n${description ? description.split('\n').map(line => ` * ${line}`.trimEnd()).join('\n') + '\n' : ''} * @module${moduleName}\n */\n\n` : ''
|
|
156
|
-
const entryPath = path.join(entryDir, `${group.name}.ts`)
|
|
157
|
-
fs.mkdirSync(path.dirname(entryPath), { recursive: true })
|
|
158
|
-
fs.writeFileSync(entryPath, moduleComment + lines.join('\n') + '\n')
|
|
159
|
-
return entryPath
|
|
160
|
-
})
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const projectName = () => {
|
|
164
|
-
try {
|
|
165
|
-
return JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')).name
|
|
166
|
-
} catch (error) {
|
|
167
|
-
return path.basename(process.cwd())
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Generate the HTML documentation of a TypeScript project with TypeDoc, straight from the TypeScript source, so the
|
|
173
|
-
* types (and the comments on them) are documented from where they are defined. The modules mirror the folders of the
|
|
174
|
-
* source, see {@link writeDocEntries}. Configure this with 'docs.from', 'docs.to', 'docs.index', 'docs.title',
|
|
175
|
-
* 'docs.tsconfig' and 'docs.logLevel'.
|
|
176
|
-
* @memberOf module:partials
|
|
177
|
-
* @param {string} [srcPath=gulpConfig.get('docs.from')] - The directory holding the TypeScript source.
|
|
178
|
-
* @param {string} [outPath=gulpConfig.get('docs.to')] - The directory the documentation is generated into (cleared first).
|
|
179
|
-
* @param {function(string): Promise<Object>} [loadDocs] - Resolves the TypeDoc module, tests can inject a stand-in.
|
|
180
|
-
* @returns {Function} A gulp task, it accepts an optional callback and returns a promise.
|
|
181
|
-
*/
|
|
182
|
-
export const typeDocsFor = (
|
|
183
|
-
srcPath = gulpConfig.get('docs.from'),
|
|
184
|
-
outPath = gulpConfig.get('docs.to'),
|
|
185
|
-
loadDocs = loadTypeDoc
|
|
186
|
-
) => {
|
|
187
|
-
if (gulpConfig.get('docs.enabled') === false) {
|
|
188
|
-
return () => {}
|
|
189
|
-
}
|
|
190
|
-
return (done) => {
|
|
191
|
-
const generate = async () => {
|
|
192
|
-
const ts = loadTypescript()
|
|
193
|
-
const { Application } = await loadDocs()
|
|
194
|
-
const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'typedocs-'))
|
|
195
|
-
try {
|
|
196
|
-
const entryPoints = writeDocEntries(ts, srcPath, path.join(workDir, 'entries'))
|
|
197
|
-
// The entry files live outside the project, so the tsconfig which is used for the documentation extends the
|
|
198
|
-
// one of the project and adds the entries to the files it includes.
|
|
199
|
-
const projectConfig = gulpConfig.get('docs.tsconfig') || gulpConfig.get('typescript.config')
|
|
200
|
-
const tsconfig = path.join(workDir, 'tsconfig.json')
|
|
201
|
-
fs.writeFileSync(tsconfig, JSON.stringify({
|
|
202
|
-
...(projectConfig ? { extends: path.resolve(projectConfig) } : {}),
|
|
203
|
-
include: [path.join(workDir, 'entries', '**', '*.ts').split(path.sep).join('/'), path.resolve(srcPath).split(path.sep).join('/') + '/**/*.ts'],
|
|
204
|
-
exclude: [path.resolve(srcPath).split(path.sep).join('/') + '/**/*.test.*']
|
|
205
|
-
}))
|
|
206
|
-
const indexFile = gulpConfig.get('docs.index')
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
await app.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import * as gulpConfig from '../../gulp.config.mjs'
|
|
5
|
+
import { importPeer } from './loadPeer.mjs'
|
|
6
|
+
import { loadTypescript } from './tsFor.mjs'
|
|
7
|
+
import { removeDirectory } from './removeDirectory.mjs'
|
|
8
|
+
|
|
9
|
+
const installHint = 'Install it in your project with: npm install --save-dev typedoc'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Find the TypeDoc of the project being built (an optional peer dependency, only needed when the docs task is
|
|
13
|
+
* enabled) and load it, so projects which do not document TypeScript never need it installed.
|
|
14
|
+
* @memberOf module:partials
|
|
15
|
+
* @param {string} [projectPath=process.cwd()] The folder of the project (which has the package.json) being built.
|
|
16
|
+
* @returns {Promise<Object>} The TypeDoc module.
|
|
17
|
+
* @throws {Error} With install instructions when typedoc is not installed.
|
|
18
|
+
*/
|
|
19
|
+
export const loadTypeDoc = (projectPath = process.cwd()) => importPeer('typedoc', { task: 'docs', installHint, projectPath })
|
|
20
|
+
|
|
21
|
+
const isSource = fileName => /\.(ts|tsx|mts|cts)$/.test(fileName) && !/\.d\.[cm]?ts$/.test(fileName) && !/\.(test|spec)\./.test(fileName)
|
|
22
|
+
|
|
23
|
+
const listSources = dirPath => fs.readdirSync(dirPath, { withFileTypes: true })
|
|
24
|
+
.filter(entry => entry.isFile() && isSource(entry.name))
|
|
25
|
+
.map(entry => path.join(dirPath, entry.name))
|
|
26
|
+
|
|
27
|
+
// Every folder below dirPath (at any depth), named by its path from dirPath, with the source files directly in it
|
|
28
|
+
const listFolders = (dirPath, prefix = '') => fs.readdirSync(dirPath, { withFileTypes: true })
|
|
29
|
+
.filter(entry => entry.isDirectory() && entry.name !== 'node_modules')
|
|
30
|
+
.flatMap(entry => {
|
|
31
|
+
const folderPath = path.join(dirPath, entry.name)
|
|
32
|
+
const name = `${prefix}${entry.name}`
|
|
33
|
+
return [{ name, path: folderPath, files: listSources(folderPath) }, ...listFolders(folderPath, `${name}/`)]
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const toIdentifier = fileName => fileName.replace(/[^\w$]+(\w)?/g, (match, letter) => (letter || '').toUpperCase()).replace(/^(\d)/, '_$1')
|
|
37
|
+
|
|
38
|
+
const toModulePath = filePath => filePath.replace(/\.[cm]?tsx?$/, '').split(path.sep).join('/')
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Describe what a source file exports: whether it has a default export, and whether it has any named exports.
|
|
42
|
+
* @memberOf module:partials
|
|
43
|
+
* @param {Object} ts - The TypeScript compiler API.
|
|
44
|
+
* @param {string} filePath
|
|
45
|
+
* @returns {{hasDefault: boolean, hasNamed: boolean}}
|
|
46
|
+
*/
|
|
47
|
+
export const describeExports = (ts, filePath) => {
|
|
48
|
+
const source = ts.createSourceFile(filePath, fs.readFileSync(filePath, 'utf8'), ts.ScriptTarget.Latest)
|
|
49
|
+
let hasDefault = false
|
|
50
|
+
let hasNamed = false
|
|
51
|
+
source.statements.forEach(statement => {
|
|
52
|
+
if (ts.isExportAssignment(statement)) {
|
|
53
|
+
hasDefault = hasDefault || !statement.isExportEquals
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
if (ts.isExportDeclaration(statement)) {
|
|
57
|
+
const names = statement.exportClause && ts.isNamedExports(statement.exportClause) ? statement.exportClause.elements : null
|
|
58
|
+
if (names && names.some(element => element.name.text === 'default')) {
|
|
59
|
+
hasDefault = true
|
|
60
|
+
}
|
|
61
|
+
if (!names || names.some(element => element.name.text !== 'default')) {
|
|
62
|
+
hasNamed = true
|
|
63
|
+
}
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
const modifiers = ts.canHaveModifiers(statement) ? ts.getModifiers(statement) || [] : []
|
|
67
|
+
if (modifiers.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
68
|
+
if (modifiers.some(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword)) {
|
|
69
|
+
hasDefault = true
|
|
70
|
+
} else {
|
|
71
|
+
hasNamed = true
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
return { hasDefault, hasNamed }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The description of a folder for its module: the first doc comment of the folder's index file, without its tags
|
|
80
|
+
* (@module, @file, ...), or an empty string when there is none.
|
|
81
|
+
* @memberOf module:partials
|
|
82
|
+
* @param {string} folderPath
|
|
83
|
+
* @returns {string}
|
|
84
|
+
*/
|
|
85
|
+
export const describeFolder = folderPath => {
|
|
86
|
+
const indexFile = ['index.ts', 'index.tsx', 'index.mts', 'index.cts'].map(name => path.join(folderPath, name)).find(file => fs.existsSync(file))
|
|
87
|
+
return indexFile ? describeFile(indexFile) : ''
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The description of a source file for its module: its first doc comment without the tags (@module, @file, ...), when
|
|
92
|
+
* that comment is a header (it is followed by an import or export, so it does not document a declaration itself), or
|
|
93
|
+
* an empty string.
|
|
94
|
+
* @memberOf module:partials
|
|
95
|
+
* @param {string} filePath
|
|
96
|
+
* @returns {string}
|
|
97
|
+
*/
|
|
98
|
+
export const describeFile = filePath => {
|
|
99
|
+
const comment = fs.readFileSync(filePath, 'utf8').match(/^\s*\/\*\*([\s\S]*?)\*\/\s*(import\b|export\s+(\*|\{[^}]*\}\s+from)|$)/)
|
|
100
|
+
if (!comment) {
|
|
101
|
+
return ''
|
|
102
|
+
}
|
|
103
|
+
const lines = comment[1].split('\n').map(line => line.replace(/^\s*\* ?/, '').trimEnd())
|
|
104
|
+
const tagAt = lines.findIndex(line => /^@\w+/.test(line))
|
|
105
|
+
return (tagAt === -1 ? lines : lines.slice(0, tagAt)).join('\n').trim()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Write one entry file per folder of the source directory into the entry directory. TypeDoc makes a module of every
|
|
110
|
+
* entry file, so the modules of the documentation mirror the folders. Nested folders are modules of their own, named
|
|
111
|
+
* by their path (utilities/parsers), and a folder's module holds only the files directly in it. The default export of each source file is
|
|
112
|
+
* re-exported under the name of the file (the source files hold one function each), and anything else a file exports
|
|
113
|
+
* is re-exported as it is (that is where the types.ts files go). Index files (the barrels), tests and declaration
|
|
114
|
+
* files are left out, but the first doc comment of a folder's index file becomes the description of its module.
|
|
115
|
+
* A file which sits beside the folders (not an index, main or browser file) is a module of its own, described by its header
|
|
116
|
+
* comment. A source directory with no folders gets a single entry called index.
|
|
117
|
+
* @memberOf module:partials
|
|
118
|
+
* @param {Object} ts - The TypeScript compiler API.
|
|
119
|
+
* @param {string} srcDir - The directory holding the TypeScript source.
|
|
120
|
+
* @param {string} entryDir - The directory to write the entry files into.
|
|
121
|
+
* @returns {Array<string>} The paths of the entry files.
|
|
122
|
+
*/
|
|
123
|
+
export const writeDocEntries = (ts, srcDir, entryDir) => {
|
|
124
|
+
const absoluteSrc = path.resolve(srcDir)
|
|
125
|
+
fs.mkdirSync(entryDir, { recursive: true })
|
|
126
|
+
const folders = listFolders(absoluteSrc).filter(folder => folder.files.some(file => !/^index\./.test(path.basename(file))))
|
|
127
|
+
const rootFiles = listSources(absoluteSrc).filter(file => !/^(index|main|browser)\./.test(path.basename(file)))
|
|
128
|
+
// Folders make modules, and so do the files which sit beside them (except the barrels: index, main and browser, the
|
|
129
|
+
// browser bundle's entry). A source directory with no folders gets a single module called index which holds its files.
|
|
130
|
+
const groups = folders.length
|
|
131
|
+
? [
|
|
132
|
+
...folders,
|
|
133
|
+
...rootFiles
|
|
134
|
+
.map(file => ({ name: toIdentifier(path.basename(file).replace(/\.[cm]?tsx?$/, '')), files: [file], description: describeFile(file) }))
|
|
135
|
+
.filter(group => !folders.some(folder => folder.name === group.name))
|
|
136
|
+
]
|
|
137
|
+
: [{ name: 'index', path: absoluteSrc, files: rootFiles }]
|
|
138
|
+
return groups.map(group => {
|
|
139
|
+
const lines = group.files
|
|
140
|
+
.filter(file => !/^index\./.test(path.basename(file)))
|
|
141
|
+
.sort()
|
|
142
|
+
.flatMap(file => {
|
|
143
|
+
const modulePath = toModulePath(file)
|
|
144
|
+
const { hasDefault, hasNamed } = describeExports(ts, file)
|
|
145
|
+
const fileName = path.basename(file).replace(/\.[cm]?tsx?$/, '')
|
|
146
|
+
return [
|
|
147
|
+
...(hasDefault ? [`export { default as ${toIdentifier(fileName)} } from '${modulePath}'`] : []),
|
|
148
|
+
...(hasNamed ? [`export * from '${modulePath}'`] : [])
|
|
149
|
+
]
|
|
150
|
+
})
|
|
151
|
+
const description = group.description ?? (group.path ? describeFolder(group.path) : '')
|
|
152
|
+
// TypeDoc takes the first comment of an entry file which has the @module tag as the description of the module
|
|
153
|
+
// A nested folder's module is named by its path (utilities/parsers), so it is not mistaken for its parent's
|
|
154
|
+
const moduleName = group.name.includes('/') ? ` ${group.name}` : ''
|
|
155
|
+
const moduleComment = description || moduleName ? `/**\n${description ? description.split('\n').map(line => ` * ${line}`.trimEnd()).join('\n') + '\n' : ''} * @module${moduleName}\n */\n\n` : ''
|
|
156
|
+
const entryPath = path.join(entryDir, `${group.name}.ts`)
|
|
157
|
+
fs.mkdirSync(path.dirname(entryPath), { recursive: true })
|
|
158
|
+
fs.writeFileSync(entryPath, moduleComment + lines.join('\n') + '\n')
|
|
159
|
+
return entryPath
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const projectName = () => {
|
|
164
|
+
try {
|
|
165
|
+
return JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')).name
|
|
166
|
+
} catch (error) {
|
|
167
|
+
return path.basename(process.cwd())
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Generate the HTML documentation of a TypeScript project with TypeDoc, straight from the TypeScript source, so the
|
|
173
|
+
* types (and the comments on them) are documented from where they are defined. The modules mirror the folders of the
|
|
174
|
+
* source, see {@link writeDocEntries}. Configure this with 'docs.from', 'docs.to', 'docs.index', 'docs.title',
|
|
175
|
+
* 'docs.tsconfig' and 'docs.logLevel'.
|
|
176
|
+
* @memberOf module:partials
|
|
177
|
+
* @param {string} [srcPath=gulpConfig.get('docs.from')] - The directory holding the TypeScript source.
|
|
178
|
+
* @param {string} [outPath=gulpConfig.get('docs.to')] - The directory the documentation is generated into (cleared first).
|
|
179
|
+
* @param {function(string): Promise<Object>} [loadDocs] - Resolves the TypeDoc module, tests can inject a stand-in.
|
|
180
|
+
* @returns {Function} A gulp task, it accepts an optional callback and returns a promise.
|
|
181
|
+
*/
|
|
182
|
+
export const typeDocsFor = (
|
|
183
|
+
srcPath = gulpConfig.get('docs.from'),
|
|
184
|
+
outPath = gulpConfig.get('docs.to'),
|
|
185
|
+
loadDocs = loadTypeDoc
|
|
186
|
+
) => {
|
|
187
|
+
if (gulpConfig.get('docs.enabled') === false) {
|
|
188
|
+
return () => {}
|
|
189
|
+
}
|
|
190
|
+
return (done) => {
|
|
191
|
+
const generate = async () => {
|
|
192
|
+
const ts = loadTypescript()
|
|
193
|
+
const { Application } = await loadDocs()
|
|
194
|
+
const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'typedocs-'))
|
|
195
|
+
try {
|
|
196
|
+
const entryPoints = writeDocEntries(ts, srcPath, path.join(workDir, 'entries'))
|
|
197
|
+
// The entry files live outside the project, so the tsconfig which is used for the documentation extends the
|
|
198
|
+
// one of the project and adds the entries to the files it includes.
|
|
199
|
+
const projectConfig = gulpConfig.get('docs.tsconfig') || gulpConfig.get('typescript.config')
|
|
200
|
+
const tsconfig = path.join(workDir, 'tsconfig.json')
|
|
201
|
+
fs.writeFileSync(tsconfig, JSON.stringify({
|
|
202
|
+
...(projectConfig ? { extends: path.resolve(projectConfig) } : {}),
|
|
203
|
+
include: [path.join(workDir, 'entries', '**', '*.ts').split(path.sep).join('/'), path.resolve(srcPath).split(path.sep).join('/') + '/**/*.ts'],
|
|
204
|
+
exclude: [path.resolve(srcPath).split(path.sep).join('/') + '/**/*.test.*']
|
|
205
|
+
}))
|
|
206
|
+
const indexFile = gulpConfig.get('docs.index')
|
|
207
|
+
const gitRevision = gulpConfig.get('docs.gitRevision')
|
|
208
|
+
await removeDirectory(outPath)
|
|
209
|
+
const app = await Application.bootstrap({
|
|
210
|
+
entryPoints,
|
|
211
|
+
tsconfig,
|
|
212
|
+
name: gulpConfig.get('docs.title') || projectName(),
|
|
213
|
+
readme: indexFile && fs.existsSync(indexFile) ? indexFile : 'none',
|
|
214
|
+
logLevel: gulpConfig.get('docs.logLevel') || 'Warn',
|
|
215
|
+
skipErrorChecking: true,
|
|
216
|
+
// Left out, TypeDoc links each declaration to its source at the current commit, so every commit changes
|
|
217
|
+
// every page; a branch name keeps the pages the same until the documentation itself changes.
|
|
218
|
+
...(gitRevision ? { gitRevision } : {})
|
|
219
|
+
})
|
|
220
|
+
const project = await app.convert()
|
|
221
|
+
if (!project) {
|
|
222
|
+
throw new Error('TypeDoc could not read the TypeScript source, see the errors above.')
|
|
223
|
+
}
|
|
224
|
+
await app.generateDocs(project, outPath)
|
|
225
|
+
} finally {
|
|
226
|
+
await removeDirectory(workDir)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return typeof done === 'function' ? generate().then(() => done(), done) : generate()
|
|
230
|
+
}
|
|
231
|
+
}
|