js-build-tools 5.3.0 → 5.3.2

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/dist/partials.js CHANGED
@@ -1,117 +1,136 @@
1
- 'use strict'
2
-
3
- Object.defineProperty(exports, '__esModule', {
4
- value: true
5
- })
6
- Object.defineProperty(exports, 'addToReadme', {
7
- enumerable: true,
8
- get: function () {
9
- return _addToReadme.addToReadme
10
- }
11
- })
12
- Object.defineProperty(exports, 'beginWatcher', {
13
- enumerable: true,
14
- get: function () {
15
- return _beginWatcher.beginWatcher
16
- }
17
- })
18
- Object.defineProperty(exports, 'clean', {
19
- enumerable: true,
20
- get: function () {
21
- return _clean.clean
22
- }
23
- })
24
- Object.defineProperty(exports, 'copyFor', {
25
- enumerable: true,
26
- get: function () {
27
- return _copyFor.copyFor
28
- }
29
- })
30
- Object.defineProperty(exports, 'distFor', {
31
- enumerable: true,
32
- get: function () {
33
- return _distFor.distFor
34
- }
35
- })
36
- Object.defineProperty(exports, 'distForSrc', {
37
- enumerable: true,
38
- get: function () {
39
- return _distForSrc.distForSrc
40
- }
41
- })
42
- Object.defineProperty(exports, 'distSeries', {
43
- enumerable: true,
44
- get: function () {
45
- return _distSeries.distSeries
46
- }
47
- })
48
- Object.defineProperty(exports, 'imagesFor', {
49
- enumerable: true,
50
- get: function () {
51
- return _imagesFor.imagesFor
52
- }
53
- })
54
- Object.defineProperty(exports, 'minifyFor', {
55
- enumerable: true,
56
- get: function () {
57
- return _minifyFor.minifyFor
58
- }
59
- })
60
- Object.defineProperty(exports, 'readmeTemplate', {
61
- enumerable: true,
62
- get: function () {
63
- return _readmeTemplate.readmeTemplate
64
- }
65
- })
66
- Object.defineProperty(exports, 'removeDirectory', {
67
- enumerable: true,
68
- get: function () {
69
- return _removeDirectory.removeDirectory
70
- }
71
- })
72
- Object.defineProperty(exports, 'runOnChange', {
73
- enumerable: true,
74
- get: function () {
75
- return _runOnChange.runOnChange
76
- }
77
- })
78
- Object.defineProperty(exports, 'sassFor', {
79
- enumerable: true,
80
- get: function () {
81
- return _sassFor.sassFor
82
- }
83
- })
84
- Object.defineProperty(exports, 'standardLint', {
85
- enumerable: true,
86
- get: function () {
87
- return _standardLint.standardLint
88
- }
89
- })
90
- Object.defineProperty(exports, 'tsFor', {
91
- enumerable: true,
92
- get: function () {
93
- return _tsFor.tsFor
94
- }
95
- })
96
- Object.defineProperty(exports, 'typeDocsFor', {
97
- enumerable: true,
98
- get: function () {
99
- return _typeDocsFor.typeDocsFor
100
- }
101
- })
102
- var _addToReadme = require('./partials/addToReadme.js')
103
- var _beginWatcher = require('./partials/beginWatcher.js')
104
- var _clean = require('./partials/clean.js')
105
- var _copyFor = require('./partials/copyFor.js')
106
- var _distFor = require('./partials/distFor.js')
107
- var _distForSrc = require('./partials/distForSrc.js')
108
- var _distSeries = require('./partials/distSeries.js')
109
- var _imagesFor = require('./partials/imagesFor.js')
110
- var _minifyFor = require('./partials/minifyFor.js')
111
- var _readmeTemplate = require('./partials/readmeTemplate.js')
112
- var _removeDirectory = require('./partials/removeDirectory.js')
113
- var _runOnChange = require('./partials/runOnChange.js')
114
- var _sassFor = require('./partials/sassFor.js')
115
- var _standardLint = require('./partials/standardLint.js')
116
- var _tsFor = require('./partials/tsFor.js')
117
- var _typeDocsFor = require('./partials/typeDocsFor.js')
1
+ 'use strict'
2
+
3
+ Object.defineProperty(exports, '__esModule', {
4
+ value: true
5
+ })
6
+ Object.defineProperty(exports, 'addToReadme', {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _addToReadme.addToReadme
10
+ }
11
+ })
12
+ Object.defineProperty(exports, 'beginWatcher', {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _beginWatcher.beginWatcher
16
+ }
17
+ })
18
+ Object.defineProperty(exports, 'clean', {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _clean.clean
22
+ }
23
+ })
24
+ Object.defineProperty(exports, 'copyFor', {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _copyFor.copyFor
28
+ }
29
+ })
30
+ Object.defineProperty(exports, 'distFor', {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _distFor.distFor
34
+ }
35
+ })
36
+ Object.defineProperty(exports, 'distForSrc', {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _distForSrc.distForSrc
40
+ }
41
+ })
42
+ Object.defineProperty(exports, 'distSeries', {
43
+ enumerable: true,
44
+ get: function () {
45
+ return _distSeries.distSeries
46
+ }
47
+ })
48
+ Object.defineProperty(exports, 'imagesFor', {
49
+ enumerable: true,
50
+ get: function () {
51
+ return _imagesFor.imagesFor
52
+ }
53
+ })
54
+ Object.defineProperty(exports, 'importModule', {
55
+ enumerable: true,
56
+ get: function () {
57
+ return _loadPeer.importModule
58
+ }
59
+ })
60
+ Object.defineProperty(exports, 'importPeer', {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _loadPeer.importPeer
64
+ }
65
+ })
66
+ Object.defineProperty(exports, 'minifyFor', {
67
+ enumerable: true,
68
+ get: function () {
69
+ return _minifyFor.minifyFor
70
+ }
71
+ })
72
+ Object.defineProperty(exports, 'readmeTemplate', {
73
+ enumerable: true,
74
+ get: function () {
75
+ return _readmeTemplate.readmeTemplate
76
+ }
77
+ })
78
+ Object.defineProperty(exports, 'removeDirectory', {
79
+ enumerable: true,
80
+ get: function () {
81
+ return _removeDirectory.removeDirectory
82
+ }
83
+ })
84
+ Object.defineProperty(exports, 'requirePeer', {
85
+ enumerable: true,
86
+ get: function () {
87
+ return _loadPeer.requirePeer
88
+ }
89
+ })
90
+ Object.defineProperty(exports, 'runOnChange', {
91
+ enumerable: true,
92
+ get: function () {
93
+ return _runOnChange.runOnChange
94
+ }
95
+ })
96
+ Object.defineProperty(exports, 'sassFor', {
97
+ enumerable: true,
98
+ get: function () {
99
+ return _sassFor.sassFor
100
+ }
101
+ })
102
+ Object.defineProperty(exports, 'standardLint', {
103
+ enumerable: true,
104
+ get: function () {
105
+ return _standardLint.standardLint
106
+ }
107
+ })
108
+ Object.defineProperty(exports, 'tsFor', {
109
+ enumerable: true,
110
+ get: function () {
111
+ return _tsFor.tsFor
112
+ }
113
+ })
114
+ Object.defineProperty(exports, 'typeDocsFor', {
115
+ enumerable: true,
116
+ get: function () {
117
+ return _typeDocsFor.typeDocsFor
118
+ }
119
+ })
120
+ var _addToReadme = require('./partials/addToReadme.js')
121
+ var _beginWatcher = require('./partials/beginWatcher.js')
122
+ var _clean = require('./partials/clean.js')
123
+ var _copyFor = require('./partials/copyFor.js')
124
+ var _distFor = require('./partials/distFor.js')
125
+ var _distForSrc = require('./partials/distForSrc.js')
126
+ var _distSeries = require('./partials/distSeries.js')
127
+ var _imagesFor = require('./partials/imagesFor.js')
128
+ var _loadPeer = require('./partials/loadPeer.js')
129
+ var _minifyFor = require('./partials/minifyFor.js')
130
+ var _readmeTemplate = require('./partials/readmeTemplate.js')
131
+ var _removeDirectory = require('./partials/removeDirectory.js')
132
+ var _runOnChange = require('./partials/runOnChange.js')
133
+ var _sassFor = require('./partials/sassFor.js')
134
+ var _standardLint = require('./partials/standardLint.js')
135
+ var _tsFor = require('./partials/tsFor.js')
136
+ var _typeDocsFor = require('./partials/typeDocsFor.js')
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"addToReadme",{enumerable:!0,get:function(){return _addToReadme.addToReadme}}),Object.defineProperty(exports,"beginWatcher",{enumerable:!0,get:function(){return _beginWatcher.beginWatcher}}),Object.defineProperty(exports,"clean",{enumerable:!0,get:function(){return _clean.clean}}),Object.defineProperty(exports,"copyFor",{enumerable:!0,get:function(){return _copyFor.copyFor}}),Object.defineProperty(exports,"distFor",{enumerable:!0,get:function(){return _distFor.distFor}}),Object.defineProperty(exports,"distForSrc",{enumerable:!0,get:function(){return _distForSrc.distForSrc}}),Object.defineProperty(exports,"distSeries",{enumerable:!0,get:function(){return _distSeries.distSeries}}),Object.defineProperty(exports,"imagesFor",{enumerable:!0,get:function(){return _imagesFor.imagesFor}}),Object.defineProperty(exports,"minifyFor",{enumerable:!0,get:function(){return _minifyFor.minifyFor}}),Object.defineProperty(exports,"readmeTemplate",{enumerable:!0,get:function(){return _readmeTemplate.readmeTemplate}}),Object.defineProperty(exports,"removeDirectory",{enumerable:!0,get:function(){return _removeDirectory.removeDirectory}}),Object.defineProperty(exports,"runOnChange",{enumerable:!0,get:function(){return _runOnChange.runOnChange}}),Object.defineProperty(exports,"sassFor",{enumerable:!0,get:function(){return _sassFor.sassFor}}),Object.defineProperty(exports,"standardLint",{enumerable:!0,get:function(){return _standardLint.standardLint}}),Object.defineProperty(exports,"tsFor",{enumerable:!0,get:function(){return _tsFor.tsFor}}),Object.defineProperty(exports,"typeDocsFor",{enumerable:!0,get:function(){return _typeDocsFor.typeDocsFor}});var _addToReadme=require("./partials/addToReadme.js"),_beginWatcher=require("./partials/beginWatcher.js"),_clean=require("./partials/clean.js"),_copyFor=require("./partials/copyFor.js"),_distFor=require("./partials/distFor.js"),_distForSrc=require("./partials/distForSrc.js"),_distSeries=require("./partials/distSeries.js"),_imagesFor=require("./partials/imagesFor.js"),_minifyFor=require("./partials/minifyFor.js"),_readmeTemplate=require("./partials/readmeTemplate.js"),_removeDirectory=require("./partials/removeDirectory.js"),_runOnChange=require("./partials/runOnChange.js"),_sassFor=require("./partials/sassFor.js"),_standardLint=require("./partials/standardLint.js"),_tsFor=require("./partials/tsFor.js"),_typeDocsFor=require("./partials/typeDocsFor.js");
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"addToReadme",{enumerable:!0,get:function(){return _addToReadme.addToReadme}}),Object.defineProperty(exports,"beginWatcher",{enumerable:!0,get:function(){return _beginWatcher.beginWatcher}}),Object.defineProperty(exports,"clean",{enumerable:!0,get:function(){return _clean.clean}}),Object.defineProperty(exports,"copyFor",{enumerable:!0,get:function(){return _copyFor.copyFor}}),Object.defineProperty(exports,"distFor",{enumerable:!0,get:function(){return _distFor.distFor}}),Object.defineProperty(exports,"distForSrc",{enumerable:!0,get:function(){return _distForSrc.distForSrc}}),Object.defineProperty(exports,"distSeries",{enumerable:!0,get:function(){return _distSeries.distSeries}}),Object.defineProperty(exports,"imagesFor",{enumerable:!0,get:function(){return _imagesFor.imagesFor}}),Object.defineProperty(exports,"importModule",{enumerable:!0,get:function(){return _loadPeer.importModule}}),Object.defineProperty(exports,"importPeer",{enumerable:!0,get:function(){return _loadPeer.importPeer}}),Object.defineProperty(exports,"minifyFor",{enumerable:!0,get:function(){return _minifyFor.minifyFor}}),Object.defineProperty(exports,"readmeTemplate",{enumerable:!0,get:function(){return _readmeTemplate.readmeTemplate}}),Object.defineProperty(exports,"removeDirectory",{enumerable:!0,get:function(){return _removeDirectory.removeDirectory}}),Object.defineProperty(exports,"requirePeer",{enumerable:!0,get:function(){return _loadPeer.requirePeer}}),Object.defineProperty(exports,"runOnChange",{enumerable:!0,get:function(){return _runOnChange.runOnChange}}),Object.defineProperty(exports,"sassFor",{enumerable:!0,get:function(){return _sassFor.sassFor}}),Object.defineProperty(exports,"standardLint",{enumerable:!0,get:function(){return _standardLint.standardLint}}),Object.defineProperty(exports,"tsFor",{enumerable:!0,get:function(){return _tsFor.tsFor}}),Object.defineProperty(exports,"typeDocsFor",{enumerable:!0,get:function(){return _typeDocsFor.typeDocsFor}});var _addToReadme=require("./partials/addToReadme.js"),_beginWatcher=require("./partials/beginWatcher.js"),_clean=require("./partials/clean.js"),_copyFor=require("./partials/copyFor.js"),_distFor=require("./partials/distFor.js"),_distForSrc=require("./partials/distForSrc.js"),_distSeries=require("./partials/distSeries.js"),_imagesFor=require("./partials/imagesFor.js"),_loadPeer=require("./partials/loadPeer.js"),_minifyFor=require("./partials/minifyFor.js"),_readmeTemplate=require("./partials/readmeTemplate.js"),_removeDirectory=require("./partials/removeDirectory.js"),_runOnChange=require("./partials/runOnChange.js"),_sassFor=require("./partials/sassFor.js"),_standardLint=require("./partials/standardLint.js"),_tsFor=require("./partials/tsFor.js"),_typeDocsFor=require("./partials/typeDocsFor.js");
package/dist/typeDocs.js CHANGED
@@ -1,14 +1,14 @@
1
- 'use strict'
2
-
3
- Object.defineProperty(exports, '__esModule', {
4
- value: true
5
- })
6
- exports.typeDocs = void 0
7
- const _typeDocsFor = require('./partials/typeDocsFor.js')
1
+ 'use strict'
2
+
3
+ Object.defineProperty(exports, '__esModule', {
4
+ value: true
5
+ })
6
+ exports.typeDocs = void 0
7
+ const _typeDocsFor = require('./partials/typeDocsFor.js')
8
8
  /**
9
9
  * Generate the HTML documentation of a TypeScript project from its source, with the modules mirroring the source
10
10
  * folders. Turn it on with docs.enabled, see {@link module:partials.typeDocsFor}.
11
11
  * @memberOf module:js-build-tools
12
12
  * @returns {Function}
13
- */
14
- const typeDocs = exports.typeDocs = (0, _typeDocsFor.typeDocsFor)()
13
+ */
14
+ const typeDocs = exports.typeDocs = (0, _typeDocsFor.typeDocsFor)()
@@ -1,5 +1,5 @@
1
1
  import cache from 'gulp-cache'
2
- import { createRequire } from 'node:module'
2
+ import { requirePeer } from './loadPeer.mjs'
3
3
  import { dest, src } from 'gulp'
4
4
  import path from 'node:path'
5
5
  import * as gulpConfig from '../../gulp.config.mjs'
@@ -17,18 +17,12 @@ const installHint = 'Add it to your project with: npm install --save-dev github:
17
17
  * @throws {Error} With install instructions when gulp-imagemin is missing or is the ESM-only upstream release.
18
18
  */
19
19
  export const loadImagemin = (projectPath = process.cwd()) => {
20
- let loaded
21
- try {
22
- loaded = createRequire(path.join(projectPath, 'package.json'))('gulp-imagemin')
23
- } catch (error) {
24
- if (error.code === 'MODULE_NOT_FOUND' && /Cannot find module 'gulp-imagemin'/.test(error.message)) {
25
- throw new Error(`The images task needs the optional peer dependency gulp-imagemin, which is not installed. ${installHint}`)
26
- }
27
- if (error.code === 'ERR_REQUIRE_ESM') {
28
- throw new Error(`gulp-imagemin could not be loaded because it is ESM-only. ${installHint}`)
29
- }
30
- throw error
31
- }
20
+ const loaded = requirePeer('gulp-imagemin', {
21
+ task: 'images',
22
+ installHint,
23
+ esmOnlyMessage: `gulp-imagemin could not be loaded because it is ESM-only. ${installHint}`,
24
+ projectPath
25
+ })
32
26
  return loaded && (loaded.__esModule || loaded.default) ? loaded.default : loaded
33
27
  }
34
28
 
@@ -0,0 +1,75 @@
1
+ import { createRequire } from 'node:module'
2
+ import path from 'node:path'
3
+ import { pathToFileURL } from 'node:url'
4
+
5
+ // Several dependencies ship ESM-only (standard, gulp-ts-compile, typedoc), but this package also publishes a CommonJS
6
+ // build (dist/*.js) whose babel transform would turn even a dynamic import() into a require() that cannot load an ES
7
+ // module. Hiding the import() inside a Function constructor keeps it a genuine dynamic import that works from either
8
+ // build, because babel cannot see (and so cannot rewrite) an import expression which only exists as a runtime string.
9
+ const dynamicImport = new Function('specifier', 'return import(specifier)')
10
+
11
+ /**
12
+ * Import an ES module (or any module) with a real dynamic import(), whichever build (ESM or CommonJS) this runs from.
13
+ * @memberOf module:partials
14
+ * @param {string} specifier - The package name, or the URL of a file.
15
+ * @returns {Promise<Object>} The module namespace.
16
+ */
17
+ export const importModule = specifier => dynamicImport(specifier)
18
+
19
+ const isMissing = (error, name) => error.code === 'MODULE_NOT_FOUND' && error.message.includes(`'${name}'`)
20
+
21
+ const notInstalled = (name, { task, installHint }) => new Error(
22
+ `The ${task} task needs the optional peer dependency ${name}, which is not installed. ${installHint}`
23
+ )
24
+
25
+ /**
26
+ * Load an optional peer dependency (one which is only needed once a task runs, so projects which never use that task
27
+ * do not have to install it) with require(), from the project being built rather than from this package. When it is
28
+ * missing the error explains how to install it.
29
+ * @memberOf module:partials
30
+ * @param {string} name - The package name.
31
+ * @param {Object} options
32
+ * @param {string} options.task - The task which needs the package, for the error message.
33
+ * @param {string} options.installHint - How to install it, for the error message.
34
+ * @param {string} [options.esmOnlyMessage] - The error message when the package turns out to be ESM-only (it cannot be require()d).
35
+ * @param {string} [options.projectPath=process.cwd()] - The folder of the project (which has the package.json) being built.
36
+ * @returns {*} Whatever the package exports.
37
+ * @throws {Error} With install instructions when the package is not installed.
38
+ */
39
+ export const requirePeer = (name, { task, installHint, esmOnlyMessage, projectPath = process.cwd() }) => {
40
+ try {
41
+ return createRequire(path.join(projectPath, 'package.json'))(name)
42
+ } catch (error) {
43
+ if (isMissing(error, name)) {
44
+ throw notInstalled(name, { task, installHint })
45
+ }
46
+ if (error.code === 'ERR_REQUIRE_ESM' && esmOnlyMessage) {
47
+ throw new Error(esmOnlyMessage)
48
+ }
49
+ throw error
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Import an optional peer dependency which ships ESM-only, from the project being built. See {@link requirePeer}.
55
+ * @memberOf module:partials
56
+ * @param {string} name - The package name.
57
+ * @param {Object} options
58
+ * @param {string} options.task - The task which needs the package, for the error message.
59
+ * @param {string} options.installHint - How to install it, for the error message.
60
+ * @param {string} [options.projectPath=process.cwd()] - The folder of the project (which has the package.json) being built.
61
+ * @returns {Promise<Object>} The module namespace.
62
+ * @throws {Error} With install instructions when the package is not installed.
63
+ */
64
+ export const importPeer = async (name, { task, installHint, projectPath = process.cwd() }) => {
65
+ let resolved
66
+ try {
67
+ resolved = createRequire(path.join(projectPath, 'package.json')).resolve(name)
68
+ } catch (error) {
69
+ if (isMissing(error, name)) {
70
+ throw notInstalled(name, { task, installHint })
71
+ }
72
+ throw error
73
+ }
74
+ return importModule(pathToFileURL(resolved).href)
75
+ }
@@ -0,0 +1,58 @@
1
+ import fs from 'fs'
2
+ import path from 'node:path'
3
+ import * as setUp from '../test-helpers/setUp.mjs'
4
+ import { importModule, importPeer, requirePeer } from './loadPeer.mjs'
5
+
6
+ setUp.setDefaults('test-load-peer')
7
+
8
+ const options = { task: 'example', installHint: 'Install it with: npm install --save-dev fake-peer' }
9
+
10
+ // A project with its own node_modules, so the packages are found from the project rather than from this package
11
+ const projectWith = (name, files) => {
12
+ const project = path.resolve('test-load-peer/project')
13
+ fs.mkdirSync(`${project}/node_modules/${name}`, { recursive: true })
14
+ fs.writeFileSync(`${project}/package.json`, '{}')
15
+ Object.entries(files).forEach(([file, contents]) => fs.writeFileSync(`${project}/node_modules/${name}/${file}`, contents))
16
+ return project
17
+ }
18
+
19
+ beforeEach(setUp.beforeEach)
20
+ afterEach(setUp.afterEach)
21
+
22
+ describe('requirePeer', () => {
23
+ test('loads the package from the project being built', () => {
24
+ const project = projectWith('fake-peer', { 'package.json': '{"name":"fake-peer","main":"index.js"}', 'index.js': 'module.exports = { found: true }' })
25
+ expect(requirePeer('fake-peer', { ...options, projectPath: project })).toEqual({ found: true })
26
+ })
27
+
28
+ test('explains how to install the package when the project does not have it', () => {
29
+ expect(() => requirePeer('fake-peer', { ...options, projectPath: '/' })).toThrow(
30
+ 'The example task needs the optional peer dependency fake-peer, which is not installed. Install it with: npm install --save-dev fake-peer'
31
+ )
32
+ })
33
+
34
+ test('does not hide other errors, and explains a package which is ESM-only when it has a message for that', () => {
35
+ const project = projectWith('broken-peer', { 'package.json': '{"name":"broken-peer","main":"index.js"}', 'index.js': 'throw new Error("boom")' })
36
+ expect(() => requirePeer('broken-peer', { ...options, projectPath: project })).toThrow('boom')
37
+ // A missing dependency of the peer is not the peer being missing
38
+ const inner = projectWith('inner-peer', { 'package.json': '{"name":"inner-peer","main":"index.js"}', 'index.js': 'require("something-else-missing")' })
39
+ expect(() => requirePeer('inner-peer', { ...options, projectPath: inner })).toThrow(/something-else-missing/)
40
+ expect(() => requirePeer('inner-peer', { ...options, projectPath: inner })).not.toThrow(/optional peer dependency/)
41
+ })
42
+ })
43
+
44
+ describe('importPeer', () => {
45
+ test('explains how to install the package when the project does not have it', async () => {
46
+ await expect(importPeer('fake-peer', { ...options, projectPath: '/' })).rejects.toThrow(
47
+ /needs the optional peer dependency fake-peer, which is not installed. Install it with/
48
+ )
49
+ })
50
+ })
51
+
52
+ describe('importModule', () => {
53
+ // The real import() cannot run under this project's CommonJS-transform-based Jest setup (see tsFor.mjs), so the
54
+ // loaders are checked by running the tasks for real, see the pull request.
55
+ test('is a function', () => {
56
+ expect(importModule).toBeInstanceOf(Function)
57
+ })
58
+ })
@@ -1,11 +1,9 @@
1
1
  import path from 'node:path'
2
2
  import through from 'through2'
3
+ import { importModule } from './loadPeer.mjs'
3
4
 
4
- // `standard` (17+) ships ESM-only, but this package also publishes a CommonJS build (dist/*.js) whose babel
5
- // transform would turn even a dynamic import() into a require() that cannot load an ES module. Hiding the import()
6
- // inside a Function constructor keeps it a genuine dynamic import that works from either build (same escape hatch
7
- // as tsFor.mjs uses for gulp-ts-compile).
8
- const importStandard = new Function('return import(\'standard\')')
5
+ // `standard` (17+) ships ESM-only, see importModule for how it is imported.
6
+ const importStandard = () => importModule('standard')
9
7
 
10
8
  /**
11
9
  * Print a file's lint messages, if it has any.
@@ -1,17 +1,10 @@
1
1
  import { dest, src } from 'gulp'
2
2
  import path from 'node:path'
3
3
  import * as gulpConfig from '../../gulp.config.mjs'
4
- import { createRequire } from 'node:module'
4
+ import { importModule, requirePeer } from './loadPeer.mjs'
5
5
 
6
- // gulp-ts-compile ships ESM-only (no CJS build - see its own package.json) since it's meant to be consumed by
7
- // gulpfiles running under a real ESM loader, same as this project's own gulpfile.mjs does. This project also
8
- // compiles itself to a CommonJS build (dist/*.js, this file's own compiled counterpart) for consumers using
9
- // require() though, and even a dynamic `import()` gets downleveled by babel's CommonJS transform into a
10
- // require() call - which can't load a real ES module. Hiding the import() call inside a Function constructor is
11
- // the standard escape hatch: babel can't see (and so can't rewrite) an import expression that only exists as a
12
- // runtime-evaluated string, so it survives as a genuine dynamic import, capable of loading gulp-ts-compile from
13
- // either build.
14
- const importGulpTsCompile = new Function('return import(\'gulp-ts-compile\')')
6
+ // gulp-ts-compile ships ESM-only (no CJS build - see its own package.json), see importModule for how it is imported.
7
+ const importGulpTsCompile = () => importModule('gulp-ts-compile')
15
8
 
16
9
  /**
17
10
  * Wrap a stream in a promise that resolves once it finishes writing.
@@ -29,16 +22,7 @@ const installHint = 'Install it in your project with: npm install --save-dev typ
29
22
  * @returns {Object} The TypeScript compiler API module.
30
23
  * @throws {Error} With install instructions when typescript is not installed.
31
24
  */
32
- export const loadTypescript = (projectPath = process.cwd()) => {
33
- try {
34
- return createRequire(path.join(projectPath, 'package.json'))('typescript')
35
- } catch (error) {
36
- if (error.code === 'MODULE_NOT_FOUND' && /Cannot find module 'typescript'/.test(error.message)) {
37
- throw new Error(`The typescript task needs the optional peer dependency typescript, which is not installed. ${installHint}`)
38
- }
39
- throw error
40
- }
41
- }
25
+ export const loadTypescript = (projectPath = process.cwd()) => requirePeer('typescript', { task: 'typescript', installHint, projectPath })
42
26
 
43
27
  const streamToPromise = (stream) => new Promise((resolve, reject) => {
44
28
  stream.on('finish', resolve)
@@ -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,44 @@ 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
+ return indexFile ? describeFile(indexFile) : ''
83
+ }
84
+
85
+ /**
86
+ * The description of a source file for its module: its first doc comment without the tags (@module, @file, ...), when
87
+ * that comment is a header (it is followed by an import or export, so it does not document a declaration itself), or
88
+ * an empty string.
89
+ * @memberOf module:partials
90
+ * @param {string} filePath
91
+ * @returns {string}
92
+ */
93
+ export const describeFile = filePath => {
94
+ const comment = fs.readFileSync(filePath, 'utf8').match(/^\s*\/\*\*([\s\S]*?)\*\/\s*(import\b|export\s+(\*|\{[^}]*\}\s+from)|$)/)
95
+ if (!comment) {
96
+ return ''
97
+ }
98
+ const lines = comment[1].split('\n').map(line => line.replace(/^\s*\* ?/, '').trimEnd())
99
+ const tagAt = lines.findIndex(line => /^@\w+/.test(line))
100
+ return (tagAt === -1 ? lines : lines.slice(0, tagAt)).join('\n').trim()
101
+ }
102
+
89
103
  /**
90
104
  * Write one entry file per folder of the source directory into the entry directory. TypeDoc makes a module of every
91
105
  * entry file, so the modules of the documentation mirror the folders. The default export of each source file is
92
106
  * re-exported under the name of the file (the source files hold one function each), and anything else a file exports
93
107
  * 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.
108
+ * files are left out, but the first doc comment of a folder's index file becomes the description of its module.
109
+ * A file which sits beside the folders (not an index or main file) is a module of its own, described by its header
110
+ * comment. A source directory with no folders gets a single entry called index.
95
111
  * @memberOf module:partials
96
112
  * @param {Object} ts - The TypeScript compiler API.
97
113
  * @param {string} srcDir - The directory holding the TypeScript source.
@@ -103,11 +119,21 @@ export const writeDocEntries = (ts, srcDir, entryDir) => {
103
119
  fs.mkdirSync(entryDir, { recursive: true })
104
120
  const folders = fs.readdirSync(absoluteSrc, { withFileTypes: true })
105
121
  .filter(entry => entry.isDirectory() && entry.name !== 'node_modules')
106
- .map(entry => ({ name: entry.name, files: listSources(path.join(absoluteSrc, entry.name)) }))
122
+ .map(entry => ({ name: entry.name, path: path.join(absoluteSrc, entry.name), files: listSources(path.join(absoluteSrc, entry.name)) }))
107
123
  .filter(folder => folder.files.length)
124
+ const rootFiles = fs.readdirSync(absoluteSrc, { withFileTypes: true })
125
+ .filter(entry => entry.isFile() && isSource(entry.name) && !/^(index|main)\./.test(entry.name))
126
+ .map(entry => path.join(absoluteSrc, entry.name))
127
+ // Folders make modules, and so do the files which sit beside them (except the barrel: index and main). A source
128
+ // directory with no folders gets a single module called index which holds its files.
108
129
  const groups = folders.length
109
- ? folders
110
- : [{ name: 'index', files: listSources(absoluteSrc).filter(file => !/^(index|main)\./.test(path.basename(file))) }]
130
+ ? [
131
+ ...folders,
132
+ ...rootFiles
133
+ .map(file => ({ name: toIdentifier(path.basename(file).replace(/\.[cm]?tsx?$/, '')), files: [file], description: describeFile(file) }))
134
+ .filter(group => !folders.some(folder => folder.name === group.name))
135
+ ]
136
+ : [{ name: 'index', path: absoluteSrc, files: rootFiles }]
111
137
  return groups.map(group => {
112
138
  const lines = group.files
113
139
  .filter(file => !/^index\./.test(path.basename(file)))
@@ -121,8 +147,11 @@ export const writeDocEntries = (ts, srcDir, entryDir) => {
121
147
  ...(hasNamed ? [`export * from '${modulePath}'`] : [])
122
148
  ]
123
149
  })
150
+ const description = group.description ?? (group.path ? describeFolder(group.path) : '')
151
+ // TypeDoc takes the first comment of an entry file which has the @module tag as the description of the module
152
+ const moduleComment = description ? `/**\n${description.split('\n').map(line => ` * ${line}`.trimEnd()).join('\n')}\n * @module\n */\n\n` : ''
124
153
  const entryPath = path.join(entryDir, `${group.name}.ts`)
125
- fs.writeFileSync(entryPath, lines.join('\n') + '\n')
154
+ fs.writeFileSync(entryPath, moduleComment + lines.join('\n') + '\n')
126
155
  return entryPath
127
156
  })
128
157
  }