--- id: adrs-adr004 title: 'ADR004: Module Export Structure' # prettier-ignore description: Architecture Decision Record (ADR) log on Module Export Structure --- ## Context With a growing number of exports of packages like `@backstage/core-components`, it is becoming more and more difficult to answer questions such as > Is the export in this module also exported by the package? or > What is exported from this directory? We currently do not use any pattern for how to structure exports. There is a mix of package-level re-exports deep into the directory tree, shallow re-exports for each directory, exports using `*` and explicit lists of each symbol, etc. The mix and lack of predictability make it difficult to reason about the boundaries of a module, and for example knowing whether it is safe to export a symbol in a given file. ## Decision We will make each exported symbol traceable through index files all the way down to the root of the package, `src/index.ts`. Each index file will only re-export from its own immediate directory children, and only index files will have re-exports. This gives a file tree similar to this: ```text index.ts components/index.ts /ComponentX/index.ts /ComponentX.tsx /SubComponentY.tsx lib/index.ts /UtilityX/index.ts /UtilityX.ts /helper.ts ``` To check whether for example `SubComponentY` is exported from the package, it should be possible to traverse the index files towards the root, starting at the adjacent one. If there is any index file that doesn't export the previous one, the symbol is not publicly exported. For example, if `components/ComponentX/index.ts` exports `SubComponentY`, but `components/index.ts` does not re-export `./ComponentX`, one should be certain that `SubComponentY` is not exported outside the package. This rule would be broken if for example the root `index.ts` re-exports `./components/ComponentX` In addition, index files that are re-exporting other index files should always use wildcard form, that is: ```ts // in components/index.ts export * from './ComponentX'; ``` Index files that are re-exporting symbols from non-index files should always enumerate all exports, that is: ```ts // in components/ComponentX/index.ts export { ComponentX } from './ComponentX'; export type { ComponentXProps } from './ComponentX'; ``` Internal cross-directory imports are allowed from non-index modules to index modules, for example: ```ts // in components/ComponentX/ComponentX.tsx import { UtilityX } from '../../lib/UtilityX'; ``` Imports that bypass an index file are discouraged, but may sometimes be necessary, for example: ```ts // in components/ComponentX/ComponentX.tsx import { helperFunc } from '../../lib/UtilityX/helper'; ``` ## Consequences We will actively work to rework the export structure in our codebase, prioritizing the library packages such as `@backstage/core-components` and `@backstage/backend-common`. If possible, we will add tools, such as lint rules, to help enforce the export structure.