Amended by 0128: enums,
classes and component folders follow that ADR.
Context
File names, how many things a file exports, how barrels look and how modules name each other are decided on the first file or re-argued on the twentieth. Four earlier decisions covered these separately. They are one convention, and it is frozen before packages are created in bulk, because a codemod over many packages costs far more than agreeing now.Decision
- Kebab-case for every file and directory under
src/. No PascalCase, camelCase or underscores. - One exported declaration per file, named for it, with a kind suffix where the kind is not a plain function:
token.class.ts,scope.enum.ts,class-provider.interface.ts,payment.error.ts. Barrels are exempt. - Barrels re-export and nothing else.
index.tsis the only barrel name; it names every symbol (export type { Token } from "./token.class"), neverexport *, so the public surface is readable without compiling. - Relative specifiers carry no extension and no trailing
/index. Exempt: package specifiers, non-code assets (.css,.svg,.json), generated output, raw Node.mjsand.cjsscripts undertools/, andpackages/tooling/*(loaded by plain Node, which needs full specifiers). - Climbing imports use package subpath imports. A member with a
src/tree declaresimportsinpackage.json(#core,#application/*, …). A relative specifier may stay in its folder or descend; it never climbs with../. Theimportsmap is generated by a script, never hand-written. - A helper used by a second file moves to
utils/<name>.util.ts. A single-use private helper stays where it is used. - Conventions change by ADR only. No package-local exceptions.
Consequences
- A file’s name tells the reader what it exports; a rename is a move.
- Wildcard barrels are not available, so adding a symbol is a visible diff in the barrel.
- Subpath imports need a bundler and Metro that resolve
package.jsonimports; native builds are verified before the first native entry ships.
Enforcement
typescript/consistent-type-importsin the base oxlint preset (off only in the decorators preset, see 0005).- Planned: a validator check for kebab-case names, one export per file and barrel shape; an import-specifier check (port basalt
tools/scripts/import-style.mjs); animportsmap drift check against the generator.
Supersedes and sources
Supersedes: none. Sources: basaltadr/0010, 0020, 0022, 0032; figentra docs/adr/0010-module-specifiers, 0019-subpath-imports, 0035-conventions-freeze.