utils.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. "use strict";
  2. /** @typedef {import("@jridgewell/trace-mapping").SourceMapInput} SourceMapInput */
  3. /** @typedef {import("terser").FormatOptions} TerserFormatOptions */
  4. /** @typedef {import("terser").MinifyOptions} TerserOptions */
  5. /** @typedef {import("terser").CompressOptions} TerserCompressOptions */
  6. /** @typedef {import("terser").ECMA} TerserECMA */
  7. /** @typedef {import("./index.js").ExtractCommentsOptions} ExtractCommentsOptions */
  8. /** @typedef {import("./index.js").ExtractCommentsFunction} ExtractCommentsFunction */
  9. /** @typedef {import("./index.js").ExtractCommentsCondition} ExtractCommentsCondition */
  10. /** @typedef {import("./index.js").Input} Input */
  11. /** @typedef {import("./index.js").MinimizedResult} MinimizedResult */
  12. /** @typedef {import("./index.js").PredefinedOptions} PredefinedOptions */
  13. /** @typedef {import("./index.js").CustomOptions} CustomOptions */
  14. /**
  15. * @typedef {Array<string>} ExtractedComments
  16. */
  17. const notSettled = Symbol(`not-settled`);
  18. /**
  19. * @template T
  20. * @typedef {() => Promise<T>} Task
  21. */
  22. /**
  23. * Run tasks with limited concurency.
  24. * @template T
  25. * @param {number} limit - Limit of tasks that run at once.
  26. * @param {Task<T>[]} tasks - List of tasks to run.
  27. * @returns {Promise<T[]>} A promise that fulfills to an array of the results
  28. */
  29. function throttleAll(limit, tasks) {
  30. if (!Number.isInteger(limit) || limit < 1) {
  31. throw new TypeError(`Expected \`limit\` to be a finite number > 0, got \`${limit}\` (${typeof limit})`);
  32. }
  33. if (!Array.isArray(tasks) || !tasks.every(task => typeof task === `function`)) {
  34. throw new TypeError(`Expected \`tasks\` to be a list of functions returning a promise`);
  35. }
  36. return new Promise((resolve, reject) => {
  37. const result = Array(tasks.length).fill(notSettled);
  38. const entries = tasks.entries();
  39. const next = () => {
  40. const {
  41. done,
  42. value
  43. } = entries.next();
  44. if (done) {
  45. const isLast = !result.includes(notSettled);
  46. if (isLast) resolve(
  47. /** @type{T[]} **/
  48. result);
  49. return;
  50. }
  51. const [index, task] = value;
  52. /**
  53. * @param {T} x
  54. */
  55. const onFulfilled = x => {
  56. result[index] = x;
  57. next();
  58. };
  59. task().then(onFulfilled, reject);
  60. };
  61. Array(limit).fill(0).forEach(next);
  62. });
  63. }
  64. /* istanbul ignore next */
  65. /**
  66. * @param {Input} input
  67. * @param {SourceMapInput | undefined} sourceMap
  68. * @param {PredefinedOptions & CustomOptions} minimizerOptions
  69. * @param {ExtractCommentsOptions | undefined} extractComments
  70. * @return {Promise<MinimizedResult>}
  71. */
  72. async function terserMinify(input, sourceMap, minimizerOptions, extractComments) {
  73. /**
  74. * @param {any} value
  75. * @returns {boolean}
  76. */
  77. const isObject = value => {
  78. const type = typeof value;
  79. return value != null && (type === "object" || type === "function");
  80. };
  81. /**
  82. * @param {TerserOptions & { sourceMap: undefined } & ({ output: TerserFormatOptions & { beautify: boolean } } | { format: TerserFormatOptions & { beautify: boolean } })} terserOptions
  83. * @param {ExtractedComments} extractedComments
  84. * @returns {ExtractCommentsFunction}
  85. */
  86. const buildComments = (terserOptions, extractedComments) => {
  87. /** @type {{ [index: string]: ExtractCommentsCondition }} */
  88. const condition = {};
  89. let comments;
  90. if (terserOptions.format) {
  91. ({
  92. comments
  93. } = terserOptions.format);
  94. } else if (terserOptions.output) {
  95. ({
  96. comments
  97. } = terserOptions.output);
  98. }
  99. condition.preserve = typeof comments !== "undefined" ? comments : false;
  100. if (typeof extractComments === "boolean" && extractComments) {
  101. condition.extract = "some";
  102. } else if (typeof extractComments === "string" || extractComments instanceof RegExp) {
  103. condition.extract = extractComments;
  104. } else if (typeof extractComments === "function") {
  105. condition.extract = extractComments;
  106. } else if (extractComments && isObject(extractComments)) {
  107. condition.extract = typeof extractComments.condition === "boolean" && extractComments.condition ? "some" : typeof extractComments.condition !== "undefined" ? extractComments.condition : "some";
  108. } else {
  109. // No extract
  110. // Preserve using "commentsOpts" or "some"
  111. condition.preserve = typeof comments !== "undefined" ? comments : "some";
  112. condition.extract = false;
  113. } // Ensure that both conditions are functions
  114. ["preserve", "extract"].forEach(key => {
  115. /** @type {undefined | string} */
  116. let regexStr;
  117. /** @type {undefined | RegExp} */
  118. let regex;
  119. switch (typeof condition[key]) {
  120. case "boolean":
  121. condition[key] = condition[key] ? () => true : () => false;
  122. break;
  123. case "function":
  124. break;
  125. case "string":
  126. if (condition[key] === "all") {
  127. condition[key] = () => true;
  128. break;
  129. }
  130. if (condition[key] === "some") {
  131. condition[key] =
  132. /** @type {ExtractCommentsFunction} */
  133. (astNode, comment) => (comment.type === "comment2" || comment.type === "comment1") && /@preserve|@lic|@cc_on|^\**!/i.test(comment.value);
  134. break;
  135. }
  136. regexStr =
  137. /** @type {string} */
  138. condition[key];
  139. condition[key] =
  140. /** @type {ExtractCommentsFunction} */
  141. (astNode, comment) => new RegExp(
  142. /** @type {string} */
  143. regexStr).test(comment.value);
  144. break;
  145. default:
  146. regex =
  147. /** @type {RegExp} */
  148. condition[key];
  149. condition[key] =
  150. /** @type {ExtractCommentsFunction} */
  151. (astNode, comment) =>
  152. /** @type {RegExp} */
  153. regex.test(comment.value);
  154. }
  155. }); // Redefine the comments function to extract and preserve
  156. // comments according to the two conditions
  157. return (astNode, comment) => {
  158. if (
  159. /** @type {{ extract: ExtractCommentsFunction }} */
  160. condition.extract(astNode, comment)) {
  161. const commentText = comment.type === "comment2" ? `/*${comment.value}*/` : `//${comment.value}`; // Don't include duplicate comments
  162. if (!extractedComments.includes(commentText)) {
  163. extractedComments.push(commentText);
  164. }
  165. }
  166. return (
  167. /** @type {{ preserve: ExtractCommentsFunction }} */
  168. condition.preserve(astNode, comment)
  169. );
  170. };
  171. };
  172. /**
  173. * @param {PredefinedOptions & TerserOptions} [terserOptions={}]
  174. * @returns {TerserOptions & { sourceMap: undefined } & { compress: TerserCompressOptions } & ({ output: TerserFormatOptions & { beautify: boolean } } | { format: TerserFormatOptions & { beautify: boolean } })}
  175. */
  176. const buildTerserOptions = (terserOptions = {}) => {
  177. // Need deep copy objects to avoid https://github.com/terser/terser/issues/366
  178. return { ...terserOptions,
  179. compress: typeof terserOptions.compress === "boolean" ? terserOptions.compress ? {} : false : { ...terserOptions.compress
  180. },
  181. // ecma: terserOptions.ecma,
  182. // ie8: terserOptions.ie8,
  183. // keep_classnames: terserOptions.keep_classnames,
  184. // keep_fnames: terserOptions.keep_fnames,
  185. mangle: terserOptions.mangle == null ? true : typeof terserOptions.mangle === "boolean" ? terserOptions.mangle : { ...terserOptions.mangle
  186. },
  187. // module: terserOptions.module,
  188. // nameCache: { ...terserOptions.toplevel },
  189. // the `output` option is deprecated
  190. ...(terserOptions.format ? {
  191. format: {
  192. beautify: false,
  193. ...terserOptions.format
  194. }
  195. } : {
  196. output: {
  197. beautify: false,
  198. ...terserOptions.output
  199. }
  200. }),
  201. parse: { ...terserOptions.parse
  202. },
  203. // safari10: terserOptions.safari10,
  204. // Ignoring sourceMap from options
  205. // eslint-disable-next-line no-undefined
  206. sourceMap: undefined // toplevel: terserOptions.toplevel
  207. };
  208. }; // eslint-disable-next-line global-require
  209. const {
  210. minify
  211. } = require("terser"); // Copy `terser` options
  212. const terserOptions = buildTerserOptions(minimizerOptions); // Let terser generate a SourceMap
  213. if (sourceMap) {
  214. // @ts-ignore
  215. terserOptions.sourceMap = {
  216. asObject: true
  217. };
  218. }
  219. /** @type {ExtractedComments} */
  220. const extractedComments = [];
  221. if (terserOptions.output) {
  222. terserOptions.output.comments = buildComments(terserOptions, extractedComments);
  223. } else if (terserOptions.format) {
  224. terserOptions.format.comments = buildComments(terserOptions, extractedComments);
  225. }
  226. if (terserOptions.compress) {
  227. // More optimizations
  228. if (typeof terserOptions.compress.ecma === "undefined") {
  229. terserOptions.compress.ecma = terserOptions.ecma;
  230. } // https://github.com/webpack/webpack/issues/16135
  231. if (terserOptions.ecma === 5 && typeof terserOptions.compress.arrows === "undefined") {
  232. terserOptions.compress.arrows = false;
  233. }
  234. }
  235. const [[filename, code]] = Object.entries(input);
  236. const result = await minify({
  237. [filename]: code
  238. }, terserOptions);
  239. return {
  240. code:
  241. /** @type {string} **/
  242. result.code,
  243. // @ts-ignore
  244. // eslint-disable-next-line no-undefined
  245. map: result.map ?
  246. /** @type {SourceMapInput} **/
  247. result.map : undefined,
  248. extractedComments
  249. };
  250. }
  251. /**
  252. * @returns {string | undefined}
  253. */
  254. terserMinify.getMinimizerVersion = () => {
  255. let packageJson;
  256. try {
  257. // eslint-disable-next-line global-require
  258. packageJson = require("terser/package.json");
  259. } catch (error) {// Ignore
  260. }
  261. return packageJson && packageJson.version;
  262. };
  263. /* istanbul ignore next */
  264. /**
  265. * @param {Input} input
  266. * @param {SourceMapInput | undefined} sourceMap
  267. * @param {PredefinedOptions & CustomOptions} minimizerOptions
  268. * @param {ExtractCommentsOptions | undefined} extractComments
  269. * @return {Promise<MinimizedResult>}
  270. */
  271. async function uglifyJsMinify(input, sourceMap, minimizerOptions, extractComments) {
  272. /**
  273. * @param {any} value
  274. * @returns {boolean}
  275. */
  276. const isObject = value => {
  277. const type = typeof value;
  278. return value != null && (type === "object" || type === "function");
  279. };
  280. /**
  281. * @param {import("uglify-js").MinifyOptions & { sourceMap: undefined } & { output: import("uglify-js").OutputOptions & { beautify: boolean }}} uglifyJsOptions
  282. * @param {ExtractedComments} extractedComments
  283. * @returns {ExtractCommentsFunction}
  284. */
  285. const buildComments = (uglifyJsOptions, extractedComments) => {
  286. /** @type {{ [index: string]: ExtractCommentsCondition }} */
  287. const condition = {};
  288. const {
  289. comments
  290. } = uglifyJsOptions.output;
  291. condition.preserve = typeof comments !== "undefined" ? comments : false;
  292. if (typeof extractComments === "boolean" && extractComments) {
  293. condition.extract = "some";
  294. } else if (typeof extractComments === "string" || extractComments instanceof RegExp) {
  295. condition.extract = extractComments;
  296. } else if (typeof extractComments === "function") {
  297. condition.extract = extractComments;
  298. } else if (extractComments && isObject(extractComments)) {
  299. condition.extract = typeof extractComments.condition === "boolean" && extractComments.condition ? "some" : typeof extractComments.condition !== "undefined" ? extractComments.condition : "some";
  300. } else {
  301. // No extract
  302. // Preserve using "commentsOpts" or "some"
  303. condition.preserve = typeof comments !== "undefined" ? comments : "some";
  304. condition.extract = false;
  305. } // Ensure that both conditions are functions
  306. ["preserve", "extract"].forEach(key => {
  307. /** @type {undefined | string} */
  308. let regexStr;
  309. /** @type {undefined | RegExp} */
  310. let regex;
  311. switch (typeof condition[key]) {
  312. case "boolean":
  313. condition[key] = condition[key] ? () => true : () => false;
  314. break;
  315. case "function":
  316. break;
  317. case "string":
  318. if (condition[key] === "all") {
  319. condition[key] = () => true;
  320. break;
  321. }
  322. if (condition[key] === "some") {
  323. condition[key] =
  324. /** @type {ExtractCommentsFunction} */
  325. (astNode, comment) => (comment.type === "comment2" || comment.type === "comment1") && /@preserve|@lic|@cc_on|^\**!/i.test(comment.value);
  326. break;
  327. }
  328. regexStr =
  329. /** @type {string} */
  330. condition[key];
  331. condition[key] =
  332. /** @type {ExtractCommentsFunction} */
  333. (astNode, comment) => new RegExp(
  334. /** @type {string} */
  335. regexStr).test(comment.value);
  336. break;
  337. default:
  338. regex =
  339. /** @type {RegExp} */
  340. condition[key];
  341. condition[key] =
  342. /** @type {ExtractCommentsFunction} */
  343. (astNode, comment) =>
  344. /** @type {RegExp} */
  345. regex.test(comment.value);
  346. }
  347. }); // Redefine the comments function to extract and preserve
  348. // comments according to the two conditions
  349. return (astNode, comment) => {
  350. if (
  351. /** @type {{ extract: ExtractCommentsFunction }} */
  352. condition.extract(astNode, comment)) {
  353. const commentText = comment.type === "comment2" ? `/*${comment.value}*/` : `//${comment.value}`; // Don't include duplicate comments
  354. if (!extractedComments.includes(commentText)) {
  355. extractedComments.push(commentText);
  356. }
  357. }
  358. return (
  359. /** @type {{ preserve: ExtractCommentsFunction }} */
  360. condition.preserve(astNode, comment)
  361. );
  362. };
  363. };
  364. /**
  365. * @param {PredefinedOptions & import("uglify-js").MinifyOptions} [uglifyJsOptions={}]
  366. * @returns {import("uglify-js").MinifyOptions & { sourceMap: undefined } & { output: import("uglify-js").OutputOptions & { beautify: boolean }}}
  367. */
  368. const buildUglifyJsOptions = (uglifyJsOptions = {}) => {
  369. // eslint-disable-next-line no-param-reassign
  370. delete minimizerOptions.ecma; // eslint-disable-next-line no-param-reassign
  371. delete minimizerOptions.module; // Need deep copy objects to avoid https://github.com/terser/terser/issues/366
  372. return { ...uglifyJsOptions,
  373. // warnings: uglifyJsOptions.warnings,
  374. parse: { ...uglifyJsOptions.parse
  375. },
  376. compress: typeof uglifyJsOptions.compress === "boolean" ? uglifyJsOptions.compress : { ...uglifyJsOptions.compress
  377. },
  378. mangle: uglifyJsOptions.mangle == null ? true : typeof uglifyJsOptions.mangle === "boolean" ? uglifyJsOptions.mangle : { ...uglifyJsOptions.mangle
  379. },
  380. output: {
  381. beautify: false,
  382. ...uglifyJsOptions.output
  383. },
  384. // Ignoring sourceMap from options
  385. // eslint-disable-next-line no-undefined
  386. sourceMap: undefined // toplevel: uglifyJsOptions.toplevel
  387. // nameCache: { ...uglifyJsOptions.toplevel },
  388. // ie8: uglifyJsOptions.ie8,
  389. // keep_fnames: uglifyJsOptions.keep_fnames,
  390. };
  391. }; // eslint-disable-next-line global-require, import/no-extraneous-dependencies
  392. const {
  393. minify
  394. } = require("uglify-js"); // Copy `uglify-js` options
  395. const uglifyJsOptions = buildUglifyJsOptions(minimizerOptions); // Let terser generate a SourceMap
  396. if (sourceMap) {
  397. // @ts-ignore
  398. uglifyJsOptions.sourceMap = true;
  399. }
  400. /** @type {ExtractedComments} */
  401. const extractedComments = []; // @ts-ignore
  402. uglifyJsOptions.output.comments = buildComments(uglifyJsOptions, extractedComments);
  403. const [[filename, code]] = Object.entries(input);
  404. const result = await minify({
  405. [filename]: code
  406. }, uglifyJsOptions);
  407. return {
  408. code: result.code,
  409. // eslint-disable-next-line no-undefined
  410. map: result.map ? JSON.parse(result.map) : undefined,
  411. errors: result.error ? [result.error] : [],
  412. warnings: result.warnings || [],
  413. extractedComments
  414. };
  415. }
  416. /**
  417. * @returns {string | undefined}
  418. */
  419. uglifyJsMinify.getMinimizerVersion = () => {
  420. let packageJson;
  421. try {
  422. // eslint-disable-next-line global-require, import/no-extraneous-dependencies
  423. packageJson = require("uglify-js/package.json");
  424. } catch (error) {// Ignore
  425. }
  426. return packageJson && packageJson.version;
  427. };
  428. /* istanbul ignore next */
  429. /**
  430. * @param {Input} input
  431. * @param {SourceMapInput | undefined} sourceMap
  432. * @param {PredefinedOptions & CustomOptions} minimizerOptions
  433. * @return {Promise<MinimizedResult>}
  434. */
  435. async function swcMinify(input, sourceMap, minimizerOptions) {
  436. /**
  437. * @param {PredefinedOptions & import("@swc/core").JsMinifyOptions} [swcOptions={}]
  438. * @returns {import("@swc/core").JsMinifyOptions & { sourceMap: undefined } & { compress: import("@swc/core").TerserCompressOptions }}
  439. */
  440. const buildSwcOptions = (swcOptions = {}) => {
  441. // Need deep copy objects to avoid https://github.com/terser/terser/issues/366
  442. return { ...swcOptions,
  443. compress: typeof swcOptions.compress === "boolean" ? swcOptions.compress ? {} : false : { ...swcOptions.compress
  444. },
  445. mangle: swcOptions.mangle == null ? true : typeof swcOptions.mangle === "boolean" ? swcOptions.mangle : { ...swcOptions.mangle
  446. },
  447. // ecma: swcOptions.ecma,
  448. // keep_classnames: swcOptions.keep_classnames,
  449. // keep_fnames: swcOptions.keep_fnames,
  450. // module: swcOptions.module,
  451. // safari10: swcOptions.safari10,
  452. // toplevel: swcOptions.toplevel
  453. // eslint-disable-next-line no-undefined
  454. sourceMap: undefined
  455. };
  456. }; // eslint-disable-next-line import/no-extraneous-dependencies, global-require
  457. const swc = require("@swc/core"); // Copy `swc` options
  458. const swcOptions = buildSwcOptions(minimizerOptions); // Let `swc` generate a SourceMap
  459. if (sourceMap) {
  460. // @ts-ignore
  461. swcOptions.sourceMap = true;
  462. }
  463. if (swcOptions.compress) {
  464. // More optimizations
  465. if (typeof swcOptions.compress.ecma === "undefined") {
  466. swcOptions.compress.ecma = swcOptions.ecma;
  467. } // https://github.com/webpack/webpack/issues/16135
  468. if (swcOptions.ecma === 5 && typeof swcOptions.compress.arrows === "undefined") {
  469. swcOptions.compress.arrows = false;
  470. }
  471. }
  472. const [[filename, code]] = Object.entries(input);
  473. const result = await swc.minify(code, swcOptions);
  474. let map;
  475. if (result.map) {
  476. map = JSON.parse(result.map); // TODO workaround for swc because `filename` is not preset as in `swc` signature as for `terser`
  477. map.sources = [filename];
  478. delete map.sourcesContent;
  479. }
  480. return {
  481. code: result.code,
  482. map
  483. };
  484. }
  485. /**
  486. * @returns {string | undefined}
  487. */
  488. swcMinify.getMinimizerVersion = () => {
  489. let packageJson;
  490. try {
  491. // eslint-disable-next-line global-require, import/no-extraneous-dependencies
  492. packageJson = require("@swc/core/package.json");
  493. } catch (error) {// Ignore
  494. }
  495. return packageJson && packageJson.version;
  496. };
  497. /* istanbul ignore next */
  498. /**
  499. * @param {Input} input
  500. * @param {SourceMapInput | undefined} sourceMap
  501. * @param {PredefinedOptions & CustomOptions} minimizerOptions
  502. * @return {Promise<MinimizedResult>}
  503. */
  504. async function esbuildMinify(input, sourceMap, minimizerOptions) {
  505. /**
  506. * @param {PredefinedOptions & import("esbuild").TransformOptions} [esbuildOptions={}]
  507. * @returns {import("esbuild").TransformOptions}
  508. */
  509. const buildEsbuildOptions = (esbuildOptions = {}) => {
  510. // eslint-disable-next-line no-param-reassign
  511. delete esbuildOptions.ecma;
  512. if (esbuildOptions.module) {
  513. // eslint-disable-next-line no-param-reassign
  514. esbuildOptions.format = "esm";
  515. } // eslint-disable-next-line no-param-reassign
  516. delete esbuildOptions.module; // Need deep copy objects to avoid https://github.com/terser/terser/issues/366
  517. return {
  518. minify: true,
  519. legalComments: "inline",
  520. ...esbuildOptions,
  521. sourcemap: false
  522. };
  523. }; // eslint-disable-next-line import/no-extraneous-dependencies, global-require
  524. const esbuild = require("esbuild"); // Copy `esbuild` options
  525. const esbuildOptions = buildEsbuildOptions(minimizerOptions); // Let `esbuild` generate a SourceMap
  526. if (sourceMap) {
  527. esbuildOptions.sourcemap = true;
  528. esbuildOptions.sourcesContent = false;
  529. }
  530. const [[filename, code]] = Object.entries(input);
  531. esbuildOptions.sourcefile = filename;
  532. const result = await esbuild.transform(code, esbuildOptions);
  533. return {
  534. code: result.code,
  535. // eslint-disable-next-line no-undefined
  536. map: result.map ? JSON.parse(result.map) : undefined,
  537. warnings: result.warnings.length > 0 ? result.warnings.map(item => {
  538. return {
  539. name: "Warning",
  540. source: item.location && item.location.file,
  541. line: item.location && item.location.line,
  542. column: item.location && item.location.column,
  543. plugin: item.pluginName,
  544. message: `${item.text}${item.detail ? `\nDetails:\n${item.detail}` : ""}${item.notes.length > 0 ? `\n\nNotes:\n${item.notes.map(note => `${note.location ? `[${note.location.file}:${note.location.line}:${note.location.column}] ` : ""}${note.text}${note.location ? `\nSuggestion: ${note.location.suggestion}` : ""}${note.location ? `\nLine text:\n${note.location.lineText}\n` : ""}`).join("\n")}` : ""}`
  545. };
  546. }) : []
  547. };
  548. }
  549. /**
  550. * @returns {string | undefined}
  551. */
  552. esbuildMinify.getMinimizerVersion = () => {
  553. let packageJson;
  554. try {
  555. // eslint-disable-next-line global-require, import/no-extraneous-dependencies
  556. packageJson = require("esbuild/package.json");
  557. } catch (error) {// Ignore
  558. }
  559. return packageJson && packageJson.version;
  560. };
  561. module.exports = {
  562. throttleAll,
  563. terserMinify,
  564. uglifyJsMinify,
  565. swcMinify,
  566. esbuildMinify
  567. };