123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- /*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2014 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
- 'use strict';
-
- var grunt = require('../grunt');
-
- // Nodejs libs.
- var fs = require('fs');
- var path = require('path');
-
- // The module to be exported.
- var file = module.exports = {};
-
- // External libs.
- file.glob = require('glob');
- file.minimatch = require('minimatch');
- file.findup = require('findup-sync');
- var YAML = require('js-yaml');
- var rimraf = require('rimraf');
- var iconv = require('iconv-lite');
-
- // Windows?
- var win32 = process.platform === 'win32';
-
- // Normalize \\ paths to / paths.
- var unixifyPath = function(filepath) {
- if (win32) {
- return filepath.replace(/\\/g, '/');
- } else {
- return filepath;
- }
- };
-
- // Change the current base path (ie, CWD) to the specified path.
- file.setBase = function() {
- var dirpath = path.join.apply(path, arguments);
- process.chdir(dirpath);
- };
-
- // Process specified wildcard glob patterns or filenames against a
- // callback, excluding and uniquing files in the result set.
- var processPatterns = function(patterns, fn) {
- // Filepaths to return.
- var result = [];
- // Iterate over flattened patterns array.
- grunt.util._.flatten(patterns).forEach(function(pattern) {
- // If the first character is ! it should be omitted
- var exclusion = pattern.indexOf('!') === 0;
- // If the pattern is an exclusion, remove the !
- if (exclusion) { pattern = pattern.slice(1); }
- // Find all matching files for this pattern.
- var matches = fn(pattern);
- if (exclusion) {
- // If an exclusion, remove matching files.
- result = grunt.util._.difference(result, matches);
- } else {
- // Otherwise add matching files.
- result = grunt.util._.union(result, matches);
- }
- });
- return result;
- };
-
- // Match a filepath or filepaths against one or more wildcard patterns. Returns
- // all matching filepaths.
- file.match = function(options, patterns, filepaths) {
- if (grunt.util.kindOf(options) !== 'object') {
- filepaths = patterns;
- patterns = options;
- options = {};
- }
- // Return empty set if either patterns or filepaths was omitted.
- if (patterns == null || filepaths == null) { return []; }
- // Normalize patterns and filepaths to arrays.
- if (!Array.isArray(patterns)) { patterns = [patterns]; }
- if (!Array.isArray(filepaths)) { filepaths = [filepaths]; }
- // Return empty set if there are no patterns or filepaths.
- if (patterns.length === 0 || filepaths.length === 0) { return []; }
- // Return all matching filepaths.
- return processPatterns(patterns, function(pattern) {
- return file.minimatch.match(filepaths, pattern, options);
- });
- };
-
- // Match a filepath or filepaths against one or more wildcard patterns. Returns
- // true if any of the patterns match.
- file.isMatch = function() {
- return file.match.apply(file, arguments).length > 0;
- };
-
- // Return an array of all file paths that match the given wildcard patterns.
- file.expand = function() {
- var args = grunt.util.toArray(arguments);
- // If the first argument is an options object, save those options to pass
- // into the file.glob.sync method.
- var options = grunt.util.kindOf(args[0]) === 'object' ? args.shift() : {};
- // Use the first argument if it's an Array, otherwise convert the arguments
- // object to an array and use that.
- var patterns = Array.isArray(args[0]) ? args[0] : args;
- // Return empty set if there are no patterns or filepaths.
- if (patterns.length === 0) { return []; }
- // Return all matching filepaths.
- var matches = processPatterns(patterns, function(pattern) {
- // Find all matching files for this pattern.
- return file.glob.sync(pattern, options);
- });
- // Filter result set?
- if (options.filter) {
- matches = matches.filter(function(filepath) {
- filepath = path.join(options.cwd || '', filepath);
- try {
- if (typeof options.filter === 'function') {
- return options.filter(filepath);
- } else {
- // If the file is of the right type and exists, this should work.
- return fs.statSync(filepath)[options.filter]();
- }
- } catch(e) {
- // Otherwise, it's probably not the right type.
- return false;
- }
- });
- }
- return matches;
- };
-
- var pathSeparatorRe = /[\/\\]/g;
-
- // The "ext" option refers to either everything after the first dot (default)
- // or everything after the last dot.
- var extDotRe = {
- first: /(\.[^\/]*)?$/,
- last: /(\.[^\/\.]*)?$/,
- };
-
- // Build a multi task "files" object dynamically.
- file.expandMapping = function(patterns, destBase, options) {
- options = grunt.util._.defaults({}, options, {
- extDot: 'first',
- rename: function(destBase, destPath) {
- return path.join(destBase || '', destPath);
- }
- });
- var files = [];
- var fileByDest = {};
- // Find all files matching pattern, using passed-in options.
- file.expand(options, patterns).forEach(function(src) {
- var destPath = src;
- // Flatten?
- if (options.flatten) {
- destPath = path.basename(destPath);
- }
- // Change the extension?
- if ('ext' in options) {
- destPath = destPath.replace(extDotRe[options.extDot], options.ext);
- }
- // Generate destination filename.
- var dest = options.rename(destBase, destPath, options);
- // Prepend cwd to src path if necessary.
- if (options.cwd) { src = path.join(options.cwd, src); }
- // Normalize filepaths to be unix-style.
- dest = dest.replace(pathSeparatorRe, '/');
- src = src.replace(pathSeparatorRe, '/');
- // Map correct src path to dest path.
- if (fileByDest[dest]) {
- // If dest already exists, push this src onto that dest's src array.
- fileByDest[dest].src.push(src);
- } else {
- // Otherwise create a new src-dest file mapping object.
- files.push({
- src: [src],
- dest: dest,
- });
- // And store a reference for later use.
- fileByDest[dest] = files[files.length - 1];
- }
- });
- return files;
- };
-
- // Like mkdir -p. Create a directory and any intermediary directories.
- file.mkdir = function(dirpath, mode) {
- if (grunt.option('no-write')) { return; }
- // Set directory mode in a strict-mode-friendly way.
- if (mode == null) {
- mode = parseInt('0777', 8) & (~process.umask());
- }
- dirpath.split(pathSeparatorRe).reduce(function(parts, part) {
- parts += part + '/';
- var subpath = path.resolve(parts);
- if (!file.exists(subpath)) {
- try {
- fs.mkdirSync(subpath, mode);
- } catch(e) {
- throw grunt.util.error('Unable to create directory "' + subpath + '" (Error code: ' + e.code + ').', e);
- }
- }
- return parts;
- }, '');
- };
-
- // Recurse into a directory, executing callback for each file.
- file.recurse = function recurse(rootdir, callback, subdir) {
- var abspath = subdir ? path.join(rootdir, subdir) : rootdir;
- fs.readdirSync(abspath).forEach(function(filename) {
- var filepath = path.join(abspath, filename);
- if (fs.statSync(filepath).isDirectory()) {
- recurse(rootdir, callback, unixifyPath(path.join(subdir || '', filename || '')));
- } else {
- callback(unixifyPath(filepath), rootdir, subdir, filename);
- }
- });
- };
-
- // The default file encoding to use.
- file.defaultEncoding = 'utf8';
- // Whether to preserve the BOM on file.read rather than strip it.
- file.preserveBOM = false;
-
- // Read a file, return its contents.
- file.read = function(filepath, options) {
- if (!options) { options = {}; }
- var contents;
- grunt.verbose.write('Reading ' + filepath + '...');
- try {
- contents = fs.readFileSync(String(filepath));
- // If encoding is not explicitly null, convert from encoded buffer to a
- // string. If no encoding was specified, use the default.
- if (options.encoding !== null) {
- contents = iconv.decode(contents, options.encoding || file.defaultEncoding);
- // Strip any BOM that might exist.
- if (!file.preserveBOM && contents.charCodeAt(0) === 0xFEFF) {
- contents = contents.substring(1);
- }
- }
- grunt.verbose.ok();
- return contents;
- } catch(e) {
- grunt.verbose.error();
- throw grunt.util.error('Unable to read "' + filepath + '" file (Error code: ' + e.code + ').', e);
- }
- };
-
- // Read a file, parse its contents, return an object.
- file.readJSON = function(filepath, options) {
- var src = file.read(filepath, options);
- var result;
- grunt.verbose.write('Parsing ' + filepath + '...');
- try {
- result = JSON.parse(src);
- grunt.verbose.ok();
- return result;
- } catch(e) {
- grunt.verbose.error();
- throw grunt.util.error('Unable to parse "' + filepath + '" file (' + e.message + ').', e);
- }
- };
-
- // Read a YAML file, parse its contents, return an object.
- file.readYAML = function(filepath, options) {
- var src = file.read(filepath, options);
- var result;
- grunt.verbose.write('Parsing ' + filepath + '...');
- try {
- result = YAML.load(src);
- grunt.verbose.ok();
- return result;
- } catch(e) {
- grunt.verbose.error();
- throw grunt.util.error('Unable to parse "' + filepath + '" file (' + e.problem + ').', e);
- }
- };
-
- // Write a file.
- file.write = function(filepath, contents, options) {
- if (!options) { options = {}; }
- var nowrite = grunt.option('no-write');
- grunt.verbose.write((nowrite ? 'Not actually writing ' : 'Writing ') + filepath + '...');
- // Create path, if necessary.
- file.mkdir(path.dirname(filepath));
- try {
- // If contents is already a Buffer, don't try to encode it. If no encoding
- // was specified, use the default.
- if (!Buffer.isBuffer(contents)) {
- contents = iconv.encode(contents, options.encoding || file.defaultEncoding);
- }
- // Actually write file.
- if (!nowrite) {
- fs.writeFileSync(filepath, contents);
- }
- grunt.verbose.ok();
- return true;
- } catch(e) {
- grunt.verbose.error();
- throw grunt.util.error('Unable to write "' + filepath + '" file (Error code: ' + e.code + ').', e);
- }
- };
-
- // Read a file, optionally processing its content, then write the output.
- file.copy = function(srcpath, destpath, options) {
- if (!options) { options = {}; }
- // If a process function was specified, and noProcess isn't true or doesn't
- // match the srcpath, process the file's source.
- var process = options.process && options.noProcess !== true &&
- !(options.noProcess && file.isMatch(options.noProcess, srcpath));
- // If the file will be processed, use the encoding as-specified. Otherwise,
- // use an encoding of null to force the file to be read/written as a Buffer.
- var readWriteOptions = process ? options : {encoding: null};
- // Actually read the file.
- var contents = file.read(srcpath, readWriteOptions);
- if (process) {
- grunt.verbose.write('Processing source...');
- try {
- contents = options.process(contents, srcpath);
- grunt.verbose.ok();
- } catch(e) {
- grunt.verbose.error();
- throw grunt.util.error('Error while processing "' + srcpath + '" file.', e);
- }
- }
- // Abort copy if the process function returns false.
- if (contents === false) {
- grunt.verbose.writeln('Write aborted.');
- } else {
- file.write(destpath, contents, readWriteOptions);
- }
- };
-
- // Delete folders and files recursively
- file.delete = function(filepath, options) {
- filepath = String(filepath);
-
- var nowrite = grunt.option('no-write');
- if (!options) {
- options = {force: grunt.option('force') || false};
- }
-
- grunt.verbose.write((nowrite ? 'Not actually deleting ' : 'Deleting ') + filepath + '...');
-
- if (!file.exists(filepath)) {
- grunt.verbose.error();
- grunt.log.warn('Cannot delete nonexistent file.');
- return false;
- }
-
- // Only delete cwd or outside cwd if --force enabled. Be careful, people!
- if (!options.force) {
- if (file.isPathCwd(filepath)) {
- grunt.verbose.error();
- grunt.fail.warn('Cannot delete the current working directory.');
- return false;
- } else if (!file.isPathInCwd(filepath)) {
- grunt.verbose.error();
- grunt.fail.warn('Cannot delete files outside the current working directory.');
- return false;
- }
- }
-
- try {
- // Actually delete. Or not.
- if (!nowrite) {
- rimraf.sync(filepath);
- }
- grunt.verbose.ok();
- return true;
- } catch(e) {
- grunt.verbose.error();
- throw grunt.util.error('Unable to delete "' + filepath + '" file (' + e.message + ').', e);
- }
- };
-
- // True if the file path exists.
- file.exists = function() {
- var filepath = path.join.apply(path, arguments);
- return fs.existsSync(filepath);
- };
-
- // True if the file is a symbolic link.
- file.isLink = function() {
- var filepath = path.join.apply(path, arguments);
- return file.exists(filepath) && fs.lstatSync(filepath).isSymbolicLink();
- };
-
- // True if the path is a directory.
- file.isDir = function() {
- var filepath = path.join.apply(path, arguments);
- return file.exists(filepath) && fs.statSync(filepath).isDirectory();
- };
-
- // True if the path is a file.
- file.isFile = function() {
- var filepath = path.join.apply(path, arguments);
- return file.exists(filepath) && fs.statSync(filepath).isFile();
- };
-
- // Is a given file path absolute?
- file.isPathAbsolute = function() {
- var filepath = path.join.apply(path, arguments);
- return path.resolve(filepath) === filepath.replace(/[\/\\]+$/, '');
- };
-
- // Do all the specified paths refer to the same path?
- file.arePathsEquivalent = function(first) {
- first = path.resolve(first);
- for (var i = 1; i < arguments.length; i++) {
- if (first !== path.resolve(arguments[i])) { return false; }
- }
- return true;
- };
-
- // Are descendant path(s) contained within ancestor path? Note: does not test
- // if paths actually exist.
- file.doesPathContain = function(ancestor) {
- ancestor = path.resolve(ancestor);
- var relative;
- for (var i = 1; i < arguments.length; i++) {
- relative = path.relative(path.resolve(arguments[i]), ancestor);
- if (relative === '' || /\w+/.test(relative)) { return false; }
- }
- return true;
- };
-
- // Test to see if a filepath is the CWD.
- file.isPathCwd = function() {
- var filepath = path.join.apply(path, arguments);
- try {
- return file.arePathsEquivalent(fs.realpathSync(process.cwd()), fs.realpathSync(filepath));
- } catch(e) {
- return false;
- }
- };
-
- // Test to see if a filepath is contained within the CWD.
- file.isPathInCwd = function() {
- var filepath = path.join.apply(path, arguments);
- try {
- return file.doesPathContain(fs.realpathSync(process.cwd()), fs.realpathSync(filepath));
- } catch(e) {
- return false;
- }
- };
|