Skip to content

Commit 2a71de0

Browse files
committed
Implement a workaround for jsdoc issue regarding temp dirs #19.
Increase the child_process maxBuffer to 100MB #24
1 parent a600973 commit 2a71de0

File tree

8 files changed

+88
-45
lines changed

8 files changed

+88
-45
lines changed

dist/index.cjs

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use strict';
22

33
var path$1 = require('path');
4-
var fs$1 = require('fs');
4+
var fs = require('fs');
55
var os = require('os');
66
var crypto = require('crypto');
7+
var fs$1 = require('node:fs');
78
var node_url = require('node:url');
89
var path$2 = require('node:path');
9-
var actualFS = require('node:fs');
1010
var promises = require('node:fs/promises');
1111
var node_events = require('node:events');
1212
var Stream = require('node:stream');
@@ -35,7 +35,7 @@ function _interopNamespaceDefault(e) {
3535
return Object.freeze(n);
3636
}
3737

38-
var actualFS__namespace = /*#__PURE__*/_interopNamespaceDefault(actualFS);
38+
var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs$1);
3939

4040
/**
4141
* Takes any input and guarantees an array back.
@@ -124,7 +124,7 @@ class Cache {
124124
}
125125
set dir (val) {
126126
this._dir = val;
127-
fs$1.mkdirSync(this.dir, { recursive: true });
127+
fs.mkdirSync(this.dir, { recursive: true });
128128
}
129129

130130
/**
@@ -135,7 +135,7 @@ class Cache {
135135
*/
136136
async read (keys) {
137137
const blobPath = path$1.resolve(this._dir, this.getChecksum(keys));
138-
return fs$1.promises.readFile(blobPath).then(JSON.parse)
138+
return fs.promises.readFile(blobPath).then(JSON.parse)
139139
}
140140

141141
/**
@@ -146,7 +146,7 @@ class Cache {
146146
readSync (keys) {
147147
const blobPath = path$1.resolve(this._dir, this.getChecksum(keys));
148148
try {
149-
const data = fs$1.readFileSync(blobPath, 'utf8');
149+
const data = fs.readFileSync(blobPath, 'utf8');
150150
return JSON.parse(data)
151151
} catch (err) {
152152
return null
@@ -161,7 +161,7 @@ class Cache {
161161
*/
162162
async write (keys, content) {
163163
const blobPath = path$1.resolve(this._dir, this.getChecksum(keys));
164-
return fs$1.promises.writeFile(blobPath, JSON.stringify(content))
164+
return fs.promises.writeFile(blobPath, JSON.stringify(content))
165165
}
166166

167167
/**
@@ -171,7 +171,7 @@ class Cache {
171171
*/
172172
writeSync (keys, content) {
173173
const blobPath = path$1.resolve(this._dir, this.getChecksum(keys));
174-
fs$1.writeFileSync(blobPath, JSON.stringify(content));
174+
fs.writeFileSync(blobPath, JSON.stringify(content));
175175
}
176176

177177
/**
@@ -190,8 +190,8 @@ class Cache {
190190
* @returns {Promise}
191191
*/
192192
async clear () {
193-
const files = await fs$1.promises.readdir(this._dir);
194-
const promises = files.map(file => fs$1.promises.unlink(path$1.resolve(this._dir, file)));
193+
const files = await fs.promises.readdir(this._dir);
194+
const promises = files.map(file => fs.promises.unlink(path$1.resolve(this._dir, file)));
195195
return Promise.all(promises)
196196
}
197197

@@ -201,14 +201,13 @@ class Cache {
201201
*/
202202
async remove () {
203203
await this.clear();
204-
return fs$1.promises.rmdir(this._dir)
204+
return fs.promises.rmdir(this._dir)
205205
}
206206
}
207207

208208
class TempFile {
209209
constructor (source) {
210-
const tempDir = fs$1.mkdtempSync(path$1.join(os.tmpdir(), 'jsdoc-api-'));
211-
this.path = path$1.join(tempDir, crypto.randomBytes(6).toString('hex') + '.js');
210+
this.path = path$1.join(TempFile.tempFileDir, crypto.randomBytes(6).toString('hex') + '.js');
212211
fs$1.writeFileSync(this.path, source);
213212
}
214213

@@ -219,6 +218,15 @@ class TempFile {
219218
// already deleted
220219
}
221220
}
221+
222+
static tempFileDir = path$1.join(os.homedir(), '.jsdoc-api/temp')
223+
static cacheDir = path$1.join(os.homedir(), '.jsdoc-api/cache')
224+
225+
static createTmpDirs () {
226+
/* No longer using os.tmpdir(). See: https://github.com/jsdoc2md/jsdoc-api/issues/19 */
227+
fs$1.mkdirSync(TempFile.tempFileDir, { recursive: true });
228+
fs$1.mkdirSync(TempFile.cacheDir, { recursive: true });
229+
}
222230
}
223231

224232
function getDefaultExportFromCjs (x) {
@@ -4822,12 +4830,12 @@ class Minipass extends node_events.EventEmitter {
48224830
}
48234831
}
48244832

4825-
const realpathSync = fs$1.realpathSync.native;
4833+
const realpathSync = fs.realpathSync.native;
48264834
const defaultFS = {
4827-
lstatSync: fs$1.lstatSync,
4828-
readdir: fs$1.readdir,
4829-
readdirSync: fs$1.readdirSync,
4830-
readlinkSync: fs$1.readlinkSync,
4835+
lstatSync: fs.lstatSync,
4836+
readdir: fs.readdir,
4837+
readdirSync: fs.readdirSync,
4838+
readlinkSync: fs.readlinkSync,
48314839
realpathSync,
48324840
promises: {
48334841
lstat: promises.lstat,
@@ -4837,7 +4845,7 @@ const defaultFS = {
48374845
},
48384846
};
48394847
// if they just gave us require('fs') then use our default
4840-
const fsFromOption = (fsOption) => !fsOption || fsOption === defaultFS || fsOption === actualFS__namespace ?
4848+
const fsFromOption = (fsOption) => !fsOption || fsOption === defaultFS || fsOption === fs__namespace ?
48414849
defaultFS
48424850
: {
48434851
...defaultFS,
@@ -8131,7 +8139,7 @@ class FileSet {
81318139
files = arrayify(files);
81328140
for (const file of files) {
81338141
try {
8134-
const stat = await actualFS.promises.stat(file);
8142+
const stat = await fs$1.promises.stat(file);
81358143
if (stat.isFile() && !this.files.includes(file)) {
81368144
this.files.push(file);
81378145
} else if (stat.isDirectory() && !this.dirs.includes(file)) {
@@ -8205,14 +8213,14 @@ class FileSet {
82058213
*/
82068214
function walkBack (startAt, lookingFor) {
82078215
startAt = path$1.resolve(startAt);
8208-
if (fs$1.existsSync(startAt) && fs$1.statSync(startAt).isDirectory()) {
8216+
if (fs.existsSync(startAt) && fs.statSync(startAt).isDirectory()) {
82098217
const dirs = path$1.resolve(startAt).split(path$1.sep);
82108218
for (let i = 0; i < dirs.length; i++) {
82118219
const basedir = i < dirs.length - 1
82128220
? dirs.slice(0, dirs.length - i).join(path$1.sep)
82138221
: path$1.sep;
82148222

8215-
if (fs$1.existsSync(path$1.join(basedir, lookingFor))) {
8223+
if (fs.existsSync(path$1.join(basedir, lookingFor))) {
82168224
return path$1.join(basedir, lookingFor)
82178225
}
82188226
}
@@ -8279,6 +8287,7 @@ class JsdocCommand {
82798287
try {
82808288
result = await this.getOutput();
82818289
} finally {
8290+
/* run even if getOutput fails */
82828291
if (this.tempFiles) {
82838292
for (const tempFile of this.tempFiles) {
82848293
tempFile.delete();
@@ -8349,8 +8358,16 @@ const exec = util.promisify(cp.exec);
83498358

83508359
class Explain extends JsdocCommand {
83518360
async getOutput () {
8352-
if (this.options.cache && !this.options.source) {
8353-
return this.readCache().catch(this._runJsdoc.bind(this))
8361+
if (this.options.cache && !this.options.source.length) {
8362+
try {
8363+
return await this.readCache()
8364+
} catch (err) {
8365+
if (err.code === 'ENOENT') {
8366+
return this._runJsdoc()
8367+
} else {
8368+
throw err
8369+
}
8370+
}
83548371
} else {
83558372
return this._runJsdoc()
83568373
}
@@ -8363,7 +8380,7 @@ class Explain extends JsdocCommand {
83638380

83648381
let jsdocOutput = { stdout: '', stderr: '' };
83658382
try {
8366-
jsdocOutput = await exec(cmd);
8383+
jsdocOutput = await exec(cmd, { maxBuffer: 1024 * 1024 * 100 }); /* 100MB */
83678384
const explainOutput = JSON.parse(jsdocOutput.stdout);
83688385
if (this.options.cache) {
83698386
await this.cache.write(this.cacheKey, explainOutput);
@@ -8380,7 +8397,12 @@ class Explain extends JsdocCommand {
83808397

83818398
async readCache () {
83828399
if (this.cache) {
8383-
const promises = this.inputFileSet.files.map(file => fs.readFile(file, 'utf8'));
8400+
/* Create the cache key then check the cache for a match, returning pre-generated output if so.
8401+
The current cache key is a union of the input file names plus their content - this could be expensive when processing a lot of files.
8402+
*/
8403+
const promises = this.inputFileSet.files.map(file => {
8404+
return fs$1.promises.readFile(file, 'utf8')
8405+
});
83848406
const contents = await Promise.all(promises);
83858407
this.cacheKey = contents.concat(this.inputFileSet.files);
83868408
return this.cache.read(this.cacheKey)
@@ -8408,16 +8430,17 @@ class Render extends JsdocCommand {
84088430
* @typicalname jsdoc
84098431
*/
84108432

8433+
TempFile.createTmpDirs();
8434+
84118435
/**
84128436
* @external cache-point
84138437
* @see https://github.com/75lb/cache-point
84148438
*/
8415-
84168439
/**
84178440
* The [cache-point](https://github.com/75lb/cache-point) instance used when `cache: true` is specified on `.explain()`.
84188441
* @type {external:cache-point}
84198442
*/
8420-
const cache = new Cache({ dir: path$1.join(os$1.tmpdir(), 'jsdoc-api') });
8443+
const cache = new Cache({ dir: TempFile.cacheDir });
84218444

84228445
/**
84238446
* @alias module:jsdoc-api
@@ -8459,7 +8482,6 @@ const jsdoc = {
84598482
*/
84608483
class JsdocOptions {
84618484
constructor (options = {}) {
8462-
84638485
/**
84648486
* One or more filenames to process. Either `files`, `source` or `configure` must be supplied.
84658487
* @type {string|string[]}

index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ import Explain from './lib/explain.js'
88
import Render from './lib/render.js'
99
import arrayify from 'array-back'
1010
import os from 'node:os'
11+
import TempFile from './lib/temp-file.js'
12+
13+
TempFile.createTmpDirs()
1114

1215
/**
1316
* @external cache-point
1417
* @see https://github.com/75lb/cache-point
1518
*/
16-
1719
/**
1820
* The [cache-point](https://github.com/75lb/cache-point) instance used when `cache: true` is specified on `.explain()`.
1921
* @type {external:cache-point}
2022
*/
21-
const cache = new Cache({ dir: path.join(os.tmpdir(), 'jsdoc-api') })
23+
const cache = new Cache({ dir: TempFile.cacheDir })
2224

2325
/**
2426
* @alias module:jsdoc-api
@@ -60,7 +62,6 @@ const jsdoc = {
6062
*/
6163
class JsdocOptions {
6264
constructor (options = {}) {
63-
6465
/**
6566
* One or more filenames to process. Either `files`, `source` or `configure` must be supplied.
6667
* @type {string|string[]}

lib/explain.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
import JsdocCommand from './jsdoc-command.js'
2-
import arrayify from 'array-back'
32
import toSpawnArgs from 'object-to-spawn-args'
43
import cp from 'child_process'
54
import util from 'node:util'
5+
import { promises as fs } from 'node:fs'
66
const exec = util.promisify(cp.exec)
77

88
class Explain extends JsdocCommand {
99
async getOutput () {
10-
if (this.options.cache && !this.options.source) {
11-
return this.readCache().catch(this._runJsdoc.bind(this))
10+
if (this.options.cache && !this.options.source.length) {
11+
try {
12+
return await this.readCache()
13+
} catch (err) {
14+
if (err.code === 'ENOENT') {
15+
return this._runJsdoc()
16+
} else {
17+
throw err
18+
}
19+
}
1220
} else {
1321
return this._runJsdoc()
1422
}
@@ -21,7 +29,7 @@ class Explain extends JsdocCommand {
2129

2230
let jsdocOutput = { stdout: '', stderr: '' }
2331
try {
24-
jsdocOutput = await exec(cmd)
32+
jsdocOutput = await exec(cmd, { maxBuffer: 1024 * 1024 * 100 }) /* 100MB */
2533
const explainOutput = JSON.parse(jsdocOutput.stdout)
2634
if (this.options.cache) {
2735
await this.cache.write(this.cacheKey, explainOutput)
@@ -38,7 +46,12 @@ class Explain extends JsdocCommand {
3846

3947
async readCache () {
4048
if (this.cache) {
41-
const promises = this.inputFileSet.files.map(file => fs.readFile(file, 'utf8'))
49+
/* Create the cache key then check the cache for a match, returning pre-generated output if so.
50+
The current cache key is a union of the input file names plus their content - this could be expensive when processing a lot of files.
51+
*/
52+
const promises = this.inputFileSet.files.map(file => {
53+
return fs.readFile(file, 'utf8')
54+
})
4255
const contents = await Promise.all(promises)
4356
this.cacheKey = contents.concat(this.inputFileSet.files)
4457
return this.cache.read(this.cacheKey)

lib/jsdoc-command.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import TempFile from './temp-file.js'
44
import FileSet from 'file-set'
55
import assert from 'assert'
66
import walkBack from 'walk-back'
7-
import { promises as fs } from 'node:fs'
87
import currentModulePaths from 'current-module-paths'
98

109
const { __dirname } = currentModulePaths(import.meta.url)
@@ -57,6 +56,7 @@ class JsdocCommand {
5756
try {
5857
result = await this.getOutput()
5958
} finally {
59+
/* run even if getOutput fails */
6060
if (this.tempFiles) {
6161
for (const tempFile of this.tempFiles) {
6262
tempFile.delete()

lib/temp-file.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import fs from 'fs'
1+
import fs from 'node:fs'
22
import os from 'os'
33
import crypto from 'crypto'
44
import path from 'path'
55

66
class TempFile {
77
constructor (source) {
8-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'jsdoc-api-'))
9-
this.path = path.join(tempDir, crypto.randomBytes(6).toString('hex') + '.js')
8+
this.path = path.join(TempFile.tempFileDir, crypto.randomBytes(6).toString('hex') + '.js')
109
fs.writeFileSync(this.path, source)
1110
}
1211

@@ -17,6 +16,15 @@ class TempFile {
1716
// already deleted
1817
}
1918
}
19+
20+
static tempFileDir = path.join(os.homedir(), '.jsdoc-api/temp')
21+
static cacheDir = path.join(os.homedir(), '.jsdoc-api/cache')
22+
23+
static createTmpDirs () {
24+
/* No longer using os.tmpdir(). See: https://github.com/jsdoc2md/jsdoc-api/issues/19 */
25+
fs.mkdirSync(TempFile.tempFileDir, { recursive: true })
26+
fs.mkdirSync(TempFile.cacheDir, { recursive: true })
27+
}
2028
}
2129

2230
export default TempFile

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
"standard": {
5151
"ignore": [
5252
"tmp",
53-
"test/fixture"
53+
"test/fixture",
54+
"dist"
5455
]
5556
},
5657
"files": [

test/caching.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import jsdoc from 'jsdoc-api'
22
import Fixture from './lib/fixture.js'
3-
import { statSync } from 'fs'
3+
import { readdirSync, readFileSync } from 'fs'
44
import { strict as a } from 'assert'
55
import path from 'path'
6-
import { readdirSync, readFileSync } from 'fs'
76

87
/* tests need to run with a maxConcurrency of 1 as `jsdoc.cache` is shared between tests */
98
const [test, only, skip] = [new Map(), new Map(), new Map()]

test/explain.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import jsdoc from 'jsdoc-api'
22
import Fixture from './lib/fixture.js'
3-
import { statSync } from 'fs'
43
import { strict as a } from 'assert'
54
import path from 'path'
65

0 commit comments

Comments
 (0)