diff --git a/cli.js b/cli.js index ce39a942..eb45654d 100644 --- a/cli.js +++ b/cli.js @@ -210,7 +210,7 @@ module.exports = (() => { }; function readPackageJson(filepath) { - const fs = require('jsdoc/fs'); + const fs = require('fs'); try { return stripJsonComments( fs.readFileSync(filepath, 'utf8') ); @@ -280,7 +280,7 @@ module.exports = (() => { scanner = new Scanner(); env.sourceFiles = scanner.scan(env.opts._, - (env.opts.recurse ? env.conf.recurseDepth : undefined), filter); + (env.opts.recurse ? env.conf.source.maxDepth : undefined), filter); } return cli; diff --git a/lib/jsdoc/fs.js b/lib/jsdoc/fs.js deleted file mode 100644 index 45feac3d..00000000 --- a/lib/jsdoc/fs.js +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Extended version of the standard `fs` module. - * @module jsdoc/fs - */ -const fs = require('fs'); -const path = require('path'); -const mkdirp = require('mkdirp'); - -const ls = exports.ls = (dir, recurse, _allFiles, _path) => { - let file; - let files; - let isFile; - - // first pass - if (_path === undefined) { - _allFiles = []; - _path = [dir]; - } - - if (!_path.length) { - return _allFiles; - } - - if (recurse === undefined) { - recurse = 1; - } - - try { - isFile = fs.statSync(dir).isFile(); - } - catch (e) { - isFile = false; - } - if (isFile) { - files = [dir]; - } - else { - files = fs.readdirSync(dir); - } - - for (let i = 0, l = files.length; i < l; i++) { - file = String(files[i]); - - // skip dot files - if (file.match(/^\.[^./\\]/)) { - continue; - } - - if ( fs.statSync(path.join(_path.join('/'), file)).isDirectory() ) { - // it's a directory - _path.push(file); - - if (_path.length - 1 < recurse) { - ls(_path.join('/'), recurse, _allFiles, _path); - } - _path.pop(); - } - else { - // it's a file - _allFiles.push( path.normalize(path.join(_path.join('/'), file)) ); - } - } - - return _allFiles; -}; - -exports.toDir = _path => { - let isDirectory; - - _path = path.normalize(_path); - - try { - isDirectory = fs.statSync(_path).isDirectory(); - } - catch (e) { - isDirectory = false; - } - - if (isDirectory) { - return _path; - } else { - return path.dirname(_path); - } -}; - -exports.mkPath = _path => { - if ( Array.isArray(_path) ) { - _path = _path.join(''); - } - - mkdirp.sync(_path); -}; - -exports.copyFileSync = (inFile, outDir = '', fileName) => { - fileName = fileName || path.basename(inFile); - - fs.copyFileSync(inFile, path.join(outDir, fileName)); -}; - -const alwaysOverride = { - 'copyFileSync': true -}; - -Object.keys(fs).forEach(member => { - if (!alwaysOverride[member]) { - exports[member] = fs[member]; - } -}); - diff --git a/lib/jsdoc/readme.js b/lib/jsdoc/readme.js index 8da87c32..5205d544 100644 --- a/lib/jsdoc/readme.js +++ b/lib/jsdoc/readme.js @@ -3,7 +3,7 @@ * @module jsdoc/readme */ const env = require('jsdoc/env'); -const fs = require('jsdoc/fs'); +const fs = require('fs'); const markdown = require('jsdoc/util/markdown'); /** diff --git a/lib/jsdoc/src/parser.js b/lib/jsdoc/src/parser.js index f3fa9854..204a1715 100644 --- a/lib/jsdoc/src/parser.js +++ b/lib/jsdoc/src/parser.js @@ -3,7 +3,7 @@ */ const _ = require('lodash'); const EventEmitter = require('events').EventEmitter; -const fs = require('jsdoc/fs'); +const fs = require('fs'); const jsdoc = { doclet: require('jsdoc/doclet'), env: require('jsdoc/env'), diff --git a/lib/jsdoc/src/scanner.js b/lib/jsdoc/src/scanner.js index 52ad9d63..a593491f 100644 --- a/lib/jsdoc/src/scanner.js +++ b/lib/jsdoc/src/scanner.js @@ -4,7 +4,8 @@ */ const EventEmitter = require('events').EventEmitter; const env = require('jsdoc/env'); -const fs = require('jsdoc/fs'); +const fs = require('fs'); +const ls = require('@jsdoc/util').fs.ls; const logger = require('@jsdoc/logger'); const path = require('jsdoc/path'); @@ -42,7 +43,7 @@ class Scanner extends EventEmitter { filePaths.push(filepath); } else { - filePaths = filePaths.concat( fs.ls(filepath, depth) ); + filePaths = filePaths.concat( ls(filepath, { depth: depth }) ); } }); diff --git a/lib/jsdoc/tutorial/resolver.js b/lib/jsdoc/tutorial/resolver.js index 1f206d26..03ee8cf1 100644 --- a/lib/jsdoc/tutorial/resolver.js +++ b/lib/jsdoc/tutorial/resolver.js @@ -2,8 +2,9 @@ * @module jsdoc/tutorial/resolver */ const env = require('jsdoc/env'); -const fs = require('jsdoc/fs'); +const fs = require('fs'); const logger = require('@jsdoc/logger'); +const ls = require('@jsdoc/util').fs.ls; const path = require('path'); const stripBom = require('strip-bom'); const tutorial = require('jsdoc/tutorial'); @@ -97,7 +98,9 @@ exports.addTutorial = current => { exports.load = filepath => { let content; let current; - const files = fs.ls(filepath, env.opts.recurse ? env.conf.recurseDepth : undefined); + const files = ls(filepath, { + depth: env.opts.recurse ? env.conf.source.maxDepth : 0 + }); let name; let match; let type; diff --git a/package-lock.json b/package-lock.json index 2cc02e30..c33cd4e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -332,8 +332,10 @@ "@jsdoc/template-original": { "version": "file:packages/jsdoc-template-original", "requires": { + "@jsdoc/util": "file:packages/jsdoc-util", "code-prettify": "^0.1.0", "color-themes-for-google-code-prettify": "^2.0.4", + "mkdirp": "^0.5.1", "open-sans-fonts": "^1.5.0", "taffydb": "2.6.2" }, @@ -352,7 +354,10 @@ "version": "file:packages/jsdoc-template-silent" }, "@jsdoc/util": { - "version": "file:packages/jsdoc-util" + "version": "file:packages/jsdoc-util", + "requires": { + "klaw-sync": "^6.0.0" + } }, "@lerna/add": { "version": "3.10.6", @@ -4841,8 +4846,7 @@ "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, "gulp": { "version": "4.0.0", @@ -5747,7 +5751,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", - "dev": true, "requires": { "graceful-fs": "^4.1.11" } diff --git a/package.json b/package.json index 98ec174c..9a1662c3 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "markdown-it": "~8.4.2", "markdown-it-anchor": "~5.0.2", "marked": "~0.6.0", - "mkdirp": "~0.5.1", "requizzle": "~0.2.1", "strip-bom": "^3.0.0", "taffydb": "2.6.2" diff --git a/packages/jsdoc-config/lib/defaults.js b/packages/jsdoc-config/lib/defaults.js index 57c10fdf..254447e5 100644 --- a/packages/jsdoc-config/lib/defaults.js +++ b/packages/jsdoc-config/lib/defaults.js @@ -15,9 +15,6 @@ module.exports = { * The JSDoc plugins to load. */ plugins: [], - // TODO(hegemonic): move to `source` or remove - recurseDepth: 10, - // TODO(hegemonic): switch to glob patterns /** * Settings for loading and parsing source files. */ @@ -36,6 +33,11 @@ module.exports = { * processed. */ includePattern: '.+\\.js(doc|x)?$', + /** + * The maximum number of levels to recursively search for source files, or `-1` to recurse + * as many levels as possible. + */ + maxDepth: -1, /** * The type of source file. In general, you should use the value `module`. If none of your * source files use ECMAScript >=2015 syntax, you can use the value `script`. diff --git a/packages/jsdoc-template-original/index.js b/packages/jsdoc-template-original/index.js index c2b87c4c..4c73302b 100644 --- a/packages/jsdoc-template-original/index.js +++ b/packages/jsdoc-template-original/index.js @@ -1,8 +1,10 @@ const _ = require('lodash'); const env = require('jsdoc/env'); -const fs = require('jsdoc/fs'); +const fs = require('fs'); const helper = require('jsdoc/util/templateHelper'); const logger = require('@jsdoc/logger'); +const ls = require('@jsdoc/util').fs.ls; +const mkdirpSync = require('mkdirp').sync; const path = require('jsdoc/path'); const taffy = require('taffydb').taffy; const Template = require('./lib/template'); @@ -404,6 +406,12 @@ function buildNav(members) { return nav; } +function sourceToDestination(parentDir, sourcePath, destDir) { + const relativeSource = path.relative(parentDir, sourcePath); + + return path.resolve(path.join(destDir, relativeSource)); +} + /** @param {object} data @param {object} opts @@ -506,44 +514,44 @@ exports.publish = ({doclets, tutorials}, opts) => { if (packageInfo && packageInfo.name) { outdir = path.join( outdir, packageInfo.name, (packageInfo.version || '') ); } - fs.mkPath(outdir); + mkdirpSync(outdir); // copy the template's built-in static files to outdir fromDir = path.join(templatePath, 'static'); - staticFiles = fs.ls(fromDir, 3); + staticFiles = ls(fromDir); staticFiles.forEach(fileName => { - const toDir = fs.toDir(fileName.replace(fromDir, outdir)); + const toPath = sourceToDestination(fromDir, fileName, outdir); - fs.mkPath(toDir); - fs.copyFileSync(fileName, toDir); + mkdirpSync(path.dirname(toPath)); + fs.copyFileSync(fileName, toPath); }); // copy the fonts used by the template to outdir - staticFiles = fs.ls(path.join(templatePath, 'node_modules/open-sans-fonts/open-sans'), 3); + staticFiles = ls(path.join(templatePath, 'node_modules/open-sans-fonts/open-sans')); staticFiles.forEach(fileName => { - const toDir = path.join(outdir, 'fonts'); + const toPath = path.join(outdir, 'fonts', path.basename(fileName)); if (FONT_NAMES.includes(path.parse(fileName).name)) { - fs.mkPath(toDir); - fs.copyFileSync(fileName, toDir); + mkdirpSync(path.dirname(toPath)); + fs.copyFileSync(fileName, toPath); } }); // copy the prettify script to outdir PRETTIFIER_SCRIPT_FILES.forEach(fileName => { - const toDir = path.join(outdir, 'scripts'); + const toPath = path.join(outdir, 'scripts', path.basename(fileName)); fs.copyFileSync( path.join(templatePath, 'node_modules/code-prettify/loader', fileName), - toDir + toPath ); }); // copy the prettify CSS to outdir PRETTIFIER_CSS_FILES.forEach(fileName => { - const toDir = path.join(outdir, 'styles'); + const toPath = path.join(outdir, 'styles', path.basename(fileName)); fs.copyFileSync( path.join( @@ -556,7 +564,7 @@ exports.publish = ({doclets, tutorials}, opts) => { 'themes', fileName ), - toDir + toPath ); }); @@ -577,11 +585,10 @@ exports.publish = ({doclets, tutorials}, opts) => { extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); extraStaticFiles.forEach(fileName => { - const sourcePath = fs.toDir(filePath); - const toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); + const toPath = sourceToDestination(fromDir, fileName, outdir); - fs.mkPath(toDir); - fs.copyFileSync(fileName, toDir); + mkdirpSync(path.dirname(toPath)); + fs.copyFileSync(fileName, toPath); }); }); } diff --git a/packages/jsdoc-template-original/lib/template.js b/packages/jsdoc-template-original/lib/template.js index ae224174..9021404e 100644 --- a/packages/jsdoc-template-original/lib/template.js +++ b/packages/jsdoc-template-original/lib/template.js @@ -2,7 +2,7 @@ * Wrapper for `_.template` to load templates from files. */ const _ = require('lodash'); -const fs = require('jsdoc/fs'); +const fs = require('fs'); const path = require('path'); /** diff --git a/packages/jsdoc-template-original/package-lock.json b/packages/jsdoc-template-original/package-lock.json index a94b1fc7..a54f15fc 100644 --- a/packages/jsdoc-template-original/package-lock.json +++ b/packages/jsdoc-template-original/package-lock.json @@ -14,6 +14,19 @@ "resolved": "https://registry.npmjs.org/color-themes-for-google-code-prettify/-/color-themes-for-google-code-prettify-2.0.4.tgz", "integrity": "sha1-3urPZX/WhXaGR1TU5IbXjf2x54Q=" }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, "open-sans-fonts": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/open-sans-fonts/-/open-sans-fonts-1.5.0.tgz", diff --git a/packages/jsdoc-template-original/package.json b/packages/jsdoc-template-original/package.json index ff98db99..6794778d 100644 --- a/packages/jsdoc-template-original/package.json +++ b/packages/jsdoc-template-original/package.json @@ -21,8 +21,10 @@ }, "homepage": "https://github.com/jsdoc3/jsdoc#readme", "dependencies": { + "@jsdoc/util": "file:../jsdoc-util", "code-prettify": "^0.1.0", "color-themes-for-google-code-prettify": "^2.0.4", + "mkdirp": "^0.5.1", "open-sans-fonts": "^1.5.0", "taffydb": "2.6.2" }, diff --git a/packages/jsdoc-util/index.js b/packages/jsdoc-util/index.js index bc09cc39..3b7791ff 100644 --- a/packages/jsdoc-util/index.js +++ b/packages/jsdoc-util/index.js @@ -3,5 +3,9 @@ */ const cast = require('./lib/cast'); +const fs = require('./lib/fs'); -exports.cast = cast; +module.exports = { + cast: cast, + fs: fs +}; diff --git a/packages/jsdoc-util/lib/fs.js b/packages/jsdoc-util/lib/fs.js new file mode 100644 index 00000000..3ed569a0 --- /dev/null +++ b/packages/jsdoc-util/lib/fs.js @@ -0,0 +1,19 @@ +/** + * @module @jsdoc/util/lib/fs + */ + +const _ = require('lodash'); +const klaw = require('klaw-sync'); +const path = require('path'); + +exports.ls = ((dir, opts = {}) => { + const depth = _.has(opts, 'depth') ? opts.depth : -1; + + const files = klaw(dir, { + depthLimit: depth, + filter: (f => !path.basename(f.path).startsWith('.')), + nodir: true + }); + + return files.map(f => f.path); +}); diff --git a/packages/jsdoc-util/package-lock.json b/packages/jsdoc-util/package-lock.json index 47467c1b..5a4fa8eb 100644 --- a/packages/jsdoc-util/package-lock.json +++ b/packages/jsdoc-util/package-lock.json @@ -1,5 +1,32 @@ { "name": "@jsdoc/util", "version": "1.0.0", - "lockfileVersion": 1 + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "requires": { + "graceful-fs": "^4.1.11" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "mock-fs": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.7.0.tgz", + "integrity": "sha512-WlQNtUlzMRpvLHf8dqeUmNqfdPjGY29KrJF50Ldb4AcL+vQeR8QH3wQcFMgrhTwb1gHjZn9xggho+84tBskLgA==", + "dev": true + } + } } diff --git a/packages/jsdoc-util/package.json b/packages/jsdoc-util/package.json index 0adc3aeb..c26b1a59 100644 --- a/packages/jsdoc-util/package.json +++ b/packages/jsdoc-util/package.json @@ -18,5 +18,12 @@ "bugs": { "url": "https://github.com/jsdoc3/jsdoc/issues" }, - "homepage": "https://github.com/jsdoc3/jsdoc#readme" + "homepage": "https://github.com/jsdoc3/jsdoc#readme", + "dependencies": { + "klaw-sync": "^6.0.0", + "lodash": "^4.17.11" + }, + "devDependencies": { + "mock-fs": "^4.7.0" + } } diff --git a/packages/jsdoc-util/test/specs/index.js b/packages/jsdoc-util/test/specs/index.js index d7e48a96..2963b7bf 100644 --- a/packages/jsdoc-util/test/specs/index.js +++ b/packages/jsdoc-util/test/specs/index.js @@ -9,6 +9,10 @@ describe('@jsdoc/util', () => { expect(util.cast).toBeFunction(); }); + it('has an fs object', () => { + expect(util.fs).toBeObject(); + }); + describe('cast', () => { it('is ./lib/cast', () => { const cast = require('../../lib/cast'); @@ -16,4 +20,12 @@ describe('@jsdoc/util', () => { expect(util.cast).toBe(cast); }); }); + + describe('fs', () => { + it('is ./lib/fs', () => { + const fs = require('../../lib/fs'); + + expect(util.fs).toBe(fs); + }); + }); }); diff --git a/packages/jsdoc-util/test/specs/lib/fs.js b/packages/jsdoc-util/test/specs/lib/fs.js new file mode 100644 index 00000000..56732546 --- /dev/null +++ b/packages/jsdoc-util/test/specs/lib/fs.js @@ -0,0 +1,71 @@ +describe('@jsdoc/util/lib/fs', () => { + const mockFs = require('mock-fs'); + const fsUtil = require('../../../lib/fs'); + const path = require('path'); + + afterEach(() => mockFs.restore()); + + it('has an ls method', () => { + expect(fsUtil.ls).toBeFunction(); + }); + + describe('ls', () => { + beforeEach(() => { + mockFs({ + head: { + eyes: '', + ears: '', + mouth: '', + nose: '', + shoulders: { + knees: { + meniscus: '', + toes: { + phalanx: '', + '.big-toe-phalanx': '' + } + } + } + } + }); + }); + + const cwd = process.cwd(); + + function resolvePaths(files) { + return files.map(f => path.join(cwd, f)).sort(); + } + + const allFiles = resolvePaths([ + 'head/eyes', + 'head/ears', + 'head/mouth', + 'head/nose', + 'head/shoulders/knees/meniscus', + 'head/shoulders/knees/toes/phalanx' + ]); + + it('gets all non-hidden files from all levels by default', () => { + const files = fsUtil.ls(cwd).sort(); + + expect(files).toEqual(allFiles); + }); + + it('limits recursion depth when asked', () => { + const files = fsUtil.ls(cwd, { depth: 1 }).sort(); + + expect(files).toEqual(resolvePaths([ + 'head/eyes', + 'head/ears', + 'head/mouth', + 'head/nose' + ])); + }); + + it('treats a depth of -1 as infinite', () => { + const files = fsUtil.ls('head', { depth: -1 }).sort(); + + expect(files).toEqual(allFiles); + }); + }); +}); diff --git a/plugins/partial.js b/plugins/partial.js index 83c84f20..e3ea0ccd 100644 --- a/plugins/partial.js +++ b/plugins/partial.js @@ -4,7 +4,7 @@ * @module plugins/partial */ const env = require('jsdoc/env'); -const fs = require('jsdoc/fs'); +const fs = require('fs'); const path = require('path'); exports.handlers = { diff --git a/test/helpers/jsdoc.js b/test/helpers/jsdoc.js index 354cf561..6fb44448 100644 --- a/test/helpers/jsdoc.js +++ b/test/helpers/jsdoc.js @@ -1,4 +1,4 @@ -const fs = require('jsdoc/fs'); +const fs = require('fs'); const jsdoc = { augment: require('jsdoc/augment'), doclet: require('jsdoc/doclet'), diff --git a/test/specs/jsdoc/src/parser.js b/test/specs/jsdoc/src/parser.js index 8e440458..5b7ea521 100644 --- a/test/specs/jsdoc/src/parser.js +++ b/test/specs/jsdoc/src/parser.js @@ -2,7 +2,7 @@ describe('jsdoc/src/parser', () => { const _ = require('lodash'); const env = require('jsdoc/env'); - const fs = require('jsdoc/fs'); + const fs = require('fs'); const handlers = require('jsdoc/src/handlers'); const logger = require('@jsdoc/logger'); const parser = require('jsdoc/src/parser');