diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index f7817129ce80..2be9eac99558 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "@babel/parser": "^7.18.10", + "@rollup/plugin-sucrase": "4.0.4", "@sentry/core": "7.11.1", "@sentry/hub": "7.11.1", "@sentry/integrations": "7.11.1", @@ -28,10 +29,12 @@ "@sentry/utils": "7.11.1", "@sentry/webpack-plugin": "1.19.0", "jscodeshift": "^0.13.1", + "rollup": "2.78.0", "tslib": "^1.9.3" }, "devDependencies": { "@babel/types": "7.18.10", + "@sentry/nextjs": "7.11.1", "@types/jscodeshift": "^0.11.5", "@types/webpack": "^4.41.31", "next": "10.1.3" diff --git a/packages/nextjs/rollup.npm.config.js b/packages/nextjs/rollup.npm.config.js index 472ed5349095..46224cfd600e 100644 --- a/packages/nextjs/rollup.npm.config.js +++ b/packages/nextjs/rollup.npm.config.js @@ -1,4 +1,4 @@ -import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; +import { makeBaseNPMConfig, makeNPMConfigVariants, plugins } from '../../rollup/index.js'; export default [ ...makeNPMConfigVariants( @@ -16,10 +16,12 @@ export default [ makeBaseNPMConfig({ entrypoints: [ 'src/config/templates/prefixLoaderTemplate.ts', + 'src/config/templates/proxyLoaderTemplate.ts', 'src/config/templates/dataFetchersLoaderTemplate.ts', ], packageSpecificConfig: { + plugins: [plugins.makeRemoveMultiLineCommentsPlugin()], output: { // 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.) @@ -29,20 +31,26 @@ export default [ // shouldn't have them, lest they muck with the module to which we're adding it) sourcemap: false, esModule: false, + + // make it so Rollup calms down about the fact that we're combining default and named exports + exports: 'named', }, - external: ['@sentry/nextjs'], + external: ['@sentry/nextjs', '__RESOURCE_PATH__'], }, }), ), ...makeNPMConfigVariants( makeBaseNPMConfig({ entrypoints: ['src/config/loaders/index.ts'], + // Needed in order to successfully import sucrase + esModuleInterop: true, packageSpecificConfig: { output: { - // make it so Rollup calms down about the fact that we're doing `export { loader as default }` + // make it so Rollup calms down about the fact that we're combining default and named exports exports: 'named', }, + external: ['@rollup/plugin-sucrase', 'rollup'], }, }), ), diff --git a/packages/nextjs/src/config/loaders/index.ts b/packages/nextjs/src/config/loaders/index.ts index 9bda9db7cd2b..bdd71281707d 100644 --- a/packages/nextjs/src/config/loaders/index.ts +++ b/packages/nextjs/src/config/loaders/index.ts @@ -1,2 +1,3 @@ export { default as prefixLoader } from './prefixLoader'; export { default as dataFetchersLoader } from './dataFetchersLoader'; +export { default as proxyLoader } from './proxyLoader'; diff --git a/packages/nextjs/src/config/loaders/proxyLoader.ts b/packages/nextjs/src/config/loaders/proxyLoader.ts new file mode 100644 index 000000000000..f0cf96b11651 --- /dev/null +++ b/packages/nextjs/src/config/loaders/proxyLoader.ts @@ -0,0 +1,91 @@ +import { escapeStringForRegex } from '@sentry/utils'; +import * as fs from 'fs'; +import * as path from 'path'; + +import { rollupize } from './rollup'; +import { LoaderThis } from './types'; + +type LoaderOptions = { + pagesDir: string; +}; + +/** + * Replace the loaded file with a proxy module "wrapping" the original file. In the proxy, the original file is loaded, + * any data-fetching functions (`getInitialProps`, `getStaticProps`, and `getServerSideProps`) it contains are wrapped, + * and then everything is re-exported. + */ +export default async function proxyLoader(this: LoaderThis, userCode: string): Promise { + // We know one or the other will be defined, depending on the version of webpack being used + const { pagesDir } = 'getOptions' in this ? this.getOptions() : this.query; + + // Get the parameterized route name from this page's filepath + const parameterizedRoute = path + // Get the path of the file insde of the pages directory + .relative(pagesDir, this.resourcePath) + // Add a slash at the beginning + .replace(/(.*)/, '/$1') + // Pull off the file extension + .replace(/\.(jsx?|tsx?)/, '') + // Any page file named `index` corresponds to root of the directory its in, URL-wise, so turn `/xyz/index` into + // just `/xyz` + .replace(/\/index$/, '') + // In case all of the above have left us with an empty string (which will happen if we're dealing with the + // homepage), sub back in the root route + .replace(/^$/, '/'); + + // TODO: For the moment we skip API routes. Those will need to be handled slightly differently because of the manual + // wrapping we've already been having people do using `withSentry`. + if (parameterizedRoute.startsWith('api')) { + return userCode; + } + + // We don't want to wrap twice (or infinitely), so in the proxy we add this query string onto references to the + // wrapped file, so that we know that it's already been processed. (Adding this query string is also necessary to + // convince webpack that it's a different file than the one it's in the middle of loading now, so that the originals + // themselves will have a chance to load.) + if (this.resourceQuery.includes('__sentry_wrapped__')) { + return userCode; + } + + const templatePath = path.resolve(__dirname, '../templates/proxyLoaderTemplate.js'); + let templateCode = fs.readFileSync(templatePath).toString(); + // Make sure the template is included when runing `webpack watch` + this.addDependency(templatePath); + + // Inject the route into the template + templateCode = templateCode.replace(/__ROUTE__/g, parameterizedRoute); + + // Fill in the path to the file we're wrapping and save the result as a temporary file in the same folder (so that + // relative imports and exports are calculated correctly). + // + // TODO: We're saving the filled-in template to disk, however temporarily, because Rollup expects a path to a code + // file, not code itself. There is a rollup plugin which can fake this (`@rollup/plugin-virtual`) but the virtual file + // seems to be inside of a virtual directory (in other words, one level down from where you'd expect it) and that + // messes up relative imports and exports. Presumably there's a way to make it work, though, and if we can, it would + // be cleaner than having to first write and then delete a temporary file each time we run this loader. + templateCode = templateCode.replace(/__RESOURCE_PATH__/g, this.resourcePath); + const tempFilePath = path.resolve(path.dirname(this.resourcePath), `temp${Math.random()}.js`); + fs.writeFileSync(tempFilePath, templateCode); + + // Run the proxy module code through Rollup, in order to split the `export * from ''` out into + // individual exports (which nextjs seems to require), then delete the tempoary file. + let proxyCode = await rollupize(tempFilePath, this.resourcePath); + fs.unlinkSync(tempFilePath); + + if (!proxyCode) { + // We will already have thrown a warning in `rollupize`, so no need to do it again here + return userCode; + } + + // Add a query string onto all references to the wrapped file, so that webpack will consider it different from the + // non-query-stringged version (which we're already in the middle of loading as we speak), and load it separately from + // this. When the second load happens this loader will run again, but we'll be able to see the query string and will + // know to immediately return without processing. This avoids an infinite loop. + const resourceFilename = path.basename(this.resourcePath); + proxyCode = proxyCode.replace( + new RegExp(`/${escapeStringForRegex(resourceFilename)}'`, 'g'), + `/${resourceFilename}?__sentry_wrapped__'`, + ); + + return proxyCode; +} diff --git a/packages/nextjs/src/config/loaders/rollup.ts b/packages/nextjs/src/config/loaders/rollup.ts new file mode 100644 index 000000000000..67daf63b0eca --- /dev/null +++ b/packages/nextjs/src/config/loaders/rollup.ts @@ -0,0 +1,103 @@ +import type { RollupSucraseOptions } from '@rollup/plugin-sucrase'; +import sucrase from '@rollup/plugin-sucrase'; +import { logger } from '@sentry/utils'; +import * as path from 'path'; +import type { InputOptions as RollupInputOptions, OutputOptions as RollupOutputOptions } from 'rollup'; +import { rollup } from 'rollup'; + +const getRollupInputOptions: (proxyPath: string, resourcePath: string) => RollupInputOptions = ( + proxyPath, + resourcePath, +) => ({ + input: proxyPath, + plugins: [ + // For some reason, even though everything in `RollupSucraseOptions` besides `transforms` is supposed to be + // optional, TS complains that there are a bunch of missing properties (hence the typecast). Similar to + // https://github.com/microsoft/TypeScript/issues/20722, though that's been fixed. (In this case it's an interface + // exporting a `Pick` picking optional properties which is turning them required somehow.)' + sucrase({ + transforms: ['jsx', 'typescript'], + } as unknown as RollupSucraseOptions), + ], + + // We want to process as few files as possible, so as not to slow down the build any more than we have to. We need the + // proxy module (living in the temporary file we've created) and the file we're wrapping not to be external, because + // otherwise they won't be processed. (We need Rollup to process the former so that we can use the code, and we need + // it to process the latter so it knows what exports to re-export from the proxy module.) Past that, we don't care, so + // don't bother to process anything else. + external: importPath => importPath !== proxyPath && importPath !== resourcePath, + + // Prevent rollup from stressing out about TS's use of global `this` when polyfilling await. (TS will polyfill if the + // user's tsconfig `target` is set to anything before `es2017`. See https://stackoverflow.com/a/72822340 and + // https://stackoverflow.com/a/60347490.) + context: 'this', + + // Rollup's path-resolution logic when handling re-exports can go wrong when wrapping pages which aren't at the root + // level of the `pages` directory. This may be a bug, as it doesn't match the behavior described in the docs, but what + // seems to happen is this: + // + // - We try to wrap `pages/xyz/userPage.js`, which contains `export { helperFunc } from '../../utils/helper'` + // - Rollup converts '../../utils/helper' into an absolute path + // - We mark the helper module as external + // - Rollup then converts it back to a relative path, but relative to `pages/` rather than `pages/xyz/`. (This is + // the part which doesn't match the docs. They say that Rollup will use the common ancestor of all modules in the + // bundle as the basis for the relative path calculation, but both our temporary file and the page being wrapped + // live in `pages/xyz/`, and they're the only two files in the bundle, so `pages/xyz/`` should be used as the + // root. Unclear why it's not.) + // - As a result of the miscalculation, our proxy module will include `export { helperFunc } from '../utils/helper'` + // rather than the expected `export { helperFunc } from '../../utils/helper'`, thereby causing a build error in + // nextjs.. + // + // It's not 100% clear why, but telling it not to do the conversion back from absolute to relative (by setting + // `makeAbsoluteExternalsRelative` to `false`) seems to also prevent it from going from relative to absolute in the + // first place, with the result that the path remains untouched (which is what we want.) + makeAbsoluteExternalsRelative: false, +}); + +const rollupOutputOptions: RollupOutputOptions = { + format: 'esm', + + // Don't create a bundle - we just want the transformed entrypoint file + preserveModules: true, +}; + +/** + * Use Rollup to process the proxy module file (located at `tempProxyFilePath`) in order to split its `export * from + * ''` call into individual exports (which nextjs seems to need). + * + * @param tempProxyFilePath The path to the temporary file containing the proxy module code + * @param resourcePath The path to the file being wrapped + * @returns The processed proxy module code, or undefined if an error occurs + */ +export async function rollupize(tempProxyFilePath: string, resourcePath: string): Promise { + let finalBundle; + + try { + const intermediateBundle = await rollup(getRollupInputOptions(tempProxyFilePath, resourcePath)); + finalBundle = await intermediateBundle.generate(rollupOutputOptions); + } catch (err) { + __DEBUG_BUILD__ && + logger.warn( + `Could not wrap ${resourcePath}. An error occurred while processing the proxy module template:\n${err}`, + ); + return undefined; + } + + // The module at index 0 is always the entrypoint, which in this case is the proxy module. + let { code } = finalBundle.output[0]; + + // Rollup does a few things to the code we *don't* want. Undo those changes before returning the code. + // + // Nextjs uses square brackets surrounding a path segment to denote a parameter in the route, but Rollup turns those + // square brackets into underscores. Further, Rollup adds file extensions to bare-path-type import and export sources. + // Because it assumes that everything will have already been processed, it always uses `.js` as the added extension. + // We need to restore the original name and extension so that Webpack will be able to find the wrapped file. + const resourceFilename = path.basename(resourcePath); + const mutatedResourceFilename = resourceFilename + // `[\\[\\]]` is the character class containing `[` and `]` + .replace(new RegExp('[\\[\\]]', 'g'), '_') + .replace(/(jsx?|tsx?)$/, 'js'); + code = code.replace(new RegExp(mutatedResourceFilename, 'g'), resourceFilename); + + return code; +} diff --git a/packages/nextjs/src/config/templates/proxyLoaderTemplate.ts b/packages/nextjs/src/config/templates/proxyLoaderTemplate.ts new file mode 100644 index 000000000000..e303ebc24ea0 --- /dev/null +++ b/packages/nextjs/src/config/templates/proxyLoaderTemplate.ts @@ -0,0 +1,51 @@ +/** + * This file is a template for the code which will be substituted when our webpack loader handles non-API files in the + * `pages/` directory. + * + * We use `__RESOURCE_PATH__` as a placeholder for the path to the file being wrapped. Because it's not a real package, + * this causes both TS and ESLint to complain, hence the pragma comments below. + */ + +// @ts-ignore See above +// eslint-disable-next-line import/no-unresolved +import * as wrapee from '__RESOURCE_PATH__'; +import * as Sentry from '@sentry/nextjs'; +import type { GetServerSideProps, GetStaticProps, NextPage as NextPageComponent } from 'next'; + +type NextPageModule = { + default: { getInitialProps?: NextPageComponent['getInitialProps'] }; + getStaticProps?: GetStaticProps; + getServerSideProps?: GetServerSideProps; +}; + +const userPageModule = wrapee as NextPageModule; + +const pageComponent = userPageModule.default; + +const origGetInitialProps = pageComponent.getInitialProps; +const origGetStaticProps = userPageModule.getStaticProps; +const origGetServerSideProps = userPageModule.getServerSideProps; + +if (typeof origGetInitialProps === 'function') { + pageComponent.getInitialProps = Sentry.withSentryGetInitialProps( + origGetInitialProps, + '__ROUTE__', + ) as NextPageComponent['getInitialProps']; +} + +export const getStaticProps = + typeof origGetStaticProps === 'function' + ? Sentry.withSentryGetStaticProps(origGetStaticProps, '__ROUTE__') + : undefined; +export const getServerSideProps = + typeof origGetServerSideProps === 'function' + ? Sentry.withSentryGetServerSideProps(origGetServerSideProps, '__ROUTE__') + : undefined; + +export default pageComponent; + +// Re-export anything exported by the page module we're wrapping. When processing this code, Rollup is smart enough to +// not include anything whose name matchs something we've explicitly exported above. +// @ts-ignore See above +// eslint-disable-next-line import/no-unresolved +export * from '__RESOURCE_PATH__'; diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 93b79a1c7313..dcc1b909ab0d 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -86,8 +86,8 @@ export function constructWebpackConfigFunction( test: new RegExp(`${escapeStringForRegex(projectDir)}(/src)?/pages/.*\\.(jsx?|tsx?)`), use: [ { - loader: path.resolve(__dirname, 'loaders/dataFetchersLoader.js'), - options: { projectDir, pagesDir }, + loader: path.resolve(__dirname, 'loaders/proxyLoader.js'), + options: { pagesDir }, }, ], }); diff --git a/packages/nextjs/test/run-integration-tests.sh b/packages/nextjs/test/run-integration-tests.sh index 715273afb42c..7253c7b66130 100755 --- a/packages/nextjs/test/run-integration-tests.sh +++ b/packages/nextjs/test/run-integration-tests.sh @@ -61,7 +61,9 @@ for NEXTJS_VERSION in 10 11 12; do else sed -i /"next.*latest"/s/latest/"${NEXTJS_VERSION}.x"/ package.json fi - yarn --no-lockfile --silent >/dev/null 2>&1 + # We have to use `--ignore-engines` because sucrase claims to need Node 12, even though tests pass just fine on Node + # 10 + yarn --no-lockfile --ignore-engines --silent >/dev/null 2>&1 # if applicable, use local versions of `@sentry/cli` and/or `@sentry/webpack-plugin` (these commands no-op unless # LINKED_CLI_REPO and/or LINKED_PLUGIN_REPO are set) linkcli && linkplugin @@ -90,7 +92,6 @@ for NEXTJS_VERSION in 10 11 12; do exit 0 fi - # next 10 defaults to webpack 4 and next 11 defaults to webpack 5, but each can use either based on settings if [ "$NEXTJS_VERSION" -eq "10" ]; then sed "s/%RUN_WEBPACK_5%/$RUN_WEBPACK_5/g" next.config.js diff --git a/packages/nextjs/tsconfig.types.json b/packages/nextjs/tsconfig.types.json index 47e38534d75e..978b51b8e126 100644 --- a/packages/nextjs/tsconfig.types.json +++ b/packages/nextjs/tsconfig.types.json @@ -1,6 +1,10 @@ { "extends": "./tsconfig.json", + // Some of the templates for code we inject into a user's app include an import from `@sentry/nextjs`. This makes + // creating types for these template files a circular exercise, which causes `tsc` to crash. Fortunately, since the + // templates aren't consumed as modules (they're essentially just text files which happen to contain code), we don't + // actually need to create types for them. "exclude": ["src/config/templates/*"], "compilerOptions": { diff --git a/rollup/plugins/npmPlugins.js b/rollup/plugins/npmPlugins.js index 87e2927d53bd..23c9e4fff864 100644 --- a/rollup/plugins/npmPlugins.js +++ b/rollup/plugins/npmPlugins.js @@ -127,6 +127,23 @@ export function makeRemoveBlankLinesPlugin() { }); } +/** + * Create a plugin to strip multi-line comments from the output. + * + * @returns A `rollup-plugin-re` instance. + */ +export function makeRemoveMultiLineCommentsPlugin() { + return regexReplace({ + patterns: [ + { + // The `s` flag makes the dot match newlines + test: /^\/\*.*\*\//gs, + replace: '', + }, + ], + }); +} + /** * Creates a plugin to replace all instances of "__DEBUG_BUILD__" with a safe statement that * a) evaluates to `true` diff --git a/yarn.lock b/yarn.lock index e2ae1044a9b0..23fdaff9c3d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4765,6 +4765,14 @@ "@rollup/pluginutils" "^3.1.0" magic-string "^0.25.7" +"@rollup/plugin-sucrase@4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@rollup/plugin-sucrase/-/plugin-sucrase-4.0.4.tgz#0a3b3d97cdc239ec3399f5a10711f751e9f95d98" + integrity sha512-YH4J8yoJb5EVnLhAwWxYAQNh2SJOR+SdZ6XdgoKEv6Kxm33riYkM8MlMaggN87UoISP52qAFyZ5ey56wu6umGg== + dependencies: + "@rollup/pluginutils" "^4.1.1" + sucrase "^3.20.0" + "@rollup/plugin-sucrase@^4.0.3": version "4.0.3" resolved "https://registry.yarnpkg.com/@rollup/plugin-sucrase/-/plugin-sucrase-4.0.3.tgz#b972ba61db0faaba397e09daaffcdbd38c167e2c" @@ -7644,7 +7652,7 @@ babel-plugin-syntax-exponentiation-operator@^6.8.0: babel-plugin-syntax-jsx@6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" - integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= + integrity sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw== babel-plugin-syntax-trailing-function-commas@^6.22.0: version "6.22.0" @@ -9378,11 +9386,16 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001173, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001254, caniuse-lite@^1.0.30001274, caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001317: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001254, caniuse-lite@^1.0.30001274, caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001317: version "1.0.30001339" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001339.tgz" integrity sha512-Es8PiVqCe+uXdms0Gu5xP5PF2bxLR7OBp3wUzUnuO7OHzhOfCyg3hdiGWVPVxhiuniOzng+hTc1u3fEQ0TlkSQ== +caniuse-lite@^1.0.30001173, caniuse-lite@^1.0.30001179: + version "1.0.30001378" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001378.tgz#3d2159bf5a8f9ca093275b0d3ecc717b00f27b67" + integrity sha512-JVQnfoO7FK7WvU4ZkBRbPjaot4+YqxogSDosHv0Hv5mWpUESmN+UubMU6L/hGz8QlQ2aY5U0vR6MOs6j/CXpNA== + canonical-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" @@ -11572,11 +11585,16 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.634, electron-to-chromium@^1.3.830: +electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.830: version "1.3.839" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.839.tgz#27a5b21468e9fefb0e328a029403617f20acec9c" integrity sha512-0O7uPs9LJNjQ/U5mW78qW8gXv9H6Ba3DHZ5/yt8aBsvomOWDkV3MddT7enUYvLQEUVOURjWmgJJWVZ3K98tIwQ== +electron-to-chromium@^1.3.634: + version "1.4.222" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.222.tgz#2ba24bef613fc1985dbffea85df8f62f2dec6448" + integrity sha512-gEM2awN5HZknWdLbngk4uQCVfhucFAfFzuchP3wM3NN6eow1eDU0dFy2kts43FB20ZfhVFF0jmFSTb1h5OhyIg== + electron-to-chromium@^1.3.886: version "1.3.894" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.894.tgz#54554ecb40d40ddac7241c4a42887e86180015d8" @@ -19272,7 +19290,12 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== -nanoid@^3.1.16, nanoid@^3.1.20, nanoid@^3.1.23, nanoid@^3.1.30: +nanoid@^3.1.16: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + +nanoid@^3.1.20, nanoid@^3.1.23, nanoid@^3.1.30: version "3.2.0" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== @@ -19610,7 +19633,12 @@ node-notifier@^9.0.1: uuid "^8.3.0" which "^2.0.2" -node-releases@^1.1.69, node-releases@^1.1.75: +node-releases@^1.1.69: + version "1.1.77" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" + integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== + +node-releases@^1.1.75: version "1.1.75" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.75.tgz#6dd8c876b9897a1b8e5a02de26afa79bb54ebbfe" integrity sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw== @@ -23171,6 +23199,13 @@ rollup@2.26.5: optionalDependencies: fsevents "~2.1.2" +rollup@2.78.0: + version "2.78.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.78.0.tgz#00995deae70c0f712ea79ad904d5f6b033209d9e" + integrity sha512-4+YfbQC9QEVvKTanHhIAFVUFSRsezvQF8vFOJwtGfb9Bb+r014S+qryr9PSmw8x6sMnPkmFBGAvIFVQxvJxjtg== + optionalDependencies: + fsevents "~2.3.2" + rollup@^2.67.1: version "2.67.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.67.1.tgz#4402665706fa00f321d446ce45f880e02cf54f01"