diff --git a/Gruntfile.js b/Gruntfile.js index 70989c7..fd590ce 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -18,6 +18,10 @@ module.exports = function (grunt) { siteUrl: 'http://mydomain.net/', generateSitemap: true, generateRobotstxt: true, + tags: { + countargs: require('./test/tag'), + markdown: require('./node_modules/swig-extras/lib/tags/markdown') + }, test: { var1: 'long path file', var2: 'short path file' diff --git a/README.md b/README.md index 607a4da..d2f88ef 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ swig: { init: { autoescape: true }, + tags: { + countargs: require('./test/tag') + }, dest: "www/", src: ['**/*.swig'], generateSitemap: true, @@ -70,6 +73,28 @@ You need to give the relative path to the output html file for this to work. 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. +The 'tags' property is an object that allows you to extend swig with any tag found on +internet with the [common structure](https://github.com/paularmstrong/swig/blob/v1.3.2/lib/tags/block.js). +Key, value of the object is respectively: name of the tag, library/file required. +Value it's always a `require()`, it may be a _custom_ file of yours or another NPM library. +[swig-extras](https://github.com/paularmstrong/swig-extras) and +[swig-extensions](https://github.com/assemble/swig-extensions) tags are supported, you need to require +directly the file, ex: `require('./node_modules/swig-extras/lib/tags/markdown')` + +Common structure: +```javascript + exports.parse = function(str, line, parser, types, stack, options) + exports.compile = function(compiler, args, content, parents, options, blockName) + exports.ends = true/false + exports.block = true/false +``` +Refer to [official Swig documentation](http://paularmstrong.github.io/swig/docs/extending/#tags). + +The 'filters' property allows you to expose custom filters to your templates. +In the above example the filter `f` is created and mapped to the CommonJS file +module named `myfilter.js` at the root of the project. The module must export a +function whose name matches the filter ('f' in this case). + ## 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/package.json b/package.json index 66816bf..53b89c8 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "grunt": "~0.4.1", "grunt-contrib-clean": "~0.5.0", "grunt-mocha-test": "~0.7.0", - "grunt-contrib-jshint": "~0.7.2" + "grunt-contrib-jshint": "~0.7.2", + "swig-extras": "0.0.1" }, "peerDependencies": { "grunt": "~0.4.1" diff --git a/tasks/swig.js b/tasks/swig.js index bce763b..bf6ecfa 100644 --- a/tasks/swig.js +++ b/tasks/swig.js @@ -21,6 +21,20 @@ module.exports = function(grunt) { swig.setDefaults(config.data.init); } + if (config.data.tags !== undefined) { + Object.keys(config.data.tags).forEach(function (tag, index) { + var tagObj = config.data.tags[tag]; + // compatibility with swig-extentions and swig-extras + var block = tagObj.blockLevel !== undefined ? tagObj.blockLevel : tagObj.block; + // ext is an extension to the interpreter, usually exposes functionality, ex: a markdown compiler + // as an object in the swig context (_ctx.) + if (tagObj.ext) { + swig.setExtension(tagObj.ext.name, tagObj.ext.obj); + } + swig.setTag(tag, tagObj.parse, tagObj.compile, tagObj.ends, block); + }); + } + try { globalVars = grunt.util._.extend(config.data, grunt.file.readJSON(process.cwd() + '/global.json')); } catch (err) { diff --git a/test/fixtures/markdown.swig b/test/fixtures/markdown.swig new file mode 100644 index 0000000..f749035 --- /dev/null +++ b/test/fixtures/markdown.swig @@ -0,0 +1,29 @@ +{% markdown %} +# swig extensions + +> Tags, filters, and extensions for [Swig](http://paularmstrong.github.io/swig/), the most awesome template engine for node.js. + +This project is based on (and complementary to) [swig-extras](https://github.com/paularmstrong/swig-extras), from [@paularmstrong](https://github.com/paularmstrong) + + +## Getting started + +Use a filter: + +``` +var swig = require('swig'); +var extensions = require('swig-extensions'); + +extensions.useFilter(swig, 'markdown'); +``` + +Use a tag: + +``` +var swig = require('swig'); +var extensions = require('swig-extensions'); +var mySwig = new swig.Swig(); + +extensions.useTag(mySwig, 'markdown'); +``` +{% endmarkdown %} diff --git a/test/fixtures/tag.swig b/test/fixtures/tag.swig new file mode 100644 index 0000000..d9146b5 --- /dev/null +++ b/test/fixtures/tag.swig @@ -0,0 +1,3 @@ +{% countargs 'firstarg' 'secondarg' %} + This part gets ignored in this test! +{% endcountargs %} diff --git a/test/swig_test.js b/test/swig_test.js index 7f250e6..ccf78a7 100644 --- a/test/swig_test.js +++ b/test/swig_test.js @@ -1,3 +1,4 @@ +/* global describe:false, it:false */ 'use strict'; var assert = require('assert'), @@ -45,4 +46,12 @@ describe('grunt-swig', function() { helpers.assertFile('test/dest/robots.txt'); }); + it('should compile a custom tag', function () { + helpers.assertFile('test/dest/fixtures/tag.html', /args\:.*firstarg/); + }); + + it('should compile a custom tag from swig-extras', function () { + helpers.assertFile('test/dest/fixtures/markdown.html', /a href=.http\:\/\/paularmstrong/); + }); + }); diff --git a/test/tag.js b/test/tag.js new file mode 100644 index 0000000..c16eb68 --- /dev/null +++ b/test/tag.js @@ -0,0 +1,27 @@ +// Official documentation about extending Swig Tags +// http://paularmstrong.github.io/swig/docs/extending/#tags + +// Source code of how parse is defined: +// https://github.com/paularmstrong/swig/blob/v1.3.2/lib/parser.js#L550-L570 +// +// Correctly writing swig extensions might be tricky, get an idea about what +// a TokenParser is: https://github.com/paularmstrong/swig/blob/v1.3.2/lib/parser.js#L109-L127 +exports.parse = function(str, line, parser, types, stack, options) { + parser.on(types.STRING, function (token) { + // strip quote or double quote, they _also_ get matched + token.match = token.match.replace(/^("|')|("|')$/g, ''); + this.out.push(token.match); + }); + return true; +}; + +// Source code, to see how it gets used: +// https://github.com/paularmstrong/swig/blob/v1.3.2/lib/parser.js#L720-L738 +// +exports.compile = function(compiler, args, content, parents, options, blockName) { + var concatArgs = '[ ' + args.join(', ') + ' ]'; + return '_output += "args: ' + concatArgs + '";\n'; +}; + +exports.ends = true; +exports.block = true;