From b281538544b9497330c57464eaa68d19f34726dd Mon Sep 17 00:00:00 2001 From: Brandon Doran Date: Tue, 6 Oct 2020 21:55:44 -0700 Subject: [PATCH 1/8] http lift and shift Minimal code changes to migrate away from request module and callback apis. * Replaces request module with node-fetch. * BREAKING CHANGE: drop support for Tapable.plugin. Plugini will no longer work in webpack v3. >= v4 will be required. * Use Tapable.hooks.tapPromise instead of tapAsync * Convert code to use async/await instead of callbacks * Update tests to reflect async methods as opposed to callbacks --- package-lock.json | 122 +++++++++++----- package.json | 4 +- src/RollbarSourceMapPlugin.js | 98 ++++++------- test/RollbarSourceMapPlugin.test.js | 219 +++++++++++----------------- 4 files changed, 221 insertions(+), 222 deletions(-) diff --git a/package-lock.json b/package-lock.json index 92d5dca..84a7ff0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4570,6 +4570,7 @@ "version": "6.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4692,6 +4693,7 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, "requires": { "safer-buffer": "~2.1.0" } @@ -4699,7 +4701,8 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true }, "assign-symbols": { "version": "1.0.0", @@ -4713,11 +4716,6 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", @@ -4739,12 +4737,14 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true }, "aws4": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", + "dev": true }, "babel-jest": { "version": "26.3.0", @@ -4950,6 +4950,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, "requires": { "tweetnacl": "^0.14.3" } @@ -5090,7 +5091,8 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true }, "chalk": { "version": "2.4.2", @@ -5514,6 +5516,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -5671,6 +5674,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -6353,7 +6357,8 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true }, "extend-shallow": { "version": "3.0.2", @@ -6444,12 +6449,14 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true }, "fast-deep-equal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true }, "fast-diff": { "version": "1.2.0", @@ -6460,7 +6467,8 @@ "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true }, "fast-levenshtein": { "version": "2.0.6", @@ -6594,15 +6602,16 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, @@ -6693,6 +6702,7 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -6756,12 +6766,14 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true }, "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -6873,6 +6885,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -7421,7 +7434,8 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "is-windows": { "version": "1.0.2", @@ -7460,7 +7474,8 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true }, "istanbul-lib-coverage": { "version": "3.0.0", @@ -10883,7 +10898,8 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true }, "jsdom": { "version": "16.4.0", @@ -10940,12 +10956,14 @@ "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -10956,7 +10974,8 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true }, "json5": { "version": "2.1.1", @@ -10979,6 +10998,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -11679,8 +11699,7 @@ "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-int64": { "version": "0.4.0", @@ -11802,7 +11821,8 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true }, "object-copy": { "version": "0.1.0", @@ -12064,7 +12084,8 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true }, "picomatch": { "version": "2.2.1", @@ -12250,7 +12271,8 @@ "psl": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", - "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", + "dev": true }, "pump": { "version": "3.0.0", @@ -12265,12 +12287,14 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true }, "react-is": { "version": "16.13.1", @@ -12432,6 +12456,7 @@ "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -12455,15 +12480,28 @@ "uuid": "^3.3.2" }, "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "safe-buffer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, "requires": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -12612,7 +12650,8 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true }, "safe-regex": { "version": "1.1.0", @@ -12626,7 +12665,8 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "sane": { "version": "4.1.0", @@ -12985,6 +13025,7 @@ "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -13437,6 +13478,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -13444,7 +13486,8 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true }, "type-check": { "version": "0.3.2", @@ -13567,6 +13610,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, "requires": { "punycode": "^2.1.0" }, @@ -13574,7 +13618,8 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true } } }, @@ -13606,7 +13651,8 @@ "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true }, "v8-compile-cache": { "version": "2.1.1", diff --git a/package.json b/package.json index 36201e0..27167a0 100644 --- a/package.json +++ b/package.json @@ -61,12 +61,12 @@ "watch": "^1.0.1" }, "dependencies": { - "async": "^3.1.1", + "form-data": "^3.0.0", "lodash.find": "^4.3.0", "lodash.isfunction": "^3.0.9", "lodash.isstring": "^4.0.1", "lodash.reduce": "^4.3.0", - "request": "^2.88.0", + "node-fetch": "^2.6.1", "verror": "^1.6.1" }, "peerDependencies": { diff --git a/src/RollbarSourceMapPlugin.js b/src/RollbarSourceMapPlugin.js index bacf824..85569d3 100644 --- a/src/RollbarSourceMapPlugin.js +++ b/src/RollbarSourceMapPlugin.js @@ -1,9 +1,9 @@ -import async from 'async'; -import request from 'request'; -import VError from 'verror'; +import fetch from 'node-fetch'; +import FormData from 'form-data'; import find from 'lodash.find'; -import reduce from 'lodash.reduce'; import isString from 'lodash.isstring'; +import reduce from 'lodash.reduce'; +import VError from 'verror'; import { handleError, validateOptions } from './helpers'; import { ROLLBAR_ENDPOINT } from './constants'; @@ -28,28 +28,30 @@ class RollbarSourceMapPlugin { this.encodeFilename = encodeFilename; } - afterEmit(compilation, cb) { + async afterEmit(compilation) { const errors = validateOptions(this); if (errors) { compilation.errors.push(...handleError(errors)); - return cb(); + return; } - this.uploadSourceMaps(compilation, err => { - if (err) { - if (!this.ignoreErrors) { - compilation.errors.push(...handleError(err)); - } else if (!this.silent) { - compilation.warnings.push(...handleError(err)); - } + try { + await this.uploadSourceMaps(compilation); + } catch (err) { + if (!this.ignoreErrors) { + compilation.errors.push(...handleError(err)); + } else if (!this.silent) { + compilation.warnings.push(...handleError(err)); } - cb(); - }); + } } apply(compiler) { - compiler.hooks.afterEmit.tapAsync('after-emit', this.afterEmit.bind(this)); + compiler.hooks.afterEmit.tapPromise( + 'after-emit', + this.afterEmit.bind(this) + ); } getAssets(compilation) { @@ -91,32 +93,9 @@ class RollbarSourceMapPlugin { return this.publicPath(sourceFile); } - uploadSourceMap(compilation, { sourceFile, sourceMap }, cb) { - const req = request.post(this.rollbarEndpoint, (err, res, body) => { - if (!err && res.statusCode === 200) { - if (!this.silent) { - // eslint-disable-next-line no-console - console.info(`Uploaded ${sourceMap} to Rollbar`); - } - return cb(); - } - - const errMessage = `failed to upload ${sourceMap} to Rollbar`; - if (err) { - return cb(new VError(err, errMessage)); - } - - try { - const { message } = JSON.parse(body); - return cb( - new Error(message ? `${errMessage}: ${message}` : errMessage) - ); - } catch (_err) { - return cb(new Error(errMessage)); - } - }); + async uploadSourceMap(compilation, { sourceFile, sourceMap }) { + const form = new FormData(); - const form = req.form(); form.append('access_token', this.accessToken); form.append('version', this.version); form.append('minified_url', this.getPublicPath(sourceFile)); @@ -124,22 +103,43 @@ class RollbarSourceMapPlugin { filename: sourceMap, contentType: 'application/json' }); + + try { + const res = await fetch(this.rollbarEndpoint, { + method: 'POST', + body: form + }); + if (res.ok) { + if (!this.silent) { + // eslint-disable-next-line no-console + console.info(`Uploaded ${sourceMap} to Rollbar`); + } + return; + } + + let message; + try { + const text = await res.text(); + ({ message } = JSON.parse(text)); + } catch (_parseErr) { + // Error parsing response + } + throw new Error(message); + } catch (err) { + throw new VError(err, `failed to upload ${sourceMap} to Rollbar`); + } } - uploadSourceMaps(compilation, cb) { + uploadSourceMaps(compilation) { const assets = this.getAssets(compilation); - const upload = this.uploadSourceMap.bind(this, compilation); /* istanbul ignore if */ if (assets.length > 0) { process.stdout.write('\n'); } - async.each(assets, upload, (err, results) => { - if (err) { - return cb(err); - } - return cb(null, results); - }); + return Promise.all( + assets.map(asset => this.uploadSourceMap(compilation, asset)) + ); } } diff --git a/test/RollbarSourceMapPlugin.test.js b/test/RollbarSourceMapPlugin.test.js index 08b8672..0e002d0 100644 --- a/test/RollbarSourceMapPlugin.test.js +++ b/test/RollbarSourceMapPlugin.test.js @@ -13,7 +13,7 @@ describe('RollbarSourceMapPlugin', () => { plugin: jest.fn(), hooks: { afterEmit: { - tapAsync: jest.fn() + tapPromise: jest.fn() } }, resolvers: { @@ -91,7 +91,7 @@ describe('RollbarSourceMapPlugin', () => { describe('apply', () => { it('hooks into "after-emit"', () => { plugin.apply(compiler); - expect(compiler.hooks.afterEmit.tapAsync).toHaveBeenCalledWith( + expect(compiler.hooks.afterEmit.tapPromise).toHaveBeenCalledWith( 'after-emit', expect.any(Function) ); @@ -104,27 +104,25 @@ describe('RollbarSourceMapPlugin', () => { beforeEach(() => { uploadSourceMaps = jest .spyOn(plugin, 'uploadSourceMaps') - .mockImplementation((_compilation, callback) => callback()); + .mockImplementation(() => {}); }); - it('calls uploadSourceMaps', done => { + it('calls uploadSourceMaps', async () => { const compilation = { errors: [], warnings: [] }; - plugin.afterEmit(compilation, () => { - expect(uploadSourceMaps).toHaveBeenCalledTimes(1); - expect(compilation.errors.length).toBe(0); - expect(compilation.warnings.length).toBe(0); - done(); - }); + await plugin.afterEmit(compilation); + expect(uploadSourceMaps).toHaveBeenCalledTimes(1); + expect(compilation.errors.length).toBe(0); + expect(compilation.warnings.length).toBe(0); }); it( 'adds upload warnings to compilation warnings, ' + 'if ignoreErrors is true and silent is false', - done => { + async () => { const compilation = { errors: [], warnings: [] @@ -134,18 +132,17 @@ describe('RollbarSourceMapPlugin', () => { plugin.silent = false; uploadSourceMaps = jest .spyOn(plugin, 'uploadSourceMaps') - .mockImplementation((_compilation, callback) => callback(err)); - plugin.afterEmit(compilation, () => { - expect(uploadSourceMaps).toHaveBeenCalledTimes(1); - expect(compilation.errors.length).toBe(0); - expect(compilation.warnings.length).toBe(1); - expect(compilation.warnings[0].cause()).toBe(err); - done(); - }); + .mockImplementation(() => { + throw err; + }); + await plugin.afterEmit(compilation); + expect(uploadSourceMaps).toHaveBeenCalledTimes(1); + expect(compilation.errors.length).toBe(0); + expect(compilation.warnings.length).toBe(1); } ); - it('does not add upload errors to compilation warnings if silent is true', done => { + it('does not add upload errors to compilation warnings if silent is true', async () => { const compilation = { errors: [], warnings: [] @@ -155,16 +152,16 @@ describe('RollbarSourceMapPlugin', () => { plugin.silent = true; uploadSourceMaps = jest .spyOn(plugin, 'uploadSourceMaps') - .mockImplementation((_comp, callback) => callback(err)); - plugin.afterEmit(compilation, () => { - expect(uploadSourceMaps).toHaveBeenCalledTimes(1); - expect(compilation.errors.length).toBe(0); - expect(compilation.warnings.length).toBe(0); - done(); - }); + .mockImplementation(() => { + throw err; + }); + await plugin.afterEmit(compilation); + expect(uploadSourceMaps).toHaveBeenCalledTimes(1); + expect(compilation.errors.length).toBe(0); + expect(compilation.warnings.length).toBe(0); }); - it('adds upload errors to compilation errors', done => { + it('adds upload errors to compilation errors', async () => { const compilation = { errors: [], warnings: [] @@ -173,17 +170,17 @@ describe('RollbarSourceMapPlugin', () => { plugin.ignoreErrors = false; uploadSourceMaps = jest .spyOn(plugin, 'uploadSourceMaps') - .mockImplementationOnce((_comp, callback) => callback(err)); - plugin.afterEmit(compilation, () => { - expect(uploadSourceMaps).toHaveBeenCalledTimes(1); - expect(compilation.warnings.length).toBe(0); - expect(compilation.errors.length).toBe(1); - expect(compilation.errors[0].cause()).toBe(err); - done(); - }); + .mockImplementationOnce(() => { + throw err; + }); + await plugin.afterEmit(compilation); + expect(uploadSourceMaps).toHaveBeenCalledTimes(1); + expect(compilation.warnings.length).toBe(0); + expect(compilation.errors.length).toBe(1); + expect(compilation.errors[0].cause()).toBe(err); }); - it('adds validation errors to compilation', done => { + it('adds validation errors to compilation', async () => { const compilation = { errors: [], warnings: [] @@ -194,11 +191,9 @@ describe('RollbarSourceMapPlugin', () => { publicPath: 'https://my.cdn.net/assets' }); - plugin.afterEmit(compilation, () => { - expect(uploadSourceMaps).not.toHaveBeenCalled(); - expect(compilation.errors.length).toBe(1); - done(); - }); + await plugin.afterEmit(compilation); + expect(uploadSourceMaps).not.toHaveBeenCalled(); + expect(compilation.errors.length).toBe(1); }); }); @@ -382,45 +377,34 @@ describe('RollbarSourceMapPlugin', () => { getAssets = jest.spyOn(plugin, 'getAssets').mockReturnValueOnce(assets); uploadSourceMap = jest .spyOn(plugin, 'uploadSourceMap') - .mockImplementation((_comp, _chunk, callback) => callback()); + .mockImplementation(() => {}); }); - it('calls uploadSourceMap for each chunk', done => { - plugin.uploadSourceMaps(compilation, err => { - if (err) { - return done(err); - } - expect(getAssets).toHaveBeenCalledTimes(1); - expect(compilation.errors.length).toBe(0); - expect(uploadSourceMap).toHaveBeenCalledTimes(2); + it('calls uploadSourceMap for each chunk', async () => { + await plugin.uploadSourceMaps(compilation); + expect(getAssets).toHaveBeenCalledTimes(1); + expect(compilation.errors.length).toBe(0); + expect(uploadSourceMap).toHaveBeenCalledTimes(2); - expect(uploadSourceMap).toHaveBeenNthCalledWith( - 1, - { name: 'test', errors: [] }, - { sourceFile: 'vendor.5190.js', sourceMap: 'vendor.5190.js.map' }, - expect.any(Function) - ); + expect(uploadSourceMap).toHaveBeenNthCalledWith( + 1, + { name: 'test', errors: [] }, + { sourceFile: 'vendor.5190.js', sourceMap: 'vendor.5190.js.map' } + ); - expect(uploadSourceMap).toHaveBeenNthCalledWith( - 2, - { name: 'test', errors: [] }, - { sourceFile: 'app.81c1.js', sourceMap: 'app.81c1.js.map' }, - expect.any(Function) - ); - done(); - }); + expect(uploadSourceMap).toHaveBeenNthCalledWith( + 2, + { name: 'test', errors: [] }, + { sourceFile: 'app.81c1.js', sourceMap: 'app.81c1.js.map' } + ); }); - it('calls err-back if uploadSourceMap errors', done => { - const error = new Error(); + it('throws if uploadSourceMap errors', async () => { + const err = new Error(); uploadSourceMap = jest .spyOn(plugin, 'uploadSourceMap') - .mockImplementationOnce((_comp, _chunk, callback) => callback(error)); - plugin.uploadSourceMaps(compilation, (err, result) => { - expect(err).toBe(error); - expect(result).toBe(undefined); - done(); - }); + .mockRejectedValueOnce(err); + await expect(plugin.uploadSourceMaps(compilation)).rejects.toThrow(err); }); }); @@ -444,57 +428,42 @@ describe('RollbarSourceMapPlugin', () => { }; }); - it('callback without err param if upload is success', done => { + it('logs to console if upload is success', async () => { info = jest.spyOn(console, 'info').mockImplementation(); const scope = nock('https://api.rollbar.com:443') // eslint-disable-line no-unused-vars .post('/api/1/sourcemap') .reply(200, JSON.stringify({ err: 0, result: 'master-latest-sha' })); - plugin.uploadSourceMap(compilation, chunk, err => { - if (err) { - return done(err); - } - expect(info).toHaveBeenCalledWith( - 'Uploaded vendor.5190.js.map to Rollbar' - ); - done(); - }); + await plugin.uploadSourceMap(compilation, chunk); + expect(info).toHaveBeenCalledWith( + 'Uploaded vendor.5190.js.map to Rollbar' + ); }); - it('does not log upload to console if silent option is true', done => { + it('does not log upload to console if silent option is true', async () => { info = jest.spyOn(console, 'info').mockImplementation(); const scope = nock('https://api.rollbar.com:443') // eslint-disable-line no-unused-vars .post('/api/1/sourcemap') .reply(200, JSON.stringify({ err: 0, result: 'master-latest-sha' })); plugin.silent = true; - plugin.uploadSourceMap(compilation, chunk, err => { - if (err) { - return done(err); - } - expect(info).not.toHaveBeenCalled(); - done(); - }); + await plugin.uploadSourceMap(compilation, chunk); + expect(info).not.toHaveBeenCalled(); }); - it('logs upload to console if silent option is false', done => { + it('logs upload to console if silent option is false', async () => { const scope = nock('https://api.rollbar.com:443') // eslint-disable-line no-unused-vars .post('/api/1/sourcemap') .reply(200, JSON.stringify({ err: 0, result: 'master-latest-sha' })); plugin.silent = false; - plugin.uploadSourceMap(compilation, chunk, err => { - if (err) { - return done(err); - } - expect(info).toHaveBeenCalledWith( - 'Uploaded vendor.5190.js.map to Rollbar' - ); - done(); - }); + await plugin.uploadSourceMap(compilation, chunk); + expect(info).toHaveBeenCalledWith( + 'Uploaded vendor.5190.js.map to Rollbar' + ); }); - it('returns error message if failure response includes message', done => { + it('returns error message if failure response includes message', async () => { const scope = nock('https://api.rollbar.com:443') // eslint-disable-line no-unused-vars .post('/api/1/sourcemap') .reply( @@ -502,55 +471,39 @@ describe('RollbarSourceMapPlugin', () => { JSON.stringify({ err: 1, message: 'missing source_map file upload' }) ); - plugin.uploadSourceMap(compilation, chunk, err => { - expect(err).toBeInstanceOf(Error); - expect(err.message).toBe( - 'failed to upload vendor.5190.js.map to Rollbar: missing source_map file upload' - ); - done(); - }); + await expect(plugin.uploadSourceMap(compilation, chunk)).rejects.toThrow( + 'failed to upload vendor.5190.js.map to Rollbar: missing source_map file upload' + ); }); - it('returns generic error message if response body does not have message', done => { + it('returns generic error message if response body does not have message', async () => { const scope = nock('https://api.rollbar.com:443') // eslint-disable-line no-unused-vars .post('/api/1/sourcemap') .reply(422, JSON.stringify({ err: 1 })); - plugin.uploadSourceMap(compilation, chunk, err => { - expect(err).toBeInstanceOf(Error); - expect(err.message).toBe( - 'failed to upload vendor.5190.js.map to Rollbar' - ); - done(); - }); + await expect(plugin.uploadSourceMap(compilation, chunk)).rejects.toThrow( + 'failed to upload vendor.5190.js.map to Rollbar' + ); }); - it('handles error response with empty body', done => { + it('handles error response with empty body', async () => { const scope = nock('https://api.rollbar.com:443') // eslint-disable-line no-unused-vars .post('/api/1/sourcemap') .reply(422, null); - plugin.uploadSourceMap(compilation, chunk, err => { - expect(err).toBeInstanceOf(Error); - expect(err.message).toBe( - 'failed to upload vendor.5190.js.map to Rollbar' - ); - done(); - }); + await expect(plugin.uploadSourceMap(compilation, chunk)).rejects.toThrow( + 'failed to upload vendor.5190.js.map to Rollbar' + ); }); - it('handles HTTP request error', done => { + it('handles HTTP request error', async () => { const scope = nock('https://api.rollbar.com:443') // eslint-disable-line no-unused-vars .post('/api/1/sourcemap') .replyWithError('something awful happened'); - plugin.uploadSourceMap(compilation, chunk, err => { - expect(err).toBeInstanceOf(Error); - expect(err.message).toBe( - 'failed to upload vendor.5190.js.map to Rollbar: something awful happened' - ); - done(); - }); + await expect(plugin.uploadSourceMap(compilation, chunk)).rejects.toThrow( + `failed to upload vendor.5190.js.map to Rollbar: request to ${ROLLBAR_ENDPOINT} failed, reason: something awful happened` + ); }); }); }); From 0050e0f038393add1c946fd763280324ea22656d Mon Sep 17 00:00:00 2001 From: Brandon Doran Date: Tue, 6 Oct 2020 22:08:43 -0700 Subject: [PATCH 2/8] Remove lodash reduce and find and use native array methods --- package-lock.json | 10 --------- package.json | 2 -- src/RollbarSourceMapPlugin.js | 42 +++++++++++++++-------------------- 3 files changed, 18 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84a7ff0..6e7352f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11327,11 +11327,6 @@ "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, - "lodash.find": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", - "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=" - }, "lodash.isfunction": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", @@ -11342,11 +11337,6 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, - "lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" - }, "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", diff --git a/package.json b/package.json index 27167a0..a6b098a 100644 --- a/package.json +++ b/package.json @@ -62,10 +62,8 @@ }, "dependencies": { "form-data": "^3.0.0", - "lodash.find": "^4.3.0", "lodash.isfunction": "^3.0.9", "lodash.isstring": "^4.0.1", - "lodash.reduce": "^4.3.0", "node-fetch": "^2.6.1", "verror": "^1.6.1" }, diff --git a/src/RollbarSourceMapPlugin.js b/src/RollbarSourceMapPlugin.js index 85569d3..469db12 100644 --- a/src/RollbarSourceMapPlugin.js +++ b/src/RollbarSourceMapPlugin.js @@ -1,8 +1,6 @@ import fetch from 'node-fetch'; import FormData from 'form-data'; -import find from 'lodash.find'; import isString from 'lodash.isstring'; -import reduce from 'lodash.reduce'; import VError from 'verror'; import { handleError, validateOptions } from './helpers'; import { ROLLBAR_ENDPOINT } from './constants'; @@ -58,31 +56,27 @@ class RollbarSourceMapPlugin { const { includeChunks, encodeFilename } = this; const { chunks } = compilation.getStats().toJson(); - return reduce( - chunks, - (result, chunk) => { - const chunkName = chunk.names[0]; - if (includeChunks.length && includeChunks.indexOf(chunkName) === -1) { - return result; - } + return chunks.reduce((result, chunk) => { + const chunkName = chunk.names[0]; + if (includeChunks.length && includeChunks.indexOf(chunkName) === -1) { + return result; + } - const sourceFile = find(chunk.files, file => /\.js$/.test(file)); - const sourceMap = find(chunk.files, file => /\.js\.map$/.test(file)); + const sourceFile = chunk.files.find(file => /\.js$/.test(file)); + const sourceMap = chunk.files.find(file => /\.js\.map$/.test(file)); - if (!sourceFile || !sourceMap) { - return result; - } + if (!sourceFile || !sourceMap) { + return result; + } - return [ - ...result, - { - sourceFile: encodeFilename ? encodeURI(sourceFile) : sourceFile, - sourceMap - } - ]; - }, - [] - ); + return [ + ...result, + { + sourceFile: encodeFilename ? encodeURI(sourceFile) : sourceFile, + sourceMap + } + ]; + }, []); } getPublicPath(sourceFile) { From cbed39e0fd0c95e7b796181ca4aaf83e3389b11d Mon Sep 17 00:00:00 2001 From: Brandon Doran Date: Thu, 8 Oct 2020 13:39:05 -0700 Subject: [PATCH 3/8] Add optional chaining and nullish coalescing support --- .eslintrc.js | 1 + package-lock.json | 31 +++++++++++++++++++++++++++++++ package.json | 3 +++ 3 files changed, 35 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index dc20477..3fa6e0b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,6 @@ module.exports = { extends: ['airbnb-base', 'plugin:prettier/recommended'], + parser: 'babel-eslint', plugins: ['prettier'], env: { jest: true, diff --git a/package-lock.json b/package-lock.json index 6e7352f..9eb6f2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4746,6 +4746,37 @@ "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", "dev": true }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, "babel-jest": { "version": "26.3.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.3.0.tgz", diff --git a/package.json b/package.json index a6b098a..db00ab5 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,10 @@ "devDependencies": { "@babel/cli": "^7.11.6", "@babel/core": "^7.11.6", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", + "@babel/plugin-proposal-optional-chaining": "^7.11.0", "@babel/preset-env": "^7.11.5", + "babel-eslint": "^10.1.0", "babel-jest": "^26.0.1", "codecov": "^3.7.2", "cross-env": "^7.0.2", From b1c391234add18ca237fa175c7b2bc4f07dfd462 Mon Sep 17 00:00:00 2001 From: Brandon Doran Date: Thu, 8 Oct 2020 14:05:28 -0700 Subject: [PATCH 4/8] Improve and cleanup of file upload error handling --- src/RollbarSourceMapPlugin.js | 41 +++++++++++++++++------------ test/RollbarSourceMapPlugin.test.js | 16 ++++++++--- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/RollbarSourceMapPlugin.js b/src/RollbarSourceMapPlugin.js index 469db12..a5c5e2c 100644 --- a/src/RollbarSourceMapPlugin.js +++ b/src/RollbarSourceMapPlugin.js @@ -88,6 +88,7 @@ class RollbarSourceMapPlugin { } async uploadSourceMap(compilation, { sourceFile, sourceMap }) { + const errMessage = `failed to upload ${sourceMap} to Rollbar`; const form = new FormData(); form.append('access_token', this.accessToken); @@ -98,36 +99,42 @@ class RollbarSourceMapPlugin { contentType: 'application/json' }); + let res; try { - const res = await fetch(this.rollbarEndpoint, { + res = await fetch(this.rollbarEndpoint, { method: 'POST', body: form }); - if (res.ok) { - if (!this.silent) { - // eslint-disable-next-line no-console - console.info(`Uploaded ${sourceMap} to Rollbar`); - } - return; - } + } catch (err) { + // Network or operational errors + throw new VError(err, errMessage); + } - let message; + // 4xx or 5xx response + if (!res.ok) { + // Attempt to parse error details from response + let details; try { - const text = await res.text(); - ({ message } = JSON.parse(text)); - } catch (_parseErr) { - // Error parsing response + const body = await res.json(); + details = body?.message ?? `${res.status} - ${res.statusText}`; + } catch (parseErr) { + details = `${res.status} - ${res.statusText}`; } - throw new Error(message); - } catch (err) { - throw new VError(err, `failed to upload ${sourceMap} to Rollbar`); + + throw new Error(`${errMessage}: ${details}`); + } + + // Success + if (!this.silent) { + // eslint-disable-next-line no-console + console.info(`Uploaded ${sourceMap} to Rollbar`); } } uploadSourceMaps(compilation) { const assets = this.getAssets(compilation); - /* istanbul ignore if */ + /* istanbul ignore next */ if (assets.length > 0) { process.stdout.write('\n'); } diff --git a/test/RollbarSourceMapPlugin.test.js b/test/RollbarSourceMapPlugin.test.js index 0e002d0..5779e31 100644 --- a/test/RollbarSourceMapPlugin.test.js +++ b/test/RollbarSourceMapPlugin.test.js @@ -476,13 +476,13 @@ describe('RollbarSourceMapPlugin', () => { ); }); - it('returns generic error message if response body does not have message', async () => { + it('returns response status text if response body does not have message', async () => { const scope = nock('https://api.rollbar.com:443') // eslint-disable-line no-unused-vars .post('/api/1/sourcemap') .reply(422, JSON.stringify({ err: 1 })); await expect(plugin.uploadSourceMap(compilation, chunk)).rejects.toThrow( - 'failed to upload vendor.5190.js.map to Rollbar' + 'failed to upload vendor.5190.js.map to Rollbar: 422 - Unprocessable Entity' ); }); @@ -492,7 +492,17 @@ describe('RollbarSourceMapPlugin', () => { .reply(422, null); await expect(plugin.uploadSourceMap(compilation, chunk)).rejects.toThrow( - 'failed to upload vendor.5190.js.map to Rollbar' + 'failed to upload vendor.5190.js.map to Rollbar: 422 - Unprocessable Entity' + ); + }); + + it('handles error response with body not in JSON format', async () => { + const scope = nock('https://api.rollbar.com:443') // eslint-disable-line no-unused-vars + .post('/api/1/sourcemap') + .reply(422, ''); + + await expect(plugin.uploadSourceMap(compilation, chunk)).rejects.toThrow( + 'failed to upload vendor.5190.js.map to Rollbar: 422 - Unprocessable Entity' ); }); From 1319399afa9a4f22d12bc14998f69f7b3fd9d50c Mon Sep 17 00:00:00 2001 From: Brandon Doran Date: Thu, 8 Oct 2020 14:10:26 -0700 Subject: [PATCH 5/8] Migrate helpers to optional chaining syntax --- src/helpers.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/helpers.js b/src/helpers.js index fc18876..4fdd6cb 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -20,8 +20,7 @@ export function validateOptions(ref) { const errors = ROLLBAR_REQ_FIELDS.reduce((result, field) => { if ( field === 'publicPath' && - ref && - ref[field] && + ref?.[field] && !isString(ref[field]) && !isFunction(ref[field]) ) { @@ -33,7 +32,7 @@ export function validateOptions(ref) { ]; } - if (ref && ref[field]) { + if (ref?.[field]) { return result; } From 83e3253ac78391daae7b49c00c25cf17de3488f5 Mon Sep 17 00:00:00 2001 From: Brandon Doran Date: Fri, 9 Oct 2020 14:42:49 -0700 Subject: [PATCH 6/8] Add note to readme about only Webpack 4 being required --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f0de5eb..120843c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ You may be doing this now in a shell script, triggered during your deploy proces that makes curl posts to the Rollbar API. This can be finicky and error prone to setup. RollbarSourceMapPlugin aims to remove that burden and automatically upload the sourcemaps when they are emitted by webpack. +## Prerequisites + +**As of version 3.0.0, Webpack 4 is required. This plugin is no longer compatible with Webpack 3 and older.** + ## Installation Install the plugin with npm: From 3384d4804da1c01887c233b857350b87b1d81754 Mon Sep 17 00:00:00 2001 From: Brandon Doran Date: Fri, 9 Oct 2020 14:47:17 -0700 Subject: [PATCH 7/8] Move plugin name to constants and for after-emit hook --- src/RollbarSourceMapPlugin.js | 7 ++----- src/constants.js | 2 ++ src/helpers.js | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/RollbarSourceMapPlugin.js b/src/RollbarSourceMapPlugin.js index a5c5e2c..fd3a275 100644 --- a/src/RollbarSourceMapPlugin.js +++ b/src/RollbarSourceMapPlugin.js @@ -3,7 +3,7 @@ import FormData from 'form-data'; import isString from 'lodash.isstring'; import VError from 'verror'; import { handleError, validateOptions } from './helpers'; -import { ROLLBAR_ENDPOINT } from './constants'; +import { PLUGIN_NAME, ROLLBAR_ENDPOINT } from './constants'; class RollbarSourceMapPlugin { constructor({ @@ -46,10 +46,7 @@ class RollbarSourceMapPlugin { } apply(compiler) { - compiler.hooks.afterEmit.tapPromise( - 'after-emit', - this.afterEmit.bind(this) - ); + compiler.hooks.afterEmit.tapPromise(PLUGIN_NAME, this.afterEmit.bind(this)); } getAssets(compilation) { diff --git a/src/constants.js b/src/constants.js index 7826ac6..ba4dd8b 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,3 +1,5 @@ +export const PLUGIN_NAME = 'RollbarSourceMapPlugin'; + export const ROLLBAR_ENDPOINT = 'https://api.rollbar.com/api/1/sourcemap'; export const ROLLBAR_REQ_FIELDS = ['accessToken', 'version', 'publicPath']; diff --git a/src/helpers.js b/src/helpers.js index 4fdd6cb..5fd87c4 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,11 +1,11 @@ import VError from 'verror'; import isFunction from 'lodash.isfunction'; import isString from 'lodash.isstring'; -import { ROLLBAR_REQ_FIELDS } from './constants'; +import { PLUGIN_NAME, ROLLBAR_REQ_FIELDS } from './constants'; // Take a single Error or array of Errors and return an array of errors that // have message prefixed. -export function handleError(err, prefix = 'RollbarSourceMapPlugin') { +export function handleError(err, prefix = PLUGIN_NAME) { if (!err) { return []; } From 1b531159b1c7ea0991c60aeccfb5b0e281d7ba27 Mon Sep 17 00:00:00 2001 From: Brandon Doran Date: Fri, 9 Oct 2020 15:52:32 -0700 Subject: [PATCH 8/8] Fix test --- test/RollbarSourceMapPlugin.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/RollbarSourceMapPlugin.test.js b/test/RollbarSourceMapPlugin.test.js index 5779e31..390eb6f 100644 --- a/test/RollbarSourceMapPlugin.test.js +++ b/test/RollbarSourceMapPlugin.test.js @@ -1,6 +1,6 @@ import nock from 'nock'; import RollbarSourceMapPlugin from '../src/RollbarSourceMapPlugin'; -import { ROLLBAR_ENDPOINT } from '../src/constants'; +import { PLUGIN_NAME, ROLLBAR_ENDPOINT } from '../src/constants'; describe('RollbarSourceMapPlugin', () => { let compiler; @@ -92,7 +92,7 @@ describe('RollbarSourceMapPlugin', () => { it('hooks into "after-emit"', () => { plugin.apply(compiler); expect(compiler.hooks.afterEmit.tapPromise).toHaveBeenCalledWith( - 'after-emit', + PLUGIN_NAME, expect.any(Function) ); });