1

I am creating a lot of HTML files that will be used as the design guideline for a development project. An HTML file will include the following HTML tags:

<title> Design pattern 001 </title>
<meta data-details="This design should show the basic homepage" />
<meta data-status="Approved" />

All of these files sit in a directory:

/designs/design-pattern-001.html    
...
/designs/design-pattern-100.html

I want to run a grunt task that will create a separate index.html with the following syntax:

<table>
  <tr>
    <td> Aprooved</td>
    <td> Design pattern 001 </td>
    <td> This design should show the basic homepage </td>
  </tr>
       ....
</table>

My development team will then use index.html

I tried the solution here: How do I generate a list of files in html, using a grunt task? but this only gets me the filename, not any of the data attributes within each respective HTML.

Is the only other solution to use jQuery (or JavaScript) to get this for each respective file like the solution here: Get HTML code of a local HTML file in Javascript ?

Community
  • 1
  • 1
rlsaj
  • 735
  • 1
  • 12
  • 37

1 Answers1

1

This can be done via a grunt custom task. I've gone ahead and built an example project on github using your outlined requirements above.

I was able to do it using the following node modules:

  • xmldom and xpath to parse the html files
  • pug to create the generated html from a template

Please check out the github project but here is most of the important code:

Grunt file ( in root of project ):

Defines custom tasks options

module.exports = function(grunt) {

    // Project configuration.
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        parseAndIndexHtmlFiles : {

            options : {
                output: "index.html",
                directoryToParse: "design",
                jadeTemplate: "index.jade",
                parseHtmlTags: ["title"],
                parseAttributes: ["data-details", "data-status"]
            }
        }
    });

    grunt.loadTasks('tasks');

    grunt.registerTask('default', [ "parseAndIndexHtmlFiles" ]);

};

Custom task file ( in tasks folder of project ) /tasks/parseDesignHtml.js:

var DOMParser = require('xmldom').DOMParser;
var xpath = require('xpath');
var Promise = require("bluebird");
var pug = require('pug');
var tidy = require('htmltidy').tidy;
var fs = Promise.promisifyAll(require("fs"));

var options, done, globalGrunt = null;
var fileIndex = [];

module.exports = function(grunt) {

    globalGrunt = grunt;
    grunt.registerTask('parseAndIndexHtmlFiles', function () {

        done = this.async();
        options = this.options({
            output : "",
            directoryToParse : "",
            jadeTemplate : "",
            parseHtmlTags : [ ],
            parseAttributes : [ ]
        });

        parseHtmlFiles(options.directoryToParse);
    });
};

function parseHtmlFiles(directory) {


    fs.readdirAsync(directory).map(function (filename) {

        if (filename.match(/.html$/)) {

            return readFile(directory + "/" + filename);
        }

    }).then(function (results) {

        var contents = [];
        for(var i = 0; i < results.length; i++){
            if(results[i]){
                contents.push(results[i]);
            }
        }

        var html = pug.renderFile(options.jadeTemplate , {
            files : contents
        });

        tidy(html, {
            indent: true
        }, function (err, result) {

            if (err) {
                globalGrunt.fail.fatal(err);
            }

            fs.writeFile(options.output, result, function (err) {

                if (err) {
                    globalGrunt.fail.fatal(err);
                }

                done();
            });
        });
    });

}

function readFile(filename) {
    var promise = Promise.pending();

    fs.readFile(filename, function (err, data) {

        if (err) {
            promise.reject(err);
        } else if (data) {
            var doc = new DOMParser().parseFromString(data.toString(), "text/html");
            var params = parseDocument(doc);
            promise.resolve(new IndexedFile(filename, params));
        } else {
            promise.reject("No Data");
        }

    });

    return promise.promise;
}

function parseDocument(doc) {

    var params = {
        tags : {},
        attributes : {}
    };

    options.parseHtmlTags.forEach(function (tag) {

        var tags = doc.getElementsByTagName(tag);
        if (tags.length > 0) {
            params.tags[tag] = tags[0].firstChild.data;
        }
    });

    options.parseAttributes.forEach(function (attrName) {

        var attr = xpath.select("//@" + attrName, doc);
        if (attr.length > 0) {
            params.attributes[attrName] = attr[0].nodeValue;
        }
    });

    return params;
} 


function IndexedFile(path, parameters) {
    this.path = path;
    this.parameters = parameters;
}

Template file ( in root of project ) /index.jade:

doctype html
html(lang="en")
  head
    title="Stackoverflow Question 40011711"
  body
    table
      - files.forEach(function (item) {
        tr
          td
            a(href=item.path)=item.parameters.tags["title"]
          td=item.parameters.attributes["data-status"]
          td=item.parameters.attributes["data-details"]
      - })
Dave Thomas
  • 3,667
  • 2
  • 33
  • 41
  • This is an amazing response, and you've opened my eyes to the wonderful world of pug(jade). I have been able to perform exactly what I need to, and now I'm changing the layout of index.js even more. Thank you very very much. – rlsaj Oct 14 '16 at 01:33