diff --git a/cli.js b/cli.js index 082318f3..138f517a 100644 --- a/cli.js +++ b/cli.js @@ -266,21 +266,21 @@ module.exports = (() => { // TODO: docs cli.scanFiles = () => { - const Filter = require('jsdoc/src/filter').Filter; - const Scanner = require('jsdoc/src/scanner').Scanner; + const makeFilter = require('@jsdoc/util/lib/path').makeFilter; + const walkSync = require('@jsdoc/util/lib/fs').walkSync; let filter; - let scanner; env.opts._ = buildSourceList(); // are there any files to scan and parse? if (env.conf.source && env.opts._.length) { - filter = new Filter(env.conf.source); - scanner = new Scanner(); + filter = makeFilter(env.conf.source); - env.sourceFiles = scanner.scan(env.opts._, - (env.opts.recurse ? env.conf.source.maxDepth : undefined), filter); + env.sourceFiles = walkSync(env.opts._, { + depth: env.opts.recurse ? env.conf.source.maxDepth : undefined, + filter: filter + }); } return cli; diff --git a/lib/jsdoc/src/astnode.js b/lib/jsdoc/src/astnode.js index 140d8793..b9afa57b 100644 --- a/lib/jsdoc/src/astnode.js +++ b/lib/jsdoc/src/astnode.js @@ -1,6 +1,6 @@ // TODO: docs /** @module jsdoc/src/astnode */ -const cast = require('@jsdoc/util').cast; +const cast = require('@jsdoc/util/lib/cast'); const env = require('jsdoc/env'); const name = require('jsdoc/name'); const Syntax = require('@jsdoc/syntax'); diff --git a/lib/jsdoc/src/filter.js b/lib/jsdoc/src/filter.js deleted file mode 100644 index ac91719a..00000000 --- a/lib/jsdoc/src/filter.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @module jsdoc/src/filter - */ -const env = require('jsdoc/env'); -const path = require('path'); - -function makeRegExp(config) { - let regExp = null; - - if (config) { - regExp = (typeof config === 'string') ? new RegExp(config) : config; - } - - return regExp; -} - -/** - * @alias module:jsdoc/src/filter.Filter - */ -class Filter { - /** - * @param {Object} opts - * @param {string[]} opts.exclude - Specific files to exclude. - * @param {(string|RegExp)} opts.includePattern - * @param {(string|RegExp)} opts.excludePattern - */ - constructor({exclude, includePattern, excludePattern}) { - this.exclude = exclude && Array.isArray(exclude) ? - exclude.map($ => path.resolve(env.pwd, $)) : - null; - this.includePattern = makeRegExp(includePattern); - this.excludePattern = makeRegExp(excludePattern); - } - - /** - * @param {string} filepath - The filepath to check. - * @returns {boolean} Should the given file be included? - */ - isIncluded(filepath) { - let included = true; - - filepath = path.resolve(env.pwd, filepath); - - if ( this.includePattern && !this.includePattern.test(filepath) ) { - included = false; - } - - if ( this.excludePattern && this.excludePattern.test(filepath) ) { - included = false; - } - - if (this.exclude) { - this.exclude.forEach(exclude => { - if ( filepath.indexOf(exclude) === 0 ) { - included = false; - } - }); - } - - return included; - } -} -exports.Filter = Filter; diff --git a/lib/jsdoc/src/scanner.js b/lib/jsdoc/src/scanner.js deleted file mode 100644 index 4961b920..00000000 --- a/lib/jsdoc/src/scanner.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @module jsdoc/src/scanner - */ -const EventEmitter = require('events').EventEmitter; -const env = require('jsdoc/env'); -const fs = require('fs'); -const ls = require('@jsdoc/util').fs.ls; -const logger = require('@jsdoc/logger'); -const path = require('path'); - -/** - * @extends module:events.EventEmitter - */ -class Scanner extends EventEmitter { - constructor() { - super(); - } - - /** - * Recursively searches the given searchPaths for js files. - * @param {Array.} searchPaths - * @param {number} [depth] - * @fires sourceFileFound - */ - scan(searchPaths = [], depth = 1, filter) { - let currentFile; - let filePaths = []; - - searchPaths.forEach($ => { - const filepath = path.resolve( env.pwd, decodeURIComponent($) ); - - try { - currentFile = fs.statSync(filepath); - } - catch (e) { - logger.error('Unable to find the source file or directory %s', filepath); - - return; - } - - if ( currentFile.isFile() ) { - filePaths.push(filepath); - } - else { - filePaths = filePaths.concat( ls(filepath, { depth: depth }) ); - } - }); - - filePaths = filePaths.filter($ => filter.isIncluded($)); - - filePaths = filePaths.filter($ => { - const e = { fileName: $ }; - - this.emit('sourceFileFound', e); - - return !e.defaultPrevented; - }); - - return filePaths; - } -} -exports.Scanner = Scanner; diff --git a/lib/jsdoc/tag/dictionary/definitions.js b/lib/jsdoc/tag/dictionary/definitions.js index d930f9c5..d8bb7599 100644 --- a/lib/jsdoc/tag/dictionary/definitions.js +++ b/lib/jsdoc/tag/dictionary/definitions.js @@ -3,7 +3,7 @@ * @module jsdoc/tag/dictionary/definitions */ const _ = require('lodash'); -const commonPrefix = require('@jsdoc/util').path.commonPrefix; +const commonPrefix = require('@jsdoc/util/lib/path').commonPrefix; const jsdoc = { env: require('jsdoc/env'), name: require('jsdoc/name'), diff --git a/lib/jsdoc/tag/type.js b/lib/jsdoc/tag/type.js index 7eddffc8..fbe1f65b 100644 --- a/lib/jsdoc/tag/type.js +++ b/lib/jsdoc/tag/type.js @@ -1,7 +1,7 @@ /** * @module jsdoc/tag/type */ -const cast = require('@jsdoc/util').cast; +const cast = require('@jsdoc/util/lib/cast'); const catharsis = require('catharsis'); const jsdoc = { name: require('jsdoc/name'), diff --git a/lib/jsdoc/tutorial/resolver.js b/lib/jsdoc/tutorial/resolver.js index 03ee8cf1..ba8ac74b 100644 --- a/lib/jsdoc/tutorial/resolver.js +++ b/lib/jsdoc/tutorial/resolver.js @@ -4,7 +4,7 @@ const env = require('jsdoc/env'); const fs = require('fs'); const logger = require('@jsdoc/logger'); -const ls = require('@jsdoc/util').fs.ls; +const lsSync = require('@jsdoc/util/lib/fs').lsSync; const path = require('path'); const stripBom = require('strip-bom'); const tutorial = require('jsdoc/tutorial'); @@ -98,7 +98,7 @@ exports.addTutorial = current => { exports.load = filepath => { let content; let current; - const files = ls(filepath, { + const files = lsSync(filepath, { depth: env.opts.recurse ? env.conf.source.maxDepth : 0 }); let name; diff --git a/package-lock.json b/package-lock.json index 74ae0f53..6a525802 100644 --- a/package-lock.json +++ b/package-lock.json @@ -335,6 +335,7 @@ "@jsdoc/template-original": { "version": "file:packages/jsdoc-template-original", "requires": { + "@jsdoc/logger": "file:packages/jsdoc-logger", "@jsdoc/util": "file:packages/jsdoc-util", "code-prettify": "^0.1.0", "color-themes-for-google-code-prettify": "^2.0.4", diff --git a/packages/jsdoc-cli/lib/args.js b/packages/jsdoc-cli/lib/args.js index 19c15fed..9741149a 100644 --- a/packages/jsdoc-cli/lib/args.js +++ b/packages/jsdoc-cli/lib/args.js @@ -3,7 +3,7 @@ */ const _ = require('lodash'); -const cast = require('@jsdoc/util').cast; +const cast = require('@jsdoc/util/lib/cast'); const querystring = require('querystring'); const yargs = require('yargs-parser'); diff --git a/packages/jsdoc-template-original/index.js b/packages/jsdoc-template-original/index.js index 3cf69e83..259c7e38 100644 --- a/packages/jsdoc-template-original/index.js +++ b/packages/jsdoc-template-original/index.js @@ -1,10 +1,10 @@ const _ = require('lodash'); -const commonPrefix = require('@jsdoc/util').path.commonPrefix; +const commonPrefix = require('@jsdoc/util/lib/path').commonPrefix; const env = require('jsdoc/env'); const fs = require('fs'); const helper = require('jsdoc/util/templateHelper'); const logger = require('@jsdoc/logger'); -const ls = require('@jsdoc/util').fs.ls; +const lsSync = require('@jsdoc/util/lib/fs').lsSync; const mkdirpSync = require('mkdirp').sync; const path = require('path'); const taffy = require('taffydb').taffy; @@ -522,7 +522,7 @@ exports.publish = ({doclets, tutorials}, opts) => { // copy the template's built-in static files to outdir fromDir = path.join(templatePath, 'static'); - staticFiles = ls(fromDir); + staticFiles = lsSync(fromDir); staticFiles.forEach(fileName => { const toPath = sourceToDestination(fromDir, fileName, outdir); @@ -532,7 +532,7 @@ exports.publish = ({doclets, tutorials}, opts) => { }); // copy the fonts used by the template to outdir - staticFiles = ls(path.join(templatePath, 'node_modules/open-sans-fonts/open-sans')); + staticFiles = lsSync(path.join(templatePath, 'node_modules/open-sans-fonts/open-sans')); staticFiles.forEach(fileName => { const toPath = path.join(outdir, 'fonts', path.basename(fileName)); diff --git a/packages/jsdoc-util/index.js b/packages/jsdoc-util/index.js deleted file mode 100644 index 97967773..00000000 --- a/packages/jsdoc-util/index.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @module @jsdoc/util - */ - -const cast = require('./lib/cast'); -const fs = require('./lib/fs'); -const path = require('./lib/path'); - -module.exports = { - cast: cast, - fs: fs, - path: path -}; diff --git a/packages/jsdoc-util/lib/fs.js b/packages/jsdoc-util/lib/fs.js old mode 100644 new mode 100755 index 3ed569a0..f114725c --- a/packages/jsdoc-util/lib/fs.js +++ b/packages/jsdoc-util/lib/fs.js @@ -3,17 +3,44 @@ */ const _ = require('lodash'); +const fs = require('fs'); const klaw = require('klaw-sync'); const path = require('path'); -exports.ls = ((dir, opts = {}) => { +const walkSync = exports.walkSync = (filepaths, opts = {}) => { const depth = _.has(opts, 'depth') ? opts.depth : -1; + let filter; + let found = []; - const files = klaw(dir, { - depthLimit: depth, - filter: (f => !path.basename(f.path).startsWith('.')), - nodir: true + if (typeof filepaths === 'string') { + filepaths = [filepaths]; + } + + if (typeof opts.filter === 'function') { + filter = f => opts.filter.call(null, f.path); + } + + filepaths.forEach(f => { + f = path.resolve(f); + + if (fs.statSync(f).isFile()) { + found.push({ path: f }); + } else { + found = found.concat( + klaw(f, { + depthLimit: depth, + filter: filter, + nodir: true + }) + ); + } }); - return files.map(f => f.path); + return found.map(f => f.path); +}; + +exports.lsSync = (dir, opts = {}) => walkSync(dir, { + depth: opts.depth, + filter: (f => !path.basename(f).startsWith('.')), + nodir: true }); diff --git a/packages/jsdoc-util/lib/path.js b/packages/jsdoc-util/lib/path.js old mode 100644 new mode 100755 index dda61ad2..97ec1d3c --- a/packages/jsdoc-util/lib/path.js +++ b/packages/jsdoc-util/lib/path.js @@ -1,4 +1,6 @@ /** + * Utility methods for working with filepaths. + * * @module @jsdoc/util/lib/path */ @@ -7,7 +9,7 @@ const path = require('path'); function prefixReducer({cwd, previousPath}, current) { let currentPath; - // if previousPath is a zero-length array, there's no common prefix; move along + // If previousPath is a zero-length array, there's no common prefix. if (Array.isArray(previousPath) && !previousPath.length) { return previousPath; } @@ -15,10 +17,10 @@ function prefixReducer({cwd, previousPath}, current) { currentPath = path.resolve(cwd, current).split(path.sep) || []; if (previousPath && currentPath.length) { - // remove chunks that exceed the previous path's length + // Remove chunks that exceed the previous path's length. currentPath = currentPath.slice(0, previousPath.length); - // if a chunk doesn't match the previous path, remove everything from that chunk on + // If a chunk doesn't match the previous path, remove that chunk and the following chunks. for (let i = 0, l = currentPath.length; i < l; i++) { if (currentPath[i] !== previousPath[i]) { currentPath.splice(i, currentPath.length - i); @@ -70,3 +72,86 @@ exports.commonPrefix = (paths = []) => { return segments.join(path.sep); }; + +/** + * A filter function that is compatible with `Array#map` and similar functions. + * + * @typedef module:@jsdoc/util/lib/path~filter + * @type {function} + * @param {string} filepath - The filepath to test. The filter function resolves the filepath, + * relative to the current working directory, before testing it. + * @returns {boolean} `true` if the filepath meets the conditions of the filter, or `false` if the + * filepath does not meet the conditions. + */ + +function makeRegExp(value) { + let regExp = null; + + if (typeof value === 'string') { + regExp = new RegExp(value); + } else if (value instanceof RegExp) { + regExp = value; + } else if (value !== undefined) { + throw new TypeError(`Expected string or RegExp, got ${typeof value}`); + } + + return regExp; +} + +/** + * Create a function that filters filepaths and is suitable for use with methods such as + * {@link Array#filter}. The filter is based on the following inputs: + * + * + An include pattern (a regular expression that matches filepaths that should pass the filter) + * + An exclude pattern (a regular expression that matches filepaths that should not pass the + * filter) + * + An exclude list (an array of values that, if present at the end of a filepath, will always + * cause that filepath to be excluded) + * + * The filter function resolves the filepath, relative to the current working directory, before + * testing it. + * + * @param {Object} opts - Options for the filter function. + * @param {(Array|string)} [opts.exclude] - An array of values (or a single value) that, if + * present at the end of a filepath, will always cause that filepath to be excluded. + * @param {(string|RegExp)} [opts.excludePattern] - A regular expression that matches filepaths to + * exclude, or a string that represents a valid regular expression. + * @param {(string|RegExp)} [opts.includePattern] - A regular expression that matches filepaths to + * include, or a string that represents a valid regular expression. + * @returns {module:@jsdoc/util/lib/path~filter} The filter function. + */ +exports.makeFilter = (({exclude, excludePattern, includePattern}) => { + const cwd = process.cwd(); + const excludeRegExp = makeRegExp(excludePattern); + const includeRegExp = makeRegExp(includePattern); + + if (typeof exclude === 'string') { + exclude = [exclude]; + } else if (exclude !== undefined && exclude !== null && !Array.isArray(exclude)) { + throw new TypeError(`Expected array or string for opts.exclude, got ${typeof exclude}`); + } + + return (filepath => { + let included = true; + + filepath = path.resolve(cwd, filepath); + + if (includeRegExp && !includeRegExp.test(filepath)) { + included = false; + } + + if (excludeRegExp && excludeRegExp.test(filepath)) { + included = false; + } + + if (exclude && included) { + exclude.forEach(value => { + if (included && filepath.endsWith(value)) { + included = false; + } + }); + } + + return included; + }); +}); diff --git a/packages/jsdoc-util/package-lock.json b/packages/jsdoc-util/package-lock.json old mode 100644 new mode 100755 diff --git a/packages/jsdoc-util/package.json b/packages/jsdoc-util/package.json old mode 100644 new mode 100755 index c26b1a59..a8a05fb1 --- a/packages/jsdoc-util/package.json +++ b/packages/jsdoc-util/package.json @@ -2,7 +2,6 @@ "name": "@jsdoc/util", "version": "1.0.0", "description": "Utility modules for JSDoc.", - "main": "index.js", "repository": { "type": "git", "url": "https://github.com/jsdoc3/jsdoc" diff --git a/packages/jsdoc-util/test/specs/index.js b/packages/jsdoc-util/test/specs/index.js deleted file mode 100644 index 375a1940..00000000 --- a/packages/jsdoc-util/test/specs/index.js +++ /dev/null @@ -1,43 +0,0 @@ -describe('@jsdoc/util', () => { - const util = require('../..'); - - it('is an object', () => { - expect(util).toBeObject(); - }); - - it('has a cast method', () => { - expect(util.cast).toBeFunction(); - }); - - it('has an fs object', () => { - expect(util.fs).toBeObject(); - }); - - it('has a path object', () => { - expect(util.path).toBeObject(); - }); - - describe('cast', () => { - it('is ./lib/cast', () => { - const cast = require('../../lib/cast'); - - expect(util.cast).toBe(cast); - }); - }); - - describe('fs', () => { - it('is ./lib/fs', () => { - const fs = require('../../lib/fs'); - - expect(util.fs).toBe(fs); - }); - }); - - describe('path', () => { - it('is ./lib/path', () => { - const path = require('../../lib/path'); - - expect(util.path).toBe(path); - }); - }); -}); diff --git a/packages/jsdoc-util/test/specs/lib/cast.js b/packages/jsdoc-util/test/specs/lib/cast.js old mode 100644 new mode 100755 index 852e0037..7ae54f33 --- a/packages/jsdoc-util/test/specs/lib/cast.js +++ b/packages/jsdoc-util/test/specs/lib/cast.js @@ -1,6 +1,6 @@ -const cast = require('../../../lib/cast'); - describe('@jsdoc/util/lib/cast', () => { + const cast = require('../../../lib/cast'); + it('exists', () => { expect(cast).toBeFunction(); }); diff --git a/packages/jsdoc-util/test/specs/lib/fs.js b/packages/jsdoc-util/test/specs/lib/fs.js old mode 100644 new mode 100755 index 56732546..831a122e --- a/packages/jsdoc-util/test/specs/lib/fs.js +++ b/packages/jsdoc-util/test/specs/lib/fs.js @@ -3,39 +3,43 @@ describe('@jsdoc/util/lib/fs', () => { const fsUtil = require('../../../lib/fs'); const path = require('path'); - afterEach(() => mockFs.restore()); + const cwd = process.cwd(); - it('has an ls method', () => { - expect(fsUtil.ls).toBeFunction(); - }); + function resolvePaths(files) { + return files.map(f => path.join(cwd, f)).sort(); + } - describe('ls', () => { - beforeEach(() => { - mockFs({ - head: { - eyes: '', - ears: '', - mouth: '', - nose: '', - shoulders: { - knees: { - meniscus: '', - toes: { - phalanx: '', - '.big-toe-phalanx': '' - } + beforeEach(() => { + mockFs({ + head: { + eyes: '', + ears: '', + mouth: '', + nose: '', + shoulders: { + knees: { + meniscus: '', + toes: { + phalanx: '', + '.big-toe-phalanx': '' } } } - }); + } }); + }); - const cwd = process.cwd(); + afterEach(() => mockFs.restore()); - function resolvePaths(files) { - return files.map(f => path.join(cwd, f)).sort(); - } + it('has an lsSync method', () => { + expect(fsUtil.lsSync).toBeFunction(); + }); + it('has a walkSync method', () => { + expect(fsUtil.walkSync).toBeFunction(); + }); + + describe('lsSync', () => { const allFiles = resolvePaths([ 'head/eyes', 'head/ears', @@ -46,13 +50,13 @@ describe('@jsdoc/util/lib/fs', () => { ]); it('gets all non-hidden files from all levels by default', () => { - const files = fsUtil.ls(cwd).sort(); + const files = fsUtil.lsSync(cwd).sort(); expect(files).toEqual(allFiles); }); it('limits recursion depth when asked', () => { - const files = fsUtil.ls(cwd, { depth: 1 }).sort(); + const files = fsUtil.lsSync(cwd, { depth: 1 }).sort(); expect(files).toEqual(resolvePaths([ 'head/eyes', @@ -63,9 +67,63 @@ describe('@jsdoc/util/lib/fs', () => { }); it('treats a depth of -1 as infinite', () => { - const files = fsUtil.ls('head', { depth: -1 }).sort(); + const files = fsUtil.lsSync('head', { depth: -1 }).sort(); expect(files).toEqual(allFiles); }); }); + + describe('walkSync', () => { + const allFiles = resolvePaths([ + 'head/eyes', + 'head/ears', + 'head/mouth', + 'head/nose', + 'head/shoulders/knees/meniscus', + 'head/shoulders/knees/toes/.big-toe-phalanx', + 'head/shoulders/knees/toes/phalanx' + ]); + + it('gets all files from all levels by default', () => { + const files = fsUtil.walkSync(cwd).sort(); + + expect(files).toEqual(allFiles); + }); + + it('limits recursion depth when asked', () => { + const files = fsUtil.walkSync(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.walkSync('head', { depth: -1 }).sort(); + + expect(files).toEqual(allFiles); + }); + + it('works with paths to specific files', () => { + const files = fsUtil.walkSync('head/shoulders/knees/meniscus'); + + expect(files).toEqual(resolvePaths([ + 'head/shoulders/knees/meniscus' + ])); + }); + + it('applies the filter if present', () => { + const files = fsUtil.walkSync( + 'head/shoulders/knees/toes', + { filter: f => path.basename(f) !== 'phalanx' } + ); + + expect(files).toEqual(resolvePaths([ + 'head/shoulders/knees/toes/.big-toe-phalanx' + ])); + }); + }); }); diff --git a/packages/jsdoc-util/test/specs/lib/path.js b/packages/jsdoc-util/test/specs/lib/path.js old mode 100644 new mode 100755 index 96c2b812..12ea3154 --- a/packages/jsdoc-util/test/specs/lib/path.js +++ b/packages/jsdoc-util/test/specs/lib/path.js @@ -4,18 +4,21 @@ describe('@jsdoc/util/lib/path', () => { const libPath = require('../../../lib/path'); const isWindows = /^win/.test(os.platform()); + const fakeCwd = isWindows ? 'C:\\Users\\jsdoc' : '/Users/jsdoc'; + + beforeEach(() => { + spyOn(process, 'cwd').and.returnValue(fakeCwd); + }); it('has a commonPrefix method', () => { expect(libPath.commonPrefix).toBeFunction(); }); + it('has a makeFilter method', () => { + expect(libPath.makeFilter).toBeFunction(); + }); + describe('commonPrefix', () => { - const fakeCwd = isWindows ? 'C:\\Users\\jsdoc' : '/Users/jsdoc'; - - beforeEach(() => { - spyOn(process, 'cwd').and.returnValue(fakeCwd); - }); - it('finds the correct prefix for a single relative path', () => { const paths = [path.join('foo', 'bar', 'baz', 'qux.js')]; const expected = path.join(fakeCwd, 'foo', 'bar', 'baz'); @@ -92,4 +95,128 @@ describe('@jsdoc/util/lib/path', () => { }); } }); + + describe('makeFilter', () => { + it('returns a function', () => { + expect(typeof libPath.makeFilter({})).toBe('function'); + }); + + it('throws on bad input', () => { + expect(() => libPath.makeFilter()).toThrow(); + }); + + describe('exclude', () => { + it('accepts a string', () => { + expect(() => libPath.makeFilter({ exclude: 'foo' })).not.toThrow(); + }); + + it('accepts an array of strings', () => { + expect(() => libPath.makeFilter({ exclude: ['foo', 'bar'] })).not.toThrow(); + }); + + it('throws on invalid types', () => { + expect(() => libPath.makeFilter({ exclude: 7 })).toThrow(); + }); + + it('filters out filepaths that end with excluded values', () => { + const filter = libPath.makeFilter({ exclude: ['nope/nuh-uh.md', 'neverever'] }); + const filteredFiles = [ + 'path/to/some/file.js', + 'another/path/nope/nuh-uh.md', + 'neverever' + ].filter(filter); + + expect(filteredFiles).toEqual(['path/to/some/file.js']); + }); + + it('does not filter out filepaths that contain excluded values in the middle', () => { + const filter = libPath.makeFilter({ exclude: 'to/some' }); + const filteredFiles = [ + 'path/to/some/file.js' + ].filter(filter); + + expect(filteredFiles).toEqual(['path/to/some/file.js']); + }); + + it('takes precedence over includePattern', () => { + const filter = libPath.makeFilter({ + exclude: ['nope/nuh-uh.md', 'neverever'], + includePattern: '(?:\\.js|\\.md)$' + }); + const filteredFiles = [ + 'path/to/some/file.js', + 'another/path/nope/nuh-uh.md', + 'neverever' + ].filter(filter); + + expect(filteredFiles).toEqual(['path/to/some/file.js']); + }); + }); + + describe('excludePattern', () => { + it('accepts a string', () => { + expect(() => libPath.makeFilter({ excludePattern: 'foo' })).not.toThrow(); + }); + + it('accepts a RegExp', () => { + expect(() => libPath.makeFilter({ excludePattern: new RegExp('z') })).not.toThrow(); + }); + + it('throws on invalid types', () => { + expect(() => libPath.makeFilter({ excludePattern: 7 })).toThrow(); + }); + + it('filters out filepaths that match excludePattern', () => { + const filter = libPath.makeFilter({ + excludePattern: '(?:nuh.+?\\.md|neverever)$' + }); + const filteredFiles = [ + 'path/to/some/file.js', + 'another/path/nope/nuh-uh.md', + 'neverever' + ].filter(filter); + + expect(filteredFiles).toEqual(['path/to/some/file.js']); + }); + + it('takes precedence over includePattern', () => { + const filter = libPath.makeFilter({ + excludePattern: '(?:nuh.+?\\.md|neverever)$', + includePattern: '(?:\\.js|\\.md)$' + }); + const filteredFiles = [ + 'path/to/some/file.js', + 'another/path/nope/nuh-uh.md', + 'neverever' + ].filter(filter); + + expect(filteredFiles).toEqual(['path/to/some/file.js']); + }); + }); + + describe('includePattern', () => { + it('accepts a string', () => { + expect(() => libPath.makeFilter({ includePattern: 'foo' })).not.toThrow(); + }); + + it('accepts a RegExp', () => { + expect(() => libPath.makeFilter({ includePattern: new RegExp('z') })).not.toThrow(); + }); + + it('throws on invalid types', () => { + expect(() => libPath.makeFilter({ includePattern: 7 })).toThrow(); + }); + + it('includes only filepaths that match includePattern', () => { + const filter = libPath.makeFilter({ includePattern: '\\.js$' }); + const filteredFiles = [ + 'path/to/some/file.js', + 'another/path/nope/nuh-uh.md', + 'neverever' + ].filter(filter); + + expect(filteredFiles).toEqual(['path/to/some/file.js']); + }); + }); + }); }); diff --git a/test/fixtures/src/_dir2/four.js b/test/fixtures/src/_dir2/four.js deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/src/_ignored.js b/test/fixtures/src/_ignored.js deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/src/dir1/three.js b/test/fixtures/src/dir1/three.js deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/src/ignored.txt b/test/fixtures/src/ignored.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/src/one.js b/test/fixtures/src/one.js deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/src/two.js b/test/fixtures/src/two.js deleted file mode 100644 index e69de29b..00000000 diff --git a/test/specs/jsdoc/src/filter.js b/test/specs/jsdoc/src/filter.js deleted file mode 100644 index 8c0019c2..00000000 --- a/test/specs/jsdoc/src/filter.js +++ /dev/null @@ -1,167 +0,0 @@ -describe('jsdoc/src/filter', () => { - const env = require('jsdoc/env'); - const filter = require('jsdoc/src/filter'); - const path = require('path'); - - it('should exist', () => { - expect(filter).toBeDefined(); - expect(typeof filter).toBe('object'); - }); - - it('should export a "Filter" class', () => { - expect(filter.Filter).toBeDefined(); - expect(typeof filter.Filter).toBe('function'); - }); - - describe('Filter', () => { - let myFilter; - - const defaultIncludePattern = new RegExp('.+\\.js(doc)?$'); - const defaultExcludePattern = new RegExp('(^|\\/|\\\\)_'); - - beforeEach(() => { - myFilter = new filter.Filter({}); - }); - - it('should have an "exclude" property', () => { - expect(myFilter.exclude).toBeDefined(); - }); - - it('should have an "excludePattern" property', () => { - expect(myFilter.excludePattern).toBeDefined(); - }); - - it('should have an "includePattern" property', () => { - expect(myFilter.includePattern).toBeDefined(); - }); - - it('should have an "isIncluded" method', () => { - expect(myFilter.isIncluded).toBeDefined(); - expect(typeof myFilter.isIncluded).toBe('function'); - }); - - describe('exclude', () => { - it('should default to null', () => { - expect(myFilter.exclude).toBe(null); - }); - - it('should be null if the value passed to the constructor was not an array', - () => { - myFilter = new filter.Filter({ - exclude: 'foo' - }); - - expect(myFilter.exclude).toBe(null); - }); - - it('should resolve paths relative to the user\'s working directory', () => { - const filename = 'bar.js'; - - myFilter = new filter.Filter({ - exclude: [filename] - }); - - expect(myFilter.exclude).toEqual([path.resolve(env.pwd, filename)]); - }); - }); - - function testRegExpProperty(name) { - it('should default to null', () => { - expect(myFilter[name]).toBe(null); - }); - - it('should contain the regexp passed to the constructor', () => { - const regExp = new RegExp('^foo$'); - const options = {}; - - options[name] = regExp; - myFilter = new filter.Filter(options); - - expect(myFilter[name]).toBe(regExp); - }); - - it('should contain a regexp if a string was passed to the constructor', () => { - const regExpString = '^foo$'; - const options = {}; - - options[name] = regExpString; - myFilter = new filter.Filter(options); - - expect(myFilter[name] instanceof RegExp).toBe(true); - expect(myFilter[name].source).toBe(regExpString); - }); - } - - describe( 'excludePattern', testRegExpProperty.bind(jasmine, 'excludePattern') ); - - describe( 'includePattern', testRegExpProperty.bind(jasmine, 'includePattern') ); - - describe('isIncluded', () => { - it('should return the correct source files', () => { - let files = [ - 'yes.js', - '/yes.jsdoc', - '/_nope.js', - '.ignore', - path.normalize(`${env.pwd}/scratch/conf.js`) - ]; - - myFilter = new filter.Filter({ - includePattern: defaultIncludePattern, - excludePattern: defaultExcludePattern, - exclude: ['.ignore', 'scratch/conf.js'] - }); - - files = files.filter($ => myFilter.isIncluded($)); - - expect(files.length).toEqual(2); - expect( files.indexOf('yes.js') ).toBeGreaterThan(-1); - expect( files.indexOf('/yes.jsdoc') ).toBeGreaterThan(-1); - }); - - it('should be able to exclude specific subdirectories', () => { - let files = [ - 'yes.js', - 'topsecret/nope.js', - 'module/yes.js', - 'module/topsecret/nope.js' - ]; - - myFilter = new filter.Filter({ - includePattern: defaultIncludePattern, - excludePattern: defaultExcludePattern, - exclude: ['topsecret', 'module/topsecret'] - }); - - files = files.filter($ => myFilter.isIncluded($)); - - expect(files.length).toBe(2); - expect( files.indexOf('yes.js') ).toBeGreaterThan(-1); - expect( files.indexOf('module/yes.js') ).toBeGreaterThan(-1); - }); - - it('should be able to exclude descendants of excluded subdirectories', () => { - let files = [ - 'yes.js', - 'topsecret/nested/nope.js', - 'module/yes.js', - 'module/topsecret/nested/nope.js' - ]; - - myFilter = new filter.Filter({ - includePattern: defaultIncludePattern, - excludePattern: defaultExcludePattern, - exclude: ['topsecret', 'module/topsecret'] - }); - - files = files.filter($ => myFilter.isIncluded($)); - - expect(files.length).toBe(2); - expect( files.indexOf('yes.js') ).toBeGreaterThan(-1); - expect( files.indexOf('module/yes.js') ).toBeGreaterThan(-1); - expect( files.indexOf('topsecret/nested/nope.js') ).toBe(-1); - expect( files.indexOf('module/topsecret/nested/nope.js') ).toBe(-1); - }); - }); - }); -}); diff --git a/test/specs/jsdoc/src/scanner.js b/test/specs/jsdoc/src/scanner.js deleted file mode 100644 index 70cdd4e1..00000000 --- a/test/specs/jsdoc/src/scanner.js +++ /dev/null @@ -1,54 +0,0 @@ -describe('jsdoc/src/scanner', () => { - const env = require('jsdoc/env'); - const path = require('path'); - const scanner = require('jsdoc/src/scanner'); - - const filter = new (require('jsdoc/src/filter').Filter)({ - includePattern: new RegExp('.+\\.js(doc)?$'), - excludePattern: new RegExp('(^|\\/|\\\\)_') - }); - const sourcePath = path.normalize(`${env.pwd}/test/fixtures/src`); - - it('should exist', () => { - expect(scanner).toBeDefined(); - expect(typeof scanner).toBe('object'); - }); - - it('should export a "Scanner" class', () => { - expect(scanner.Scanner).toBeDefined(); - expect(typeof scanner.Scanner).toBe('function'); - }); - - describe('Scanner', () => { - it('should inherit from EventEmitter', () => { - const EventEmitter = require('events').EventEmitter; - const testScanner = new scanner.Scanner(); - - expect(testScanner instanceof EventEmitter).toBe(true); - }); - - it('should have a "scan" method', () => { - const testScanner = new scanner.Scanner(); - - expect(testScanner.scan).toBeDefined(); - expect(typeof testScanner.scan).toBe('function'); - }); - - describe('scan', () => { - it('should return the correct source files', () => { - const testScanner = new scanner.Scanner(); - let sourceFiles = testScanner.scan([sourcePath], 3, filter); - - sourceFiles = sourceFiles.map($ => path.relative(env.pwd, $)); - - expect(sourceFiles.length).toEqual(3); - expect( sourceFiles.indexOf(path.join('test', 'fixtures', 'src', 'one.js')) ) - .toBeGreaterThan(-1); - expect( sourceFiles.indexOf(path.join('test', 'fixtures', 'src', 'two.js')) ) - .toBeGreaterThan(-1); - expect( sourceFiles.indexOf(path.join('test', 'fixtures', 'src', 'dir1', 'three.js')) ) - .toBeGreaterThan(-1); - }); - }); - }); -});