diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index b2241c05cf53..60198a3935a2 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -17,9 +17,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "6.2.0", + "@sentry/core": "6.2.0", + "@sentry/minimal": "6.2.0", "@sentry/node": "6.2.0", - "@sentry/minimal": "6.2.0" + "@sentry/react": "6.2.0" }, "devDependencies": { "@sentry/types": "6.2.0", diff --git a/packages/nextjs/src/browser.ts b/packages/nextjs/src/browser.ts index da49c2089482..7c431144f3fd 100644 --- a/packages/nextjs/src/browser.ts +++ b/packages/nextjs/src/browser.ts @@ -1,12 +1,20 @@ -import { init as browserInit } from '@sentry/browser'; +import { init as reactInit } from '@sentry/react'; -import { MetadataBuilder, NextjsOptions } from './options'; +import { InitDecider } from './utils/initDecider'; +import { MetadataBuilder } from './utils/metadataBuilder'; +import { NextjsOptions } from './utils/nextjsOptions'; -/** Inits the Sentry NextJS SDK on the browser. */ +/** Inits the Sentry NextJS SDK on the browser with the React SDK. */ export function init(options: NextjsOptions): any { - const metadataBuilder = new MetadataBuilder(options, ['nextjs', 'browser']); + const metadataBuilder = new MetadataBuilder(options, ['nextjs', 'react']); metadataBuilder.addSdkMetadata(); - browserInit(options); + const initDecider = new InitDecider(options); + if (initDecider.shouldInitSentry()) { + reactInit(options); + } else { + // eslint-disable-next-line no-console + console.log('[Sentry] Detected a non-production environment. Not initializing Sentry.'); + } } export * from '@sentry/minimal'; diff --git a/packages/nextjs/src/node.ts b/packages/nextjs/src/node.ts index aefb8d6557a1..d17a6b2b5cd2 100644 --- a/packages/nextjs/src/node.ts +++ b/packages/nextjs/src/node.ts @@ -1,12 +1,20 @@ import { init as nodeInit } from '@sentry/node'; -import { MetadataBuilder, NextjsOptions } from './options'; +import { InitDecider } from './utils/initDecider'; +import { MetadataBuilder } from './utils/metadataBuilder'; +import { NextjsOptions } from './utils/nextjsOptions'; /** Inits the Sentry NextJS SDK on node. */ export function init(options: NextjsOptions): any { const metadataBuilder = new MetadataBuilder(options, ['nextjs', 'node']); metadataBuilder.addSdkMetadata(); - nodeInit(options); + const initDecider = new InitDecider(options); + if (initDecider.shouldInitSentry()) { + nodeInit(options); + } else { + // eslint-disable-next-line no-console + console.log('[Sentry] Detected a non-production environment. Not initializing Sentry.'); + } } export * from '@sentry/minimal'; diff --git a/packages/nextjs/src/sdk.ts b/packages/nextjs/src/sdk.ts index 0cbfbc1e472f..880a45edc4f2 100644 --- a/packages/nextjs/src/sdk.ts +++ b/packages/nextjs/src/sdk.ts @@ -1,6 +1,8 @@ -import { init as browserInit } from '@sentry/browser'; +import { init as reactInit } from '@sentry/react'; -import { MetadataBuilder, NextjsOptions } from './options'; +import { InitDecider } from './utils/initDecider'; +import { MetadataBuilder } from './utils/metadataBuilder'; +import { NextjsOptions } from './utils/nextjsOptions'; /** * The Sentry NextJS SDK Client. @@ -11,5 +13,11 @@ import { MetadataBuilder, NextjsOptions } from './options'; export function init(options: NextjsOptions): void { const metadataBuilder = new MetadataBuilder(options, ['nextjs']); metadataBuilder.addSdkMetadata(); - browserInit(options); + const initDecider = new InitDecider(options); + if (initDecider.shouldInitSentry()) { + reactInit(options); + } else { + // eslint-disable-next-line no-console + console.log('[Sentry] Detected a non-production environment. Not initializing Sentry.'); + } } diff --git a/packages/nextjs/src/utils/initDecider.ts b/packages/nextjs/src/utils/initDecider.ts new file mode 100644 index 000000000000..eca6ed1b9382 --- /dev/null +++ b/packages/nextjs/src/utils/initDecider.ts @@ -0,0 +1,37 @@ +import { NextjsOptions } from './nextjsOptions'; + +export class InitDecider { + private _options: NextjsOptions; + + constructor(options: NextjsOptions) { + this._options = options; + } + + /** + * Returns a boolean representing whether the NextJS SDK should be initialised. + * + * The SDK should be initialised if the `dev` option is set to true. + * `dev` is optional, so if it isn't set or is set to false, the SDK will only + * be initialised in a production environment. + */ + public shouldInitSentry(): boolean { + if (this._isEnabledInDev() || this._isProdEnv()) { + return true; + } + return false; + } + + /** + * Returns true if the option `dev` is true, and false otherwise. + */ + private _isEnabledInDev(): boolean { + return this._options.dev || false; + } + + /** + * Returns whether the environment is a production environment. + */ + private _isProdEnv(): boolean { + return process.env.NODE_ENV !== undefined && process.env.NODE_ENV === 'production'; + } +} diff --git a/packages/nextjs/src/options.ts b/packages/nextjs/src/utils/metadataBuilder.ts similarity index 76% rename from packages/nextjs/src/options.ts rename to packages/nextjs/src/utils/metadataBuilder.ts index 2d7424db17aa..a20ca13b5700 100644 --- a/packages/nextjs/src/options.ts +++ b/packages/nextjs/src/utils/metadataBuilder.ts @@ -1,14 +1,11 @@ -import { BrowserOptions, SDK_VERSION } from '@sentry/browser'; -import { NodeOptions } from '@sentry/node'; -import { Options, Package, SdkInfo } from '@sentry/types'; +import { SDK_VERSION } from '@sentry/core'; +import { Package, SdkInfo } from '@sentry/types'; + +import { NextjsOptions } from './nextjsOptions'; const SDK_NAME = 'sentry.javascript.nextjs'; const PACKAGE_NAME_PREFIX = 'npm:@sentry/'; -export interface NextjsOptions extends Options, BrowserOptions, NodeOptions { - // TODO: options for NextJS -} - /** * A builder for the SDK metadata in the options for the SDK initialization. */ diff --git a/packages/nextjs/src/utils/nextjsOptions.ts b/packages/nextjs/src/utils/nextjsOptions.ts new file mode 100644 index 000000000000..b9eefefbfb20 --- /dev/null +++ b/packages/nextjs/src/utils/nextjsOptions.ts @@ -0,0 +1,12 @@ +import { NodeOptions } from '@sentry/node'; +import { BrowserOptions } from '@sentry/react'; +import { Options } from '@sentry/types'; + +export interface NextjsOptions extends Options, BrowserOptions, NodeOptions { + /** + * A flag enabling the initialization of the SDK in development and other + * non-production environments. By default, the SDK is only initialised in + * production. + */ + dev?: boolean; +} diff --git a/packages/nextjs/test/initDecider.test.ts b/packages/nextjs/test/initDecider.test.ts new file mode 100644 index 000000000000..eaafa7ef3397 --- /dev/null +++ b/packages/nextjs/test/initDecider.test.ts @@ -0,0 +1,60 @@ +import { InitDecider } from '../src/utils/initDecider'; +import { NextjsOptions } from '../src/utils/nextjsOptions'; + +function setDevEnv(): void { + process.env.NODE_ENV = 'development'; +} + +function setProdEnv(): void { + process.env.NODE_ENV = 'production'; +} + +function getEmptyOptions(): NextjsOptions { + return {}; +} + +function getDevTrueOptions(): NextjsOptions { + return { dev: true }; +} + +function getDevFalseOptions(): NextjsOptions { + return { dev: false }; +} + +describe('decide initialization in development', () => { + beforeEach(setDevEnv); + + test('without options', () => { + const initDecider = new InitDecider(getEmptyOptions()); + expect(initDecider.shouldInitSentry()).toBeFalsy(); + }); + + test('without development', () => { + const initDecider = new InitDecider(getDevFalseOptions()); + expect(initDecider.shouldInitSentry()).toBeFalsy(); + }); + + test('with development', () => { + const initDecider = new InitDecider(getDevTrueOptions()); + expect(initDecider.shouldInitSentry()).toBeTruthy(); + }); +}); + +describe('decide initialization in production', () => { + beforeEach(setProdEnv); + + test('without options', () => { + const initDecider = new InitDecider(getEmptyOptions()); + expect(initDecider.shouldInitSentry()).toBeTruthy(); + }); + + test('without development', () => { + const initDecider = new InitDecider(getDevFalseOptions()); + expect(initDecider.shouldInitSentry()).toBeTruthy(); + }); + + test('with development', () => { + const initDecider = new InitDecider(getDevTrueOptions()); + expect(initDecider.shouldInitSentry()).toBeTruthy(); + }); +}); diff --git a/yarn.lock b/yarn.lock index 8d6999cf827d..afc2984a37f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3276,7 +3276,7 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@sentry/browser@^6.2.0": +"@sentry/browser@6.2.0": version "6.2.0" resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.2.0.tgz#4113a92bc82f55e63f30cb16a94f717bd0b95817" integrity sha512-4r3paHcHXLemj471BtNDhUs2kvJxk5XDRplz1dbC/LHXN5PWEXP4anhGILxOlxqi4y33r53PIZu3xXFjznaVZA== @@ -3306,7 +3306,7 @@ "@sentry/utils" "6.2.0" tslib "^1.9.3" -"@sentry/minimal@6.2.0", "@sentry/minimal@^6.2.0": +"@sentry/minimal@6.2.0": version "6.2.0" resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.2.0.tgz#718b70babb55912eeb38babaf7823d1bcdd77d1e" integrity sha512-haxsx8/ZafhZUaGeeMtY7bJt9HbDlqeiaXrRMp1CxGtd0ZRQwHt60imEjl6IH1I73SEWxNfqScGsX2s3HzztMg== @@ -3315,7 +3315,7 @@ "@sentry/types" "6.2.0" tslib "^1.9.3" -"@sentry/node@^6.2.0": +"@sentry/node@6.2.0": version "6.2.0" resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.2.0.tgz#4c1860822a4a73d24242e5254124bd3bf9028d52" integrity sha512-02lXk+56tPA3lWTvNLMGorp77wUVti8wOs+TlYARkJ+N+16dwqEBSBTy3hCDxlxriB+qHchSIS+ovPGi6WNiYA== @@ -3330,6 +3330,18 @@ lru_map "^0.3.3" tslib "^1.9.3" +"@sentry/react@6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.2.0.tgz#bf46c38762554f246ca9dec527e6a5b3168fb0b0" + integrity sha512-Jf3s7om1iLpApkN26O7c3Ult3lS91ekZNC4WKtcPb6b+KOBQ36sB0d1KhL3hGZ55UKLmgZu3jn2hd7bJ9EY3yA== + dependencies: + "@sentry/browser" "6.2.0" + "@sentry/minimal" "6.2.0" + "@sentry/types" "6.2.0" + "@sentry/utils" "6.2.0" + hoist-non-react-statics "^3.3.2" + tslib "^1.9.3" + "@sentry/tracing@6.2.0": version "6.2.0" resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.2.0.tgz#76c17e5dd3f1e61c8a4e3bd090f904a63d674765" @@ -3341,7 +3353,7 @@ "@sentry/utils" "6.2.0" tslib "^1.9.3" -"@sentry/types@6.2.0", "@sentry/types@^6.2.0": +"@sentry/types@6.2.0": version "6.2.0" resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.2.0.tgz#ca020ff42913c6b9f88a9d0c375b5ee3965a2590" integrity sha512-vN4P/a+QqAuVfWFB9G3nQ7d6bgnM9jd/RLVi49owMuqvM24pv5mTQHUk2Hk4S3k7ConrHFl69E7xH6Dv5VpQnQ== @@ -9967,25 +9979,26 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.6.0.tgz#522d67cfaea09724d96949c70e7a0550614d64d6" - integrity sha512-QlAManNtqr7sozWm5TF4wIH9gmUm2hE3vNRUvyoYAa4y1l5/jxD/PQStEjBMQtCqZmSep8UxrcecI60hOpe61w== +eslint@7.20.0: + version "7.20.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.20.0.tgz#db07c4ca4eda2e2316e7aa57ac7fc91ec550bdc7" + integrity sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw== dependencies: - "@babel/code-frame" "^7.0.0" + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.3.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" enquirer "^2.3.5" - eslint-scope "^5.1.0" + eslint-scope "^5.1.1" eslint-utils "^2.1.0" - eslint-visitor-keys "^1.3.0" - espree "^7.2.0" - esquery "^1.2.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + file-entry-cache "^6.0.0" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" globals "^12.1.0" @@ -9996,7 +10009,7 @@ eslint@7.6.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.19" + lodash "^4.17.20" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -10005,30 +10018,29 @@ eslint@7.6.0: semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^5.2.3" + table "^6.0.4" text-table "^0.2.0" v8-compile-cache "^2.0.3" -eslint@^7.20.0: - version "7.20.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.20.0.tgz#db07c4ca4eda2e2316e7aa57ac7fc91ec550bdc7" - integrity sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw== +eslint@7.6.0: + version "7.6.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.6.0.tgz#522d67cfaea09724d96949c70e7a0550614d64d6" + integrity sha512-QlAManNtqr7sozWm5TF4wIH9gmUm2hE3vNRUvyoYAa4y1l5/jxD/PQStEjBMQtCqZmSep8UxrcecI60hOpe61w== dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.3.0" + "@babel/code-frame" "^7.0.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" enquirer "^2.3.5" - eslint-scope "^5.1.1" + eslint-scope "^5.1.0" eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" + eslint-visitor-keys "^1.3.0" + espree "^7.2.0" + esquery "^1.2.0" esutils "^2.0.2" - file-entry-cache "^6.0.0" + file-entry-cache "^5.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" globals "^12.1.0" @@ -10039,7 +10051,7 @@ eslint@^7.20.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.20" + lodash "^4.17.19" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -10048,7 +10060,7 @@ eslint@^7.20.0: semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^6.0.4" + table "^5.2.3" text-table "^0.2.0" v8-compile-cache "^2.0.3" @@ -17947,7 +17959,7 @@ rimraf@2, rimraf@2.6.3, rimraf@^2.2.8, rimraf@^2.3.4, rimraf@^2.4.3, rimraf@^2.4 dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.1, rimraf@^3.0.2: +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.1, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==