From 2be7f3f7ad78cd9ac3966762bd24325b8168a4ca Mon Sep 17 00:00:00 2001 From: David Sheldrick Date: Sun, 28 Feb 2021 14:22:21 +0000 Subject: [PATCH] add create-issue feature --- README.md | 5 + .../__snapshots__/create-issue.test.ts.snap | 62 ++++ .../create-issue/create-issue.sh | 20 ++ .../create-issue/create-issue.test.ts | 5 + integration-tests/create-issue/open.mock.js | 7 + integration-tests/create-issue/package.json | 13 + integration-tests/create-issue/yarn.lock | 264 ++++++++++++++++++ .../__snapshots__/happy-path-npm.test.ts.snap | 5 + .../happy-path-yarn.test.ts.snap | 5 + ...res-scripts-when-making-patch.test.ts.snap | 1 + .../__snapshots__/lerna-canary.test.ts.snap | 5 + .../nested-packages.test.ts.snap | 5 + .../nested-scoped-packages.test.ts.snap | 5 + package.json | 3 +- src/createIssue.ts | 104 +++++++ src/index.ts | 8 + src/makePatch.ts | 40 ++- yarn.lock | 10 +- 18 files changed, 555 insertions(+), 12 deletions(-) create mode 100644 integration-tests/create-issue/__snapshots__/create-issue.test.ts.snap create mode 100755 integration-tests/create-issue/create-issue.sh create mode 100644 integration-tests/create-issue/create-issue.test.ts create mode 100644 integration-tests/create-issue/open.mock.js create mode 100644 integration-tests/create-issue/package.json create mode 100644 integration-tests/create-issue/yarn.lock create mode 100644 src/createIssue.ts diff --git a/README.md b/README.md index 49761623..ba3bae74 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,11 @@ team. #### Options +- `--create-issue` + + For packages whose source is hosted on GitHub this option opens a web browser + with a draft issue based on your diff. + - `--use-yarn` By default, patch-package checks whether you use npm or yarn based on which diff --git a/integration-tests/create-issue/__snapshots__/create-issue.test.ts.snap b/integration-tests/create-issue/__snapshots__/create-issue.test.ts.snap new file mode 100644 index 00000000..8766cb6f --- /dev/null +++ b/integration-tests/create-issue/__snapshots__/create-issue.test.ts.snap @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Test create-issue: patching left-pad prompts to submit an issue 1`] = ` +"SNAPSHOT: patching left-pad prompts to submit an issue +patch-package 0.0.0 +• Creating temporary folder +• Installing left-pad@1.3.0 with yarn +• Diffing your files with clean files +✔ Created file patches/left-pad+1.3.0.patch + +💡 left-pad is on GitHub! To draft an issue based on your patch run + + yarn patch-package left-pad --create-issue + +END SNAPSHOT" +`; + +exports[`Test create-issue: patching left-pad with --create-issue opens the url 1`] = ` +"SNAPSHOT: patching left-pad with --create-issue opens the url +patch-package 0.0.0 +• Creating temporary folder +• Installing left-pad@1.3.0 with yarn +• Diffing your files with clean files +✔ Created file patches/left-pad+1.3.0.patch + +MOCK opening https://github.com/stevemao/left-pad/issues/new +MOCK title [Replace me] +MOCK body Hi! 👋 + +Firstly, thanks for your work on this project! 🙂 + +Today I used [patch-package](https://github.com/ds300/patch-package) to patch \`left-pad\` for the project I'm working on because [Insert reason here]. + +Here is the diff that solved my problem: + +\`\`\`diff +diff --git a/node_modules/left-pad/index.js b/node_modules/left-pad/index.js +index e90aec3..cbca95b 100644 +--- a/node_modules/left-pad/index.js ++++ b/node_modules/left-pad/index.js +@@ -4,7 +4,7 @@ + * To Public License, Version 2, as published by Sam Hocevar. See + * http://www.wtfpl.net/ for more details. */ + 'use strict'; +-module.exports = leftPad; ++module.exports = patch-package; + + var cache = [ + '', +@@ -19,7 +19,7 @@ var cache = [ + ' ' + ]; + +-function leftPad (str, len, ch) { ++function patch-package (str, len, ch) { + // convert \`str\` to a \`string\` + str = str + ''; + // \`len\` is the \`pad\`'s length now +\`\`\` + +END SNAPSHOT" +`; diff --git a/integration-tests/create-issue/create-issue.sh b/integration-tests/create-issue/create-issue.sh new file mode 100755 index 00000000..884162ca --- /dev/null +++ b/integration-tests/create-issue/create-issue.sh @@ -0,0 +1,20 @@ +# make sure errors stop the script +set -e + +echo "add patch-package" +yarn add $1 +alias patch-package=./node_modules/.bin/patch-package + +echo "modify left-pad" +npx replace leftPad patch-package node_modules/left-pad/index.js + +echo "SNAPSHOT: patching left-pad prompts to submit an issue" +patch-package left-pad +echo "END SNAPSHOT" + +echo "mock open" +cp open.mock.js node_modules/open/index.js + +echo "SNAPSHOT: patching left-pad with --create-issue opens the url" +patch-package left-pad --create-issue +echo "END SNAPSHOT" diff --git a/integration-tests/create-issue/create-issue.test.ts b/integration-tests/create-issue/create-issue.test.ts new file mode 100644 index 00000000..1e15d034 --- /dev/null +++ b/integration-tests/create-issue/create-issue.test.ts @@ -0,0 +1,5 @@ +import { runIntegrationTest } from "../runIntegrationTest" +runIntegrationTest({ + projectName: "create-issue", + shouldProduceSnapshots: true, +}) diff --git a/integration-tests/create-issue/open.mock.js b/integration-tests/create-issue/open.mock.js new file mode 100644 index 00000000..b2fe72e6 --- /dev/null +++ b/integration-tests/create-issue/open.mock.js @@ -0,0 +1,7 @@ +module.exports = (url) => { + const [resource, query] = url.split("?") + console.log("MOCK opening", resource) + const { title, body } = require("querystring").parse(query) + console.log("MOCK title", title) + console.log("MOCK body", body) +} diff --git a/integration-tests/create-issue/package.json b/integration-tests/create-issue/package.json new file mode 100644 index 00000000..e8177ce2 --- /dev/null +++ b/integration-tests/create-issue/package.json @@ -0,0 +1,13 @@ +{ + "name": "create-issue", + "version": "1.0.0", + "description": "integration test for patch-package", + "main": "index.js", + "author": "", + "license": "ISC", + "dependencies": { + "left-pad": "^1.3.0", + "lodash": "^4.17.21", + "replace": "^1.2.0" + } +} diff --git a/integration-tests/create-issue/yarn.lock b/integration-tests/create-issue/yarn.lock new file mode 100644 index 00000000..e32f79dc --- /dev/null +++ b/integration-tests/create-issue/yarn.lock @@ -0,0 +1,264 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +chalk@2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +left-pad@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" + integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +minimatch@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +replace@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/replace/-/replace-1.2.0.tgz#a25da288841aab22f0f7e95dc1d249dbd2ed6e26" + integrity sha512-e3AP5GkRk+N/Qm1MUBaMhEHr4X3sHNI44a8m4ww6/qShJphTsStxSezbYtFNTFGCXZtWrwz4McVvCEwBv+ebAw== + dependencies: + chalk "2.4.2" + minimatch "3.0.4" + yargs "^15.3.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.1.tgz#1933ce1f470973d224368009bd1316cad81d5f4f" + integrity sha512-LL0OLyN6AnfV9xqGQpDBwedT2Rt63737LxvsRxbcwpa2aIeynBApG2Sm//F3TaLHIR1aJBN52DWklc06b94o5Q== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +y18n@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" diff --git a/integration-tests/happy-path-npm/__snapshots__/happy-path-npm.test.ts.snap b/integration-tests/happy-path-npm/__snapshots__/happy-path-npm.test.ts.snap index ba8b69d5..ee54e635 100644 --- a/integration-tests/happy-path-npm/__snapshots__/happy-path-npm.test.ts.snap +++ b/integration-tests/happy-path-npm/__snapshots__/happy-path-npm.test.ts.snap @@ -7,6 +7,11 @@ patch-package 0.0.0 • Installing left-pad@1.1.3 with npm • Diffing your files with clean files ✔ Created file patches/left-pad+1.1.3.patch + +💡 left-pad is on GitHub! To draft an issue based on your patch run + + npx patch-package left-pad --create-issue + END SNAPSHOT" `; diff --git a/integration-tests/happy-path-yarn/__snapshots__/happy-path-yarn.test.ts.snap b/integration-tests/happy-path-yarn/__snapshots__/happy-path-yarn.test.ts.snap index b007ce6c..0b22e347 100644 --- a/integration-tests/happy-path-yarn/__snapshots__/happy-path-yarn.test.ts.snap +++ b/integration-tests/happy-path-yarn/__snapshots__/happy-path-yarn.test.ts.snap @@ -7,6 +7,11 @@ patch-package 0.0.0 • Installing left-pad@1.1.3 with yarn • Diffing your files with clean files ✔ Created file patches/left-pad+1.1.3.patch + +💡 left-pad is on GitHub! To draft an issue based on your patch run + + yarn patch-package left-pad --create-issue + END SNAPSHOT" `; diff --git a/integration-tests/ignores-scripts-when-making-patch/__snapshots__/ignores-scripts-when-making-patch.test.ts.snap b/integration-tests/ignores-scripts-when-making-patch/__snapshots__/ignores-scripts-when-making-patch.test.ts.snap index c7bd32ac..1c31fe46 100644 --- a/integration-tests/ignores-scripts-when-making-patch/__snapshots__/ignores-scripts-when-making-patch.test.ts.snap +++ b/integration-tests/ignores-scripts-when-making-patch/__snapshots__/ignores-scripts-when-making-patch.test.ts.snap @@ -22,6 +22,7 @@ patch-package 0.0.0 • Installing naughty-package@1.0.0 with yarn • Diffing your files with clean files ✔ Created file patches/naughty-package+1.0.0.patch + END SNAPSHOT" `; diff --git a/integration-tests/lerna-canary/__snapshots__/lerna-canary.test.ts.snap b/integration-tests/lerna-canary/__snapshots__/lerna-canary.test.ts.snap index f82f56a8..1dfdf267 100644 --- a/integration-tests/lerna-canary/__snapshots__/lerna-canary.test.ts.snap +++ b/integration-tests/lerna-canary/__snapshots__/lerna-canary.test.ts.snap @@ -7,6 +7,11 @@ patch-package 0.0.0 • Installing @parcel/codeframe@2.0.0-nightly.137 with yarn • Diffing your files with clean files ✔ Created file patches/@parcel+codeframe+2.0.0-nightly.137.patch + +💡 @parcel/codeframe is on GitHub! To draft an issue based on your patch run + + yarn patch-package @parcel/codeframe --create-issue + END SNAPSHOT" `; diff --git a/integration-tests/nested-packages/__snapshots__/nested-packages.test.ts.snap b/integration-tests/nested-packages/__snapshots__/nested-packages.test.ts.snap index 2e69ced0..420488cc 100644 --- a/integration-tests/nested-packages/__snapshots__/nested-packages.test.ts.snap +++ b/integration-tests/nested-packages/__snapshots__/nested-packages.test.ts.snap @@ -7,6 +7,11 @@ patch-package 0.0.0 • Installing string-width@2.1.1 with yarn • Diffing your files with clean files ✔ Created file patches/wrap-ansi++string-width+2.1.1.patch + +💡 string-width is on GitHub! To draft an issue based on your patch run + + yarn patch-package wrap-ansi/string-width --create-issue + END SNAPSHOT" `; diff --git a/integration-tests/nested-scoped-packages/__snapshots__/nested-scoped-packages.test.ts.snap b/integration-tests/nested-scoped-packages/__snapshots__/nested-scoped-packages.test.ts.snap index c03604b6..e30b642e 100644 --- a/integration-tests/nested-scoped-packages/__snapshots__/nested-scoped-packages.test.ts.snap +++ b/integration-tests/nested-scoped-packages/__snapshots__/nested-scoped-packages.test.ts.snap @@ -7,6 +7,11 @@ patch-package 0.0.0 • Installing @types/angular@1.6.53 with yarn • Diffing your files with clean files ✔ Created file patches/@microsoft+mezzurite-core++@types+angular+1.6.53.patch + +💡 @types/angular is on GitHub! To draft an issue based on your patch run + + yarn patch-package @microsoft/mezzurite-core/@types/angular --create-issue + END SNAPSHOT" `; diff --git a/package.json b/package.json index ed8f1cf0..cffc6642 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "ts-jest": "^24.0.0", "ts-node": "8.0.3", "tslint": "^5.14.0", - "typescript": "^3.6.3" + "typescript": "^4.2.2" }, "dependencies": { "@yarnpkg/lockfile": "^1.1.0", @@ -79,6 +79,7 @@ "is-ci": "^2.0.0", "klaw-sync": "^6.0.0", "minimist": "^1.2.0", + "open": "^7.4.2", "rimraf": "^2.6.3", "semver": "^5.6.0", "slash": "^2.0.0", diff --git a/src/createIssue.ts b/src/createIssue.ts new file mode 100644 index 00000000..95c42a02 --- /dev/null +++ b/src/createIssue.ts @@ -0,0 +1,104 @@ +import chalk from "chalk" +import open from "open" +import { stringify } from "querystring" +import { PackageManager } from "./detectPackageManager" +import { PackageDetails } from "./PackageDetails" +import { join, resolve } from "./path" + +const repoSpecifier = /^([\w.-]+)\/([\w.-]+)$/ +const githubURL = /github.com(:|\/)([\w.-]+\/[\w.-]+?)(.git|\/.*)?$/ + +function parseRepoString( + repository: string, +): null | { repo: string; org: string; provider: "GitHub" } { + if (repository.startsWith("github:")) { + repository = repository.replace(/^github:/, "") + } + const urlMatch = repository.match(githubURL) + if (urlMatch) { + repository = urlMatch[2] + } + + const specMatch = repository.match(repoSpecifier) + + if (!specMatch) { + return null + } + const [, org, repo] = specMatch + + return { org, repo, provider: "GitHub" } +} + +function getPackageVCSDetails(packageDetails: PackageDetails) { + const repository = require(resolve(join(packageDetails.path, "package.json"))) + .repository as undefined | string | { url: string } + + if (!repository) { + return null + } + if (typeof repository === "string") { + return parseRepoString(repository) + } else if ( + typeof repository === "object" && + typeof repository.url === "string" + ) { + return parseRepoString(repository.url) + } +} + +export function maybePrintIssueCreationPrompt( + packageDetails: PackageDetails, + packageManager: PackageManager, +) { + const vcs = getPackageVCSDetails(packageDetails) + if (vcs) { + console.log(`💡 ${chalk.bold(packageDetails.name)} is on ${ + vcs.provider + }! To draft an issue based on your patch run + + ${packageManager === "yarn" ? "yarn" : "npx"} patch-package ${ + packageDetails.pathSpecifier + } --create-issue +`) + } +} + +export function openIssueCreationLink({ + packageDetails, + patchFileContents, +}: { + packageDetails: PackageDetails + patchFileContents: string +}) { + const vcs = getPackageVCSDetails(packageDetails) + + if (!vcs) { + console.error( + `Error: Couldn't find VCS details for ${packageDetails.pathSpecifier}`, + ) + process.exit(1) + } + + // trim off trailing newline since we add an extra one in the markdown block + if (patchFileContents.endsWith("\n")) { + patchFileContents = patchFileContents.slice(0, -1) + } + + open( + `https://github.com/${vcs.org}/${vcs.repo}/issues/new?${stringify({ + title: "[Replace me]", + body: `Hi! 👋 + +Firstly, thanks for your work on this project! 🙂 + +Today I used [patch-package](https://github.com/ds300/patch-package) to patch \`${packageDetails.name}\` for the project I'm working on because [Insert reason here]. + +Here is the diff that solved my problem: + +\`\`\`diff +${patchFileContents} +\`\`\` +`, + })}`, + ) +} diff --git a/src/index.ts b/src/index.ts index 1847e4f4..a4691a28 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,6 +21,7 @@ const argv = minimist(process.argv.slice(2), { "help", "version", "error-on-fail", + "create-issue", ], string: ["patch-dir"], }) @@ -58,6 +59,7 @@ if (argv.version || argv.v) { appPath, argv["use-yarn"] ? "yarn" : null, ) + const createIssue = argv["create-issue"] packageNames.forEach((packagePathSpecifier: string) => { makePatch({ packagePathSpecifier, @@ -66,6 +68,7 @@ if (argv.version || argv.v) { includePaths, excludePaths, patchDir, + createIssue, }) }) } else { @@ -137,6 +140,11 @@ Usage: based on any changes you've made to the versions installed by yarn/npm. Options: + + ${chalk.bold("--create-issue")} + + For packages whose source is hosted on GitHub this option opens a web + browser with a draft issue based on your diff. ${chalk.bold("--use-yarn")} diff --git a/src/makePatch.ts b/src/makePatch.ts index 97787ef7..5ff928cf 100644 --- a/src/makePatch.ts +++ b/src/makePatch.ts @@ -17,12 +17,17 @@ import { getPatchFiles } from "./patchFs" import { getPatchDetailsFromCliString, getPackageDetailsFromPatchFilename, + PackageDetails, } from "./PackageDetails" import { resolveRelativeFileDependencies } from "./resolveRelativeFileDependencies" import { getPackageResolution } from "./getPackageResolution" import { parsePatchFile } from "./patch/parse" import { gzipSync } from "zlib" import { getPackageVersion } from "./getPackageVersion" +import { + maybePrintIssueCreationPrompt, + openIssueCreationLink, +} from "./createIssue" function printNoPackageFoundError( packageName: string, @@ -42,6 +47,7 @@ export function makePatch({ includePaths, excludePaths, patchDir, + createIssue, }: { packagePathSpecifier: string appPath: string @@ -49,6 +55,7 @@ export function makePatch({ includePaths: RegExp excludePaths: RegExp patchDir: string + createIssue: boolean }) { const packageDetails = getPatchDetailsFromCliString(packagePathSpecifier) @@ -264,10 +271,6 @@ export function makePatch({ return } - const packageNames = packageDetails.packageNames - .map((name) => name.replace(/\//g, "+")) - .join("++") - // maybe delete existing getPatchFiles(patchDir).forEach((filename) => { const deets = getPackageDetailsFromPatchFilename(filename) @@ -276,7 +279,10 @@ export function makePatch({ } }) - const patchFileName = `${packageNames}+${packageVersion}.patch` + const patchFileName = createPatchFileName({ + packageDetails, + packageVersion, + }) const patchPath = join(patchesDir, patchFileName) if (!existsSync(dirname(patchPath))) { @@ -285,8 +291,16 @@ export function makePatch({ } writeFileSync(patchPath, diffResult.stdout) console.log( - `${chalk.green("✔")} Created file ${join(patchDir, patchFileName)}`, + `${chalk.green("✔")} Created file ${join(patchDir, patchFileName)}\n`, ) + if (createIssue) { + openIssueCreationLink({ + packageDetails, + patchFileContents: diffResult.stdout.toString(), + }) + } else { + maybePrintIssueCreationPrompt(packageDetails, packageManager) + } } catch (e) { console.error(e) throw e @@ -294,3 +308,17 @@ export function makePatch({ tmpRepo.removeCallback() } } + +function createPatchFileName({ + packageDetails, + packageVersion, +}: { + packageDetails: PackageDetails + packageVersion: string +}) { + const packageNames = packageDetails.packageNames + .map((name) => name.replace(/\//g, "+")) + .join("++") + + return `${packageNames}+${packageVersion}.patch` +} diff --git a/yarn.lock b/yarn.lock index 1d0efdc4..6e3bfe03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4143,7 +4143,7 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@^7.3.0: +open@^7.3.0, open@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== @@ -5666,10 +5666,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^3.6.3: - version "3.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" - integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== +typescript@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.2.tgz#1450f020618f872db0ea17317d16d8da8ddb8c4c" + integrity sha512-tbb+NVrLfnsJy3M59lsDgrzWIflR4d4TIUjz+heUnHZwdF7YsrMTKoRERiIvI2lvBG95dfpLxB21WZhys1bgaQ== uglify-js@^3.1.4: version "3.5.2"