Skip to content

Commit

Permalink
feat: add ignorePaths option
Browse files Browse the repository at this point in the history
Fixes: #1053
  • Loading branch information
subzero10 committed Apr 25, 2023
1 parent a39a9e9 commit 586c0f5
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 75 deletions.
8 changes: 4 additions & 4 deletions packages/rollup-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ export default function honeybadgerRollupPlugin(
const hbOptions = cleanOptions(options)

return {
name: 'honeybadger',
name: 'honeybadger',
writeBundle: async (
outputOptions: NormalizedOutputOptions,
outputOptions: NormalizedOutputOptions,
bundle: OutputBundle
) => {
if (isNonProdEnv()) {
Expand All @@ -22,12 +22,12 @@ export default function honeybadgerRollupPlugin(
return
}

const sourcemapData = extractSourcemapDataFromBundle(outputOptions, bundle)
const sourcemapData = extractSourcemapDataFromBundle(outputOptions, bundle, hbOptions.ignorePaths)
await uploadSourcemaps(sourcemapData, hbOptions)

if (hbOptions.deploy) {
await sendDeployNotification(hbOptions)
}
}
}
}
}
17 changes: 13 additions & 4 deletions packages/rollup-plugin/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const DEFAULT_REVISION = 'main'
export const DEFAULT_SILENT = false
export const DEFAULT_DEPLOY = false
export const DEPLOY_ENDPOINT = 'https://api.honeybadger.io/v1/deploys'
export const IGNORE_PATHS = []

/******************************
* Everything in this file is designed to be shared with the webpack plugin
Expand All @@ -19,12 +20,13 @@ const required = [
]

const defaultOptions = {
endpoint: DEFAULT_ENDPOINT,
retries: DEFAULT_RETRIES,
revision: DEFAULT_REVISION,
silent: DEFAULT_SILENT,
endpoint: DEFAULT_ENDPOINT,
retries: DEFAULT_RETRIES,
revision: DEFAULT_REVISION,
silent: DEFAULT_SILENT,
deploy: DEFAULT_DEPLOY,
deployEndpoint: DEPLOY_ENDPOINT,
ignorePaths: IGNORE_PATHS,
}

export function cleanOptions(
Expand All @@ -36,13 +38,20 @@ export function cleanOptions(
throw new Error(`${field} is required`)
}
})

// Validate ignorePaths
if (options.ignorePaths && !Array.isArray(options.ignorePaths)) {
throw new Error('ignorePaths must be an array')
}

// Don't allow excessive retries
if (options.retries && options.retries > MAX_RETRIES) {
if (!options.silent) {
console.warn(`Using max retries: ${MAX_RETRIES}`)
}
options.retries = MAX_RETRIES
}

// Merge in our defaults
return { ...defaultOptions, ...options }
}
47 changes: 35 additions & 12 deletions packages/rollup-plugin/src/rollupUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,53 @@ import type { SourcemapInfo } from './types'
/**
* Extracts the data we need for sourcemap upload from the bundle
*/
export function extractSourcemapDataFromBundle (
outputOptions: NormalizedOutputOptions,
bundle: OutputBundle
export function extractSourcemapDataFromBundle (
outputOptions: NormalizedOutputOptions,
bundle: OutputBundle,
ignorePaths: Array<string | RegExp> = []
): SourcemapInfo[] {
const sourceMaps = Object.values(bundle).filter(isSourcemap)

return sourceMaps.map(sourcemap => {
return formatSourcemapData(outputOptions, sourcemap)
})
}).filter((sourcemap) => isNotIgnored(sourcemap, ignorePaths))
}

function isSourcemap(file: OutputAsset | OutputChunk): file is OutputAsset {
return file.type === 'asset' && file.fileName.endsWith('.js.map')
if (file.type !== 'asset' || !file.fileName.endsWith('.js.map') || !file.source) {
return false
}

const source = typeof file.source === 'string' ? file.source : file.source.toString()
const json = JSON.parse(source)

return json.sourcesContent && json.sourcesContent.length > 0
}

function isNotIgnored(sourceMapInfo: SourcemapInfo, ignorePaths: Array<string | RegExp>) {
for (const ignorePath of ignorePaths) {
let regex = ignorePath
if (!(regex instanceof RegExp)) {
regex = new RegExp(regex)
}

if (regex.test(sourceMapInfo.jsFilePath)) {
return false
}
}

return true
}

function formatSourcemapData(
outputOptions: NormalizedOutputOptions,
outputOptions: NormalizedOutputOptions,
sourcemap: OutputAsset): SourcemapInfo {
// This fileName could include a path like 'subfolder/foo.js.map'
const sourcemapFilename = sourcemap.fileName
const sourcemapFilePath = path.resolve(outputOptions.dir || '', sourcemapFilename)
// It should be safe to assume that rollup will name the map with
// the same name as the js file... however we can pull the file name
// from the sourcemap source just in case.
// It should be safe to assume that rollup will name the map with
// the same name as the js file... however we can pull the file name
// from the sourcemap source just in case.
let jsFilename: string
if (typeof sourcemap.source === 'string') {
const { file } = JSON.parse(sourcemap.source)
Expand All @@ -47,9 +70,9 @@ function formatSourcemapData(
/**
* Determines if we are in a non-production environment
* Note that in Vite setups, NODE_ENV should definitely be available
* In Rollup without Vite, it may or may not be available,
* 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'
}
}
5 changes: 3 additions & 2 deletions packages/rollup-plugin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export interface HbPluginOptions {
revision: string;
silent: boolean;
deployEndpoint: string;
deploy: boolean | Deploy
deploy: boolean | Deploy;
ignorePaths: Array<string | RegExp>;
}

export interface Deploy {
Expand All @@ -29,4 +30,4 @@ export interface SourcemapInfo {
sourcemapFilePath: string;
jsFilename: string;
jsFilePath: string;
}
}
46 changes: 23 additions & 23 deletions packages/rollup-plugin/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { HbPluginOptions } from '../src/types'

describe('Index', () => {
let honeybadgerRollupPlugin:(opts: Partial<HbPluginOptions> & Pick<HbPluginOptions, 'apiKey' | 'assetsUrl'>) => Plugin
let cleanOptionsMock,
isNonProdEnvMock,
extractSourcemapDataFromBundleMock,
uploadSourcemapsMock,
let cleanOptionsMock,
isNonProdEnvMock,
extractSourcemapDataFromBundleMock,
uploadSourcemapsMock,
sendDeployNotificationMock
const options = { apiKey: 'test_key', assetsUrl: 'https://foo.bar' }

Expand All @@ -19,18 +19,18 @@ describe('Index', () => {
uploadSourcemapsMock = td.func()
sendDeployNotificationMock = td.func()

td.replace('../src/options', {
cleanOptions: cleanOptionsMock
td.replace('../src/options', {
cleanOptions: cleanOptionsMock
})
td.replace('../src/rollupUtils', {
extractSourcemapDataFromBundle: extractSourcemapDataFromBundleMock,
isNonProdEnv: isNonProdEnvMock
})
td.replace('../src/hbUtils', {
uploadSourcemaps: uploadSourcemapsMock,
uploadSourcemaps: uploadSourcemapsMock,
sendDeployNotification: sendDeployNotificationMock
})

const indexModule = await import('../src/index')
honeybadgerRollupPlugin = indexModule.default
})
Expand All @@ -44,7 +44,7 @@ describe('Index', () => {

td.verify(cleanOptionsMock(options))
expect(plugin.name).to.equal('honeybadger')
expect(plugin.writeBundle).to.be.a('function')
expect(plugin.writeBundle).to.be.a('function')
})

describe('writeBundle', () => {
Expand All @@ -55,13 +55,13 @@ describe('Index', () => {

it('should upload sourcemaps', async () => {
td.when(isNonProdEnvMock()).thenReturn(false)
td.when(extractSourcemapDataFromBundleMock(outputOptions, bundle)).thenReturn(sourcemapData)
td.when(extractSourcemapDataFromBundleMock(outputOptions, bundle, undefined)).thenReturn(sourcemapData)
td.when(cleanOptionsMock(options)).thenReturn(options)

const plugin = honeybadgerRollupPlugin(options)
//@ts-ignore
await plugin.writeBundle(outputOptions, bundle)

td.verify(uploadSourcemapsMock(sourcemapData, options))
})

Expand All @@ -70,11 +70,11 @@ describe('Index', () => {
td.when(isNonProdEnvMock()).thenReturn(false)
td.when(extractSourcemapDataFromBundleMock({ outputOptions, bundle })).thenReturn(sourcemapData)
td.when(cleanOptionsMock(deployTrueOpt)).thenReturn(deployTrueOpt)

const plugin = honeybadgerRollupPlugin(deployTrueOpt)
//@ts-ignore
await plugin.writeBundle(outputOptions, bundle)

td.verify(sendDeployNotificationMock(deployTrueOpt))
})

Expand All @@ -83,11 +83,11 @@ describe('Index', () => {
td.when(isNonProdEnvMock()).thenReturn(false)
td.when(extractSourcemapDataFromBundleMock({ outputOptions, bundle })).thenReturn(sourcemapData)
td.when(cleanOptionsMock(deployObjOpt)).thenReturn(deployObjOpt)

const plugin = honeybadgerRollupPlugin(deployObjOpt)
//@ts-ignore
await plugin.writeBundle(outputOptions, bundle)

td.verify(sendDeployNotificationMock(deployObjOpt))
})

Expand All @@ -96,29 +96,29 @@ describe('Index', () => {
td.when(isNonProdEnvMock()).thenReturn(false)
td.when(extractSourcemapDataFromBundleMock({ outputOptions, bundle })).thenReturn(sourcemapData)
td.when(cleanOptionsMock(deployFalseOpt)).thenReturn(deployFalseOpt)

const plugin = honeybadgerRollupPlugin(deployFalseOpt)
//@ts-ignore
await plugin.writeBundle(outputOptions, bundle)

// Verify not called
td.verify(sendDeployNotificationMock(), { times: 0, ignoreExtraArgs: true })
})

it('should do nothing in non-prod environments', async () => {
td.when(isNonProdEnvMock()).thenReturn(true)
td.when(cleanOptionsMock(options)).thenReturn(options)

const plugin = honeybadgerRollupPlugin(options)
//@ts-ignore
await plugin.writeBundle(outputOptions, bundle)

// Verify these were not called
td.verify(extractSourcemapDataFromBundleMock(), { times: 0, ignoreExtraArgs: true })
td.verify(uploadSourcemapsMock(), { times: 0, ignoreExtraArgs: true })
td.verify(sendDeployNotificationMock(), { times: 0, ignoreExtraArgs: true })
})
})

})

})
43 changes: 26 additions & 17 deletions packages/rollup-plugin/test/options.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { expect } from 'chai'
import {
MAX_RETRIES,
DEFAULT_ENDPOINT,
DEFAULT_REVISION,
import {
MAX_RETRIES,
DEFAULT_ENDPOINT,
DEFAULT_REVISION,
DEFAULT_SILENT,
DEPLOY_ENDPOINT,
cleanOptions,
cleanOptions,
} from '../src/options';

describe('Options', () => {
Expand All @@ -14,32 +14,41 @@ describe('Options', () => {
expect(cleanOptions.bind(cleanOptions, {})).to.throw('apiKey is required')
});

it('should error if ignorePaths is not an array', () => {
expect(cleanOptions.bind(cleanOptions, {
apiKey: 'test_key',
assetsUrl: 'https://foo.bar',
ignorePaths: 'foo'
})).to.throw('ignorePaths must be an array')
});

it('should not allow retries above the MAX_RETRIES', () => {
const result = cleanOptions({
apiKey: 'test_key',
const result = cleanOptions({
apiKey: 'test_key',
assetsUrl: 'https://foo.bar',
retries: 100
retries: 100
})
expect(result.retries).to.equal(MAX_RETRIES)
})

it('should merge in default options', () => {
const result = cleanOptions({
apiKey: 'test_key',
const result = cleanOptions({
apiKey: 'test_key',
assetsUrl: 'https://foo.bar',
retries: 0,
retries: 0,
deploy: { localUsername: 'BethanyBerkowitz' },
})
expect(result).to.deep.equal({
apiKey: 'test_key',
apiKey: 'test_key',
assetsUrl: 'https://foo.bar',
retries: 0,
endpoint: DEFAULT_ENDPOINT,
revision: DEFAULT_REVISION,
silent: DEFAULT_SILENT,
retries: 0,
endpoint: DEFAULT_ENDPOINT,
revision: DEFAULT_REVISION,
silent: DEFAULT_SILENT,
deploy: { localUsername: 'BethanyBerkowitz' },
deployEndpoint: DEPLOY_ENDPOINT,
ignorePaths: [],
})
})
});
});
});

0 comments on commit 586c0f5

Please sign in to comment.