dittory 0.0.1 → 0.0.3
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/README.md +232 -38
- package/dist/analyzeProps-B0TjCZhP.mjs +1103 -0
- package/dist/analyzeProps-B0TjCZhP.mjs.map +1 -0
- package/dist/cli.mjs +255 -282
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +101 -3
- package/dist/index.mjs +2 -2
- package/package.json +11 -12
- package/dist/analyzeProps-YWnY-Mf7.mjs +0 -532
- package/dist/analyzeProps-YWnY-Mf7.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,73 +1,267 @@
|
|
|
1
1
|
# dittory
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/dittory)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
A static analysis CLI that detects **parameters always receiving the same value** in React components and functions.
|
|
6
7
|
|
|
7
|
-
-
|
|
8
|
-
- コードベースのリファクタリング支援
|
|
9
|
-
- APIの簡素化
|
|
8
|
+
> **dittory** = "ditto" (same) + "-ory" — finds repetitive patterns in your code
|
|
10
9
|
|
|
11
|
-
##
|
|
10
|
+
## Why?
|
|
11
|
+
|
|
12
|
+
When a prop or argument is always passed the same value across your codebase, it's often a sign that:
|
|
13
|
+
|
|
14
|
+
- The parameter could be **removed** and replaced with a default value
|
|
15
|
+
- The API could be **simplified** by eliminating unnecessary options
|
|
16
|
+
- There's **copy-paste code** that should be refactored
|
|
17
|
+
|
|
18
|
+
dittory helps you identify these opportunities automatically.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
12
21
|
|
|
13
22
|
```bash
|
|
14
23
|
npm install -g dittory
|
|
15
24
|
```
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
Or use directly with npx:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx dittory
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
18
33
|
|
|
19
34
|
```bash
|
|
20
|
-
# src
|
|
35
|
+
# Analyze ./src directory (default)
|
|
21
36
|
dittory
|
|
22
37
|
|
|
23
|
-
#
|
|
38
|
+
# Analyze a specific directory
|
|
24
39
|
dittory ./path/to/src
|
|
25
40
|
|
|
26
|
-
#
|
|
41
|
+
# Set minimum usage count (default: 2)
|
|
27
42
|
dittory --min=3
|
|
28
43
|
|
|
29
|
-
#
|
|
30
|
-
dittory --target=components # React
|
|
31
|
-
dittory --target=functions #
|
|
32
|
-
dittory --target=all #
|
|
44
|
+
# Analyze specific targets
|
|
45
|
+
dittory --target=components # React components only
|
|
46
|
+
dittory --target=functions # Functions and class methods only
|
|
47
|
+
dittory --target=all # Both (default)
|
|
48
|
+
|
|
49
|
+
# Output mode
|
|
50
|
+
dittory --output=simple # Show only constants (default)
|
|
51
|
+
dittory --output=verbose # Show all exported functions and details
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Example Output
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
Button src/components/Button.tsx:15
|
|
58
|
+
Constant Arguments:
|
|
59
|
+
- variant = "primary"
|
|
60
|
+
Usages (5):
|
|
61
|
+
- src/pages/Home.tsx:23
|
|
62
|
+
- src/pages/About.tsx:45
|
|
63
|
+
- src/pages/Contact.tsx:12
|
|
64
|
+
- src/features/auth/Login.tsx:67
|
|
65
|
+
- src/features/auth/Register.tsx:89
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
fetchUser src/api/users.ts:42
|
|
69
|
+
Constant Arguments:
|
|
70
|
+
- includeProfile = true
|
|
71
|
+
- cache = false
|
|
72
|
+
Usages (3):
|
|
73
|
+
- src/hooks/useUser.ts:18
|
|
74
|
+
- src/pages/Profile.tsx:31
|
|
75
|
+
- src/components/UserCard.tsx:55
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
Found 2 function(s) with constant arguments out of 24 function(s).
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## What It Detects
|
|
83
|
+
|
|
84
|
+
| Target | Description |
|
|
85
|
+
|--------|-------------|
|
|
86
|
+
| **React Components** | Props passed to JSX elements (`<Button variant="primary" />`) |
|
|
87
|
+
| **Functions** | Arguments passed to exported function calls |
|
|
88
|
+
| **Class Methods** | Arguments passed to methods of exported classes |
|
|
89
|
+
|
|
90
|
+
## Supported Detection Patterns
|
|
91
|
+
|
|
92
|
+
### Value Types
|
|
93
|
+
|
|
94
|
+
| Pattern | Example | Supported |
|
|
95
|
+
|---------|---------|-----------|
|
|
96
|
+
| String literals | `"hello"` | ✅ |
|
|
97
|
+
| Number literals | `42` | ✅ |
|
|
98
|
+
| Boolean literals | `true`, `false` | ✅ |
|
|
99
|
+
| Enum values | `Status.Active` | ✅ |
|
|
100
|
+
| Imported constants | `import { VALUE } from "./constants"` | ✅ |
|
|
101
|
+
| Variable references | `const x = 3; fn(x)` | ✅ |
|
|
102
|
+
| Variable chains | `const a = 1; const b = a; fn(b)` → resolves to `1` | ✅ |
|
|
103
|
+
| Object literals | `{ nested: { value: 1 } }` | ✅ |
|
|
104
|
+
| Function references | `onClick={handleClick}` | ✅ (by location) |
|
|
105
|
+
| `undefined` | `fn(undefined)` | ✅ |
|
|
106
|
+
|
|
107
|
+
### Parameter Propagation (Call Graph Analysis)
|
|
108
|
+
|
|
109
|
+
dittory can track values through component/function chains:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
// App passes "42" to Parent, Parent passes props.number to Child
|
|
113
|
+
// → Child.number is detected as constant "42"
|
|
114
|
+
|
|
115
|
+
const Child = ({ number }) => <div>{number}</div>;
|
|
116
|
+
const Parent = ({ number }) => <Child number={number} />;
|
|
117
|
+
export const App = () => <Parent number="42" />;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
| Pattern | Example | Supported |
|
|
121
|
+
|---------|---------|-----------|
|
|
122
|
+
| Direct props access | `props.value` | ✅ |
|
|
123
|
+
| Destructured props | `({ value }) => ...` | ✅ |
|
|
124
|
+
| Nested access | `props.user.name` | ✅ |
|
|
125
|
+
| Multi-level chains | `A → B → C` propagation | ✅ |
|
|
126
|
+
| Circular reference protection | Prevents infinite loops | ✅ |
|
|
127
|
+
|
|
128
|
+
### Scope
|
|
129
|
+
|
|
130
|
+
| Pattern | Supported |
|
|
131
|
+
|---------|-----------|
|
|
132
|
+
| Exported functions/components | ✅ |
|
|
133
|
+
| Non-exported (internal) functions | ❌ |
|
|
134
|
+
|
|
135
|
+
## Unsupported Detection Patterns
|
|
33
136
|
|
|
34
|
-
|
|
35
|
-
|
|
137
|
+
### Spread Syntax
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
// ❌ Props passed via spread are NOT tracked
|
|
141
|
+
const Parent = ({ num, ...others }) => <Child {...others} />;
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Dynamic Values
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
// ❌ Values computed at runtime
|
|
148
|
+
<Button count={items.length} />
|
|
149
|
+
<Input value={getValue()} />
|
|
150
|
+
<List items={data.filter(x => x.active)} />
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Conditional Branches
|
|
154
|
+
|
|
155
|
+
When different code paths pass different values, dittory correctly identifies them as different values (not constant):
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
// Different values in branches → correctly NOT reported as constant
|
|
159
|
+
const App = () => {
|
|
160
|
+
if (condition) {
|
|
161
|
+
return <Button variant="primary" />;
|
|
162
|
+
}
|
|
163
|
+
return <Button variant="secondary" />;
|
|
164
|
+
};
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Note: This is expected behavior. dittory performs static analysis and considers all code paths.
|
|
168
|
+
|
|
169
|
+
### Template Literals
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
// ❌ Template strings with expressions
|
|
173
|
+
<Label text={`Hello, ${name}`} />
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Array/Object Spread in Arguments
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
// ❌ Spread in function arguments
|
|
180
|
+
fn(...args);
|
|
181
|
+
fn({ ...defaults, custom: value });
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Higher-Order Components / Render Props
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
// ❌ HOC patterns are not analyzed
|
|
188
|
+
const Enhanced = withAuth(Component);
|
|
189
|
+
<Enhanced role="admin" />
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## CLI Options
|
|
193
|
+
|
|
194
|
+
| Option | Description | Default |
|
|
195
|
+
|--------|-------------|---------|
|
|
196
|
+
| `--min=<n>` | Minimum number of usages to consider | `2` |
|
|
197
|
+
| `--target=<mode>` | What to analyze: `all`, `components`, `functions` | `all` |
|
|
198
|
+
| `--output=<mode>` | Output verbosity: `simple`, `verbose` | `simple` |
|
|
199
|
+
| `--tsconfig=<path>` | Path to tsconfig.json | `./tsconfig.json` |
|
|
200
|
+
| `--help` | Show help message | — |
|
|
201
|
+
|
|
202
|
+
## Configuration File
|
|
203
|
+
|
|
204
|
+
Create a configuration file to set default options. dittory looks for:
|
|
205
|
+
|
|
206
|
+
1. `dittory.config.js` or `dittory.config.mjs`
|
|
207
|
+
2. `dittory.config.json`
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
// dittory.config.js
|
|
211
|
+
/** @type {import('dittory').DittoryConfig} */
|
|
212
|
+
export default {
|
|
213
|
+
minUsages: 3,
|
|
214
|
+
target: "components",
|
|
215
|
+
output: "verbose",
|
|
216
|
+
tsconfig: "./tsconfig.app.json",
|
|
217
|
+
targetDir: "./src",
|
|
218
|
+
};
|
|
36
219
|
```
|
|
37
220
|
|
|
38
|
-
|
|
221
|
+
**Priority:** CLI options > Config file > Default values
|
|
222
|
+
|
|
223
|
+
## Disabling Detection
|
|
224
|
+
|
|
225
|
+
Exclude specific usages from detection using comments:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
// Exclude the next line
|
|
229
|
+
// dittory-disable-next-line
|
|
230
|
+
fetchData(id, { cache: false });
|
|
39
231
|
|
|
232
|
+
// Exclude the same line
|
|
233
|
+
fetchData(id, { cache: false }); // dittory-disable-line
|
|
40
234
|
```
|
|
41
|
-
解析対象ディレクトリ: ./src
|
|
42
|
-
最小使用箇所数: 2
|
|
43
|
-
解析対象: all
|
|
44
235
|
|
|
45
|
-
|
|
46
|
-
→ 15個のコンポーネントを検出
|
|
236
|
+
Works alongside other directives like `eslint-disable-line` or `@ts-ignore`.
|
|
47
237
|
|
|
48
|
-
|
|
238
|
+
## Use Cases
|
|
49
239
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
240
|
+
### Simplify Component APIs
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
// Before: variant is always "primary" across 20 usages
|
|
244
|
+
<Button variant="primary" onClick={handleClick}>Submit</Button>
|
|
245
|
+
|
|
246
|
+
// After: make "primary" the default
|
|
247
|
+
<Button onClick={handleClick}>Submit</Button>
|
|
58
248
|
```
|
|
59
249
|
|
|
60
|
-
|
|
250
|
+
### Remove Unused Flexibility
|
|
61
251
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
252
|
+
```ts
|
|
253
|
+
// Before: cache is always false in all 15 call sites
|
|
254
|
+
const data = await fetchData(id, { cache: false });
|
|
255
|
+
|
|
256
|
+
// After: remove the option or change the default
|
|
257
|
+
const data = await fetchData(id);
|
|
258
|
+
```
|
|
65
259
|
|
|
66
|
-
##
|
|
260
|
+
## Requirements
|
|
67
261
|
|
|
68
262
|
- Node.js >= 18
|
|
69
|
-
-
|
|
263
|
+
- Project must have a `tsconfig.json`
|
|
70
264
|
|
|
71
|
-
##
|
|
265
|
+
## License
|
|
72
266
|
|
|
73
267
|
MIT
|