Repository: BRIKEV/express-jsdoc-swagger
Pull request #110 (2020-11-18 22:47:05)

Allow passing multiple file patterns
### What kind of change does this PR introduce?
 - [x] Feature
 - [x] Test
 - [x] Docs

### Description:
Adds support for passing multiple file patterns to `options.filesPattern`. Addresses #101 

bri06: LGTM 👌
kevinccbsg: @all-contributors please add @Stosiu for code
allcontributors[bot]: @kevinccbsg 

I've put up [a pull request](https://github.com/BRIKEV/express-jsdoc-swagger/pull/112) to add @Stosiu! :tada:

Patch:
diff --git a/README.md b/README.md
index c2ff993..e71ba26 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ const options = {
       scheme: 'basic',
     },
   },
-  filesPattern: './**/*.js', // Glob pattern to find your jsdoc files
+  filesPattern: './**/*.js', // Glob pattern to find your jsdoc files (it supports arrays too ['./**/*.controller.js', './**/*.route.js'])
   swaggerUIPath: '/your-url', // SwaggerUI will be render in this url. Default: '/api-docs'
   baseDir: __dirname,
 };
diff --git a/consumers/globFilesMatches.js b/consumers/globFilesMatches.js
index c46c099..1aab2a3 100644
--- a/consumers/globFilesMatches.js
+++ b/consumers/globFilesMatches.js
@@ -2,20 +2,48 @@ const glob = require('glob');
 const path = require('path');
 
 const DEFAULT_EXCLUDED_FOLDER = 'node_modules';
+const DEFAULT_GLOB_OPTIONS = { ignore: '**/node_modules/**' };
 
-const globFilesMatches = (baseDir, filePath, excludedFolder = DEFAULT_EXCLUDED_FOLDER) => (
+const globFilesMatches = (baseDir, filesPattern, excludedFolder = DEFAULT_EXCLUDED_FOLDER) => (
   new Promise((resolve, reject) => {
-    if (!baseDir || !filePath) {
+    if (!baseDir || !filesPattern) {
       const error = new Error('baseDir and filePath are required');
       return reject(error);
     }
-    return glob(path.resolve(baseDir, filePath), { ignore: '**/node_modules/**' }, (err, files) => {
-      if (err) {
-        return reject(err);
+
+    if (!Array.isArray(filesPattern) && typeof filesPattern !== 'string') {
+      const error = new Error('files pattern has to be a type of string');
+      return reject(error);
+    }
+
+    if (Array.isArray(filesPattern)) {
+      if (filesPattern.length === 0) {
+        const error = new Error('if you submit an array of filesPattern it must contain at least one pattern');
+        return reject(error);
+      }
+
+      if (filesPattern.some(pattern => typeof pattern !== 'string')) {
+        const error = new Error('all file patterns have to be strings');
+        return reject(error);
       }
-      const filterFiles = files.filter(file => !file.includes(excludedFolder));
-      return resolve(filterFiles);
-    });
+    }
+
+    try {
+      let files;
+      if (!Array.isArray(filesPattern)) {
+        files = glob.sync(path.resolve(baseDir, filesPattern), DEFAULT_GLOB_OPTIONS);
+      } else {
+        files = filesPattern
+          .map(pattern => glob.sync(path.resolve(baseDir, pattern), DEFAULT_EXCLUDED_FOLDER))
+          .reduce((memo, it) => memo.concat(it), [])
+          .filter((value, index, self) => self.indexOf(value) === index);
+      }
+
+      const filteredFiles = files.filter(file => !file.includes(excludedFolder));
+      return resolve(filteredFiles);
+    } catch (error) {
+      return reject(error);
+    }
   })
 );
 
diff --git a/index.d.ts b/index.d.ts
index 243dd82..2741350 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -35,7 +35,7 @@ interface SecurityObject {
 interface Options {
 	info: InfoObject;
 	baseDir: string;
-	filesPattern: string;
+	filesPattern: string | string[];
 	security?: SecurityObject[];
 	servers?: string[];
 	swaggerUIPath?: string;


Test patch:
diff --git a/test/consumers/globFilesMatches/index.test.js b/test/consumers/globFilesMatches/index.test.js
index 74a7f2c..7874dcc 100644
--- a/test/consumers/globFilesMatches/index.test.js
+++ b/test/consumers/globFilesMatches/index.test.js
@@ -1,7 +1,7 @@
 const globFilesMatches = require('../../../consumers/globFilesMatches');
 
 describe('glob Files matches method', () => {
-  it('should return error array when required param is not send', done => {
+  it('should return error when required param is not send', done => {
     globFilesMatches()
       .catch(error => {
         expect(error.message).toEqual('baseDir and filePath are required');
@@ -19,6 +19,16 @@ describe('glob Files matches method', () => {
       });
   });
 
+  it('should return error when filePath is not a string', done => {
+    const baseDir = __dirname;
+    const filePath = 3;
+    globFilesMatches(baseDir, filePath)
+      .catch(error => {
+        expect(error.message).toEqual('files pattern has to be a type of string');
+        done();
+      });
+  });
+
   it('should return error when glob method not receives a string type', done => {
     const baseDir = 2; // This forces error in glob method
     const filePath = './**/**.txt';
@@ -54,4 +64,78 @@ describe('glob Files matches method', () => {
         done();
       });
   });
+
+  describe('Array of file paths', () => {
+    it('should return error when passed empty array', done => {
+      const baseDir = __dirname;
+      const filePath = [];
+      globFilesMatches(baseDir, filePath)
+        .catch(error => {
+          expect(error.message).toEqual('if you submit an array of filesPattern it must contain at least one pattern');
+          done();
+        });
+    });
+
+    it('should return error when one of the array parameters is not a string', done => {
+      const baseDir = __dirname;
+      const filePath = ['a', 'b', 'c', 2];
+      globFilesMatches(baseDir, filePath)
+        .catch(error => {
+          expect(error.message).toEqual('all file patterns have to be strings');
+          done();
+        });
+    });
+
+    it('should return example.txt and excluded/example.txt file', done => {
+      const baseDir = __dirname;
+      const filePath = ['./**/**.txt'];
+      globFilesMatches(baseDir, filePath)
+        .then(files => {
+          const [exampleFile, excludedFile] = files;
+          expect(exampleFile.includes('example.txt')).toBe(true);
+          expect(excludedFile.includes('excluded/example.txt')).toBe(true);
+          expect(files).toHaveLength(2);
+          done();
+        });
+    });
+
+    it('should return example.txt and excluded/example.txt file if multiple paths were passed', done => {
+      const baseDir = __dirname;
+      const filePath = ['./fixtures/example.txt', './fixtures/excluded/example.txt'];
+      globFilesMatches(baseDir, filePath)
+        .then(files => {
+          const [exampleFile, excludedFile] = files;
+          expect(exampleFile.includes('example.txt')).toBe(true);
+          expect(excludedFile.includes('excluded/example.txt')).toBe(true);
+          expect(files).toHaveLength(2);
+          done();
+        });
+    });
+
+    it('should not return duplicated paths', done => {
+      const baseDir = __dirname;
+      const filePath = ['./**/**.txt', './**/**.txt', './fixtures/example.txt'];
+      globFilesMatches(baseDir, filePath)
+        .then(files => {
+          const [exampleFile, excludedFile] = files;
+          expect(exampleFile.includes('example.txt')).toBe(true);
+          expect(excludedFile.includes('excluded/example.txt')).toBe(true);
+          expect(files).toHaveLength(2);
+          done();
+        });
+    });
+
+    it('should return only example.txt file when we add exclude condition', done => {
+      const baseDir = __dirname;
+      const filePath = ['./**/**.txt'];
+      const excludedFolder = 'excluded';
+      globFilesMatches(baseDir, filePath, excludedFolder)
+        .then(files => {
+          const [exampleFile] = files;
+          expect(exampleFile.includes('example.txt')).toBe(true);
+          expect(files).toHaveLength(1);
+          done();
+        });
+    });
+  });
 });