Skip to content

Commit 12882f0

Browse files
committed
feat: add onViolation callback
1 parent 64d94a3 commit 12882f0

File tree

2 files changed

+40
-0
lines changed

2 files changed

+40
-0
lines changed

src/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ import { isAbsolute, join, relative } from 'pathe'
33
import { createUnplugin } from 'unplugin'
44
import { createFilter } from 'unplugin-utils'
55

6+
export interface ImpoundViolationInfo {
7+
/** The resolved import specifier that was denied. */
8+
id: string
9+
/** The file that contains the denied import. */
10+
importer: string
11+
/** The formatted error message. */
12+
message: string
13+
}
14+
615
export interface ImpoundMatcherOptions {
716
/** An array of patterns of importers to apply the import protection rules to. */
817
include?: Array<string | RegExp>
@@ -18,6 +27,11 @@ export interface ImpoundMatcherOptions {
1827
* This has no effect when `error` is `true` (the default), since the build fails on the first violation.
1928
*/
2029
warn?: 'once' | 'always'
30+
/**
31+
* Callback invoked on every violation. Receives the violation details.
32+
* Return `false` to allow the import and suppress the default error/warning.
33+
*/
34+
onViolation?: (info: ImpoundViolationInfo) => boolean | void
2135
/** An array of patterns to prevent being imported, along with an optional warning to display. */
2236
patterns: [importPattern: string | RegExp | ((id: string, importer: string) => boolean | string), warning?: string][]
2337
}
@@ -67,6 +81,9 @@ export const ImpoundPlugin = createUnplugin((globalOptions: ImpoundOptions) => {
6781

6882
if (usesImport) {
6983
const message = `${typeof usesImport === 'string' ? usesImport : (warning || 'Invalid import')} [importing \`${id}\` from \`${relativeImporter}\`]`
84+
if (options.onViolation?.({ id, importer: relativeImporter, message }) === false) {
85+
continue
86+
}
7087
if (!warnedMessages || !warnedMessages.has(message)) {
7188
warnedMessages?.add(message)
7289
logError(message)

test/index.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,29 @@ describe('impound plugin', () => {
9191
`)
9292
})
9393

94+
it('calls onViolation callback with violation info', async () => {
95+
const onViolation = vi.fn()
96+
const result = await process(code('bar'), { patterns: [['bar']], onViolation }) as RollupError
97+
expect(onViolation).toHaveBeenCalledWith({
98+
id: 'bar',
99+
importer: 'entry.js',
100+
message: 'Invalid import [importing `bar` from `entry.js`]',
101+
})
102+
expect(result.message).toMatchInlineSnapshot(`"[plugin impound] Invalid import [importing \`bar\` from \`entry.js\`]"`)
103+
})
104+
105+
it('allows import when onViolation returns false', async () => {
106+
const result = await process(code('bar'), {
107+
patterns: [['bar']],
108+
onViolation: () => false,
109+
})
110+
expect(result).toMatchInlineSnapshot(`
111+
"var thing = "loaded";
112+
113+
console.log(thing);"
114+
`)
115+
})
116+
94117
it('provides a helpful error message when importing a disallowed pattern', async () => {
95118
const result = await process(code('bar'), { patterns: [['bar', '"bar" is a dangerous library and should never be used.']] }) as RollupError
96119
expect(result.message).toMatchInlineSnapshot(`"[plugin impound] "bar" is a dangerous library and should never be used. [importing \`bar\` from \`entry.js\`]"`)

0 commit comments

Comments
 (0)