blob: 8a8f8b337656c16e5ac2ff9981ddfe710f5ded0f [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001/*
2 * grunt-contrib-watch
3 * http://gruntjs.com/
4 *
5 * Copyright (c) 2018 "Cowboy" Ben Alman, contributors
6 * Licensed under the MIT license.
7 */
8
9'use strict';
10
11var path = require('path');
12var Gaze = require('gaze').Gaze;
13var _ = require('lodash');
14var waiting = 'Waiting...';
15var changedFiles = Object.create(null);
16var watchers = [];
17
18module.exports = function(grunt) {
19
20 var taskrun = require('./lib/taskrunner')(grunt);
21
22 // Default date format logged
23 var dateFormat = function(time) {
24 grunt.log.writeln(String(
25 'Completed in ' +
26 time.toFixed(3) +
27 's at ' +
28 (new Date()).toString()
29 ).cyan + ' - ' + waiting);
30 };
31
32 // When task runner has started
33 taskrun.on('start', function() {
34 Object.keys(changedFiles).forEach(function(filepath) {
35 // Log which file has changed, and how.
36 grunt.log.ok('File "' + filepath + '" ' + changedFiles[filepath] + '.');
37 });
38 // Reset changedFiles
39 changedFiles = Object.create(null);
40 });
41
42 // When task runner has ended
43 taskrun.on('end', function(time) {
44 if (time > 0) {
45 dateFormat(time);
46 }
47 });
48
49 // When a task run has been interrupted
50 taskrun.on('interrupt', function() {
51 grunt.log.writeln('').write('Scheduled tasks have been interrupted...'.yellow);
52 });
53
54 // When taskrun is reloaded
55 taskrun.on('reload', function() {
56 taskrun.clearRequireCache(Object.keys(changedFiles));
57 grunt.log.writeln('').writeln('Reloading watch config...'.cyan);
58 });
59
60 grunt.registerTask('watch', 'Run predefined tasks whenever watched files change.', function(target) {
61 var self = this;
62 var name = self.name || 'watch';
63
64 // Close any previously opened watchers
65 watchers.forEach(function(watcher) {
66 watcher.close();
67 });
68 watchers = [];
69
70 // Never gonna give you up, never gonna let you down
71 if (grunt.config([name, 'options', 'forever']) !== false) {
72 taskrun.forever();
73 }
74
75 // If a custom dateFormat function
76 var df = grunt.config([name, 'options', 'dateFormat']);
77 if (typeof df === 'function') {
78 dateFormat = df;
79 }
80
81 if (taskrun.running === false) {
82 grunt.log.writeln(waiting);
83 }
84
85 // Initialize taskrun
86 var targets = taskrun.init(name, {target: target});
87
88 targets.forEach(function(target) {
89 if (typeof target.files === 'string') {
90 target.files = [target.files];
91 }
92
93 // Process into raw patterns
94 var patterns = _.chain(target.files).flatten().map(function(pattern) {
95 return grunt.config.process(pattern);
96 }).value();
97
98 // Validate the event option
99 if (typeof target.options.event === 'string') {
100 target.options.event = [target.options.event];
101 }
102
103 var eventCwd = process.cwd();
104 if (target.options.cwd && target.options.cwd.event) {
105 eventCwd = target.options.cwd.event;
106 }
107
108 // Set cwd if options.cwd.file is set
109 if (typeof target.options.cwd !== 'string' && target.options.cwd.files) {
110 target.options.cwd = target.options.cwd.files;
111 }
112
113 // Create watcher per target
114 watchers.push(new Gaze(patterns, target.options, function(err) {
115 if (err) {
116 if (typeof err === 'string') {
117 err = new Error(err);
118 }
119 grunt.log.writeln('ERROR'.red);
120 grunt.fatal(err);
121 return taskrun.done();
122 }
123
124 // Log all watched files with --verbose set
125 if (grunt.option('verbose')) {
126 var watched = this.watched();
127 Object.keys(watched).forEach(function(watchedDir) {
128 watched[watchedDir].forEach(function(watchedFile) {
129 grunt.log.writeln('Watching ' + path.relative(process.cwd(), watchedFile) + ' for changes.');
130 });
131 });
132 }
133
134 // On changed/added/deleted
135 this.on('all', function(status, filepath) {
136
137 // Skip events not specified
138 if (!_.includes(target.options.event, 'all') &&
139 !_.includes(target.options.event, status)) {
140 return;
141 }
142
143 filepath = path.relative(eventCwd, filepath);
144
145 // Skip empty filepaths
146 if (filepath === '') {
147 return;
148 }
149
150 // If Gruntfile.js changed, reload self task
151 if (target.options.reload || /gruntfile\.(js|coffee)/i.test(filepath)) {
152 taskrun.reload = true;
153 }
154
155 // Emit watch events if anyone is listening
156 if (grunt.event.listeners('watch').length > 0) {
157 grunt.event.emit('watch', status, filepath, target.name);
158 }
159
160 // Group changed files only for display
161 changedFiles[filepath] = status;
162
163 // Add changed files to the target
164 if (taskrun.targets[target.name]) {
165 if (!taskrun.targets[target.name].changedFiles) {
166 taskrun.targets[target.name].changedFiles = Object.create(null);
167 }
168 taskrun.targets[target.name].changedFiles[filepath] = status;
169 }
170
171 // Queue the target
172 if (taskrun.queue.indexOf(target.name) === -1) {
173 taskrun.queue.push(target.name);
174 }
175
176 // Run the tasks
177 taskrun.run();
178 });
179
180 // On watcher error
181 this.on('error', function(err) {
182 if (typeof err === 'string') {
183 err = new Error(err);
184 }
185 grunt.log.error(err.message);
186 });
187 }));
188 });
189
190 });
191};