Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adds configurable dev environments for source map upload plugins #1412

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/esbuild-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ These plugin parameters correspond to the Honeybadger [Source Map Upload API](ht
<dd>The name of the user that triggered this deploy, for example, "Jane"</dd>
</dl>
</dd>

<dt><code>developmentEnvironments</code> (optional &mdash; default: ["dev", "development", "test"])</dt>
<dd>Used to decide whether source maps should be uploaded or not.</dd>
</dl>

### esbuild.config.js
Expand Down
10 changes: 7 additions & 3 deletions packages/esbuild-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class HoneybadgerSourceMapPlugin {
return {
name: PLUGIN_NAME,
setup(build: PluginBuild) {
if (self.isNonProdEnv()) {
if (self.isDevEnv(self.options.developmentEnvironments)) {
return
}

Expand Down Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions packages/plugin-core/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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(
Expand All @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type HbPluginOptions = {
ignorePaths: Array<string>;
ignoreErrors: boolean;
workerCount: number;
developmentEnvironments: Array<string>;
}

// Options passed in by a user
Expand Down
4 changes: 3 additions & 1 deletion packages/plugin-core/test/options.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
DEFAULT_IGNORE_ERRORS,
DEFAULT_WORKER_COUNT,
MIN_WORKER_COUNT,
DEFAULT_DEVELOPMENT_ENVIRONMENTS,
} from '../src/options';

describe('Options', () => {
Expand Down Expand Up @@ -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({
Expand All @@ -62,6 +63,7 @@ describe('Options', () => {
ignorePaths: [],
ignoreErrors: DEFAULT_IGNORE_ERRORS,
workerCount: DEFAULT_WORKER_COUNT,
developmentEnvironments: DEFAULT_DEVELOPMENT_ENVIRONMENTS,
})
})
});
Expand Down
3 changes: 3 additions & 0 deletions packages/rollup-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ These plugin parameters correspond to the Honeybadger [Source Map Upload API](ht
<dd>The name of the user that triggered this deploy, for example, "Jane"</dd>
</dl>
</dd>

<dt><code>developmentEnvironments</code> (optional &mdash; default: ["dev", "development", "test"])</dt>
<dd>Used to decide whether source maps should be uploaded or not.</dd>
</dl>

### rollup.config.js
Expand Down
4 changes: 2 additions & 2 deletions packages/rollup-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -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.')
}
Expand Down
8 changes: 6 additions & 2 deletions packages/rollup-plugin/src/rollupUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
16 changes: 10 additions & 6 deletions packages/rollup-plugin/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)
Expand Down
16 changes: 11 additions & 5 deletions packages/rollup-plugin/test/rollupUtils.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -91,7 +91,8 @@ describe('extractSourcemapDataFromBundle', () => {
})
})

describe('isNonProdEnv', () => {
describe('isDevEnv', () => {
const developmentEnvironments = ['dev', 'development', 'test']
let restore

beforeEach(() => {
Expand All @@ -104,16 +105,21 @@ describe('isNonProdEnv', () => {

it('returns true if NODE_ENV is non-prod', () => {
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', () => {
process.env.NODE_ENV = 'staging'
expect(isDevEnv(developmentEnvironments)).to.equal(false)
})

it('returns false if NODE_ENV is missing', () => {
delete process.env.NODE_ENV
expect(isNonProdEnv()).to.equal(false)
expect(isDevEnv(developmentEnvironments)).to.equal(false)
})

it('returns false if NODE_ENV is prod', () => {
process.env.NODE_ENV = 'production'
expect(isNonProdEnv()).to.equal(false)
expect(isDevEnv(developmentEnvironments)).to.equal(false)
})
})
11 changes: 8 additions & 3 deletions packages/rollup-plugin/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
"extends": "../../tsconfig.base.json",
"include": [
"src/**/*.ts"
],
],
"compilerOptions": {
"rootDir": "./",
"rootDir": "./",
"allowSyntheticDefaultImports": true
},
}
"references": [
{
"path": "../plugin-core"
}
]
}
3 changes: 3 additions & 0 deletions packages/webpack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ These plugin parameters correspond to the Honeybadger [Source Map Upload API](ht
<dd>The name of the user that triggered this deploy, for example, "Jane"</dd>
</dl>
</dd>

<dt><code>developmentEnvironments</code> (optional &mdash; default: ["dev", "development", "test"])</dt>
<dd>Source maps upload will be skipped when the environment matches any of the values in this array or the webpack dev server is running.</dd>
</dl>

### Vanilla webpack.config.js
Expand Down
10 changes: 7 additions & 3 deletions packages/webpack/src/HoneybadgerSourceMapPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.')
}
Expand All @@ -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) {
Expand Down
19 changes: 10 additions & 9 deletions packages/webpack/test/HoneybadgerSourceMapPlugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
}
Expand Down Expand Up @@ -62,6 +62,7 @@ describe('HoneybadgerSourceMapPlugin', function () {
revision: 'main',
silent: false,
workerCount: 5,
developmentEnvironments: ['dev', 'development', 'test']
})
})
})
Expand Down Expand Up @@ -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'

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -209,7 +210,7 @@ describe('HoneybadgerSourceMapPlugin', function () {
id: 0,
names: ['app'],
files: ['app.5190.js', 'app.5190.js.map']
},
},
{
id: 1,
names: ['foo'],
Expand All @@ -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',
}
]

Expand Down
Loading