From 1f994fc98327e19a70157edf3df590f836317e4b Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Mon, 21 Oct 2024 09:36:46 +0300 Subject: [PATCH 1/2] feat: adds configurable dev environments for source map upload plugins --- packages/esbuild-plugin/README.md | 3 +++ packages/esbuild-plugin/src/index.ts | 10 ++++++--- packages/plugin-core/src/options.ts | 7 +++++++ packages/plugin-core/src/types.ts | 1 + packages/rollup-plugin/README.md | 3 +++ packages/rollup-plugin/src/index.ts | 4 ++-- packages/rollup-plugin/src/rollupUtils.ts | 8 +++++-- .../rollup-plugin/test/rollupUtils.test.ts | 21 ++++++++++++++----- packages/rollup-plugin/tsconfig.json | 11 +++++++--- packages/webpack/README.md | 3 +++ .../webpack/src/HoneybadgerSourceMapPlugin.js | 10 ++++++--- 11 files changed, 63 insertions(+), 18 deletions(-) diff --git a/packages/esbuild-plugin/README.md b/packages/esbuild-plugin/README.md index 0ea8c02bf..a73646554 100644 --- a/packages/esbuild-plugin/README.md +++ b/packages/esbuild-plugin/README.md @@ -68,6 +68,9 @@ These plugin parameters correspond to the Honeybadger [Source Map Upload API](ht
The name of the user that triggered this deploy, for example, "Jane"
+ +
developmentEnvironments (optional — default: ["dev", "development", "test"])
+
Used to decide whether source maps should be uploaded or not.
### esbuild.config.js diff --git a/packages/esbuild-plugin/src/index.ts b/packages/esbuild-plugin/src/index.ts index d38fde8fd..3d9d1e400 100644 --- a/packages/esbuild-plugin/src/index.ts +++ b/packages/esbuild-plugin/src/index.ts @@ -24,7 +24,7 @@ class HoneybadgerSourceMapPlugin { return { name: PLUGIN_NAME, setup(build: PluginBuild) { - if (self.isNonProdEnv()) { + if (self.isDevEnv(self.options.developmentEnvironments)) { return } @@ -95,8 +95,12 @@ class HoneybadgerSourceMapPlugin { return assets } - private isNonProdEnv(): boolean { - return !!process.env.NODE_ENV && process.env.NODE_ENV !== 'production' + private isDevEnv(devEnvironments: string[]): boolean { + if (!process.env.NODE_ENV || process.env.NODE_ENV === '') { + return false + } + + return devEnvironments.includes(process.env.NODE_ENV) } private handleError(error: Error): Message { diff --git a/packages/plugin-core/src/options.ts b/packages/plugin-core/src/options.ts index 8e95008d4..36bae73e6 100644 --- a/packages/plugin-core/src/options.ts +++ b/packages/plugin-core/src/options.ts @@ -11,6 +11,7 @@ export const DEFAULT_DEPLOY = false export const DEFAULT_DEPLOY_ENDPOINT = 'https://api.honeybadger.io/v1/deploys' export const DEFAULT_IGNORE_PATHS = [] export const DEFAULT_IGNORE_ERRORS = false +export const DEFAULT_DEVELOPMENT_ENVIRONMENTS = ['dev', 'development', 'test']; const required = [ 'apiKey', @@ -27,6 +28,7 @@ const defaultOptions = { ignorePaths: DEFAULT_IGNORE_PATHS, ignoreErrors: DEFAULT_IGNORE_ERRORS, workerCount: DEFAULT_WORKER_COUNT, + developmentEnvironments: DEFAULT_DEVELOPMENT_ENVIRONMENTS } export function cleanOptions( @@ -44,6 +46,11 @@ export function cleanOptions( throw new Error('ignorePaths must be an array') } + // Validate developmentEnvironments + if (options.developmentEnvironments && !Array.isArray(options.developmentEnvironments)) { + throw new Error('developmentEnvironments must be an array') + } + // Don't allow excessive retries if (options.retries && options.retries > MAX_RETRIES) { if (!options.silent) { diff --git a/packages/plugin-core/src/types.ts b/packages/plugin-core/src/types.ts index 22be5b830..6c16b4895 100644 --- a/packages/plugin-core/src/types.ts +++ b/packages/plugin-core/src/types.ts @@ -11,6 +11,7 @@ export type HbPluginOptions = { ignorePaths: Array; ignoreErrors: boolean; workerCount: number; + developmentEnvironments: Array; } // Options passed in by a user diff --git a/packages/rollup-plugin/README.md b/packages/rollup-plugin/README.md index 131f268c7..8596c5809 100644 --- a/packages/rollup-plugin/README.md +++ b/packages/rollup-plugin/README.md @@ -70,6 +70,9 @@ These plugin parameters correspond to the Honeybadger [Source Map Upload API](ht
The name of the user that triggered this deploy, for example, "Jane"
+ +
developmentEnvironments (optional — default: ["dev", "development", "test"])
+
Used to decide whether source maps should be uploaded or not.
### rollup.config.js diff --git a/packages/rollup-plugin/src/index.ts b/packages/rollup-plugin/src/index.ts index 59c4df207..29fe88c1b 100644 --- a/packages/rollup-plugin/src/index.ts +++ b/packages/rollup-plugin/src/index.ts @@ -1,4 +1,4 @@ -import { extractSourcemapDataFromBundle, isNonProdEnv } from './rollupUtils' +import { extractSourcemapDataFromBundle, isDevEnv } from './rollupUtils' import { sendDeployNotification, uploadSourcemaps, cleanOptions, Types } from '@honeybadger-io/plugin-core' import type { OutputBundle, Plugin, NormalizedOutputOptions } from 'rollup' @@ -13,7 +13,7 @@ export default function honeybadgerRollupPlugin( outputOptions: NormalizedOutputOptions, bundle: OutputBundle ) => { - if (isNonProdEnv()) { + if (isDevEnv(hbOptions.developmentEnvironments)) { if (!hbOptions.silent) { console.info('Honeybadger will not upload sourcemaps in non-production environment.') } diff --git a/packages/rollup-plugin/src/rollupUtils.ts b/packages/rollup-plugin/src/rollupUtils.ts index 159dcc907..8575791e4 100644 --- a/packages/rollup-plugin/src/rollupUtils.ts +++ b/packages/rollup-plugin/src/rollupUtils.ts @@ -55,6 +55,10 @@ function formatSourcemapData( * In Rollup without Vite, it may or may not be available, * so if it's missing we'll assume prod */ -export function isNonProdEnv(): boolean { - return !!process.env.NODE_ENV && process.env.NODE_ENV !== 'production' +export function isDevEnv(devEnvironments: string[]): boolean { + if (!process.env.NODE_ENV || process.env.NODE_ENV === '') { + return false + } + + return devEnvironments.includes(process.env.NODE_ENV) } diff --git a/packages/rollup-plugin/test/rollupUtils.test.ts b/packages/rollup-plugin/test/rollupUtils.test.ts index d705cfe2c..1f0c7f84b 100644 --- a/packages/rollup-plugin/test/rollupUtils.test.ts +++ b/packages/rollup-plugin/test/rollupUtils.test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai' -import { extractSourcemapDataFromBundle, isNonProdEnv } from '../src/rollupUtils'; +import { extractSourcemapDataFromBundle, isDevEnv } from '../src/rollupUtils'; import bundle from './fixtures/bundle' import path from 'node:path' import { NormalizedOutputOptions } from 'rollup'; @@ -91,7 +91,8 @@ describe('extractSourcemapDataFromBundle', () => { }) }) -describe('isNonProdEnv', () => { +describe('isDevEnv', () => { + const developmentEnvironments = ['development', 'test', 'staging'] let restore beforeEach(() => { @@ -99,21 +100,31 @@ describe('isNonProdEnv', () => { }) afterEach(() => { + // @ts-expect-error process.env.NODE_ENV = restore }) it('returns true if NODE_ENV is non-prod', () => { + // @ts-expect-error process.env.NODE_ENV = 'development' - expect(isNonProdEnv()).to.equal(true) + expect(isDevEnv(developmentEnvironments)).to.equal(true) + }) + + it('returns false if NODE_ENV is non-prod but not in developmentEnvironments array', () => { + // @ts-expect-error + process.env.NODE_ENV = 'staging' + expect(isDevEnv(developmentEnvironments)).to.equal(false) }) it('returns false if NODE_ENV is missing', () => { + // @ts-expect-error delete process.env.NODE_ENV - expect(isNonProdEnv()).to.equal(false) + expect(isDevEnv(developmentEnvironments)).to.equal(false) }) it('returns false if NODE_ENV is prod', () => { + // @ts-expect-error process.env.NODE_ENV = 'production' - expect(isNonProdEnv()).to.equal(false) + expect(isDevEnv(developmentEnvironments)).to.equal(false) }) }) diff --git a/packages/rollup-plugin/tsconfig.json b/packages/rollup-plugin/tsconfig.json index d501a6417..a512647b2 100644 --- a/packages/rollup-plugin/tsconfig.json +++ b/packages/rollup-plugin/tsconfig.json @@ -2,9 +2,14 @@ "extends": "../../tsconfig.base.json", "include": [ "src/**/*.ts" - ], + ], "compilerOptions": { - "rootDir": "./", + "rootDir": "./", "allowSyntheticDefaultImports": true }, -} \ No newline at end of file + "references": [ + { + "path": "../plugin-core" + } + ] +} diff --git a/packages/webpack/README.md b/packages/webpack/README.md index 9ff3f3814..757163399 100644 --- a/packages/webpack/README.md +++ b/packages/webpack/README.md @@ -77,6 +77,9 @@ These plugin parameters correspond to the Honeybadger [Source Map Upload API](ht
The name of the user that triggered this deploy, for example, "Jane"
+ +
developmentEnvironments (optional — default: ["dev", "development", "test"])
+
Source maps upload will be skipped when the environment matches any of the values in this array or the webpack dev server is running.
### Vanilla webpack.config.js diff --git a/packages/webpack/src/HoneybadgerSourceMapPlugin.js b/packages/webpack/src/HoneybadgerSourceMapPlugin.js index 80af00be6..bb9abe3a4 100644 --- a/packages/webpack/src/HoneybadgerSourceMapPlugin.js +++ b/packages/webpack/src/HoneybadgerSourceMapPlugin.js @@ -19,7 +19,7 @@ class HoneybadgerSourceMapPlugin { } async afterEmit (compilation) { - if (this.isDevServerRunning()) { + if (this.isDevEnv(this.options.developmentEnvironments)) { if (!this.options.silent) { console.info('\nHoneybadgerSourceMapPlugin will not upload source maps because webpack-dev-server is running.') } @@ -41,8 +41,12 @@ class HoneybadgerSourceMapPlugin { } } - isDevServerRunning () { - return process.env.WEBPACK_DEV_SERVER === 'true' + isDevEnv (devEnvironments) { + if (process.env.WEBPACK_DEV_SERVER === 'true') { + return true + } + + return !!(devEnvironments && devEnvironments.includes(process.env.NODE_ENV)); } apply (compiler) { From 6f99fc6e555060728aca0567f1355b466f24ae23 Mon Sep 17 00:00:00 2001 From: Pangratios Cosma Date: Mon, 21 Oct 2024 09:55:37 +0300 Subject: [PATCH 2/2] chore: fix tests --- packages/plugin-core/test/options.test.ts | 4 +++- packages/rollup-plugin/test/index.test.ts | 16 ++++++++++------ .../rollup-plugin/test/rollupUtils.test.ts | 7 +------ .../test/HoneybadgerSourceMapPlugin.test.js | 19 ++++++++++--------- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/packages/plugin-core/test/options.test.ts b/packages/plugin-core/test/options.test.ts index 8b68b6150..4a2b08bd2 100644 --- a/packages/plugin-core/test/options.test.ts +++ b/packages/plugin-core/test/options.test.ts @@ -9,6 +9,7 @@ import { DEFAULT_IGNORE_ERRORS, DEFAULT_WORKER_COUNT, MIN_WORKER_COUNT, + DEFAULT_DEVELOPMENT_ENVIRONMENTS, } from '../src/options'; describe('Options', () => { @@ -41,7 +42,7 @@ describe('Options', () => { workerCount: 0 }) expect(result.workerCount).to.equal(MIN_WORKER_COUNT) - }) + }) it('should merge in default options', () => { const result = cleanOptions({ @@ -62,6 +63,7 @@ describe('Options', () => { ignorePaths: [], ignoreErrors: DEFAULT_IGNORE_ERRORS, workerCount: DEFAULT_WORKER_COUNT, + developmentEnvironments: DEFAULT_DEVELOPMENT_ENVIRONMENTS, }) }) }); diff --git a/packages/rollup-plugin/test/index.test.ts b/packages/rollup-plugin/test/index.test.ts index 1187b9d00..1d2331b1a 100644 --- a/packages/rollup-plugin/test/index.test.ts +++ b/packages/rollup-plugin/test/index.test.ts @@ -6,7 +6,11 @@ import * as td from 'testdouble' describe('Index', () => { let honeybadgerRollupPlugin let core, rollupUtils - const options = { apiKey: 'test_key', assetsUrl: 'https://foo.bar' } + const options = { + apiKey: 'test_key', + assetsUrl: 'https://foo.bar', + developmentEnvironments: ['dev', 'development', 'test'] + } beforeEach(async () => { core = td.replace('@honeybadger-io/plugin-core') @@ -35,7 +39,7 @@ describe('Index', () => { const sourcemapData = [{ sourcemapFilename: 'index.map.js' }] it('should upload sourcemaps', async () => { - td.when(rollupUtils.isNonProdEnv()).thenReturn(false) + td.when(rollupUtils.isDevEnv(options.developmentEnvironments)).thenReturn(false) td.when(rollupUtils.extractSourcemapDataFromBundle(outputOptions, bundle, undefined)).thenReturn(sourcemapData) td.when(core.cleanOptions(options)).thenReturn(options) @@ -48,7 +52,7 @@ describe('Index', () => { it('should send deploy notification if deploy is true', async () => { const deployTrueOpt = { ...options, deploy: true } - td.when(rollupUtils.isNonProdEnv()).thenReturn(false) + td.when(rollupUtils.isDevEnv(options.developmentEnvironments)).thenReturn(false) td.when(rollupUtils.extractSourcemapDataFromBundle({ outputOptions, bundle })).thenReturn(sourcemapData) td.when(core.cleanOptions(deployTrueOpt)).thenReturn(deployTrueOpt) @@ -61,7 +65,7 @@ describe('Index', () => { it('should send deploy notification if deploy is an object', async () => { const deployObjOpt = { ...options, deploy: { localUsername: 'me' } } - td.when(rollupUtils.isNonProdEnv()).thenReturn(false) + td.when(rollupUtils.isDevEnv(options.developmentEnvironments)).thenReturn(false) td.when(rollupUtils.extractSourcemapDataFromBundle({ outputOptions, bundle })).thenReturn(sourcemapData) td.when(core.cleanOptions(deployObjOpt)).thenReturn(deployObjOpt) @@ -74,7 +78,7 @@ describe('Index', () => { it('should not send deploy notification if deploy is false', async () => { const deployFalseOpt = { ...options, deploy: false } - td.when(rollupUtils.isNonProdEnv()).thenReturn(false) + td.when(rollupUtils.isDevEnv(options.developmentEnvironments)).thenReturn(false) td.when(rollupUtils.extractSourcemapDataFromBundle({ outputOptions, bundle })).thenReturn(sourcemapData) td.when(core.cleanOptions(deployFalseOpt)).thenReturn(deployFalseOpt) @@ -87,7 +91,7 @@ describe('Index', () => { }) it('should do nothing in non-prod environments', async () => { - td.when(rollupUtils.isNonProdEnv()).thenReturn(true) + td.when(rollupUtils.isDevEnv(options.developmentEnvironments)).thenReturn(true) td.when(core.cleanOptions(options)).thenReturn(options) const plugin = honeybadgerRollupPlugin(options) diff --git a/packages/rollup-plugin/test/rollupUtils.test.ts b/packages/rollup-plugin/test/rollupUtils.test.ts index 1f0c7f84b..770d97520 100644 --- a/packages/rollup-plugin/test/rollupUtils.test.ts +++ b/packages/rollup-plugin/test/rollupUtils.test.ts @@ -92,7 +92,7 @@ describe('extractSourcemapDataFromBundle', () => { }) describe('isDevEnv', () => { - const developmentEnvironments = ['development', 'test', 'staging'] + const developmentEnvironments = ['dev', 'development', 'test'] let restore beforeEach(() => { @@ -100,30 +100,25 @@ describe('isDevEnv', () => { }) afterEach(() => { - // @ts-expect-error process.env.NODE_ENV = restore }) it('returns true if NODE_ENV is non-prod', () => { - // @ts-expect-error process.env.NODE_ENV = 'development' expect(isDevEnv(developmentEnvironments)).to.equal(true) }) it('returns false if NODE_ENV is non-prod but not in developmentEnvironments array', () => { - // @ts-expect-error process.env.NODE_ENV = 'staging' expect(isDevEnv(developmentEnvironments)).to.equal(false) }) it('returns false if NODE_ENV is missing', () => { - // @ts-expect-error delete process.env.NODE_ENV expect(isDevEnv(developmentEnvironments)).to.equal(false) }) it('returns false if NODE_ENV is prod', () => { - // @ts-expect-error process.env.NODE_ENV = 'production' expect(isDevEnv(developmentEnvironments)).to.equal(false) }) diff --git a/packages/webpack/test/HoneybadgerSourceMapPlugin.test.js b/packages/webpack/test/HoneybadgerSourceMapPlugin.test.js index f33a720cc..f3a2c8f7e 100644 --- a/packages/webpack/test/HoneybadgerSourceMapPlugin.test.js +++ b/packages/webpack/test/HoneybadgerSourceMapPlugin.test.js @@ -17,7 +17,7 @@ describe('HoneybadgerSourceMapPlugin', function () { const options = { apiKey: 'abcd1234', - assetsUrl: 'https://cdn.example.com/assets', + assetsUrl: 'https://cdn.example.com/assets', endpoint: `${TEST_ENDPOINT}${SOURCEMAP_PATH}`, deployEndpoint: `${TEST_ENDPOINT}${DEPLOY_PATH}`, } @@ -62,6 +62,7 @@ describe('HoneybadgerSourceMapPlugin', function () { revision: 'main', silent: false, workerCount: 5, + developmentEnvironments: ['dev', 'development', 'test'] }) }) }) @@ -91,13 +92,13 @@ describe('HoneybadgerSourceMapPlugin', function () { id: 0, names: ['app'], files: ['app.5190.js', 'app.5190.js.map'] - }, + }, ] - const assets = [{ + const assets = [{ sourcemapFilePath: '/fake/output/path/app.5190.js.map', sourcemapFilename: 'app.5190.js.map', jsFilePath: '/fake/output/path/app.5190.js', - jsFilename: 'app.5190.js', + jsFilename: 'app.5190.js', }] const outputPath = '/fake/output/path' @@ -141,7 +142,7 @@ describe('HoneybadgerSourceMapPlugin', function () { } sinon.stub(plugin, 'uploadSourceMaps') sinon.stub(plugin, 'sendDeployNotification') - + await plugin.afterEmit(compilation) expect(plugin.sendDeployNotification.callCount).to.eq(1) expect(plugin.sendDeployNotification.calledWith(plugin.options)).to.equal(true) @@ -209,7 +210,7 @@ describe('HoneybadgerSourceMapPlugin', function () { id: 0, names: ['app'], files: ['app.5190.js', 'app.5190.js.map'] - }, + }, { id: 1, names: ['foo'], @@ -220,17 +221,17 @@ describe('HoneybadgerSourceMapPlugin', function () { const compilation = { getStats: () => ({ toJson: () => ({ chunks }) - }), + }), compiler: { outputPath }, getPath: () => outputPath, } const expectedAssets = [ - { + { sourcemapFilePath: '/fake/output/path/app.5190.js.map', sourcemapFilename: 'app.5190.js.map', jsFilePath: '/fake/output/path/app.5190.js', - jsFilename: 'app.5190.js', + jsFilename: 'app.5190.js', } ]