watch-cli.js 18 KB

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