exports.generateManifest = function(productPath, options) { options = options || {}; indexFilePath = options.index || FILE.join(productPath, "index.html"); if (!FILE.isFile(indexFilePath)) { print("Warning: Skipping cache manifest generation, no index file at "+indexFilePath); return; } var index = FILE.read(indexFilePath, { charset : "UTF-8" }); var manifestName = "app.manifest"; var manifestPath = FILE.join(productPath, manifestName); var manifestAttribute = 'manifest="'+manifestName+'"'; print("Generating cache manifest: " + manifestPath); var manifestOut = FILE.open(manifestPath, "w", { charset : "UTF-8" }); manifestOut.print("CACHE MANIFEST"); manifestOut.print(""); manifestOut.print("CACHE:"); var list = new FileList(FILE.join(productPath, "**", "*")); list.exclude(manifestPath); list.exclude("**/.DS_Store", "**/.htaccess"); list.exclude("**/LICENSE"); list.exclude("**/MHTML*"); list.exclude("**/CommonJS.environment/*"); list.exclude("**/*.cur"); if (index.indexOf('"Frameworks/Debug"') < 0) list.exclude("**/Frameworks/Debug/*"); if (options.exclude) options.exclude.forEach(list.exclude.bind(list)); list.forEach(function(path) { if (FILE.isFile(path)) { var relative = FILE.relative(productPath, path); if (BundleTask.isSpritable(path) && index.indexOf(relative) < 0) return; var hash = MD5.hash(FILE.read(path, "b")).decodeToString("base16"); manifestOut.print("# " + hash); manifestOut.print(relative); } }); manifestOut.print(""); manifestOut.print("NETWORK:"); manifestOut.print("*"); manifestOut.close(); var matchTag = index.match(/<html[^>]*>/i); if (matchTag) { var htmlTag = matchTag[0]; var newHTMLTag = null; var matchAttr = htmlTag.match(/manifest\s*=\s*"([^"]*)"/i); if (matchAttr) { if (matchAttr[1] !== manifestName) { newHTMLTag = htmlTag.replace(matchAttr[0], manifestAttribute); } } else { newHTMLTag = htmlTag.replace(/>$/, " "+manifestAttribute+">"); } if (newHTMLTag) { print("Replacing html tag: \n " + htmlTag + "\nwith:\n " + newHTMLTag); var newIndex = index.replace(htmlTag, newHTMLTag); if (newIndex === index) { print("Warning: No change!"); } else { FILE.write(indexFilePath, newIndex, { charset : "UTF-8" }); } } } else { print("Warning: Couldn't find <html> tag in "+indexFilePath); } var htaccessPath = FILE.join(productPath, ".htaccess"); var htaccess = FILE.isFile(htaccessPath) ? FILE.read(htaccessPath, { charset : "UTF-8" }) : ""; var htaccessOut = FILE.open(htaccessPath, "w", { charset : "UTF-8" }); htaccessOut.print(htaccess); var openTag = "<Files "+manifestName+">"; if (htaccess.indexOf(openTag) < 0) { htaccessOut.print(""); htaccessOut.print(openTag); htaccessOut.print("\tHeader set Content-Type text/cache-manifest"); htaccessOut.print("</Files>"); } htaccessOut.close(); }
task('test', ['concat'], function () { // TODO: use a testing suite for testing: nodeunit, mocha, tap, ... var filelist = new jake.FileList(); filelist.include([ './test/**/*.js' ]); var files = filelist.toArray(); files.forEach(function (file) { require('./' + file); }); console.log('Executed ' + files.length + ' test files successfully'); });
task("lint", [], function() { console.log('---------- lint the code'); var files = new jake.FileList(); files.include("src/*.js"); var options = { node: true }; var globals = { describe: false }; var pass = lint.validateFileList(files.toArray(), options, globals); if (!pass) fail("Lint failed"); });
/** * Minify files using uglify-js. * * Example: * var result = minify({ * src: [ * './lib/mylibrary.js' * ], * dest: './lib/mylibrary.min.js', // optional * options: {}, // uglify-js options. optional * header: '// license information...', // optional * separator: '\n', // optional * footer: '// the end...' // optional * }); * * @param {Object} params Object containing: * {String | String[]} src One source file or an array * with source files to be * minified. The file names * can contain patterns. * {String} [dest] The target file. Optional * {Object} [options] uglify-js options. * {String} [header] Text to be added on top. * Optional. * {String} [separator] Text to be inserted between * header, contents and footer. * Optional. * {String} [footer] Text to be added at the bottom. * Optional. * @return {Object} res Result information. The object contains: * {String[]} src List with the filenames of the * files which are minified * {String} code The contents of the minified * file. */ function minify (params) { // do some checks on the provided parameters if (!(params instanceof Object)) { throw new Error('Object with parameters expected as first argument.'); } if (!params.src) { throw new Error('Parameter "src" containing an array with filenames missing.'); } if (params.options) { if (!(params.options instanceof Object)) { throw new Error('Parameter "options" must be an object.'); } } var code = ''; var separator = params.separator ? String(params.separator) : ''; var options = params.options || {}; // header if (params.header) { code += String(params.header) + separator; } // src var filelist = new jake.FileList(); filelist.include(params.src); var filenames = filelist.toArray(); var minified = uglify.minify(filenames, options); code += minified.code; // footer if (params.footer) { code += separator + String(params.footer); } // write output if (params.dest) { write(params.dest, code); } return { src: filenames, code: code }; }
/** * Replace patterns in text files. * Patterns can be a string or a regular expression. * * Example usage: * var result = replace({ * replacements: [ * {pattern: '@@date', replacement: '2013-02-18'}, * {pattern: '@@version', replacement: '1.4.0'} * }, * src: [ * 'main.js', * 'other/*.js' * ] * }); * * @param {Object} params Object containing parameters: * {Object[]} replacements Array containing objects with * parameters: * {String | RegExp} pattern * {String} replacement * {String | String[]} src The filenames. Can contain * patterns. * @return {Object} res Result information. The object contains: * {String[]} src List with the filenames on which * the replacement is executed * @throws {Error} */ // TODO: change params of src such that we can also use regex expressions function replace (params) { // do some checks on the provided parameters if (!(params instanceof Object)) { throw new Error('Object with parameters expected as first argument.'); } if (!params.replacements) { throw new Error('Parameter "replacements" missing.'); } if (!(params.replacements instanceof Array)) { throw new Error('Parameter "replacements" must be an array.'); } if (!params.src) { throw new Error('Parameter "src" containing an array with filenames missing.'); } var filelist = new jake.FileList(); filelist.include(params.src); var filenames = filelist.toArray(); filenames.forEach(function (filename) { var file = String(read(filename)); params.replacements.forEach(function (replacement, index) { // check the replacement parameters if (!(replacement instanceof Object)) { throw new Error('Parameter "replacement" must be an object.'); } if (!replacement.pattern) { throw new Error('Parameter "pattern" in missing replacement object ' + '(index ' + index + ')'); } if (!replacement.replacement) { throw new Error('Parameter "replacement" missing in replacement object ' + '(index ' + index + ')'); } file = file.replace(replacement.pattern, replacement.replacement); }); write(filename, file); }); return { src: filenames } }
/** * Concatenate a list with files into one file * * Example: * var result = concat({ * src: [ * './src/main.js', * './src/extra.js', * './src/functions/**', * ], * dest: './lib/mylibrary.js', // optional * header: '// license information...', // optional * separator: '\n', // optional * footer: '// the end...' // optional * }); * * @param {Object} params Object containing: * {String[]} src A list with source files to be * Included. Can contain patterns. * {String} [dest] The target file. Optional * {String} [separator] Text to be inserted between the * files. Optional. * {String} [header] Text to be added on top. * Optional. * {String} [footer] Text to be added at the bottom. * Optional. * @return {Object} res Result information. The object contains: * {String[]} src List with the filenames of the * files which are concatenated * {String} code The contents of the concatenated * file */ function concat (params) { // do some checks on the provided parameters if (!(params instanceof Object)) { throw new Error('Object with parameters expected as first argument.'); } if (!params.src) { throw new Error('Parameter "src" containing an array with filenames missing.'); } var code = ''; var separator = params.separator ? String(params.separator) : ''; // header if (params.header) { code += String(params.header) + separator; } // files var filelist = new jake.FileList(); filelist.include(params.src); var filenames = filelist.toArray(); filenames.map(function(filename) { code += read(filename) + separator; }); // footer if (params.footer) { code += String(params.footer); } // write output if (params.dest) { // write file write(params.dest, code); } return { src: filenames, code: code }; }
var TARGET_UNCOMPRESSED_DEPENDENCIES = (function () { var all = new jake.FileList(); all.include('./Jakefile.js', 'own-testcases.json'); all.include('src/**'); all.include('test/**'); all.exclude(TARGET_COMPRESSED); return all.toArray(); }());
/** * Compiles less files to css * * @param Object options * @param Function cb * * Options: * * - Array src - List of glob patterns for source files * - String dest - Destination directory * - Boolean debug - if true source maps will be generated */ function compileLess(options, cb) { cb = cb || noop; var src = options.src; var dest = options.dest; var lessFiles = new jake.FileList(); lessFiles.include(src); var done = 0; var fileCb = (typeof options.dest === 'function') ? toCb(options.dest) : toFile; lessFiles = lessFiles.toArray(); lessFiles.forEach(fileCb); function toFile(file) { var destFile = path.join(dest, path.basename(file).replace('.less', '.css')); // remove existing css jake.rmRf(destFile, {silent: true}); console.log('Writing css to ' + destFile); less.render( fs.readFileSync(file, 'utf8'), { paths: src.map(function(_src) { return path.dirname(_src); }), sourceMap: true }, onRendered.bind(null, destFile) ); } function toCb(cb) { return function(file) { less.render( fs.readFileSync(file, 'utf8'), { paths: src.map(function(_src) { return path.dirname(_src); }), sourceMap: true }, cb ); }; } function onRendered(dest, error, css) { if (error) { console.error(error); onDone(); } else { if (!options.debug) { fs.writeFile(dest, css.css, 'utf8', onDone); return; } var s = new stream.Readable(); s._read = noop; s.push(css.css); s.push(null); var smap = new stream.Duplex(); smap._write = function(data) { smap.push(data.toString().replace(new RegExp("\/\/# sourceMappingURL=(.*)?"), '/*# sourceMappingURL=$1*/')); smap.push(null); }; smap._read = noop; s.pipe(exorcist(dest + '.map')) .pipe(smap) .pipe(fs.createWriteStream(dest, 'utf8')) .on('finish', onDone); } } function onDone() { done++; if (done === lessFiles.length) { cb(null); } } }