@rolandjlevy/css-ghost 1.0.4 โ†’ 1.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,24 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Roland Levy
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.
22
+
23
+ ---
24
+ Standard MIT License text sourced from https://choosealicense.com/licenses/mit/
package/README.md CHANGED
@@ -1,16 +1,19 @@
1
1
  # ๐Ÿ‘ป css-ghost
2
2
 
3
- Find orphaned CSS class selectors in your codebase โ€” defined in style files but never referenced in your components.
3
+ Find and remove unused CSS selectors and orphaned CSS custom properties โ€” selectors defined in your style files but never referenced in your components, across React, Vue, Svelte, SCSS, and CSS Modules
4
4
 
5
5
  ## What it does
6
6
 
7
7
  Scans one or more directories for style files, extracts every class selector defined, then cross-references them against usages in your component files. Anything defined but never used is reported as an orphan โ€” with its line number and the actual CSS printed inline.
8
8
 
9
9
  **Supported style files:**
10
+
10
11
  - `.css`, `.module.css`, `.scss`, `.sass`, `.less`
11
- - `.js` / `.ts` files using `StyleSheet.create({})` or named style objects
12
+ - `.js` / `.ts` files using `StyleSheet.create({})`, `makeStyles({})` (MUI), or named style objects
13
+ - `.css.ts` / `.css.js` files using `style()` or `styleVariants()` (vanilla-extract)
12
14
 
13
15
  **Detected usage patterns:**
16
+
14
17
  - `className="foo"`
15
18
  - `className={styles.foo}`
16
19
  - `styles['foo']`
@@ -18,6 +21,8 @@ Scans one or more directories for style files, extracts every class selector def
18
21
  - `cx('foo')`, `clsx('foo')`, `classnames('foo')`
19
22
  - `cx({ active: isOn, disabled: true })` โ€” object keys extracted
20
23
  - `cx(['foo', condition && 'bar'])` โ€” array string literals extracted
24
+ - `classes.foo` โ€” MUI `makeStyles` / `useStyles` convention
25
+ - `import { foo } from './styles.css'` โ€” vanilla-extract named imports
21
26
 
22
27
  **Dynamic class detection:**
23
28
  Patterns where the class name is truly unknowable at parse time โ€” `styles[variable]` or template literals containing `${}` โ€” are flagged with a โš ๏ธ warning. All statically resolvable patterns (string literals, ternaries, object keys) are analysed without warnings.
@@ -53,24 +58,177 @@ npx @rolandjlevy/css-ghost ./src
53
58
  ## Usage
54
59
 
55
60
  ```bash
56
- css-ghost <directory> [directory2] ... [options]
61
+ css-ghost [directory] [directory2] ... [options]
57
62
  ```
58
63
 
59
64
  **Options:**
60
65
 
61
- | Flag | Description | Default |
62
- | --- | --- | --- |
63
- | `--output <file>` | Output report file path | `orphan-report.md` |
64
- | `--ai` | Append an AI-generated summary (requires setup) | off |
65
- | `--setup` | Save your OpenRouter API key for `--ai` | โ€” |
66
- | `-h, --help` | Show help | โ€” |
66
+ | Flag | Description | Default |
67
+ | ----------------- | ---------------------------------------------------- | ------------------ |
68
+ | `--output <file>` | Output report file path | `orphan-report.md` |
69
+ | `--delete` | Delete all orphaned selectors from source files | off |
70
+ | `--yes` | Skip the deletion confirmation prompt | off |
71
+ | `--strict` | Exit with code 1 when any orphans are found (CI use) | off |
72
+ | `--ghost-trail` | Show the last git commit that touched each orphan | off |
73
+ | `--install-hook` | Write a pre-commit hook to `.git/hooks/pre-commit` | โ€” |
74
+ | `--ai` | Append an AI-generated summary (requires setup) | off |
75
+ | `--setup` | Save your OpenRouter API key for `--ai` | โ€” |
76
+ | `-h, --help` | Show help | โ€” |
77
+
78
+ ## CSS variable orphan detection
79
+
80
+ css-ghost also catches **orphaned CSS custom properties** โ€” variables defined with `--my-token: value` but never consumed anywhere via `var(--my-token)`.
81
+
82
+ Usages are detected across all file types: CSS, SCSS, JS, TS, JSX, TSX, Vue, Svelte, HTML. A variable defined in `variables.css` and used in a React component is correctly seen as _used_.
83
+
84
+ ```
85
+ ๐Ÿ”ฎ --colour-danger-hover (line 8)
86
+ --colour-danger-hover: #dc2626;
87
+
88
+ ๐Ÿ”ฎ --spacing-xl (line 26)
89
+ --spacing-xl: 64px;
90
+ ```
91
+
92
+ Orphaned variables appear under a separate **๐Ÿ”ฎ Orphaned CSS variables** section in both the terminal output and the markdown report.
93
+
94
+ ---
95
+
96
+ ## Ghost trail โ€” git history for orphans
97
+
98
+ Pass `--ghost-trail` to fetch the last git commit that changed each orphan's occurrence count in the codebase. For class-name orphans the search is limited to component file types (`.tsx`, `.jsx`, `.ts`, `.js`, etc.) so the result reflects _usage_ history rather than the style-file definition.
99
+
100
+ ```bash
101
+ css-ghost ./src --ghost-trail
102
+ ```
103
+
104
+ ```
105
+ ๐Ÿ‘ป .iconRight (line 28)
106
+ ๐Ÿ•ต๏ธ b5c6efb ยท Roland Levy ยท 3 weeks ago โ€” "Refactor Button component"
107
+ .iconRight {
108
+ margin-left: 6px;
109
+ }
110
+ ```
111
+
112
+ For CSS variables the search looks for `var(--name` across all file types.
113
+
114
+ > **Note:** ghost trail runs one `git log -S` per orphan in parallel. On large histories with many orphans it may take a few seconds.
115
+
116
+ ---
117
+
118
+ ## Pre-commit hook
119
+
120
+ Install a hook that blocks commits when orphaned selectors are present:
121
+
122
+ ```bash
123
+ css-ghost --install-hook ./src
124
+ ```
125
+
126
+ This writes `.git/hooks/pre-commit` (executable, `chmod 755`) containing:
127
+
128
+ ```sh
129
+ #!/bin/sh
130
+ echo "๐Ÿ‘ป css-ghost โ€” checking for orphaned CSS selectors..."
131
+ npx --yes @rolandjlevy/css-ghost ./src --strict --output /tmp/css-ghost-hook-report.md
132
+
133
+ if [ $? -ne 0 ]; then
134
+ echo "โŒ Commit blocked: orphaned CSS selectors found"
135
+ echo " Run 'css-ghost ./src --delete' to remove them"
136
+ echo " Or skip: git commit --no-verify"
137
+ exit 1
138
+ fi
139
+ ```
140
+
141
+ Pass multiple directories to the hook installer the same way you would to a normal scan:
142
+
143
+ ```bash
144
+ css-ghost --install-hook ./src ./components ./pages
145
+ ```
146
+
147
+ To uninstall: `rm .git/hooks/pre-commit`
148
+
149
+ ### `--strict` flag
150
+
151
+ `--strict` makes css-ghost exit with code 1 when any orphans are found โ€” the mechanism the hook depends on, but also useful in CI workflows independently:
152
+
153
+ ```bash
154
+ # In CI: fail the build if orphans exist
155
+ css-ghost ./src --strict
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Interactive folder picker
161
+
162
+ Run `css-ghost` with no arguments and an arrow-key picker opens in your terminal:
163
+
164
+ ```
165
+ ๐Ÿ“‚ Directory picker โ€” navigate into a folder, then press [a] to add it
166
+
167
+ ๐Ÿ“ /home/user/my-project
168
+
169
+ ๐Ÿ“ components
170
+ โ–ถ ๐Ÿ“ src
171
+ ๐Ÿ“ styles
172
+
173
+ โœ… Added to scan:
174
+ /home/user/my-project/src
175
+
176
+ [โ†‘โ†“/jk] navigate [โ†ต/โ†’] enter [โ†] parent [a] add current dir [d] done [^C] quit
177
+ ```
178
+
179
+ | Key | Action |
180
+ | ---------------------- | --------------------------------------- |
181
+ | `โ†‘` / `โ†“` or `k` / `j` | Move up and down the folder list |
182
+ | `โ†ต` or `โ†’` | Enter the highlighted folder |
183
+ | `โ†` or Backspace | Go up to the parent folder |
184
+ | `a` | Add the current folder to the scan list |
185
+ | `d` | Done โ€” start the scan |
186
+ | `Ctrl+C` | Quit without scanning |
187
+
188
+ You can add **multiple folders** before pressing `d` โ€” just navigate to each one and press `a`. If you press `d` without adding anything, the folder you're currently browsing is used.
189
+
190
+ ## Deleting orphans
191
+
192
+ Pass `--delete` after a scan to remove every flagged selector directly from its source file:
193
+
194
+ ```bash
195
+ css-ghost ./src --delete
196
+ ```
197
+
198
+ css-ghost prints the full report first, then lists what will be deleted and asks for confirmation before touching anything. Pass `--yes` to skip the prompt (useful in CI or scripts):
199
+
200
+ ```bash
201
+ css-ghost ./src --delete --yes
202
+ ```
203
+
204
+ Works across all supported file types โ€” `.css`, `.scss`, `.less`, MUI `makeStyles`, and vanilla-extract exports.
205
+
206
+ > **Tip:** commit or stash your work before running `--delete`. There is no undo.
67
207
 
68
208
  ## Examples
69
209
 
70
210
  ```bash
211
+ # Interactive folder picker (no args)
212
+ css-ghost
213
+
71
214
  # Scan your src folder
72
215
  css-ghost ./src
73
216
 
217
+ # Show git history for each orphan
218
+ css-ghost ./src --ghost-trail
219
+
220
+ # Fail with exit code 1 when orphans exist (CI)
221
+ css-ghost ./src --strict
222
+
223
+ # Install a pre-commit hook that scans ./src
224
+ css-ghost --install-hook ./src
225
+
226
+ # Scan and delete all orphans (with confirmation prompt)
227
+ css-ghost ./src --delete
228
+
229
+ # Scan and delete without prompting
230
+ css-ghost ./src --delete --yes
231
+
74
232
  # Scan multiple directories
75
233
  css-ghost ./src ./components ./pages
76
234
 
@@ -111,6 +269,7 @@ Running `css-ghost ./src` prints a live summary to your terminal:
111
269
  ```
112
270
 
113
271
  Each orphan shows:
272
+
114
273
  - the selector name and the **exact line number** where it's defined
115
274
  - the **full CSS rule** so you can see what it does before deciding to delete it
116
275
 
@@ -160,39 +319,58 @@ npm run scan
160
319
  npm run scan -- --output ./reports/audit.md
161
320
  ```
162
321
 
163
- ## No dependencies
322
+ ## Troubleshooting
323
+
324
+ ### `css-ghost: command not found` after global install
325
+
326
+ Your npm global bin directory may not be on your PATH. Run:
327
+
328
+ ```bash
329
+ npm bin -g
330
+ ```
331
+
332
+ Then add that path to your shell config (`~/.zshrc`, `~/.bashrc`, etc.):
333
+
334
+ ```bash
335
+ export PATH="$(npm bin -g):$PATH"
336
+ ```
337
+
338
+ Reload with `source ~/.zshrc`. This is a one-time fix โ€” it won't be needed again after that.
339
+
340
+ > This is common when Node was installed via the official installer rather than nvm or Homebrew.
164
341
 
165
- Built with Node.js built-ins only (`fs`, `path`). No npm packages required.
342
+ ## Dependencies
343
+
344
+ One runtime dependency: [`picocolors`](https://github.com/alexeyraspopov/picocolors) for terminal colours. Everything else uses Node.js built-ins (`fs`, `path`, `readline`).
166
345
 
167
346
  ## Origin โ€” built with OpenRouter free models via Claude Code
168
347
 
169
348
  This tool was created as a test project to explore [using OpenRouter's free model tier with Claude Code](https://www.mindstudio.ai/blog/open-router-free-models-claude-code-cost-reduction) as a way to cut AI development costs.
170
349
 
171
- Claude Code normally calls Anthropic's API directly. By overriding two environment variables you can route it through OpenRouter's free tier instead โ€” meaning you can run agentic coding sessions at no cost using models like Llama 3.3 70B or Deepseek R1.
172
-
173
- ### Setup
350
+ Claude Code normally calls Anthropic's API directly. By overriding two environment variables you can route it through OpenRouter's free tier instead โ€” meaning you can run agentic coding sessions at no cost.
174
351
 
175
- Get a free API key at [openrouter.ai/keys](https://openrouter.ai/keys), then add these aliases to your `~/.zshrc`:
352
+ If you've already run `css-ghost --setup`, you have an OpenRouter key. You can also export it as an environment variable โ€” css-ghost picks it up automatically, so you don't even need `--setup`:
176
353
 
177
354
  ```bash
178
- # Store your key once
179
355
  export OPENROUTER_API_KEY="sk-or-v1-YOUR_KEY"
356
+ ```
180
357
 
181
- # Llama 3.3 70B โ€” fast, capable, free
358
+ And you can reuse the same key with Claude Code by adding these aliases to your `~/.zshrc`:
359
+
360
+ ```bash
182
361
  alias claude-free='ANTHROPIC_BASE_URL="https://openrouter.ai/api/v1" \
183
362
  ANTHROPIC_API_KEY="$OPENROUTER_API_KEY" \
184
- claude --model "meta-llama/llama-3.3-70b-instruct:free"'
363
+ claude --model "nvidia/nemotron-3.5-lightning:free"'
185
364
 
186
- # Deepseek R1 โ€” strongest free option for reasoning and debugging
187
365
  alias claude-r1='ANTHROPIC_BASE_URL="https://openrouter.ai/api/v1" \
188
366
  ANTHROPIC_API_KEY="$OPENROUTER_API_KEY" \
189
- claude --model "deepseek/deepseek-r1:free"'
367
+ claude --model "qwen/qwen3.8-27b:free"'
190
368
  ```
191
369
 
192
370
  Then `source ~/.zshrc`. Use `claude-free` or `claude-r1` in place of `claude` to start a free session.
193
371
 
194
- > **Note:** Free tier models handle Claude Code's tool call format well in practice, but run a quick agentic task first to confirm file read/edit/bash loops work end to end in your environment before committing to it for serious work.
372
+ > **Note:** Free model availability changes frequently. Browse current options at [openrouter.ai/models](https://openrouter.ai/models?q=free) โ€” filter by `:free`.
195
373
 
196
374
  ## License
197
375
 
198
- MIT
376
+ [MIT](./LICENSE)
package/bin/css-ghost.js CHANGED
@@ -4,6 +4,7 @@ import { resolve } from 'path';
4
4
  import { existsSync as fsExists } from 'fs';
5
5
  import { scan } from '../src/scanner.js';
6
6
  import { generateReport } from '../src/reporter.js';
7
+ import { deleteOrphans } from '../src/deleter.js';
7
8
  import { runSetup } from '../src/setup.js';
8
9
  import { getAiSummary } from '../src/ai.js';
9
10
 
@@ -15,22 +16,44 @@ if (args.includes('--setup')) {
15
16
  process.exit(0);
16
17
  }
17
18
 
18
- if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
19
+ // --install-hook [dirs...]
20
+ if (args.includes('--install-hook')) {
21
+ const { installHook } = await import('../src/hook.js');
22
+ // Any positional args become the directories the hook will scan
23
+ const hookDirs = args.filter(a => !a.startsWith('-'));
24
+ installHook(hookDirs, process.cwd());
25
+ process.exit(0);
26
+ }
27
+
28
+ if (args.includes('--help') || args.includes('-h')) {
19
29
  console.log(`
20
- ๐Ÿ‘ป css-ghost โ€” Find orphaned CSS class selectors
30
+ ๐Ÿ‘ป css-ghost โ€” Find orphaned CSS class selectors and custom properties
21
31
 
22
32
  Usage:
23
- css-ghost <directory> [directory2] ... [options]
33
+ css-ghost [directory] [directory2] ... [options]
34
+
35
+ When no directory is given, an interactive folder picker opens.
24
36
 
25
37
  Options:
26
- --output <file> Output report file path (default: orphan-report.md)
27
- --ai Append an AI-generated summary to the report
28
- --setup Save your OpenRouter API key for use with --ai
29
- -h, --help Show this help message
38
+ --output <file> Output report file path (default: orphan-report.md)
39
+ --delete Delete all orphaned selectors from source files (prompts first)
40
+ --yes Skip the deletion confirmation prompt (use with --delete)
41
+ --strict Exit with code 1 when orphans are found (useful in CI)
42
+ --ghost-trail Show the last git commit that touched each orphan
43
+ --install-hook Write a pre-commit hook to .git/hooks/pre-commit
44
+ --ai Append an AI-generated summary to the report
45
+ --setup Save your OpenRouter API key for use with --ai
46
+ -h, --help Show this help message
30
47
 
31
48
  Examples:
49
+ css-ghost # interactive folder picker
32
50
  css-ghost ./src
33
51
  css-ghost ./src ./components
52
+ css-ghost ./src --ghost-trail # show git history for each orphan
53
+ css-ghost ./src --strict # exit 1 when orphans found (CI mode)
54
+ css-ghost ./src --delete
55
+ css-ghost ./src --delete --yes
56
+ css-ghost --install-hook ./src # install pre-commit hook for ./src
34
57
  css-ghost ./src --ai
35
58
  css-ghost ./src --output ./reports/css-audit.md
36
59
  `);
@@ -38,34 +61,41 @@ Examples:
38
61
  }
39
62
 
40
63
  // Parse flags
41
- const useAi = args.includes('--ai');
42
- const outputIdx = args.indexOf('--output');
43
- const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : 'orphan-report.md';
64
+ const useAi = args.includes('--ai');
65
+ const doDelete = args.includes('--delete');
66
+ const skipConfirm = args.includes('--yes') || args.includes('-y');
67
+ const strict = args.includes('--strict');
68
+ const ghostTrail = args.includes('--ghost-trail');
69
+ const outputIdx = args.indexOf('--output');
70
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : 'orphan-report.md';
44
71
 
45
72
  if (outputIdx !== -1 && !outputPath) {
46
73
  console.error('โŒ --output requires a file path');
47
74
  process.exit(1);
48
75
  }
49
76
 
50
- // Remaining args are directories (strip flags and their values)
77
+ // Remaining positional args are directories (strip flags and their values)
51
78
  const rawDirs = args.filter((a, i) =>
52
- !a.startsWith('--') && (outputIdx === -1 || i !== outputIdx + 1)
79
+ !a.startsWith('-') && (outputIdx === -1 || i !== outputIdx + 1)
53
80
  );
54
81
 
82
+ // No directories given โ†’ launch interactive picker
83
+ let dirs;
55
84
  if (rawDirs.length === 0) {
56
- console.error('โŒ At least one directory is required');
57
- process.exit(1);
58
- }
59
-
60
- // Validate directories exist
61
- const dirs = [];
62
- for (const d of rawDirs) {
63
- const resolved = resolve(d);
64
- if (!fsExists(resolved)) {
65
- console.error(`โŒ Directory not found: ${d}`);
66
- process.exit(1);
85
+ const { pickDirectories } = await import('../src/picker.js');
86
+ dirs = await pickDirectories();
87
+ console.log('');
88
+ } else {
89
+ // Validate provided directories exist
90
+ dirs = [];
91
+ for (const d of rawDirs) {
92
+ const resolved = resolve(d);
93
+ if (!fsExists(resolved)) {
94
+ console.error(`โŒ Directory not found: ${d}`);
95
+ process.exit(1);
96
+ }
97
+ dirs.push(resolved);
67
98
  }
68
- dirs.push(resolved);
69
99
  }
70
100
 
71
101
  console.log(`\n๐Ÿ‘ป css-ghost scanning ${dirs.length} director${dirs.length === 1 ? 'y' : 'ies'}...`);
@@ -79,7 +109,40 @@ if (results.length === 0) {
79
109
  process.exit(0);
80
110
  }
81
111
 
112
+ // Ghost trail โ€” fetch git history for each orphan (opt-in, runs before report)
113
+ if (ghostTrail) {
114
+ const { isGitRepo, getGhostTrails } = await import('../src/git.js');
115
+ if (!isGitRepo(process.cwd())) {
116
+ console.warn('โš ๏ธ --ghost-trail: not a git repository โ€” skipping git history.\n');
117
+ } else {
118
+ const totalOrphans = results.reduce(
119
+ (n, r) => n + r.orphaned.length + (r.orphanedVars?.length ?? 0), 0
120
+ );
121
+ if (totalOrphans > 0) {
122
+ process.stdout.write(`๐Ÿ” Fetching git history for ${totalOrphans} orphan${totalOrphans !== 1 ? 's' : ''}...`);
123
+ for (const result of results) {
124
+ if (result.orphaned.length > 0) {
125
+ result.trails = await getGhostTrails(result.orphaned, process.cwd());
126
+ }
127
+ if ((result.orphanedVars?.length ?? 0) > 0) {
128
+ result.varTrails = await getGhostTrails(result.orphanedVars, process.cwd(), { isVar: true });
129
+ }
130
+ }
131
+ process.stdout.write(' done\n\n');
132
+ }
133
+ }
134
+ }
135
+
82
136
  // Get AI summary before writing report (so it can be included in the .md)
83
137
  const aiSummary = useAi ? await getAiSummary(results) : null;
84
138
 
85
- generateReport(results, outputPath, aiSummary);
139
+ const { totalAllOrphans } = generateReport(results, outputPath, aiSummary);
140
+
141
+ if (doDelete) {
142
+ await deleteOrphans(results, { yes: skipConfirm });
143
+ }
144
+
145
+ // --strict: exit 1 if any orphans were found (for CI / pre-commit hooks)
146
+ if (strict && totalAllOrphans > 0) {
147
+ process.exit(1);
148
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rolandjlevy/css-ghost",
3
- "version": "1.0.4",
4
- "description": "Find orphaned CSS class selectors in your codebase",
3
+ "version": "1.1.1",
4
+ "description": "Find and remove unused CSS selectors and orphaned CSS custom properties โ€” a CLI linter and auditor for React, Vue, Svelte, SCSS, and CSS Modules",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "css-ghost": "bin/css-ghost.js"
@@ -9,24 +9,34 @@
9
9
  "scripts": {
10
10
  "start": "node bin/css-ghost.js",
11
11
  "scan": "node bin/css-ghost.js ./test",
12
+ "scan:trail": "node bin/css-ghost.js ./test --ghost-trail",
13
+ "delete": "node bin/css-ghost.js ./test --delete",
14
+ "delete:yes": "node bin/css-ghost.js ./test --delete --yes",
12
15
  "setup": "node bin/css-ghost.js --setup"
13
16
  },
14
17
  "files": [
15
18
  "bin",
16
19
  "src",
17
- "README.md"
20
+ "README.md",
21
+ "LICENSE"
18
22
  ],
19
23
  "keywords": [
20
24
  "css",
21
- "orphan",
22
25
  "unused",
26
+ "orphan",
23
27
  "selectors",
28
+ "dead-code",
29
+ "cleanup",
24
30
  "cli",
25
31
  "linter",
32
+ "audit",
33
+ "tree-shaking",
34
+ "custom-properties",
26
35
  "react",
36
+ "vue",
37
+ "svelte",
27
38
  "scss",
28
- "stylesheet",
29
- "audit"
39
+ "css-modules"
30
40
  ],
31
41
  "author": "Roland Levy",
32
42
  "license": "MIT",
@@ -37,5 +47,8 @@
37
47
  "homepage": "https://github.com/rolandjlevy/css-ghost#readme",
38
48
  "engines": {
39
49
  "node": ">=18"
50
+ },
51
+ "dependencies": {
52
+ "picocolors": "^1.1.1"
40
53
  }
41
54
  }