watch-cli.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. /*
  2. @license
  3. Rollup.js v3.23.1
  4. Sun, 04 Jun 2023 18:41:19 GMT - commit d83684788ef0e8bf0fc4449680960c3783a37def
  5. https://github.com/rollup/rollup
  6. Released under the MIT License.
  7. */
  8. 'use strict';
  9. Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  10. const promises = require('node:fs/promises');
  11. const process$2 = require('node:process');
  12. const index = require('./index.js');
  13. const cli = require('../bin/rollup');
  14. const rollup = require('./rollup.js');
  15. const loadConfigFile_js = require('./loadConfigFile.js');
  16. const node_child_process = require('node:child_process');
  17. const watchProxy = require('./watch-proxy.js');
  18. require('fs');
  19. require('util');
  20. require('stream');
  21. require('path');
  22. require('os');
  23. require('./fsevents-importer.js');
  24. require('events');
  25. require('node:path');
  26. require('tty');
  27. require('node:perf_hooks');
  28. require('node:crypto');
  29. require('node:url');
  30. function timeZone(date = new Date()) {
  31. const offset = date.getTimezoneOffset();
  32. const absOffset = Math.abs(offset);
  33. const hours = Math.floor(absOffset / 60);
  34. const minutes = absOffset % 60;
  35. const minutesOut = minutes > 0 ? ':' + ('0' + minutes).slice(-2) : '';
  36. return (offset < 0 ? '+' : '-') + hours + minutesOut;
  37. }
  38. function dateTime(options = {}) {
  39. let {
  40. date = new Date(),
  41. local = true,
  42. showTimeZone = false,
  43. showMilliseconds = false
  44. } = options;
  45. if (local) {
  46. // Offset the date so it will return the correct value when getting the ISO string.
  47. date = new Date(date.getTime() - (date.getTimezoneOffset() * 60000));
  48. }
  49. let end = '';
  50. if (showTimeZone) {
  51. end = ' UTC' + (local ? timeZone(date) : '');
  52. }
  53. if (showMilliseconds && date.getUTCMilliseconds() > 0) {
  54. end = ` ${date.getUTCMilliseconds()}ms${end}`;
  55. }
  56. return date
  57. .toISOString()
  58. .replace(/T/, ' ')
  59. .replace(/\..+/, end);
  60. }
  61. /**
  62. * This is not the set of all possible signals.
  63. *
  64. * It IS, however, the set of all signals that trigger
  65. * an exit on either Linux or BSD systems. Linux is a
  66. * superset of the signal names supported on BSD, and
  67. * the unknown signals just fail to register, so we can
  68. * catch that easily enough.
  69. *
  70. * Windows signals are a different set, since there are
  71. * signals that terminate Windows processes, but don't
  72. * terminate (or don't even exist) on Posix systems.
  73. *
  74. * Don't bother with SIGKILL. It's uncatchable, which
  75. * means that we can't fire any callbacks anyway.
  76. *
  77. * If a user does happen to register a handler on a non-
  78. * fatal signal like SIGWINCH or something, and then
  79. * exit, it'll end up firing `process.emit('exit')`, so
  80. * the handler will be fired anyway.
  81. *
  82. * SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised
  83. * artificially, inherently leave the process in a
  84. * state from which it is not safe to try and enter JS
  85. * listeners.
  86. */
  87. const signals = [];
  88. signals.push('SIGHUP', 'SIGINT', 'SIGTERM');
  89. if (process.platform !== 'win32') {
  90. signals.push('SIGALRM', 'SIGABRT', 'SIGVTALRM', 'SIGXCPU', 'SIGXFSZ', 'SIGUSR2', 'SIGTRAP', 'SIGSYS', 'SIGQUIT', 'SIGIOT'
  91. // should detect profiler and enable/disable accordingly.
  92. // see #21
  93. // 'SIGPROF'
  94. );
  95. }
  96. if (process.platform === 'linux') {
  97. signals.push('SIGIO', 'SIGPOLL', 'SIGPWR', 'SIGSTKFLT');
  98. }
  99. // Note: since nyc uses this module to output coverage, any lines
  100. // that are in the direct sync flow of nyc's outputCoverage are
  101. // ignored, since we can never get coverage for them.
  102. // grab a reference to node's real process object right away
  103. const processOk = (process) => !!process &&
  104. typeof process === 'object' &&
  105. typeof process.removeListener === 'function' &&
  106. typeof process.emit === 'function' &&
  107. typeof process.reallyExit === 'function' &&
  108. typeof process.listeners === 'function' &&
  109. typeof process.kill === 'function' &&
  110. typeof process.pid === 'number' &&
  111. typeof process.on === 'function';
  112. const kExitEmitter = Symbol.for('signal-exit emitter');
  113. const global = globalThis;
  114. const ObjectDefineProperty = Object.defineProperty.bind(Object);
  115. // teeny tiny ee
  116. class Emitter {
  117. emitted = {
  118. afterExit: false,
  119. exit: false,
  120. };
  121. listeners = {
  122. afterExit: [],
  123. exit: [],
  124. };
  125. count = 0;
  126. id = Math.random();
  127. constructor() {
  128. if (global[kExitEmitter]) {
  129. return global[kExitEmitter];
  130. }
  131. ObjectDefineProperty(global, kExitEmitter, {
  132. value: this,
  133. writable: false,
  134. enumerable: false,
  135. configurable: false,
  136. });
  137. }
  138. on(ev, fn) {
  139. this.listeners[ev].push(fn);
  140. }
  141. removeListener(ev, fn) {
  142. const list = this.listeners[ev];
  143. const i = list.indexOf(fn);
  144. /* c8 ignore start */
  145. if (i === -1) {
  146. return;
  147. }
  148. /* c8 ignore stop */
  149. if (i === 0 && list.length === 1) {
  150. list.length = 0;
  151. }
  152. else {
  153. list.splice(i, 1);
  154. }
  155. }
  156. emit(ev, code, signal) {
  157. if (this.emitted[ev]) {
  158. return;
  159. }
  160. this.emitted[ev] = true;
  161. for (const fn of this.listeners[ev]) {
  162. fn(code, signal);
  163. }
  164. }
  165. }
  166. class SignalExitBase {
  167. }
  168. const signalExitWrap = (handler) => {
  169. return {
  170. onExit(cb, opts) {
  171. return handler.onExit(cb, opts);
  172. },
  173. load() {
  174. return handler.load();
  175. },
  176. unload() {
  177. return handler.unload();
  178. },
  179. };
  180. };
  181. class SignalExitFallback extends SignalExitBase {
  182. onExit() {
  183. return () => { };
  184. }
  185. load() { }
  186. unload() { }
  187. }
  188. class SignalExit extends SignalExitBase {
  189. // "SIGHUP" throws an `ENOSYS` error on Windows,
  190. // so use a supported signal instead
  191. /* c8 ignore start */
  192. #hupSig = process$1.platform === 'win32' ? 'SIGINT' : 'SIGHUP';
  193. /* c8 ignore stop */
  194. #emitter = new Emitter();
  195. #process;
  196. #originalProcessEmit;
  197. #originalProcessReallyExit;
  198. #sigListeners = {};
  199. #loaded = false;
  200. constructor(process) {
  201. super();
  202. this.#process = process;
  203. // { <signal>: <listener fn>, ... }
  204. this.#sigListeners = {};
  205. for (const sig of signals) {
  206. this.#sigListeners[sig] = () => {
  207. // If there are no other listeners, an exit is coming!
  208. // Simplest way: remove us and then re-send the signal.
  209. // We know that this will kill the process, so we can
  210. // safely emit now.
  211. const listeners = this.#process.listeners(sig);
  212. let { count } = this.#emitter;
  213. // This is a workaround for the fact that signal-exit v3 and signal
  214. // exit v4 are not aware of each other, and each will attempt to let
  215. // the other handle it, so neither of them do. To correct this, we
  216. // detect if we're the only handler *except* for previous versions
  217. // of signal-exit.
  218. /* c8 ignore start */
  219. //@ts-ignore
  220. if (typeof process.__signal_exit_emitter__ === 'object')
  221. count++;
  222. /* c8 ignore stop */
  223. if (listeners.length === count) {
  224. this.unload();
  225. this.#emitter.emit('exit', null, sig);
  226. this.#emitter.emit('afterExit', null, sig);
  227. /* c8 ignore start */
  228. process.kill(process.pid, sig === 'SIGHUP' ? this.#hupSig : sig);
  229. /* c8 ignore stop */
  230. }
  231. };
  232. }
  233. this.#originalProcessReallyExit = process.reallyExit;
  234. this.#originalProcessEmit = process.emit;
  235. }
  236. onExit(cb, opts) {
  237. /* c8 ignore start */
  238. if (!processOk(this.#process)) {
  239. return () => { };
  240. }
  241. /* c8 ignore stop */
  242. if (this.#loaded === false) {
  243. this.load();
  244. }
  245. const ev = opts?.alwaysLast ? 'afterExit' : 'exit';
  246. this.#emitter.on(ev, cb);
  247. return () => {
  248. this.#emitter.removeListener(ev, cb);
  249. if (this.#emitter.listeners['exit'].length === 0 &&
  250. this.#emitter.listeners['afterExit'].length === 0) {
  251. this.unload();
  252. }
  253. };
  254. }
  255. load() {
  256. if (this.#loaded) {
  257. return;
  258. }
  259. this.#loaded = true;
  260. // This is the number of onSignalExit's that are in play.
  261. // It's important so that we can count the correct number of
  262. // listeners on signals, and don't wait for the other one to
  263. // handle it instead of us.
  264. this.#emitter.count += 1;
  265. for (const sig of signals) {
  266. try {
  267. const fn = this.#sigListeners[sig];
  268. if (fn)
  269. this.#process.on(sig, fn);
  270. }
  271. catch (_) { }
  272. }
  273. this.#process.emit = (ev, ...a) => {
  274. return this.#processEmit(ev, ...a);
  275. };
  276. this.#process.reallyExit = (code) => {
  277. return this.#processReallyExit(code);
  278. };
  279. }
  280. unload() {
  281. if (!this.#loaded) {
  282. return;
  283. }
  284. this.#loaded = false;
  285. signals.forEach(sig => {
  286. const listener = this.#sigListeners[sig];
  287. /* c8 ignore start */
  288. if (!listener) {
  289. throw new Error('Listener not defined for signal: ' + sig);
  290. }
  291. /* c8 ignore stop */
  292. try {
  293. this.#process.removeListener(sig, listener);
  294. /* c8 ignore start */
  295. }
  296. catch (_) { }
  297. /* c8 ignore stop */
  298. });
  299. this.#process.emit = this.#originalProcessEmit;
  300. this.#process.reallyExit = this.#originalProcessReallyExit;
  301. this.#emitter.count -= 1;
  302. }
  303. #processReallyExit(code) {
  304. /* c8 ignore start */
  305. if (!processOk(this.#process)) {
  306. return 0;
  307. }
  308. this.#process.exitCode = code || 0;
  309. /* c8 ignore stop */
  310. this.#emitter.emit('exit', this.#process.exitCode, null);
  311. this.#emitter.emit('afterExit', this.#process.exitCode, null);
  312. return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
  313. }
  314. #processEmit(ev, ...args) {
  315. const og = this.#originalProcessEmit;
  316. if (ev === 'exit' && processOk(this.#process)) {
  317. if (typeof args[0] === 'number') {
  318. this.#process.exitCode = args[0];
  319. /* c8 ignore start */
  320. }
  321. /* c8 ignore start */
  322. const ret = og.call(this.#process, ev, ...args);
  323. /* c8 ignore start */
  324. this.#emitter.emit('exit', this.#process.exitCode, null);
  325. this.#emitter.emit('afterExit', this.#process.exitCode, null);
  326. /* c8 ignore stop */
  327. return ret;
  328. }
  329. else {
  330. return og.call(this.#process, ev, ...args);
  331. }
  332. }
  333. }
  334. const process$1 = globalThis.process;
  335. // wrap so that we call the method on the actual handler, without
  336. // exporting it directly.
  337. const {
  338. /**
  339. * Called when the process is exiting, whether via signal, explicit
  340. * exit, or running out of stuff to do.
  341. *
  342. * If the global process object is not suitable for instrumentation,
  343. * then this will be a no-op.
  344. *
  345. * Returns a function that may be used to unload signal-exit.
  346. */
  347. onExit,
  348. /**
  349. * Load the listeners. Likely you never need to call this, unless
  350. * doing a rather deep integration with signal-exit functionality.
  351. * Mostly exposed for the benefit of testing.
  352. *
  353. * @internal
  354. */
  355. load,
  356. /**
  357. * Unload the listeners. Likely you never need to call this, unless
  358. * doing a rather deep integration with signal-exit functionality.
  359. * Mostly exposed for the benefit of testing.
  360. *
  361. * @internal
  362. */
  363. unload, } = signalExitWrap(processOk(process$1) ? new SignalExit(process$1) : new SignalExitFallback());
  364. const CLEAR_SCREEN = '\u001Bc';
  365. function getResetScreen(configs, allowClearScreen) {
  366. let clearScreen = allowClearScreen;
  367. for (const config of configs) {
  368. if (config.watch && config.watch.clearScreen === false) {
  369. clearScreen = false;
  370. }
  371. }
  372. if (clearScreen) {
  373. return (heading) => rollup.stderr(CLEAR_SCREEN + heading);
  374. }
  375. let firstRun = true;
  376. return (heading) => {
  377. if (firstRun) {
  378. rollup.stderr(heading);
  379. firstRun = false;
  380. }
  381. };
  382. }
  383. function extractWatchHooks(command) {
  384. if (!Array.isArray(command.watch))
  385. return {};
  386. return command.watch
  387. .filter(value => typeof value === 'object')
  388. .reduce((accumulator, keyValueOption) => ({ ...accumulator, ...keyValueOption }), {});
  389. }
  390. function createWatchHooks(command) {
  391. const watchHooks = extractWatchHooks(command);
  392. return function (hook) {
  393. if (watchHooks[hook]) {
  394. const cmd = watchHooks[hook];
  395. if (!command.silent) {
  396. rollup.stderr(rollup.cyan(`watch.${hook} ${rollup.bold(`$ ${cmd}`)}`));
  397. }
  398. try {
  399. // !! important - use stderr for all writes from execSync
  400. const stdio = [process.stdin, process.stderr, process.stderr];
  401. node_child_process.execSync(cmd, { stdio: command.silent ? 'ignore' : stdio });
  402. }
  403. catch (error) {
  404. rollup.stderr(error.message);
  405. }
  406. }
  407. };
  408. }
  409. async function watch(command) {
  410. process$2.env.ROLLUP_WATCH = 'true';
  411. const isTTY = process$2.stderr.isTTY;
  412. const silent = command.silent;
  413. let watcher;
  414. let configWatcher;
  415. let resetScreen;
  416. const configFile = command.config ? await cli.getConfigPath(command.config) : null;
  417. const runWatchHook = createWatchHooks(command);
  418. onExit(close);
  419. process$2.on('uncaughtException', closeWithError);
  420. if (!process$2.stdin.isTTY) {
  421. process$2.stdin.on('end', close);
  422. process$2.stdin.resume();
  423. }
  424. async function loadConfigFromFileAndTrack(configFile) {
  425. let configFileData = null;
  426. let configFileRevision = 0;
  427. configWatcher = index.chokidar.watch(configFile).on('change', reloadConfigFile);
  428. await reloadConfigFile();
  429. async function reloadConfigFile() {
  430. try {
  431. const newConfigFileData = await promises.readFile(configFile, 'utf8');
  432. if (newConfigFileData === configFileData) {
  433. return;
  434. }
  435. configFileRevision++;
  436. const currentConfigFileRevision = configFileRevision;
  437. if (configFileData) {
  438. rollup.stderr(`\nReloading updated config...`);
  439. }
  440. configFileData = newConfigFileData;
  441. const { options, warnings } = await loadConfigFile_js.loadConfigFile(configFile, command);
  442. if (currentConfigFileRevision !== configFileRevision) {
  443. return;
  444. }
  445. if (watcher) {
  446. await watcher.close();
  447. }
  448. start(options, warnings);
  449. }
  450. catch (error) {
  451. rollup.handleError(error, true);
  452. }
  453. }
  454. }
  455. if (configFile) {
  456. await loadConfigFromFileAndTrack(configFile);
  457. }
  458. else {
  459. const { options, warnings } = await cli.loadConfigFromCommand(command);
  460. await start(options, warnings);
  461. }
  462. async function start(configs, warnings) {
  463. watcher = watchProxy.watch(configs);
  464. watcher.on('event', event => {
  465. switch (event.code) {
  466. case 'ERROR': {
  467. warnings.flush();
  468. rollup.handleError(event.error, true);
  469. runWatchHook('onError');
  470. break;
  471. }
  472. case 'START': {
  473. if (!silent) {
  474. if (!resetScreen) {
  475. resetScreen = getResetScreen(configs, isTTY);
  476. }
  477. resetScreen(rollup.underline(`rollup v${rollup.version}`));
  478. }
  479. runWatchHook('onStart');
  480. break;
  481. }
  482. case 'BUNDLE_START': {
  483. if (!silent) {
  484. let input = event.input;
  485. if (typeof input !== 'string') {
  486. input = Array.isArray(input)
  487. ? input.join(', ')
  488. : Object.values(input).join(', ');
  489. }
  490. rollup.stderr(rollup.cyan(`bundles ${rollup.bold(input)} → ${rollup.bold(event.output.map(rollup.relativeId).join(', '))}...`));
  491. }
  492. runWatchHook('onBundleStart');
  493. break;
  494. }
  495. case 'BUNDLE_END': {
  496. warnings.flush();
  497. if (!silent)
  498. rollup.stderr(rollup.green(`created ${rollup.bold(event.output.map(rollup.relativeId).join(', '))} in ${rollup.bold(cli.prettyMilliseconds(event.duration))}`));
  499. runWatchHook('onBundleEnd');
  500. if (event.result && event.result.getTimings) {
  501. cli.printTimings(event.result.getTimings());
  502. }
  503. break;
  504. }
  505. case 'END': {
  506. runWatchHook('onEnd');
  507. if (!silent && isTTY) {
  508. rollup.stderr(`\n[${dateTime()}] waiting for changes...`);
  509. }
  510. }
  511. }
  512. if ('result' in event && event.result) {
  513. event.result.close().catch(error => rollup.handleError(error, true));
  514. }
  515. });
  516. }
  517. async function close(code) {
  518. process$2.removeListener('uncaughtException', closeWithError);
  519. // removing a non-existent listener is a no-op
  520. process$2.stdin.removeListener('end', close);
  521. if (watcher)
  522. await watcher.close();
  523. if (configWatcher)
  524. configWatcher.close();
  525. process$2.exit(code || 0);
  526. }
  527. // return a promise that never resolves to keep the process running
  528. return new Promise(() => { });
  529. }
  530. function closeWithError(error) {
  531. error.name = `Uncaught ${error.name}`;
  532. rollup.handleError(error);
  533. }
  534. exports.watch = watch;
  535. //# sourceMappingURL=watch-cli.js.map