MDL-52127 grunt: be aware of third party paths
authorDan Poltawski <dan@moodle.com>
Fri, 13 May 2016 20:20:21 +0000 (21:20 +0100)
committerDan Poltawski <dan@moodle.com>
Mon, 13 Jun 2016 23:37:29 +0000 (00:37 +0100)
1) Parse thirdpartylibs.xml and generate an array of third party
file paths to use in grunt tasks
2) In the lint tasks, we filter third party files from being linted
3) We add a new task to generate ignore files - currently for eslint,
but will be potentially useful for other things in the future
4) Remove .eslintignore from source control

Why have the ability to generate a .eslintignore file? For tooling
integration - by having the eslintignore file people can use other
eslint tools without having to just use grunt (e.g. editor
integrations).

.eslintignore [deleted file]
.gitignore
Gruntfile.js
npm-shrinkwrap.json
package.json

diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644 (file)
index a682167..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-theme/bootstrapbase/amd/src/bootstrap.js
-lib/amd/src/mustache.js
-lib/amd/src/loglevel.js
-lib/editor/atto/yui/src/rangy/
-**/yui/src/*/meta/*.js
-lib/yui/src/notification/js/shared.js
index 1dab0f7..71a7224 100644 (file)
@@ -36,3 +36,4 @@ composer.phar
 /lib/yuilib/*/*/*-coverage.js
 atlassian-ide-plugin.xml
 /node_modules/
+.eslintignore
index 2ec167e..3cc3bb0 100644 (file)
 module.exports = function(grunt) {
     var path = require('path'),
         tasks = {},
-        cwd = process.env.PWD || process.cwd();
+        cwd = process.env.PWD || process.cwd(),
+        async = require('async'),
+        DOMParser = require('xmldom').DOMParser,
+        xpath = require('xpath');
 
     // Windows users can't run grunt in a subdirectory, so allow them to set
     // the root by passing --root=path/to/dir.
@@ -63,6 +66,55 @@ module.exports = function(grunt) {
         return destPath;
     };
 
+    /**
+     * Find thirdpartylibs.xml and generate an array of paths contained within
+     * them (used to generate ignore files and so on).
+     *
+     * @return {array} The list of thirdparty paths.
+     */
+    var getThirdPartyPathsFromXML = function() {
+        var thirdpartyfiles = grunt.file.expand('*/**/thirdpartylibs.xml');
+        var libs = ['node_modules/', 'vendor/'];
+
+        thirdpartyfiles.forEach( function(file) {
+          var dirname = path.dirname(file);
+
+          var doc = new DOMParser().parseFromString(grunt.file.read(file));
+          var nodes = xpath.select("/libraries/library/location/text()", doc);
+
+          nodes.forEach(function(node) {
+            var lib = path.join(dirname, node.toString());
+            if (grunt.file.isDir(lib)) {
+                // Ensure trailing slash on dirs.
+                lib = lib.replace(/\/?$/, '/');
+            }
+
+            // Look for duplicate paths before adding to array.
+            if (libs.indexOf(lib) === -1) {
+                libs.push(lib);
+            }
+          });
+        });
+        return libs;
+    };
+
+    // An array of paths to third party directories.
+    var thirdPartyPaths = getThirdPartyPathsFromXML();
+
+    /**
+     * Determine if the file is a Moodle file, or its listed in
+     * the thirdpartylibs.xml file paths as a third party file.
+     *
+     * @param {string} file The file path to determine if thirdparty
+     * @return {bool} false If thid party file.
+     */
+    var isMoodleFile = function(file) {
+      if (grunt.file.isMatch(thirdPartyPaths, file)) {
+        return false;
+      }
+      return true;
+    };
+
     // Project configuration.
     grunt.initConfig({
         jshint: {
@@ -70,20 +122,20 @@ module.exports = function(grunt) {
             amd: { src: amdSrc }
         },
         eslint: {
-            // Even though warnings dont stop the build we don't display warnings by default because:
-            // * At this moment we've got too many core warnings
-            // * It will complain about ignored files (https://github.com/sindresorhus/grunt-eslint/issues/119)
-            // * It's better experience to use editor integrations or eslint natively
+            // Even though warnings dont stop the build we don't display warnings by default because
+            // at this moment we've got too many core warnings.
             options: { quiet: !grunt.option('show-lint-warnings') },
             // Check AMD files. We add some stricter rules which we can't apply to the default configuration due
             // to YUI rollups.
             amd: {
               src: amdSrc,
+              filter: isMoodleFile,
               options: { rules: {'no-undef': 'error', 'no-unused-vars': 'error', 'no-empty': 'error', 'no-unused-expressions': 'error'} }
             },
             // Check YUI module source files.
             yui: {
-               src: ['**/yui/src/**/*.js']
+               src: ['**/yui/src/**/*.js', '!*/**/yui/src/*/meta/*.js'],
+               filter: isMoodleFile
             }
         },
         uglify: {
@@ -131,6 +183,15 @@ module.exports = function(grunt) {
         }
     });
 
+    /**
+     * Generate ignore files (utilising thirdpartylibs.xml data)
+     */
+    tasks.ignorefiles = function() {
+      // Generate .eslintignore.
+      var eslintIgnores = ['# Generated by "grunt ignorefiles"', '*/**/yui/src/*/meta/', '*/**/build/'].concat(thirdPartyPaths);
+      grunt.file.write('.eslintignore', eslintIgnores.join('\n'));
+    };
+
     /**
      * Shifter task. Is configured with a path to a specific file or a directory,
      * in the case of a specific file it will work out the right module to be built.
@@ -139,8 +200,7 @@ module.exports = function(grunt) {
      * so be careful to to call done().
      */
     tasks.shifter = function() {
-        var async = require('async'),
-            done = this.async(),
+        var done = this.async(),
             options = grunt.config('shifter.options');
 
         // Run the shifter processes one at a time to avoid confusing output.
@@ -260,6 +320,7 @@ module.exports = function(grunt) {
 
     // Register JS tasks.
     grunt.registerTask('shifter', 'Run Shifter against the current directory', tasks.shifter);
+    grunt.registerTask('ignorefiles', 'Generate ignore files for linters', tasks.ignorefiles);
     grunt.registerTask('yui', ['eslint:yui', 'shifter']);
     grunt.registerTask('amd', ['eslint:amd', 'jshint', 'uglify']);
     grunt.registerTask('js', ['amd', 'yui']);
index 45c7704..6411bc5 100644 (file)
       "from": "xml2js@>=0.2.0 <0.3.0",
       "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.8.tgz"
     },
+    "xmldom": {
+      "version": "0.1.22",
+      "from": "xmldom@latest",
+      "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.22.tgz"
+    },
+    "xpath": {
+      "version": "0.0.23",
+      "from": "xpath@latest",
+      "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.23.tgz"
+    },
     "xregexp": {
       "version": "3.1.1",
       "from": "xregexp@>=3.0.0 <4.0.0",
index d6d275d..5989a0e 100644 (file)
@@ -10,6 +10,8 @@
     "grunt-contrib-uglify": "0.11.0",
     "grunt-contrib-watch": "0.6.1",
     "grunt-eslint": "^18.1.0",
-    "shifter": "0.5.0"
+    "shifter": "0.5.0",
+    "xmldom": "^0.1.22",
+    "xpath": "0.0.23"
   }
 }