562 lines
18 KiB
JavaScript
562 lines
18 KiB
JavaScript
/*
|
|
@license
|
|
Rollup.js v3.29.4
|
|
Thu, 28 Sep 2023 04:54:30 GMT - commit 4e92d60fa90cead39481e3703d26e5d812f43bd1
|
|
|
|
https://github.com/rollup/rollup
|
|
|
|
Released under the MIT License.
|
|
*/
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
|
const promises = require('node:fs/promises');
|
|
const process$2 = require('node:process');
|
|
const index = require('./index.js');
|
|
const cli = require('../bin/rollup');
|
|
const rollup = require('./rollup.js');
|
|
const loadConfigFile_js = require('./loadConfigFile.js');
|
|
const node_child_process = require('node:child_process');
|
|
const watchProxy = require('./watch-proxy.js');
|
|
require('fs');
|
|
require('util');
|
|
require('stream');
|
|
require('path');
|
|
require('os');
|
|
require('./fsevents-importer.js');
|
|
require('events');
|
|
require('node:path');
|
|
require('tty');
|
|
require('node:perf_hooks');
|
|
require('node:crypto');
|
|
require('node:url');
|
|
require('../getLogFilter.js');
|
|
|
|
function timeZone(date = new Date()) {
|
|
const offset = date.getTimezoneOffset();
|
|
const absOffset = Math.abs(offset);
|
|
const hours = Math.floor(absOffset / 60);
|
|
const minutes = absOffset % 60;
|
|
const minutesOut = minutes > 0 ? ':' + ('0' + minutes).slice(-2) : '';
|
|
return (offset < 0 ? '+' : '-') + hours + minutesOut;
|
|
}
|
|
|
|
function dateTime(options = {}) {
|
|
let {
|
|
date = new Date(),
|
|
local = true,
|
|
showTimeZone = false,
|
|
showMilliseconds = false
|
|
} = options;
|
|
|
|
if (local) {
|
|
// Offset the date so it will return the correct value when getting the ISO string.
|
|
date = new Date(date.getTime() - (date.getTimezoneOffset() * 60000));
|
|
}
|
|
|
|
let end = '';
|
|
|
|
if (showTimeZone) {
|
|
end = ' UTC' + (local ? timeZone(date) : '');
|
|
}
|
|
|
|
if (showMilliseconds && date.getUTCMilliseconds() > 0) {
|
|
end = ` ${date.getUTCMilliseconds()}ms${end}`;
|
|
}
|
|
|
|
return date
|
|
.toISOString()
|
|
.replace(/T/, ' ')
|
|
.replace(/\..+/, end);
|
|
}
|
|
|
|
/**
|
|
* This is not the set of all possible signals.
|
|
*
|
|
* It IS, however, the set of all signals that trigger
|
|
* an exit on either Linux or BSD systems. Linux is a
|
|
* superset of the signal names supported on BSD, and
|
|
* the unknown signals just fail to register, so we can
|
|
* catch that easily enough.
|
|
*
|
|
* Windows signals are a different set, since there are
|
|
* signals that terminate Windows processes, but don't
|
|
* terminate (or don't even exist) on Posix systems.
|
|
*
|
|
* Don't bother with SIGKILL. It's uncatchable, which
|
|
* means that we can't fire any callbacks anyway.
|
|
*
|
|
* If a user does happen to register a handler on a non-
|
|
* fatal signal like SIGWINCH or something, and then
|
|
* exit, it'll end up firing `process.emit('exit')`, so
|
|
* the handler will be fired anyway.
|
|
*
|
|
* SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised
|
|
* artificially, inherently leave the process in a
|
|
* state from which it is not safe to try and enter JS
|
|
* listeners.
|
|
*/
|
|
const signals = [];
|
|
signals.push('SIGHUP', 'SIGINT', 'SIGTERM');
|
|
if (process.platform !== 'win32') {
|
|
signals.push('SIGALRM', 'SIGABRT', 'SIGVTALRM', 'SIGXCPU', 'SIGXFSZ', 'SIGUSR2', 'SIGTRAP', 'SIGSYS', 'SIGQUIT', 'SIGIOT'
|
|
// should detect profiler and enable/disable accordingly.
|
|
// see #21
|
|
// 'SIGPROF'
|
|
);
|
|
}
|
|
if (process.platform === 'linux') {
|
|
signals.push('SIGIO', 'SIGPOLL', 'SIGPWR', 'SIGSTKFLT');
|
|
}
|
|
|
|
// Note: since nyc uses this module to output coverage, any lines
|
|
// that are in the direct sync flow of nyc's outputCoverage are
|
|
// ignored, since we can never get coverage for them.
|
|
// grab a reference to node's real process object right away
|
|
const processOk = (process) => !!process &&
|
|
typeof process === 'object' &&
|
|
typeof process.removeListener === 'function' &&
|
|
typeof process.emit === 'function' &&
|
|
typeof process.reallyExit === 'function' &&
|
|
typeof process.listeners === 'function' &&
|
|
typeof process.kill === 'function' &&
|
|
typeof process.pid === 'number' &&
|
|
typeof process.on === 'function';
|
|
const kExitEmitter = Symbol.for('signal-exit emitter');
|
|
const global = globalThis;
|
|
const ObjectDefineProperty = Object.defineProperty.bind(Object);
|
|
// teeny special purpose ee
|
|
class Emitter {
|
|
emitted = {
|
|
afterExit: false,
|
|
exit: false,
|
|
};
|
|
listeners = {
|
|
afterExit: [],
|
|
exit: [],
|
|
};
|
|
count = 0;
|
|
id = Math.random();
|
|
constructor() {
|
|
if (global[kExitEmitter]) {
|
|
return global[kExitEmitter];
|
|
}
|
|
ObjectDefineProperty(global, kExitEmitter, {
|
|
value: this,
|
|
writable: false,
|
|
enumerable: false,
|
|
configurable: false,
|
|
});
|
|
}
|
|
on(ev, fn) {
|
|
this.listeners[ev].push(fn);
|
|
}
|
|
removeListener(ev, fn) {
|
|
const list = this.listeners[ev];
|
|
const i = list.indexOf(fn);
|
|
/* c8 ignore start */
|
|
if (i === -1) {
|
|
return;
|
|
}
|
|
/* c8 ignore stop */
|
|
if (i === 0 && list.length === 1) {
|
|
list.length = 0;
|
|
}
|
|
else {
|
|
list.splice(i, 1);
|
|
}
|
|
}
|
|
emit(ev, code, signal) {
|
|
if (this.emitted[ev]) {
|
|
return false;
|
|
}
|
|
this.emitted[ev] = true;
|
|
let ret = false;
|
|
for (const fn of this.listeners[ev]) {
|
|
ret = fn(code, signal) === true || ret;
|
|
}
|
|
if (ev === 'exit') {
|
|
ret = this.emit('afterExit', code, signal) || ret;
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
class SignalExitBase {
|
|
}
|
|
const signalExitWrap = (handler) => {
|
|
return {
|
|
onExit(cb, opts) {
|
|
return handler.onExit(cb, opts);
|
|
},
|
|
load() {
|
|
return handler.load();
|
|
},
|
|
unload() {
|
|
return handler.unload();
|
|
},
|
|
};
|
|
};
|
|
class SignalExitFallback extends SignalExitBase {
|
|
onExit() {
|
|
return () => { };
|
|
}
|
|
load() { }
|
|
unload() { }
|
|
}
|
|
class SignalExit extends SignalExitBase {
|
|
// "SIGHUP" throws an `ENOSYS` error on Windows,
|
|
// so use a supported signal instead
|
|
/* c8 ignore start */
|
|
#hupSig = process$1.platform === 'win32' ? 'SIGINT' : 'SIGHUP';
|
|
/* c8 ignore stop */
|
|
#emitter = new Emitter();
|
|
#process;
|
|
#originalProcessEmit;
|
|
#originalProcessReallyExit;
|
|
#sigListeners = {};
|
|
#loaded = false;
|
|
constructor(process) {
|
|
super();
|
|
this.#process = process;
|
|
// { <signal>: <listener fn>, ... }
|
|
this.#sigListeners = {};
|
|
for (const sig of signals) {
|
|
this.#sigListeners[sig] = () => {
|
|
// If there are no other listeners, an exit is coming!
|
|
// Simplest way: remove us and then re-send the signal.
|
|
// We know that this will kill the process, so we can
|
|
// safely emit now.
|
|
const listeners = this.#process.listeners(sig);
|
|
let { count } = this.#emitter;
|
|
// This is a workaround for the fact that signal-exit v3 and signal
|
|
// exit v4 are not aware of each other, and each will attempt to let
|
|
// the other handle it, so neither of them do. To correct this, we
|
|
// detect if we're the only handler *except* for previous versions
|
|
// of signal-exit, and increment by the count of listeners it has
|
|
// created.
|
|
/* c8 ignore start */
|
|
const p = process;
|
|
if (typeof p.__signal_exit_emitter__ === 'object' &&
|
|
typeof p.__signal_exit_emitter__.count === 'number') {
|
|
count += p.__signal_exit_emitter__.count;
|
|
}
|
|
/* c8 ignore stop */
|
|
if (listeners.length === count) {
|
|
this.unload();
|
|
const ret = this.#emitter.emit('exit', null, sig);
|
|
/* c8 ignore start */
|
|
const s = sig === 'SIGHUP' ? this.#hupSig : sig;
|
|
if (!ret)
|
|
process.kill(process.pid, s);
|
|
/* c8 ignore stop */
|
|
}
|
|
};
|
|
}
|
|
this.#originalProcessReallyExit = process.reallyExit;
|
|
this.#originalProcessEmit = process.emit;
|
|
}
|
|
onExit(cb, opts) {
|
|
/* c8 ignore start */
|
|
if (!processOk(this.#process)) {
|
|
return () => { };
|
|
}
|
|
/* c8 ignore stop */
|
|
if (this.#loaded === false) {
|
|
this.load();
|
|
}
|
|
const ev = opts?.alwaysLast ? 'afterExit' : 'exit';
|
|
this.#emitter.on(ev, cb);
|
|
return () => {
|
|
this.#emitter.removeListener(ev, cb);
|
|
if (this.#emitter.listeners['exit'].length === 0 &&
|
|
this.#emitter.listeners['afterExit'].length === 0) {
|
|
this.unload();
|
|
}
|
|
};
|
|
}
|
|
load() {
|
|
if (this.#loaded) {
|
|
return;
|
|
}
|
|
this.#loaded = true;
|
|
// This is the number of onSignalExit's that are in play.
|
|
// It's important so that we can count the correct number of
|
|
// listeners on signals, and don't wait for the other one to
|
|
// handle it instead of us.
|
|
this.#emitter.count += 1;
|
|
for (const sig of signals) {
|
|
try {
|
|
const fn = this.#sigListeners[sig];
|
|
if (fn)
|
|
this.#process.on(sig, fn);
|
|
}
|
|
catch (_) { }
|
|
}
|
|
this.#process.emit = (ev, ...a) => {
|
|
return this.#processEmit(ev, ...a);
|
|
};
|
|
this.#process.reallyExit = (code) => {
|
|
return this.#processReallyExit(code);
|
|
};
|
|
}
|
|
unload() {
|
|
if (!this.#loaded) {
|
|
return;
|
|
}
|
|
this.#loaded = false;
|
|
signals.forEach(sig => {
|
|
const listener = this.#sigListeners[sig];
|
|
/* c8 ignore start */
|
|
if (!listener) {
|
|
throw new Error('Listener not defined for signal: ' + sig);
|
|
}
|
|
/* c8 ignore stop */
|
|
try {
|
|
this.#process.removeListener(sig, listener);
|
|
/* c8 ignore start */
|
|
}
|
|
catch (_) { }
|
|
/* c8 ignore stop */
|
|
});
|
|
this.#process.emit = this.#originalProcessEmit;
|
|
this.#process.reallyExit = this.#originalProcessReallyExit;
|
|
this.#emitter.count -= 1;
|
|
}
|
|
#processReallyExit(code) {
|
|
/* c8 ignore start */
|
|
if (!processOk(this.#process)) {
|
|
return 0;
|
|
}
|
|
this.#process.exitCode = code || 0;
|
|
/* c8 ignore stop */
|
|
this.#emitter.emit('exit', this.#process.exitCode, null);
|
|
return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
|
|
}
|
|
#processEmit(ev, ...args) {
|
|
const og = this.#originalProcessEmit;
|
|
if (ev === 'exit' && processOk(this.#process)) {
|
|
if (typeof args[0] === 'number') {
|
|
this.#process.exitCode = args[0];
|
|
/* c8 ignore start */
|
|
}
|
|
/* c8 ignore start */
|
|
const ret = og.call(this.#process, ev, ...args);
|
|
/* c8 ignore start */
|
|
this.#emitter.emit('exit', this.#process.exitCode, null);
|
|
/* c8 ignore stop */
|
|
return ret;
|
|
}
|
|
else {
|
|
return og.call(this.#process, ev, ...args);
|
|
}
|
|
}
|
|
}
|
|
const process$1 = globalThis.process;
|
|
// wrap so that we call the method on the actual handler, without
|
|
// exporting it directly.
|
|
const {
|
|
/**
|
|
* Called when the process is exiting, whether via signal, explicit
|
|
* exit, or running out of stuff to do.
|
|
*
|
|
* If the global process object is not suitable for instrumentation,
|
|
* then this will be a no-op.
|
|
*
|
|
* Returns a function that may be used to unload signal-exit.
|
|
*/
|
|
onExit,
|
|
/**
|
|
* Load the listeners. Likely you never need to call this, unless
|
|
* doing a rather deep integration with signal-exit functionality.
|
|
* Mostly exposed for the benefit of testing.
|
|
*
|
|
* @internal
|
|
*/
|
|
load,
|
|
/**
|
|
* Unload the listeners. Likely you never need to call this, unless
|
|
* doing a rather deep integration with signal-exit functionality.
|
|
* Mostly exposed for the benefit of testing.
|
|
*
|
|
* @internal
|
|
*/
|
|
unload, } = signalExitWrap(processOk(process$1) ? new SignalExit(process$1) : new SignalExitFallback());
|
|
|
|
const CLEAR_SCREEN = '\u001Bc';
|
|
function getResetScreen(configs, allowClearScreen) {
|
|
let clearScreen = allowClearScreen;
|
|
for (const config of configs) {
|
|
if (config.watch && config.watch.clearScreen === false) {
|
|
clearScreen = false;
|
|
}
|
|
}
|
|
if (clearScreen) {
|
|
return (heading) => rollup.stderr(CLEAR_SCREEN + heading);
|
|
}
|
|
let firstRun = true;
|
|
return (heading) => {
|
|
if (firstRun) {
|
|
rollup.stderr(heading);
|
|
firstRun = false;
|
|
}
|
|
};
|
|
}
|
|
|
|
function extractWatchHooks(command) {
|
|
if (!Array.isArray(command.watch))
|
|
return {};
|
|
return command.watch
|
|
.filter(value => typeof value === 'object')
|
|
.reduce((accumulator, keyValueOption) => ({ ...accumulator, ...keyValueOption }), {});
|
|
}
|
|
function createWatchHooks(command) {
|
|
const watchHooks = extractWatchHooks(command);
|
|
return function (hook) {
|
|
if (watchHooks[hook]) {
|
|
const cmd = watchHooks[hook];
|
|
if (!command.silent) {
|
|
rollup.stderr(rollup.cyan$1(`watch.${hook} ${rollup.bold(`$ ${cmd}`)}`));
|
|
}
|
|
try {
|
|
// !! important - use stderr for all writes from execSync
|
|
const stdio = [process.stdin, process.stderr, process.stderr];
|
|
node_child_process.execSync(cmd, { stdio: command.silent ? 'ignore' : stdio });
|
|
}
|
|
catch (error) {
|
|
rollup.stderr(error.message);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
async function watch(command) {
|
|
process$2.env.ROLLUP_WATCH = 'true';
|
|
const isTTY = process$2.stderr.isTTY;
|
|
const silent = command.silent;
|
|
let watcher;
|
|
let configWatcher;
|
|
let resetScreen;
|
|
const configFile = command.config ? await cli.getConfigPath(command.config) : null;
|
|
const runWatchHook = createWatchHooks(command);
|
|
onExit(close);
|
|
process$2.on('uncaughtException', closeWithError);
|
|
if (!process$2.stdin.isTTY) {
|
|
process$2.stdin.on('end', close);
|
|
process$2.stdin.resume();
|
|
}
|
|
async function loadConfigFromFileAndTrack(configFile) {
|
|
let configFileData = null;
|
|
let configFileRevision = 0;
|
|
configWatcher = index.chokidar.watch(configFile).on('change', reloadConfigFile);
|
|
await reloadConfigFile();
|
|
async function reloadConfigFile() {
|
|
try {
|
|
const newConfigFileData = await promises.readFile(configFile, 'utf8');
|
|
if (newConfigFileData === configFileData) {
|
|
return;
|
|
}
|
|
configFileRevision++;
|
|
const currentConfigFileRevision = configFileRevision;
|
|
if (configFileData) {
|
|
rollup.stderr(`\nReloading updated config...`);
|
|
}
|
|
configFileData = newConfigFileData;
|
|
const { options, warnings } = await loadConfigFile_js.loadConfigFile(configFile, command, true);
|
|
if (currentConfigFileRevision !== configFileRevision) {
|
|
return;
|
|
}
|
|
if (watcher) {
|
|
await watcher.close();
|
|
}
|
|
start(options, warnings);
|
|
}
|
|
catch (error) {
|
|
rollup.handleError(error, true);
|
|
}
|
|
}
|
|
}
|
|
if (configFile) {
|
|
await loadConfigFromFileAndTrack(configFile);
|
|
}
|
|
else {
|
|
const { options, warnings } = await cli.loadConfigFromCommand(command, true);
|
|
await start(options, warnings);
|
|
}
|
|
async function start(configs, warnings) {
|
|
watcher = watchProxy.watch(configs);
|
|
watcher.on('event', event => {
|
|
switch (event.code) {
|
|
case 'ERROR': {
|
|
warnings.flush();
|
|
rollup.handleError(event.error, true);
|
|
runWatchHook('onError');
|
|
break;
|
|
}
|
|
case 'START': {
|
|
if (!silent) {
|
|
if (!resetScreen) {
|
|
resetScreen = getResetScreen(configs, isTTY);
|
|
}
|
|
resetScreen(rollup.underline(`rollup v${rollup.version}`));
|
|
}
|
|
runWatchHook('onStart');
|
|
break;
|
|
}
|
|
case 'BUNDLE_START': {
|
|
if (!silent) {
|
|
let input = event.input;
|
|
if (typeof input !== 'string') {
|
|
input = Array.isArray(input)
|
|
? input.join(', ')
|
|
: Object.values(input).join(', ');
|
|
}
|
|
rollup.stderr(rollup.cyan$1(`bundles ${rollup.bold(input)} → ${rollup.bold(event.output.map(rollup.relativeId).join(', '))}...`));
|
|
}
|
|
runWatchHook('onBundleStart');
|
|
break;
|
|
}
|
|
case 'BUNDLE_END': {
|
|
warnings.flush();
|
|
if (!silent)
|
|
rollup.stderr(rollup.green(`created ${rollup.bold(event.output.map(rollup.relativeId).join(', '))} in ${rollup.bold(cli.prettyMilliseconds(event.duration))}`));
|
|
runWatchHook('onBundleEnd');
|
|
if (event.result && event.result.getTimings) {
|
|
cli.printTimings(event.result.getTimings());
|
|
}
|
|
break;
|
|
}
|
|
case 'END': {
|
|
runWatchHook('onEnd');
|
|
if (!silent && isTTY) {
|
|
rollup.stderr(`\n[${dateTime()}] waiting for changes...`);
|
|
}
|
|
}
|
|
}
|
|
if ('result' in event && event.result) {
|
|
event.result.close().catch(error => rollup.handleError(error, true));
|
|
}
|
|
});
|
|
}
|
|
async function close(code) {
|
|
process$2.removeListener('uncaughtException', closeWithError);
|
|
// removing a non-existent listener is a no-op
|
|
process$2.stdin.removeListener('end', close);
|
|
if (watcher)
|
|
await watcher.close();
|
|
if (configWatcher)
|
|
configWatcher.close();
|
|
if (code)
|
|
process$2.exit(code);
|
|
}
|
|
// return a promise that never resolves to keep the process running
|
|
return new Promise(() => { });
|
|
}
|
|
function closeWithError(error) {
|
|
error.name = `Uncaught ${error.name}`;
|
|
rollup.handleError(error);
|
|
}
|
|
|
|
exports.watch = watch;
|
|
//# sourceMappingURL=watch-cli.js.map
|