utils.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. "use strict";
  2. /** @typedef {import("webpack").Compilation["inputFileSystem"] } InputFileSystem */
  3. /** @typedef {import("fs").Stats } Stats */
  4. /**
  5. * @param {InputFileSystem} inputFileSystem
  6. * @param {string} path
  7. * @return {Promise<undefined | Stats>}
  8. */
  9. function stat(inputFileSystem, path) {
  10. return new Promise((resolve, reject) => {
  11. inputFileSystem.stat(path,
  12. /**
  13. * @param {null | undefined | NodeJS.ErrnoException} err
  14. * @param {undefined | Stats} stats
  15. */
  16. // @ts-ignore
  17. (err, stats) => {
  18. if (err) {
  19. reject(err);
  20. return;
  21. }
  22. resolve(stats);
  23. });
  24. });
  25. }
  26. /**
  27. * @param {InputFileSystem} inputFileSystem
  28. * @param {string} path
  29. * @return {Promise<string | Buffer>}
  30. */
  31. function readFile(inputFileSystem, path) {
  32. return new Promise((resolve, reject) => {
  33. inputFileSystem.readFile(path,
  34. /**
  35. * @param {null | undefined | NodeJS.ErrnoException} err
  36. * @param {undefined | string | Buffer} data
  37. */
  38. (err, data) => {
  39. if (err) {
  40. reject(err);
  41. return;
  42. }
  43. resolve(
  44. /** @type {string | Buffer} */
  45. data);
  46. });
  47. });
  48. }
  49. const notSettled = Symbol(`not-settled`);
  50. /**
  51. * @template T
  52. * @typedef {() => Promise<T>} Task
  53. */
  54. /**
  55. * Run tasks with limited concurency.
  56. * @template T
  57. * @param {number} limit - Limit of tasks that run at once.
  58. * @param {Task<T>[]} tasks - List of tasks to run.
  59. * @returns {Promise<T[]>} A promise that fulfills to an array of the results
  60. */
  61. function throttleAll(limit, tasks) {
  62. if (!Number.isInteger(limit) || limit < 1) {
  63. throw new TypeError(`Expected \`limit\` to be a finite number > 0, got \`${limit}\` (${typeof limit})`);
  64. }
  65. if (!Array.isArray(tasks) || !tasks.every(task => typeof task === `function`)) {
  66. throw new TypeError(`Expected \`tasks\` to be a list of functions returning a promise`);
  67. }
  68. return new Promise((resolve, reject) => {
  69. const result = Array(tasks.length).fill(notSettled);
  70. const entries = tasks.entries();
  71. const next = () => {
  72. const {
  73. done,
  74. value
  75. } = entries.next();
  76. if (done) {
  77. const isLast = !result.includes(notSettled);
  78. if (isLast) {
  79. resolve(
  80. /** @type{T[]} **/
  81. result);
  82. }
  83. return;
  84. }
  85. const [index, task] = value;
  86. /**
  87. * @param {T} x
  88. */
  89. const onFulfilled = x => {
  90. result[index] = x;
  91. next();
  92. };
  93. task().then(onFulfilled, reject);
  94. };
  95. Array(limit).fill(0).forEach(next);
  96. });
  97. }
  98. module.exports = {
  99. stat,
  100. readFile,
  101. throttleAll
  102. };