From 366f60058129d9100751c2e1a4dee6d03c16743b Mon Sep 17 00:00:00 2001 From: Ray Viljoen Date: Thu, 31 Jan 2013 00:43:20 +0000 Subject: [PATCH] Initial src commit --- .gitignore | 2 + Cakefile | 23 ++++++++ LICENSE-MIT | 22 ++++++++ bin/grunt-sitemap | 2 + package.json | 43 +++++++++++++++ src/sitemap.coffee | 121 +++++++++++++++++++++++++++++++++++++++++++ tasks/sitemap.js | 69 ++++++++++++++++++++++++ test/sitemap_test.js | 34 ++++++++++++ 8 files changed, 316 insertions(+) create mode 100644 Cakefile create mode 100644 LICENSE-MIT create mode 100644 bin/grunt-sitemap create mode 100644 package.json create mode 100644 src/sitemap.coffee create mode 100644 tasks/sitemap.js create mode 100644 test/sitemap_test.js diff --git a/.gitignore b/.gitignore index f356293..6b0eb7c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.DS_Store +node_modules/ lib-cov *.seed *.log diff --git a/Cakefile b/Cakefile new file mode 100644 index 0000000..e1e3969 --- /dev/null +++ b/Cakefile @@ -0,0 +1,23 @@ +fs = require 'fs' + +{print} = require 'sys' +{spawn} = require 'child_process' + +build = (callback) -> + coffee = spawn 'coffee', ['-c', '-o', 'tasks', 'src'] + coffee.stderr.on 'data', (data) -> + process.stderr.write data.toString() + coffee.stdout.on 'data', (data) -> + print data.toString() + coffee.on 'exit', (code) -> + callback?() if code is 0 + +task 'build', 'Build tasks/ from src/', -> + build() + + task 'watch', 'Watch src/ for changes', -> + coffee = spawn 'coffee', ['-w', '-c', '-o', 'tasks', 'src'] + coffee.stderr.on 'data', (data) -> + process.stderr.write data.toString() + coffee.stdout.on 'data', (data) -> + print data.toString() \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..7a1be31 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,22 @@ +Copyright (c) 2013 Ray Viljoen + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/bin/grunt-sitemap b/bin/grunt-sitemap new file mode 100644 index 0000000..4f72f10 --- /dev/null +++ b/bin/grunt-sitemap @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('grunt').npmTasks('grunt-sitemap').cli(); diff --git a/package.json b/package.json new file mode 100644 index 0000000..9f407c8 --- /dev/null +++ b/package.json @@ -0,0 +1,43 @@ +{ + "name": "grunt-sitemap", + "description": "Grunt sitemap generator plugin", + "version": "0.1.0", + "homepage": "https://github.com/RayViljoen/grunt-sitemap", + "author": { + "name": "Ray Viljoen", + "email": "rayviljoen@me.com", + "url": "rayviljoen.github.com" + }, + "repository": { + "type": "git", + "url": "git://github.com/RayViljoen/grunt-sitemap.git" + }, + "bugs": { + "url": "https://github.com/RayViljoen/grunt-sitemap/issues" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/RayViljoen/grunt-sitemap/blob/master/LICENSE-MIT" + } + ], + "main": "grunt.js", + "bin": "bin/grunt-sitemap", + "engines": { + "node": "*" + }, + "scripts": { + "test": "grunt test", + "watch": "coffee -c -o tasks/ -w src/" + }, + "devDependencies": { + "grunt": "~0.3.17" + }, + "keywords": [ + "gruntplugin" + ], + "dependencies": { + "xmlbuilder": "~0.4.2", + "moment": "~1.7.2" + } +} diff --git a/src/sitemap.coffee b/src/sitemap.coffee new file mode 100644 index 0000000..2999864 --- /dev/null +++ b/src/sitemap.coffee @@ -0,0 +1,121 @@ + +# grunt-sitemap +# https://github.com/RayViljoen/grunt-sitemap +# Copyright (c) 2013 Ray Viljoen +# Licensed under the MIT license. + +module.exports = (grunt) -> + + # Node modules + path = require 'path' + fs = require 'fs' + + # https://github.com/oozcitak/xmlbuilder-js + xml = require 'xmlbuilder' + + # http://momentjs.com/ + moment = require 'moment' + + # Please see the grunt documentation for more information regarding task and + # helper creation: https://github.com/cowboy/grunt/blob/master/docs/toc.md + + # ========================================================================== + # TASKS + # ========================================================================== + + grunt.registerMultiTask 'sitemap', 'sitemap description', -> + + # Homepage from pkg + url = grunt.config.get('pkg.homepage') or @data.homepage + + # Add trailing slash to url if not there + url += '/' unless url[-1..] is '/' + + # Site root dir + root = path.normalize (@data.siteRoot or '.') + + # changereq setting + changefreq = @data.changefreq or 'daily' + + # priority setting + # Must be string + priority = (@data.priority or 0.5).toString() + + # File pattern + pattern = path.join root, (@data.pattern or '/**/*.html') + + # Check homepage is set + homeErrMess = 'Requires "homepage" parameter. Sitemap was not created.' + grunt.fail.warn(homeErrMess, 3) unless url + + # Check a site root was set + rootWarnMess = 'No "siteRoot" parameter defined. Using current directory.' + grunt.log.subhead rootWarnMess if root is '.' + + # Glob root + files = grunt.file.expand pattern + + # Remove root from path and prepend homepage url + files = grunt.utils._.map files, (file) -> + + # Create object with url an mtime + fileStat = {} + + # Get path relative to root, but still containing index paths + rawUrlPath = file.replace root, '' + + # Remove index.html + urlPath = rawUrlPath.replace /(index)\.[A-z]+$/, '', 'i' + + # Join path with homepage url + fileStat.url = url + urlPath + + # Get last modified time + mtime = (fs.statSync(file).mtime).getTime() + + # Format mtime + fileStat.mtime = moment(mtime).format() + + # Return fileStat object + fileStat + + # ----------------------- + # Build xml + # ----------------------- + + # Create xml root + sitemap = xml.create 'urlset', + {'version':'1.0', 'encoding':'UTF-8'} + + # Add root schema + sitemap.attribute 'xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9/' + + # Create url nodes + for file in files + urlNode = sitemap.e 'url' + urlNode.e 'loc', file.url + urlNode.e 'lastmod', file.mtime + urlNode.e 'changefreq', changefreq + urlNode.e 'priority', priority + + # Finalise xml to string + sitemapStr = sitemap.end + 'pretty': true, 'indent': ' ' + 'newline': '\n' + + # Write sitemap to root + sitemapPath = path.join root, 'sitemap.xml' + grunt.file.write sitemapPath, sitemapStr + + + grunt.log.writeln 'Sitemap created successfully' + grunt.log.writeln 'OK' + + # Return true / false + if grunt.task.current.errorCount then no else yes + + + + + + diff --git a/tasks/sitemap.js b/tasks/sitemap.js new file mode 100644 index 0000000..cb3d86d --- /dev/null +++ b/tasks/sitemap.js @@ -0,0 +1,69 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + + module.exports = function(grunt) { + var fs, moment, path, xml; + path = require('path'); + fs = require('fs'); + xml = require('xmlbuilder'); + moment = require('moment'); + return grunt.registerMultiTask('sitemap', 'sitemap description', function() { + var changefreq, file, files, homeErrMess, pattern, priority, root, rootWarnMess, sitemap, sitemapPath, sitemapStr, url, urlNode, _i, _len; + url = grunt.config.get('pkg.homepage') || this.data.homepage; + if (url.slice(-1) !== '/') { + url += '/'; + } + root = path.normalize(this.data.siteRoot || '.'); + changefreq = this.data.changefreq || 'daily'; + priority = (this.data.priority || 0.5).toString(); + pattern = path.join(root, this.data.pattern || '/**/*.html'); + homeErrMess = 'Requires "homepage" parameter. Sitemap was not created.'; + if (!url) { + grunt.fail.warn(homeErrMess, 3); + } + rootWarnMess = 'No "siteRoot" parameter defined. Using current directory.'; + if (root === '.') { + grunt.log.subhead(rootWarnMess); + } + files = grunt.file.expand(pattern); + files = grunt.utils._.map(files, function(file) { + var fileStat, mtime, rawUrlPath, urlPath; + fileStat = {}; + rawUrlPath = file.replace(root, ''); + urlPath = rawUrlPath.replace(/(index)\.[A-z]+$/, '', 'i'); + fileStat.url = url + urlPath; + mtime = (fs.statSync(file).mtime).getTime(); + fileStat.mtime = moment(mtime).format(); + return fileStat; + }); + sitemap = xml.create('urlset', { + 'version': '1.0', + 'encoding': 'UTF-8' + }); + sitemap.attribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9/'); + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + urlNode = sitemap.e('url'); + urlNode.e('loc', file.url); + urlNode.e('lastmod', file.mtime); + urlNode.e('changefreq', changefreq); + urlNode.e('priority', priority); + } + sitemapStr = sitemap.end({ + 'pretty': true, + 'indent': ' ', + 'newline': '\n' + }); + sitemapPath = path.join(root, 'sitemap.xml'); + grunt.file.write(sitemapPath, sitemapStr); + grunt.log.writeln('Sitemap created successfully'); + grunt.log.writeln('OK'); + if (grunt.task.current.errorCount) { + return false; + } else { + return true; + } + }); + }; + +}).call(this); diff --git a/test/sitemap_test.js b/test/sitemap_test.js new file mode 100644 index 0000000..fe4a039 --- /dev/null +++ b/test/sitemap_test.js @@ -0,0 +1,34 @@ +var grunt = require('grunt'); + +/* + ======== A Handy Little Nodeunit Reference ======== + https://github.com/caolan/nodeunit + + Test methods: + test.expect(numAssertions) + test.done() + Test assertions: + test.ok(value, [message]) + test.equal(actual, expected, [message]) + test.notEqual(actual, expected, [message]) + test.deepEqual(actual, expected, [message]) + test.notDeepEqual(actual, expected, [message]) + test.strictEqual(actual, expected, [message]) + test.notStrictEqual(actual, expected, [message]) + test.throws(block, [error], [message]) + test.doesNotThrow(block, [error], [message]) + test.ifError(value) +*/ + +exports['sitemap'] = { + setUp: function(done) { + // setup here + done(); + }, + 'helper': function(test) { + test.expect(1); + // tests here + test.equal(grunt.helper('sitemap'), 'sitemap!!!', 'should return the correct value.'); + test.done(); + } +};