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,34 +1,27 @@
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.tsFor = exports.loadTypescript = void 0;
8
- var _gulp = require("gulp");
9
- var _nodePath = _interopRequireDefault(require("node:path"));
10
- var gulpConfig = _interopRequireWildcard(require("../../gulp.config.js"));
11
- var _nodeModule = require("node:module");
12
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.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); }
13
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
- // gulp-ts-compile ships ESM-only (no CJS build - see its own package.json) since it's meant to be consumed by
15
- // gulpfiles running under a real ESM loader, same as this project's own gulpfile.mjs does. This project also
16
- // compiles itself to a CommonJS build (dist/*.js, this file's own compiled counterpart) for consumers using
17
- // require() though, and even a dynamic `import()` gets downleveled by babel's CommonJS transform into a
18
- // require() call - which can't load a real ES module. Hiding the import() call inside a Function constructor is
19
- // the standard escape hatch: babel can't see (and so can't rewrite) an import expression that only exists as a
20
- // runtime-evaluated string, so it survives as a genuine dynamic import, capable of loading gulp-ts-compile from
21
- // either build.
22
- const importGulpTsCompile = new Function('return import(\'gulp-ts-compile\')');
23
-
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.tsFor = exports.loadTypescript = void 0;
8
+ var _gulp = require("gulp");
9
+ var _nodePath = _interopRequireDefault(require("node:path"));
10
+ var gulpConfig = _interopRequireWildcard(require("../../gulp.config.js"));
11
+ var _loadPeer = require("./loadPeer.js");
12
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.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); }
13
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
+ // gulp-ts-compile ships ESM-only (no CJS build - see its own package.json), see importModule for how it is imported.
15
+ const importGulpTsCompile = () => (0, _loadPeer.importModule)('gulp-ts-compile');
16
+
24
17
  /**
25
18
  * Wrap a stream in a promise that resolves once it finishes writing.
26
19
  * @memberOf module:partials
27
20
  * @param {import('node:stream').Stream} stream
28
21
  * @returns {Promise<void>}
29
- */
30
- const installHint = 'Install it in your project with: npm install --save-dev typescript';
31
-
22
+ */
23
+ const installHint = 'Install it in your project with: npm install --save-dev typescript';
24
+
32
25
  /**
33
26
  * Load the project's own copy of TypeScript (an optional peer dependency, only needed when the typescript task is
34
27
  * enabled) from the project being built, so projects which do not use TypeScript never need it installed.
@@ -36,23 +29,18 @@ const installHint = 'Install it in your project with: npm install --save-dev typ
36
29
  * @param {string} [projectPath=process.cwd()] The folder of the project (which has the package.json) being built.
37
30
  * @returns {Object} The TypeScript compiler API module.
38
31
  * @throws {Error} With install instructions when typescript is not installed.
39
- */
40
- const loadTypescript = (projectPath = process.cwd()) => {
41
- try {
42
- return (0, _nodeModule.createRequire)(_nodePath.default.join(projectPath, 'package.json'))('typescript');
43
- } catch (error) {
44
- if (error.code === 'MODULE_NOT_FOUND' && /Cannot find module 'typescript'/.test(error.message)) {
45
- throw new Error(`The typescript task needs the optional peer dependency typescript, which is not installed. ${installHint}`);
46
- }
47
- throw error;
48
- }
49
- };
50
- exports.loadTypescript = loadTypescript;
51
- const streamToPromise = stream => new Promise((resolve, reject) => {
52
- stream.on('finish', resolve);
53
- stream.on('error', reject);
54
- });
55
-
32
+ */
33
+ const loadTypescript = (projectPath = process.cwd()) => (0, _loadPeer.requirePeer)('typescript', {
34
+ task: 'typescript',
35
+ installHint,
36
+ projectPath
37
+ });
38
+ exports.loadTypescript = loadTypescript;
39
+ const streamToPromise = stream => new Promise((resolve, reject) => {
40
+ stream.on('finish', resolve);
41
+ stream.on('error', reject);
42
+ });
43
+
56
44
  /**
57
45
  * Read a tsconfig.json file into real typescript.CompilerOptions values, using only TypeScript's stable, current,
58
46
  * public config-reading API (the same one `tsc --project` itself uses internally) - never
@@ -61,13 +49,13 @@ const streamToPromise = stream => new Promise((resolve, reject) => {
61
49
  * @memberOf module:partials
62
50
  * @param {string} configPath - Path to a tsconfig.json file.
63
51
  * @returns {import('typescript').CompilerOptions}
64
- */
65
- const readCompilerOptions = (ts, configPath) => {
66
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
67
- const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, _nodePath.default.dirname(configPath));
68
- return parsedConfig.options;
69
- };
70
-
52
+ */
53
+ const readCompilerOptions = (ts, configPath) => {
54
+ const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
55
+ const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, _nodePath.default.dirname(configPath));
56
+ return parsedConfig.options;
57
+ };
58
+
71
59
  /**
72
60
  * Starting at the source directory, find all the ts files and convert them into the distribution directory.
73
61
  * @memberOf module:partials
@@ -80,36 +68,36 @@ const readCompilerOptions = (ts, configPath) => {
80
68
  * suite already covers real TypeScript-compile correctness - this function's own tests only need to verify its
81
69
  * config resolution and stream wiring.
82
70
  * @returns {Function}
83
- */
84
- const tsFor = (srcPath = gulpConfig.get('typescript.from'), distPath = gulpConfig.get('typescript.to'), loadTsCompile = () => importGulpTsCompile().then(({
85
- default: tsCompile
86
- }) => tsCompile)) => {
87
- if (gulpConfig.get('typescript.enabled') === false) {
88
- return () => {};
89
- }
90
- const ts = loadTypescript();
91
- const configPath = gulpConfig.get('typescript.config');
92
- // When no tsconfig.json is configured, target/module are pinned explicitly rather than left for TypeScript's
93
- // own compiler defaults - those defaults have already shifted once across a TypeScript version bump in this
94
- // project's own history (silently changing whether output keeps `const`/`let` or downlevels to `var`), and
95
- // pinning them keeps this task's output stable across future TypeScript upgrades too. ES5/CommonJS matches
96
- // what the downstream babel/browserify bundling pipeline has always assumed it receives.
97
- const compilerOptions = configPath ? readCompilerOptions(ts, configPath) : {
98
- declaration: true,
99
- target: ts.ScriptTarget.ES5,
100
- module: ts.ModuleKind.CommonJS,
101
- esModuleInterop: false
102
- };
103
- // Accepts gulp's own callback-style task convention (an optional `done`, called once finished) rather than
104
- // just returning a Promise, since callers may invoke the returned function directly with a callback instead
105
- // of relying on gulp's own runner to observe a returned Promise.
106
- return done => {
107
- const compile = async () => {
108
- const tsCompile = await loadTsCompile();
109
- const tsResult = (0, _gulp.src)(srcPath).pipe(tsCompile(compilerOptions));
110
- await Promise.all([streamToPromise(tsResult.dts.pipe((0, _gulp.dest)(distPath))), streamToPromise(tsResult.js.pipe((0, _gulp.dest)(distPath)))]);
111
- };
112
- return typeof done === 'function' ? compile().then(() => done(), done) : compile();
113
- };
114
- };
71
+ */
72
+ const tsFor = (srcPath = gulpConfig.get('typescript.from'), distPath = gulpConfig.get('typescript.to'), loadTsCompile = () => importGulpTsCompile().then(({
73
+ default: tsCompile
74
+ }) => tsCompile)) => {
75
+ if (gulpConfig.get('typescript.enabled') === false) {
76
+ return () => {};
77
+ }
78
+ const ts = loadTypescript();
79
+ const configPath = gulpConfig.get('typescript.config');
80
+ // When no tsconfig.json is configured, target/module are pinned explicitly rather than left for TypeScript's
81
+ // own compiler defaults - those defaults have already shifted once across a TypeScript version bump in this
82
+ // project's own history (silently changing whether output keeps `const`/`let` or downlevels to `var`), and
83
+ // pinning them keeps this task's output stable across future TypeScript upgrades too. ES5/CommonJS matches
84
+ // what the downstream babel/browserify bundling pipeline has always assumed it receives.
85
+ const compilerOptions = configPath ? readCompilerOptions(ts, configPath) : {
86
+ declaration: true,
87
+ target: ts.ScriptTarget.ES5,
88
+ module: ts.ModuleKind.CommonJS,
89
+ esModuleInterop: false
90
+ };
91
+ // Accepts gulp's own callback-style task convention (an optional `done`, called once finished) rather than
92
+ // just returning a Promise, since callers may invoke the returned function directly with a callback instead
93
+ // of relying on gulp's own runner to observe a returned Promise.
94
+ return done => {
95
+ const compile = async () => {
96
+ const tsCompile = await loadTsCompile();
97
+ const tsResult = (0, _gulp.src)(srcPath).pipe(tsCompile(compilerOptions));
98
+ await Promise.all([streamToPromise(tsResult.dts.pipe((0, _gulp.dest)(distPath))), streamToPromise(tsResult.js.pipe((0, _gulp.dest)(distPath)))]);
99
+ };
100
+ return typeof done === 'function' ? compile().then(() => done(), done) : compile();
101
+ };
102
+ };
115
103
  exports.tsFor = tsFor;
@@ -4,10 +4,11 @@ require("core-js/modules/esnext.weak-map.delete-all.js");
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.writeDocEntries = exports.typeDocsFor = exports.loadTypeDoc = exports.describeExports = void 0;
7
+ exports.writeDocEntries = exports.typeDocsFor = exports.loadTypeDoc = exports.describeFolder = exports.describeExports = void 0;
8
8
  require("core-js/modules/es.json.stringify.js");
9
9
  require("core-js/modules/esnext.iterator.constructor.js");
10
10
  require("core-js/modules/esnext.iterator.filter.js");
11
+ require("core-js/modules/esnext.iterator.find.js");
11
12
  require("core-js/modules/esnext.iterator.flat-map.js");
12
13
  require("core-js/modules/esnext.iterator.for-each.js");
13
14
  require("core-js/modules/esnext.iterator.map.js");
@@ -15,16 +16,12 @@ require("core-js/modules/esnext.iterator.some.js");
15
16
  var _nodeFs = _interopRequireDefault(require("node:fs"));
16
17
  var _nodeOs = _interopRequireDefault(require("node:os"));
17
18
  var _nodePath = _interopRequireDefault(require("node:path"));
18
- var _nodeModule = require("node:module");
19
- var _nodeUrl = require("node:url");
20
19
  var gulpConfig = _interopRequireWildcard(require("../../gulp.config.js"));
20
+ var _loadPeer = require("./loadPeer.js");
21
21
  var _tsFor = require("./tsFor.js");
22
22
  var _removeDirectory = require("./removeDirectory.js");
23
23
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.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); }
24
24
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
25
- // TypeDoc ships ESM-only, and this project also compiles itself to a CommonJS build (see tsFor.mjs, which hides its
26
- // own import of gulp-ts-compile the same way), so the import() is hidden from babel's CommonJS transform.
27
- const importModule = new Function('specifier', 'return import(specifier)');
28
25
  const installHint = 'Install it in your project with: npm install --save-dev typedoc';
29
26
 
30
27
  /**
@@ -35,18 +32,11 @@ const installHint = 'Install it in your project with: npm install --save-dev typ
35
32
  * @returns {Promise<Object>} The TypeDoc module.
36
33
  * @throws {Error} With install instructions when typedoc is not installed.
37
34
  */
38
- const loadTypeDoc = async (projectPath = process.cwd()) => {
39
- let typeDocPath;
40
- try {
41
- typeDocPath = (0, _nodeModule.createRequire)(_nodePath.default.join(projectPath, 'package.json')).resolve('typedoc');
42
- } catch (error) {
43
- if (error.code === 'MODULE_NOT_FOUND') {
44
- throw new Error(`The docs task needs the optional peer dependency typedoc, which is not installed. ${installHint}`);
45
- }
46
- throw error;
47
- }
48
- return importModule((0, _nodeUrl.pathToFileURL)(typeDocPath).href);
49
- };
35
+ const loadTypeDoc = (projectPath = process.cwd()) => (0, _loadPeer.importPeer)('typedoc', {
36
+ task: 'docs',
37
+ installHint,
38
+ projectPath
39
+ });
50
40
  exports.loadTypeDoc = loadTypeDoc;
51
41
  const isSource = fileName => /\.(ts|tsx|mts|cts)$/.test(fileName) && !/\.d\.[cm]?ts$/.test(fileName) && !/\.(test|spec)\./.test(fileName);
52
42
  const listSources = dirPath => _nodeFs.default.readdirSync(dirPath, {
@@ -102,19 +92,38 @@ const describeExports = (ts, filePath) => {
102
92
  };
103
93
  };
104
94
 
95
+ /**
96
+ * The description of a folder for its module: the first doc comment of the folder's index file, without its tags
97
+ * (@module, @file, ...), or an empty string when there is none.
98
+ * @memberOf module:partials
99
+ * @param {string} folderPath
100
+ * @returns {string}
101
+ */
102
+ exports.describeExports = describeExports;
103
+ const describeFolder = folderPath => {
104
+ const indexFile = ['index.ts', 'index.tsx', 'index.mts', 'index.cts'].map(name => _nodePath.default.join(folderPath, name)).find(file => _nodeFs.default.existsSync(file));
105
+ const comment = indexFile && _nodeFs.default.readFileSync(indexFile, 'utf8').match(/^\s*\/\*\*([\s\S]*?)\*\//);
106
+ if (!comment) {
107
+ return '';
108
+ }
109
+ const lines = comment[1].split('\n').map(line => line.replace(/^\s*\* ?/, '').trimEnd());
110
+ const tagAt = lines.findIndex(line => /^@\w+/.test(line));
111
+ return (tagAt === -1 ? lines : lines.slice(0, tagAt)).join('\n').trim();
112
+ };
113
+
105
114
  /**
106
115
  * Write one entry file per folder of the source directory into the entry directory. TypeDoc makes a module of every
107
116
  * entry file, so the modules of the documentation mirror the folders. The default export of each source file is
108
117
  * re-exported under the name of the file (the source files hold one function each), and anything else a file exports
109
118
  * is re-exported as it is (that is where the types.ts files go). Index files (the barrels), tests and declaration
110
- * files are left out. A source directory with no folders gets a single entry called index.
119
+ * 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.
111
120
  * @memberOf module:partials
112
121
  * @param {Object} ts - The TypeScript compiler API.
113
122
  * @param {string} srcDir - The directory holding the TypeScript source.
114
123
  * @param {string} entryDir - The directory to write the entry files into.
115
124
  * @returns {Array<string>} The paths of the entry files.
116
125
  */
117
- exports.describeExports = describeExports;
126
+ exports.describeFolder = describeFolder;
118
127
  const writeDocEntries = (ts, srcDir, entryDir) => {
119
128
  const absoluteSrc = _nodePath.default.resolve(srcDir);
120
129
  _nodeFs.default.mkdirSync(entryDir, {
@@ -124,10 +133,12 @@ const writeDocEntries = (ts, srcDir, entryDir) => {
124
133
  withFileTypes: true
125
134
  }).filter(entry => entry.isDirectory() && entry.name !== 'node_modules').map(entry => ({
126
135
  name: entry.name,
136
+ path: _nodePath.default.join(absoluteSrc, entry.name),
127
137
  files: listSources(_nodePath.default.join(absoluteSrc, entry.name))
128
138
  })).filter(folder => folder.files.length);
129
139
  const groups = folders.length ? folders : [{
130
140
  name: 'index',
141
+ path: absoluteSrc,
131
142
  files: listSources(absoluteSrc).filter(file => !/^(index|main)\./.test(_nodePath.default.basename(file)))
132
143
  }];
133
144
  return groups.map(group => {
@@ -140,8 +151,11 @@ const writeDocEntries = (ts, srcDir, entryDir) => {
140
151
  const fileName = _nodePath.default.basename(file).replace(/\.[cm]?tsx?$/, '');
141
152
  return [...(hasDefault ? [`export { default as ${toIdentifier(fileName)} } from '${modulePath}'`] : []), ...(hasNamed ? [`export * from '${modulePath}'`] : [])];
142
153
  });
154
+ const description = group.path ? describeFolder(group.path) : '';
155
+ // TypeDoc takes the first comment of an entry file which has the @module tag as the description of the module
156
+ const moduleComment = description ? `/**\n${description.split('\n').map(line => ` * ${line}`.trimEnd()).join('\n')}\n * @module\n */\n\n` : '';
143
157
  const entryPath = _nodePath.default.join(entryDir, `${group.name}.ts`);
144
- _nodeFs.default.writeFileSync(entryPath, lines.join('\n') + '\n');
158
+ _nodeFs.default.writeFileSync(entryPath, moduleComment + lines.join('\n') + '\n');
145
159
  return entryPath;
146
160
  });
147
161
  };
package/dist/partials.js CHANGED
@@ -51,6 +51,18 @@ Object.defineProperty(exports, 'imagesFor', {
51
51
  return _imagesFor.imagesFor
52
52
  }
53
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
+ })
54
66
  Object.defineProperty(exports, 'minifyFor', {
55
67
  enumerable: true,
56
68
  get: function () {
@@ -69,6 +81,12 @@ Object.defineProperty(exports, 'removeDirectory', {
69
81
  return _removeDirectory.removeDirectory
70
82
  }
71
83
  })
84
+ Object.defineProperty(exports, 'requirePeer', {
85
+ enumerable: true,
86
+ get: function () {
87
+ return _loadPeer.requirePeer
88
+ }
89
+ })
72
90
  Object.defineProperty(exports, 'runOnChange', {
73
91
  enumerable: true,
74
92
  get: function () {
@@ -107,6 +125,7 @@ var _distFor = require('./partials/distFor.js')
107
125
  var _distForSrc = require('./partials/distForSrc.js')
108
126
  var _distSeries = require('./partials/distSeries.js')
109
127
  var _imagesFor = require('./partials/imagesFor.js')
128
+ var _loadPeer = require('./partials/loadPeer.js')
110
129
  var _minifyFor = require('./partials/minifyFor.js')
111
130
  var _readmeTemplate = require('./partials/readmeTemplate.js')
112
131
  var _removeDirectory = require('./partials/removeDirectory.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)