| 
									
										
										
										
											2022-02-22 11:15:34 +01:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const { promisify } = require('util'); | 
					
						
							|  |  |  | const path = require('path'); | 
					
						
							|  |  |  | const readFile = promisify(require('fs').readFile); | 
					
						
							|  |  |  | const { readFileSync, writeFileSync } = require('fs'); | 
					
						
							|  |  |  | const _ = require('lodash'); | 
					
						
							|  |  |  | const glob = promisify(require('glob').glob); | 
					
						
							|  |  |  | const chalk = require('chalk'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const findFilesInDirectories = async ( | 
					
						
							|  |  |  |   directories = [], | 
					
						
							|  |  |  |   matchPatterns = [], | 
					
						
							|  |  |  |   ignorePatterns = [] | 
					
						
							|  |  |  | ) => { | 
					
						
							|  |  |  |   let files = []; | 
					
						
							|  |  |  |   for (const directory of directories) { | 
					
						
							|  |  |  |     for (const pattern of matchPatterns) { | 
					
						
							|  |  |  |       files = files.concat(await glob(path.join(directory, pattern), { ignore: ignorePatterns })); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return files; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-08 23:33:39 +02:00
										 |  |  | const loadFilesInMemory = async (files) => { | 
					
						
							| 
									
										
										
										
											2022-02-22 11:15:34 +01:00
										 |  |  |   return Promise.all( | 
					
						
							| 
									
										
										
										
											2022-08-08 23:33:39 +02:00
										 |  |  |     files.map(async (file) => ({ | 
					
						
							| 
									
										
										
										
											2022-02-22 11:15:34 +01:00
										 |  |  |       path: file, | 
					
						
							|  |  |  |       content: (await readFile(file)).toString(), | 
					
						
							|  |  |  |     })) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const getMatches = (content, matchedString) => { | 
					
						
							|  |  |  |   const lines = content.split('\n'); | 
					
						
							|  |  |  |   const highlightedLines = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (const line of lines) { | 
					
						
							|  |  |  |     if (line.includes(matchedString)) { | 
					
						
							|  |  |  |       highlightedLines.push( | 
					
						
							| 
									
										
										
										
											2022-08-08 23:33:39 +02:00
										 |  |  |         line.split(matchedString).join(chalk.bgMagentaBright(matchedString)).trim() | 
					
						
							| 
									
										
										
										
											2022-02-22 11:15:34 +01:00
										 |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return highlightedLines; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FilesContentSearch { | 
					
						
							|  |  |  |   constructor(directories, matchPatterns, ignorePatterns) { | 
					
						
							|  |  |  |     this.directories = directories; | 
					
						
							|  |  |  |     this.matchPatterns = matchPatterns; | 
					
						
							|  |  |  |     this.ignorePatterns = ignorePatterns; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async loadFiles() { | 
					
						
							|  |  |  |     console.log('Searching for matching files'); | 
					
						
							|  |  |  |     this.fileList = await findFilesInDirectories( | 
					
						
							|  |  |  |       this.directories, | 
					
						
							|  |  |  |       this.matchPatterns, | 
					
						
							|  |  |  |       this.ignorePatterns | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     console.log(`Found ${this.fileList.length} files`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     console.log('Loading files content in memory'); | 
					
						
							|  |  |  |     this.files = await loadFilesInMemory(this.fileList); | 
					
						
							|  |  |  |     console.log(`Loaded ${this.files.length} files in memory`); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   search(matchFunction) { | 
					
						
							|  |  |  |     const results = []; | 
					
						
							|  |  |  |     const localFiles = _.cloneDeep(this.files); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const file of localFiles) { | 
					
						
							|  |  |  |       const matchedString = matchFunction(file.content); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (matchedString) { | 
					
						
							|  |  |  |         file.matches = getMatches(file.content, matchedString); | 
					
						
							| 
									
										
										
										
											2022-08-08 23:33:39 +02:00
										 |  |  |         file.replaceAll = (replaceValue) => { | 
					
						
							| 
									
										
										
										
											2022-02-22 11:15:34 +01:00
										 |  |  |           let fileContent = readFileSync(file.path, { encoding: 'utf-8' }); | 
					
						
							|  |  |  |           fileContent = fileContent.replaceAll(matchedString, replaceValue); | 
					
						
							|  |  |  |           writeFileSync(file.path, fileContent, { encoding: 'utf-8' }); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         results.push(file); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return results; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   searchString(string) { | 
					
						
							| 
									
										
										
										
											2022-08-08 23:33:39 +02:00
										 |  |  |     return this.search((fileContent) => { | 
					
						
							| 
									
										
										
										
											2022-02-22 11:15:34 +01:00
										 |  |  |       if (fileContent.includes(string)) return string; | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = FilesContentSearch; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Usage example
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // const fcs = new FilesContentSearch(
 | 
					
						
							|  |  |  | //   [path.join(__dirname, '../../../')],
 | 
					
						
							|  |  |  | //   ['**/*.js'],
 | 
					
						
							|  |  |  | //   ['**/node_modules/**', '**/cache/**', '**/build/**']
 | 
					
						
							|  |  |  | // );
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // await fcs.loadFiles();
 | 
					
						
							|  |  |  | // const results = await fcs.searchString(yolo);
 |