diff --git a/package-lock.json b/package-lock.json index 0593a79..dea3f8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2132,6 +2132,21 @@ "fastq": "^1.6.0" } }, + "@npmcli/move-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", + "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", + "requires": { + "mkdirp": "^1.0.4" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, "@sinonjs/commons": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", @@ -2595,7 +2610,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", - "dev": true, "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -3079,8 +3093,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -3205,7 +3218,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3402,51 +3414,67 @@ "dev": true }, "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz", + "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==", "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" }, "dependencies": { "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "requires": { - "yallist": "^3.0.2" + "yallist": "^4.0.0" } }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "requires": { - "glob": "^7.1.3" + "aggregate-error": "^3.0.0" + } + }, + "tar": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.2.tgz", + "integrity": "sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.0", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" } }, "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -3592,10 +3620,9 @@ } }, "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, "chrome-trace-event": { "version": "1.0.2", @@ -3648,8 +3675,7 @@ "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" }, "cli-cursor": { "version": "3.1.0", @@ -3983,8 +4009,7 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" }, "compare-func": { "version": "1.3.4", @@ -4028,8 +4053,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "2.0.0", @@ -6879,69 +6903,35 @@ } }, "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", "requires": { "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" }, "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "requires": { - "p-limit": "^2.0.0" - }, - "dependencies": { - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - } + "semver": "^6.0.0" } }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "requires": { - "find-up": "^3.0.0" + "find-up": "^4.0.0" } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -6949,7 +6939,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -7070,6 +7059,14 @@ "universalify": "^0.1.0" } }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, "fs-monkey": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz", @@ -7097,8 +7094,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.2.13", @@ -7577,7 +7573,6 @@ "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -8092,8 +8087,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "in-publish": { "version": "2.0.1", @@ -8104,8 +8098,7 @@ "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" }, "indexes-of": { "version": "1.0.1", @@ -8115,14 +8108,12 @@ "infer-owner": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -8131,8 +8122,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.5", @@ -10073,7 +10063,6 @@ "version": "26.1.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.1.0.tgz", "integrity": "sha512-Z9P5pZ6UC+kakMbNJn+tA2RdVdNX5WH1x+5UCBZ9MxIK24pjYtFt96fK+UwBTrjLYm232g1xz0L3eTh51OW+yQ==", - "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" @@ -10082,14 +10071,12 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -10681,7 +10668,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -11161,8 +11147,7 @@ "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, "merge2": { "version": "1.4.1", @@ -11289,7 +11274,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -11318,6 +11302,61 @@ } } }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.3.tgz", + "integrity": "sha512-cFOknTvng5vqnwOpDsZTWhNll6Jf8o2x+/diplafmxpuIymAjzoOolZG0VvQf3V2HgqzJNhnuKHYp2BqDgz8IQ==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz", + "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, "mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -12148,7 +12187,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -12234,7 +12272,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" }, @@ -12243,7 +12280,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "requires": { "p-try": "^2.0.0" } @@ -12390,14 +12426,12 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "2.0.1", @@ -13120,8 +13154,7 @@ "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" }, "prompts": { "version": "2.3.2", @@ -13271,7 +13304,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, "requires": { "safe-buffer": "^5.1.0" } @@ -13701,7 +13733,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -13749,8 +13780,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -14010,10 +14040,9 @@ "dev": true }, "serialize-javascript": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", - "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", "requires": { "randombytes": "^2.1.0" } @@ -14422,12 +14451,11 @@ } }, "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", + "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", "requires": { - "figgy-pudding": "^3.5.1" + "minipass": "^3.1.1" } }, "stable": { @@ -14893,12 +14921,122 @@ "worker-farm": "^1.7.0" }, "dependencies": { + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", @@ -14909,6 +15047,30 @@ "ajv-errors": "^1.0.0", "ajv-keywords": "^3.1.0" } + }, + "serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true } } }, @@ -15219,7 +15381,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, "requires": { "unique-slug": "^2.0.0" } @@ -15228,7 +15389,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, "requires": { "imurmurhash": "^0.1.4" } @@ -15871,8 +16031,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "1.0.3", diff --git a/package.json b/package.json index 75379d0..3b1ac92 100644 --- a/package.json +++ b/package.json @@ -41,9 +41,13 @@ "webpack": "^4.0.0 || ^5.0.0" }, "dependencies": { + "cacache": "^15.0.5", "cssnano": "^4.1.10", + "find-cache-dir": "^3.3.1", + "jest-worker": "^26.1.0", "p-limit": "^3.0.2", "schema-utils": "^2.7.0", + "serialize-javascript": "^4.0.0", "source-map": "^0.6.1", "webpack-sources": "^1.4.3" }, diff --git a/src/Webpack4Cache.js b/src/Webpack4Cache.js new file mode 100644 index 0000000..8313c9e --- /dev/null +++ b/src/Webpack4Cache.js @@ -0,0 +1,35 @@ +import os from 'os'; + +import cacache from 'cacache'; +import findCacheDir from 'find-cache-dir'; +import serialize from 'serialize-javascript'; + +export default class Webpack4Cache { + constructor(compilation, options) { + this.cacheDir = + options.cache === true + ? Webpack4Cache.getCacheDirectory() + : options.cache; + } + + static getCacheDirectory() { + return findCacheDir({ name: 'cssnano-webpack-plugin' }) || os.tmpdir(); + } + + isEnabled() { + return Boolean(this.cacheDir); + } + + get(task) { + // eslint-disable-next-line no-param-reassign + task.cacheIdent = task.cacheIdent || serialize(task.cacheKeys); + + return cacache + .get(this.cacheDir, task.cacheIdent) + .then(({ data }) => JSON.parse(data)); + } + + store(task, data) { + return cacache.put(this.cacheDir, task.cacheIdent, JSON.stringify(data)); + } +} diff --git a/src/Webpack5Cache.js b/src/Webpack5Cache.js new file mode 100644 index 0000000..941e0fd --- /dev/null +++ b/src/Webpack5Cache.js @@ -0,0 +1,75 @@ +// eslint-disable-next-line import/extensions,import/no-unresolved +import getLazyHashedEtag from 'webpack/lib/cache/getLazyHashedEtag'; +import serialize from 'serialize-javascript'; + +import { util } from 'webpack'; + +export default class Cache { + // eslint-disable-next-line no-unused-vars + constructor(compilation, ignored) { + this.compilation = compilation; + } + + isEnabled() { + return Boolean(this.compilation.cache); + } + + createCacheIdent(task) { + const { + outputOptions: { hashSalt, hashDigest, hashDigestLength, hashFunction }, + } = this.compilation; + + const hash = util.createHash(hashFunction); + + if (hashSalt) { + hash.update(hashSalt); + } + + hash.update(serialize(task.cacheKeys)); + + const digest = hash.digest(hashDigest); + const cacheKeys = digest.substr(0, hashDigestLength); + + return `${this.compilation.compilerPath}/CssnanoWebpackPlugin/${cacheKeys}/${task.file}`; + } + + get(task) { + // eslint-disable-next-line no-param-reassign + task.cacheIdent = task.cacheIdent || this.createCacheIdent(task); + // eslint-disable-next-line no-param-reassign + task.cacheETag = task.cacheETag || getLazyHashedEtag(task.assetSource); + + return new Promise((resolve, reject) => { + this.compilation.cache.get( + task.cacheIdent, + task.cacheETag, + (err, result) => { + if (err) { + reject(err); + } else if (result) { + resolve(result); + } else { + reject(); + } + } + ); + }); + } + + store(task, data) { + return new Promise((resolve, reject) => { + this.compilation.cache.store( + task.cacheIdent, + task.cacheETag, + data, + (err) => { + if (err) { + reject(err); + } else { + resolve(data); + } + } + ); + }); + } +} diff --git a/src/index.js b/src/index.js index 88e6c89..139cf21 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,25 @@ -import cssnano from 'cssnano'; +import os from 'os'; + import pLimit from 'p-limit'; import RequestShortener from 'webpack/lib/RequestShortener'; -import { ModuleFilenameHelpers } from 'webpack'; +import cssnanoPackageJson from 'cssnano/package.json'; +import { + util, + ModuleFilenameHelpers, + SourceMapDevToolPlugin, + javascript, + version as webpackVersion, +} from 'webpack'; import { SourceMapSource, RawSource } from 'webpack-sources'; import { SourceMapConsumer } from 'source-map'; import validateOptions from 'schema-utils'; +import serialize from 'serialize-javascript'; +import Worker from 'jest-worker'; import schema from './options.json'; +import { minify as minifyFn } from './minify'; + const warningRegex = /\[.+:([0-9]+),([0-9]+)\]/; class CssnanoPlugin { @@ -17,16 +29,31 @@ class CssnanoPlugin { baseDataPath: 'options', }); - this.options = Object.assign( - { - test: /\.css(\?.*)?$/i, - sourceMap: false, - cssnanoOptions: { - preset: 'default', - }, + const { + test = /\.css(\?.*)?$/i, + sourceMap = false, + cssnanoOptions = { + preset: 'default', }, - options - ); + warningsFilter = () => true, + cache = true, + cacheKeys = (defaultCacheKeys) => defaultCacheKeys, + parallel = true, + include, + exclude, + } = options; + + this.options = { + test, + sourceMap, + cssnanoOptions, + warningsFilter, + cache, + cacheKeys, + parallel, + include, + exclude, + }; if (this.options.sourceMap === true) { this.options.sourceMap = { inline: false }; @@ -53,6 +80,45 @@ class CssnanoPlugin { return new SourceMapConsumer(inputSourceMap); } + static buildError(error, file, sourceMap, requestShortener) { + if (error.line) { + const original = + sourceMap && + sourceMap.originalPositionFor({ + line: error.line, + column: error.col, + }); + + if (original && original.source && requestShortener) { + return new Error( + `${file} from Cssnano Webpack Plugin\n${ + error.message + } [${requestShortener.shorten(original.source)}:${original.line},${ + original.column + }][${file}:${error.line},${error.col}]${ + error.stack + ? `\n${error.stack.split('\n').slice(1).join('\n')}` + : '' + }` + ); + } + + return new Error( + `${file} from Cssnano Webpack Plugin\n${error.message} [${file}:${ + error.line + },${error.col}]${ + error.stack ? `\n${error.stack.split('\n').slice(1).join('\n')}` : '' + }` + ); + } + + if (error.stack) { + return new Error(`${file} from Cssnano Webpack Plugin\n${error.stack}`); + } + + return new Error(`${file} from Cssnano Webpack Plugin\n${error.message}`); + } + static buildWarning( warning, file, @@ -98,6 +164,20 @@ class CssnanoPlugin { return `Cssnano Webpack Plugin: ${warningMessage}${locationMessage}`; } + static isWebpack4() { + return webpackVersion[0] === '4'; + } + + static getAvailableNumberOfCores(parallel) { + // In some cases cpus() returns undefined + // https://github.com/nodejs/node/issues/19022 + const cpus = os.cpus() || { length: 1 }; + + return parallel === true + ? cpus.length - 1 + : Math.min(Number(parallel) || 0, cpus.length - 1); + } + *taskGenerator(compiler, compilation, file) { const assetSource = compilation.assets[file]; @@ -126,9 +206,12 @@ class CssnanoPlugin { inputSourceMap = null; } + if (Buffer.isBuffer(input)) { + input = input.toString(); + } + const callback = (taskResult) => { - const { css: code } = taskResult; - const { error, map, warnings } = taskResult; + const { css: code, error, map, warnings } = taskResult; let sourceMap = null; @@ -157,7 +240,7 @@ class CssnanoPlugin { outputSource = new SourceMapSource( code, file, - JSON.parse(map), + map, input, inputSourceMap, true @@ -192,7 +275,7 @@ class CssnanoPlugin { if (inputSourceMap) { postcssOptions.map = Object.assign( - { prev: inputSourceMap || false }, + { prev: inputSourceMap }, this.options.sourceMap ); } @@ -206,30 +289,107 @@ class CssnanoPlugin { callback, }; + if (CssnanoPlugin.isWebpack4()) { + const { + outputOptions: { hashSalt, hashDigest, hashDigestLength, hashFunction }, + } = compilation; + const hash = util.createHash(hashFunction); + + if (hashSalt) { + hash.update(hashSalt); + } + + hash.update(input); + + const digest = hash.digest(hashDigest); + + if (this.options.cache) { + const defaultCacheKeys = { + cssnano: cssnanoPackageJson.version, + // eslint-disable-next-line global-require + 'cssnano-webpack-plugin': require('../package.json').version, + 'cssnano-webpack-plugin-options': this.options, + nodeVersion: process.version, + filename: file, + contentHash: digest.substr(0, hashDigestLength), + }; + + task.cacheKeys = this.options.cacheKeys(defaultCacheKeys, file); + } + } else { + // For webpack@5 cache + task.assetSource = assetSource; + + task.cacheKeys = { + cssnano: cssnanoPackageJson.version, + // eslint-disable-next-line global-require + 'cssnano-webpack-plugin': require('../package.json').version, + 'cssnano-webpack-plugin-options': this.options, + }; + } + yield task; } // eslint-disable-next-line class-methods-use-this - async runTasks(assetNames, getTaskForAsset) { - const limit = pLimit(100); + async runTasks(assetNames, getTaskForAsset, cache) { + const availableNumberOfCores = CssnanoPlugin.getAvailableNumberOfCores( + this.options.parallel + ); + + let concurrency = Infinity; + let worker; + + if (availableNumberOfCores > 0) { + // Do not create unnecessary workers when the number of files is less than the available cores, it saves memory + const numWorkers = Math.min(assetNames.length, availableNumberOfCores); + + concurrency = numWorkers; + + worker = new Worker(require.resolve('./minify'), { numWorkers }); + + // https://github.com/facebook/jest/issues/8872#issuecomment-524822081 + const workerStdout = worker.getStdout(); + + if (workerStdout) { + workerStdout.on('data', (chunk) => { + return process.stdout.write(chunk); + }); + } + + const workerStderr = worker.getStderr(); + + if (workerStderr) { + workerStderr.on('data', (chunk) => { + return process.stderr.write(chunk); + }); + } + } + + const limit = pLimit(concurrency); const scheduledTasks = []; for (const assetName of assetNames) { const enqueue = async (task) => { - const { input, postcssOptions, cssnanoOptions } = task; - let taskResult; try { - taskResult = await cssnano.process( - input, - postcssOptions, - cssnanoOptions - ); + if (worker) { + taskResult = await worker.transform(serialize(task)); + } else { + taskResult = minifyFn(task); + } } catch (error) { taskResult = { error }; } + if (cache.isEnabled() && !taskResult.error) { + taskResult = await cache.store(task, taskResult).then( + () => taskResult, + () => taskResult + ); + } + task.callback(taskResult); return taskResult; @@ -244,17 +404,47 @@ class CssnanoPlugin { return Promise.resolve(); } + if (cache.isEnabled()) { + return cache.get(task).then( + (taskResult) => task.callback(taskResult), + () => enqueue(task) + ); + } + return enqueue(task); }) ); } return Promise.all(scheduledTasks).then(() => { + if (worker) { + return worker.end(); + } + return Promise.resolve(); }); } apply(compiler) { + const { devtool, plugins } = compiler.options; + + this.options.sourceMap = + typeof this.options.sourceMap === 'undefined' + ? (devtool && + !devtool.includes('eval') && + !devtool.includes('cheap') && + (devtool.includes('source-map') || + // Todo remove when `webpack@4` support will be dropped + devtool.includes('sourcemap'))) || + (plugins && + plugins.some( + (plugin) => + plugin instanceof SourceMapDevToolPlugin && + plugin.options && + plugin.options.columns + )) + : this.options.sourceMap; + const matchObject = ModuleFilenameHelpers.matchObject.bind( // eslint-disable-next-line no-undefined undefined, @@ -262,15 +452,27 @@ class CssnanoPlugin { ); const optimizeFn = async (compilation, chunksOrAssets) => { - const assetNames = [] - .concat(Array.from(compilation.additionalChunkAssets || [])) - .concat( - Array.from(chunksOrAssets).reduce( - (acc, chunk) => acc.concat(Array.from(chunk.files || [])), - [] + let assetNames; + + if (CssnanoPlugin.isWebpack4()) { + assetNames = [] + .concat(Array.from(compilation.additionalChunkAssets || [])) + .concat( + Array.from(chunksOrAssets).reduce( + (acc, chunk) => acc.concat(Array.from(chunk.files || [])), + [] + ) ) - ) - .filter((file) => matchObject(file)); + .filter((file) => matchObject(file)); + } else { + assetNames = [] + .concat(Object.keys(chunksOrAssets)) + .filter((file) => matchObject(file)); + } + + if (assetNames.length === 0) { + return Promise.resolve(); + } const getTaskForAsset = this.taskGenerator.bind( this, @@ -278,17 +480,67 @@ class CssnanoPlugin { compilation ); - await this.runTasks(assetNames, getTaskForAsset); + const CacheEngine = CssnanoPlugin.isWebpack4() + ? // eslint-disable-next-line global-require + require('./Webpack4Cache').default + : // eslint-disable-next-line global-require + require('./Webpack5Cache').default; + const cache = new CacheEngine(compilation, { cache: this.options.cache }); + + await this.runTasks(assetNames, getTaskForAsset, cache); return Promise.resolve(); }; const plugin = { name: this.constructor.name }; + compiler.hooks.compilation.tap(plugin, (compilation) => { - compilation.hooks.optimizeChunkAssets.tapPromise( - plugin, - optimizeFn.bind(this, compilation) - ); + if (this.options.sourceMap) { + compilation.hooks.buildModule.tap(plugin, (moduleArg) => { + // to get detailed location info about errors + // eslint-disable-next-line no-param-reassign + moduleArg.useSourceMap = true; + }); + } + + if (CssnanoPlugin.isWebpack4()) { + const { mainTemplate, chunkTemplate } = compilation; + const data = serialize({ + cssnano: cssnanoPackageJson.version, + cssnanoOptions: this.options.cssnanoOptions, + }); + + // Regenerate `contenthash` for minified assets + for (const template of [mainTemplate, chunkTemplate]) { + template.hooks.hashForChunk.tap(plugin, (hash) => { + hash.update('CssnanoPlugin'); + hash.update(data); + }); + } + + compilation.hooks.optimizeChunkAssets.tapPromise( + plugin, + optimizeFn.bind(this, compilation) + ); + } else { + const hooks = javascript.JavascriptModulesPlugin.getCompilationHooks( + compilation + ); + const data = serialize({ + cssnano: cssnanoPackageJson.version, + cssnanoOptions: this.options.cssnanoOptions, + }); + + hooks.chunkHash.tap(plugin, (chunk, hash) => { + hash.update('CssnanoPlugin'); + hash.update(data); + }); + + compilation.hooks.optimizeAssets.tapPromise( + plugin, + optimizeFn.bind(this, compilation) + ); + } }); } } diff --git a/src/minify.js b/src/minify.js new file mode 100644 index 0000000..c09f6a0 --- /dev/null +++ b/src/minify.js @@ -0,0 +1,32 @@ +const cssnano = require('cssnano'); + +const minify = (options) => { + const { input, postcssOptions, cssnanoOptions } = options; + + return cssnano.process(input, postcssOptions, cssnanoOptions); +}; + +function transform(options) { + // 'use strict' => this === undefined (Clean Scope) + // Safer for possible security issues, albeit not critical at all here + // eslint-disable-next-line no-new-func, no-param-reassign + options = new Function( + 'exports', + 'require', + 'module', + '__filename', + '__dirname', + `'use strict'\nreturn ${options}` + )(exports, require, module, __filename, __dirname); + + const result = minify(options); + + if (result.error) { + throw result.error; + } else { + return result; + } +} + +module.exports.minify = minify; +module.exports.transform = transform; diff --git a/src/options.json b/src/options.json index ff042ec..d55c583 100644 --- a/src/options.json +++ b/src/options.json @@ -77,6 +77,36 @@ "description": "Options for `cssnanoOptions`.", "additionalProperties": true, "type": "object" + }, + "cache": { + "description": "Enable file caching. Ignored in webpack 5, for webpack 5 please use https://webpack.js.org/configuration/other-options/#cache.", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "cacheKeys": { + "description": "Allows you to override default cache keys. Ignored in webpack 5, for webpack 5 please use https://webpack.js.org/configuration/other-options/#cache.", + "instanceof": "Function" + }, + "parallel": { + "description": "Use multi-process parallel running to improve the build speed.", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "integer" + } + ] + }, + "warningsFilter": { + "description": "Allow to filter `cssnano` warnings.", + "instanceof": "Function" } }, "additionalProperties": false diff --git a/test/CssNano.test.js b/test/CssNano.test.js index d3bdc01..014fea5 100644 --- a/test/CssNano.test.js +++ b/test/CssNano.test.js @@ -4,11 +4,15 @@ import CssnanoPlugin from '../src/index'; import { createCompiler, compile } from './compiler'; -import { readAsset, normalizedSourceMap } from './helpers'; +import { readAsset, normalizedSourceMap, removeCache } from './helpers'; describe('CssnanoPlugin', () => { jest.setTimeout(30000); + beforeEach(() => Promise.all([removeCache()])); + + afterEach(() => Promise.all([removeCache()])); + it('should work with assets using querystring', () => { const config = { devtool: 'source-map', diff --git a/test/__snapshots__/validate-options.test.js.snap b/test/__snapshots__/validate-options.test.js.snap index 7089c5a..a403fdf 100644 --- a/test/__snapshots__/validate-options.test.js.snap +++ b/test/__snapshots__/validate-options.test.js.snap @@ -110,7 +110,43 @@ exports[`validation 8`] = ` `; exports[`validation 9`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.cache should be one of these: + boolean | string + -> Enable file caching. Ignored in webpack 5, for webpack 5 please use https://webpack.js.org/configuration/other-options/#cache. + Details: + * options.cache should be a boolean. + * options.cache should be a string." +`; + +exports[`validation 10`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.cacheKeys should be an instance of function. + -> Allows you to override default cache keys. Ignored in webpack 5, for webpack 5 please use https://webpack.js.org/configuration/other-options/#cache." +`; + +exports[`validation 11`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.parallel should be one of these: + boolean | integer + -> Use multi-process parallel running to improve the build speed. + Details: + * options.parallel should be a boolean. + * options.parallel should be a integer." +`; + +exports[`validation 12`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.parallel should be one of these: + boolean | integer + -> Use multi-process parallel running to improve the build speed. + Details: + * options.parallel should be a boolean. + * options.parallel should be a integer." +`; + +exports[`validation 13`] = ` "Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. - options has an unknown property 'unknown'. These properties are valid: - object { test?, include?, exclude?, sourceMap?, cssnanoOptions? }" + object { test?, include?, exclude?, sourceMap?, cssnanoOptions?, cache?, cacheKeys?, parallel?, warningsFilter? }" `; diff --git a/test/cssnanoOptions-option.test.js b/test/cssnanoOptions-option.test.js index 454ea0b..97f22ad 100644 --- a/test/cssnanoOptions-option.test.js +++ b/test/cssnanoOptions-option.test.js @@ -2,11 +2,15 @@ import CssnanoPlugin from '../src/index'; import { createCompiler, compile } from './compiler'; -import { readAsset } from './helpers'; +import { readAsset, removeCache } from './helpers'; describe('when applied with "cssnanoOptions" option', () => { jest.setTimeout(30000); + beforeEach(() => Promise.all([removeCache()])); + + afterEach(() => Promise.all([removeCache()])); + it('matches snapshot for "discardComments" option (enable [default])', () => { const compiler = createCompiler({ entry: { diff --git a/test/helpers/getCacheDirectory.js b/test/helpers/getCacheDirectory.js new file mode 100644 index 0000000..fc04188 --- /dev/null +++ b/test/helpers/getCacheDirectory.js @@ -0,0 +1,9 @@ +import os from 'os'; + +import findCacheDir from 'find-cache-dir'; + +function getCacheDirectory() { + return findCacheDir({ name: 'cssnano-webpack-plugin' }) || os.tmpdir(); +} + +export default getCacheDirectory; diff --git a/test/helpers/index.js b/test/helpers/index.js index 15bbc92..1ea198f 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -1,4 +1,5 @@ import readAsset from './readAsset'; import normalizedSourceMap from './normalizedSourceMap'; +import removeCache from './removeCache'; -export { readAsset, normalizedSourceMap }; +export { readAsset, normalizedSourceMap, removeCache }; diff --git a/test/helpers/removeCache.js b/test/helpers/removeCache.js new file mode 100644 index 0000000..05c2f81 --- /dev/null +++ b/test/helpers/removeCache.js @@ -0,0 +1,11 @@ +import cacache from 'cacache'; + +import getCacheDirectory from './getCacheDirectory'; + +async function removeCache(cacheDirectory) { + const cacheDir = cacheDirectory || getCacheDirectory(); + + return cacache.rm.all(cacheDir); +} + +export default removeCache; diff --git a/test/sourceMap-option.test.js b/test/sourceMap-option.test.js index 0910fb2..33cf124 100644 --- a/test/sourceMap-option.test.js +++ b/test/sourceMap-option.test.js @@ -6,7 +6,7 @@ import CssnanoPlugin from '../src/index'; import { createCompiler, compile } from './compiler'; -import { readAsset, normalizedSourceMap } from './helpers'; +import { readAsset, normalizedSourceMap, removeCache } from './helpers'; describe('when applied with "sourceMap" option', () => { jest.setTimeout(30000); @@ -32,6 +32,10 @@ describe('when applied with "sourceMap" option', () => { ], }; + beforeEach(() => Promise.all([removeCache()])); + + afterEach(() => Promise.all([removeCache()])); + it('matches snapshot for "false" value, without previous sourcemap', () => { const compiler = createCompiler(baseConfig); new CssnanoPlugin().apply(compiler); diff --git a/test/test-option.test.js b/test/test-option.test.js index 4f3920c..7afa5f2 100644 --- a/test/test-option.test.js +++ b/test/test-option.test.js @@ -2,13 +2,15 @@ import CssnanoPlugin from '../src/index'; import { createCompiler, compile } from './compiler'; -import { readAsset } from './helpers'; +import { readAsset, removeCache } from './helpers'; describe('when applied with "test" option', () => { jest.setTimeout(30000); let compiler; beforeEach(() => { + Promise.all([removeCache()]); + compiler = createCompiler({ entry: { bar1: `${__dirname}/fixtures/test/bar1.css`, @@ -18,6 +20,8 @@ describe('when applied with "test" option', () => { }); }); + afterEach(() => Promise.all([removeCache()])); + it('matches snapshot with empty value', () => { new CssnanoPlugin().apply(compiler); diff --git a/test/validate-options.test.js b/test/validate-options.test.js index 4e8718c..f80ed74 100644 --- a/test/validate-options.test.js +++ b/test/validate-options.test.js @@ -128,6 +128,50 @@ it('validation', () => { }); }).not.toThrow(); + expect(() => { + new CssnanoWebpackPlugin({ cache: true }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ cache: false }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ cache: 'path/to/cache/directory' }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ cache: {} }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ cacheKeys() {} }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ cacheKeys: 'test' }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ parallel: true }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ parallel: false }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ parallel: 2 }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ parallel: '2' }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ parallel: {} }); + }).toThrowErrorMatchingSnapshot(); + expect(() => { new CssnanoWebpackPlugin({ unknown: true }); }).toThrowErrorMatchingSnapshot();