watch-cli.js 19 KB

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