123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- 'use strict';
- var util = require('gulp-util');
- var assign = require('object-assign');
- var path = require('path');
- var PluginError = require('gulp-util').PluginError;
- var chokidar = require('chokidar');
- var Duplex = require('readable-stream').Duplex;
- var vinyl = require('vinyl-file');
- var File = require('vinyl');
- var anymatch = require('anymatch');
- var pathIsAbsolute = require('path-is-absolute');
- var globParent = require('glob-parent');
- var slash = require('slash');
- function normalizeGlobs(globs) {
- if (!globs) {
- throw new PluginError('gulp-watch', 'glob argument required');
- }
- if (typeof globs === 'string') {
- globs = [globs];
- }
- if (!Array.isArray(globs)) {
- throw new PluginError('gulp-watch', 'glob should be String or Array, not ' + (typeof globs));
- }
- return globs;
- }
- function watch(globs, opts, cb) {
- var originalGlobs = globs;
- globs = normalizeGlobs(globs);
- if (typeof opts === 'function') {
- cb = opts;
- opts = {};
- }
- opts = assign({}, watch._defaultOptions, opts);
- cb = cb || function () {};
- function resolveFilepath(filepath) {
- if (pathIsAbsolute(filepath)) {
- return path.normalize(filepath);
- }
- return path.resolve(opts.cwd || process.cwd(), filepath);
- }
- function resolveGlob(glob) {
- var mod = '';
- if (glob[0] === '!') {
- mod = glob[0];
- glob = glob.slice(1);
- }
- return mod + slash(resolveFilepath(glob));
- }
- globs = globs.map(resolveGlob);
- var baseForced = Boolean(opts.base);
- var outputStream = new Duplex({objectMode: true, allowHalfOpen: true});
- outputStream._write = function _write(file, enc, done) {
- cb(file);
- this.push(file);
- done();
- };
- outputStream._read = function _read() { };
- var watcher = chokidar.watch(globs, opts);
- opts.events.forEach(function (ev) {
- watcher.on(ev, processEvent.bind(undefined, ev));
- });
- ['add', 'change', 'unlink', 'addDir', 'unlinkDir', 'error', 'ready', 'raw']
- .forEach(function (ev) {
- watcher.on(ev, outputStream.emit.bind(outputStream, ev));
- });
- outputStream.add = function add(newGlobs) {
- newGlobs = normalizeGlobs(newGlobs)
- .map(resolveGlob);
- watcher.add(newGlobs);
- globs.push.apply(globs, newGlobs);
- };
- outputStream.unwatch = watcher.unwatch.bind(watcher);
- outputStream.close = function () {
- watcher.close();
- this.emit('end');
- };
- function processEvent(event, filepath) {
- filepath = resolveFilepath(filepath);
- var fileOpts = assign({}, opts);
- var glob;
- var currentFilepath = filepath;
- while (!(glob = globs[anymatch(globs, currentFilepath, true)]) && currentFilepath !== (currentFilepath = path.dirname(currentFilepath))) {} // eslint-disable-line no-empty-blocks/no-empty-blocks
- if (!glob) {
- util.log(
- util.colors.cyan('[gulp-watch]'),
- util.colors.yellow('Watched unexpected path. This is likely a bug. Please open this link to report the issue:\n') +
- 'https://github.com/floatdrop/gulp-watch/issues/new?title=' +
- encodeURIComponent('Watched unexpected filepath') + '&body=' +
- encodeURIComponent('Node.js version: `' + process.version + ' ' + process.platform + ' ' + process.arch + '`\ngulp-watch version: `' + require('./package.json').version + '`\nGlobs: `' + JSON.stringify(originalGlobs) + '`\nFilepath: `' + filepath + '`\nEvent: `' + event + '`\nProcess CWD: `' + process.cwd() + '`\nOptions:\n```js\n' + JSON.stringify(opts, null, 2) + '\n```')
- );
- return;
- }
- if (!baseForced) {
- fileOpts.base = path.normalize(globParent(glob));
- }
- // Do not stat deleted files
- if (event === 'unlink' || event === 'unlinkDir') {
- fileOpts.path = filepath;
- write(event, null, new File(fileOpts));
- return;
- }
- // Workaround for early read
- setTimeout(function () {
- vinyl.read(filepath, fileOpts).then(function (file) {
- write(event, null, file);
- });
- }, opts.readDelay);
- }
- function write(event, err, file) {
- if (err) {
- outputStream.emit('error', err);
- return;
- }
- if (opts.verbose) {
- log(event, file);
- }
- file.event = event;
- outputStream.push(file);
- cb(file);
- }
- function log(event, file) {
- event = event[event.length - 1] === 'e' ? event + 'd' : event + 'ed';
- var msg = [util.colors.magenta(file.relative), 'was', event];
- if (opts.name) {
- msg.unshift(util.colors.cyan(opts.name) + ' saw');
- }
- util.log.apply(util, msg);
- }
- return outputStream;
- }
- // This is not part of the public API as that would lead to global state (singleton) pollution,
- // and allow unexpected interference between unrelated modules that make use of gulp-watch.
- // This can be useful for unit tests and root application configuration, though.
- // Avoid modifying gulp-watch's default options inside a library/reusable package, please.
- watch._defaultOptions = {
- events: ['add', 'change', 'unlink'],
- ignoreInitial: true,
- readDelay: 10
- };
- module.exports = watch;
|