Skip to content

Commit 5e3c4ee

Browse files
authored
Gulp Newer Fix (#119)
* putting gulp-newer directly in the repo * npm install issues * Adding readme explanation * ignoring linting * updating jest to ignore gulp plugins * it should be a path, not a glob
1 parent 6832e3a commit 5e3c4ee

File tree

7 files changed

+1321
-3
lines changed

7 files changed

+1321
-3
lines changed

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lib/gulp-plugins/gulp-newer/**

lib/cmd/compile/fonts.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const _ = require('lodash'),
55
path = require('path'),
66
es = require('event-stream'),
77
gulp = require('gulp'),
8-
newer = require('gulp-newer'),
8+
newer = require('../../gulp-plugins/gulp-newer'),
99
concat = require('gulp-concat'),
1010
rename = require('gulp-rename'),
1111
gulpIf = require('gulp-if'),

lib/gulp-plugins/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Explanation
2+
3+
The modules in this directory are meant to be Gulp plugins. Right now the `gulp-newer` package has changes we need to be released to NPM. Once that update happens we can go back to including in the package.json
4+
5+
There were issues with people npm installing claycli and having the package installed from github, so this is the workaround.

lib/gulp-plugins/gulp-newer/index.js

+255
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
var Transform = require('stream').Transform;
2+
var fs = require('fs');
3+
var path = require('path');
4+
var util = require('util');
5+
var glob = require('glob');
6+
7+
var Q = require('kew');
8+
var PluginError = require('plugin-error');
9+
10+
var PLUGIN_NAME = 'gulp-newer';
11+
12+
function Newer(options) {
13+
Transform.call(this, {objectMode: true});
14+
15+
if (!options) {
16+
throw new PluginError(
17+
PLUGIN_NAME,
18+
'Requires a dest string or options object'
19+
);
20+
}
21+
22+
if (typeof options === 'string') {
23+
options = {dest: options};
24+
} else if (options.dest && typeof options.dest !== 'string') {
25+
throw new PluginError(PLUGIN_NAME, 'Requires a dest string');
26+
}
27+
28+
if (options.ext && typeof options.ext !== 'string') {
29+
throw new PluginError(PLUGIN_NAME, 'Requires ext to be a string');
30+
}
31+
32+
if (options.map && typeof options.map !== 'function') {
33+
throw new PluginError(PLUGIN_NAME, 'Requires map to be a function');
34+
}
35+
36+
if (!options.dest && !options.map) {
37+
throw new PluginError(
38+
PLUGIN_NAME,
39+
'Requires either options.dest or options.map or both'
40+
);
41+
}
42+
43+
if (options.extra) {
44+
if (typeof options.extra === 'string') {
45+
options.extra = [options.extra];
46+
} else if (!Array.isArray(options.extra)) {
47+
throw new PluginError(
48+
PLUGIN_NAME,
49+
'Requires options.extra to be a string or array'
50+
);
51+
}
52+
}
53+
54+
/**
55+
* Path to destination directory or file.
56+
* @type {string}
57+
*/
58+
this._dest = options.dest;
59+
60+
/**
61+
* Optional extension for destination files.
62+
* @type {string}
63+
*/
64+
this._ext = options.ext;
65+
66+
/**
67+
* Optional function for mapping relative source files to destination files.
68+
* @type {function(string): string}
69+
*/
70+
this._map = options.map;
71+
72+
/**
73+
* Key for the timestamp in files' stats object
74+
* @type {string}
75+
*/
76+
this._timestamp = options.ctime ? 'ctime' : 'mtime';
77+
78+
/**
79+
* Promise for the dest file/directory stats.
80+
* @type {[type]}
81+
*/
82+
this._destStats = this._dest
83+
? Q.nfcall(fs.stat, this._dest)
84+
: Q.resolve(null);
85+
86+
/**
87+
* If the provided dest is a file, we want to pass through all files if any
88+
* one of the source files is newer than the dest. To support this, source
89+
* files need to be buffered until a newer file is found. When a newer file
90+
* is found, buffered source files are flushed (and the `_all` flag is set).
91+
* @type {[type]}
92+
*/
93+
this._bufferedFiles = null;
94+
95+
/**
96+
* Indicates that all files should be passed through. This is set when the
97+
* provided dest is a file and we have already encountered a newer source
98+
* file. When true, all remaining source files should be passed through.
99+
* @type {boolean}
100+
*/
101+
this._all = false;
102+
103+
/**
104+
* Indicates that there are extra files (configuration files, etc.)
105+
* that are not to be fed into the stream, but that should force
106+
* all files to be rebuilt if *any* are older than one of the extra
107+
* files.
108+
*/
109+
this._extraStats = null;
110+
111+
if (options.extra) {
112+
var extraFiles = [];
113+
var timestamp = this._timestamp;
114+
for (var i = 0; i < options.extra.length; ++i) {
115+
extraFiles.push(Q.nfcall(glob, options.extra[i]));
116+
}
117+
this._extraStats = Q.all(extraFiles)
118+
.then(function(fileArrays) {
119+
// First collect all the files in all the glob result arrays
120+
var allFiles = [];
121+
var i;
122+
for (i = 0; i < fileArrays.length; ++i) {
123+
allFiles = allFiles.concat(fileArrays[i]);
124+
}
125+
var extraStats = [];
126+
for (i = 0; i < allFiles.length; ++i) {
127+
extraStats.push(Q.nfcall(fs.stat, allFiles[i]));
128+
}
129+
return Q.all(extraStats);
130+
})
131+
.then(function(resolvedStats) {
132+
// We get all the file stats here; find the *latest* modification.
133+
var latestStat = resolvedStats[0];
134+
for (var j = 1; j < resolvedStats.length; ++j) {
135+
if (resolvedStats[j][timestamp] > latestStat[timestamp]) {
136+
latestStat = resolvedStats[j];
137+
}
138+
}
139+
return latestStat;
140+
})
141+
.fail(function(error) {
142+
if (error && error.path) {
143+
throw new PluginError(
144+
PLUGIN_NAME,
145+
'Failed to read stats for an extra file: ' + error.path
146+
);
147+
} else {
148+
throw new PluginError(
149+
PLUGIN_NAME,
150+
'Failed to stat extra files; unknown error: ' + error
151+
);
152+
}
153+
});
154+
}
155+
}
156+
util.inherits(Newer, Transform);
157+
158+
/**
159+
* Pass through newer files only.
160+
* @param {File} srcFile A vinyl file.
161+
* @param {string} encoding Encoding (ignored).
162+
* @param {function(Error, File)} done Callback.
163+
*/
164+
Newer.prototype._transform = function(srcFile, encoding, done) {
165+
if (!srcFile || !srcFile.stat) {
166+
done(new PluginError(PLUGIN_NAME, 'Expected a source file with stats'));
167+
return;
168+
}
169+
var self = this;
170+
Q.resolve([this._destStats, this._extraStats])
171+
.spread(function(destStats, extraStats) {
172+
if ((destStats && destStats.isDirectory()) || self._ext || self._map) {
173+
// stat dest/relative file
174+
var relative = srcFile.relative;
175+
var ext = path.extname(relative);
176+
var destFileRelative = self._ext
177+
? relative.substr(0, relative.length - ext.length) + self._ext
178+
: relative;
179+
if (self._map) {
180+
destFileRelative = self._map(destFileRelative);
181+
}
182+
var destFileJoined = self._dest
183+
? path.join(self._dest, destFileRelative)
184+
: destFileRelative;
185+
return Q.all([Q.nfcall(fs.stat, destFileJoined), extraStats]);
186+
} else {
187+
// wait to see if any are newer, then pass through all
188+
if (!self._bufferedFiles) {
189+
self._bufferedFiles = [];
190+
}
191+
return [destStats, extraStats];
192+
}
193+
})
194+
.fail(function(err) {
195+
if (err.code === 'ENOENT') {
196+
// dest file or directory doesn't exist, pass through all
197+
return Q.resolve([null, this._extraStats]);
198+
} else {
199+
// unexpected error
200+
return Q.reject(err);
201+
}
202+
})
203+
.spread(function(destFileStats, extraFileStats) {
204+
var timestamp = self._timestamp;
205+
var newer =
206+
!destFileStats || srcFile.stat[timestamp] > destFileStats[timestamp];
207+
// If *any* extra file is newer than a destination file, then ALL
208+
// are newer.
209+
if (
210+
extraFileStats &&
211+
extraFileStats[timestamp] > destFileStats[timestamp]
212+
) {
213+
newer = true;
214+
}
215+
if (self._all) {
216+
self.push(srcFile);
217+
} else if (!newer) {
218+
if (self._bufferedFiles) {
219+
self._bufferedFiles.push(srcFile);
220+
}
221+
} else {
222+
if (self._bufferedFiles) {
223+
// flush buffer
224+
self._bufferedFiles.forEach(function(file) {
225+
self.push(file);
226+
});
227+
self._bufferedFiles.length = 0;
228+
// pass through all remaining files as well
229+
self._all = true;
230+
}
231+
self.push(srcFile);
232+
}
233+
done();
234+
})
235+
.fail(done)
236+
.end();
237+
};
238+
239+
/**
240+
* Remove references to buffered files.
241+
* @param {function(Error)} done Callback.
242+
*/
243+
Newer.prototype._flush = function(done) {
244+
this._bufferedFiles = null;
245+
done();
246+
};
247+
248+
/**
249+
* Only pass through source files that are newer than the provided destination.
250+
* @param {Object} options An options object or path to destination.
251+
* @return {Newer} A transform stream.
252+
*/
253+
module.exports = function(options) {
254+
return new Newer(options);
255+
};

lib/gulp-plugins/gulp-newer/readme.md

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# `gulp-newer`
2+
3+
A [Gulp](http://gulpjs.com/) plugin for passing through only those source files that are newer than corresponding destination files.
4+
5+
## Install
6+
7+
```
8+
npm install gulp-newer --save-dev
9+
```
10+
11+
## Example
12+
13+
### Using `newer` with 1:1 source:dest mappings
14+
15+
The default task in the example below sets up a watch that minifies images on changes. Piping the source files to `newer` before `imagemin` ensures that only those images that have changed are minified. The `newer` plugin is configured with the directory path for minified images.
16+
17+
```js
18+
var gulp = require('gulp');
19+
var newer = require('gulp-newer');
20+
var imagemin = require('gulp-imagemin');
21+
22+
var imgSrc = 'src/img/**';
23+
var imgDest = 'build/img';
24+
25+
// Minify any new images
26+
gulp.task('images', function() {
27+
28+
// Add the newer pipe to pass through newer images only
29+
return gulp.src(imgSrc)
30+
.pipe(newer(imgDest))
31+
.pipe(imagemin())
32+
.pipe(gulp.dest(imgDest));
33+
34+
});
35+
36+
gulp.task('default', function() {
37+
gulp.watch(imgSrc, ['images']);
38+
});
39+
```
40+
41+
### Using `newer` with many:1 source:dest mappings
42+
43+
Plugins like `gulp-concat` take many source files and generate a single destination file. In this case, the `newer` stream will pass through *all* source files if *any one* of them is newer than the destination file. The `newer` plugin is configured with the destination file path.
44+
45+
```js
46+
var gulp = require('gulp');
47+
var newer = require('gulp-newer');
48+
var concat = require('gulp-concat');
49+
50+
// Concatenate all if any are newer
51+
gulp.task('concat', function() {
52+
53+
// Add the newer pipe to pass through all sources if any are newer
54+
return gulp.src('lib/*.js')
55+
.pipe(newer('dist/all.js'))
56+
.pipe(concat('all.js'))
57+
.pipe(gulp.dest('dist'));
58+
59+
});
60+
```
61+
62+
63+
## API
64+
65+
### `newer(dest)`
66+
* **dest** - `string` Path to destination directory or file.
67+
68+
### `newer(options)`
69+
70+
* **options.dest** - `string` As above, *required*.
71+
* **options.ext** - `string` Source files will be matched to destination files with the provided extension (e.g. '.css').
72+
* **options.map** - `function` Map relative source paths to relative destination paths (e.g. `function(relativePath) { return relativePath + '.bak'; }`)
73+
* **options.extra** - `string` or `array` An extra file, file glob, or list of extra files and/or globs, to check for updated time stamp(s). If any of these files are newer than the destination files, then all source files will be passed into the stream.
74+
* **options.ctime** - `boolean` Source files will be matched to destination files based on `ctime` rather than `mtime`.
75+
76+
Create a [transform stream](http://nodejs.org/api/stream.html#stream_class_stream_transform_1) that passes through files whose modification time is more recent than the corresponding destination file's modification time.
77+
78+
If `dest` is a directory path, the `newer` stream will check for files in the destination directory with the same relative path as source files. Source files that have been modified more recently than the resolved destination file will be passed through. If the `dest` directory or resolved destination file does not exist, all source files will be passed through.
79+
80+
If `dest` is a file path, the `newer` stream will pass through *all* files if *any one* of them has been modified more recently than the destination file. If the `dest` file does not exist, all source files will be passed through.
81+
82+
By default, the `newer` stream will check files' *modification time*, or `mtime`, which updates when the file contents are changed. If `ctime` is `true`, files' *change time* will be checked instead, which updates when file contents or attributes like the file name or permissions have changed.
83+
84+
[![Current Status](https://secure.travis-ci.org/tschaub/gulp-newer.png?branch=master)](https://travis-ci.org/tschaub/gulp-newer)

0 commit comments

Comments
 (0)