Skip to content

match_lib_folder_structure

v1.0.0WarningConfigurableCode Organization

Flags a file under lib/ sitting in a folder whose name is not lower_snake_case.

A folder name becomes part of every package: URI that imports through it, so it is public API in a way a local variable name is not — renaming it later is a breaking change for every consumer.

CamelCase and kebab-case folders also break on case-insensitive filesystems. A folder renamed from Models to models is invisible to git on macOS by default, so the import keeps resolving on the machine that made the change and fails in CI.

The SDK’s file_names rule checks the file; nothing in the SDK checks the directories above it, which is the gap this fills.

This rule is in the pedantic preset. It works out of the box; enable it in any other preset by name:

many_lints.yaml
rules:
match_lib_folder_structure:
enabled: true

See also: file_names, Effective Dart: naming

lib/dataSources/user_repository.dart
lib/data-sources/user_repository.dart
lib/Models/user.dart
lib/data_sources/user_repository.dart
lib/models/user.dart

The diagnostic names both the offending folder and its lower_snake_case form, so the rename is spelled out:

lib/dataSources/user.dart → data_sources
lib/data-sources/user.dart → data_sources
lib/UserProfile/view.dart → user_profile

Nested folders are all checked, but reported once per file

Section titled “Nested folders are all checked, but reported once per file”

Every folder between the root and the file is examined. Only the first offender reports, since a second diagnostic on the same path would be fixed by the same kind of rename:

lib/Features/userProfile/view.dart

Reported once, at Features. Fix it and re-analyze to see userProfile.

The check is ^[a-z0-9]+(_[a-z0-9]+)*$, so a folder may contain digits and underscore-separated words — it just may not start or end with an underscore, or double one up:

lib/api_v2/client.dart ✓
lib/oauth2/token.dart ✓
lib/_internal/helpers.dart ✗ leading underscore
lib/data__sources/user.dart ✗ doubled underscore

root: moves the whole check. Set it when your package’s sources are not under lib/:

analysis_options.yaml
many_lints:
rules:
match_lib_folder_structure:
root: packages

Files outside packages/ are then never reported — including everything under lib/.

Option Type Default Description
root string lib The top-level directory the rule applies to. Files outside it are never reported

A file directly in the root has no folders to check. lib/main.dart is never reported; the rule looks only at the segments between the root and the file name.

The root itself is never checked, only what is below it — root: lib does not report lib for anything.

The file name is not this rule’s job. file_names covers that, and prefer_match_file_name covers whether the name describes the contents.

No quick fix. Renaming a folder moves files and rewrites every import that passes through it, which is a refactoring your IDE or git mv should drive, not a single-file edit.

To disable this rule:

many_lints.yaml
rules:
match_lib_folder_structure: false

To keep the rule on but skip certain paths, use per-rule exclude.