size-plugin
Version:
Tracks your Webpack asset sizes over time.
1,708 lines (1,464 loc) • 87.7 kB
JavaScript
var path = require('path');
var promisify = require('util.promisify');
var globPromise = require('glob');
var minimatch = require('minimatch');
var gzipSize = require('gzip-size');
var chalk = require('chalk');
var prettyBytes = require('pretty-bytes');
var escapeRegExp = require('escape-string-regexp');
var ciEnv = require('ci-env');
var axios = require('axios');
var fs = require('fs');
var constants = require('constants');
var require$$0 = require('stream');
var util = require('util');
var assert = require('assert');
var os = require('os');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var promisify__default = /*#__PURE__*/_interopDefaultLegacy(promisify);
var globPromise__default = /*#__PURE__*/_interopDefaultLegacy(globPromise);
var minimatch__default = /*#__PURE__*/_interopDefaultLegacy(minimatch);
var gzipSize__default = /*#__PURE__*/_interopDefaultLegacy(gzipSize);
var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
var prettyBytes__default = /*#__PURE__*/_interopDefaultLegacy(prettyBytes);
var escapeRegExp__default = /*#__PURE__*/_interopDefaultLegacy(escapeRegExp);
var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
var constants__default = /*#__PURE__*/_interopDefaultLegacy(constants);
var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0);
var util__default = /*#__PURE__*/_interopDefaultLegacy(util);
var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert);
var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
/**
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
function toMap(names, values) {
return names.reduce((map, name, i) => {
map[name] = values[i];
return map;
}, {});
}
function dedupe(item, index, arr) {
return arr.indexOf(item) === index;
}
function toFileMap(files) {
return files.reduce((result, file) => {
if (file.size) {
// excluding files with size 0
result[file.filename] = file.size;
}
return result;
}, {});
}
const SIZE_STORE_ENDPOINT = process.env.SIZE_STORE_ENDPOINT || 'https://size-plugin-store.now.sh'; // TODO: add option to turn off publishing of sizes.
async function publishDiff(diff, filename) {
if (process.env.NODE_ENV !== 'test' && ciEnv.ci && ciEnv.event == 'pull_request') {
try {
const params = {
ci: ciEnv.ci,
repo: ciEnv.repo,
branch: ciEnv.branch,
sha: ciEnv.sha,
pull_request_number: ciEnv.pull_request_number,
diff,
filename
};
await axios__default['default'].post(`${SIZE_STORE_ENDPOINT}/diff`, params);
} catch (error) {
console.error('error: while publishing diff', error);
}
}
}
async function publishSizes(size, filename) {
// TODO: read allowed branch from configuration
if (process.env.NODE_ENV !== 'test' && ciEnv.ci && ciEnv.event == 'push' && ciEnv.branch === 'master') {
try {
const params = {
ci: ciEnv.ci,
repo: ciEnv.repo,
branch: ciEnv.branch,
sha: ciEnv.sha,
pull_request_number: ciEnv.pull_request_number,
size,
filename
};
await axios__default['default'].post(`${SIZE_STORE_ENDPOINT}/size`, params);
} catch (error) {
console.error('error: while publishing sizes', error);
}
}
}
function createCommonjsModule(fn) {
var module = { exports: {} };
return fn(module, module.exports), module.exports;
}
var fromCallback = function (fn) {
return Object.defineProperty(function () {
if (typeof arguments[arguments.length - 1] === 'function') fn.apply(this, arguments);
else {
return new Promise((resolve, reject) => {
arguments[arguments.length] = (err, res) => {
if (err) return reject(err)
resolve(res);
};
arguments.length++;
fn.apply(this, arguments);
})
}
}, 'name', { value: fn.name })
};
var fromPromise = function (fn) {
return Object.defineProperty(function () {
const cb = arguments[arguments.length - 1];
if (typeof cb !== 'function') return fn.apply(this, arguments)
else fn.apply(this, arguments).then(r => cb(null, r), cb);
}, 'name', { value: fn.name })
};
var universalify = {
fromCallback: fromCallback,
fromPromise: fromPromise
};
var fs_1 = clone(fs__default['default']);
function clone (obj) {
if (obj === null || typeof obj !== 'object')
return obj
if (obj instanceof Object)
var copy = { __proto__: obj.__proto__ };
else
var copy = Object.create(null);
Object.getOwnPropertyNames(obj).forEach(function (key) {
Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key));
});
return copy
}
var origCwd = process.cwd;
var cwd = null;
var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform;
process.cwd = function() {
if (!cwd)
cwd = origCwd.call(process);
return cwd
};
try {
process.cwd();
} catch (er) {}
var chdir = process.chdir;
process.chdir = function(d) {
cwd = null;
chdir.call(process, d);
};
var polyfills = patch;
function patch (fs) {
// (re-)implement some things that are known busted or missing.
// lchmod, broken prior to 0.6.2
// back-port the fix here.
if (constants__default['default'].hasOwnProperty('O_SYMLINK') &&
process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
patchLchmod(fs);
}
// lutimes implementation, or no-op
if (!fs.lutimes) {
patchLutimes(fs);
}
// https://github.com/isaacs/node-graceful-fs/issues/4
// Chown should not fail on einval or eperm if non-root.
// It should not fail on enosys ever, as this just indicates
// that a fs doesn't support the intended operation.
fs.chown = chownFix(fs.chown);
fs.fchown = chownFix(fs.fchown);
fs.lchown = chownFix(fs.lchown);
fs.chmod = chmodFix(fs.chmod);
fs.fchmod = chmodFix(fs.fchmod);
fs.lchmod = chmodFix(fs.lchmod);
fs.chownSync = chownFixSync(fs.chownSync);
fs.fchownSync = chownFixSync(fs.fchownSync);
fs.lchownSync = chownFixSync(fs.lchownSync);
fs.chmodSync = chmodFixSync(fs.chmodSync);
fs.fchmodSync = chmodFixSync(fs.fchmodSync);
fs.lchmodSync = chmodFixSync(fs.lchmodSync);
fs.stat = statFix(fs.stat);
fs.fstat = statFix(fs.fstat);
fs.lstat = statFix(fs.lstat);
fs.statSync = statFixSync(fs.statSync);
fs.fstatSync = statFixSync(fs.fstatSync);
fs.lstatSync = statFixSync(fs.lstatSync);
// if lchmod/lchown do not exist, then make them no-ops
if (!fs.lchmod) {
fs.lchmod = function (path, mode, cb) {
if (cb) process.nextTick(cb);
};
fs.lchmodSync = function () {};
}
if (!fs.lchown) {
fs.lchown = function (path, uid, gid, cb) {
if (cb) process.nextTick(cb);
};
fs.lchownSync = function () {};
}
// on Windows, A/V software can lock the directory, causing this
// to fail with an EACCES or EPERM if the directory contains newly
// created files. Try again on failure, for up to 60 seconds.
// Set the timeout this long because some Windows Anti-Virus, such as Parity
// bit9, may lock files for up to a minute, causing npm package install
// failures. Also, take care to yield the scheduler. Windows scheduling gives
// CPU to a busy looping process, which can cause the program causing the lock
// contention to be starved of CPU by node, so the contention doesn't resolve.
if (platform === "win32") {
fs.rename = (function (fs$rename) { return function (from, to, cb) {
var start = Date.now();
var backoff = 0;
fs$rename(from, to, function CB (er) {
if (er
&& (er.code === "EACCES" || er.code === "EPERM")
&& Date.now() - start < 60000) {
setTimeout(function() {
fs.stat(to, function (stater, st) {
if (stater && stater.code === "ENOENT")
fs$rename(from, to, CB);
else
cb(er);
});
}, backoff);
if (backoff < 100)
backoff += 10;
return;
}
if (cb) cb(er);
});
}})(fs.rename);
}
// if read() returns EAGAIN, then just try it again.
fs.read = (function (fs$read) { return function (fd, buffer, offset, length, position, callback_) {
var callback;
if (callback_ && typeof callback_ === 'function') {
var eagCounter = 0;
callback = function (er, _, __) {
if (er && er.code === 'EAGAIN' && eagCounter < 10) {
eagCounter ++;
return fs$read.call(fs, fd, buffer, offset, length, position, callback)
}
callback_.apply(this, arguments);
};
}
return fs$read.call(fs, fd, buffer, offset, length, position, callback)
}})(fs.read);
fs.readSync = (function (fs$readSync) { return function (fd, buffer, offset, length, position) {
var eagCounter = 0;
while (true) {
try {
return fs$readSync.call(fs, fd, buffer, offset, length, position)
} catch (er) {
if (er.code === 'EAGAIN' && eagCounter < 10) {
eagCounter ++;
continue
}
throw er
}
}
}})(fs.readSync);
}
function patchLchmod (fs) {
fs.lchmod = function (path, mode, callback) {
fs.open( path
, constants__default['default'].O_WRONLY | constants__default['default'].O_SYMLINK
, mode
, function (err, fd) {
if (err) {
if (callback) callback(err);
return
}
// prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
fs.fchmod(fd, mode, function (err) {
fs.close(fd, function(err2) {
if (callback) callback(err || err2);
});
});
});
};
fs.lchmodSync = function (path, mode) {
var fd = fs.openSync(path, constants__default['default'].O_WRONLY | constants__default['default'].O_SYMLINK, mode);
// prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
var threw = true;
var ret;
try {
ret = fs.fchmodSync(fd, mode);
threw = false;
} finally {
if (threw) {
try {
fs.closeSync(fd);
} catch (er) {}
} else {
fs.closeSync(fd);
}
}
return ret
};
}
function patchLutimes (fs) {
if (constants__default['default'].hasOwnProperty("O_SYMLINK")) {
fs.lutimes = function (path, at, mt, cb) {
fs.open(path, constants__default['default'].O_SYMLINK, function (er, fd) {
if (er) {
if (cb) cb(er);
return
}
fs.futimes(fd, at, mt, function (er) {
fs.close(fd, function (er2) {
if (cb) cb(er || er2);
});
});
});
};
fs.lutimesSync = function (path, at, mt) {
var fd = fs.openSync(path, constants__default['default'].O_SYMLINK);
var ret;
var threw = true;
try {
ret = fs.futimesSync(fd, at, mt);
threw = false;
} finally {
if (threw) {
try {
fs.closeSync(fd);
} catch (er) {}
} else {
fs.closeSync(fd);
}
}
return ret
};
} else {
fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb); };
fs.lutimesSync = function () {};
}
}
function chmodFix (orig) {
if (!orig) return orig
return function (target, mode, cb) {
return orig.call(fs_1, target, mode, function (er) {
if (chownErOk(er)) er = null;
if (cb) cb.apply(this, arguments);
})
}
}
function chmodFixSync (orig) {
if (!orig) return orig
return function (target, mode) {
try {
return orig.call(fs_1, target, mode)
} catch (er) {
if (!chownErOk(er)) throw er
}
}
}
function chownFix (orig) {
if (!orig) return orig
return function (target, uid, gid, cb) {
return orig.call(fs_1, target, uid, gid, function (er) {
if (chownErOk(er)) er = null;
if (cb) cb.apply(this, arguments);
})
}
}
function chownFixSync (orig) {
if (!orig) return orig
return function (target, uid, gid) {
try {
return orig.call(fs_1, target, uid, gid)
} catch (er) {
if (!chownErOk(er)) throw er
}
}
}
function statFix (orig) {
if (!orig) return orig
// Older versions of Node erroneously returned signed integers for
// uid + gid.
return function (target, cb) {
return orig.call(fs_1, target, function (er, stats) {
if (!stats) return cb.apply(this, arguments)
if (stats.uid < 0) stats.uid += 0x100000000;
if (stats.gid < 0) stats.gid += 0x100000000;
if (cb) cb.apply(this, arguments);
})
}
}
function statFixSync (orig) {
if (!orig) return orig
// Older versions of Node erroneously returned signed integers for
// uid + gid.
return function (target) {
var stats = orig.call(fs_1, target);
if (stats.uid < 0) stats.uid += 0x100000000;
if (stats.gid < 0) stats.gid += 0x100000000;
return stats;
}
}
// ENOSYS means that the fs doesn't support the op. Just ignore
// that, because it doesn't matter.
//
// if there's no getuid, or if getuid() is something other
// than 0, and the error is EINVAL or EPERM, then just ignore
// it.
//
// This specific case is a silent failure in cp, install, tar,
// and most other unix tools that manage permissions.
//
// When running as root, or if other types of errors are
// encountered, then it's strict.
function chownErOk (er) {
if (!er)
return true
if (er.code === "ENOSYS")
return true
var nonroot = !process.getuid || process.getuid() !== 0;
if (nonroot) {
if (er.code === "EINVAL" || er.code === "EPERM")
return true
}
return false
}
var Stream = require$$0__default['default'].Stream;
var legacyStreams = legacy;
function legacy (fs) {
return {
ReadStream: ReadStream,
WriteStream: WriteStream
}
function ReadStream (path, options) {
if (!(this instanceof ReadStream)) return new ReadStream(path, options);
Stream.call(this);
var self = this;
this.path = path;
this.fd = null;
this.readable = true;
this.paused = false;
this.flags = 'r';
this.mode = 438; /*=0666*/
this.bufferSize = 64 * 1024;
options = options || {};
// Mixin options into this
var keys = Object.keys(options);
for (var index = 0, length = keys.length; index < length; index++) {
var key = keys[index];
this[key] = options[key];
}
if (this.encoding) this.setEncoding(this.encoding);
if (this.start !== undefined) {
if ('number' !== typeof this.start) {
throw TypeError('start must be a Number');
}
if (this.end === undefined) {
this.end = Infinity;
} else if ('number' !== typeof this.end) {
throw TypeError('end must be a Number');
}
if (this.start > this.end) {
throw new Error('start must be <= end');
}
this.pos = this.start;
}
if (this.fd !== null) {
process.nextTick(function() {
self._read();
});
return;
}
fs.open(this.path, this.flags, this.mode, function (err, fd) {
if (err) {
self.emit('error', err);
self.readable = false;
return;
}
self.fd = fd;
self.emit('open', fd);
self._read();
});
}
function WriteStream (path, options) {
if (!(this instanceof WriteStream)) return new WriteStream(path, options);
Stream.call(this);
this.path = path;
this.fd = null;
this.writable = true;
this.flags = 'w';
this.encoding = 'binary';
this.mode = 438; /*=0666*/
this.bytesWritten = 0;
options = options || {};
// Mixin options into this
var keys = Object.keys(options);
for (var index = 0, length = keys.length; index < length; index++) {
var key = keys[index];
this[key] = options[key];
}
if (this.start !== undefined) {
if ('number' !== typeof this.start) {
throw TypeError('start must be a Number');
}
if (this.start < 0) {
throw new Error('start must be >= zero');
}
this.pos = this.start;
}
this.busy = false;
this._queue = [];
if (this.fd === null) {
this._open = fs.open;
this._queue.push([this._open, this.path, this.flags, this.mode, undefined]);
this.flush();
}
}
}
var gracefulFs = createCommonjsModule(function (module) {
var queue = [];
function noop () {}
var debug = noop;
if (util__default['default'].debuglog)
debug = util__default['default'].debuglog('gfs4');
else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ''))
debug = function() {
var m = util__default['default'].format.apply(util__default['default'], arguments);
m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ');
console.error(m);
};
if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) {
process.on('exit', function() {
debug(queue);
assert__default['default'].equal(queue.length, 0);
});
}
module.exports = patch(fs_1);
if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH) {
module.exports = patch(fs__default['default']);
}
// Always patch fs.close/closeSync, because we want to
// retry() whenever a close happens *anywhere* in the program.
// This is essential when multiple graceful-fs instances are
// in play at the same time.
module.exports.close =
fs__default['default'].close = (function (fs$close) { return function (fd, cb) {
return fs$close.call(fs__default['default'], fd, function (err) {
if (!err)
retry();
if (typeof cb === 'function')
cb.apply(this, arguments);
})
}})(fs__default['default'].close);
module.exports.closeSync =
fs__default['default'].closeSync = (function (fs$closeSync) { return function (fd) {
// Note that graceful-fs also retries when fs.closeSync() fails.
// Looks like a bug to me, although it's probably a harmless one.
var rval = fs$closeSync.apply(fs__default['default'], arguments);
retry();
return rval
}})(fs__default['default'].closeSync);
function patch (fs) {
// Everything that references the open() function needs to be in here
polyfills(fs);
fs.gracefulify = patch;
fs.FileReadStream = ReadStream; // Legacy name.
fs.FileWriteStream = WriteStream; // Legacy name.
fs.createReadStream = createReadStream;
fs.createWriteStream = createWriteStream;
var fs$readFile = fs.readFile;
fs.readFile = readFile;
function readFile (path, options, cb) {
if (typeof options === 'function')
cb = options, options = null;
return go$readFile(path, options, cb)
function go$readFile (path, options, cb) {
return fs$readFile(path, options, function (err) {
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
enqueue([go$readFile, [path, options, cb]]);
else {
if (typeof cb === 'function')
cb.apply(this, arguments);
retry();
}
})
}
}
var fs$writeFile = fs.writeFile;
fs.writeFile = writeFile;
function writeFile (path, data, options, cb) {
if (typeof options === 'function')
cb = options, options = null;
return go$writeFile(path, data, options, cb)
function go$writeFile (path, data, options, cb) {
return fs$writeFile(path, data, options, function (err) {
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
enqueue([go$writeFile, [path, data, options, cb]]);
else {
if (typeof cb === 'function')
cb.apply(this, arguments);
retry();
}
})
}
}
var fs$appendFile = fs.appendFile;
if (fs$appendFile)
fs.appendFile = appendFile;
function appendFile (path, data, options, cb) {
if (typeof options === 'function')
cb = options, options = null;
return go$appendFile(path, data, options, cb)
function go$appendFile (path, data, options, cb) {
return fs$appendFile(path, data, options, function (err) {
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
enqueue([go$appendFile, [path, data, options, cb]]);
else {
if (typeof cb === 'function')
cb.apply(this, arguments);
retry();
}
})
}
}
var fs$readdir = fs.readdir;
fs.readdir = readdir;
function readdir (path, options, cb) {
var args = [path];
if (typeof options !== 'function') {
args.push(options);
} else {
cb = options;
}
args.push(go$readdir$cb);
return go$readdir(args)
function go$readdir$cb (err, files) {
if (files && files.sort)
files.sort();
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
enqueue([go$readdir, [args]]);
else {
if (typeof cb === 'function')
cb.apply(this, arguments);
retry();
}
}
}
function go$readdir (args) {
return fs$readdir.apply(fs, args)
}
if (process.version.substr(0, 4) === 'v0.8') {
var legStreams = legacyStreams(fs);
ReadStream = legStreams.ReadStream;
WriteStream = legStreams.WriteStream;
}
var fs$ReadStream = fs.ReadStream;
ReadStream.prototype = Object.create(fs$ReadStream.prototype);
ReadStream.prototype.open = ReadStream$open;
var fs$WriteStream = fs.WriteStream;
WriteStream.prototype = Object.create(fs$WriteStream.prototype);
WriteStream.prototype.open = WriteStream$open;
fs.ReadStream = ReadStream;
fs.WriteStream = WriteStream;
function ReadStream (path, options) {
if (this instanceof ReadStream)
return fs$ReadStream.apply(this, arguments), this
else
return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
}
function ReadStream$open () {
var that = this;
open(that.path, that.flags, that.mode, function (err, fd) {
if (err) {
if (that.autoClose)
that.destroy();
that.emit('error', err);
} else {
that.fd = fd;
that.emit('open', fd);
that.read();
}
});
}
function WriteStream (path, options) {
if (this instanceof WriteStream)
return fs$WriteStream.apply(this, arguments), this
else
return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
}
function WriteStream$open () {
var that = this;
open(that.path, that.flags, that.mode, function (err, fd) {
if (err) {
that.destroy();
that.emit('error', err);
} else {
that.fd = fd;
that.emit('open', fd);
}
});
}
function createReadStream (path, options) {
return new ReadStream(path, options)
}
function createWriteStream (path, options) {
return new WriteStream(path, options)
}
var fs$open = fs.open;
fs.open = open;
function open (path, flags, mode, cb) {
if (typeof mode === 'function')
cb = mode, mode = null;
return go$open(path, flags, mode, cb)
function go$open (path, flags, mode, cb) {
return fs$open(path, flags, mode, function (err, fd) {
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
enqueue([go$open, [path, flags, mode, cb]]);
else {
if (typeof cb === 'function')
cb.apply(this, arguments);
retry();
}
})
}
}
return fs
}
function enqueue (elem) {
debug('ENQUEUE', elem[0].name, elem[1]);
queue.push(elem);
}
function retry () {
var elem = queue.shift();
if (elem) {
debug('RETRY', elem[0].name, elem[1]);
elem[0].apply(null, elem[1]);
}
}
});
var fs_1$1 = createCommonjsModule(function (module, exports) {
// This is adapted from https://github.com/normalize/mz
// Copyright (c) 2014-2016 Jonathan Ong me@jongleberry.com and Contributors
const u = universalify.fromCallback;
const api = [
'access',
'appendFile',
'chmod',
'chown',
'close',
'copyFile',
'fchmod',
'fchown',
'fdatasync',
'fstat',
'fsync',
'ftruncate',
'futimes',
'lchown',
'lchmod',
'link',
'lstat',
'mkdir',
'mkdtemp',
'open',
'readFile',
'readdir',
'readlink',
'realpath',
'rename',
'rmdir',
'stat',
'symlink',
'truncate',
'unlink',
'utimes',
'writeFile'
].filter(key => {
// Some commands are not available on some systems. Ex:
// fs.copyFile was added in Node.js v8.5.0
// fs.mkdtemp was added in Node.js v5.10.0
// fs.lchown is not available on at least some Linux
return typeof gracefulFs[key] === 'function'
});
// Export all keys:
Object.keys(gracefulFs).forEach(key => {
if (key === 'promises') {
// fs.promises is a getter property that triggers ExperimentalWarning
// Don't re-export it here, the getter is defined in "lib/index.js"
return
}
exports[key] = gracefulFs[key];
});
// Universalify async methods:
api.forEach(method => {
exports[method] = u(gracefulFs[method]);
});
// We differ from mz/fs in that we still ship the old, broken, fs.exists()
// since we are a drop-in replacement for the native module
exports.exists = function (filename, callback) {
if (typeof callback === 'function') {
return gracefulFs.exists(filename, callback)
}
return new Promise(resolve => {
return gracefulFs.exists(filename, resolve)
})
};
// fs.read() & fs.write need special treatment due to multiple callback args
exports.read = function (fd, buffer, offset, length, position, callback) {
if (typeof callback === 'function') {
return gracefulFs.read(fd, buffer, offset, length, position, callback)
}
return new Promise((resolve, reject) => {
gracefulFs.read(fd, buffer, offset, length, position, (err, bytesRead, buffer) => {
if (err) return reject(err)
resolve({ bytesRead, buffer });
});
})
};
// Function signature can be
// fs.write(fd, buffer[, offset[, length[, position]]], callback)
// OR
// fs.write(fd, string[, position[, encoding]], callback)
// We need to handle both cases, so we use ...args
exports.write = function (fd, buffer, ...args) {
if (typeof args[args.length - 1] === 'function') {
return gracefulFs.write(fd, buffer, ...args)
}
return new Promise((resolve, reject) => {
gracefulFs.write(fd, buffer, ...args, (err, bytesWritten, buffer) => {
if (err) return reject(err)
resolve({ bytesWritten, buffer });
});
})
};
});
// get drive on windows
function getRootPath (p) {
p = path__default['default'].normalize(path__default['default'].resolve(p)).split(path__default['default'].sep);
if (p.length > 0) return p[0]
return null
}
// http://stackoverflow.com/a/62888/10333 contains more accurate
// TODO: expand to include the rest
const INVALID_PATH_CHARS = /[<>:"|?*]/;
function invalidWin32Path (p) {
const rp = getRootPath(p);
p = p.replace(rp, '');
return INVALID_PATH_CHARS.test(p)
}
var win32 = {
getRootPath,
invalidWin32Path
};
const invalidWin32Path$1 = win32.invalidWin32Path;
const o777 = parseInt('0777', 8);
function mkdirs (p, opts, callback, made) {
if (typeof opts === 'function') {
callback = opts;
opts = {};
} else if (!opts || typeof opts !== 'object') {
opts = { mode: opts };
}
if (process.platform === 'win32' && invalidWin32Path$1(p)) {
const errInval = new Error(p + ' contains invalid WIN32 path characters.');
errInval.code = 'EINVAL';
return callback(errInval)
}
let mode = opts.mode;
const xfs = opts.fs || gracefulFs;
if (mode === undefined) {
mode = o777 & (~process.umask());
}
if (!made) made = null;
callback = callback || function () {};
p = path__default['default'].resolve(p);
xfs.mkdir(p, mode, er => {
if (!er) {
made = made || p;
return callback(null, made)
}
switch (er.code) {
case 'ENOENT':
if (path__default['default'].dirname(p) === p) return callback(er)
mkdirs(path__default['default'].dirname(p), opts, (er, made) => {
if (er) callback(er, made);
else mkdirs(p, opts, callback, made);
});
break
// In the case of any other error, just see if there's a dir
// there already. If so, then hooray! If not, then something
// is borked.
default:
xfs.stat(p, (er2, stat) => {
// if the stat fails, then that's super weird.
// let the original error be the failure reason.
if (er2 || !stat.isDirectory()) callback(er, made);
else callback(null, made);
});
break
}
});
}
var mkdirs_1 = mkdirs;
const invalidWin32Path$2 = win32.invalidWin32Path;
const o777$1 = parseInt('0777', 8);
function mkdirsSync (p, opts, made) {
if (!opts || typeof opts !== 'object') {
opts = { mode: opts };
}
let mode = opts.mode;
const xfs = opts.fs || gracefulFs;
if (process.platform === 'win32' && invalidWin32Path$2(p)) {
const errInval = new Error(p + ' contains invalid WIN32 path characters.');
errInval.code = 'EINVAL';
throw errInval
}
if (mode === undefined) {
mode = o777$1 & (~process.umask());
}
if (!made) made = null;
p = path__default['default'].resolve(p);
try {
xfs.mkdirSync(p, mode);
made = made || p;
} catch (err0) {
if (err0.code === 'ENOENT') {
if (path__default['default'].dirname(p) === p) throw err0
made = mkdirsSync(path__default['default'].dirname(p), opts, made);
mkdirsSync(p, opts, made);
} else {
// In the case of any other error, just see if there's a dir there
// already. If so, then hooray! If not, then something is borked.
let stat;
try {
stat = xfs.statSync(p);
} catch (err1) {
throw err0
}
if (!stat.isDirectory()) throw err0
}
}
return made
}
var mkdirsSync_1 = mkdirsSync;
const u = universalify.fromCallback;
const mkdirs$1 = u(mkdirs_1);
var mkdirs_1$1 = {
mkdirs: mkdirs$1,
mkdirsSync: mkdirsSync_1,
// alias
mkdirp: mkdirs$1,
mkdirpSync: mkdirsSync_1,
ensureDir: mkdirs$1,
ensureDirSync: mkdirsSync_1
};
// HFS, ext{2,3}, FAT do not, Node.js v0.10 does not
function hasMillisResSync () {
let tmpfile = path__default['default'].join('millis-test-sync' + Date.now().toString() + Math.random().toString().slice(2));
tmpfile = path__default['default'].join(os__default['default'].tmpdir(), tmpfile);
// 550 millis past UNIX epoch
const d = new Date(1435410243862);
gracefulFs.writeFileSync(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141');
const fd = gracefulFs.openSync(tmpfile, 'r+');
gracefulFs.futimesSync(fd, d, d);
gracefulFs.closeSync(fd);
return gracefulFs.statSync(tmpfile).mtime > 1435410243000
}
function hasMillisRes (callback) {
let tmpfile = path__default['default'].join('millis-test' + Date.now().toString() + Math.random().toString().slice(2));
tmpfile = path__default['default'].join(os__default['default'].tmpdir(), tmpfile);
// 550 millis past UNIX epoch
const d = new Date(1435410243862);
gracefulFs.writeFile(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141', err => {
if (err) return callback(err)
gracefulFs.open(tmpfile, 'r+', (err, fd) => {
if (err) return callback(err)
gracefulFs.futimes(fd, d, d, err => {
if (err) return callback(err)
gracefulFs.close(fd, err => {
if (err) return callback(err)
gracefulFs.stat(tmpfile, (err, stats) => {
if (err) return callback(err)
callback(null, stats.mtime > 1435410243000);
});
});
});
});
});
}
function timeRemoveMillis (timestamp) {
if (typeof timestamp === 'number') {
return Math.floor(timestamp / 1000) * 1000
} else if (timestamp instanceof Date) {
return new Date(Math.floor(timestamp.getTime() / 1000) * 1000)
} else {
throw new Error('fs-extra: timeRemoveMillis() unknown parameter type')
}
}
function utimesMillis (path, atime, mtime, callback) {
// if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback)
gracefulFs.open(path, 'r+', (err, fd) => {
if (err) return callback(err)
gracefulFs.futimes(fd, atime, mtime, futimesErr => {
gracefulFs.close(fd, closeErr => {
if (callback) callback(futimesErr || closeErr);
});
});
});
}
function utimesMillisSync (path, atime, mtime) {
const fd = gracefulFs.openSync(path, 'r+');
gracefulFs.futimesSync(fd, atime, mtime);
return gracefulFs.closeSync(fd)
}
var utimes = {
hasMillisRes,
hasMillisResSync,
timeRemoveMillis,
utimesMillis,
utimesMillisSync
};
/* eslint-disable node/no-deprecated-api */
var buffer = function (size) {
if (typeof Buffer.allocUnsafe === 'function') {
try {
return Buffer.allocUnsafe(size)
} catch (e) {
return new Buffer(size)
}
}
return new Buffer(size)
};
const mkdirpSync = mkdirs_1$1.mkdirsSync;
const utimesSync = utimes.utimesMillisSync;
const notExist = Symbol('notExist');
function copySync (src, dest, opts) {
if (typeof opts === 'function') {
opts = {filter: opts};
}
opts = opts || {};
opts.clobber = 'clobber' in opts ? !!opts.clobber : true; // default to true for now
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber; // overwrite falls back to clobber
// Warn about using preserveTimestamps on 32-bit node
if (opts.preserveTimestamps && process.arch === 'ia32') {
console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
see https://github.com/jprichardson/node-fs-extra/issues/269`);
}
const destStat = checkPaths(src, dest);
if (opts.filter && !opts.filter(src, dest)) return
const destParent = path__default['default'].dirname(dest);
if (!gracefulFs.existsSync(destParent)) mkdirpSync(destParent);
return startCopy(destStat, src, dest, opts)
}
function startCopy (destStat, src, dest, opts) {
if (opts.filter && !opts.filter(src, dest)) return
return getStats(destStat, src, dest, opts)
}
function getStats (destStat, src, dest, opts) {
const statSync = opts.dereference ? gracefulFs.statSync : gracefulFs.lstatSync;
const srcStat = statSync(src);
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts)
}
function onFile (srcStat, destStat, src, dest, opts) {
if (destStat === notExist) return copyFile(srcStat, src, dest, opts)
return mayCopyFile(srcStat, src, dest, opts)
}
function mayCopyFile (srcStat, src, dest, opts) {
if (opts.overwrite) {
gracefulFs.unlinkSync(dest);
return copyFile(srcStat, src, dest, opts)
} else if (opts.errorOnExist) {
throw new Error(`'${dest}' already exists`)
}
}
function copyFile (srcStat, src, dest, opts) {
if (typeof gracefulFs.copyFileSync === 'function') {
gracefulFs.copyFileSync(src, dest);
gracefulFs.chmodSync(dest, srcStat.mode);
if (opts.preserveTimestamps) {
return utimesSync(dest, srcStat.atime, srcStat.mtime)
}
return
}
return copyFileFallback(srcStat, src, dest, opts)
}
function copyFileFallback (srcStat, src, dest, opts) {
const BUF_LENGTH = 64 * 1024;
const _buff = buffer(BUF_LENGTH);
const fdr = gracefulFs.openSync(src, 'r');
const fdw = gracefulFs.openSync(dest, 'w', srcStat.mode);
let pos = 0;
while (pos < srcStat.size) {
const bytesRead = gracefulFs.readSync(fdr, _buff, 0, BUF_LENGTH, pos);
gracefulFs.writeSync(fdw, _buff, 0, bytesRead);
pos += bytesRead;
}
if (opts.preserveTimestamps) gracefulFs.futimesSync(fdw, srcStat.atime, srcStat.mtime);
gracefulFs.closeSync(fdr);
gracefulFs.closeSync(fdw);
}
function onDir (srcStat, destStat, src, dest, opts) {
if (destStat === notExist) return mkDirAndCopy(srcStat, src, dest, opts)
if (destStat && !destStat.isDirectory()) {
throw new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`)
}
return copyDir(src, dest, opts)
}
function mkDirAndCopy (srcStat, src, dest, opts) {
gracefulFs.mkdirSync(dest);
copyDir(src, dest, opts);
return gracefulFs.chmodSync(dest, srcStat.mode)
}
function copyDir (src, dest, opts) {
gracefulFs.readdirSync(src).forEach(item => copyDirItem(item, src, dest, opts));
}
function copyDirItem (item, src, dest, opts) {
const srcItem = path__default['default'].join(src, item);
const destItem = path__default['default'].join(dest, item);
const destStat = checkPaths(srcItem, destItem);
return startCopy(destStat, srcItem, destItem, opts)
}
function onLink (destStat, src, dest, opts) {
let resolvedSrc = gracefulFs.readlinkSync(src);
if (opts.dereference) {
resolvedSrc = path__default['default'].resolve(process.cwd(), resolvedSrc);
}
if (destStat === notExist) {
return gracefulFs.symlinkSync(resolvedSrc, dest)
} else {
let resolvedDest;
try {
resolvedDest = gracefulFs.readlinkSync(dest);
} catch (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return gracefulFs.symlinkSync(resolvedSrc, dest)
throw err
}
if (opts.dereference) {
resolvedDest = path__default['default'].resolve(process.cwd(), resolvedDest);
}
if (isSrcSubdir(resolvedSrc, resolvedDest)) {
throw new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`)
}
// prevent copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (gracefulFs.statSync(dest).isDirectory() && isSrcSubdir(resolvedDest, resolvedSrc)) {
throw new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`)
}
return copyLink(resolvedSrc, dest)
}
}
function copyLink (resolvedSrc, dest) {
gracefulFs.unlinkSync(dest);
return gracefulFs.symlinkSync(resolvedSrc, dest)
}
// return true if dest is a subdir of src, otherwise false.
function isSrcSubdir (src, dest) {
const srcArray = path__default['default'].resolve(src).split(path__default['default'].sep);
const destArray = path__default['default'].resolve(dest).split(path__default['default'].sep);
return srcArray.reduce((acc, current, i) => acc && destArray[i] === current, true)
}
function checkStats (src, dest) {
const srcStat = gracefulFs.statSync(src);
let destStat;
try {
destStat = gracefulFs.statSync(dest);
} catch (err) {
if (err.code === 'ENOENT') return {srcStat, destStat: notExist}
throw err
}
return {srcStat, destStat}
}
function checkPaths (src, dest) {
const {srcStat, destStat} = checkStats(src, dest);
if (destStat.ino && destStat.ino === srcStat.ino) {
throw new Error('Source and destination must not be the same.')
}
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) {
throw new Error(`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`)
}
return destStat
}
var copySync_1 = copySync;
var copySync$1 = {
copySync: copySync_1
};
const u$1 = universalify.fromPromise;
function pathExists (path) {
return fs_1$1.access(path).then(() => true).catch(() => false)
}
var pathExists_1 = {
pathExists: u$1(pathExists),
pathExistsSync: fs_1$1.existsSync
};
const mkdirp = mkdirs_1$1.mkdirs;
const pathExists$1 = pathExists_1.pathExists;
const utimes$1 = utimes.utimesMillis;
const notExist$1 = Symbol('notExist');
function copy (src, dest, opts, cb) {
if (typeof opts === 'function' && !cb) {
cb = opts;
opts = {};
} else if (typeof opts === 'function') {
opts = {filter: opts};
}
cb = cb || function () {};
opts = opts || {};
opts.clobber = 'clobber' in opts ? !!opts.clobber : true; // default to true for now
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber; // overwrite falls back to clobber
// Warn about using preserveTimestamps on 32-bit node
if (opts.preserveTimestamps && process.arch === 'ia32') {
console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
see https://github.com/jprichardson/node-fs-extra/issues/269`);
}
checkPaths$1(src, dest, (err, destStat) => {
if (err) return cb(err)
if (opts.filter) return handleFilter(checkParentDir, destStat, src, dest, opts, cb)
return checkParentDir(destStat, src, dest, opts, cb)
});
}
function checkParentDir (destStat, src, dest, opts, cb) {
const destParent = path__default['default'].dirname(dest);
pathExists$1(destParent, (err, dirExists) => {
if (err) return cb(err)
if (dirExists) return startCopy$1(destStat, src, dest, opts, cb)
mkdirp(destParent, err => {
if (err) return cb(err)
return startCopy$1(destStat, src, dest, opts, cb)
});
});
}
function handleFilter (onInclude, destStat, src, dest, opts, cb) {
Promise.resolve(opts.filter(src, dest)).then(include => {
if (include) {
if (destStat) return onInclude(destStat, src, dest, opts, cb)
return onInclude(src, dest, opts, cb)
}
return cb()
}, error => cb(error));
}
function startCopy$1 (destStat, src, dest, opts, cb) {
if (opts.filter) return handleFilter(getStats$1, destStat, src, dest, opts, cb)
return getStats$1(destStat, src, dest, opts, cb)
}
function getStats$1 (destStat, src, dest, opts, cb) {
const stat = opts.dereference ? gracefulFs.stat : gracefulFs.lstat;
stat(src, (err, srcStat) => {
if (err) return cb(err)
if (srcStat.isDirectory()) return onDir$1(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile$1(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isSymbolicLink()) return onLink$1(destStat, src, dest, opts, cb)
});
}
function onFile$1 (srcStat, destStat, src, dest, opts, cb) {
if (destStat === notExist$1) return copyFile$1(srcStat, src, dest, opts, cb)
return mayCopyFile$1(srcStat, src, dest, opts, cb)
}
function mayCopyFile$1 (srcStat, src, dest, opts, cb) {
if (opts.overwrite) {
gracefulFs.unlink(dest, err => {
if (err) return cb(err)
return copyFile$1(srcStat, src, dest, opts, cb)
});
} else if (opts.errorOnExist) {
return cb(new Error(`'${dest}' already exists`))
} else return cb()
}
function copyFile$1 (srcStat, src, dest, opts, cb) {
if (typeof gracefulFs.copyFile === 'function') {
return gracefulFs.copyFile(src, dest, err => {
if (err) return cb(err)
return setDestModeAndTimestamps(srcStat, dest, opts, cb)
})
}
return copyFileFallback$1(srcStat, src, dest, opts, cb)
}
function copyFileFallback$1 (srcStat, src, dest, opts, cb) {
const rs = gracefulFs.createReadStream(src);
rs.on('error', err => cb(err)).once('open', () => {
const ws = gracefulFs.createWriteStream(dest, { mode: srcStat.mode });
ws.on('error', err => cb(err))
.on('open', () => rs.pipe(ws))
.once('close', () => setDestModeAndTimestamps(srcStat, dest, opts, cb));
});
}
function setDestModeAndTimestamps (srcStat, dest, opts, cb) {
gracefulFs.chmod(dest, srcStat.mode, err => {
if (err) return cb(err)
if (opts.preserveTimestamps) {
return utimes$1(dest, srcStat.atime, srcStat.mtime, cb)
}
return cb()
});
}
function onDir$1 (srcStat, destStat, src, dest, opts, cb) {
if (destStat === notExist$1) return mkDirAndCopy$1(srcStat, src, dest, opts, cb)
if (destStat && !destStat.isDirectory()) {
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`))
}
return copyDir$1(src, dest, opts, cb)
}
function mkDirAndCopy$1 (srcStat, src, dest, opts, cb) {
gracefulFs.mkdir(dest, err => {
if (err) return cb(err)
copyDir$1(src, dest, opts, err => {
if (err) return cb(err)
return gracefulFs.chmod(dest, srcStat.mode, cb)
});
});
}
function copyDir$1 (src, dest, opts, cb) {
gracefulFs.readdir(src, (err, items) => {
if (err) return cb(err)
return copyDirItems(items, src, dest, opts, cb)
});
}
function copyDirItems (items, src, dest, opts, cb) {
const item = items.pop();
if (!item) return cb()
return copyDirItem$1(items, item, src, dest, opts, cb)
}
function copyDirItem$1 (items, item, src, dest, opts, cb) {
const srcItem = path__default['default'].join(src, item);
const destItem = path__default['default'].join(dest, item);
checkPaths$1(srcItem, destItem, (err, destStat) => {
if (err) return cb(err)
startCopy$1(destStat, srcItem, destItem, opts, err => {
if (err) return cb(err)
return copyDirItems(items, src, dest, opts, cb)
});
});
}
function onLink$1 (destStat, src, dest, opts, cb) {
gracefulFs.readlink(src, (err, resolvedSrc) => {
if (err) return cb(err)
if (opts.dereference) {
resolvedSrc = path__default['default'].resolve(process.cwd(), resolvedSrc);
}
if (destStat === notExist$1) {
return gracefulFs.symlink(resolvedSrc, dest, cb)
} else {
gracefulFs.readlink(dest, (err, resolvedDest) => {
if (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return gracefulFs.symlink(resolvedSrc, dest, cb)
return cb(err)
}
if (opts.dereference) {
resolvedDest = path__default['default'].resolve(process.cwd(), resolvedDest);
}
if (isSrcSubdir$1(resolvedSrc, resolvedDest)) {
return cb(new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`))
}
// do not copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (destStat.isDirectory() && isSrcSubdir$1(resolvedDest, resolvedSrc)) {
return cb(new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`))
}
return copyLink$1(resolvedSrc, dest, cb)
});
}
});
}
function copyLink$1 (resolvedSrc, dest, cb) {
gracefulFs.unlink(dest, err => {
if (err) return cb(err)
return gracefulFs.symlink(resolvedSrc, dest, cb)
});
}
// return true if dest is a subdir of src, otherwise false.
function isSrcSubdir$1 (src, dest) {
const srcArray = path__default['default'].resolve(src).split(path__default['default'].sep);
const destArray = path__default['default'].resolve(dest).split(path__default['default'].sep);
return srcArray.reduce((acc, current, i) => acc && destArray[i] === current, true)
}
function checkStats$1 (src, dest, cb) {
gracefulFs.stat(src, (err, srcStat) => {
if (err) return cb(err)
gracefulFs.stat(dest, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb(null, {srcStat, destStat: notExist$1})
return cb(err)
}
return cb(null, {srcStat, destStat})
});
});
}
function checkPaths$1 (src, dest, cb) {
checkStats$1(src, dest, (err, stats) => {
if (err) return cb(err)
const {srcStat, destStat} = stats;
if (destStat.ino && destStat.ino === srcStat.ino) {
return cb(new Error('Source and destination must not be the same.'))
}
if (srcStat.isDirectory() && isSrcSubdir$1(src, dest)) {
return cb(new Error(`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`))
}
return cb(null, destStat)
});
}
var copy_1 = copy;
const u$2 = universalify.fromCallback;
var copy$1 = {
copy: u$2(copy_1)
};
const isWindows = (process.platform === 'win32');
function defaults (options) {
const methods = [
'unlink',
'chmod',
'stat',
'lstat',
'rmdir',
'readdir'
];
methods.forEach(m => {
options[m] = options[m] || gracefulFs[m];
m = m + 'Sync';
options[m] = options[m] || gracefulFs[m];
});
options.maxBusyTries = options.maxBusyTries || 3;
}
function rimraf (p, options, cb) {
let busyTries = 0;
if (typeof options === 'function') {
cb = options;
options = {};
}
assert__default['default'](p, 'rimraf: missing path');
assert__default['default'].strictEqual(typeof p, 'string', 'rimraf: path should be a string');
assert__default['default'].strictEqual(typeof cb, 'function', 'rimraf: callback function required');
assert__default['default'](options, 'rimraf: invalid options argument provided');
assert__default['default'].strictEqual(typeof options, 'object', 'rimraf: options should be object');
defaults(options);
rimraf_(p, options, function CB (er) {
if (er) {
if ((er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') &&
busyTries < options.maxBusyTries) {
busyTries++;
const time = busyTries * 100;
// try again, with the same exact callback as this one.
return setTimeout(() => rimr