Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using grunt js, how can I dynamically add the <title> to static html files?

More specifically, I want to add the titles by referencing an external JSON file, let's call it titles.json.

One of the things I use grunt for is to build static HTML files for design and debugging. This would be really helpful during development, not just for titles but potentially other data as well, such as setting the active nav link.

I'm currently concatenating HTML files using the process described by the accepted answer here: Using grunt concat, how would I automate the concatenation of the same file to many other files?

EDIT: I now use assemble for this, it was created to make working with templates and external data very easy.

like image 664
jonschlinkert Avatar asked Oct 19 '12 21:10

jonschlinkert


1 Answers

Here is my take on this. Uses grunt's standard template mechanism, page metadata is defined in an object, outside of actual page files, as you suggested (I can't say I like this).

gruntfile (including the wrap task from Using grunt concat, how would I automate the concatenation of the same file to many other files?):

/*global module:false*/    
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    meta: {
      version: '0.1.0',
      banner: '/*! PROJECT_NAME - v<%= meta.version %> - ' +
        '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
        '* http://PROJECT_WEBSITE/\n' +
        '* Copyright (c) <%= grunt.template.today("yyyy") %> ' +
        'YOUR_NAME; Licensed MIT */'
    },

    // Paths
    project: {
      partials: 'assets/partials',  // don't put trailing slash
      pages:    'assets/pages',     // don't put trailing slash
      less:     'assets/less',
      css:      'assets/css',
      img:      'assets/img',
      js:       'assets/js'
    },

    // Used for page title and nav generation.
    // It's an array to ensure correct order for nav
    pages: [{   
        file: 'index.html',
        title: 'My homepage'
        /* This format can be extended to something like:
         * {
         *      title: 'My homepage',
         *      header: 'Welcome to my site',
         *      navtitle: 'Home'
         * }
         * Although I think it's best to keep page metadata as close to content as possible,
         * i.e. right inside pages files (think YAML headers in Jekyll pages)  
         */
    }, {
        file: 'about.html',
        title: 'About me'
    }, {
        file: 'contact.html',
        title: 'Contact'
    }],

    // wraps files with header and footer
    wrap: {
        html: {
            header: '<%= project.partials %>/head.tmpl',
            footer: '<%= project.partials %>/footer.tmpl',
            src: [
                '<%= project.pages %>/index.html',
                '<%= project.pages %>/about.html',
                '<%= project.pages %>/contact.html'
            ],
            dest: '.'   // destination *directory*, probably better than specifying same file names twice
        }
    },

    // processes templates in page files
    buildPages: {
        pages: '<config:pages>',    // page files metadata
        dir: '.'                    // page files location dir
    }

  });


  // Default task.
  grunt.registerTask('default', 'wrap buildPages');

  grunt.registerMultiTask('wrap', 'Wraps source files with specified header and footer', function() {
        var data = this.data,
            path = require('path'),
            dest = grunt.template.process(data.dest),
            files = grunt.file.expandFiles(this.file.src),
            header = grunt.file.read(grunt.template.process(data.header)),
            footer = grunt.file.read(grunt.template.process(data.footer)),
            sep = grunt.utils.linefeed; 

        files.forEach(function(f) {
            var p = dest + '/' + path.basename(f),
                contents = grunt.file.read(f);

            grunt.file.write(p, header + sep + contents + sep + footer);
            grunt.log.writeln('File "' + p + '" created.');
        });
  });

  grunt.registerTask('buildPages', 'Processes templates in page files', function() {
  // NOTE: current implementation replaces files
    var data = grunt.config('buildPages'),
        pages = data.pages,
        dir = data.dir,
        contents,
        curPath;

    pages.forEach(function(page) {
        curPath = dir + '/' + page.file;
        contents = grunt.file.read(curPath);

        // feed the entire pages array and current entry to the template
        grunt.file.write(curPath, grunt.template.process(contents, {
            pages: pages,
            curPage: page
        }));
        grunt.log.writeln('Page at "' + curPath + '" built.');
    });
  });
};

head.tmpl:

<!DOCTYPE html>
<html>
    <head>
        <title><%= curPage.title %></title>
    </head>
    <body>
        <!-- NAV -->
        <ul class="nav">
            <%
                pages.forEach(function(p) {
                    print(
                        '<li class="' + ((curPage === p) ? 'active' : '') + '">' +
                            ((curPage === p) ? p.title : ('<a href="' + p.file + '">' + p.title + '</a>')) + 
                        '</li>\n'
                    );
                });
            %>
        </ul>
        <!-- /NAV -->

        <!-- MAIN CONTENT -->
        <div class="main">

footer.tmpl:

        </div>
        <!-- /MAIN CONTENT -->
    </body>
</html>
like image 114
Dmitry Pashkevich Avatar answered Oct 05 '22 23:10

Dmitry Pashkevich