diff --git a/Gruntfile.js b/Gruntfile.js index 70989c7..bbb2817 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,63 +1,72 @@ 'use strict'; -module.exports = function (grunt) { - grunt.loadTasks('tasks'); - grunt.loadNpmTasks('grunt-mocha-test'); - grunt.loadNpmTasks('grunt-contrib-clean'); - grunt.loadNpmTasks('grunt-contrib-jshint'); +module.exports = function(grunt) { + grunt.loadTasks('tasks'); + grunt.loadNpmTasks('grunt-mocha-test'); + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-jsbeautifier'); - grunt.initConfig({ - swig: { - development: { - init: { - allowErrors: false, - autoescape: true + grunt.initConfig({ + swig: { + development: { + init: { + allowErrors: false, + autoescape: true + }, + dest: 'test/dest', + src: ['**/*.swig', '!templates/*.swig'], + siteUrl: 'http://mydomain.net/', + generateSitemap: true, + generateRobotstxt: true, + test: { + var1: 'long path file', + var2: 'short path file' + }, + sitemap_priorities: { + '_DEFAULT_': '0.7', + 'fixtures/index.html': 0.8 + } + } }, - dest: 'test/dest', - src: ['**/*.swig', '!templates/*.swig'], - siteUrl: 'http://mydomain.net/', - generateSitemap: true, - generateRobotstxt: true, - test: { - var1: 'long path file', - var2: 'short path file' + jshint: { + options: { + 'jshintrc': '.jshintrc', + 'reporter': 'jslint', + 'reporterOutput': 'jslint.xml', + 'force': true + }, + all: [ + 'Gruntfile.js', + 'tasks/*.js' + ] }, - sitemap_priorities: { - '_DEFAULT_': '0.7', - 'fixtures/index.html': 0.8 + mochaTest: { + options: { + reporter: 'xunit', + captureFile: 'tests.xml' + }, + files: ['test/*_test.js'] + }, + clean: { + files: 'test/dest' + }, + jsbeautifier: { + files: [ + 'tasks/**/*.js', + 'Gruntfile.js' + ], + options: {} } - } - }, - jshint: { - options: { - 'jshintrc': '.jshintrc', - 'reporter': 'jslint', - 'reporterOutput': 'jslint.xml', - 'force': true - }, - all: [ - 'Gruntfile.js', - 'tasks/*.js' - ] - }, - mochaTest: { - options: { - reporter: 'xunit', - captureFile: 'tests.xml' - }, - files: ['test/*_test.js'] - }, - clean: { - files: 'test/dest' - } - }); + }); - grunt.registerTask('test', [ - 'clean', - 'swig', - 'jshint', - 'mochaTest', - ]); + grunt.registerTask('test', [ + 'clean', + 'swig', + 'jshint', + 'mochaTest', + ]); - grunt.registerTask('default', 'test'); + grunt.registerTask('default', 'test'); + grunt.registerTask('beautify', ['jsbeautifier']); }; diff --git a/README.md b/README.md index 6c9a596..4828145 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ > A static site compiler for grunt based on [swig templates](http://paularmstrong.github.com/swig/) +## Version 1.0.0 + +This branch will serve as the integration point for the next release: v1.0.0. + +[Check out the discussion of v1.0.0 features](https://github.com/rtgibbons/grunt-swig/issues/33) + +The latest stable release is [v0.2.1](https://github.com/rtgibbons/grunt-swig/tree/master) which can be found in the master branch + ## Call for Help As time has passed, I haven't had a need to use this beyond the initial creation of the task. I've tried to keep up with it, but I no longer have the time to bug fix and verify the pull requests. If you are interested in helping out, get some pull requests coming in and let me know you are interested in maintaining this package. @@ -32,51 +40,67 @@ In your project's Gruntfile, add a section named `swig` to the data object passe ```js swig: { - development: { - init: { - autoescape: true + // The default options. If the task does not specify an 'options' object, this will be used instead. Or it can be omitted entirely. + options: { + generateSitemap: false, + templateData: { + greeting: "Hello Default Greeting!" + }, + }, + dev: { + options: { + generateSitemap: true, // Whether or not to generate a sitemap + siteUrl: 'http://mydomaindev.net/', // Used when sitemap is generated + templateData: { + greeting: "Hello overridden Greeting!", + myVar: "some other variable" + }, + // The sitemap weightings. If omitted, the default will be used + sitemap: { + 'default': { // Override the default sitemap options. This will be used if the specific file is not specified. + changefreq: 'never', + lastmod: '2013-01-01', + priority: '0.5' + }, + 'index.html': { + changefreq: 'daily', + lastmod: '2009-01-01', + priority: '0.8' + }, + 'test/index.html': { + changefreq: 'monthly', + priority: '0.4' + } + }, + swigOptions: { // Options to pass in to swig + cache: false + } + }, + src: ['**/*.swig'], // The pattern to search for files + dest: 'www/' // The destination directory path where the generated files will be placed }, - dest: "www/", - src: ['**/*.swig'], - generateSitemap: true, - generateRobotstxt: true, - siteUrl: 'http://mydomain.net/', - production: false, - fb_appid: '1349v', - ga_account_id: 'UA-xxxxxxxx-1', - robots_directive: 'Disallow /', - sitemap_priorities: { - '_DEFAULT_': '0.5', - 'index.html': '0.8', - 'subpage.html': '0.7' + prod: { + src: ['src-prod/**/*.swig'], + dest: 'www-prod/' } - } } ``` -Grunt Swig will loop through files listed in `src` +Grunt Swig will loop through files listed in ```src``` + +__Note: Only files ending with ```.swig``` will be processed__ + +Your file extension is specified within your filename. For example: -Ex. `source/index.swig`. It will look for a `source/index.json` and add it to -the rest of the variables provided in `swig:development` or in `global.js`, and then run swig -against `source/index.swig` saving the output to `www/index.html` +```index.html.swig``` will generate the file ```index.html``` -You can also provide context, for example `swig:development:blue` which will -perform the same actions above, but after process the JSON it will also expand -the variable list with `source/index.blue.json` and provide the variable -`context` to the rest of the swig template. +```myFile.txt.swig``` will generate the file ```myFile.txt``` -The siteUrl is used to build a sitemap. Right now all the other elements are -hard coded, eventually this could be set in the config object. +The siteUrl is used to build a sitemap. You can adjust the defaults for each file/page in the options. -The 'sitemap_priorities' will set custom priorities based on the page name when -building the sitemap. The first item '_DEFAULT_' will be the default priority -used if a page name is not explicitly set. In the above example the page -'index.html' would be given priority of '0.8', 'subpage.html' would be given -a priority of '0.7', and all other pages would get a priority of '0.5', -You need to give the relative path to the output html file for this to work. +To override the default extension of `.html` by specifying the ```generatedExtension``` (without the period) in your config. -Path and base name of the source template file are available in `tplFile` variable, `tplFile.path` for -the path and `tplFile.basename` for the basename. +To write your files directly to the specified `dest` directory and not have it expand the subdirectory path, set ```expandDirectories``` to ```false```. ## Contributing In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/). diff --git a/grunt-swig/index.js b/grunt-swig/index.js new file mode 100644 index 0000000..264aeb7 --- /dev/null +++ b/grunt-swig/index.js @@ -0,0 +1,13 @@ +"use strict" +var swig = require('swig'); +var fs = require('fs'); + +var GruntSwig = { + init: function init(options) { + console.log(options.config.data); + + + } +}; + +module.exports = GruntSwig; diff --git a/package.json b/package.json index 66816bf..ecb4b05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "grunt-swig", - "version": "0.2.1", + "version": "0.9.0", "description": "Static site compiler built around swig", "repository": { "type": "git", @@ -26,8 +26,11 @@ }, "devDependencies": { "grunt": "~0.4.1", + "chai": "~1.9.2", + "mocha": "~1.21.4", "grunt-contrib-clean": "~0.5.0", "grunt-mocha-test": "~0.7.0", + "grunt-jsbeautifier": "^0.2.7", "grunt-contrib-jshint": "~0.7.2" }, "peerDependencies": { diff --git a/scratch.txt b/scratch.txt new file mode 100644 index 0000000..5d2ab58 --- /dev/null +++ b/scratch.txt @@ -0,0 +1,16 @@ +swig: { + options { + generateSitemap: true + }, + dev: { + options{ + generateSitemap: false + }, + src: [**/*.swig], + dest: 'www/' + }, + prod: { + src: [**/*.swig], + dest: 'www/' + } +} diff --git a/tasks/swig.js b/tasks/swig.js index bce763b..2560332 100644 --- a/tasks/swig.js +++ b/tasks/swig.js @@ -1,93 +1,109 @@ 'use strict'; +var fs = require('fs'); +var swig = require('swig'); +var path = require('path'); module.exports = function(grunt) { + grunt.registerMultiTask('swig', 'swig templater', function(tpl_context) { + var config = this; + var context = tpl_context || ''; + var date = new Date(); + var d = date.toISOString(); + var globalVars = {}; + var options = config.options(); - var fs = require('fs'), - swig = require('swig'), - path = require('path'); - - grunt.registerMultiTask('swig', 'swig templater', function(tpl_context) { - var config = this, - context = tpl_context || '', - pages = [], - date = new Date(), - d = date.toISOString(), - defaultPriority = (config.data.sitemap_priorities !== undefined)? config.data.sitemap_priorities._DEFAULT_ : '0.5', - generateSitemap = config.data.generateSitemap != undefined ? config.data.generateSitemap : true, - generateRobotstxt = config.data.generateRobotstxt != undefined ? config.data.generateSitemap : true, - globalVars = {}; - - if (config.data.init !== undefined) { - swig.setDefaults(config.data.init); - } - - try { - globalVars = grunt.util._.extend(config.data, grunt.file.readJSON(process.cwd() + '/global.json')); - } catch (err) { - globalVars = grunt.util._.clone(config.data); - } - - this.filesSrc.forEach(function(file) { - if (!grunt.file.exists(file)) { - grunt.log.warn('Source file "' + file.src + '" not found.'); - - return false; - } else { - var dirName = path.dirname(file).split('/'), - destPath = dirName.splice(1, dirName.length).join('/'), - outputFile = path.basename(file, '.swig'), - htmlFile = config.data.dest + '/' + destPath + '/' + outputFile + '.html', - tplVars = {}, - contextVars = {}; - - try { - tplVars = grunt.file.readJSON(path.dirname(file) + '/' + outputFile + ".json"); - } catch(err) { - tplVars = {}; + if (options.swigOptions) { + swig.setDefaults(options.swigOptions); } - try { - contextVars = grunt.file.readJSON(path.dirname(file) + '/' + outputFile + "." + context + ".json"); - } catch(err) { - contextVars = {}; - } + var pages = {}; + + + var baseUrl = options.siteUrl || ''; + + config.filesSrc.forEach(function(file) { + var globalTemplateData = options.templateData || {}; + + if (!file) { + grunt.log.warn('Source file not found.'); + } else { + + var tpl = swig.compileFile(file); + var filePathArr = file.split('/'); + var fullFileName = filePathArr[filePathArr.length - 1]; + var matches = fullFileName.match(/(.*)\.([^\.]*)\.?([^\.]*)$/); + + if (matches && matches[1] && matches[2]) { + var fileName = matches[1]; + var ext = matches[2]; + var templateExtension = matches[3]; + if (!templateExtension) { + templateExtension = ext; + ext = ''; + } else { + ext = '.' + ext + } + + var relativeOutputFile = path.dirname(file) + '/' + fileName + ext; + var outputFile = config.data.dest + relativeOutputFile; - tplVars.context = context; - tplVars.tplFile = { - path: destPath, - basename: outputFile - }; - - grunt.log.writeln('Writing HTML to ' + htmlFile); - - grunt.file.write(htmlFile, swig.renderFile(file, grunt.util._.extend(globalVars, tplVars, contextVars))); - - if (config.data.sitemap_priorities !== undefined && config.data.sitemap_priorities[destPath + '/' + outputFile + '.html'] !== undefined) { - pages.push({ - url: config.data.siteUrl + htmlFile.replace(config.data.dest + '/', ''), - date: d, - changefreq: 'weekly', - priority: config.data.sitemap_priorities[destPath + '/' + outputFile + '.html'] - }); - } else { - pages.push({ - url: config.data.siteUrl + htmlFile.replace(config.data.dest + '/', ''), - date: d, - changefreq: 'weekly', - priority: defaultPriority - }); + // Only process files that end in .swig + if (templateExtension == 'swig') { + // Swig file match. Build up all the data required to create the destination file. + pages[outputFile] = pages[outputFile] || {}; + pages[outputFile].src = fullFileName; + pages[outputFile].dest = outputFile; + + var templateData = {}; + try { + templateData = grunt.file.readJSON(path.dirname(file) + '/' + fileName + ext + '.json'); + } catch (e) { + grunt.log.warn('No json file corresponding with: ' + file + '. Using only global template data.'); + } + pages[outputFile].templateData = {}; + grunt.util._.extend(pages[outputFile].templateData, globalTemplateData, templateData); + pages[outputFile].tpl = tpl; + pages[outputFile].date = d; + pages[outputFile].url = baseUrl + outputFile; + + // Base sitemap defaults + pages[outputFile].changefreq = 'daily'; + pages[outputFile].lastmod = d; + pages[outputFile].priority = '0.8'; + + // Check if this file has specific sitemap properties set + if (options.sitemap && options.sitemap[relativeOutputFile]) { + pages[outputFile].changefreq = options.sitemap[relativeOutputFile].changefreq || pages[outputFile].changefreq; + pages[outputFile].lastmod = options.sitemap[relativeOutputFile].lastmod || pages[outputFile].lastmod; + pages[outputFile].priority = options.sitemap[relativeOutputFile].priority || pages[outputFile].priority; + } else if (options.sitemap && options.sitemap.default) { + // If not, check to see if there are defaults + pages[outputFile].changefreq = options.sitemap.default.changefreq || pages[outputFile].changefreq; + pages[outputFile].lastmod = options.sitemap.default.lastmod || pages[outputFile].lastmod; + pages[outputFile].priority = options.sitemap.default.priority || pages[outputFile].priority; + } + } + } else { + grunt.log.warn('Unable to process file as template: ' + fullFileName); + } + } + }); + + for (var i in pages) { + var page = pages[i]; + grunt.log.writeln('Creating file: ' + page.dest); + grunt.file.write(page.dest, page.tpl(page.templateData)); } - } - }); - if (generateSitemap) { - grunt.log.writeln('Creating sitemap.xml'); - grunt.file.write(config.data.dest + '/sitemap.xml', swig.renderFile(__dirname + '/../templates/sitemap.xml.swig', { pages: pages})); - } + if (options.generateSitemap) { + var currentDate = date.toISOString(); - if (generateRobotstxt) { - grunt.log.writeln('Creating robots.txt'); - grunt.file.write(config.data.dest + '/robots.txt', swig.renderFile(__dirname + '/../templates/robots.txt.swig', { robots_directive: config.data.robots_directive || '' })); - } - }); + grunt.log.writeln('Creating sitemap.xml'); + var sitemapData = options.sitemap || {}; + grunt.file.write(config.data.dest + '/sitemap.xml', swig.renderFile(__dirname + '/../templates/sitemap.xml.swig', { + sitemapData: sitemapData, + pages: pages + })); + } + }); }; diff --git a/templates/robots.txt.swig b/templates/robots.txt.swig deleted file mode 100644 index becf404..0000000 --- a/templates/robots.txt.swig +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -{{ robots_directive }} \ No newline at end of file diff --git a/templates/sitemap.xml.swig b/templates/sitemap.xml.swig index 22f8068..eb33c56 100644 --- a/templates/sitemap.xml.swig +++ b/templates/sitemap.xml.swig @@ -1,17 +1,17 @@ - {%- for page in pages %} + {% for page in pages %} {{ page.url }} - {%- if page.date %} - {{ page.date }} - {%- endif %} - {%- if page.changefreq %} + {% if page.date %} + {{ page.lastmod }} + {% endif %} + {% if page.changefreq %} {{ page.changefreq }} - {%- endif %} - {%- if page.priority %} + {% endif %} + {% if page.priority %} {{ page.priority }} - {%- endif %} + {% endif %} - {%- endfor %} + {% endfor %} diff --git a/test/grunt-swig-test.js b/test/grunt-swig-test.js new file mode 100644 index 0000000..86a82a0 --- /dev/null +++ b/test/grunt-swig-test.js @@ -0,0 +1,10 @@ +var expect = require('chai').expect; +var GruntSwig = require(__dirname + '/../tasks/swig.js'); + +describe('Grunt Swig', function () { + it('should load swig', function(done) { + var gruntSwig = new GruntSwig; + console.log(gruntSwig); + done(); + }); +});