diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index ba1535d799de..664f057ee808 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -9,9 +9,7 @@ "engines": { "node": ">=18" }, - "files": [ - "/build" - ], + "files": ["/build"], "main": "build/cjs/index.server.js", "module": "build/esm/index.server.js", "browser": "build/esm/index.client.js", @@ -44,7 +42,7 @@ "@sentry/node": "9.1.0", "@sentry/opentelemetry": "9.1.0", "@sentry/svelte": "9.1.0", - "@sentry/vite-plugin": "2.22.6", + "@sentry/vite-plugin": "3.2.0", "magic-string": "0.30.7", "magicast": "0.2.8", "sorcery": "1.0.0" diff --git a/packages/sveltekit/src/vite/sourceMaps.ts b/packages/sveltekit/src/vite/sourceMaps.ts index 799688b33845..78ee6389c5da 100644 --- a/packages/sveltekit/src/vite/sourceMaps.ts +++ b/packages/sveltekit/src/vite/sourceMaps.ts @@ -24,13 +24,6 @@ type Sorcery = { load(filepath: string): Promise; }; -type GlobalWithSourceMapSetting = typeof globalThis & { - _sentry_sourceMapSetting?: { - updatedSourceMapSetting?: boolean | 'inline' | 'hidden'; - previousSourceMapSetting?: UserSourceMapSetting; - }; -}; - // storing this in the module scope because `makeCustomSentryVitePlugin` is called multiple times // and we only want to generate a uuid once in case we have to fall back to it. const releaseName = detectSentryRelease(); @@ -57,8 +50,6 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug const usedAdapter = options?.adapter || 'other'; const adapterOutputDir = await getAdapterOutputDir(svelteConfig, usedAdapter); - const globalWithSourceMapSetting = globalThis as GlobalWithSourceMapSetting; - const defaultPluginOptions: SentryVitePluginOptions = { release: { name: releaseName, @@ -70,61 +61,8 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug }, }; - // Including all hidden (`.*`) directories by default so that folders like .vercel, - // .netlify, etc are also cleaned up. Additionally, we include the adapter output - // dir which could be a non-hidden directory, like `build` for the Node adapter. - const defaultFileDeletionGlob = ['./.*/**/*.map', `./${adapterOutputDir}/**/*.map`]; - - if (!globalWithSourceMapSetting._sentry_sourceMapSetting) { - let configFile: { - path: string; - config: UserConfig; - dependencies: string[]; - } | null = null; - - try { - // @ts-expect-error - the dynamic import here works fine - const Vite = await import('vite'); - configFile = await Vite.loadConfigFromFile({ command: 'build', mode: 'production' }); - } catch { - if (options?.debug) { - consoleSandbox(() => { - // eslint-disable-next-line no-console - console.warn( - '[Sentry] Could not import Vite to load your vite config. Please set `build.sourcemap` to `true` or `hidden` to enable source map generation.', - ); - }); - } - } - - if (configFile) { - globalWithSourceMapSetting._sentry_sourceMapSetting = getUpdatedSourceMapSetting(configFile.config); - } else { - if (options?.debug) { - consoleSandbox(() => { - // eslint-disable-next-line no-console - console.warn( - '[Sentry] Could not load Vite config with Vite "production" mode. This is needed for Sentry to automatically update source map settings.', - ); - }); - } - } - - if (options?.debug && globalWithSourceMapSetting._sentry_sourceMapSetting?.previousSourceMapSetting === 'unset') { - consoleSandbox(() => { - // eslint-disable-next-line no-console - console.warn( - `[Sentry] Automatically setting \`sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload: [${defaultFileDeletionGlob - .map(file => `"${file}"`) - .join(', ')}]\` to delete generated source maps after they were uploaded to Sentry.`, - ); - }); - } - } - - const shouldDeleteDefaultSourceMaps = - globalWithSourceMapSetting._sentry_sourceMapSetting?.previousSourceMapSetting === 'unset' && - !options?.sourcemaps?.filesToDeleteAfterUpload; + const { promise: filesToDeleteAfterUpload, resolve: resolveFilesToDeleteAfterUpload } = + createFilesToDeleteAfterUploadPromise(); const mergedOptions = { ...defaultPluginOptions, @@ -135,9 +73,7 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug }, sourcemaps: { ...options?.sourcemaps, - filesToDeleteAfterUpload: shouldDeleteDefaultSourceMaps - ? defaultFileDeletionGlob - : options?.sourcemaps?.filesToDeleteAfterUpload, + filesToDeleteAfterUpload, }, }; @@ -163,6 +99,10 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug console.warn( 'sentry-vite-debug-id-upload-plugin not found in sentryPlugins! Cannot modify plugin - returning default Sentry Vite plugins', ); + + // resolving filesToDeleteAfterUpload here, because we return the original deletion plugin which awaits the promise + resolveFilesToDeleteAfterUpload(undefined); + return sentryPlugins; } @@ -172,6 +112,10 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug console.warn( 'sentry-file-deletion-plugin not found in sentryPlugins! Cannot modify plugin - returning default Sentry Vite plugins', ); + + // resolving filesToDeleteAfterUpload here, because we return the original deletion plugin which awaits the promise + resolveFilesToDeleteAfterUpload(undefined); + return sentryPlugins; } @@ -181,6 +125,10 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug console.warn( 'sentry-release-management-plugin not found in sentryPlugins! Cannot modify plugin - returning default Sentry Vite plugins', ); + + // resolving filesToDeleteAfterUpload here, because we return the original deletion plugin which awaits the promise + resolveFilesToDeleteAfterUpload(undefined); + return sentryPlugins; } @@ -205,37 +153,66 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug const sourceMapSettingsPlugin: Plugin = { name: 'sentry-sveltekit-update-source-map-setting-plugin', apply: 'build', // only apply this plugin at build time - config: (config: UserConfig) => { + config: async (config: UserConfig) => { const settingKey = 'build.sourcemap'; - if (globalWithSourceMapSetting._sentry_sourceMapSetting?.previousSourceMapSetting === 'unset') { + const { updatedSourceMapSetting, previousSourceMapSetting } = getUpdatedSourceMapSetting(config); + + const userProvidedFilesToDeleteAfterUpload = await options?.sourcemaps?.filesToDeleteAfterUpload; + + if (previousSourceMapSetting === 'unset') { consoleSandbox(() => { // eslint-disable-next-line no-console console.log(`[Sentry] Enabled source map generation in the build options with \`${settingKey}: "hidden"\`.`); }); + if (userProvidedFilesToDeleteAfterUpload) { + resolveFilesToDeleteAfterUpload(userProvidedFilesToDeleteAfterUpload); + } else { + // Including all hidden (`.*`) directories by default so that folders like .vercel, + // .netlify, etc are also cleaned up. Additionally, we include the adapter output + // dir which could be a non-hidden directory, like `build` for the Node adapter. + const defaultFileDeletionGlob = ['./.*/**/*.map', `./${adapterOutputDir}/**/*.map`]; + + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + `[Sentry] Automatically setting \`sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload: [${defaultFileDeletionGlob + .map(file => `"${file}"`) + .join(', ')}]\` to delete generated source maps after they were uploaded to Sentry.`, + ); + }); + + // In case we enabled source maps and users didn't specify a glob patter to delete, we set a default pattern: + resolveFilesToDeleteAfterUpload(defaultFileDeletionGlob); + } + return { ...config, - build: { ...config.build, sourcemap: 'hidden' }, + build: { ...config.build, sourcemap: updatedSourceMapSetting }, }; - } else if (globalWithSourceMapSetting._sentry_sourceMapSetting?.previousSourceMapSetting === 'disabled') { + } + + if (previousSourceMapSetting === 'disabled') { consoleSandbox(() => { // eslint-disable-next-line no-console console.warn( `[Sentry] Parts of source map generation are currently disabled in your Vite configuration (\`${settingKey}: false\`). This setting is either a default setting or was explicitly set in your configuration. Sentry won't override this setting. Without source maps, code snippets on the Sentry Issues page will remain minified. To show unminified code, enable source maps in \`${settingKey}\` (e.g. by setting them to \`hidden\`).`, ); }); - } else if (globalWithSourceMapSetting._sentry_sourceMapSetting?.previousSourceMapSetting === 'enabled') { + } else if (previousSourceMapSetting === 'enabled') { if (mergedOptions?.debug) { consoleSandbox(() => { // eslint-disable-next-line no-console console.log( - `[Sentry] We discovered you enabled source map generation in your Vite configuration (\`${settingKey}\`). Sentry will keep this source map setting. This will un-minify the code snippet on the Sentry Issue page.`, + `[Sentry] We discovered you enabled source map generation in your Vite configuration (\`${settingKey}\`). Sentry will keep this source map setting. This will un-minify the code snippet on the Sentry Issue page.`, ); }); } } + resolveFilesToDeleteAfterUpload(userProvidedFilesToDeleteAfterUpload); + return config; }, }; @@ -423,7 +400,7 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug /** * Whether the user enabled (true, 'hidden', 'inline') or disabled (false) source maps */ -export type UserSourceMapSetting = 'enabled' | 'disabled' | 'unset' | undefined; +type UserSourceMapSetting = 'enabled' | 'disabled' | 'unset' | undefined; /** There are 3 ways to set up source map generation (https://github.com/getsentry/sentry-javascript/issues/13993) * @@ -445,25 +422,25 @@ export function getUpdatedSourceMapSetting(viteConfig: { sourcemap?: boolean | 'inline' | 'hidden'; }; }): { updatedSourceMapSetting: boolean | 'inline' | 'hidden'; previousSourceMapSetting: UserSourceMapSetting } { - let previousSourceMapSetting: UserSourceMapSetting; - let updatedSourceMapSetting: boolean | 'inline' | 'hidden' | undefined; - viteConfig.build = viteConfig.build || {}; - const viteSourceMap = viteConfig.build.sourcemap; - - if (viteSourceMap === false) { - previousSourceMapSetting = 'disabled'; - updatedSourceMapSetting = viteSourceMap; - } else if (viteSourceMap && ['hidden', 'inline', true].includes(viteSourceMap)) { - previousSourceMapSetting = 'enabled'; - updatedSourceMapSetting = viteSourceMap; - } else { - previousSourceMapSetting = 'unset'; - updatedSourceMapSetting = 'hidden'; + const originalSourcemapSetting = viteConfig.build.sourcemap; + + if (originalSourcemapSetting === false) { + return { + previousSourceMapSetting: 'disabled', + updatedSourceMapSetting: originalSourcemapSetting, + }; + } + + if (originalSourcemapSetting && ['hidden', 'inline', true].includes(originalSourcemapSetting)) { + return { previousSourceMapSetting: 'enabled', updatedSourceMapSetting: originalSourcemapSetting }; } - return { previousSourceMapSetting, updatedSourceMapSetting }; + return { + previousSourceMapSetting: 'unset', + updatedSourceMapSetting: 'hidden', + }; } function getFiles(dir: string): string[] { @@ -499,3 +476,22 @@ function detectSentryRelease(): string { return release; } + +/** + * Creates a deferred promise that can be resolved/rejected by calling the + * `resolve` or `reject` function. + * Inspired by: https://stackoverflow.com/a/69027809 + */ +function createFilesToDeleteAfterUploadPromise(): { + promise: Promise; + resolve: (value: string | string[] | undefined) => void; + reject: (reason?: unknown) => void; +} { + let resolve!: (value: string | string[] | undefined) => void; + let reject!: (reason?: unknown) => void; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { resolve, reject, promise }; +} diff --git a/packages/sveltekit/test/vite/sourceMaps.test.ts b/packages/sveltekit/test/vite/sourceMaps.test.ts index 378cbd2099e1..1410e9b992f0 100644 --- a/packages/sveltekit/test/vite/sourceMaps.test.ts +++ b/packages/sveltekit/test/vite/sourceMaps.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { makeCustomSentryVitePlugins } from '../../src/vite/sourceMaps'; +import { getUpdatedSourceMapSetting, makeCustomSentryVitePlugins } from '../../src/vite/sourceMaps'; import type { Plugin } from 'vite'; @@ -113,7 +113,7 @@ describe('makeCustomSentryVitePlugins()', () => { const plugin = await getSentryViteSubPlugin('sentry-sveltekit-update-source-map-setting-plugin'); // @ts-expect-error this function exists! - const sentryConfig = plugin.config(originalConfig); + const sentryConfig = await plugin.config(originalConfig); expect(sentryConfig).toEqual(originalConfig); }); @@ -132,7 +132,7 @@ describe('makeCustomSentryVitePlugins()', () => { const plugin = await getSentryViteSubPlugin('sentry-sveltekit-update-source-map-setting-plugin'); // @ts-expect-error this function exists! - const sentryConfig = plugin.config(originalConfig); + const sentryConfig = await plugin.config(originalConfig); expect(sentryConfig).toEqual({ build: { @@ -155,7 +155,7 @@ describe('makeCustomSentryVitePlugins()', () => { const plugin = await getSentryViteSubPlugin('sentry-sveltekit-update-source-map-setting-plugin'); // @ts-expect-error this function exists! - const sentryConfig = plugin.config(originalConfig); + const sentryConfig = await plugin.config(originalConfig); expect(sentryConfig).toEqual({ ...originalConfig, build: { @@ -320,22 +320,23 @@ describe('makeCustomSentryVitePlugins()', () => { describe('changeViteSourceMapSettings()', () => { const cases = [ { sourcemap: false, expectedSourcemap: false, expectedPrevious: 'disabled' }, - { sourcemap: 'hidden', expectedSourcemap: 'hidden', expectedPrevious: 'enabled' }, - { sourcemap: 'inline', expectedSourcemap: 'inline', expectedPrevious: 'enabled' }, + { sourcemap: 'hidden' as const, expectedSourcemap: 'hidden', expectedPrevious: 'enabled' }, + { sourcemap: 'inline' as const, expectedSourcemap: 'inline', expectedPrevious: 'enabled' }, { sourcemap: true, expectedSourcemap: true, expectedPrevious: 'enabled' }, { sourcemap: undefined, expectedSourcemap: 'hidden', expectedPrevious: 'unset' }, ]; - it.each(cases)('handles vite source map settings $1', async ({ sourcemap, expectedSourcemap, expectedPrevious }) => { - const viteConfig = { build: { sourcemap } }; + it.each(cases)( + 'handles vite source map setting `build.sourcemap: $sourcemap`', + async ({ sourcemap, expectedSourcemap, expectedPrevious }) => { + const viteConfig = { build: { sourcemap } }; - const { getUpdatedSourceMapSetting } = await import('../../src/vite/sourceMaps'); + const result = getUpdatedSourceMapSetting(viteConfig); - const result = getUpdatedSourceMapSetting(viteConfig); - - expect(result).toEqual({ - updatedSourceMapSetting: expectedSourcemap, - previousSourceMapSetting: expectedPrevious, - }); - }); + expect(result).toEqual({ + updatedSourceMapSetting: expectedSourcemap, + previousSourceMapSetting: expectedPrevious, + }); + }, + ); }); diff --git a/yarn.lock b/yarn.lock index 8b41988eb8ec..0c7b60c816e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6592,6 +6592,11 @@ resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.1.2.tgz#5497ca5adbe775955e96c566511a0bed3ab0a3ce" integrity sha512-5h2WXRJ6swKA0TwxHHryC8M2QyOfS9QhTAL6ElPfkEYe9HhJieXmxsDpyspbqAa26ccnCUcmwE5vL34jAjt4sQ== +"@sentry/babel-plugin-component-annotate@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.2.0.tgz#17c000cf6cc315bb620eddbd95c88dfb2471cfb9" + integrity sha512-Sg7nLRP1yiJYl/KdGGxYGbjvLq5rswyeB5yESgfWX34XUNZaFgmNvw4pU/QEKVeYgcPyOulgJ+y80ewujyffTA== + "@sentry/bundler-plugin-core@2.22.6": version "2.22.6" resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.6.tgz#a1ea1fd43700a3ece9e7db016997e79a2782b87d" @@ -6620,6 +6625,20 @@ magic-string "0.30.8" unplugin "1.0.1" +"@sentry/bundler-plugin-core@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-3.2.0.tgz#023ec92530a35fbec7c7077b7a8be2e79f0f9dd5" + integrity sha512-Q/ogVylue3XaFawyIxzuiic+7Dp4w63eJtRtVH8VBebNURyJ/re4GVoP1QNGccE1R243tXY1y2GiwqiJkAONOg== + dependencies: + "@babel/core" "^7.18.5" + "@sentry/babel-plugin-component-annotate" "3.2.0" + "@sentry/cli" "2.41.1" + dotenv "^16.3.1" + find-up "^5.0.0" + glob "^9.3.2" + magic-string "0.30.8" + unplugin "1.0.1" + "@sentry/cli-darwin@2.41.1": version "2.41.1" resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.41.1.tgz#ca7e12bf1ad59bc2df35868ae98abc8869108efa" @@ -6690,6 +6709,14 @@ "@sentry/bundler-plugin-core" "2.22.6" unplugin "1.0.1" +"@sentry/vite-plugin@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-3.2.0.tgz#0785b6e04e0aed8a4d6b57a433a2da11c14e6cd0" + integrity sha512-IVBoAzZmpoX9+mnmIMq2ndxlFPoWMuYSE5Mek5zOWpYh+GbPxvkrxvM+vg0HeLH4r5v9Tm0FWcEZDgDIZqtoSg== + dependencies: + "@sentry/bundler-plugin-core" "3.2.0" + unplugin "1.0.1" + "@sentry/webpack-plugin@3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-3.1.2.tgz#e7cf2b10b6d2fb2d6106e692469d02b6ab684bba" @@ -7903,12 +7930,7 @@ dependencies: "@types/unist" "*" -"@types/history-4@npm:@types/history@4.7.8": - version "4.7.8" - resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" - integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== - -"@types/history-5@npm:@types/history@4.7.8": +"@types/history-4@npm:@types/history@4.7.8", "@types/history-5@npm:@types/history@4.7.8": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== @@ -27443,16 +27465,7 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -27555,14 +27568,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -27727,7 +27733,6 @@ stylus@0.59.0, stylus@^0.59.0: sucrase@^3.27.0, sucrase@^3.35.0, sucrase@getsentry/sucrase#es2020-polyfills: version "3.36.0" - uid fd682f6129e507c00bb4e6319cc5d6b767e36061 resolved "https://codeload.github.com/getsentry/sucrase/tar.gz/fd682f6129e507c00bb4e6319cc5d6b767e36061" dependencies: "@jridgewell/gen-mapping" "^0.3.2" @@ -30363,16 +30368,7 @@ wrangler@^3.67.1: optionalDependencies: fsevents "~2.3.2" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@7.0.0, wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@7.0.0, wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==