From 070358e8456b2458ab99c1480ed25cdbd896c828 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 1 Aug 2022 21:43:07 -0700 Subject: [PATCH 01/10] add `jscodeshift` and `@babel/parser` --- packages/nextjs/package.json | 4 + yarn.lock | 415 ++++++++++++++++++++++++++++++++++- 2 files changed, 414 insertions(+), 5 deletions(-) diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index f45f060b6355..a04f0a20c320 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -17,6 +17,7 @@ "access": "public" }, "dependencies": { + "@babel/parser": "^7.18.10", "@sentry/core": "7.9.0", "@sentry/hub": "7.9.0", "@sentry/integrations": "7.9.0", @@ -26,9 +27,12 @@ "@sentry/types": "7.9.0", "@sentry/utils": "7.9.0", "@sentry/webpack-plugin": "1.19.0", + "jscodeshift": "^0.13.1", "tslib": "^1.9.3" }, "devDependencies": { + "@babel/types": "7.18.10", + "@types/jscodeshift": "^0.11.5", "@types/webpack": "^4.41.31", "next": "10.1.3" }, diff --git a/yarn.lock b/yarn.lock index 5e290ac8ed4c..752747d71c27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -260,6 +260,13 @@ dependencies: "@babel/highlight" "^7.16.7" +"@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + "@babel/compat-data@^7.11.0", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.7": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" @@ -275,6 +282,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.0.tgz#ea269d7f78deb3a7826c39a4048eecda541ebdaa" integrity sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew== +"@babel/compat-data@^7.18.6": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" + integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== + "@babel/core@7.11.1": version "7.11.1" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" @@ -360,6 +372,27 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/core@^7.13.16": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.6.tgz#54a107a3c298aee3fe5e1947a6464b9b6faca03d" + integrity sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.6" + "@babel/helper-compilation-targets" "^7.18.6" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helpers" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + "@babel/core@^7.14.8": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.0.tgz#c4ff44046f5fe310525cc9eb4ef5147f0c5374d4" @@ -426,6 +459,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.18.6", "@babel/generator@^7.18.7": + version "7.18.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.7.tgz#2aa78da3c05aadfc82dbac16c99552fc802284bd" + integrity sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A== + dependencies: + "@babel/types" "^7.18.7" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" @@ -447,6 +489,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz#6bc20361c88b0a74d05137a65cac8d3cbf6f61fc" @@ -493,6 +542,16 @@ browserslist "^4.17.5" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz#18d35bfb9f83b1293c22c55b3d576c1315b6ed96" + integrity sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg== + dependencies: + "@babel/compat-data" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.20.2" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.5.5": version "7.13.11" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz#30d30a005bca2c953f5653fc25091a492177f4f6" @@ -542,6 +601,19 @@ "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" +"@babel/helper-create-class-features-plugin@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.6.tgz#6f15f8459f3b523b39e00a99982e2c040871ed72" + integrity sha512-YfDzdnoxHGV8CzqHGyCbFvXg5QESPFkXlHtvdCkesLjjVMT2Adxe4FGUR5ChIb3DxSaXO12iIOCWoXdsUVwnqw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-function-name" "^7.18.6" + "@babel/helper-member-expression-to-functions" "^7.18.6" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-create-regexp-features-plugin@^7.12.13": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz#a2ac87e9e319269ac655b8d4415e94d38d663cb7" @@ -579,6 +651,11 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz#b7eee2b5b9d70602e59d1a6cad7dd24de7ca6cd7" + integrity sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q== + "@babel/helper-explode-assignable-expression@^7.12.13": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz#17b5c59ff473d9f956f40ef570cf3a76ca12657f" @@ -628,6 +705,14 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.17.0" +"@babel/helper-function-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz#8334fecb0afba66e6d87a7e8c6bb7fed79926b83" + integrity sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw== + dependencies: + "@babel/template" "^7.18.6" + "@babel/types" "^7.18.6" + "@babel/helper-get-function-arity@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b" @@ -670,6 +755,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-member-expression-to-functions@^7.13.0", "@babel/helper-member-expression-to-functions@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz#bfd34dc9bba9824a4658b0317ec2fd571a51e6ef" @@ -698,6 +790,13 @@ dependencies: "@babel/types" "^7.17.0" +"@babel/helper-member-expression-to-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.6.tgz#44802d7d602c285e1692db0bad9396d007be2afc" + integrity sha512-CeHxqwwipekotzPDUuJOfIMtcIHBuc7WAzLmTYWctVigqS5RktNMQ5bEwQSuGewzYnCtTWa3BARXeiLxDTv+Ng== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" @@ -719,6 +818,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-module-transforms@^7.11.0", "@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^7.17.7": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" @@ -761,6 +867,20 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-module-transforms@^7.18.6": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.8.tgz#4f8408afead0188cfa48672f9d0e5787b61778c8" + integrity sha512-che3jvZwIcZxrwh63VfnFTUzcAM9v/lznYkkRxIBGMPt1SudOKHAEec0SIRCfiuIzTcF7VGj/CaTT6gY4eWxvA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.8" + "@babel/types" "^7.18.8" + "@babel/helper-optimise-call-expression@^7.12.13", "@babel/helper-optimise-call-expression@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" @@ -782,6 +902,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" @@ -792,6 +919,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== +"@babel/helper-plugin-utils@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz#9448974dd4fb1d80fefe72e8a0af37809cd30d6d" + integrity sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg== + "@babel/helper-remap-async-to-generator@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz#376a760d9f7b4b2077a9dd05aa9c3927cadb2209" @@ -841,6 +973,17 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helper-replace-supers@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.6.tgz#efedf51cfccea7b7b8c0f00002ab317e7abfe420" + integrity sha512-fTf7zoXnUGl9gF25fXCWE26t7Tvtyn6H4hkLSYhATwJvw2uYxd3aoXplMSe0g9XbwK7bmxNes7+FGO0rB/xC0g== + dependencies: + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-member-expression-to-functions" "^7.18.6" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + "@babel/helper-simple-access@^7.12.13", "@babel/helper-simple-access@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b" @@ -862,6 +1005,13 @@ dependencies: "@babel/types" "^7.17.0" +"@babel/helper-simple-access@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" + integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-skip-transparent-expression-wrappers@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" @@ -897,6 +1047,18 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" + integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== + "@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9": version "7.14.9" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48" @@ -912,6 +1074,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-validator-identifier@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" + integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== + "@babel/helper-validator-option@^7.12.17", "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" @@ -922,6 +1089,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + "@babel/helper-wrap-function@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz#bdb5c66fda8526ec235ab894ad53a1235c79fcc4" @@ -969,6 +1141,15 @@ "@babel/traverse" "^7.15.4" "@babel/types" "^7.15.4" +"@babel/helpers@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.6.tgz#4c966140eaa1fcaa3d5a8c09d7db61077d4debfd" + integrity sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ== + dependencies: + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + "@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" @@ -996,6 +1177,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.13.13", "@babel/parser@^7.4.3", "@babel/parser@^7.4.5", "@babel/parser@^7.7.0": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.2.tgz#0c1680aa44ad4605b16cbdcc5c341a61bde9c746" @@ -1006,6 +1196,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== +"@babel/parser@^7.13.16", "@babel/parser@^7.18.6", "@babel/parser@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.8.tgz#822146080ac9c62dac0823bb3489622e0bc1cbdf" + integrity sha512-RSKRfYX20dyH+elbJK2uqAkVyucL+xXzhqlMD5/ZXx+dAAwpyB7HsvnHe/ZUGOF+xLr5Wx9/JoXVTj6BQE2/oA== + "@babel/parser@^7.15.4", "@babel/parser@^7.15.5": version "7.15.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.6.tgz#043b9aa3c303c0722e5377fef9197f4cf1796549" @@ -1021,6 +1216,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.8.tgz#61c243a3875f7d0b0962b0543a33ece6ff2f1f17" integrity sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw== +"@babel/parser@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.10.tgz#94b5f8522356e69e8277276adf67ed280c90ecc1" + integrity sha512-TYk3OA0HKL6qNryUayb5UUEhM/rkOQozIBEA5ITXh5DWrSp0TlUQXMyZmnWxG/DizSWBeeQ0Zbc5z8UGaaqoeg== + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a" @@ -1374,6 +1574,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-syntax-flow@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" + integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -1472,6 +1679,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-syntax-typescript@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" + integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-arrow-functions@^7.10.4": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" @@ -1633,6 +1847,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.12.13" "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-flow-strip-types@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.6.tgz#6d3dd9f9c0fe13349428569fef00b31310bb3f9f" + integrity sha512-wE0xtA7csz+hw4fKPwxmu5jnzAsXPIO57XnRwzXP3T19jWh1BODnPGoG9xKYwvAwusP7iUktHayRFbMPGtODaQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-flow" "^7.18.6" + "@babel/plugin-transform-for-of@^7.10.4": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" @@ -2006,6 +2228,15 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-typescript" "^7.16.7" +"@babel/plugin-transform-typescript@^7.18.6": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz#303feb7a920e650f2213ef37b36bbf327e6fa5a0" + integrity sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-typescript" "^7.18.6" + "@babel/plugin-transform-typescript@~7.4.0": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.4.5.tgz#ab3351ba35307b79981993536c93ff8be050ba28" @@ -2210,6 +2441,15 @@ core-js-compat "^3.9.0" semver "^6.3.0" +"@babel/preset-flow@^7.13.13": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.18.6.tgz#83f7602ba566e72a9918beefafef8ef16d2810cb" + integrity sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-flow-strip-types" "^7.18.6" + "@babel/preset-modules@^0.1.3": version "0.1.5" resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" @@ -2232,6 +2472,15 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" +"@babel/preset-typescript@^7.13.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" + integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-typescript" "^7.18.6" + "@babel/preset-typescript@^7.14.5": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.0.tgz#b0b4f105b855fb3d631ec036cdc9d1ffd1fa5eac" @@ -2250,6 +2499,17 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.16.7" +"@babel/register@^7.13.16": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.6.tgz#48a4520f1b2a7d7ac861e8148caeb0cefe6c59db" + integrity sha512-tkYtONzaO8rQubZzpBnvZPFcHgh8D9F55IjOsYton4X2IBoyRn2ZSWQqySTZnUn2guZbxbQiAB27hJEbvXamhQ== + dependencies: + clone-deep "^4.0.1" + find-cache-dir "^2.0.0" + make-dir "^2.1.0" + pirates "^4.0.5" + source-map-support "^0.5.16" + "@babel/runtime@7.11.2": version "7.11.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" @@ -2321,6 +2581,15 @@ "@babel/parser" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/template@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" + integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/types" "^7.18.6" + "@babel/traverse@^7.1.6", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": version "7.13.13" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d" @@ -2397,6 +2666,31 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.18.6", "@babel/traverse@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.8.tgz#f095e62ab46abf1da35e5a2011f43aee72d8d5b0" + integrity sha512-UNg/AcSySJYR/+mIcJQDCv00T+AqRO7j/ZEJLzpaYtgM48rMg5MnkJgyNqkzo88+p4tfRvZJCEiwwfG6h4jkRg== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.7" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-function-name" "^7.18.6" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.8" + "@babel/types" "^7.18.8" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" + integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== + dependencies: + "@babel/helper-string-parser" "^7.18.10" + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + "@babel/types@7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" @@ -2447,6 +2741,14 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@babel/types@^7.18.6", "@babel/types@^7.18.7", "@babel/types@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.8.tgz#c5af199951bf41ba4a6a9a6d0d8ad722b30cd42f" + integrity sha512-qwpdsmraq0aJ3osLJRApsc2ouSJCdnMeZwB0DhbtHAtRpZNZCdlbRnHIgcRKzdE1g0iOGg644fzjOBcdOz9cPw== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -3199,11 +3501,25 @@ resolved "https://registry.yarnpkg.com/@josephg/resolvable/-/resolvable-1.0.1.tgz#69bc4db754d79e1a2f17a650d3466e038d94a5eb" integrity sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg== +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/resolve-uri@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.11" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" @@ -3217,6 +3533,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.14" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" + integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jsdevtools/coverage-istanbul-loader@3.0.5": version "3.0.5" resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#2a4bc65d0271df8d4435982db4af35d81754ee26" @@ -5148,6 +5472,14 @@ dependencies: "@types/sizzle" "*" +"@types/jscodeshift@^0.11.5": + version "0.11.5" + resolved "https://registry.yarnpkg.com/@types/jscodeshift/-/jscodeshift-0.11.5.tgz#51198aa72ceb66d36ceba3918e1ab445b868f29b" + integrity sha512-7JV0qdblTeWFigevmwFUgROXX395F+MQx6v0YqPn8Bx0B4Sng6alEejz9PENzgLYpG+zL0O4tGdBzc4gKZH8XA== + dependencies: + ast-types "^0.14.1" + recast "^0.20.3" + "@types/jsdom@^16.2.3": version "16.2.9" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.9.tgz#9c219e5c387f045aae8b80ae4d4cf61d098c15eb" @@ -6637,7 +6969,7 @@ ast-types@0.13.3: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.3.tgz#50da3f28d17bdbc7969a3a2d83a0e4a72ae755a7" integrity sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA== -ast-types@0.14.2: +ast-types@0.14.2, ast-types@^0.14.1: version "0.14.2" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== @@ -6848,6 +7180,11 @@ babel-core@^6.26.0, babel-core@^6.26.3: slash "^1.0.0" source-map "^0.5.7" +babel-core@^7.0.0-bridge.0: + version "7.0.0-bridge.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" + integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== + babel-eslint@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" @@ -9058,6 +9395,14 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -9341,6 +9686,15 @@ clone-deep@^0.2.4: lazy-cache "^1.0.3" shallow-clone "^0.1.2" +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + clone-response@1.0.2, clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -13133,7 +13487,7 @@ find-cache-dir@3.3.1, find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-cache-dir@^2.1.0: +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== @@ -13334,6 +13688,11 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== +flow-parser@0.*: + version "0.182.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.182.0.tgz#badada9392caac8e2b47b621bc0b68b51232d9f2" + integrity sha512-Caoy6YFlh0jz+qWpMGuI2CEIDcQGa/YRRnQ5d8+jtj30weXApWDyTSN5gPNve9cQN73JKXE2LFnpZ5AOUI1bXA== + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -16755,6 +17114,31 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jscodeshift@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.13.1.tgz#69bfe51e54c831296380585c6d9e733512aecdef" + integrity sha512-lGyiEbGOvmMRKgWk4vf+lUrCWO/8YR8sUR3FKF1Cq5fovjZDlIcw3Hu5ppLHAnEXshVffvaM0eyuY/AbOeYpnQ== + dependencies: + "@babel/core" "^7.13.16" + "@babel/parser" "^7.13.16" + "@babel/plugin-proposal-class-properties" "^7.13.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8" + "@babel/plugin-proposal-optional-chaining" "^7.13.12" + "@babel/plugin-transform-modules-commonjs" "^7.13.8" + "@babel/preset-flow" "^7.13.13" + "@babel/preset-typescript" "^7.13.0" + "@babel/register" "^7.13.16" + babel-core "^7.0.0-bridge.0" + chalk "^4.1.2" + flow-parser "0.*" + graceful-fs "^4.2.4" + micromatch "^3.1.10" + neo-async "^2.5.0" + node-dir "^0.1.17" + recast "^0.20.4" + temp "^0.8.4" + write-file-atomic "^2.3.0" + jsdoctypeparser@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz#8c97e2fb69315eb274b0f01377eaa5c940bd7b26" @@ -19018,6 +19402,13 @@ nock@^13.1.0: lodash.set "^4.3.2" propagate "^2.0.0" +node-dir@^0.1.17: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== + dependencies: + minimatch "^3.0.2" + node-environment-flags@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" @@ -20478,7 +20869,7 @@ pirates@^4.0.1: dependencies: node-modules-regexp "^1.0.0" -pirates@^4.0.4: +pirates@^4.0.4, pirates@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== @@ -21940,7 +22331,7 @@ recast@^0.18.1: private "^0.1.8" source-map "~0.6.1" -recast@^0.20.5: +recast@^0.20.3, recast@^0.20.4, recast@^0.20.5: version "0.20.5" resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ== @@ -23133,6 +23524,13 @@ shallow-clone@^0.1.2: lazy-cache "^0.2.3" mixin-object "^2.0.1" +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -23567,7 +23965,7 @@ source-map-support@^0.4.15, source-map-support@^0.4.18: dependencies: source-map "^0.5.6" -source-map-support@^0.5.21, source-map-support@^0.5.5, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.20: +source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@^0.5.5, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -24618,6 +25016,13 @@ temp@0.9.4: mkdirp "^0.5.1" rimraf "~2.6.2" +temp@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" + integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== + dependencies: + rimraf "~2.6.2" + temp@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/temp/-/temp-0.4.0.tgz#671ad63d57be0fe9d7294664b3fc400636678a60" From 05f9d1faef84f5996aeef3266bcaab6792150e52 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 2 Aug 2022 20:45:45 -0700 Subject: [PATCH 02/10] add jsx and tsx parsers --- packages/nextjs/src/config/loaders/parsers.ts | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 packages/nextjs/src/config/loaders/parsers.ts diff --git a/packages/nextjs/src/config/loaders/parsers.ts b/packages/nextjs/src/config/loaders/parsers.ts new file mode 100644 index 000000000000..2c90ed498d6f --- /dev/null +++ b/packages/nextjs/src/config/loaders/parsers.ts @@ -0,0 +1,64 @@ +/** + * Note: The implementation here is loosely based on the jsx and tsx parsers in 'jscodeshift'. It doesn't expose its + * parsers, so we have to provide our own if we want to use anything besides the default. Fortunately, its parsers turn + * out to just be wrappers around `babel.parse` with certain options set. The options chosen here are different from the + * `jscodeshift` parsers in that a) unrecognized and deprecated options and options set to default values have been + * removed, and b) all standard plugins are included, meaning the widest range of user code is able to be parsed. + */ + +import * as babel from '@babel/parser'; +import { File } from '@babel/types'; + +type Parser = { + parse: (code: string) => babel.ParseResult; +}; + +const jsxOptions: babel.ParserOptions = { + // Nextjs supports dynamic import, so this seems like a good idea + allowImportExportEverywhere: true, + // We're only supporting wrapping in ESM pages + sourceType: 'module', + // Without `tokens`, jsx parsing breaks + tokens: true, + // The maximal set of non-mutually-exclusive standard plugins, so as to support as much weird syntax in our users' + // code as possible + plugins: [ + 'asyncDoExpressions', + 'decimal', + ['decorators', { decoratorsBeforeExport: false }], + 'decoratorAutoAccessors', + 'destructuringPrivate', + 'doExpressions', + 'estree', + 'exportDefaultFrom', + 'functionBind', + 'importMeta', + 'importAssertions', + 'jsx', + 'moduleBlocks', + 'partialApplication', + ['pipelineOperator', { proposal: 'hack', topicToken: '^' }], + 'regexpUnicodeSets', + 'throwExpressions', + ] as babel.ParserPlugin[], +}; + +const tsxOptions = { + ...jsxOptions, + // Because `jsxOptions` is typed as a `ParserOptions` object, TS doesn't discount the possibility of its `plugins` + // property being undefined, even though it is, in fact, very clearly defined - hence the empty array. + plugins: [...(jsxOptions.plugins || []), 'typescript'] as babel.ParserPlugin[], +}; + +/** + * Create either a jsx or tsx parser to be used by `jscodeshift`. + * + * @param type Either 'jsx' or 'tsx' + * @returns An object with the appropriate `parse` method. + */ +export function makeParser(type: 'jsx' | 'tsx'): Parser { + const options = type === 'jsx' ? jsxOptions : tsxOptions; + return { + parse: code => babel.parse(code, options), + }; +} From 578e7c924e888a9beeaa079fb0c24b3a3c871a1c Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 1 Aug 2022 22:22:37 -0700 Subject: [PATCH 03/10] add AST helper functions --- packages/nextjs/src/config/loaders/ast.ts | 322 ++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 packages/nextjs/src/config/loaders/ast.ts diff --git a/packages/nextjs/src/config/loaders/ast.ts b/packages/nextjs/src/config/loaders/ast.ts new file mode 100644 index 000000000000..18f7380553b3 --- /dev/null +++ b/packages/nextjs/src/config/loaders/ast.ts @@ -0,0 +1,322 @@ +/* eslint-disable max-lines */ +import * as jscsTypes from 'jscodeshift'; +import { default as jscodeshiftDefault } from 'jscodeshift'; + +import { makeParser } from './parsers'; + +// In `jscodeshift`, the exports look like this: +// +// function core(...) { ... } +// core.ABC = ... +// core.XYZ = ... +// module.exports = core +// +// In other words, when required/imported, the module is both a callable function and an object containing all sorts of +// properties. Meanwhile, its TS export is a namespace continaing the types of all of the properties attached to `core`. +// In order to use the types, we thus need to use `import *` syntax. But when we do that, Rollup only sees it as a +// namespace, and will complain if we try to use it as a function. In order to get around this, we take advantage of the +// fact that Rollup wraps imports in its own version of TS's `esModuleInterop` functions, aliasing the export to a +// `default` property inside the export. (So, here, we basically end up with `core.default = core`.) When referenced +// through that alias, `core` is correctly seen as callable by Rollup. Outside of a Rollup context, however, that +// `default` alias doesn't exist. So, we try both and use whichever one is defined. (See +// https://github.com/rollup/rollup/issues/1267.) +const jscodeshiftNamespace = jscsTypes; +const jscs = jscodeshiftDefault || jscodeshiftNamespace; + +// These are types not in the TS sense, but in the instance-of-a-Type-class sense +const { + ExportSpecifier, + Identifier, + ImportSpecifier, + MemberExpression, + Node, + ObjectExpression, + ObjectPattern, + Property, + VariableDeclaration, + VariableDeclarator, +} = jscs; + +type ASTNode = jscsTypes.ASTNode; +export type AST = jscsTypes.Collection; +// `parentPath` is on the prototype, but not included in the type for some reason. (`parent`, which is an instance +// property referencing the same object as `parentPath`, is in the type, and we could use that instead. But the +// `parentPath` name makes it clearer that said object is in fact a `NodePath`, not a `Node`, so we choose to use it +// over `parent`, even if it means adding it to the type.) +interface ASTPath extends jscsTypes.ASTPath { + parentPath: ASTPath; +} +type IdentifierNode = jscsTypes.Identifier; +type ExportSpecifierNode = jscsTypes.ExportSpecifier; +type VariableDeclarationNode = jscsTypes.VariableDeclaration; + +/** + * Create an AST based on the given code. + * + * @param code The code to convert to an AST. + * @param isTS Flag indicating what parser to use. + * @throws Parsing error if the code is unparsable + * @returns The AST + */ +export function makeAST(code: string, isTS: boolean): AST { + const parser = isTS ? makeParser('tsx') : makeParser('jsx'); + // If this errors, it will be caught in the calling function, where we know more information and can construct a + // better warning message + return jscs(code, { parser }); +} + +/** + * Find all nodes which represent Identifiers with the given name + * + * @param ast The code, in AST form + * @param name The Identifier name to search for + * @returns A collection of NodePaths pointing to any nodes which were found + */ +function findIdentifiers(ast: AST, name: string): AST { + const identifierFilter = function (path: ASTPath): boolean { + // Check that what we have is indeed an Identifier, and that the name matches + // + // Note: If we were being super precise about this, we'd also check the context in which the identifier is being + // used, because there are some cases where we actually don't want to be renaming things (if the identifier is being + // used to name a class property, for example). But the chances that someone is going to have a class property in a + // nextjs page file with the same name as one of the canonical functions are slim to none, so for simplicity we can + // stop filtering here. If this ever becomes a problem, more precise filter checks can be found in a comment at the + // bottom of this file. + return path.node.name === name; + }; + + return ast.find(Identifier).filter(identifierFilter); +} + +/** + * Find all nodes which are declarations of variables with the given name + * + * @param ast The code, in AST form + * @param name The variable name to search for + * @returns A collection of NodePaths pointing to any nodes which were found + */ +export function findDeclarations(ast: AST, name: string): AST { + // Check for a structure of the form + // + // node: VariableDeclaration + // \ + // declarations: VariableDeclarator[] + // \ + // 0 : VariableDeclarator + // \ + // id: Identifier + // \ + // name: string + // + // where `name` matches the given name. + const declarationFilter = function (path: ASTPath): boolean { + return ( + path.node.declarations.length === 1 && + VariableDeclarator.check(path.node.declarations[0]) && + Identifier.check(path.node.declarations[0].id) && + path.node.declarations[0].id.name === name + ); + }; + + return ast.find(VariableDeclaration).filter(declarationFilter); +} + +/** + * Find all nodes which are exports of variables with the given name + * + * @param ast The code, in AST form + * @param name The variable name to search for + * @returns A collection of NodePaths pointing to any nodes which were found + */ +export function findExports(ast: AST, name: string): AST { + const exportFilter = function (path: ASTPath): boolean { + return ExportSpecifier.check(path.node) && path.node.exported.name === name; + }; + + return ast.find(ExportSpecifier).filter(exportFilter); +} + +/** + * Rename all identifiers with the given name, except in cases where it would break outside references. + * + * @param ast The AST representing the code + * @param origName The name being replaced + * @param newName The new name to use, if already chosen (one will be generated if not given) + * @returns The new name assigned to the identifiers, or undefined if no identifiers were renamed + */ +export function renameIdentifiers(ast: AST, origName: string, newName?: string): string | undefined { + const matchingNodes = findIdentifiers(ast, origName); + + if (matchingNodes.length > 0) { + // Find an available new name for the function by prefixing all references to it with an underscore (or a few + // underscores, if that's what it takes to avoid a name collision). + const alias = newName || findAvailibleAlias(ast, origName); + matchingNodes.forEach(nodePath => { + // Rename the node, except in cases where it might break an outside reference to it. + maybeRenameNode(ast, nodePath, alias); + }); + return alias; + } + + // technically redundant, but needed to keep TS happy + return undefined; +} + +/** + * Find an unused identifier name in the AST by repeatedly adding underscores to the beginning of the given original + * name until we find one which hasn't already been taken. + * + * @param userAST The AST to search + * @param origName The original name we want to alias + * @returns + */ +function findAvailibleAlias(userAST: AST, origName: string): string { + let foundAvailableName = false; + let newName = origName; + + while (!foundAvailableName) { + // Prefix the original function name (or the last name we tried) with an underscore and search for identifiers with + // the new name in the AST + newName = `_${newName}`; + const existingIdentifiers = findIdentifiers(userAST, newName); + + // If we haven't found anything, we're good to go + foundAvailableName = existingIdentifiers.length === 0; + } + + return newName; +} + +// When we're searching for and renaming the user's data-fetching functions, the general idea is to rename all +// identifiers matching the function names, but there are a few things to watch out for: +// - We can't rename any identifiers that refer to something outside of the module, because then we'd break the link +// between the external thing and the module's reference to it. The two key examples of this are named imports and +// property access in objects instantiated outside of the module. +// - What nextjs cares about is just the identifier which gets exported, which may or may not be what it's called +// locally. In other words, if we find something like `export { something as getServerSideProps }`, we have to +// rename both `something` and `getServerSideProps`, the former so we can wrap it and the latter so as not to +// conflict with the wrapped function of the same name we're planning to export. +// - Shorthand object notation is a thing. Specifically, it's a thing which makes two separate identifiers appear as +// one, even though they have separate functions and may need to be treated differently from one another. This shows +// up not just in object literals but also when destructuring and in imports and exports. + +function maybeRenameNode(ast: AST, identifierPath: ASTPath, alias: string): void { + const node = identifierPath.node; + const parent = identifierPath.parentPath.node; + const grandparent = identifierPath.parentPath.parentPath.node; + + // In general we want to rename all nodes, unless we're in one of a few specific situations. (Anything which doesn't + // get handled by one of these checks will be renamed at the end of this function.) In all of the scenarios below, + // we'll use `gSSP` as our stand-in for any of `getServerSideProps`, `getStaticProps`, and `getStaticPaths`. + + // Imports: + // + // - `import { gSSP } from 'yyy'`, which is equivalent (in AST terms) to `import { gSSP as gSSP } from 'yyy'` + // - `import { xxx as gSSP } from 'yyy'` + // + // The `xxx as gSSP` corresponds to an ImportSpecifier, with `imported = xxx` and `local = gSSP`. In both of these + // cases, we want to rename `local` (the thing on the right; that will happen below) but not `imported` (the thing on + // the left). + if (ImportSpecifier.check(parent)) { + if (node === parent.imported) return; + // The only other option is that `node === parent.local`. This will get renamed below. + } + + // Destructuring: + // + // - `const { gSSP } = yyy`, which is equivalent (in AST terms) to `const { gSSP:gSSP } = yyy` + // - `const { xxx:gSSP } = yyy` + // + // This would come up if, for example, we were grabbing something from a namespace (`import * as yyy from 'zzz'; const + // { xxx:gSSP } = yyy`). Here the `xxx:gSSP` corresponds to a Property (inside of an array inside of an ObjectPatten + // inside of a VariableDeclarator), with `key = xxx` and `value = gSSP`. In both of these cases, we want to rename + // `value` but not `key`. (Again here we're renaming the righthand thing but leaving the lefthand thing alone.) + + // And + // though it's unlikely to be as relevant here, it's worth noting that we see the exact same pattern when + // instantiating an object literal - `{ xxx }` or `{ xxx: yyy }` - where we rename the value but not the key. The only + // difference there is that it's an `ObjectExpression` rather than an `ObjectPattern`.) + if (Property.check(parent) && ObjectPattern.check(grandparent)) { + if (node === parent.key) return; + // The only other option is that `node === parent.value`. This will get renamed below. When it does, the names of + // `parent.key` and `parent.value` won't match (if they ever did), so we need to make sure to update `shorthand`. + parent.shorthand = false; + } + + // Object literal instantiation: + // + // - `const xxx = { gSSP }`, which is equivalent (in AST terms) to `const xxx = { gSSP: gSSP }` + // - `const xxx = { yyy: gSSP }` + // + // This is the same as destructuring in every way, with the exception that where there it was an `ObjectPattern`, here + // it's an `ObjectExpression`. + if (Property.check(parent) && ObjectExpression.check(grandparent)) { + if (node === parent.key) return; + // The only other option is that `node === parent.value`. This will get renamed below. When it does, the names of + // `parent.key` and `parent.value` won't match (if they ever did), so we need to make sure to update `shorthand`. + parent.shorthand = false; + } + + // Object property access: + // + // - xxx.yyy + // + // This is similar to destructuring (in that we we don't want to rename object keys), and would come up in similar + // circumstances: `import * as xxx from 'abc'; const zzz = xxx.yyy`. In this case the `xxx.yyy` corresponds to a + // `MemberExpression`, with `object = xxx` and `property = yyy`. (This is unlikely to be relevant in our case with + // data-fetching functions, which is why none of the part of this example are `gSSP`. Nonetheless, good to be accurate + // with these things.) + if (MemberExpression.check(parent)) { + if (node === parent.property) return; + // The only other option is that `node === parent.object`. This will get renamed below. + } + + // Exports: + // + // - `export { gSSP }, which is equivalent (in AST terms) to `export { gSSP as gSSP }` + // - `export { xxx as gSSP }` + // + // Similar to the `import` cases, here the `xxx as gSSP` corresponds to an `ExportSpecifier`, with `local = xxx` and + // `exported = gSSP`. And as before, we want to change `local`, but this time there's a twist. (Two of them, + // actually.) + // + // First, if we care about this ExportSpecifier at all, it's because it's the export of one of our data-fetching + // functions, as in the example above. Because we want to export a replacement version of said function, we need to + // rename `exported`, to prevent a name conflict. (This is different than what you'd expect from a simple "rename a + // variable" algorithm, because in that case you normally wouldn't rename the thing which could be referred to outside + // of the module.) + // + // Second, because need to wrap the object using its local name, we need to rename `local`. This tracks with how we + // thought about `import` statements above, but is different from everything else we're doing in this function in that + // it means we potentially need to rename something *not* already named `getServerSideProps`, `getStaticProps`, or + // `getStaticPaths`, meaning we need to rename nodes outside of the collection upon which we're currently acting. + if (ExportSpecifier.check(parent)) { + // console.log(node); + // debugger; + if (parent.exported.name !== parent.local?.name && node === parent.exported) { + const currentLocalName = parent.local?.name || ''; + renameIdentifiers(ast, currentLocalName, alias); + } + + // The only other options are that a) the names match, in which case both `local` and `exported` both have the name + // of the function we're trying to wrap, and will get renamed below, or b) the names are different but `node` is + // `local`, meaning this must be the second go-round of `renameIdentifiers`, where we're renaming everything with + // the local name, not the name of our wrapped data-fetching function, in which case `node` (a.k.a. `local`) will + // also get renamed below. + } + + // handle any node which hasn't gotten otherwise dealt with above + node.name = alias; +} + +/** + * Remove comments from all nodes in the given AST. + * + * Note: Comments are not nodes in and of themselves, but are instead attached to the nodes above and below them. + * + * @param ast The code, in AST form + */ +export function removeComments(ast: AST): void { + const nodesWithComments = ast.find(Node).filter(nodePath => !!nodePath.node.comments); + nodesWithComments.forEach(nodePath => (nodePath.node.comments = null)); +} From 8fb2c4bb05a047cfa674f1d9715b2cd8a20dcfcd Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 1 Aug 2022 22:30:32 -0700 Subject: [PATCH 04/10] add `isESM` helper --- packages/nextjs/src/utils/isESM.ts | 17 ++++++ packages/nextjs/test/utils/isESM.test.ts | 69 ++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 packages/nextjs/src/utils/isESM.ts create mode 100644 packages/nextjs/test/utils/isESM.test.ts diff --git a/packages/nextjs/src/utils/isESM.ts b/packages/nextjs/src/utils/isESM.ts new file mode 100644 index 000000000000..30ce7f4e87de --- /dev/null +++ b/packages/nextjs/src/utils/isESM.ts @@ -0,0 +1,17 @@ +/** + * Determine if the given source code represents a file written using ES6 modules. + * + * The regexes used are from https://github.com/component/is-module, which got them from + * https://github.com/formatjs/js-module-formats, which says it got them from + * https://github.com/ModuleLoader/es-module-loader, though the originals are now nowhere to be found. + * + * @param moduleSource The source code of the module + * @returns True if the module contains ESM-patterned code, false otherwise. + */ +export function isESM(moduleSource: string): boolean { + const importExportRegex = + /(?:^\s*|[}{();,\n]\s*)(import\s+['"]|(import|module)\s+[^"'()\n;]+\s+from\s+['"]|export\s+(\*|\{|default|function|var|const|let|[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*))/; + const exportStarRegex = /(?:^\s*|[}{();,\n]\s*)(export\s*\*\s*from\s*(?:'([^']+)'|"([^"]+)"))/; + + return importExportRegex.test(moduleSource) || exportStarRegex.test(moduleSource); +} diff --git a/packages/nextjs/test/utils/isESM.test.ts b/packages/nextjs/test/utils/isESM.test.ts new file mode 100644 index 000000000000..22169377e730 --- /dev/null +++ b/packages/nextjs/test/utils/isESM.test.ts @@ -0,0 +1,69 @@ +import { isESM } from '../../src/utils/isESM'; + +// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import +describe('import syntax', function () { + it('recognizes import syntax', function () { + expect(isESM("import dogs from 'dogs';")).toBe(true); + expect(isESM("import * as dogs from 'dogs';")).toBe(true); + expect(isESM("import { maisey } from 'dogs';")).toBe(true); + expect(isESM("import { charlie as goofball } from 'dogs';")).toBe(true); + expect(isESM("import { default as maisey } from 'dogs';")).toBe(true); + expect(isESM("import { charlie, masiey } from 'dogs';")).toBe(true); + expect(isESM("import { masiey, charlie as pickle } from 'dogs';")).toBe(true); + expect(isESM("import charlie, { maisey } from 'dogs';")).toBe(true); + expect(isESM("import maisey, * as dogs from 'dogs';")).toBe(true); + expect(isESM("import 'dogs';")).toBe(true); + }); +}); + +// Based on https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/export +describe('export syntax', function () { + it('recognizes exported declarations', () => { + expect(isESM('export var maisey, charlie;')).toBe(true); + expect(isESM('export let charlie, maisey;')).toBe(true); + expect(isESM("export var maisey = 'silly', charlie = 'goofy';")).toBe(true); + expect(isESM("export let charlie = 'goofy', maisey = 'silly';")).toBe(true); + expect(isESM("export const maisey = 'silly', charlie = 'goofy';")).toBe(true); + expect(isESM('export function doDogStuff() { /* ... */ }')).toBe(true); + expect(isESM('export class Dog { /* ... */ }')).toBe(true); + expect(isESM('export function* generateWayTooManyPhotosOnMyPhone() { /* ... */ }')).toBe(true); + expect(isESM('export const { maisey, charlie } = dogObject;')).toBe(true); + expect(isESM('export const { charlie, masiey: maiseyTheDog } = dogObject;')).toBe(true); + expect(isESM('export const [ maisey, charlie ] = dogArray;')).toBe(true); + }); + + it('recognizes lists of exports', () => { + expect(isESM('export { maisey, charlie };')).toBe(true); + expect(isESM('export { charlie as charlieMcCharlerson, masiey as theMaiseyMaiseyDog };')).toBe(true); + expect(isESM('export { charlie as default };')).toBe(true); + }); + + it('recognizes default exports', () => { + expect(isESM("export default 'dogs are great';")).toBe(true); + expect(isESM('export default function doDogStuff() { /* ... */ }')).toBe(true); + expect(isESM('export default class Dog { /* ... */ }')).toBe(true); + expect(isESM('export default function* generateWayTooManyPhotosOnMyPhone() { /* ... */ }')).toBe(true); + expect(isESM('export default function () { /* ... */ }')).toBe(true); + expect(isESM('export default class { /* ... */ }')).toBe(true); + expect(isESM('export default function* () { /* ... */ }')).toBe(true); + }); + + it('recognizes exports directly from another module', () => { + expect(isESM("export * from 'dogs';")).toBe(true); + expect(isESM("export * as dogs from 'dogs';")).toBe(true); + expect(isESM("export { maisey, charlie } from 'dogs';")).toBe(true); + expect( + isESM("export { maisey as goodGirl, charlie as omgWouldYouJustPeeAlreadyIWantToGoToBed } from 'dogs';"), + ).toBe(true); + expect(isESM("export { default } from 'dogs';")).toBe(true); + expect(isESM("export { default, maisey } from 'dogs';")).toBe(true); + }); +}); + +describe('potential false positives', () => { + it("doesn't get fooled by look-alikes", () => { + expect(isESM("'this is an import statement'")).toBe(false); + expect(isESM("'this is an export statement'")).toBe(false); + expect(isESM('import(dogs)')).toBe(false); + }); +}); From 84aa63bfa9048a9b5423476138018b6802ccee2f Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 1 Aug 2022 22:27:51 -0700 Subject: [PATCH 05/10] add template for wrapping data-fetching functions --- packages/nextjs/rollup.npm.config.js | 9 +++-- .../templates/dataFetchersLoaderTemplate.ts | 34 +++++++++++++++++++ packages/nextjs/tsconfig.types.json | 2 ++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 packages/nextjs/src/config/templates/dataFetchersLoaderTemplate.ts diff --git a/packages/nextjs/rollup.npm.config.js b/packages/nextjs/rollup.npm.config.js index 761b0aba3b83..310457960350 100644 --- a/packages/nextjs/rollup.npm.config.js +++ b/packages/nextjs/rollup.npm.config.js @@ -14,11 +14,15 @@ export default [ ), ...makeNPMConfigVariants( makeBaseNPMConfig({ - entrypoints: ['src/config/templates/prefixLoaderTemplate.ts'], + entrypoints: [ + 'src/config/templates/prefixLoaderTemplate.ts', + 'src/config/templates/dataFetchersLoaderTemplate.ts', + ], packageSpecificConfig: { output: { - // preserve the original file structure (i.e., so that everything is still relative to `src`) + // Preserve the original file structure (i.e., so that everything is still relative to `src`). (Not entirely + // clear why this is necessary here and not for other entrypoints in this file.) entryFileNames: 'config/templates/[name].js', // this is going to be add-on code, so it doesn't need the trappings of a full module (and in fact actively @@ -26,6 +30,7 @@ export default [ sourcemap: false, esModule: false, }, + external: ['@sentry/nextjs'], }, }), ), diff --git a/packages/nextjs/src/config/templates/dataFetchersLoaderTemplate.ts b/packages/nextjs/src/config/templates/dataFetchersLoaderTemplate.ts new file mode 100644 index 000000000000..b3fc03c45091 --- /dev/null +++ b/packages/nextjs/src/config/templates/dataFetchersLoaderTemplate.ts @@ -0,0 +1,34 @@ +import type { + GetServerSideProps as GetServerSidePropsFunction, + GetStaticPaths as GetStaticPathsFunction, + GetStaticProps as GetStaticPropsFunction, +} from 'next'; + +declare const __ORIG_GSSP__: GetServerSidePropsFunction; +declare const __ORIG_GSPROPS__: GetStaticPropsFunction; +declare const __ORIG_GSPATHS__: GetStaticPathsFunction; + +// We import the SDK under a purposefully clunky name, to lessen to near zero the chances of a name collision in case +// the user has also imported Sentry for some reason. (In the future, we could check for such a collision using the AST, +// but this is a lot simpler.) +// +// TODO: This import line is here because it needs to be in the injected code, but it also would (ideally) +// let us take advantage of typechecking, via the linter (both eslint and the TS linter), using intellisense, and when +// building. Solving for all five simultaneously seems to be tricky, however, because of the circular dependency. This +// is one of a number of possible compromise options, which seems to hit everything except eslint linting and +// typechecking via `tsc`. (TS linting and intellisense both work, though, so we do get at least some type safety.) See +// https://github.com/getsentry/sentry-javascript/pull/5503#discussion_r936827996 for more details. +// +// eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved +import * as ServerSideSentryNextjsSDK from '@sentry/nextjs'; + +export const getServerSideProps = + typeof __ORIG_GSSP__ === 'function' ? ServerSideSentryNextjsSDK.withSentryGSSP(__ORIG_GSSP__) : __ORIG_GSSP__; +export const getStaticProps = + typeof __ORIG_GSPROPS__ === 'function' + ? ServerSideSentryNextjsSDK.withSentryGSProps(__ORIG_GSPROPS__) + : __ORIG_GSPROPS__; +export const getStaticPaths = + typeof __ORIG_GSPATHS__ === 'function' + ? ServerSideSentryNextjsSDK.withSentryGSPaths(__ORIG_GSPATHS__) + : __ORIG_GSPATHS__; diff --git a/packages/nextjs/tsconfig.types.json b/packages/nextjs/tsconfig.types.json index 65455f66bd75..47e38534d75e 100644 --- a/packages/nextjs/tsconfig.types.json +++ b/packages/nextjs/tsconfig.types.json @@ -1,6 +1,8 @@ { "extends": "./tsconfig.json", + "exclude": ["src/config/templates/*"], + "compilerOptions": { "declaration": true, "declarationMap": true, From 93d6ffd710e6a51fa06a006462dda26cb4b32235 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 1 Aug 2022 22:30:16 -0700 Subject: [PATCH 06/10] add loader for wrapping data-fetching functions --- packages/nextjs/rollup.npm.config.js | 7 +- .../src/config/loaders/dataFetchersLoader.ts | 139 ++++++++++++++++++ packages/nextjs/src/config/loaders/index.ts | 2 + packages/nextjs/src/config/loaders/types.ts | 3 + 4 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 packages/nextjs/src/config/loaders/dataFetchersLoader.ts create mode 100644 packages/nextjs/src/config/loaders/index.ts diff --git a/packages/nextjs/rollup.npm.config.js b/packages/nextjs/rollup.npm.config.js index 310457960350..472ed5349095 100644 --- a/packages/nextjs/rollup.npm.config.js +++ b/packages/nextjs/rollup.npm.config.js @@ -36,15 +36,12 @@ export default [ ), ...makeNPMConfigVariants( makeBaseNPMConfig({ - entrypoints: ['src/config/loaders/prefixLoader.ts'], + entrypoints: ['src/config/loaders/index.ts'], packageSpecificConfig: { output: { // make it so Rollup calms down about the fact that we're doing `export { loader as default }` - exports: 'default', - - // preserve the original file structure (i.e., so that everything is still relative to `src`) - entryFileNames: 'config/loaders/[name].js', + exports: 'named', }, }, }), diff --git a/packages/nextjs/src/config/loaders/dataFetchersLoader.ts b/packages/nextjs/src/config/loaders/dataFetchersLoader.ts new file mode 100644 index 000000000000..34bdfac9031f --- /dev/null +++ b/packages/nextjs/src/config/loaders/dataFetchersLoader.ts @@ -0,0 +1,139 @@ +/** + * This loader auto-wraps a user's page-level data-fetching functions (`getStaticPaths`, `getStaticProps`, and + * `getServerSideProps`) in order to instrument them for tracing. At a high level, this is done by finding the relevant + * functions, renaming them so as not to create a name collision, and then creating a new version of each function which + * is a wrapped version of the original. We do this by parsing the user's code and some template code into ASTs, + * manipulating them, and then turning them back into strings and appending our template code to the user's (modified) + * page code. Greater detail and explanations can be found in situ in the functions below and in the helper functions in + * `ast.ts`. + */ + +import { logger } from '@sentry/utils'; +import * as fs from 'fs'; +import * as path from 'path'; + +import { isESM } from '../../utils/isESM'; +import type { AST } from './ast'; +import { findDeclarations, findExports, makeAST, removeComments, renameIdentifiers } from './ast'; +import type { LoaderThis } from './types'; + +// Map to keep track of each function's placeholder in the template and what it should be replaced with. (The latter +// will get added as we process the user code. Setting it to an empty string here means TS won't complain when we set it +// to a non-empty string later.) +const DATA_FETCHING_FUNCTIONS = { + getServerSideProps: { placeholder: '__ORIG_GSSP__', alias: '' }, + getStaticProps: { placeholder: '__ORIG_GSPROPS__', alias: '' }, + getStaticPaths: { placeholder: '__ORIG_GSPATHS__', alias: '' }, +}; + +type LoaderOptions = { + projectDir: string; +}; + +/** + * Find any data-fetching functions the user's code contains and rename them to prevent clashes, then whittle the + * template exporting wrapped versions instead down to only the functions found. + * + * @param userCode The source code of the current page file + * @param templateCode The source code of the full template, including all functions + * @param filepath The path to the current pagefile, within the project directory + * @returns A tuple of modified user and template code + */ +function wrapFunctions(userCode: string, templateCode: string, filepath: string): string[] { + let userAST: AST, templateAST: AST; + const isTS = new RegExp('\\.tsx?$').test(filepath); + + try { + userAST = makeAST(userCode, isTS); + templateAST = makeAST(templateCode, false); + } catch (err) { + logger.warn(`Couldn't add Sentry to ${filepath} because there was a parsing error: ${err}`); + // Replace the template code with an empty string, so in the end the user code is untouched + return [userCode, '']; + } + + // Comments are useful to have in the template for anyone reading it, but don't make sense to be injected into user + // code, because they're about the template-i-ness of the template, not the code itself + // TODO: Move this to our rollup build + removeComments(templateAST); + + for (const functionName of Object.keys(DATA_FETCHING_FUNCTIONS)) { + // Find and rename all identifiers whose name is `functionName` + const alias = renameIdentifiers(userAST, functionName); + + // `alias` will be defined iff the user code contains the function in question and renaming has been done + if (alias) { + // We keep track of the alias for each function, so that later on we can fill it in for the placeholder in the + // template. (Not doing that now because it's much more easily done once the template code has gone back to being + // a string.) + DATA_FETCHING_FUNCTIONS[functionName as keyof typeof DATA_FETCHING_FUNCTIONS].alias = alias; + } + + // Otherwise, if the current function doesn't exist anywhere in the user's code, delete the code in the template + // wrapping that function + // + // Note: We start with all of the possible wrapper lines in the template and delete the ones we don't need (rather + // than starting with none and adding in the ones we do need) because it allows them to live in our souce code as + // *code*. If we added them in, they'd have to be strings containing code, and we'd lose all of the benefits of + // syntax highlighting, linting, etc. + else { + // We have to look for declarations and exports separately because when we build the SDK, Rollup turns + // export const XXX = ... + // into + // const XXX = ... + // export { XXX } + findExports(templateAST, functionName).remove(); + findDeclarations(templateAST, functionName).remove(); + } + } + + return [userAST.toSource(), templateAST.toSource()]; +} + +/** + * Wrap `getStaticPaths`, `getStaticProps`, and `getServerSideProps` (if they exist) in the given page code + */ +function wrapDataFetchersLoader(this: LoaderThis, userCode: string): string { + // We know one or the other will be defined, depending on the version of webpack being used + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { projectDir } = this.getOptions ? this.getOptions() : this.query!; + + // For now this loader only works for ESM code + if (!isESM(userCode)) { + return userCode; + } + + // If none of the functions we want to wrap appears in the page's code, there's nothing to do. (Note: We do this as a + // simple substring match (rather than waiting until we've parsed the code) because it's meant to be an + // as-fast-as-possible fail-fast. It's possible for user code to pass this check, even if it contains none of the + // functions in question, just by virtue of the correct string having been found, be it in a comment, as part of a + // longer variable name, etc. That said, when we actually do the code manipulation we'll be working on the code's AST, + // meaning we'll be able to differentiate between code we actually want to change and any false positives which might + // come up here.) + if (Object.keys(DATA_FETCHING_FUNCTIONS).every(functionName => !userCode.includes(functionName))) { + return userCode; + } + + const templatePath = path.resolve(__dirname, '../templates/dataFetchersLoaderTemplate.js'); + // make sure the template is included when runing `webpack watch` + this.addDependency(templatePath); + + const templateCode = fs.readFileSync(templatePath).toString(); + + const [modifiedUserCode, modifiedTemplateCode] = wrapFunctions( + userCode, + templateCode, + // Relative path to the page we're currently processing, for use in error messages + path.relative(projectDir, this.resourcePath), + ); + + // Fill in template placeholders + let injectedCode = modifiedTemplateCode; + for (const { placeholder, alias } of Object.values(DATA_FETCHING_FUNCTIONS)) { + injectedCode = injectedCode.replace(placeholder, alias); + } + + return `${modifiedUserCode}\n${injectedCode}`; +} + +export { wrapDataFetchersLoader as default }; diff --git a/packages/nextjs/src/config/loaders/index.ts b/packages/nextjs/src/config/loaders/index.ts new file mode 100644 index 000000000000..9bda9db7cd2b --- /dev/null +++ b/packages/nextjs/src/config/loaders/index.ts @@ -0,0 +1,2 @@ +export { default as prefixLoader } from './prefixLoader'; +export { default as dataFetchersLoader } from './dataFetchersLoader'; diff --git a/packages/nextjs/src/config/loaders/types.ts b/packages/nextjs/src/config/loaders/types.ts index aa79b5c1d5e7..17f7a737c72a 100644 --- a/packages/nextjs/src/config/loaders/types.ts +++ b/packages/nextjs/src/config/loaders/types.ts @@ -1,5 +1,8 @@ // TODO Use real webpack types export type LoaderThis = { + // Path to the file being loaded + resourcePath: string; + // Loader options in Webpack 4 query?: Options; // Loader options in Webpack 5 From 0dabf8d0b80bca63cfb0243678286cf49186279b Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 1 Aug 2022 22:33:32 -0700 Subject: [PATCH 07/10] add and export pass-through wrapper functions --- packages/nextjs/src/config/wrappers/index.ts | 3 ++ packages/nextjs/src/config/wrappers/types.ts | 37 +++++++++++++++++++ .../src/config/wrappers/withSentryGSPaths.ts | 16 ++++++++ .../src/config/wrappers/withSentryGSProps.ts | 16 ++++++++ .../src/config/wrappers/withSentryGSSP.ts | 16 ++++++++ .../src/config/wrappers/wrapperUtils.ts | 25 +++++++++++++ packages/nextjs/src/index.server.ts | 3 +- 7 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 packages/nextjs/src/config/wrappers/index.ts create mode 100644 packages/nextjs/src/config/wrappers/types.ts create mode 100644 packages/nextjs/src/config/wrappers/withSentryGSPaths.ts create mode 100644 packages/nextjs/src/config/wrappers/withSentryGSProps.ts create mode 100644 packages/nextjs/src/config/wrappers/withSentryGSSP.ts create mode 100644 packages/nextjs/src/config/wrappers/wrapperUtils.ts diff --git a/packages/nextjs/src/config/wrappers/index.ts b/packages/nextjs/src/config/wrappers/index.ts new file mode 100644 index 000000000000..ee3fdcdd8f2f --- /dev/null +++ b/packages/nextjs/src/config/wrappers/index.ts @@ -0,0 +1,3 @@ +export { withSentryGSPaths } from './withSentryGSPaths'; +export { withSentryGSProps } from './withSentryGSProps'; +export { withSentryGSSP } from './withSentryGSSP'; diff --git a/packages/nextjs/src/config/wrappers/types.ts b/packages/nextjs/src/config/wrappers/types.ts new file mode 100644 index 000000000000..5587aa1a71f7 --- /dev/null +++ b/packages/nextjs/src/config/wrappers/types.ts @@ -0,0 +1,37 @@ +import type { + GetServerSideProps, + GetServerSidePropsContext, + GetServerSidePropsResult, + GetStaticPaths, + GetStaticPathsContext, + GetStaticPathsResult, + GetStaticProps, + GetStaticPropsContext, + GetStaticPropsResult, +} from 'next'; + +type Paths = { [key: string]: string | string[] }; +type Props = { [key: string]: unknown }; + +export type GSPaths = { + fn: GetStaticPaths; + wrappedFn: GetStaticPaths; + context: GetStaticPathsContext; + result: GetStaticPathsResult; +}; + +export type GSProps = { + fn: GetStaticProps; + wrappedFn: GetStaticProps; + context: GetStaticPropsContext; + result: GetStaticPropsResult; +}; + +export type GSSP = { + fn: GetServerSideProps; + wrappedFn: GetServerSideProps; + context: GetServerSidePropsContext; + result: GetServerSidePropsResult; +}; + +export type DataFetchingFunction = GSPaths | GSProps | GSSP; diff --git a/packages/nextjs/src/config/wrappers/withSentryGSPaths.ts b/packages/nextjs/src/config/wrappers/withSentryGSPaths.ts new file mode 100644 index 000000000000..7161f81f9780 --- /dev/null +++ b/packages/nextjs/src/config/wrappers/withSentryGSPaths.ts @@ -0,0 +1,16 @@ +import type { GSPaths } from './types'; +import { callOriginal } from './wrapperUtils'; + +/** + * Create a wrapped version of the user's exported `getStaticPaths` function + * + * @param origGSPaths: The user's `getStaticPaths` function + * @returns A wrapped version of the function + */ +export function withSentryGSPaths(origGSPaths: GSPaths['fn']): GSPaths['wrappedFn'] { + const wrappedGSPaths = async function (context: GSPaths['context']): Promise { + return callOriginal(origGSPaths, context); + }; + + return wrappedGSPaths; +} diff --git a/packages/nextjs/src/config/wrappers/withSentryGSProps.ts b/packages/nextjs/src/config/wrappers/withSentryGSProps.ts new file mode 100644 index 000000000000..7121de35f28b --- /dev/null +++ b/packages/nextjs/src/config/wrappers/withSentryGSProps.ts @@ -0,0 +1,16 @@ +import { GSProps } from './types'; +import { callOriginal } from './wrapperUtils'; + +/** + * Create a wrapped version of the user's exported `getStaticProps` function + * + * @param origGSProps: The user's `getStaticProps` function + * @returns A wrapped version of the function + */ +export function withSentryGSProps(origGSProps: GSProps['fn']): GSProps['wrappedFn'] { + const wrappedGSProps = async function (context: GSProps['context']): Promise { + return callOriginal(origGSProps, context); + }; + + return wrappedGSProps; +} diff --git a/packages/nextjs/src/config/wrappers/withSentryGSSP.ts b/packages/nextjs/src/config/wrappers/withSentryGSSP.ts new file mode 100644 index 000000000000..ddcc8f8035ca --- /dev/null +++ b/packages/nextjs/src/config/wrappers/withSentryGSSP.ts @@ -0,0 +1,16 @@ +import { GSSP } from './types'; +import { callOriginal } from './wrapperUtils'; + +/** + * Create a wrapped version of the user's exported `getServerSideProps` function + * + * @param origGSSP: The user's `getServerSideProps` function + * @returns A wrapped version of the function + */ +export function withSentryGSSP(origGSSP: GSSP['fn']): GSSP['wrappedFn'] { + const wrappedGSSP = async function (context: GSSP['context']): Promise { + return callOriginal(origGSSP, context); + }; + + return wrappedGSSP; +} diff --git a/packages/nextjs/src/config/wrappers/wrapperUtils.ts b/packages/nextjs/src/config/wrappers/wrapperUtils.ts new file mode 100644 index 000000000000..6e6162588233 --- /dev/null +++ b/packages/nextjs/src/config/wrappers/wrapperUtils.ts @@ -0,0 +1,25 @@ +import { DataFetchingFunction } from './types'; + +/** + * Pass-through wrapper for the original function, used as a first step in eventually wrapping the data-fetching + * functions with code for tracing. + * + * @template T Types for `getStaticPaths`, `getStaticProps`, and `getServerSideProps` + * @param origFunction The user's exported `getStaticPaths`, `getStaticProps`, or `getServerSideProps` function + * @param context The context object passed by nextjs to the function + * @returns The result of calling the user's function + */ +export async function callOriginal( + origFunction: T['fn'], + context: T['context'], +): Promise { + let pathsOrProps; + + // TODO: Can't figure out how to tell TS that the types are correlated - that a `GSPropsFunction` will only get passed + // `GSPropsContext` and never, say, `GSSPContext`. That's what wrapping everything in objects and using the generic + // and pulling the types from the generic rather than specifying them directly was supposed to do, but... no luck. + // eslint-disable-next-line prefer-const, @typescript-eslint/no-explicit-any + pathsOrProps = await (origFunction as any)(context); + + return pathsOrProps; +} diff --git a/packages/nextjs/src/index.server.ts b/packages/nextjs/src/index.server.ts index e5d43a44fd8c..ef398eb49d8f 100644 --- a/packages/nextjs/src/index.server.ts +++ b/packages/nextjs/src/index.server.ts @@ -124,8 +124,9 @@ function addServerIntegrations(options: NextjsOptions): void { export type { SentryWebpackPluginOptions } from './config/types'; export { withSentryConfig } from './config'; -export { withSentry } from './utils/withSentry'; export { isBuild } from './utils/isBuild'; +export { withSentryGSProps, withSentryGSSP, withSentryGSPaths } from './config/wrappers'; +export { withSentry } from './utils/withSentry'; // Wrap various server methods to enable error monitoring and tracing. (Note: This only happens for non-Vercel // deployments, because the current method of doing the wrapping a) crashes Next 12 apps deployed to Vercel and From 565f25922c93f7d26fec90d8783044c8b28ac884 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 1 Aug 2022 22:33:45 -0700 Subject: [PATCH 08/10] use loader in webpack config --- packages/nextjs/src/config/webpack.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 04346de8b7c5..9bd5dd3ae602 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ import { getSentryRelease } from '@sentry/node'; -import { dropUndefinedKeys, logger } from '@sentry/utils'; +import { dropUndefinedKeys, escapeStringForRegex, logger } from '@sentry/utils'; import { default as SentryWebpackPlugin } from '@sentry/webpack-plugin'; import * as fs from 'fs'; import * as path from 'path'; @@ -53,6 +53,8 @@ export function constructWebpackConfigFunction( newConfig = userNextConfig.webpack(newConfig, buildContext); } + const pageRegex = new RegExp(`${escapeStringForRegex(projectDir)}(/src)?/pages(/.+)\\.(jsx?|tsx?)`); + if (isServer) { newConfig.module = { ...newConfig.module, @@ -72,6 +74,15 @@ export function constructWebpackConfigFunction( }, ], }, + { + test: pageRegex, + use: [ + { + loader: path.resolve(__dirname, 'loaders/dataFetchersLoader.js'), + options: { projectDir }, + }, + ], + }, ], }; } From cf0056edc4ce91b7e34eaf6f9cb3a46a9abfa232 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 3 Aug 2022 21:44:26 -0700 Subject: [PATCH 09/10] switch to using `export default` for loaders --- packages/nextjs/src/config/loaders/dataFetchersLoader.ts | 4 +--- packages/nextjs/src/config/loaders/prefixLoader.ts | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/nextjs/src/config/loaders/dataFetchersLoader.ts b/packages/nextjs/src/config/loaders/dataFetchersLoader.ts index 34bdfac9031f..2758a0f49807 100644 --- a/packages/nextjs/src/config/loaders/dataFetchersLoader.ts +++ b/packages/nextjs/src/config/loaders/dataFetchersLoader.ts @@ -93,7 +93,7 @@ function wrapFunctions(userCode: string, templateCode: string, filepath: string) /** * Wrap `getStaticPaths`, `getStaticProps`, and `getServerSideProps` (if they exist) in the given page code */ -function wrapDataFetchersLoader(this: LoaderThis, userCode: string): string { +export default function wrapDataFetchersLoader(this: LoaderThis, userCode: string): string { // We know one or the other will be defined, depending on the version of webpack being used // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const { projectDir } = this.getOptions ? this.getOptions() : this.query!; @@ -135,5 +135,3 @@ function wrapDataFetchersLoader(this: LoaderThis, userCode: strin return `${modifiedUserCode}\n${injectedCode}`; } - -export { wrapDataFetchersLoader as default }; diff --git a/packages/nextjs/src/config/loaders/prefixLoader.ts b/packages/nextjs/src/config/loaders/prefixLoader.ts index 5e95a32e8342..668ca194dfbb 100644 --- a/packages/nextjs/src/config/loaders/prefixLoader.ts +++ b/packages/nextjs/src/config/loaders/prefixLoader.ts @@ -10,7 +10,7 @@ type LoaderOptions = { /** * Inject templated code into the beginning of a module. */ -function prefixLoader(this: LoaderThis, userCode: string): string { +export default function prefixLoader(this: LoaderThis, userCode: string): string { // We know one or the other will be defined, depending on the version of webpack being used // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const { distDir } = this.getOptions ? this.getOptions() : this.query!; @@ -25,5 +25,3 @@ function prefixLoader(this: LoaderThis, userCode: string): string return `${templateCode}\n${userCode}`; } - -export { prefixLoader as default }; From d9e41189f74cf6dd2444c38a0b3e4312695519d7 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Sun, 7 Aug 2022 21:46:43 -0700 Subject: [PATCH 10/10] add flag to enable auto-wrapping --- packages/nextjs/src/config/types.ts | 4 ++++ packages/nextjs/src/config/webpack.ts | 21 ++++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index 2a5ee51d9d57..ee7643ef491e 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -49,6 +49,10 @@ export type UserSentryOptions = { // uploaded. At the same time, we don't want to widen the scope if we don't have to, because we're guaranteed to end // up uploading too many files, which is why this defaults to `false`. widenClientFileUpload?: boolean; + + // Automatically wrap `getServerSideProps`, `getStaticProps`, and `getStaticPaths` in order to instrument them for + // tracing. + autoWrapDataFetchers?: boolean; }; export type NextConfigFunction = (phase: string, defaults: { defaultConfig: NextConfigObject }) => NextConfigObject; diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 9bd5dd3ae602..f1eabb351094 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -74,17 +74,20 @@ export function constructWebpackConfigFunction( }, ], }, - { - test: pageRegex, - use: [ - { - loader: path.resolve(__dirname, 'loaders/dataFetchersLoader.js'), - options: { projectDir }, - }, - ], - }, ], }; + + if (userSentryOptions.autoWrapDataFetchers) { + newConfig.module.rules.push({ + test: pageRegex, + use: [ + { + loader: path.resolve(__dirname, 'loaders/dataFetchersLoader.js'), + options: { projectDir }, + }, + ], + }); + } } // The SDK uses syntax (ES6 and ES6+ features like object spread) which isn't supported by older browsers. For users