js-build-tools 5.2.0 → 5.3.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 (60) hide show
  1. package/README.md +75 -10
  2. package/dist/build.js +7 -2
  3. package/dist/build.min.js +1 -1
  4. package/dist/bundle.js +27 -27
  5. package/dist/bundleLint.js +16 -16
  6. package/dist/bundleMinify.js +13 -13
  7. package/dist/compileReadme.js +10 -10
  8. package/dist/copyFonts.js +13 -13
  9. package/dist/default.js +15 -15
  10. package/dist/dist.js +10 -10
  11. package/dist/distLint.js +16 -16
  12. package/dist/distMinify.js +13 -13
  13. package/dist/images.js +10 -10
  14. package/dist/partials/addToReadme.js +17 -17
  15. package/dist/partials/beginWatcher.js +12 -12
  16. package/dist/partials/clean.js +14 -14
  17. package/dist/partials/copyFor.js +12 -12
  18. package/dist/partials/distFor.js +21 -21
  19. package/dist/partials/distForSrc.js +11 -11
  20. package/dist/partials/distSeries.js +19 -19
  21. package/dist/partials/imagesFor.js +48 -48
  22. package/dist/partials/minifyFor.js +16 -16
  23. package/dist/partials/readmeTemplate.js +14 -14
  24. package/dist/partials/removeDirectory.js +29 -29
  25. package/dist/partials/runOnChange.js +28 -28
  26. package/dist/partials/sassFor.js +28 -28
  27. package/dist/partials/standardLint.js +75 -75
  28. package/dist/partials/tsFor.js +82 -82
  29. package/dist/partials/typeDocsFor.js +214 -0
  30. package/dist/partials.js +7 -0
  31. package/dist/partials.min.js +1 -1
  32. package/dist/sass.js +10 -10
  33. package/dist/test-helpers/setUp.js +89 -89
  34. package/dist/test-helpers/standardLintWithRealStandard.js +27 -27
  35. package/dist/testFull.js +20 -20
  36. package/dist/testHelpers.js +10 -10
  37. package/dist/testQuick.js +22 -22
  38. package/dist/typeDocs.js +14 -0
  39. package/dist/typeDocs.min.js +1 -0
  40. package/dist/typeScript.js +9 -9
  41. package/dist/utilities/dotGet.js +46 -46
  42. package/dist/utilities/dotNotate.js +46 -46
  43. package/dist/utilities/dotSet.js +37 -37
  44. package/dist/utilities/isObject.js +8 -8
  45. package/dist/utilities/strAfter.js +11 -11
  46. package/dist/utilities/strBefore.js +11 -11
  47. package/dist/watchFull.js +10 -10
  48. package/dist/watchTest.js +16 -16
  49. package/functions/build.mjs +7 -2
  50. package/functions/build.test.mjs +29 -0
  51. package/functions/partials/typeDocsFor.mjs +194 -0
  52. package/functions/partials/typeDocsFor.test.mjs +183 -0
  53. package/functions/partials.mjs +1 -0
  54. package/functions/partials.test.mjs +1 -1
  55. package/functions/typeDocs.mjs +9 -0
  56. package/functions/typeDocs.test.mjs +11 -0
  57. package/gulp.config.mjs +22 -0
  58. package/gulpfile.base.js +2 -0
  59. package/gulpfile.base.mjs +1 -0
  60. package/package.json +12 -7
@@ -1,12 +1,12 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.dotSet = void 0;
7
- var _isObject = require("./isObject.js");
8
- var _strAfter = require("./strAfter.js");
9
- var _strBefore = require("./strBefore.js");
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.dotSet = void 0;
7
+ var _isObject = require("./isObject.js");
8
+ var _strAfter = require("./strAfter.js");
9
+ var _strBefore = require("./strBefore.js");
10
10
  /**
11
11
  * Set a nested property value an object.
12
12
  * Original source concepts from {@link https://github.com/jheagle/si-funciona/blob/main/src/helpers/objects/dotSet.ts Sí, funciona}
@@ -14,32 +14,32 @@ var _strBefore = require("./strBefore.js");
14
14
  * @param {string} dotNotation - The path for the property
15
15
  * @param {*} value - The default value to return if the property is not found
16
16
  * @returns {Object} The modified object
17
- */
18
- const dotSet = (arrayObject, dotNotation, value = null) => {
19
- let key = (0, _strBefore.strBefore)(dotNotation, '.');
20
- const lastKey = !key;
21
- if (lastKey) {
22
- key = dotNotation;
23
- }
24
- if (key === '*') {
25
- for (let wildKey in arrayObject) {
26
- if (lastKey) {
27
- arrayObject[wildKey] = value;
28
- continue;
29
- }
30
- if (!(0, _isObject.isObject)(arrayObject[wildKey])) {
31
- continue;
32
- }
33
- dotSet(arrayObject[wildKey], (0, _strAfter.strAfter)(dotNotation, '.'), value);
34
- }
35
- return arrayObject;
36
- }
37
- if (lastKey) {
38
- arrayObject[dotNotation] = value;
39
- return arrayObject;
40
- }
41
- const next = arrayObject[key] ?? [];
42
- arrayObject[key] = dotSet(next, (0, _strAfter.strAfter)(dotNotation, '.'), value);
43
- return arrayObject;
44
- };
17
+ */
18
+ const dotSet = (arrayObject, dotNotation, value = null) => {
19
+ let key = (0, _strBefore.strBefore)(dotNotation, '.');
20
+ const lastKey = !key;
21
+ if (lastKey) {
22
+ key = dotNotation;
23
+ }
24
+ if (key === '*') {
25
+ for (let wildKey in arrayObject) {
26
+ if (lastKey) {
27
+ arrayObject[wildKey] = value;
28
+ continue;
29
+ }
30
+ if (!(0, _isObject.isObject)(arrayObject[wildKey])) {
31
+ continue;
32
+ }
33
+ dotSet(arrayObject[wildKey], (0, _strAfter.strAfter)(dotNotation, '.'), value);
34
+ }
35
+ return arrayObject;
36
+ }
37
+ if (lastKey) {
38
+ arrayObject[dotNotation] = value;
39
+ return arrayObject;
40
+ }
41
+ const next = arrayObject[key] ?? [];
42
+ arrayObject[key] = dotSet(next, (0, _strAfter.strAfter)(dotNotation, '.'), value);
43
+ return arrayObject;
44
+ };
45
45
  exports.dotSet = dotSet;
@@ -1,14 +1,14 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.isObject = void 0;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isObject = void 0;
7
7
  /**
8
8
  * Check if the provided thing is an object / array.
9
9
  * Original source concepts from {@link https://github.com/jheagle/si-funciona/blob/main/src/helpers/objects/isObject.ts Sí, funciona}
10
10
  * @param {*} object
11
11
  * @returns {boolean}
12
- */
13
- const isObject = object => typeof object === 'object' && object !== null;
12
+ */
13
+ const isObject = object => typeof object === 'object' && object !== null;
14
14
  exports.isObject = isObject;
@@ -1,18 +1,18 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.strAfter = void 0;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.strAfter = void 0;
7
7
  /**
8
8
  * Retrieve the string part after the search match.
9
9
  * Original source from {@link https://github.com/jheagle/si-funciona/blob/main/src/helpers/strings/strAfter.ts Sí, funciona}
10
10
  * @param {string} str
11
11
  * @param {string} search
12
12
  * @returns {string}
13
- */
14
- const strAfter = (str, search) => {
15
- const index = str.indexOf(search);
16
- return index === -1 ? '' : str.substring(index + search.length);
17
- };
13
+ */
14
+ const strAfter = (str, search) => {
15
+ const index = str.indexOf(search);
16
+ return index === -1 ? '' : str.substring(index + search.length);
17
+ };
18
18
  exports.strAfter = strAfter;
@@ -1,18 +1,18 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.strBefore = void 0;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.strBefore = void 0;
7
7
  /**
8
8
  * Retrieve the string part before the search match.
9
9
  * Original source from {@link https://github.com/jheagle/si-funciona/blob/main/src/helpers/strings/strBefore.ts Sí, funciona}
10
10
  * @param {string} str
11
11
  * @param {string} search
12
12
  * @returns {string}
13
- */
14
- const strBefore = (str, search) => {
15
- const index = str.indexOf(search);
16
- return index === -1 ? '' : str.slice(0, index);
17
- };
13
+ */
14
+ const strBefore = (str, search) => {
15
+ const index = str.indexOf(search);
16
+ return index === -1 ? '' : str.slice(0, index);
17
+ };
18
18
  exports.strBefore = strBefore;
package/dist/watchFull.js CHANGED
@@ -1,14 +1,14 @@
1
- 'use strict'
2
-
3
- Object.defineProperty(exports, '__esModule', {
4
- value: true
5
- })
6
- exports.watchFull = void 0
7
- const _partials = require('./partials.js')
1
+ 'use strict'
2
+
3
+ Object.defineProperty(exports, '__esModule', {
4
+ value: true
5
+ })
6
+ exports.watchFull = void 0
7
+ const _partials = require('./partials.js')
8
8
  /**
9
9
  * Watch for changes and run the distribution for the changed files, then bundle and test the changed files.
10
10
  * @memberOf module:js-build-tools
11
11
  * @returns {FSWatcher}
12
- */
13
- const watchFull = () => (0, _partials.beginWatcher)().on('change', _partials.runOnChange)
14
- exports.watchFull = watchFull
12
+ */
13
+ const watchFull = () => (0, _partials.beginWatcher)().on('change', _partials.runOnChange)
14
+ exports.watchFull = watchFull
package/dist/watchTest.js CHANGED
@@ -1,20 +1,20 @@
1
- 'use strict'
2
-
3
- require('core-js/modules/esnext.weak-map.delete-all.js')
4
- Object.defineProperty(exports, '__esModule', {
5
- value: true
6
- })
7
- exports.watchTest = void 0
8
- const gulpConfig = _interopRequireWildcard(require('../gulp.config.js'))
9
- const _gulp = require('gulp')
10
- const _testQuick = require('./testQuick.js')
11
- function _interopRequireWildcard (e, t) { if (typeof WeakMap === 'function') var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; let o; let i; const f = { __proto__: null, default: e }; if (e === null || typeof e !== 'object' && typeof e !== 'function') return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f) } for (const t in e) t !== 'default' && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f })(e, t) }
1
+ 'use strict'
2
+
3
+ require('core-js/modules/esnext.weak-map.delete-all.js')
4
+ Object.defineProperty(exports, '__esModule', {
5
+ value: true
6
+ })
7
+ exports.watchTest = void 0
8
+ const gulpConfig = _interopRequireWildcard(require('../gulp.config.js'))
9
+ const _gulp = require('gulp')
10
+ const _testQuick = require('./testQuick.js')
11
+ function _interopRequireWildcard (e, t) { if (typeof WeakMap === 'function') var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; let o; let i; const f = { __proto__: null, default: e }; if (e === null || typeof e !== 'object' && typeof e !== 'function') return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f) } for (const t in e) t !== 'default' && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f })(e, t) }
12
12
  /**
13
13
  * Watch for changes and run the tests.
14
14
  * @memberOf module:js-build-tools
15
15
  * @returns {*}
16
- */
17
- const watchTest = () => (0, _gulp.watch)(gulpConfig.get('test.watch'), {
18
- ignoreInitial: false
19
- }, _testQuick.testQuick)
20
- exports.watchTest = watchTest
16
+ */
17
+ const watchTest = () => (0, _gulp.watch)(gulpConfig.get('test.watch'), {
18
+ ignoreInitial: false
19
+ }, _testQuick.testQuick)
20
+ exports.watchTest = watchTest
@@ -3,6 +3,7 @@ import { bundleLint } from './bundleLint.mjs'
3
3
  import { bundleMinify } from './bundleMinify.mjs'
4
4
  import { clean, distSeries } from './partials.mjs'
5
5
  import { compileReadme } from './compileReadme.mjs'
6
+ import { typeDocs } from './typeDocs.mjs'
6
7
  import { distLint } from './distLint.mjs'
7
8
  import { distMinify } from './distMinify.mjs'
8
9
  import { parallel, series } from 'gulp'
@@ -22,7 +23,11 @@ export const build = (done = null) => {
22
23
  const distLintMinify = parallel(distLint, distMinify)
23
24
  const bundleLintMinify = parallel(bundleLint, bundleMinify)
24
25
  const buildActions = [distSeries(), distLintMinify]
25
- if (gulpConfig.get('typescript.enabled')) {
26
+ const generateDocs = gulpConfig.get('docs.enabled')
27
+ if (generateDocs) {
28
+ // The documentation is generated from the TypeScript source itself, the readme is written by hand
29
+ buildActions.push(typeDocs)
30
+ } else if (gulpConfig.get('typescript.enabled')) {
26
31
  // For ts usage, we need to run the readme on the dist directly since that is where the .js files are located
27
32
  buildActions.push(compileReadme)
28
33
  }
@@ -34,7 +39,7 @@ export const build = (done = null) => {
34
39
  const runActions = [
35
40
  series(...buildActions),
36
41
  ]
37
- if (!gulpConfig.get('typescript.enabled')) {
42
+ if (!generateDocs && !gulpConfig.get('typescript.enabled')) {
38
43
  // Since we didn't run this in series after dist because of typescript, we need to run it now. Potentially faster here.
39
44
  runActions.push(compileReadme)
40
45
  }
@@ -3,6 +3,7 @@ import * as setUp from './test-helpers/setUp.mjs'
3
3
  import { build } from './build.mjs'
4
4
  import { clean } from './partials/clean.mjs'
5
5
  import { testFull } from './testFull.mjs'
6
+ import { typeDocs } from './typeDocs.mjs'
6
7
  import { countMatches } from 'test-filesystem'
7
8
 
8
9
  // Run the real Standard (in a subprocess) - Jest's CommonJS transform can't load the ESM-only package itself.
@@ -15,6 +16,8 @@ const genericFunction = () => Promise.resolve(true)
15
16
 
16
17
  jest.mock('./partials/clean.mjs', () => ({ clean: jest.fn(genericFunction) }))
17
18
  jest.mock('./testFull.mjs', () => ({ testFull: jest.fn(genericFunction) }))
19
+ // The documentation is generated by TypeDoc, whose own tests cover that (see typeDocsFor.test.mjs)
20
+ jest.mock('./typeDocs.mjs', () => ({ typeDocs: jest.fn(genericFunction) }))
18
21
  // tsFor's real implementation loads gulp-ts-compile (a genuine ESM-only package) via a dynamic import() hidden
19
22
  // from babel's static analysis - necessary for production correctness (see tsFor.mjs's own comments), but this
20
23
  // project's current CommonJS-transform-based Jest setup can't execute a real dynamic import at all: it doesn't
@@ -625,6 +628,31 @@ describe('build', () => {
625
628
  })
626
629
  }, 30000)
627
630
 
631
+ test('when docs.enabled is set: generates the documentation from the TypeScript source instead of the readme', done => {
632
+ const srcPath = gulpConfig.get('srcPath')
633
+ const rootPath = gulpConfig.get('rootPath')
634
+ const tsConfig = `${rootPath}/tsconfig.json`
635
+ const readmePath = gulpConfig.get('readme.to')
636
+ const readmeFile = gulpConfig.get('readme.file')
637
+ fs.writeFileSync(`${srcPath}/main.ts`, mainTypescript)
638
+ fs.writeFileSync(tsConfig, tsConfigContents)
639
+ fs.rmSync(`${readmePath}/${readmeFile}`, { force: true })
640
+ typeDocs.mockClear()
641
+ gulpConfig.set('browser.enabled', false)
642
+ gulpConfig.set('typescript.config', tsConfig)
643
+ gulpConfig.set('typescript.enabled', true)
644
+ gulpConfig.set('docs.enabled', true)
645
+ expect.assertions(3)
646
+ build(() => {
647
+ gulpConfig.set('docs.enabled', false)
648
+ expect(typeDocs).toHaveBeenCalledTimes(1)
649
+ // The readme is not generated, it is written by hand
650
+ expect(fs.existsSync(`${readmePath}/${readmeFile}`)).toBe(false)
651
+ expect(testFull).toHaveBeenCalled()
652
+ done()
653
+ })
654
+ }, 30000)
655
+
628
656
  test('finishes clean before starting the tasks that write into the output folders', done => {
629
657
  const srcPath = gulpConfig.get('srcPath')
630
658
  const sourcesPath = `${srcPath}/sources`
@@ -647,6 +675,7 @@ describe('build', () => {
647
675
  gulpConfig.set('images.enabled', false)
648
676
  gulpConfig.set('sass.enabled', false)
649
677
  gulpConfig.set('typescript.enabled', false)
678
+ gulpConfig.set('docs.enabled', false)
650
679
  build(() => {
651
680
  expect(events).toEqual(['clean finished', 'testFull started'])
652
681
  done()
@@ -0,0 +1,194 @@
1
+ import fs from 'node:fs'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+ import { createRequire } from 'node:module'
5
+ import { pathToFileURL } from 'node:url'
6
+ import * as gulpConfig from '../../gulp.config.mjs'
7
+ import { loadTypescript } from './tsFor.mjs'
8
+ import { removeDirectory } from './removeDirectory.mjs'
9
+
10
+ // TypeDoc ships ESM-only, and this project also compiles itself to a CommonJS build (see tsFor.mjs, which hides its
11
+ // own import of gulp-ts-compile the same way), so the import() is hidden from babel's CommonJS transform.
12
+ const importModule = new Function('specifier', 'return import(specifier)')
13
+
14
+ const installHint = 'Install it in your project with: npm install --save-dev typedoc'
15
+
16
+ /**
17
+ * Find the TypeDoc of the project being built (an optional peer dependency, only needed when the docs task is
18
+ * enabled) and load it, so projects which do not document TypeScript never need it installed.
19
+ * @memberOf module:partials
20
+ * @param {string} [projectPath=process.cwd()] The folder of the project (which has the package.json) being built.
21
+ * @returns {Promise<Object>} The TypeDoc module.
22
+ * @throws {Error} With install instructions when typedoc is not installed.
23
+ */
24
+ export const loadTypeDoc = async (projectPath = process.cwd()) => {
25
+ let typeDocPath
26
+ try {
27
+ typeDocPath = createRequire(path.join(projectPath, 'package.json')).resolve('typedoc')
28
+ } catch (error) {
29
+ if (error.code === 'MODULE_NOT_FOUND') {
30
+ throw new Error(`The docs task needs the optional peer dependency typedoc, which is not installed. ${installHint}`)
31
+ }
32
+ throw error
33
+ }
34
+ return importModule(pathToFileURL(typeDocPath).href)
35
+ }
36
+
37
+ const isSource = fileName => /\.(ts|tsx|mts|cts)$/.test(fileName) && !/\.d\.[cm]?ts$/.test(fileName) && !/\.(test|spec)\./.test(fileName)
38
+
39
+ const listSources = dirPath => fs.readdirSync(dirPath, { withFileTypes: true }).flatMap(entry => {
40
+ const entryPath = path.join(dirPath, entry.name)
41
+ if (entry.isDirectory()) {
42
+ return entry.name === 'node_modules' ? [] : listSources(entryPath)
43
+ }
44
+ return isSource(entry.name) ? [entryPath] : []
45
+ })
46
+
47
+ const toIdentifier = fileName => fileName.replace(/[^\w$]+(\w)?/g, (match, letter) => (letter || '').toUpperCase()).replace(/^(\d)/, '_$1')
48
+
49
+ const toModulePath = filePath => filePath.replace(/\.[cm]?tsx?$/, '').split(path.sep).join('/')
50
+
51
+ /**
52
+ * Describe what a source file exports: whether it has a default export, and whether it has any named exports.
53
+ * @memberOf module:partials
54
+ * @param {Object} ts - The TypeScript compiler API.
55
+ * @param {string} filePath
56
+ * @returns {{hasDefault: boolean, hasNamed: boolean}}
57
+ */
58
+ export const describeExports = (ts, filePath) => {
59
+ const source = ts.createSourceFile(filePath, fs.readFileSync(filePath, 'utf8'), ts.ScriptTarget.Latest)
60
+ let hasDefault = false
61
+ let hasNamed = false
62
+ source.statements.forEach(statement => {
63
+ if (ts.isExportAssignment(statement)) {
64
+ hasDefault = hasDefault || !statement.isExportEquals
65
+ return
66
+ }
67
+ if (ts.isExportDeclaration(statement)) {
68
+ const names = statement.exportClause && ts.isNamedExports(statement.exportClause) ? statement.exportClause.elements : null
69
+ if (names && names.some(element => element.name.text === 'default')) {
70
+ hasDefault = true
71
+ }
72
+ if (!names || names.some(element => element.name.text !== 'default')) {
73
+ hasNamed = true
74
+ }
75
+ return
76
+ }
77
+ const modifiers = ts.canHaveModifiers(statement) ? ts.getModifiers(statement) || [] : []
78
+ if (modifiers.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
79
+ if (modifiers.some(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword)) {
80
+ hasDefault = true
81
+ } else {
82
+ hasNamed = true
83
+ }
84
+ }
85
+ })
86
+ return { hasDefault, hasNamed }
87
+ }
88
+
89
+ /**
90
+ * Write one entry file per folder of the source directory into the entry directory. TypeDoc makes a module of every
91
+ * entry file, so the modules of the documentation mirror the folders. The default export of each source file is
92
+ * re-exported under the name of the file (the source files hold one function each), and anything else a file exports
93
+ * is re-exported as it is (that is where the types.ts files go). Index files (the barrels), tests and declaration
94
+ * files are left out. A source directory with no folders gets a single entry called index.
95
+ * @memberOf module:partials
96
+ * @param {Object} ts - The TypeScript compiler API.
97
+ * @param {string} srcDir - The directory holding the TypeScript source.
98
+ * @param {string} entryDir - The directory to write the entry files into.
99
+ * @returns {Array<string>} The paths of the entry files.
100
+ */
101
+ export const writeDocEntries = (ts, srcDir, entryDir) => {
102
+ const absoluteSrc = path.resolve(srcDir)
103
+ fs.mkdirSync(entryDir, { recursive: true })
104
+ const folders = fs.readdirSync(absoluteSrc, { withFileTypes: true })
105
+ .filter(entry => entry.isDirectory() && entry.name !== 'node_modules')
106
+ .map(entry => ({ name: entry.name, files: listSources(path.join(absoluteSrc, entry.name)) }))
107
+ .filter(folder => folder.files.length)
108
+ const groups = folders.length
109
+ ? folders
110
+ : [{ name: 'index', files: listSources(absoluteSrc).filter(file => !/^(index|main)\./.test(path.basename(file))) }]
111
+ return groups.map(group => {
112
+ const lines = group.files
113
+ .filter(file => !/^index\./.test(path.basename(file)))
114
+ .sort()
115
+ .flatMap(file => {
116
+ const modulePath = toModulePath(file)
117
+ const { hasDefault, hasNamed } = describeExports(ts, file)
118
+ const fileName = path.basename(file).replace(/\.[cm]?tsx?$/, '')
119
+ return [
120
+ ...(hasDefault ? [`export { default as ${toIdentifier(fileName)} } from '${modulePath}'`] : []),
121
+ ...(hasNamed ? [`export * from '${modulePath}'`] : [])
122
+ ]
123
+ })
124
+ const entryPath = path.join(entryDir, `${group.name}.ts`)
125
+ fs.writeFileSync(entryPath, lines.join('\n') + '\n')
126
+ return entryPath
127
+ })
128
+ }
129
+
130
+ const projectName = () => {
131
+ try {
132
+ return JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')).name
133
+ } catch (error) {
134
+ return path.basename(process.cwd())
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Generate the HTML documentation of a TypeScript project with TypeDoc, straight from the TypeScript source, so the
140
+ * types (and the comments on them) are documented from where they are defined. The modules mirror the folders of the
141
+ * source, see {@link writeDocEntries}. Configure this with 'docs.from', 'docs.to', 'docs.index', 'docs.title',
142
+ * 'docs.tsconfig' and 'docs.logLevel'.
143
+ * @memberOf module:partials
144
+ * @param {string} [srcPath=gulpConfig.get('docs.from')] - The directory holding the TypeScript source.
145
+ * @param {string} [outPath=gulpConfig.get('docs.to')] - The directory the documentation is generated into (cleared first).
146
+ * @param {function(string): Promise<Object>} [loadDocs] - Resolves the TypeDoc module, tests can inject a stand-in.
147
+ * @returns {Function} A gulp task, it accepts an optional callback and returns a promise.
148
+ */
149
+ export const typeDocsFor = (
150
+ srcPath = gulpConfig.get('docs.from'),
151
+ outPath = gulpConfig.get('docs.to'),
152
+ loadDocs = loadTypeDoc
153
+ ) => {
154
+ if (gulpConfig.get('docs.enabled') === false) {
155
+ return () => {}
156
+ }
157
+ return (done) => {
158
+ const generate = async () => {
159
+ const ts = loadTypescript()
160
+ const { Application } = await loadDocs()
161
+ const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'typedocs-'))
162
+ try {
163
+ const entryPoints = writeDocEntries(ts, srcPath, path.join(workDir, 'entries'))
164
+ // The entry files live outside the project, so the tsconfig which is used for the documentation extends the
165
+ // one of the project and adds the entries to the files it includes.
166
+ const projectConfig = gulpConfig.get('docs.tsconfig') || gulpConfig.get('typescript.config')
167
+ const tsconfig = path.join(workDir, 'tsconfig.json')
168
+ fs.writeFileSync(tsconfig, JSON.stringify({
169
+ ...(projectConfig ? { extends: path.resolve(projectConfig) } : {}),
170
+ include: [path.join(workDir, 'entries', '*.ts').split(path.sep).join('/'), path.resolve(srcPath).split(path.sep).join('/') + '/**/*.ts'],
171
+ exclude: [path.resolve(srcPath).split(path.sep).join('/') + '/**/*.test.*']
172
+ }))
173
+ const indexFile = gulpConfig.get('docs.index')
174
+ await removeDirectory(outPath)
175
+ const app = await Application.bootstrap({
176
+ entryPoints,
177
+ tsconfig,
178
+ name: gulpConfig.get('docs.title') || projectName(),
179
+ readme: indexFile && fs.existsSync(indexFile) ? indexFile : 'none',
180
+ logLevel: gulpConfig.get('docs.logLevel') || 'Warn',
181
+ skipErrorChecking: true
182
+ })
183
+ const project = await app.convert()
184
+ if (!project) {
185
+ throw new Error('TypeDoc could not read the TypeScript source, see the errors above.')
186
+ }
187
+ await app.generateDocs(project, outPath)
188
+ } finally {
189
+ await removeDirectory(workDir)
190
+ }
191
+ }
192
+ return typeof done === 'function' ? generate().then(() => done(), done) : generate()
193
+ }
194
+ }