1 module.exports = function (grunt) {
4 // Register the 'sync' task.
5 grunt.registerTask('sync', 'Syncs necessary libraries for development purposes. Read more in: MAINTAINERS.md.', function () {
6 var cwd = process.cwd();
7 var force = grunt.option('force');
8 var path = require('path');
9 var pkg = require('../package');
11 // Internal variables.
13 var librariesCache = path.join(cwd, pkg.caches.libraries);
14 var librariesPath = path.join(cwd, pkg.paths.libraries);
15 var exists = grunt.file.exists(librariesCache);
18 // Determine the validity of any existing cached libraries file.
19 if (!force && exists) {
20 grunt.verbose.write('Cached library information detected, checking...');
21 var fs = require('fs');
22 var stat = fs.statSync(librariesCache);
23 var now = new Date().getTime();
24 var expire = new Date(stat.mtime);
25 expire.setDate(expire.getDate() + 7); // 1 week
26 expired = now > expire.getTime();
27 grunt.verbose.writeln((expired ? 'EXPIRED' : 'VALID')[expired ? 'red' : 'green']);
30 // Register a private sub-task. Doing this inside the main task prevents
31 // this private sub-task from being executed directly and also prevents it
32 // from showing up on the list of available tasks on --help.
33 grunt.registerTask('sync:api', function () {
34 var done = this.async();
35 var request = require('request');
36 grunt.verbose.write(pkg.urls.jsdelivr + ' ');
37 request(pkg.urls.jsdelivr, function (error, response, body) {
38 if (!error && response.statusCode == 200) {
41 grunt.verbose.write("\nParsing JSON response...");
43 json = JSON.parse(body);
46 grunt.verbose.error();
47 throw grunt.util.error('Unable to parse the response value (' + e.message + ').', e);
49 grunt.verbose.write("\nExtracting versions and themes from libraries...");
51 json.forEach(function (library) {
52 if (library.name === 'bootstrap' || library.name === 'bootswatch') {
53 library.assets.forEach(function (asset) {
54 if (asset.version.match(/^3.\d\.\d$/)) {
55 if (!libraries[library.name]) libraries[library.name] = {};
56 if (!libraries[library.name][asset.version]) libraries[library.name][asset.version] = {};
57 asset.files.forEach(function (file) {
58 if (!file.match(/bootstrap\.min\.css$/)) return;
59 if (library.name === 'bootstrap') {
60 libraries[library.name][asset.version]['bootstrap'] = true;
63 libraries[library.name][asset.version][file.split(path.sep)[0]] = true;
73 for (var library in libraries) {
74 grunt.verbose.header(library);
75 if (!libraries.hasOwnProperty(library)) continue;
76 var versions = Object.keys(libraries[library]);
77 grunt.verbose.ok('Versions: ' + versions.join(', '));
79 for (var version in libraries[library]) {
80 if (!libraries[library].hasOwnProperty(version)) continue;
81 var themes = Object.keys(libraries[library][version]).sort();
82 libraries[library][version] = themes;
83 if (themes.length > themeCount) {
84 themeCount = themes.length;
87 grunt.verbose.ok(grunt.util.pluralize(themeCount, 'Themes: 1/Themes: ' + themeCount));
89 grunt.verbose.writeln();
90 grunt.file.write(librariesCache, JSON.stringify(libraries, null, 2));
92 grunt.log.ok('Synced');
95 grunt.verbose.error();
96 if (error) grunt.verbose.error(error);
97 grunt.verbose.error('Request URL: ' + pkg.urls.jsdelivr);
98 grunt.verbose.error('Status Code: ' + response.statusCode);
99 grunt.verbose.error('Response Headers: ' + JSON.stringify(response.headers, null, 2));
100 grunt.verbose.error('Response:');
101 grunt.verbose.error(body);
102 grunt.fail.fatal('Unable to establish a connection. Run with --verbose to view the response received.');
108 // Run API sync sub-task.
109 if (!exists || force || expired) {
110 if (!exists) grunt.verbose.writeln('No libraries cache detected, syncing libraries.');
111 else if (force) grunt.verbose.writeln('Forced option detected, syncing libraries.');
112 else if (expired) grunt.verbose.writeln('Libraries cache is over a week old, syncing libraries.');
113 grunt.task.run(['sync:api']);
116 // Register another private sub-task.
117 grunt.registerTask('sync:libraries', function () {
118 var bower = require('bower');
119 var done = this.async();
120 var inquirer = require('inquirer');
121 var queue = require('queue')({concurrency: 1, timeout: 60000});
122 if (!grunt.file.exists(librariesCache)) {
123 return grunt.fail.fatal('No libraries detected. Please run `grunt sync --force`.');
125 libraries = grunt.file.readJSON(librariesCache);
126 var bowerJson = path.join(cwd, 'bower.json');
127 if (!grunt.file.isDir(librariesPath)) grunt.file.mkdir(librariesPath);
129 // Iterate over libraries.
130 for (var library in libraries) {
131 if (!libraries.hasOwnProperty(library)) continue;
133 // Iterate over versions.
134 for (var version in libraries[library]) {
135 if (!libraries[library].hasOwnProperty(version)) continue;
137 var endpoint = library + '#' + version;
139 // Check if library is already installed. If so, skip.
140 var versionPath = path.join(librariesPath, version);
141 grunt.verbose.write('Checking ' + endpoint + '...');
142 if (grunt.file.isDir(versionPath) && grunt.file.isDir(versionPath + '/' + library)) {
143 grunt.verbose.writeln('INSTALLED'.green);
147 grunt.verbose.writeln('MISSING'.red);
148 grunt.file.mkdir(versionPath);
149 grunt.file.copy(bowerJson, path.join(versionPath, 'bower.json'));
156 postinstall: 'rm -rf jquery && rm -rf font-awesome'
160 // Enqueue bower installations.
161 (function (endpoint, config) {
162 queue.push(function (done) {
164 .install([endpoint], {saveDev: true}, config)
165 .on('log', function (result) {
166 if (result.level === 'action' && result.id !== 'validate' && !result.message.match(/(jquery|font-awesome)/)) {
167 grunt.log.writeln(['bower', result.id.cyan, result.message.green].join(' '));
169 else if (result.level === 'action') {
170 grunt.verbose.writeln(['bower', result.id.cyan, result.message.green].join(' '));
173 grunt.log.debug(['bower', result.id.cyan, result.message.green].join(' '));
176 .on('prompt', function (prompts, callback) {
177 inquirer.prompt(prompts, callback);
179 .on('end', function () { done() })
182 })(endpoint, config);
186 // Start bower queue.
187 queue.start(function (e) {
192 // Run private sub-task.
193 grunt.task.run(['sync:libraries']);