Code Monkey home page Code Monkey logo

grunt-search's Introduction

grunt-search

This is a Grunt plugin that searches a list of files for particular search strings and logs the results in JSON, XML, text or JUnit format - or just output to the console. It also provides an option to fail the build process, should you need it.

Use-case

There are a bunch of search-and-replace Grunt plugins out there, but we needed something simpler for logging purposes only. We wanted to run various tests on our codebase to look for certain things: inline styles, inline event handlers, old, unwanted HTML tags. None of these weren't significant enough to warrant failing the build, but they do give a clue as the health of the codebase.

So basically, we run this function along with jshint in our dev environments to warn us about the accumulation of crap.

Installation

This plugin requires Grunt v0.4.1+.

In your project folder, run the following command:

npm install grunt-search --save-dev

Once the plugin has been installed, you need to add this line of JS to your gruntfile:

grunt.loadNpmTasks('grunt-search');

That will reference this module and allow you to use it.

Usage examples

If you're familiar with Grunt, it's pretty straightforward to use. Here's a few example searches so you can get the idea of how it operates.

grunt.initConfig({
    search: {

        // Example 1: search for inline style tags
        inlineStyles: {
            files: {
                src: ["*.html", "**/*.hbs"]
            },
            options: {
                searchString: /style\s?=\s?["']*/g,
                logFile: "tmp/results.json"
            }
        },

        // Example 2: look for any developers leaving obscenities in the codebase
        obscenities: {
            files: {
                src: ["*"]
            },
            options: {
                searchString: /(poop|fart|Barbara\sStreisand)/g,
                logFile: "tmp/results.xml",
                logFormat: "xml",
                failOnMatch: true,
                onMatch: function(match) {
                    // called when a match is made. The parameter is an object of the
                    // following structure: { file: "", line: X, match: "" }
                },
                onComplete: function(matches) {
                    // called when all files have been parsed for the target. The
                    // matches parameter is an object of the format:
                    // `{ numMatches: N, matches: {} }`. The matches /property is
                    // an object of filename => array of matches
                },
            }
        },

        // Example 3: search a PHP codebase for short-tags and just output the findings to
        // the console (short tags can be disabled, so this helps prevent them sneaking in!)
		short_tags: {
			files: {
				src: ["**/*.php"]
			},
			options: {
				searchString: /(<\?[^p])|(<\?$)/,
				logFormat: "console"
			}
		},

		// Example 4: custom logging function. This example shows how you can access the raw results to
		// do whatever you want with it.
		chicken_sounds: {
			files: {
				src: ["*"],
			},
			options: {
				searchString: /cluck|cluckity|bwaaaaaah!|/,
				logFormat: "custom",
				customLogFormatCallback: function(params) {
					/*
					// here, params is an object containing the following
					{
						filePaths: [], // an array of file paths
						results: [], // the results
						numResults: X // the number of results
					}
					*/
				}
			}
		}
    }
});

File matching

The files property should be an object with a single src property containing an array of files, or file patterns. This plugin uses Grunt's file globbing patterns, documented here: http://gruntjs.com/configuring-tasks

Options

The options property can contain any of the following:

required setting

  • searchString: a string or regexp, or array of strings/regexps. This is the string or strings you're looking for.

optional settings

  • logFormat: (optional, defaults to json) the format of the log file: json, xml, junit, text, custom, or console. The json, XML, text and console options are self explanatory; the junit option logs the information in an XML format understood by JUnit; and the custom option lets you pass off the logging to your own function, defined (or at least accessible) to your gruntfile. For that, you need to
  • customLogFormatCallback (optional, unless you choose custom for the logFormat setting). If you want, you can define your own logging function to access the raw info. Take a look at the chicken_sounds example above to see how to configure this, and the data structure you get passed to your callback function.
  • logFile: (required, unless logFormat is set to console) the location of the file to be created. Like all things with Grunt, this is relative to the Grunt root.
  • failOnMatch: (optional, defaults to false). This option lets you choose to fail the build process if any matches are found.
  • outputExaminedFiles: (optional) a boolean - default to false). Sometimes it's not totally clear what files are being matched by the file globbing. When this option is set to true, the generated output file contains a list of the files that had been examined.
  • scopeMatchToFile: (optional) a boolean - default to false. Determines if the match should be scoped to the line or file. For example, when set to true, all matches would be handled for files and parameters for onMatch and logCondition would be passed as { file: "", line: [X, X], match: ["", ""] } per one for each file where one or multiple matches occurred.
  • onComplete: (optional) a function. This is called when all file searching is complete. It's passed a single parameter. An object of the following format: { numMatches: N, matches: {} }. The matches property is an object of filename => array of matches. Note: this function doesn't get called in the event of a fatal error (i.e. a required options parameter wasn't properly included).
  • onMatch: (optional) a function. This is called after each match is made. It's passed a single parameter - an object with the following structure: { file: "", line: X, match: "" }
  • logCondition: (optional) a function. This can be called to check if this match should be included in output. It's passed a single parameter - an object with the following structure: { file: "", line: X, match: "" }. If this function returns true the match would be included, if false it is not.
  • JUnitTestsuiteName: (optional) a function. If logFormat property set to true this function would be evaluated to determine if should this item be marked in JUnit report or not. It's passed two parameters, file dir object with the following structure: { line: X, match: "" } ( or { line: [X,..], match: ["",..] }, if scopeMatchToFile set to true ). If this function returns true JUnit will mark this match as failed, if false JUnit will mark this match as passed.
  • JUnitTestsuiteName: (optional) a string - name for test suite in JUnit report.
  • JUnitFailureMessage: (optional) a string - message for failed test suites in JUnit report.

Note: if either of the required parameters are omitted, the build will fail.

Changelog

  • 0.1.8 - Feb 21, 2016 - Grunt 1.0 compatibility update.
  • 0.1.7 - May 12th, 2015 - searchString now allows arrays; generated output of XML, JSON and text now include search query details.
  • 0.1.6 - May 17th, 2014 - custom log option added. jshint added and additional JUnit options added by Sergei Z.
  • 0.1.5 - May 13th, 2014 - logCondition and scopeMatchToFile, courtesy of Sergei Z.
  • 0.1.4 - Mar 5th, 2014. junit logFile option value added for generating JUnit XML reports. Courtesy of Sergii Iavorsky.
  • 0.1.3 - Dec 18th, 2013. console logFile option value added for simply outputting results to console. Now the number of matches is always output to the console regardless of logFile type, as well as being logged in the generated file.
  • 0.1.2 - Dec 15th, 2013. Tests added, minor tweaks.
  • 0.1.1 - Dec 14th, 2013. Bug fix for JSON report files.
  • 0.1.0 - Dec 13th, 2013. Initial release.

Things To Improve

  • Each file being examined is loaded entirely into memory right now. From a memory perspective it would be better to stream them in.
  • Multi-line matches won't work.
  • Having some sort of omit list for the file search would be pretty nice. I find that often it finds dud matches in specific files, but I still want the general blobbing map to be searched...
  • Better tests!

License

MIT, baby.

grunt-search's People

Contributors

benkeen avatar sagens42 avatar walterhiggins avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

grunt-search's Issues

Add error codes

Add unix error codes 0/1 (?) on the task. 0 = success; 1 otherwise.

JSHint Style Ignores

Hi benkeen, great work here. I just spent the last hour adding this to a bunch of our repos, can't wait to find all the bad code with it.

The Problem

My biggest issue is that I have to do some pretty icky regexes to determine if a search match is ok or not. A great example is that in our js code, 90% of the time it is a mistake to include a console.log statement. That usually indicates someone forgot to remove some debug code, or someone got lazy and isn't properly reporting error conditions.

However there are valid use cases for console.log, such as in the error reporting system itself (one reporter is a console logger).

Current Workaround:

 options: {
    searchString: /^.*console\.((?!grunt\-search:console ignore:line).)*$/ig
  , failOnMatch: true
  , logFormat: "console"
}

Suggested Improvement

Many developers in the js community are already familiar with jshint. They have great support for ignoring all or some errors for single or multiple lines: JSHint Docs - search for ignore.

I propose that grunt-search adds a similar ignore comment system. Based on the regex above, it could look something like this:

// grunt-search[:console] ignore:line

Where the grunt-search term provides an anchor to search for, the optional :console specifies a specific grunt task to ignore (excluding it ignores the line for all tasks), and the ignore directive indicates the scope of what to ignore.

Multi-line ignores can be added fairly easily as well:

// grunt-search[:console] ignore:start
// grunt-search[:console] ignore:end

I feel like this strikes the right balance between tool scope and flexibility. It doesn't require a complicated generic ignore system, but the users can still mark specific sections of code to not check, and specify specific (or all) tasks to ignore.

Implementation Notes

The place to do this would be at the start of the main loop which iterates through each line.

Specify a regex for the ignore: grunt-search[:(\S)] ignore:line (and start / end). At the start of the loop, run the regex for a match. If there is one, check the regex's group for a specific grunt command, and if the command doesn't match the one proceed with the search.

If a line ignore is found, just continue with the loop.

If a multi-line ignore is found, continue with the loop until the multi-line end ignore is found.

If a multi-line end ignore is found without a preceding multi-line start ignore, throw a warning (but probably don't fail the task).

If there are "nested" multi-line ignores, throw an error. Or implement a counter which increments on multi-line starts and decrements on multi-line ends, and only resumes searching when it reaches 0. Or just wait until the the first multi-line end is reached, which closes any / all preceding starts. As long as this case is documented clearly, we'll all be fine.

Might also want to throw warnings if there are malformed comments, such as:

  • // grunt-search
  • // grunt-search[:console]
  • // grunt-search[:console] [not ignore]
  • // grunt-search[:console] ignore
  • // grunt-search[:console] ignore:[not line / start / end]

Other Possible Workarounds

Excluding specific files is problematic because nothing in the file will be checked (and most of the lines should be), and we'd have to update the file listings quite often- basically anytime a file becomes an exception to the search term.

Closing Thoughts

I'm super busy through January but can try to get a PR out in February. If you want to implement this, I'd also be happy to buy you a beer via Paypal / Venmo / Patreon / Grattipay.

Cheers,
jtfairbank

Wrong example

Dear Ben Keen! Do you know that documentation is not correct? I spend 2 hours trying to find out what I do wrong. This plugin doesn't expect regex itself - only inside part of it. For example - I was trying to find usages of obsolete jQuery function using /toggle()/ and you code turns it into //toggle()//g - so sure it wouldn't work. You wrote that we can use /bla-bla-bla/ but we should put only bla-bla-bla inside searchString param otherwise it will find nothing.
Have a nice day!

Have searchString accept arrays

I was hoping to use this in a project, but I need to search for a large set of data in an array rather than a single string or regex.

Support async or Promises in handlers

There seems to be no way to access grunt’s this.async() from within onComplete. See this stackoverflow question.

I know that in the case of that question, fs.writeFileSync() is a workaround or using grunt.file.write() is more correct. But the user may want to

  1. Call some other async API for which there is no synchronous alternative.
  2. Have a way for the search task to wait for the completion before continuing (i.e., I know I can start a Promise() in the callback—which keeps node from exiting before the operation completes. But there’s no way to make the launched task wait right now and you might be launching tasks using grunt’s API rather than the CLI and want to know that when the task completes all of the assets generated by the task are fully written to disk).

IMO, all of the callbacks should just have their return value evaluated by Promise.resolve(). This would work with existing synchronous callbacks and magically support Promises whenever callbacks opt in by returning Promises.

How can we create a custom result file?

We use your grunt-search module to search translation key in our source files here is config :

    search: {
      inlineStyles: {
        files: {
          src: ["views/**/*.hbs", "views/*.hbs", "public/javascripts/*.js"]
        },
        options: {
          searchString:/(?:trads|\$\.i18n)\._\((["'])[^\1]*?\1\)/g,
          logFormat: 'json',
          logFile: "./scripts/frontTranslation/translationTestResults.json"
        }
      }
    },

We want logFile contains only the matched strings with some custom search and replace... I saw the customLogFormatCallback but I don't figure out how I can write my custom result in the final file...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.