Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit 02a6bb03dff843705170e16eaedc887ec5e1fa9c @johnnyhalife johnnyhalife committed Dec 9, 2012
Showing with 262 additions and 0 deletions.
  1. +43 −0 .editorconfig
  2. +19 −0 .gitignore
  3. +72 −0 README.md
  4. +14 −0 grunt.js
  5. +24 −0 package.json
  6. +90 −0 tasks/grunt-cdn.js
@@ -0,0 +1,43 @@
+# This file is for unifying the coding style for different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+# Tabs in JS unless otherwise specified
+[**.js]
+indent_style = tab
+
+[Makefile]
+indent_style = tab
+
+
+[speed/**.html]
+indent_style = tab
+
+[speed/**.css]
+indent_style = tab
+
+[speed/benchmarker.js]
+indent_style = space
+indent_size = 2
+
+
+[test/**.xml]
+indent_style = tab
+
+[test/**.php]
+indent_style = tab
+
+[test/**.html]
+indent_style = tab
+
+[test/**.css]
+indent_style = space
+indent_size = 8
@@ -0,0 +1,19 @@
+node_modules
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+*.DS_Store
+
+pids
+logs
+results
+
+dist
+static
+
+npm-debug.log
@@ -0,0 +1,72 @@
+[Grunt][grunt] plugin for properly prepending a CDN url to those assets referenced with absolute paths (but not URLs)
+
+## Getting Started
+
+Install this grunt plugin next to your project's gruntfile with: `npm install grunt-cdn`
+
+Then add this line to your project's `grunt.js` gruntfile:
+
+```javascript
+grunt.loadNpmTasks('grunt-cnd');
+```
+
+Then specify your config:
+
+```javascript
+ grunt.initConfig({
+ cdn: {
+ dist: {
+ /** @required - string (or array of) including grunt glob variables */
+ src: ['./static/*.html', './static/*.css', './static/*.soy'],
+ /** @optional - if provided a copy will be stored without modifying original file */
+ dest: './dist/static/',
+ /** @required - root URL of your CDN (may contains sub-paths as shown below) */
+ cdn: 'http://cdn.cloudfront.net/container/'
+ }
+ }
+ });
+```
+
+### Example
+
+With the following input
+
+```html
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Mural.ly</title>
+ <link rel="stylesheet" type="text/css" href="/static/compiled.css?v=13512tyu3kds" />
+</head>
+<body id="landing-page">
+...
+</body>
+</html>
+```
+
+After running the task the output looks like
+
+```html
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Mural.ly</title>
+ <link rel="stylesheet" type="text/css" href="http://cdn.cloudfront.net/container/static/compiled.css?v=13512tyu3kds" />
+</head>
+<body id="landing-page">
+...
+</body>
+</html>
+```
+
+As you can see we maintain the "container" pathname in this case, and we also keep the original
+query strings. This task is really handy if you upload stuff from your CI to make it transparent
+to developers.
+
+**NOTE**: This task only works with "absolute" paths. We consider paths starting with
+. (or ..) as relative, and with / absolute. We're using the same aproach as the *NIX file-system.
+
+## Release History
+* 0.1.0 Initial Release
+
+[grunt]: https://github.com/cowboy/grunt
@@ -0,0 +1,14 @@
+module.exports = function (grunt) {
+ grunt.initConfig({
+ pkg: '<json:package.json>',
+ cdn: {
+ dist: {
+ src: ['./static/*.html', './static/*.css', './static/*.soy'],
+ dest: './dist/static/',
+ cdn: 'http://cdn.cloudfront.net/container/'
+ }
+ }
+ });
+
+ grunt.loadTasks('tasks');
+};
@@ -0,0 +1,24 @@
+{
+ "name": "grunt-cdn",
+ "description": "Properly prepends a CDN url to those assets referenced with absolute paths (but not URLs)",
+ "version": "0.1.0",
+ "homepage": "https://github.com/tactivos/grunt-cdn",
+ "author": {
+ "name": "Johnny Halife",
+ "url": "http://github.com/johnnyhalife/"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/tactivos/grunt-cdn.git"
+ },
+ "bugs": {
+ "url": "https://github.com/tactivos/grunt-cdn/issues"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "devDependencies": {
+ "grunt": "~0.3.17"
+ },
+ "keywords": ["gruntplugin", "cdn"]
+}
@@ -0,0 +1,90 @@
+/*
+ * grunt-cdn
+ * https://github.com/tactivos/grunt-cdn
+ *
+ * Copyright (c) 2012 Johnny G. Halife & Mural.ly Dev Team
+ */
+module.exports = function (grunt) {
+
+ var fs = require('fs');
+ var url = require('url');
+ var path = require('path');
+ var crypto = require('crypto');
+
+ var supportedTypes = {
+ html: 'html',
+ css: 'css',
+ soy: 'html'
+ };
+
+ var reghtml = new RegExp(/(src|href)=['"]([^'"]+)['"]/ig);
+
+ var regcss = new RegExp(/url\(([^)]+)\)/ig);
+
+ grunt.registerMultiTask('cdn', "Properly prepends a CDN url to those assets referenced with absolute paths (but not URLs)", function () {
+ var relativeTo = this.data.cdn;
+ var files = grunt.file.expandFiles(this.file.src);
+ var dest = this.file.dest;
+
+ files.map(grunt.file.read).forEach(function (content, i) {
+ var filename = files[i];
+ var type = path.extname(filename).replace(/^\./, '');
+ content = content.toString(); // sometimes css is interpreted as object
+ if(!supportedTypes[type]) { //next
+ console.warn("unrecognized extension: <%= type %> - <%= filename>");
+ return;
+ }
+
+ content = grunt.helper('cdn:' + supportedTypes[type], content, filename, relativeTo);
+
+ // write the contents to destination
+ var filePath = dest ? path.join(dest, path.basename(filename)) : file;
+ grunt.file.write(filePath, content);
+ });
+ });
+
+ grunt.registerHelper('cdn:html', function (content, filename, relativeTo) {
+ return content.replace(reghtml, function (attribute, type, resource) {
+ return grunt.template.process("<%= type %>=\"<%= url %>\"", {
+ type: type,
+ url: (cdnUrl(resource, filename, relativeTo) || resource)
+ });
+ });
+ });
+
+ grunt.registerHelper('cdn:css', function (content, filename, relativeTo) {
+ return content.replace(regcss, function (attr, resource) {
+ resource = resource.replace(/^['"]/, '').replace(/['"]$/, '');
+ var url = cdnUrl(resource, filename, relativeTo);
+
+ if(!url) return attr;
+
+ return grunt.template.process("url(<%= url %>)", {
+ url: url
+ });
+ });
+ });
+
+ function cdnUrl(resource, filename, relativeTo) {
+ // skip those absolute urls
+ if(resource.match(/^https?:\/\//i) || resource.match(/^\/\//) || resource.match(/^data:/i)) {
+ grunt.verbose.writeln("skipping " + resource + " it's an absolute (or data) URL");
+ return;
+ }
+
+ var resourceUrl = url.parse(resource);
+
+ // if path is relative let it be
+ if(!grunt.file.isPathAbsolute(resourceUrl.pathname)) {
+ grunt.verbose.writeln("skipping " + resource + " it's a relative URL");
+ return;
+ }
+
+ var src = path.join(relativeTo, resourceUrl.pathname);
+
+ return grunt.template.process("<%= url %><%= search %>", {
+ url: src,
+ search: (resourceUrl.search || '') // keep the original querystring
+ });
+ }
+};

0 comments on commit 02a6bb0

Please sign in to comment.