js-build-tools 5.3.0 → 5.3.1

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.
@@ -1,16 +1,11 @@
1
1
  import fs from 'node:fs'
2
2
  import os from 'node:os'
3
3
  import path from 'node:path'
4
- import { createRequire } from 'node:module'
5
- import { pathToFileURL } from 'node:url'
6
4
  import * as gulpConfig from '../../gulp.config.mjs'
5
+ import { importPeer } from './loadPeer.mjs'
7
6
  import { loadTypescript } from './tsFor.mjs'
8
7
  import { removeDirectory } from './removeDirectory.mjs'
9
8
 
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
9
  const installHint = 'Install it in your project with: npm install --save-dev typedoc'
15
10
 
16
11
  /**
@@ -21,18 +16,7 @@ const installHint = 'Install it in your project with: npm install --save-dev typ
21
16
  * @returns {Promise<Object>} The TypeDoc module.
22
17
  * @throws {Error} With install instructions when typedoc is not installed.
23
18
  */
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
- }
19
+ export const loadTypeDoc = (projectPath = process.cwd()) => importPeer('typedoc', { task: 'docs', installHint, projectPath })
36
20
 
37
21
  const isSource = fileName => /\.(ts|tsx|mts|cts)$/.test(fileName) && !/\.d\.[cm]?ts$/.test(fileName) && !/\.(test|spec)\./.test(fileName)
38
22
 
@@ -86,12 +70,30 @@ export const describeExports = (ts, filePath) => {
86
70
  return { hasDefault, hasNamed }
87
71
  }
88
72
 
73
+ /**
74
+ * The description of a folder for its module: the first doc comment of the folder's index file, without its tags
75
+ * (@module, @file, ...), or an empty string when there is none.
76
+ * @memberOf module:partials
77
+ * @param {string} folderPath
78
+ * @returns {string}
79
+ */
80
+ export const describeFolder = folderPath => {
81
+ const indexFile = ['index.ts', 'index.tsx', 'index.mts', 'index.cts'].map(name => path.join(folderPath, name)).find(file => fs.existsSync(file))
82
+ const comment = indexFile && fs.readFileSync(indexFile, 'utf8').match(/^\s*\/\*\*([\s\S]*?)\*\//)
83
+ if (!comment) {
84
+ return ''
85
+ }
86
+ const lines = comment[1].split('\n').map(line => line.replace(/^\s*\* ?/, '').trimEnd())
87
+ const tagAt = lines.findIndex(line => /^@\w+/.test(line))
88
+ return (tagAt === -1 ? lines : lines.slice(0, tagAt)).join('\n').trim()
89
+ }
90
+
89
91
  /**
90
92
  * Write one entry file per folder of the source directory into the entry directory. TypeDoc makes a module of every
91
93
  * entry file, so the modules of the documentation mirror the folders. The default export of each source file is
92
94
  * re-exported under the name of the file (the source files hold one function each), and anything else a file exports
93
95
  * 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.
96
+ * files are left out, but the first doc comment of a folder's index file becomes the description of its module. A source directory with no folders gets a single entry called index.
95
97
  * @memberOf module:partials
96
98
  * @param {Object} ts - The TypeScript compiler API.
97
99
  * @param {string} srcDir - The directory holding the TypeScript source.
@@ -103,11 +105,11 @@ export const writeDocEntries = (ts, srcDir, entryDir) => {
103
105
  fs.mkdirSync(entryDir, { recursive: true })
104
106
  const folders = fs.readdirSync(absoluteSrc, { withFileTypes: true })
105
107
  .filter(entry => entry.isDirectory() && entry.name !== 'node_modules')
106
- .map(entry => ({ name: entry.name, files: listSources(path.join(absoluteSrc, entry.name)) }))
108
+ .map(entry => ({ name: entry.name, path: path.join(absoluteSrc, entry.name), files: listSources(path.join(absoluteSrc, entry.name)) }))
107
109
  .filter(folder => folder.files.length)
108
110
  const groups = folders.length
109
111
  ? folders
110
- : [{ name: 'index', files: listSources(absoluteSrc).filter(file => !/^(index|main)\./.test(path.basename(file))) }]
112
+ : [{ name: 'index', path: absoluteSrc, files: listSources(absoluteSrc).filter(file => !/^(index|main)\./.test(path.basename(file))) }]
111
113
  return groups.map(group => {
112
114
  const lines = group.files
113
115
  .filter(file => !/^index\./.test(path.basename(file)))
@@ -121,8 +123,11 @@ export const writeDocEntries = (ts, srcDir, entryDir) => {
121
123
  ...(hasNamed ? [`export * from '${modulePath}'`] : [])
122
124
  ]
123
125
  })
126
+ const description = group.path ? describeFolder(group.path) : ''
127
+ // TypeDoc takes the first comment of an entry file which has the @module tag as the description of the module
128
+ const moduleComment = description ? `/**\n${description.split('\n').map(line => ` * ${line}`.trimEnd()).join('\n')}\n * @module\n */\n\n` : ''
124
129
  const entryPath = path.join(entryDir, `${group.name}.ts`)
125
- fs.writeFileSync(entryPath, lines.join('\n') + '\n')
130
+ fs.writeFileSync(entryPath, moduleComment + lines.join('\n') + '\n')
126
131
  return entryPath
127
132
  })
128
133
  }
@@ -2,7 +2,7 @@ import fs from 'fs'
2
2
  import path from 'node:path'
3
3
  import ts from 'typescript'
4
4
  import * as setUp from '../test-helpers/setUp.mjs'
5
- import { describeExports, loadTypeDoc, typeDocsFor, writeDocEntries } from './typeDocsFor.mjs'
5
+ import { describeExports, describeFolder, loadTypeDoc, typeDocsFor, writeDocEntries } from './typeDocsFor.mjs'
6
6
 
7
7
  setUp.setDefaults('test-type-docs-for')
8
8
  const gulpConfig = setUp.gulpConfig
@@ -19,7 +19,7 @@ beforeEach(
19
19
  write(`${srcPath}/point/add.ts`, '/** Add two numbers. */\nconst add = (a: number, b: number): number => a + b\n\nexport default add\n')
20
20
  write(`${srcPath}/point/get-name.ts`, 'export default function getName (): string { return "name" }\n')
21
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`, "import add from './add'\nexport default { add }\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
23
  write(`${srcPath}/point/add.test.ts`, "test('adds', () => {})\n")
24
24
  write(`${srcPath}/point/add.d.ts`, 'export {}\n')
25
25
  write(`${srcPath}/line/length.ts`, 'export const length = 5\nexport default length\n')
@@ -54,6 +54,18 @@ describe('describeExports', () => {
54
54
  })
55
55
  })
56
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
+
57
69
  describe('writeDocEntries', () => {
58
70
  test('writes one entry file for each folder which has source', () => {
59
71
  const entryDir = `${gulpConfig.get('srcPath')}/../entries`
@@ -65,12 +77,13 @@ describe('writeDocEntries', () => {
65
77
  const entryDir = `${gulpConfig.get('srcPath')}/../entries`
66
78
  writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
67
79
  const absoluteSrc = path.resolve(gulpConfig.get('srcPath')).split(path.sep).join('/')
68
- expect(fs.readFileSync(`${entryDir}/point.ts`, 'utf8').trim().split('\n')).toEqual([
80
+ const exportLines = file => fs.readFileSync(file, 'utf8').trim().split('\n').filter(line => line.startsWith('export'))
81
+ expect(exportLines(`${entryDir}/point.ts`)).toEqual([
69
82
  `export { default as add } from '${absoluteSrc}/point/add'`,
70
83
  `export { default as getName } from '${absoluteSrc}/point/get-name'`,
71
84
  `export * from '${absoluteSrc}/point/types'`
72
85
  ])
73
- expect(fs.readFileSync(`${entryDir}/line.ts`, 'utf8').trim().split('\n')).toEqual([
86
+ expect(exportLines(`${entryDir}/line.ts`)).toEqual([
74
87
  `export { default as length } from '${absoluteSrc}/line/length'`,
75
88
  `export * from '${absoluteSrc}/line/length'`
76
89
  ])
@@ -80,7 +93,15 @@ describe('writeDocEntries', () => {
80
93
  const entryDir = `${gulpConfig.get('srcPath')}/../entries`
81
94
  writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
82
95
  const entry = fs.readFileSync(`${entryDir}/point.ts`, 'utf8')
83
- expect(entry).not.toMatch(/index|\.test|add\.d/)
96
+ expect(entry).not.toMatch(/from '.*(index|\.test|add\.d)/)
97
+ })
98
+
99
+ test('describes the module with the first comment of the index file of the folder', () => {
100
+ const entryDir = `${gulpConfig.get('srcPath')}/../entries`
101
+ writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
102
+ 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/)
103
+ // A folder without an index file has no comment
104
+ expect(fs.readFileSync(`${entryDir}/line.ts`, 'utf8').startsWith('export')).toBe(true)
84
105
  })
85
106
 
86
107
  test('makes one entry called index when the source has no folders', () => {
@@ -15,6 +15,7 @@ export { distFor } from './partials/distFor.mjs'
15
15
  export { distForSrc } from './partials/distForSrc.mjs'
16
16
  export { distSeries } from './partials/distSeries.mjs'
17
17
  export { imagesFor } from './partials/imagesFor.mjs'
18
+ export { importModule, importPeer, requirePeer } from './partials/loadPeer.mjs'
18
19
  export { minifyFor } from './partials/minifyFor.mjs'
19
20
  export { readmeTemplate } from './partials/readmeTemplate.mjs'
20
21
  export { removeDirectory } from './partials/removeDirectory.mjs'
@@ -2,6 +2,6 @@ import * as partials from './partials.mjs'
2
2
 
3
3
  describe('partials', () => {
4
4
  test('is object with all partial exports', () => {
5
- expect(Object.keys(partials).length).toBe(16)
5
+ expect(Object.keys(partials).length).toBe(19)
6
6
  })
7
7
  })
package/gulp.config.mjs CHANGED
@@ -49,8 +49,8 @@ import { fileExists } from 'test-filesystem'
49
49
  * Configurations for building the browser files.
50
50
  * @typedef {Object<string, Setting>} module:gulpConfig~BrowserConfig
51
51
  * @property {BooleanSetting} enabled - 'true' to generate browser bundled files; 'false' for node environment only
52
- * @property {StringSetting} from - The name to use for the browser-bundled output file (.js will be appended).
53
- * @property {StringSetting} name - The search pattern used for retrieving compiled distribution files.
52
+ * @property {StringSetting} from - The search pattern used for retrieving compiled distribution files.
53
+ * @property {StringSetting} name - The name to use for the browser-bundled output file (.js will be appended).
54
54
  * @property {StringSetting} to - The output directory for browser-bundled files.
55
55
  * @property {ArrayableSetting} ignore - Module names or file paths which resolve to an empty object ({}) in the
56
56
  * bundle instead of their real contents. For a dependency which is only reached because it is the default value of
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-build-tools",
3
- "version": "5.3.0",
3
+ "version": "5.3.1",
4
4
  "description": "Standardised build process with gulp.",
5
5
  "main": "gulpfile.base.js",
6
6
  "exports": {