apotheke 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marco Raffaello
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,22 @@
1
- # apotheke
2
-
3
- Import organizer for JavaScript and TypeScript projects. Groups, sorts, and deduplicates imports based on a simple config file.
1
+ <p align="left">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/header/graph.svg?title=apotheke&subtitle=Deterministic%20import%20organization%20for%20JavaScript%20and%20TypeScript&logo=https://raw.githubusercontent.com/mRaffaello/apotheke/main/apps/docs/public/mark-dark.png&align=left&font=geist&mode=dark" />
4
+ <img alt="apotheke — deterministic import organization for JavaScript and TypeScript" src="https://shieldcn.dev/header/graph.svg?title=apotheke&subtitle=Deterministic%20import%20organization%20for%20JavaScript%20and%20TypeScript&logo=https://raw.githubusercontent.com/mRaffaello/apotheke/main/apps/docs/public/mark.png&align=left&font=geist&mode=light" />
5
+ </picture>
6
+ </p>
7
+
8
+ <p align="left">
9
+ <a href="https://www.npmjs.com/package/apotheke">
10
+ <picture>
11
+ <source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/group/npm/apotheke+npm/dm/apotheke+bundlephobia/minzip/apotheke+npm/types/apotheke+github/ci/mRaffaello/apotheke+github/license/mRaffaello/apotheke.svg?variant=secondary&mode=dark" />
12
+ <img alt="npm version, monthly downloads, minzipped size, TypeScript types, CI status, MIT license" src="https://shieldcn.dev/group/npm/apotheke+npm/dm/apotheke+bundlephobia/minzip/apotheke+npm/types/apotheke+github/ci/mRaffaello/apotheke+github/license/mRaffaello/apotheke.svg?variant=secondary&mode=light" />
13
+ </picture>
14
+ </a>
15
+ </p>
16
+
17
+ **Prettier for imports.** You describe the groups your codebase actually has —
18
+ apotheke sorts every import into them, the same way, in every file. Run it as a
19
+ Prettier plugin or as a standalone CLI.
4
20
 
5
21
  ```ts
6
22
  // Before
@@ -25,224 +41,36 @@ import { useQuery } from '@tanstack/react-query';
25
41
  import { createRoute } from '@tanstack/react-router';
26
42
  ```
27
43
 
28
- ## Requirements
29
-
30
- - Node.js ≥ 18
44
+ Note where `../api/tsr` and `@tanstack/react-query` landed: grouping is by
45
+ **role in your app**, not by whether a module is local or comes from
46
+ `node_modules`.
31
47
 
32
- ## Installation
48
+ ## Getting started
33
49
 
34
50
  ```sh
35
- npm install -D apotheke
36
- # or
37
51
  pnpm add -D apotheke
38
52
  ```
39
53
 
40
- ## Usage
41
-
42
- ```sh
43
- apotheke --write src/**/*.{ts,tsx} # format in place
44
- apotheke --check src/**/*.{ts,tsx} # CI — exit 1 if anything would change
45
- apotheke --diff src/**/*.{ts,tsx} # print diff without writing
46
- apotheke --stdin-filepath src/app.tsx # read from stdin, write to stdout
47
- ```
48
-
49
- ## Config
50
-
51
- Create `apotheke.config.mjs` at your project root:
52
-
53
- ```js
54
- // apotheke.config.mjs
55
- export default {
56
- groups: [
57
- { name: 'React', match: ['react', 'react-dom', 'react-*'] },
58
- { name: 'Hooks', match: ['**/hooks/**'] },
59
- { name: 'Api', match: ['**/api/**', '@tanstack/react-query'] },
60
- { name: 'Navigation', match: ['@tanstack/react-router'] },
61
- { name: 'Assets', match: ['lucide-react'] }
62
- ],
63
- aliases: {
64
- '@': './src' // mirrors tsconfig paths — auto-read if omitted
65
- },
66
- groupSeparator: true, // blank line between groups
67
- groupComments: true // // GroupName header above each group
68
- };
69
- ```
70
-
71
- Apotheke also reads `tsconfig.json` (or `jsconfig.json`) automatically to pick up `paths` aliases and `baseUrl`, so you often don't need to set `aliases` manually.
72
-
73
- Unmatched imports collect in an **Others** group at the end. Side-effect imports (`import './styles.css'`) always go first.
74
-
75
- ### Monorepo
76
-
77
- Place a root config and extend it per-package:
78
-
79
- ```js
80
- // apps/web/apotheke.config.mjs
81
- export default {
82
- extends: '../../apotheke.config.mjs',
83
- groups: [{ name: 'Shared', match: ['@acme/*'] }]
84
- };
85
- ```
86
-
87
- ---
88
-
89
- ## Prettier plugin
90
-
91
- Apotheke ships as a prettier plugin. When loaded, it runs as a `preprocess` step — apotheke organises imports first, then prettier formats the result. One pass, correct order, no conflicts.
92
-
93
- **Requirements:** Prettier v3 or later (v3 supports async `preprocess`; v2 does not).
94
-
95
- ### Permanent setup
96
-
97
- **1. Install apotheke**
98
-
99
- ```sh
100
- pnpm add -D apotheke
101
- ```
102
-
103
- **2. Add the plugin to your prettier config**
104
-
105
- ```js
106
- // .prettierrc.js
107
- module.exports = {
108
- plugins: ['apotheke'],
109
- };
110
- ```
111
-
112
- That's it — `prettier --write` will now organise imports automatically.
113
-
114
- ### Quick test (without installing)
115
-
116
- ```sh
117
- cd /path/to/your-project
118
- npx prettier@3 \
119
- --plugin /path/to/apotheke/dist/index.js \
120
- --write 'src/App.tsx'
121
- ```
122
-
123
- ### VS Code on-save
124
-
125
- Install the [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) extension. Because `prettier.format()` calls our plugin's `preprocess` hook directly, no extra config is needed:
126
-
127
- ```json
128
- {
129
- "editor.defaultFormatter": "esbenp.prettier-vscode",
130
- "editor.formatOnSave": true
131
- }
132
- ```
133
-
134
- ### Pre-commit with Husky + lint-staged
135
-
136
- ```json
137
- {
138
- "lint-staged": {
139
- "*.{ts,tsx,js,jsx}": ["prettier --write"]
140
- }
141
- }
142
- ```
143
-
144
- ### Prettier v2 fallback
54
+ 📖 **[Read the documentation →](https://marcoraffaello.com/apotheke)**
145
55
 
146
- If you can't upgrade to prettier v3, use the CLI sequentially via lint-staged:
147
-
148
- ```json
149
- {
150
- "lint-staged": {
151
- "*.{ts,tsx,js,jsx}": [
152
- "apotheke --write",
153
- "prettier --write"
154
- ]
155
- }
156
- }
157
- ```
158
-
159
- ---
160
-
161
- ## Testing locally without publishing
162
-
163
- ### Option 1 — Direct invocation (no setup)
164
-
165
- ```sh
166
- node /path/to/apotheke/dist/cli.js --write 'src/**/*.{ts,tsx}'
167
- ```
168
-
169
- Build first if needed: `cd /path/to/apotheke && pnpm build`
170
-
171
- ### Option 2 — `pnpm link` (recommended)
172
-
173
- **In the apotheke repo:**
174
-
175
- ```sh
176
- pnpm build
177
- pnpm link --global
178
- ```
179
-
180
- **In your target repo:**
181
-
182
- ```sh
183
- pnpm link --global apotheke
184
- ```
185
-
186
- Now `apotheke --write src/**/*.tsx` works as if it were installed normally. You'll need to re-run `pnpm build` in the apotheke repo after making source changes.
187
-
188
- **To unlink when done:**
189
-
190
- ```sh
191
- # In your target repo
192
- pnpm unlink --global apotheke
193
-
194
- # In the apotheke repo
195
- pnpm unlink --global
196
- ```
197
-
198
- ### Option 3 — Path dependency in `package.json`
199
-
200
- ```sh
201
- pnpm add /path/to/apotheke
202
- ```
203
-
204
- ---
56
+ The [quick start](https://marcoraffaello.com/apotheke/docs/quick-start) goes
57
+ from an empty config to organised imports in about two minutes.
205
58
 
206
59
  ## Development
207
60
 
61
+ This repository is a pnpm workspace. The published package lives in
62
+ `packages/apotheke`, the documentation site in `apps/docs`.
63
+
208
64
  ```sh
209
65
  pnpm install
210
-
211
- # Build (required before running locally or testing the plugin)
212
- pnpm build
213
-
214
- # Run all tests (unit + e2e)
215
- pnpm test
216
-
217
- # Unit tests only
218
- pnpm test:unit
219
-
220
- # E2E tests against real repos (cloned automatically on first run)
221
- pnpm test:e2e
222
-
223
- # Type check
224
- pnpm typecheck
66
+ pnpm build # build the package (required before format and dogfooding)
67
+ pnpm test # unit + e2e
68
+ pnpm docs:dev # run the documentation site locally
225
69
  ```
226
70
 
227
- ### Project structure
71
+ Issues and pull requests are welcome at
72
+ [github.com/mRaffaello/apotheke](https://github.com/mRaffaello/apotheke).
228
73
 
229
- ```
230
- src/
231
- parser.ts OXC-based import extractor
232
- grouper.ts Glob-based group assignment
233
- sorter.ts Alphabetical sort, type imports float to top
234
- deduplicator.ts Merge named/default imports from same specifier
235
- printer.ts Reconstruct import block with group headers
236
- config.ts Load apotheke.config.mjs, merge tsconfig aliases
237
- format.ts Top-level formatImports(source, config)
238
- types.ts Shared types
239
- cli.ts CLI source (Node.js)
240
- index.ts Prettier plugin + programmatic API
241
- dist/
242
- cli.js Compiled CLI — run with node or via the apotheke bin
243
- index.js Compiled prettier plugin
244
- index.d.ts Types for programmatic use
245
- tests/
246
- unit/ 76 unit tests
247
- e2e/ 20 e2e tests against sonner and tremor
248
- ```
74
+ ## License
75
+
76
+ MIT © Marco Raffaello
package/SKILL.md ADDED
@@ -0,0 +1,249 @@
1
+ ---
2
+ name: setup-apotheke
3
+ description: Set up apotheke import organizer in any JS/TS project. Scans existing imports, proposes group config, writes apotheke.config.mjs, and wires up prettier plugin. Use when user wants to organize imports, set up apotheke, or add import formatting to a project.
4
+ ---
5
+
6
+ # Setup Apotheke
7
+
8
+ Apotheke is **Prettier for imports** — deterministic, configurable import organization for JavaScript and TypeScript. It is not a replacement for prettier: it runs *inside* prettier as a `preprocess` plugin, so imports get organized and code gets formatted in a single `prettier --write` pass. It also ships a standalone CLI for projects that do not use prettier.
9
+
10
+ This skill installs and configures it by analyzing the project's actual imports, then wiring it up as a prettier plugin.
11
+
12
+ Full documentation: <https://marcoraffaello.com/apotheke>
13
+
14
+ ## Step 1 — Detect project structure (monorepo vs single package)
15
+
16
+ Before doing anything else, determine whether this is a monorepo. Check for these signals at the working directory root:
17
+
18
+ | File / field | Tool |
19
+ | ---------------------------------------- | ----------------------- |
20
+ | `pnpm-workspace.yaml` | pnpm workspaces |
21
+ | `turbo.json` | Turborepo |
22
+ | `nx.json` | Nx |
23
+ | `lerna.json` | Lerna |
24
+ | `rush.json` | Rush |
25
+ | `package.json` with `"workspaces"` field | npm/yarn/bun workspaces |
26
+
27
+ If any of these are present, it's a **monorepo**. Follow the monorepo path below. Otherwise follow the single-package path.
28
+
29
+ ### Single-package path
30
+
31
+ Proceed to Step 2 with the repo root as the single package root.
32
+
33
+ ### Monorepo path
34
+
35
+ 1. Identify all package roots by reading the workspaces glob (e.g. `packages/*`, `apps/*`) from whichever workspace config is present. List them to the user.
36
+ 2. Ask: "Should I set up a shared root config that all packages inherit from, or configure each package independently?"
37
+ - **Shared root** (recommended): write a root `apotheke.config.mjs` with groups common across all packages; each package gets its own config with `extends: "../../apotheke.config.mjs"` that overrides or adds package-specific groups.
38
+ - **Independent**: each package gets its own self-contained config with no inheritance.
39
+ 3. Scan each package separately (Step 2 runs once per package root). Combine the findings to build the shared group set before writing configs.
40
+ 4. In Step 6, add scripts to each package's `package.json` scoped to that package's source folder. Optionally add a root-level script that runs prettier across all packages.
41
+
42
+ ## Step 2 — Scan the project
43
+
44
+ For each package root (or just the single root), search for all `.ts`, `.tsx`, `.js`, `.jsx` files (excluding `node_modules`, `dist`, `.next`, `build`, `out`). Read a sample of files (up to 20 per package, spread across the directory tree) and extract the unique import specifiers.
45
+
46
+ Pay attention to:
47
+
48
+ - **Package imports**: `react`, `@tanstack/react-query`, `lucide-react`, etc.
49
+ - **Path alias imports**: `@/components/...`, `~/lib/...`, `#utils/...`
50
+ - **Relative path patterns**: which folders appear most — `hooks/`, `api/`, `components/`, `views/`, `pages/`, `utils/`, `lib/`, `stores/`, etc.
51
+ - **tsconfig.json / jsconfig.json**: read `compilerOptions.paths` and `compilerOptions.baseUrl` to understand existing aliases. In a monorepo, check both the root tsconfig and each package's tsconfig.
52
+
53
+ ## Step 3 — Check for existing config
54
+
55
+ Look for `apotheke.config.mjs`, `apotheke.config.js`, or `apotheke.config.ts` at each relevant root. If one already exists, read it, tell the user, and ask whether to update it or abort.
56
+
57
+ ## Step 4 — Propose groups
58
+
59
+ Based on what you found, propose a `groups` array. Use your judgment to create meaningful groups. Common patterns:
60
+
61
+ - If the project uses React → group `react`, `react-dom`, `react-*`
62
+ > **Warning — `react-*` is greedy**: it will also match `react-hook-form`, `react-i18next`, `react-day-picker`, etc. Groups are matched in order and the first match wins. If you want those libraries in their own groups (Forms, i18n, Date), either list them in those groups _before_ the React group in the array, or narrow the React match to just `['react', 'react-dom']` without the wildcard.
63
+ - If the project has `hooks/` folder → group `**/hooks/**`
64
+ - If the project has `api/`, `services/`, `queries/` → group those paths + data-fetching libraries (react-query, swr, etc.)
65
+ - If the project has `components/`, `ui/` → group those
66
+ - If the project uses a router (react-router, tanstack-router, next/navigation) → group it
67
+ - If the project uses icon libraries (lucide-react, @heroicons, react-icons) → group as Assets
68
+ - If the project has a `store/`, `context/` folder → group it
69
+ - If the project uses a UI kit (@mui, @radix-ui, shadcn) → group it
70
+ - In a monorepo, internal workspace packages (e.g. `@acme/*`) deserve their own group
71
+
72
+ Always propose a sensible **alias map** based on tsconfig `paths`.
73
+
74
+ In a monorepo with a shared root config: put universal groups (React, external libraries) in the root config, and package-specific groups (internal paths, local components) in each package config.
75
+
76
+ Present the proposed config clearly and ask the user:
77
+
78
+ 1. Are these groups right? Any to add, remove, or rename?
79
+ 2. Is the order right? Groups are matched top-to-bottom and the first match wins.
80
+ 3. Should there be blank lines between groups? (`groupSeparator`, default: yes)
81
+ 4. Should group comments (`// React`) be added? (`groupComments`, default: yes)
82
+
83
+ Do **not** offer to toggle sorting or deduplication. Imports are always sorted
84
+ alphabetically within a group, named imports are always sorted within the
85
+ braces, and duplicate specifiers are always merged. There are no options for
86
+ these.
87
+
88
+ Wait for confirmation before proceeding.
89
+
90
+ ## Step 5 — Write apotheke.config.mjs
91
+
92
+ **Always use `.mjs`** — it works with both the CLI and the Node.js prettier plugin. Never write `.ts` configs: Node.js (where prettier runs) cannot `import()` TypeScript files. Only `apotheke.config.mjs` and `apotheke.config.js` are discovered.
93
+
94
+ ```js
95
+ // apotheke.config.mjs
96
+ export default {
97
+ groups: [
98
+ // ... confirmed groups
99
+ ],
100
+ aliases: {
101
+ // ... from tsconfig paths
102
+ },
103
+ groupSeparator: true,
104
+ groupComments: true
105
+ };
106
+ ```
107
+
108
+ For a monorepo with a shared root:
109
+
110
+ ```js
111
+ // apps/web/apotheke.config.mjs
112
+ export default {
113
+ extends: '../../apotheke.config.mjs',
114
+ groups: [
115
+ // package-specific groups only — root groups are inherited
116
+ { name: 'Local Components', match: ['**/components/**'] }
117
+ ]
118
+ };
119
+ ```
120
+
121
+ ## Step 6 — Install apotheke and wire up prettier
122
+
123
+ ### 6a. Install apotheke
124
+
125
+ Check if apotheke is already in `package.json` dependencies. If not, install it:
126
+
127
+ - Bun project (`bun.lock` present) → `bun add -D apotheke`
128
+ - pnpm (`pnpm-lock.yaml`) → `pnpm add -D apotheke`
129
+ - yarn (`yarn.lock`) → `yarn add -D apotheke`
130
+ - npm (`package-lock.json`) → `npm install -D apotheke`
131
+
132
+ In a monorepo, install at the root unless the workspace requires per-package installs.
133
+
134
+ ### 6b. Add apotheke to the prettier config
135
+
136
+ Check for `.prettierrc`, `.prettierrc.js`, `.prettierrc.json`, or `prettier.config.js`. Add `"apotheke"` to the `plugins` array (create the file if none exists):
137
+
138
+ ```js
139
+ // prettier.config.js (or .prettierrc.js)
140
+ module.exports = {
141
+ // ...existing options unchanged...
142
+ plugins: ['apotheke']
143
+ };
144
+ ```
145
+
146
+ For JSON prettier configs (`.prettierrc` or `.prettierrc.json`):
147
+
148
+ ```json
149
+ {
150
+ "plugins": ["apotheke"]
151
+ }
152
+ ```
153
+
154
+ > **Ordering is critical**: apotheke must be the **last** entry in `plugins`. It chains to the previous plugin's `preprocess` hook via `getBase()?.preprocess`, so any plugin listed after it will completely override apotheke's preprocess and imports will not be organized. If another prettier plugin (e.g. `prettier-plugin-tailwindcss`) is already present, place `"apotheke"` after it:
155
+ >
156
+ > ```json
157
+ > { "plugins": ["prettier-plugin-tailwindcss", "apotheke"] }
158
+ > ```
159
+
160
+ ### 6c. Verify prettier version
161
+
162
+ Check that prettier v3 is installed (`"prettier": "^3"` in devDependencies). If v2 is found, tell the user:
163
+
164
+ > "Apotheke's prettier plugin requires prettier v3 (async `preprocess`). I can upgrade it, or set up the v2 fallback (CLI + prettier in sequence). Which do you prefer?"
165
+
166
+ **v2 fallback** (lint-staged only):
167
+
168
+ ```json
169
+ {
170
+ "lint-staged": {
171
+ "*.{ts,tsx,js,jsx}": ["apotheke --write", "prettier --write"]
172
+ }
173
+ }
174
+ ```
175
+
176
+ ## Step 7 — Add scripts to package.json
177
+
178
+ With the prettier plugin wired up, a single `prettier --write` does everything. Add:
179
+
180
+ ```json
181
+ {
182
+ "scripts": {
183
+ "format": "prettier --write 'src/**/*.{ts,tsx,js,jsx}'",
184
+ "format:check": "prettier --check 'src/**/*.{ts,tsx,js,jsx}'"
185
+ }
186
+ }
187
+ ```
188
+
189
+ Adjust the glob to match the project's source folder. In a monorepo, also add a root script:
190
+
191
+ ```json
192
+ {
193
+ "scripts": {
194
+ "format": "prettier --write 'packages/*/src/**/*.{ts,tsx}' 'apps/*/src/**/*.{ts,tsx}'",
195
+ "format:check": "prettier --check 'packages/*/src/**/*.{ts,tsx}' 'apps/*/src/**/*.{ts,tsx}'"
196
+ }
197
+ }
198
+ ```
199
+
200
+ ## Step 8 — VS Code on-save (optional)
201
+
202
+ Ask the user: "Do you want imports organized automatically on save in VS Code?"
203
+
204
+ If yes, the prettier VS Code extension handles this automatically once the plugin is in the prettier config — no extra config needed. Confirm that the extension is installed and `editor.formatOnSave` is enabled:
205
+
206
+ ```json
207
+ // .vscode/settings.json
208
+ {
209
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
210
+ "editor.formatOnSave": true,
211
+ "editor.codeActionsOnSave": {
212
+ "source.organizeImports": false
213
+ }
214
+ }
215
+ ```
216
+
217
+ Disable `source.organizeImports` to prevent VS Code's built-in import sorter from conflicting with apotheke.
218
+
219
+ ## Step 9 — Run a dry-run
220
+
221
+ Run a true dry-run (no writes) on a small sample (3–5 files, one per package in a monorepo) and show the user what would change:
222
+
223
+ ```sh
224
+ prettier --list-different 'src/App.tsx' 'src/main.tsx'
225
+ ```
226
+
227
+ > `--write --list-different` together would actually write the files; use `--list-different` alone for a real dry-run.
228
+
229
+ Show the before/after for imports to confirm the groups look right.
230
+
231
+ Ask: "Does this look right? Shall I run `format` on the whole project?"
232
+
233
+ If yes, run the `format` script using the package manager detected in Step 6a
234
+ (`pnpm run format`, `bun run format`, `yarn format` or `npm run format`).
235
+
236
+ ## Final summary
237
+
238
+ Report:
239
+
240
+ - Project type: single-package or monorepo (list package roots if monorepo)
241
+ - Config(s) written to: paths
242
+ - Groups configured: list them
243
+ - Prettier plugin: wired up in `prettier.config.js` (or equivalent)
244
+ - Script added: `format` / `format:check`
245
+ - Files that would be changed: N files
246
+ - Whether `--write` was run
247
+
248
+ Point the user at the documentation for anything beyond this setup:
249
+ <https://marcoraffaello.com/apotheke>