apotheke

apotheke.config.mjs

Every configuration option, with defaults and behaviour.

Config lives in apotheke.config.mjs (or .js) and default-exports an object. It is discovered by walking up from each formatted file.

Always use .mjs

A .ts config is never loaded. Node — where Prettier runs — cannot import() TypeScript, so only apotheke.config.mjs and apotheke.config.js are discovered.

apotheke.config.mjs
export default {
    extends: '../../apotheke.config.mjs',
    groups: [{ name: 'React', match: ['react', 'react-dom'] }],
    aliases: { '@': './src' },
    baseUrl: '.',
    groupSeparator: true,
    groupComments: true
};

Options

OptionTypeDefaultDescription
groupsGroupConfig[]— (required)Ordered group definitions. First match wins.
extendsstringundefinedPath to a parent config, relative to this file.
aliasesRecord<string, string>from tsconfigImport prefix → directory. Merged with tsconfig paths.
baseUrlstringfrom tsconfigBase for resolving alias targets.
groupSeparatorbooleantrueBlank line between groups.
groupCommentsbooleantrue// GroupName header above each group.

There is no option to disable sorting or deduplication; both always run.

groups

interface GroupConfig {
    name: string; // used as the comment header
    match: string[]; // glob patterns tested against the specifier
}

Required — apotheke has no built-in group set and throws if the config is missing. Groups are evaluated in array order and the first match claims the import. Unmatched imports fall into an implicit trailing Others group. Side-effect imports are not grouped at all: they stay where they were written and nothing moves across them — see side-effect imports.

Supported glob syntax:

TokenMeaning
*any run of characters within one path segment
**any number of segments; a leading **/ also matches zero segments
?exactly one character, not /

Everything else is literal. Character classes ([abc]) and brace expansion ({a,b}) are not supported — write separate patterns instead.

extends

Resolved relative to the directory of the file containing it, and may chain to further parents. A missing parent throws with the resolved path.

Merge semantics:

FieldBehaviour
groupsMerged by name — a child group replaces the parent's entry in place; new names append.
aliasesShallow-merged, child wins per key.
everything elseChild value replaces parent value when present.

You cannot delete an inherited group.

aliases

aliases: {
    '@': './src',
    '#internal': './packages/internal/src'
}

Keys omit the trailing /*. Entries from tsconfig.json compilerOptions.paths are merged in automatically, and your explicit entries win on key collisions. Only the first target of a multi-target path mapping is used.

Alias targets resolve against the nearest package.json directory under the CLI, but against process.cwd() under the Prettier plugin — see monorepos.

baseUrl

Taken from tsconfig.json when present and not set here. Only read from tsconfig.json; jsconfig.json is ignored.

groupSeparator / groupComments

Both default to true and are only disabled by an explicit false.

Ordering and blank lines, no // headers
export default {
    groups: [/* ... */],
    groupComments: false
};

tsconfig integration

At load time apotheke reads tsconfig.json from the config file's directory and merges compilerOptions.paths and compilerOptions.baseUrl. Comments in the tsconfig are stripped before parsing, and an unreadable or malformed tsconfig is ignored rather than fatal.

Full example

apotheke.config.mjs
export default {
    groups: [
        { name: 'Node', match: ['node:*'] },
        { name: 'React', match: ['react', 'react-dom'] },
        { name: 'Forms', match: ['react-hook-form', '@hookform/resolvers/**', 'zod'] },
        { name: 'Api', match: ['@tanstack/react-query', 'axios', '**/api/**'] },
        { name: 'Hooks', match: ['**/hooks/**'] },
        { name: 'Components', match: ['**/components/**'] },
        { name: 'Styling', match: ['clsx', 'tailwind-merge'] }
    ],
    aliases: { '@': './src' },
    groupSeparator: true,
    groupComments: true
};

Note Forms precedes any wildcard React group so react-hook-form is not swallowed by react-*.

On this page