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
@@ -0,0 +1,183 @@
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, 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`, "import 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('writeDocEntries', () => {
58
+ test('writes one entry file for each folder which has source', () => {
59
+ const entryDir = `${gulpConfig.get('srcPath')}/../entries`
60
+ const entries = writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
61
+ expect(entries.map(entry => path.basename(entry)).sort()).toEqual(['line.ts', 'point.ts'])
62
+ })
63
+
64
+ test('re-exports the default of each file by its name and the named exports as they are', () => {
65
+ const entryDir = `${gulpConfig.get('srcPath')}/../entries`
66
+ writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
67
+ const absoluteSrc = path.resolve(gulpConfig.get('srcPath')).split(path.sep).join('/')
68
+ expect(fs.readFileSync(`${entryDir}/point.ts`, 'utf8').trim().split('\n')).toEqual([
69
+ `export { default as add } from '${absoluteSrc}/point/add'`,
70
+ `export { default as getName } from '${absoluteSrc}/point/get-name'`,
71
+ `export * from '${absoluteSrc}/point/types'`
72
+ ])
73
+ expect(fs.readFileSync(`${entryDir}/line.ts`, 'utf8').trim().split('\n')).toEqual([
74
+ `export { default as length } from '${absoluteSrc}/line/length'`,
75
+ `export * from '${absoluteSrc}/line/length'`
76
+ ])
77
+ })
78
+
79
+ test('leaves out index files, tests and declaration files', () => {
80
+ const entryDir = `${gulpConfig.get('srcPath')}/../entries`
81
+ writeDocEntries(ts, gulpConfig.get('srcPath'), entryDir)
82
+ const entry = fs.readFileSync(`${entryDir}/point.ts`, 'utf8')
83
+ expect(entry).not.toMatch(/index|\.test|add\.d/)
84
+ })
85
+
86
+ test('makes one entry called index when the source has no folders', () => {
87
+ const flat = `${gulpConfig.get('srcPath')}/../flat`
88
+ write(`${flat}/one.ts`, 'export default 1\n')
89
+ write(`${flat}/main.ts`, 'export default 2\n')
90
+ const entries = writeDocEntries(ts, flat, `${flat}/../flat-entries`)
91
+ expect(entries.map(entry => path.basename(entry))).toEqual(['index.ts'])
92
+ expect(fs.readFileSync(entries[0], 'utf8')).toMatch(/default as one/)
93
+ expect(fs.readFileSync(entries[0], 'utf8')).not.toMatch(/main/)
94
+ })
95
+ })
96
+
97
+ describe('typeDocsFor', () => {
98
+ const fakeTypeDoc = calls => () => Promise.resolve({
99
+ Application: {
100
+ bootstrap: async options => {
101
+ calls.options = options
102
+ calls.entryContents = options.entryPoints.map(entry => fs.readFileSync(entry, 'utf8'))
103
+ calls.tsconfig = JSON.parse(fs.readFileSync(options.tsconfig, 'utf8'))
104
+ return {
105
+ convert: async () => ({ name: 'project' }),
106
+ generateDocs: async (project, out) => {
107
+ calls.generated = { project, out }
108
+ fs.mkdirSync(out, { recursive: true })
109
+ fs.writeFileSync(`${out}/index.html`, '<html></html>')
110
+ }
111
+ }
112
+ }
113
+ }
114
+ })
115
+
116
+ test('generates the documentation into the output directory, clearing what was there', done => {
117
+ gulpConfig.set('docs.enabled', true)
118
+ const outPath = `${gulpConfig.get('srcPath')}/../docs`
119
+ write(`${outPath}/stale.html`, 'old')
120
+ const calls = {}
121
+ typeDocsFor(gulpConfig.get('srcPath'), outPath, fakeTypeDoc(calls))(() => {
122
+ expect(fs.existsSync(`${outPath}/stale.html`)).toBe(false)
123
+ expect(fs.existsSync(`${outPath}/index.html`)).toBe(true)
124
+ expect(calls.generated.out).toBe(outPath)
125
+ expect(calls.options.entryPoints.map(entry => path.basename(entry)).sort()).toEqual(['line.ts', 'point.ts'])
126
+ expect(calls.options.skipErrorChecking).toBe(true)
127
+ done()
128
+ })
129
+ })
130
+
131
+ test('uses the front page and the title from the config, and none when the front page is missing', async () => {
132
+ gulpConfig.set('docs.enabled', true)
133
+ gulpConfig.set('docs.title', 'my-title')
134
+ gulpConfig.set('docs.index', `${gulpConfig.get('srcPath')}/../MAIN.md`)
135
+ write(`${gulpConfig.get('srcPath')}/../MAIN.md`, '# hello\n')
136
+ const calls = {}
137
+ await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
138
+ expect(calls.options.name).toBe('my-title')
139
+ expect(calls.options.readme).toBe(`${gulpConfig.get('srcPath')}/../MAIN.md`)
140
+ gulpConfig.set('docs.index', 'does-not-exist.md')
141
+ await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
142
+ expect(calls.options.readme).toBe('none')
143
+ })
144
+
145
+ test('reads the source with the tsconfig of the project, adding the entries to it', async () => {
146
+ gulpConfig.set('docs.enabled', true)
147
+ gulpConfig.set('typescript.config', 'tsconfig.json')
148
+ const calls = {}
149
+ await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
150
+ expect(calls.tsconfig.extends).toBe(path.resolve('tsconfig.json'))
151
+ expect(calls.tsconfig.include).toHaveLength(2)
152
+ expect(calls.tsconfig.exclude[0]).toMatch(/\*\*\/\*\.test\.\*$/)
153
+ gulpConfig.set('docs.tsconfig', 'other.json')
154
+ await typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, fakeTypeDoc(calls))()
155
+ expect(calls.tsconfig.extends).toBe(path.resolve('other.json'))
156
+ gulpConfig.set('docs.tsconfig', false)
157
+ gulpConfig.set('typescript.config', false)
158
+ })
159
+
160
+ test('fails with a clear error when TypeDoc could not read the source', async () => {
161
+ gulpConfig.set('docs.enabled', true)
162
+ const failing = () => Promise.resolve({
163
+ Application: { bootstrap: async () => ({ convert: async () => undefined }) }
164
+ })
165
+ await expect(typeDocsFor(gulpConfig.get('srcPath'), `${gulpConfig.get('srcPath')}/../docs`, failing)()).rejects.toThrow(/could not read the TypeScript source/)
166
+ })
167
+
168
+ test('skips if docs.enabled is false', () => {
169
+ gulpConfig.set('docs.enabled', false)
170
+ const result = typeDocsFor()
171
+ expect(result).toBeInstanceOf(Function)
172
+ expect(result()).toBeUndefined()
173
+ })
174
+ })
175
+
176
+ describe('loadTypeDoc', () => {
177
+ // The real import() of the ESM-only TypeDoc cannot run in this project's CommonJS-transform-based Jest setup (see
178
+ // tsFor.mjs), so loading it for real is checked by generating the documentation of a project, see the pull request.
179
+
180
+ test('explains how to install typedoc when the project does not have it', async () => {
181
+ await expect(loadTypeDoc('/')).rejects.toThrow(/optional peer dependency typedoc.*npm install --save-dev typedoc/)
182
+ })
183
+ })
@@ -22,3 +22,4 @@ export { runOnChange } from './partials/runOnChange.mjs'
22
22
  export { sassFor } from './partials/sassFor.mjs'
23
23
  export { standardLint } from './partials/standardLint.mjs'
24
24
  export { tsFor } from './partials/tsFor.mjs'
25
+ export { typeDocsFor } from './partials/typeDocsFor.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(15)
5
+ expect(Object.keys(partials).length).toBe(16)
6
6
  })
7
7
  })
@@ -0,0 +1,9 @@
1
+ import { typeDocsFor } from './partials/typeDocsFor.mjs'
2
+
3
+ /**
4
+ * Generate the HTML documentation of a TypeScript project from its source, with the modules mirroring the source
5
+ * folders. Turn it on with docs.enabled, see {@link module:partials.typeDocsFor}.
6
+ * @memberOf module:js-build-tools
7
+ * @returns {Function}
8
+ */
9
+ export const typeDocs = typeDocsFor()
@@ -0,0 +1,11 @@
1
+ import { typeDocs } from './typeDocs.mjs'
2
+ import { typeDocsFor } from './partials/typeDocsFor.mjs'
3
+
4
+ jest.mock('./partials/typeDocsFor.mjs', () => ({ typeDocsFor: jest.fn(() => () => null) }))
5
+
6
+ describe('typeDocs', () => {
7
+ test('is the task made by typeDocsFor with no parameters', () => {
8
+ expect(typeDocsFor).toHaveBeenCalledWith()
9
+ expect(typeDocs).toBeInstanceOf(Function)
10
+ })
11
+ })
package/gulp.config.mjs CHANGED
@@ -91,6 +91,18 @@ import { fileExists } from 'test-filesystem'
91
91
  * @property {StringSetting} template - The file which will be pre-fixed to your README.md output.
92
92
  * @property {StringSetting} to - The directory to output the readme file in.
93
93
  */
94
+ /**
95
+ * Configurations for generating the HTML documentation of a TypeScript project (with TypeDoc, from the source).
96
+ * @typedef {Object<string, Setting>} module:gulpConfig~DocsConfig
97
+ * @property {BooleanSetting} enabled - Toggle generating the documentation from the TypeScript source. When it is on the
98
+ * build does this instead of generating the readme from the jsdoc comments of the compiled files.
99
+ * @property {StringSetting} from - The directory holding the TypeScript source, each folder in it becomes a module.
100
+ * @property {StringSetting} index - The markdown file used as the front page of the documentation.
101
+ * @property {StringSetting} logLevel - How much TypeDoc reports: Verbose, Info, Warn, Error or None.
102
+ * @property {StringSetting} title - The name of the documentation, defaults to the name in the package.json.
103
+ * @property {StringSetting} to - The directory to generate the documentation in (it is cleared first).
104
+ * @property {FlagStringSetting} tsconfig - The tsconfig file to read the source with, defaults to the one of typescript.config.
105
+ */
94
106
  /**
95
107
  * Configurations to compile and copy the sass files into css.
96
108
  * @typedef {Object<string, Setting>} module:gulpConfig~SassConfig
@@ -121,6 +133,7 @@ import { fileExists } from 'test-filesystem'
121
133
  * @property {BrowserConfig} browser - Browser bundling configuration group.
122
134
  * @property {ArrayableSetting} cleanPaths - The paths for directories to delete before build.
123
135
  * @property {DistConfig} dist - Distribution file generation configuration group.
136
+ * @property {DocsConfig} docs - Generate the documentation of a TypeScript project configuration group.
124
137
  * @property {FontConfig} fonts - Fonts copy configuration group.
125
138
  * @property {ImageConfig} images - Minify and copy the images configuration.
126
139
  * @property {ReadmeConfig} readme - Build readme files configuration.
@@ -150,6 +163,15 @@ const setDefaults = {
150
163
  main: 'dist/main',
151
164
  to: 'dist',
152
165
  },
166
+ docs: {
167
+ enabled: false,
168
+ from: 'src',
169
+ index: 'MAIN.md',
170
+ logLevel: 'Warn',
171
+ title: '',
172
+ to: 'docs',
173
+ tsconfig: false,
174
+ },
153
175
  fonts: {
154
176
  enabled: false,
155
177
  from: 'src/fonts/**/*',
package/gulpfile.base.js CHANGED
@@ -23,6 +23,7 @@ const sass = require('./dist/sass.js')
23
23
  const testFull = require('./dist/testFull.js')
24
24
  const testHelpers = require('./dist/testHelpers.js')
25
25
  const testQuick = require('./dist/testQuick.js')
26
+ const typeDocs = require('./dist/typeDocs.js')
26
27
  const typescript = require('./dist/typeScript')
27
28
  const watchFull = require('./dist/watchFull.js')
28
29
  const watchTest = require('./dist/watchTest.js')
@@ -34,6 +35,7 @@ module.exports = {
34
35
  bundleMinify,
35
36
  copyFonts,
36
37
  defaultCmd,
38
+ docs: typeDocs,
37
39
  dist,
38
40
  distLint,
39
41
  distMinify,
package/gulpfile.base.mjs CHANGED
@@ -22,6 +22,7 @@ export { sass } from './functions/sass.mjs'
22
22
  export { testFull } from './functions/testFull.mjs'
23
23
  export * as testHelpers from './functions/testHelpers.mjs'
24
24
  export { testQuick } from './functions/testQuick.mjs'
25
+ export { typeDocs as docs } from './functions/typeDocs.mjs'
25
26
  export { typeScript as typescript } from './functions/typeScript.mjs'
26
27
  export { watchFull } from './functions/watchFull.mjs'
27
28
  export { watchTest } from './functions/watchTest.mjs'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-build-tools",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "description": "Standardised build process with gulp.",
5
5
  "main": "gulpfile.base.js",
6
6
  "exports": {
@@ -52,9 +52,9 @@
52
52
  "@babel/preset-typescript": "^7.29.7",
53
53
  "@babel/runtime-corejs3": "^8.0.6",
54
54
  "autoprefixer": "^10.6.1",
55
- "babel-jest": "^30.4.1",
55
+ "babel-jest": "^30.5.2",
56
56
  "browserify": "^17.0.1",
57
- "core-js": "^3.49.0",
57
+ "core-js": "^3.50.0",
58
58
  "cssnano": "^6.1.2",
59
59
  "glob": "^13.0.6",
60
60
  "gulp": "^5.0.1",
@@ -63,27 +63,31 @@
63
63
  "gulp-postcss": "^10.0.0",
64
64
  "gulp-rename": "^2.1.0",
65
65
  "gulp-sass": "^6.0.1",
66
- "gulp-ts-compile": "^1.0.0",
66
+ "gulp-ts-compile": "^1.0.1",
67
67
  "gulp-uglify-es": "^3.0.0",
68
68
  "jest": "^30.5.2",
69
- "jest-environment-jsdom": "^30.4.1",
69
+ "jest-environment-jsdom": "^30.5.2",
70
70
  "jsdoc-to-markdown": "^9.1.3",
71
71
  "postcss": "^8.5.28",
72
72
  "regenerator-runtime": "^0.14.1",
73
- "sass": "^1.101.0",
73
+ "sass": "^1.105.0",
74
74
  "standard": "^17.1.2",
75
- "test-filesystem": "^1.5.0",
75
+ "test-filesystem": "^1.5.1",
76
76
  "through2": "^4.0.2",
77
77
  "vinyl-source-stream": "^2.0.0"
78
78
  },
79
79
  "peerDependencies": {
80
80
  "gulp-imagemin": ">=9.0.0",
81
+ "typedoc": ">=0.28.0",
81
82
  "typescript": ">=5.0.0 <7.0.0"
82
83
  },
83
84
  "peerDependenciesMeta": {
84
85
  "gulp-imagemin": {
85
86
  "optional": true
86
87
  },
88
+ "typedoc": {
89
+ "optional": true
90
+ },
87
91
  "typescript": {
88
92
  "optional": true
89
93
  }
@@ -101,6 +105,7 @@
101
105
  "funding": "https://www.paypal.com/donate/?business=LRYR6SZRWKXVY&no_recurring=0&item_name=Let%27s+build+together+today+to+simplify+our+jobs+tomorrow.&currency_code=CAD",
102
106
  "devDependencies": {
103
107
  "gulp-imagemin": "github:jheagle/gulp-imagemin#common-js-compatibility",
108
+ "typedoc": "^0.28.20",
104
109
  "typescript": "^6.0.3"
105
110
  }
106
111
  }