Skip to content

avoid_banned_exports

v1.0.0WarningConfigurableArchitecture

Flags export directives for libraries you ban, optionally only inside the directories you name. Barrel-file hygiene: it keeps internal libraries out of a package’s public API.

A barrel file decides what a package promises. An export added there for convenience — to save one import in a test, say — silently makes everything in that library public, and removing it later is a breaking change for every consumer.

This is kept separate from avoid_banned_imports on purpose: depending on a library internally and re-exporting it to consumers are different decisions. A package is often free to use something it must not expose.

This rule is in no preset and reports nothing until you add banned:. Every example below shows the configuration that produces it.

analysis_options.yaml
many_lints:
rules:
avoid_banned_exports:
banned:
- deny_pattern: ['src/internal/.*']
in: ['lib/*.dart']
message: 'Internal libraries must not be part of the public API.'

The URI is matched as written in the export directive. in: ['lib/*.dart'] is the useful default: a single * matches only top-level barrel files, so everything under lib/src/** stays free to export internally.

- deny_pattern: ['src/internal/.*']
in: ['lib/*.dart']
message: 'Internal libraries must not be part of the public API.'
// Don't — in lib/my_package.dart
export 'src/internal/cache.dart'; // LINT: makes the cache public API
// Do — in lib/my_package.dart: export only what consumers depend on
export 'src/api/client.dart';
export 'src/api/models.dart';

Stop a dependency leaking through your API

Section titled “Stop a dependency leaking through your API”

Re-exporting a package makes its types part of your signature, so its next major version becomes your breaking change:

- deny: ['package:dio/dio.dart', 'package:http/http.dart']
in: ['lib/*.dart']
message: 'Wrap the HTTP client; do not re-export it.'
// Don't — in lib/api_client.dart
export 'package:dio/dio.dart'; // LINT
// Do — expose your own types only
export 'src/api/api_client.dart';
export 'src/api/api_failure.dart';

Omit deny in favour of a pattern that matches everything, scoped to the directories where a re-export is never right:

- deny_pattern: ['.*']
in: ['lib/src/features/**']
message: 'Feature libraries import what they need; they re-export nothing.'
// Don't — in lib/src/features/cart/cart.dart
export 'cart_controller.dart'; // LINT
// Do
import 'cart_controller.dart';
CartController build() => CartController();

Keep a generated part out of the public surface

Section titled “Keep a generated part out of the public surface”
- deny_pattern: ['.*\.g\.dart']
message: 'Generated code is an implementation detail.'
// Don't
export 'models/user.g.dart'; // LINT
// Do
export 'models/user.dart';
Option Type Default Description
banned list of entries [] The entries to enforce. With none, the rule reports nothing

Per entry:

Key Type Required Description
deny string or list one of deny / deny_pattern Export URIs banned by exact match
deny_pattern string or list one of deny / deny_pattern Regular expressions, anchored to the whole URI
in list of globs no Paths, relative to the package root, where the entry applies. Omit to apply everywhere
message string no A project-specific explanation appended to the diagnostic

The URI is matched as written. A relative export 'src/internal/cache.dart'; is matched as that relative path, not as a resolved package: URI — so an entry written one way does not catch the other. Write the spelling your codebase uses, or cover both.

show / hide are ignored. export 'src/internal/cache.dart' show Cache; is reported exactly like a bare export; the rule bans the library, not the symbols taken from it.

Chained exports are invisible. If a barrel exports a permitted library that itself exports a banned one, only the file with the banned line is reported.

A malformed entry is skipped silently. An invalid regular expression costs you that pattern and nothing else — a plugin cannot report problems against a YAML file.