Gulps gulp.watch not triggered for new or deleted files? - javascript

The following Gulpjs task works fine when editing files in the glob match:
// watch task.
gulp.task('watch', ['build'], function () {
gulp.watch(src + '/js/**/*.js', ['scripts']);
gulp.watch(src + '/img//**/*.{jpg,jpeg,png,gif}', ['copy:images']);
gulp.watch(src + '/less/*.less', ['styles']);
gulp.watch(src + '/templates/**/*.{swig,json}', ['html']);
});
// build task.
gulp.task('build', ['clean'], function() {
return gulp.start('copy', 'scripts', 'less', 'htmlmin');
});
However it doesn't work (it's not triggered) for new or deleted files. Is there something I'm missing?
EDIT: even using grunt-watch plugin it seems not working:
gulp.task('scripts', function() {
return streamqueue(
{ objectMode: true },
gulp.src([
vendor + '/jquery/dist/jquery.min.js',
vendor + '/bootstrap/dist/js/bootstrap.min.js'
]),
gulp.src([
src + '/js/**/*.js'
]).pipe(plugins.uglify())
)
.pipe(plugins.concat(pkg.name + '.min.js'))
.pipe(gulp.dest(dest + '/js/'));
});
gulp.task('watch', ['build'], function () {
plugins.watch({glob: src + '/js/**/*.js'}, function () {
gulp.start('scripts');
});
});
EDIT: Solved, it was this issue. Globs starting with ./ (that was the value of src) seems not working ATM.

Edit: Apparently gulp.watch does work with new or deleted files now. It did not when the question was asked.
The rest of my answer still stands: gulp-watch is usually a better solution because it lets you perform specific actions only on the files that have been modified, while gulp.watch only lets you run complete tasks. For a project of a reasonable size, this will quickly become too slow to be useful.
You aren't missing anything. gulp.watch does not work with new or deleted files. It's a simple solution designed for simple projects.
To get file watching that can look for new files, use the gulp-watch plugin, which is much more powerful. Usage looks like this:
var watch = require('gulp-watch');
// in a task
watch({glob: <<glob or array of globs>> })
.pipe( << add per-file tasks here>> );
// if you'd rather rerun the whole task, you can do this:
watch({glob: <<glob or array of globs>>}, function() {
gulp.start( <<task name>> );
});
Personally, I recommend the first option. This allows for much faster, per-file processes. It works great during development with livereload as long as you aren't concatenating any files.
You can wrap up your streams either using my lazypipe library, or simply using a function and stream-combiner like this:
var combine = require('stream-combiner');
function scriptsPipeline() {
return combine(coffeeescript(), uglify(), gulp.dest('/path/to/dest'));
}
watch({glob: 'src/scripts/**/*.js' })
.pipe(scriptsPipeline());
UPDATE October 15, 2014
As pointed out by #pkyeck below, apparently the 1.0 release of gulp-watch changed the format slightly, so the above examples should now be:
var watch = require('gulp-watch');
// in a task
watch(<<glob or array of globs>>)
.pipe( << add per-file tasks here>> );
// if you'd rather rerun the whole task, you can do this:
watch(<<glob or array of globs>>, function() {
gulp.start( <<task name>> );
});
and
var combine = require('stream-combiner');
function scriptsPipeline() {
return combine(coffeeescript(), uglify(), gulp.dest('/path/to/dest'));
}
watch('src/scripts/**/*.js')
.pipe(scriptsPipeline());

Both gulp.watch() and require('gulp-watch')() will trigger for new/deleted files however not if you use absolute directories. In my tests I did not use "./" for relative directories BTW.
Both won't trigger if whole directories are deleted though.
var watch = require('gulp-watch');
//Wont work for new files until gaze is fixed if using absolute dirs. It won't trigger if whole directories are deleted though.
//gulp.watch(config.localDeploy.path + '/reports/**/*', function (event) {
//gulp.watch('src/app1/reports/**/*', function (event) {
// console.log('*************************** Event received in gulp.watch');
// console.log(event);
// gulp.start('localDeployApp');
});
//Won't work for new files until gaze is fixed if using absolute dirs. It won't trigger if whole directories are deleted though. See https://github.com/floatdrop/gulp-watch/issues/104
//watch(config.localDeploy.path + '/reports/**/*', function() {
watch('src/krfs-app/reports/**/*', function(event) {
console.log("watch triggered");
console.log(event);
gulp.start('localDeployApp');
//});

If src is an absolute path (starting with /), your code is not going to detect new or deleted files. However there's still a way:
Instead of:
gulp.watch(src + '/js/**/*.js', ['scripts']);
write:
gulp.watch('js/**/*.js', {cwd: src}, ['scripts']);
and it will work!

Globs must have a separate base directory specified and that base location must not be specified in the glob itself.
If you have lib/*.js, it'll look under the current working dir which is process.cwd()
Gulp uses Gaze to watch files and in the Gulp API doc we see that we can pass Gaze specific options to the watch function: gulp.watch(glob[, opts], tasks)
Now in the Gaze doc we can find that the current working dir (glob base dir) is the cwd option.
Which leads us to alexk's answer:
gulp.watch('js/**/*.js', {cwd: src}, ['scripts']);

I know this is an older question, but I thought I'd throw the solution I came up with. None of the gulp plugins I found would notify me of new or renamed files. So I ended up wrapping monocle in a convenience function.
Here's an example of how that function is used:
watch({
root: config.src.root,
match: [{
when: 'js/**',
then: gulpStart('js')
}, {
when: '+(scss|css)/**',
then: gulpStart('css')
}, {
when: '+(fonts|img)/**',
then: gulpStart('assets')
}, {
when: '*.+(html|ejs)',
then: gulpStart('html')
}]
});
I should note that gulpStart is also a convenience function I made.
And here is the actual watch module.
module.exports = function (options) {
var path = require('path'),
monocle = require('monocle'),
minimatch = require('minimatch');
var fullRoot = path.resolve(options.root);
function onFileChange (e) {
var relativePath = path.relative(fullRoot, e.fullPath);
options.match.some(function (match) {
var isMatch = minimatch(relativePath, match.when);
isMatch && match.then();
return isMatch;
});
}
monocle().watchDirectory({
root: options.root,
listener: onFileChange
});
};
Pretty simple, eh? The whole thing can be found over at my gulp starter kit: https://github.com/chrisdavies/gulp_starter_kit

It is important to note that it looks like gulp.watch only reports changed and deleted files on Windows but listens for new and deleted files by default on OSX:
https://github.com/gulpjs/gulp/issues/675

You should use 'gulp-watch' for new/renamed/deleted files instead of gulp.watch
var gulpwatch = require('gulp-watch');
var source = './assets',
destination = './dest';
gulp.task('copy-changed-assets', function() {
gulpwatch(source+'/**/*', function(obj){
gulp.src( obj.path, { "base": source})
.pipe(gulp.dest(destination));
});
});

Related

How to sync a local folder with gulp, with update for deleted files and folders?

I am working on a WordPress plugin and have all the files in my working directory and run gulp in that project folder. Now, I'd like to have a watch task that copies all the changes to my local WP installation for testing.
Therefore I am looking for a way to sync (only in one direction) the project folder with the plugin folder of WP.
I managed to get it to work with gulp-directory-sync
...
var dirSync = require("gulp-directory-sync");
var localDir = "../newDir/";
var buildDir = "./buildDir/";
...
function copy_to_local_folder() {
return pipeline(
gulp.src(buildDir+'**/*'),
dirSync( buildDir, localDir, { printSummary: true } )
);
}
function watch_local() {
gulp.watch(buildDir+'**/*', copy_to_local_folder);
exports.default = watch_local;
However, the plugin hasn't been updated in 4 years and according to this answer, it is not doing it the proper "gulp way" (e.g. not using gulp-src) and this task should be possible with other basic gulp functions.
Copying changed files is pretty easy, but also keeping track of deleted files is more complicated. I also would prefer to only update changed/deleted/new files and not clearing the folder every time before coping all files.
Starting with the updated code in the aforementioned answer, I tried to implement it and made changes to make it work.
...
var newer = require("gulp-newer");
var pipeline = require("readable-stream").pipeline;
var del = require("del");
var localDir = "../newDir/";
var buildDir = "./buildDir/";
function copy_to_local_folder() {
return pipeline(
gulp.src([buildDir+'**/*']),
newer(localDir),
gulp.dest(localDir),
);
}
function watch_local() {
var watcher = gulp.watch(buildDir + '**/*', copy_to_local_folder );
watcher.on('unlink', function(path) {
console.log(path);
var newPath = './'+path;
newPath = newPath.replace(buildDir, localDir);
console.log(newPath);
(async () => {
const deletedPaths = await del(newPath, {dryRun: true, force: true});
console.log('Deleted files and directories:\n', deletedPaths.join('\n'));
})();
});
}
exports.default = watch_local;
With this code, the folder gets updated when I change or delete files, but it does not trigger when I delete an entire folder. Which is probably because I use unlink and not unlinkDir. But even if I use the version of the function below, it doesn't get triggered by deleting a folder (with containing files).
watcher.on('unlinkDir', function(path) {
console.log('folder deleted');
console.log(path);
var newPath = './'+path;
newPath = newPath.replace(buildDir, localDir);
console.log(newPath);
});
What am I doing wrong?
Or is there in general a better way to achieve this?
PS: I'm using
node v11.15.0
gulp v4.0.2
on Linux
deleting files and folders in VS Code
Update:
When I run it with:
watcher.on('unlink', ... and delete a file:
it works
with the console.log output and the ( async () => ...
and Starting and Finished for copy_to_local_folder
watcher.on('unlinkDir', ... and delete a folder:
it works not
nothing happens in the console output
(not even Starting)
watcher.on('unlinkDir', ... and delete a file:
Starting and Finished for copy_to_local_folder
but not the console.log and ( async () => ...
watcher.on('add', ... and watcher.on('addDir', ...
work both
Seems to me that the watcher.on('unlinkDir', ... does never get triggered ... is unlinkDir not supported by gulp-watch?

Gulp 4 watch doesn't detect changes

Is it possible with gulp v.4.0.0 to watch for files outside of the folder where gulpfile.js is located?
In older gulp it was possible to set file path to ../../someFile.css and watch for its changes, but in v4 it doesn't detect changes for the same path.
// snip....
let rootFolder = '..\..\root\';
// Watch function
let watchThis = (filePath, gulpTasks) => {
gulp.watch(filePath, gulp.series(gulpTasks))
.on('change', function(path) {
console.log(`${chalk.blue('Changed')}: ${chalk.yellow(path)}`);
})
.on('unlink', function(path) {
console.log(`${chalk.red('Deleted')}: ${chalk.yellow(path)}`);
});
}
// Watch task configuration
let watchFiles = () => {
watchThis([`${rootFolder}/style1.scss`, `${rootFolder}/style2.scss`], 'scss')
watchThis('./js/main.js', 'js')
}
// Final watch task
gulp.task('watch', gulp.series(
'development',
gulp.parallel(
watchFiles,
'startLiveServers'
)
));
// ...snip
Changes to files ['../../style1.scss', '../../style2.scss'] will not be detected, but they will be for './js/main.js'. Am I missing something?
Problem with new Gulp 4 watch task is in paths. Unlike watch task from Gulp 3, new version is unforgiving and requires correct path structure with correct paths separators.
So instead of using paths that may result in ..\\..\\root\\/style1.scss, we must convert paths to proper structure like ../../root/style1.scss.
This simple function helps and the rest is handled by gulp and nodejs
let fixPath = (oldPath) => {
const pathString = oldPath;
const fix = /\\{1,}|\/{1,}/;
return pathString.replace(new RegExp(fix, 'gm'), '/').replace(new RegExp(fix, 'gm'), '/')
}

Deleting files in a gulp task

I have a gulp task in which I want to take some source files and copy them to build/premium and build/free and then remove some extra files from
build/free.
My attempt at that was doing this:
gulp.task("build", ["clean"], function () {
gulp.src(["src/*", "!src/composer.*", "LICENSE"])
.pipe(gulp.dest("build/premium"))
.pipe(del(["build/free/plugins/*", "!build/free/plugins/index.php"]))
.pipe(gulp.dest("build/free"));
});
Which results in an error:
TypeError: dest.on is not a function
at DestroyableTransform.Stream.pipe (stream.js:45:8)
at Gulp.<anonymous> (/Users/gezim/projects/myproj/gulpfile.js:9:6)
How do I accomplish this the deleting port? Is there a better way altogether to do this?
This is a simple clean task implementation with gulp-del:
var del = require('gulp-del');
gulp.task('clean', function(){
return del(['folderA/js', 'folderA/css', 'folderB/js']);
});
In your case you can just call it after build (read "use build as a dependency"):
gulp.task("build", function () {
return gulp.src(['src/*', '!src/composer.*', 'LICENSE'])
.pipe(gulp.dest("build/premium"))
.pipe(gulp.dest("build/free"));
});
gulp.task("complete-build", ["build"] function(){
return del(['build/free/plugins/*', '!build/free/plugins/index.php']);
});
Then call the "complete-build" task to perform it.
To be honest this is more a "Grunt"-like approach to the problem, but done with Gulp. Perhaps the recommendation to filter things before writing them in the build/free folder is more in the Gulp spirit.
Update 2/2018
The delete module has been renamed to del now as reported by #gerl:
var del = require('del');
gulp.task('clean', function(){
return del(['folderA/js', 'folderA/css', 'folderB/js']);
});
I would use gulp-filter to drop only what should not be copied from the 2nd destination.
I interpreted the intent of the task as wanting everything present in src to be present in build/premium. However, build/free should exclude everything which was originally in src/plugins but should still include src/plugins/index.php.
Here is a working gulpfile:
var gulp = require("gulp");
var filter = require("gulp-filter");
var del = require("del");
gulp.task("clean", function () {
return del("build");
});
gulp.task("build", ["clean"], function () {
return gulp.src(["src/**", "!src/composer.*", "LICENSE"])
.pipe(gulp.dest("build/premium"))
.pipe(filter(["**", "!plugins/**", "plugins/index.php"]))
.pipe(gulp.dest("build/free"));
});
The patterns passed to filter are relative paths. Since the gulp.src pattern has src/** it means they are relative to src.
Note also that del cannot be passed straight to .pipe() as it returns a promise. It can be returned from a task, like the clean task does.

gulp-protractor e2e task fails to run

Update: Seems like this is a bug in gulp-protractor. On their github page they filed it as a bug and will take a look into it. Source: https://github.com/mllrsohn/gulp-protractor/issues/64
Only possible workaround you can do until the bug is resolved is change the directory of your project to something that doesn't include spaces.
So I'm trying to get an Aurelia project started including front end unit testing. Here is where the problem starts. When I try to run the e2e gulp task I get the following error:
[10:45:44] using gulpfile ~\Documents\Visual Studio 2013\Projects\ProjectX\ProjectX\gulpfile.js
[10:45:44] Starting 'build-e2e'...
[10:45:44] Finished 'build-e2e' after 207 ms
[10:45:44] Starting 'e2e'...
'C:\Users\jorisd\Documents\Visual' is not recognized as an internal or external command, operable program or batch file.
C:\Users\jorisd\Documents\Visual Studio 2013\Projects\ProjectX\ProjectX\build\tasks\e2e.js:34
.on('error', function(e) {throw e; });
Error: protractor exited with code 1
Basically it's the highlighted code that has the problem. Since my path includes a space, it'll stop there for some reason.
Here's how my e2e.js file looks like right now:
var gulp = require('gulp');
var paths = require('../paths');
var to5 = require('gulp-babel');
var plumber = require('gulp-plumber');
var webdriverUpdate = require('gulp-protractor').webdriver_update;
var webdriverStandalone = require("gulp-protractor").webdriver_standalone;
var protractor = require('gulp-protractor').protractor;
// for full documentation of gulp-protractor,
// please check https://github.com/mllrsohn/gulp-protractor
gulp.task('webdriver-update', webdriverUpdate);
gulp.task('webdriver-standalone', ['webdriver-update'], webdriverStandalone);
// transpiles files in
// /test/e2e/src/ from es6 to es5
// then copies them to test/e2e/dist/
gulp.task('build-e2e', function() {
return gulp.src(paths.e2eSpecsSrc)
.pipe(plumber())
.pipe(to5())
.pipe(gulp.dest(paths.e2eSpecsDist));
});
// runs build-e2e task
// then runs end to end tasks
// using Protractor: http://angular.github.io/protractor/
gulp.task('e2e', ['build-e2e'], function(cb) {
return gulp.src(paths.e2eSpecsDist + '/*.js')
.pipe(protractor({
configFile: '/protractor.conf.js',
args: ['--baseUrl', 'http://127.0.0.1:9000']
}))
.on('end', function() { process.exit(); })
.on('error', function(e) { throw e; });
});
The problem is situating in the e2e task with the configFile option.
I tried change the line into the following:
configFIle: __dirname + '/protractor.conf.js',
But this aswell without result. If any of you know a workaround for including spaces in the configFile path, I'll be happy to hear it.
For me its working fine.
var angularProtractor = require('gulp-angular-protractor');
gulp.task('test', function (callback) {
gulp
.src([__dirname+'/public/apps/adminapp/**/test/**_test.js'])
.pipe(angularProtractor({
'configFile': 'public/apps/adminapp/app.test.config.js',
'debug': false,
'args': ['--suite', 'adminapp'],
'autoStartStopServer': true
}))
.on('error', function(e) {
console.log(e);
})
.on('end',callback);
});

Gulp pipe and caching issue when using "gulp-translate-html" and "gulp-inject"

I have a project where I have to generate translated static pages.
The choice was to use gulp because it helps a lot in minifying resources, watch for file changes and re-compile, and can also inject html templates in several pages.
I used:
- 'gulp-inject': for inserting templates into final files
- 'gulp-translate-html': for translating because I have '.json' dictionaries
So I have two issues:
'gulp-translate-html'
This uses the json as input for translating, using the following code:
gulp.task('translate', function() {
return gulp.src('./temp/en/template.html')
.pipe(translateHtml({
messages: require('./dictionary/en.json'),
templateSettings: {
interpolate: /{{([\s\S]+?)}}/g
}
}))
.pipe(gulp.dest('./en'));
});
I created a watch on the '.json' file, when modified, it should re-apply the translation. But somehow it uses the old file instead of the modified one.
Is there a workaround for this? Or other plugin that I could use for the json files?
'gulp-inject'
In the code-sample above, I translated only one file. But I need to do so for several languages that have different destinations, so I used a loop for the languages.(sorry for the code indentation)
var gulp = require('gulp'),
inject = require('gulp-inject'),
translateHtml = require('gulp-translate-html');
var languages = ['en', 'de'];
gulp.task('injectContent', function() {
/* the base file used as a reference*/
var target = gulp.src('./templates/base/baseTemplate.html');
/* get each language*/
languages.forEach(function(lang) {
target.pipe(inject(gulp.src('./templates/partials/injectTemplate.html'), {
relative: true,
starttag: '<!-- inject:template -->',
transform: function (filePath, file) {
return file.contents.toString('utf8');
}
}))
/* put the merged files into "temp" folder under its language folder*/
.pipe(gulp.dest('./temp/'+lang));
});
});
/* The translation has to be made after the injection above is finished*/
gulp.task('translate', ['injectContent'] function() {
/* get each language*/
languages.forEach(function(lang) {
gulp.src('./temp/'+ lang +'/baseTemplate.html')
.pipe(translateHtml({
messages: require('./dictionary/'+lang+'.json');,
templateSettings: {
interpolate: /{{([\s\S]+?)}}/g
}
}))
.pipe(gulp.dest('./'+lang)); /* put file in the "en" or "de" language folder*/
});
});
gulp.task('watch', function() {
gulp.watch(['./templates/**/*.html', './dictionary/*.json'], ['translate']);
});
gulp.task('default', ['translate', 'watch']);
Here I want the 'injectContent' task to be ran before the 'translation' task, but the latter runs too soon. This happens because there is not a specific return gulp callback in the 'injectContent', right?
How can I merge the results and not let the tasks intercalate?
Just found a solution for the caching issue from point 1:
Based on this answer: https://stackoverflow.com/a/16060619/944637 I deleted the cache and then the "require" function could reload the file from the filesystem and not from cache.
I added delete require.cache[require.resolve('./dictionary/en.json')]; at the beginning of the 'translate' task, before return.
EDIT: Just found the solution on Point 2 to merge the results using "merge-stream", in this answer here: https://stackoverflow.com/a/26786529
so my code turned out to be like this:
....
merge = require('merge-stream');
gulp.task('injectContent', function() {
var tasks = languages.map(function(lang){
return gulp.src('./templates/base/injectContent.html')
.pipe(plumber())
.pipe(debug())
.pipe(inject(gulp.src('./templates/partials/injectTemplate.html'), {
relative: true,
starttag: '<!-- inject:release -->',
transform: function (filePath, file) {
return file.contents.toString('utf8');
}
}))
.pipe(gulp.dest('./temp/'+lang));
});
return merge(tasks);
});
gulp.task('translate', ['injectContent'], function() {
for (var i in languages) {
var lang = languages[i];
delete require.cache[require.resolve('./dictionary/'+lang+'.json')];
gulp.src('./temp/'+lang+'/injectContent.html')
.pipe(plumber())
.pipe(debug())
.pipe(translateHtml({
messages: require('./dictionary/'+lang+'.json'),
templateSettings: {
interpolate: /{{([\s\S]+?)}}/g // this is for Angular-like variable syntax
}
}))
.pipe(gulp.dest('./'+lang));
}
});
....

Categories