UNPKG

8.61 kBJavaScriptView Raw
1/*
2 * grunt
3 * https://github.com/cowboy/grunt
4 *
5 * Copyright (c) 2012 "Cowboy" Ben Alman
6 * Licensed under the MIT license.
7 * http://benalman.com/about/license/
8 */
9
10var grunt = require('../grunt');
11
12// Nodejs libs.
13var fs = require('fs');
14var path = require('path');
15
16// The module to be exported.
17var file = module.exports = {};
18
19// External libs.
20file.glob = require('glob-whatev');
21
22// Change the current base path (ie, CWD) to the specified path.
23file.setBase = function() {
24 var dirpath = path.join.apply(path, arguments);
25 process.chdir(dirpath);
26};
27
28// Match a filepath against one or more wildcard patterns. Returns true if
29// any of the patterns match.
30file.isMatch = function(patterns, filepath) {
31 patterns = Array.isArray(patterns) ? patterns : [patterns];
32 return patterns.some(function(pattern) {
33 return file.glob.minimatch(filepath, pattern, {matchBase: true});
34 });
35};
36
37// Return an array of all file paths that match the given wildcard patterns.
38file.expand = function() {
39 var args = grunt.utils.toArray(arguments);
40 // If the first argument is an options object, save those options to pass
41 // into the file.glob.glob method for minimatch to use.
42 var options = grunt.utils.kindOf(args[0]) === 'object' ? args.shift() : {};
43 // Use the first argument if it's an Array, otherwise convert the arguments
44 // object to an array and use that.
45 var patterns = Array.isArray(args[0]) ? args[0] : args;
46 // Generate a should-be-unique number.
47 var uid = +new Date();
48 // Return a flattened, uniqued array of matching file paths.
49 return grunt.utils._(patterns).chain().flatten().map(function(pattern) {
50 // If pattern is a template, process it accordingly.
51 pattern = grunt.template.process(pattern);
52 // Just return the pattern if it's an internal directive.
53 if (grunt.task.getDirectiveParts(pattern)) { return pattern; }
54 // Otherwise, expand paths.
55 return file.glob.glob(pattern, options);
56 }).flatten().uniq(false, function(filepath) {
57 // Only unique file paths, but don't unique <something> directives, in case
58 // they are repeated intentionally.
59 return grunt.task.getDirectiveParts(filepath) ? ++uid : filepath;
60 }).value();
61};
62
63// Further filter file.expand.
64function expandByType(type) {
65 var args = grunt.utils.toArray(arguments).slice(1);
66 return file.expand.apply(file, args).filter(function(filepath) {
67 // Just return the filepath if it's an internal directive.
68 if (grunt.task.getDirectiveParts(filepath)) { return filepath; }
69 try {
70 return fs.statSync(filepath)[type]();
71 } catch(e) {
72 throw grunt.task.taskError(e.message, e);
73 }
74 });
75}
76
77// A few type-specific file expansion methods.
78file.expandDirs = expandByType.bind(file, 'isDirectory');
79file.expandFiles = expandByType.bind(file, 'isFile');
80
81// Return an array of all file paths that match the given wildcard patterns,
82// plus any URLs that were passed at the end.
83file.expandFileURLs = function() {
84 // Use the first argument if it's an Array, otherwise convert the arguments
85 // object to an array and use that.
86 var patterns = Array.isArray(arguments[0]) ? arguments[0] : grunt.utils.toArray(arguments);
87 var urls = [];
88 // Filter all URLs out of patterns list and store them in a separate array.
89 patterns = patterns.filter(function(pattern) {
90 if (/^(?:file|https?):\/\//i.test(pattern)) {
91 // Push onto urls array.
92 urls.push(pattern);
93 // Remove from patterns array.
94 return false;
95 }
96 // Otherwise, keep pattern.
97 return true;
98 });
99 // Return expanded filepaths with urls at end.
100 return file.expandFiles(patterns).map(function(filepath) {
101 var abspath = path.resolve(filepath);
102 // Convert C:\foo\bar style paths to /C:/foo/bar.
103 if (abspath.indexOf('/') !== 0) {
104 abspath = ('/' + abspath).replace(/\\/g, '/');
105 }
106 return 'file://' + abspath;
107 }).concat(urls);
108};
109
110// Like mkdir -p. Create a directory and any intermediary directories.
111file.mkdir = function(dirpath) {
112 if (grunt.option('no-write')) { return; }
113 dirpath.split(/[\/\\]/).reduce(function(parts, part) {
114 parts += part + '/';
115 var subpath = path.resolve(parts);
116 if (!path.existsSync(subpath)) {
117 try {
118 fs.mkdirSync(subpath, '0755');
119 } catch(e) {
120 throw grunt.task.taskError('Unable to create directory "' + subpath + '" (Error code: ' + e.code + ').', e);
121 }
122 }
123 return parts;
124 }, '');
125};
126
127// Recurse into a directory, executing callback for each file.
128file.recurse = function recurse(rootdir, callback, subdir) {
129 var abspath = subdir ? path.join(rootdir, subdir) : rootdir;
130 fs.readdirSync(abspath).forEach(function(filename) {
131 var filepath = path.join(abspath, filename);
132 if (fs.statSync(filepath).isDirectory()) {
133 recurse(rootdir, callback, path.join(subdir, filename));
134 } else {
135 callback(path.join(rootdir, subdir, filename), rootdir, subdir, filename);
136 }
137 });
138};
139
140// Is a given file path absolute?
141file.isPathAbsolute = function() {
142 var filepath = path.join.apply(path, arguments);
143 return path.resolve(filepath) === filepath;
144};
145
146// Search for a filename in the given directory or all parent directories.
147file.findup = function findup(dirpath, filename) {
148 var filepath = path.join(dirpath, filename);
149 // Return file if found.
150 if (path.existsSync(filepath)) { return filepath; }
151 // If parentpath is the same as dirpath, we can't go any higher.
152 var parentpath = path.resolve(dirpath, '..');
153 return parentpath === dirpath ? null : findup(parentpath, filename);
154};
155
156// Write a file.
157file.write = function(filepath, contents) {
158 var nowrite = grunt.option('no-write');
159 grunt.verbose.write((nowrite ? 'Not actually writing ' : 'Writing ') + filepath + '...');
160 // Create path, if necessary.
161 file.mkdir(path.dirname(filepath));
162 try {
163 if (!nowrite) {
164 // Actually write file.
165 fs.writeFileSync(filepath, contents);
166 }
167 grunt.verbose.ok();
168 return true;
169 } catch(e) {
170 grunt.verbose.error();
171 throw grunt.task.taskError('Unable to write "' + filepath + '" file (Error code: ' + e.code + ').', e);
172 }
173};
174
175// Read a file, return its contents.
176file.read = function(filepath, encoding) {
177 var src;
178 grunt.verbose.write('Reading ' + filepath + '...');
179 try {
180 src = fs.readFileSync(String(filepath), encoding ? null : 'utf8');
181 grunt.verbose.ok();
182 return src;
183 } catch(e) {
184 grunt.verbose.error();
185 throw grunt.task.taskError('Unable to read "' + filepath + '" file (Error code: ' + e.code + ').', e);
186 }
187};
188
189// Read a file, optionally processing its content, then write the output.
190file.copy = function(srcpath, destpath, options) {
191 if (!options) { options = {}; }
192 var src = file.read(srcpath, true);
193 if (options.process && options.noProcess !== true &&
194 !(options.noProcess && file.isMatch(options.noProcess, srcpath))) {
195 grunt.verbose.write('Processing source...');
196 try {
197 src = options.process(src.toString('utf8'));
198 grunt.verbose.ok();
199 } catch(e) {
200 grunt.verbose.error();
201 throw grunt.task.taskError('Error while processing "' + srcpath + '" file.', e);
202 }
203 }
204 file.write(destpath, src);
205};
206
207// Read a file, parse its contents, return an object.
208file.readJSON = function(filepath) {
209 var src = this.read(String(filepath));
210 var result;
211 grunt.verbose.write('Parsing ' + filepath + '...');
212 try {
213 result = JSON.parse(src);
214 grunt.verbose.ok();
215 return result;
216 } catch(e) {
217 grunt.verbose.error();
218 throw grunt.task.taskError('Unable to parse "' + filepath + '" file (' + e.message + ').', e);
219 }
220};
221
222// Clear the require cache for all passed filepaths.
223file.clearRequireCache = function() {
224 // If a non-string argument is passed, it's an array of filepaths, otherwise
225 // each filepath is passed individually.
226 var filepaths = typeof arguments[0] !== 'string' ? arguments[0] : grunt.utils.toArray(arguments);
227 // For each filepath, clear the require cache, if necessary.
228 filepaths.forEach(function(filepath) {
229 var abspath = path.resolve(filepath);
230 if (require.cache[abspath]) {
231 grunt.verbose.write('Clearing require cache for "' + filepath + '" file...').ok();
232 delete require.cache[abspath];
233 }
234 });
235};
236
237// Access files in the user's ".grunt" folder.
238file.userDir = function() {
239 var dirpath = path.join.apply(path, arguments);
240 var win32 = process.platform === 'win32';
241 var homepath = process.env[win32 ? 'USERPROFILE' : 'HOME'];
242 dirpath = path.resolve(homepath, '.grunt', dirpath);
243 return path.existsSync(dirpath) ? dirpath : null;
244};