Skip to content

Commit

Permalink
Extracted cdnizer from this library, so it could be more useful outsi…
Browse files Browse the repository at this point in the history
…de gulp. Cleaned up npm, as well.
  • Loading branch information
OverZealous committed Feb 3, 2014
1 parent 10ad73f commit 114e68e
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 162 deletions.
3 changes: 3 additions & 0 deletions .bowerrc
@@ -0,0 +1,3 @@
{
"directory": "./test/bower/components"
}
7 changes: 6 additions & 1 deletion .npmignore
Expand Up @@ -5,4 +5,9 @@ node_modules/
temp/
coverage
.idea
test
test
.bowerrc
.travis.yml
.gitattributes
.editorconfig
.jshintrc
171 changes: 14 additions & 157 deletions index.js
@@ -1,125 +1,19 @@
var through = require("through2"),
gutil = require("gulp-util"),
minimatch = require('minimatch'),
merge = require('deepmerge'),
fs = require('fs'),
path = require('path'),
_ = require('lodash');
cdnizer = require('cdnizer');

var error = function(msg) {
return new gutil.PluginError("gulp-cdnizer", msg);
},

parseOptions = function(opts) {
if(!opts || (typeof opts !== 'object' && !Array.isArray(opts))) {
throw error("No options or invalid options supplied");
}
if(Array.isArray(opts)) {
opts = {files: opts};
}
if(!Array.isArray(opts.files) || opts.files.length === 0) {
throw error("Invalid or empty files list supplied");
}
opts = merge({
defaultCDNBase: '',
defaultCDN: '<%= defaultCDNBase %>/<%= filepathRel %>',
allowRev: true,
allowMin: true,
bowerComponents: null,
// escaped to prevent IntelliJ from being too smart
fallbackScript: '<scr'+'ipt>function cdnizerLoad(u) {document.write(\'<scr\'+\'ipt src="\'+encodeURIComponent(u)+\'"></scr\'+\'ipt>\');}</script>',
fallbackTest: '<scr'+'ipt>if(!(${ test })) cdnizerLoad("${ filepath }");</script>',
shouldAddFallback: false
}, opts);

opts.files = opts.files.map(function(fileInfo) {
if(typeof fileInfo === 'string' && fileInfo.length > 0) {
fileInfo = { file: fileInfo };
}
if(!fileInfo.file || typeof fileInfo.file !== 'string') {
throw error('File declaration is invalid');
}
if(fileInfo.test) {
opts.shouldAddFallback = true;
}
if(opts.allowMin && fileInfo.file.indexOf('.min') === -1) {
fileInfo.file = fileInfo.file.replace(/\.(.*)$/, '.?(min.)$1');
}
if(opts.allowRev) {
fileInfo.file = fileInfo.file.replace(/(\..*)$/, '?(-????????)$1');
}
return fileInfo;
});

opts.defaultCDNBase = opts.defaultCDNBase.replace(/\/$/, '');
return opts;
},

matchers = [
{ pattern: /(<script\s.*?src=["'])(.+?)(["'].*?>\s*<\/script>)/gi, fallback: true },
{ pattern: /(<link\s.*?href=["'])(.+?)(["'].*?>\s*<\/link>)/gi, fallback: true },
{ pattern: /(<link\s.*?href=["'])(.+?)(["'].*?\/?>)/gi, fallback: true },
{ pattern: /(<img\s.*?src=["'])(.+?)(["'])/gi, fallback: false },
{ pattern: /(url\()(.+?)(\))/gi, fallback: false }
];
function pluginError(msg) {
return new gutil.PluginError("gulp-cdnizer", msg);
}

module.exports = function(opts) {
"use strict";

opts = parseOptions(opts);

function findFileInfo(url) {
url = decodeURIComponent(url);
return _.find(opts.files, function(fileInfo) {
return minimatch(url, fileInfo.file);
});
}

var bowerRoot = './bower_components',
bowerrc;
if(opts.bowerComponents) {
bowerRoot = opts.bowerComponents;
} else if(fs.existsSync('./.bowerrc')) {
bowerrc = JSON.parse(require('fs').readFileSync('./.bowerrc', {encoding: 'utf8'}));
if(bowerrc && bowerrc.directory) {
bowerRoot = path.join('.', bowerrc.directory);
}
}

var versionInfoCache = {};
function getVersionInfo(pkg) {
if(!pkg) return {};
if(!versionInfoCache[pkg]) {
var packageInfo, version, packageRoot = path.join(process.cwd(), bowerRoot, pkg);
if(fs.existsSync(path.join(packageRoot, 'bower.json'))) {
packageInfo = require(path.join(packageRoot, 'bower.json'));
} else if(fs.existsSync(path.join(packageRoot, '.bower.json'))) {
packageInfo = require(path.join(packageRoot, '.bower.json'));
} else {
throw error('Unable to load bower.json for package "'+pkg+'". Looked under "'+packageRoot+'"');
}
version = (packageInfo.version || '0.0.0').match(/(\d+)?\.(\d+)?\.(\d+)?/);
versionInfoCache[pkg] = {
version: packageInfo.version || '0.0.0',
major: version[1] || 0,
minor: version[2] || 0,
patch: version[3] || 0
}
}
return versionInfoCache[pkg];
}

function getFilenameMin(url) {
url = path.basename(url);
if(opts.allowRev) {
url = url.replace(/-\w{8}(\..+)$/, '$1');
}
url = url.replace(/\.(min\.)?(\..+)$/, '.min.$2');
return url;
}
var cdnizerHandler = cdnizer(opts);


function cdnizer(file, enc, callback) {
//noinspection JSUnusedLocalSymbols
function cdnizerStream(file, enc, callback) {

// Do nothing if no contents
if(file.isNull()) {
Expand All @@ -128,58 +22,21 @@ module.exports = function(opts) {
}

if(file.isStream()) {
this.emit("error", error("Stream content is not supported"));
this.emit("error", pluginError("Stream content is not supported"));
return callback();
}

// check if file.contents is a `Buffer`
if(file.isBuffer()) {

var contents = String(file.contents),
canAddFallback = opts.shouldAddFallback && contents.indexOf('<head') !== -1,
didAddFallback = false;

matchers.forEach(function(m) {
contents = contents.replace(m.pattern, function(match, pre, url, post) {
var fileInfo = findFileInfo(url), result, params;
if(fileInfo) {
result = pre;
params = merge(getVersionInfo(fileInfo.package), {
defaultCDNBase: opts.defaultCDNBase,
filepath: url,
filepathRel: url.replace(/^\//, ''),
filename: path.basename(url),
filenameMin: getFilenameMin(url),
package: fileInfo.package,
test: fileInfo.test
});
result += _.template(fileInfo.cdn || opts.defaultCDN, params);
result += post;
if(canAddFallback && m.fallback && fileInfo.test) {
result += _.template(opts.fallbackTest, params);
didAddFallback = true;
}
return result;
} else {
// do nothing
return match;
}
});
});

if(didAddFallback) {
contents = contents.replace(/<link|<script|<\/head/i, function(m) {
return opts.fallbackScript + m;
});
try {
file.contents = new Buffer(cdnizerHandler(String(file.contents)));
this.push(file);
} catch(error) {
this.emit("error", error(error.toString()))
}

file.contents = new Buffer(contents);

this.push(file);

}
return callback();
}

return through.obj(cdnizer);
return through.obj(cdnizerStream);
};
4 changes: 1 addition & 3 deletions package.json
Expand Up @@ -25,9 +25,7 @@
"dependencies": {
"through2": "*",
"gulp-util": "~2.2.0",
"minimatch": "~0.2.14",
"deepmerge": "~0.2.7",
"lodash": "~2.4.1"
"cdnizer": "~0.2.1"
},
"devDependencies": {
"mocha": "*",
Expand Down
7 changes: 7 additions & 0 deletions test/bower/components/angular/bower.json
@@ -0,0 +1,7 @@
{
"name": "angular",
"version": "1.2.10",
"main": "./angular.js",
"dependencies": {
}
}
16 changes: 16 additions & 0 deletions test/expected/index-bowerrc.html
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<link href="http://netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" href="css/main-ab12cd34.css">
</head>
<body>
<h1>Hello World</h1>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
<script src="js/vendor/firebase/firebase.js"></script>
<script src="js/vendor/angularfire/angularfire.js"></script>
<script src="js/app/app.js"></script>
</body>
</html>
12 changes: 11 additions & 1 deletion test/main.js
Expand Up @@ -77,7 +77,17 @@ describe("gulp-cdnizer", function () {
}, 'index-fallback.html', done);
});

it("should handle bower versions", function (done) {
it("should handle bower versions (.bowerrc)", function (done) {
processInput({
files: [{
file: 'js/**/angular/angular.js',
package: 'angular',
cdn: '//ajax.googleapis.com/ajax/libs/angularjs/${ major }.${ minor }.${ patch }/angular.min.js'
}]
}, 'index-bowerrc.html', done);
});

it("should handle bower versions (passed in)", function (done) {
processInput({
bowerComponents: './test/bower_components',
files: [{
Expand Down

0 comments on commit 114e68e

Please sign in to comment.