@dmitryrechkin/eslint-standard 1.5.7 → 1.5.9
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 +91 -10
- package/eslint.config.mjs +91 -0
- package/package.json +5 -3
- package/src/cli/check-deps.mjs +11 -21
- package/src/cli/format.mjs +22 -0
- package/src/cli/index.mjs +10 -0
- package/src/cli/install-deps.mjs +54 -36
- package/src/cli/lint.mjs +21 -0
- package/src/cli/postinstall-check.mjs +4 -4
- package/src/cli/postinstall.mjs +3 -3
- package/src/plugins/standard-conventions.mjs +1156 -0
- package/src/cli/setup-aggressive-cleanup.mjs +0 -97
package/README.md
CHANGED
|
@@ -62,6 +62,66 @@ export default config();
|
|
|
62
62
|
- ✅ No console/debugging code
|
|
63
63
|
- ✅ Enhanced type safety rules
|
|
64
64
|
|
|
65
|
+
### Strict Mode
|
|
66
|
+
Enterprise-grade architectural conventions for large-scale TypeScript monorepos. Enforces consistent project structure, naming conventions, and design patterns.
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
import config from '@dmitryrechkin/eslint-standard';
|
|
70
|
+
|
|
71
|
+
export default config({
|
|
72
|
+
strict: true,
|
|
73
|
+
tsconfigPath: './tsconfig.json'
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Features:**
|
|
78
|
+
- ✅ Enforced folder structure (services/, repositories/, helpers/, etc.)
|
|
79
|
+
- ✅ Single Responsibility Principle for services and transformers
|
|
80
|
+
- ✅ CQRS pattern enforcement for repositories
|
|
81
|
+
- ✅ Static-only helpers, no static methods in other classes
|
|
82
|
+
- ✅ Consistent interface naming conventions
|
|
83
|
+
- ✅ One class per file
|
|
84
|
+
|
|
85
|
+
#### Strict Mode Rules
|
|
86
|
+
|
|
87
|
+
| Rule | Enforcement | Rationale |
|
|
88
|
+
|------|-------------|-----------|
|
|
89
|
+
| **Class Location** | `*Service` → `services/`, `*Repository` → `repositories/`, `*Helper` → `helpers/`, `*Factory` → `factories/`, `*Transformer` → `transformers/`, `*Registry` → `registries/`, `*Adapter` → `adapters/` | Predictable project structure enables faster navigation and onboarding |
|
|
90
|
+
| **Service Single Method** | Services must have only one public method | Single Responsibility Principle - each service does one thing well |
|
|
91
|
+
| **Transformer Single Method** | Transformers must have only one public method | Clear data transformation contracts |
|
|
92
|
+
| **Helper Static Only** | Helper classes must contain only static methods | Helpers are utility collections, not stateful objects |
|
|
93
|
+
| **No Static in Non-Helpers** | Non-helper/factory classes cannot have static methods | Prevents hidden global state, improves testability via dependency injection |
|
|
94
|
+
| **Type Location** | `interface TypeXXX` must be in `types/` folder | Centralized type definitions for reusability |
|
|
95
|
+
| **Interface Naming** | `TypeXXX` for data types, `XXXInterface` for class contracts | Clear distinction between data structures and behavioral contracts |
|
|
96
|
+
| **One Class Per File** | Each file can contain only one class | Simplifies imports, improves code organization |
|
|
97
|
+
| **Repository CQRS** | `CommandRepository` cannot have query methods (`get`, `find`, `list`), `QueryRepository` cannot have command methods (`create`, `update`, `delete`) | Command Query Responsibility Segregation for scalable data access |
|
|
98
|
+
| **Folder CamelCase** | All folder names must be camelCase | Consistent naming across the codebase |
|
|
99
|
+
| **Function Name Match Filename** | Top-level function name must match filename | Predictable imports and file discovery |
|
|
100
|
+
| **No Utils Folder** | `utils/` folder is forbidden | Use `helpers/` for stateless logic or domain objects for stateful logic |
|
|
101
|
+
|
|
102
|
+
#### Example Project Structure
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
src/
|
|
106
|
+
├── services/
|
|
107
|
+
│ ├── UserService.ts # class UserService { execute() }
|
|
108
|
+
│ └── PaymentService.ts # class PaymentService { process() }
|
|
109
|
+
├── repositories/
|
|
110
|
+
│ ├── UserCommandRepository.ts # create(), update(), delete()
|
|
111
|
+
│ └── UserQueryRepository.ts # find(), getById(), list()
|
|
112
|
+
├── helpers/
|
|
113
|
+
│ └── ValidationHelper.ts # static validate(), static sanitize()
|
|
114
|
+
├── factories/
|
|
115
|
+
│ └── UserFactory.ts # static create()
|
|
116
|
+
├── transformers/
|
|
117
|
+
│ └── UserTransformer.ts # transform()
|
|
118
|
+
├── types/
|
|
119
|
+
│ ├── TypeUser.ts # interface TypeUser { id: string }
|
|
120
|
+
│ └── TypePayment.ts # interface TypePayment { amount: number }
|
|
121
|
+
└── adapters/
|
|
122
|
+
└── StripeAdapter.ts # class StripeAdapter
|
|
123
|
+
```
|
|
124
|
+
|
|
65
125
|
## ⚙️ Configuration Options
|
|
66
126
|
|
|
67
127
|
All configurations accept the same configuration options:
|
|
@@ -124,16 +184,22 @@ npx @dmitryrechkin/eslint-standard check-deps --install
|
|
|
124
184
|
|
|
125
185
|
## 📊 Preset Comparison
|
|
126
186
|
|
|
127
|
-
| Feature | Standard | Aggressive | Library |
|
|
128
|
-
|
|
129
|
-
| Unused imports cleanup | ✅ | ✅ | ✅ |
|
|
130
|
-
| Unused variables detection | Basic | Enhanced | Enhanced |
|
|
131
|
-
| Dead code detection | Basic | ✅ | ✅ |
|
|
132
|
-
| Unused exports check | ❌ | ✅ | Very Strict |
|
|
133
|
-
| JSDoc requirements | Basic | Basic | Strict |
|
|
134
|
-
| Console statements | Warning | Warning | Error |
|
|
135
|
-
| Return type hints | Error | Error | Explicit |
|
|
136
|
-
| Type safety | High | High | Very High |
|
|
187
|
+
| Feature | Standard | Aggressive | Library | Strict Mode |
|
|
188
|
+
|---------|----------|------------|---------|-------------|
|
|
189
|
+
| Unused imports cleanup | ✅ | ✅ | ✅ | ✅ |
|
|
190
|
+
| Unused variables detection | Basic | Enhanced | Enhanced | Basic |
|
|
191
|
+
| Dead code detection | Basic | ✅ | ✅ | Basic |
|
|
192
|
+
| Unused exports check | ❌ | ✅ | Very Strict | ❌ |
|
|
193
|
+
| JSDoc requirements | Basic | Basic | Strict | Basic |
|
|
194
|
+
| Console statements | Warning | Warning | Error | Warning |
|
|
195
|
+
| Return type hints | Error | Error | Explicit | Error |
|
|
196
|
+
| Type safety | High | High | Very High | High |
|
|
197
|
+
| Folder structure enforcement | ❌ | ❌ | ❌ | ✅ |
|
|
198
|
+
| Single Responsibility (Services) | ❌ | ❌ | ❌ | ✅ |
|
|
199
|
+
| CQRS Repository pattern | ❌ | ❌ | ❌ | ✅ |
|
|
200
|
+
| Interface naming conventions | ❌ | ❌ | ❌ | ✅ |
|
|
201
|
+
| One class per file | ❌ | ❌ | ❌ | ✅ |
|
|
202
|
+
| Static method restrictions | ❌ | ❌ | ❌ | ✅ |
|
|
137
203
|
|
|
138
204
|
## 📈 Industry Standards Comparison
|
|
139
205
|
|
|
@@ -198,6 +264,9 @@ Use **Library** preset - ensures clean public APIs and comprehensive documentati
|
|
|
198
264
|
### For Maximum Code Quality
|
|
199
265
|
Use **Aggressive** preset - comprehensive unused code detection.
|
|
200
266
|
|
|
267
|
+
### For Enterprise Monorepos
|
|
268
|
+
Use **Strict Mode** - enforces architectural conventions, folder structure, and design patterns for large teams.
|
|
269
|
+
|
|
201
270
|
## 🔍 Example Projects
|
|
202
271
|
|
|
203
272
|
### React Application
|
|
@@ -235,6 +304,18 @@ export default config({
|
|
|
235
304
|
});
|
|
236
305
|
```
|
|
237
306
|
|
|
307
|
+
### Enterprise Monorepo
|
|
308
|
+
```javascript
|
|
309
|
+
// eslint.config.mjs
|
|
310
|
+
import config from '@dmitryrechkin/eslint-standard';
|
|
311
|
+
|
|
312
|
+
export default config({
|
|
313
|
+
strict: true,
|
|
314
|
+
tsconfigPath: './tsconfig.json',
|
|
315
|
+
ignores: ['dist/**', 'node_modules/**']
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
238
319
|
## 🤝 Contributing
|
|
239
320
|
|
|
240
321
|
Issues and pull requests are welcome on [GitHub](https://github.com/dmitryrechkin/eslint-standard).
|
package/eslint.config.mjs
CHANGED
|
@@ -27,16 +27,19 @@ import unicornPlugin from 'eslint-plugin-unicorn';
|
|
|
27
27
|
import noSecretsPlugin from 'eslint-plugin-no-secrets';
|
|
28
28
|
import regexpPlugin from 'eslint-plugin-regexp';
|
|
29
29
|
import functionalPlugin from 'eslint-plugin-functional';
|
|
30
|
+
import standardConventionsPlugin from './src/plugins/standard-conventions.mjs';
|
|
30
31
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
31
32
|
import prettierConfig from 'eslint-config-prettier';
|
|
32
33
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
33
34
|
import prettierPlugin from 'eslint-plugin-prettier';
|
|
35
|
+
|
|
34
36
|
export default function ({
|
|
35
37
|
tsconfigPath = './tsconfig.json',
|
|
36
38
|
ignores = [],
|
|
37
39
|
files = [],
|
|
38
40
|
plugins = {},
|
|
39
41
|
rules = {},
|
|
42
|
+
strict = false,
|
|
40
43
|
prettierPlugin: externalPrettierPlugin = undefined
|
|
41
44
|
} = {}) {
|
|
42
45
|
// Use external prettier plugin if provided, otherwise fallback to bundled one
|
|
@@ -77,6 +80,7 @@ export default function ({
|
|
|
77
80
|
'no-secrets': noSecretsPlugin,
|
|
78
81
|
'regexp': regexpPlugin,
|
|
79
82
|
'functional': functionalPlugin,
|
|
83
|
+
'standard-conventions': standardConventionsPlugin,
|
|
80
84
|
'prettier': activePrettierPlugin,
|
|
81
85
|
...plugins,
|
|
82
86
|
},
|
|
@@ -99,6 +103,7 @@ export default function ({
|
|
|
99
103
|
|
|
100
104
|
// Original @dmitryrechkin/eslint-standard rules
|
|
101
105
|
'@typescript-eslint/explicit-function-return-type': 'error',
|
|
106
|
+
'@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
|
|
102
107
|
'@typescript-eslint/no-explicit-any': 'error', // Ban 'any' type for type safety
|
|
103
108
|
|
|
104
109
|
// Original coding guidelines - formatting rules disabled in favor of prettier
|
|
@@ -843,6 +848,92 @@ export default function ({
|
|
|
843
848
|
'unicorn/prefer-query-selector': 'off', // Allow different DOM query methods
|
|
844
849
|
'unicorn/prevent-abbreviations': 'off', // Allow abbreviations for domain-specific terms
|
|
845
850
|
|
|
851
|
+
...(strict ? {
|
|
852
|
+
// Standard conventions for services, transformers, and function naming
|
|
853
|
+
'standard-conventions/service-single-public-method': 'error',
|
|
854
|
+
'standard-conventions/transformer-single-public-method': 'error',
|
|
855
|
+
'standard-conventions/function-name-match-filename': 'error',
|
|
856
|
+
'standard-conventions/folder-camel-case': 'error',
|
|
857
|
+
|
|
858
|
+
// Helper static-only rule: Helpers MUST have only static methods
|
|
859
|
+
'standard-conventions/helper-static-only': 'error',
|
|
860
|
+
// Non-helper classes should NOT have static methods (except Factories)
|
|
861
|
+
'standard-conventions/no-static-in-non-helpers': 'error',
|
|
862
|
+
|
|
863
|
+
// Class location rules: enforce proper folder structure
|
|
864
|
+
'standard-conventions/class-location': ['error', {
|
|
865
|
+
mappings: {
|
|
866
|
+
Service: 'services',
|
|
867
|
+
Repository: 'repositories',
|
|
868
|
+
Helper: 'helpers',
|
|
869
|
+
Factory: 'factories',
|
|
870
|
+
Transformer: 'transformers',
|
|
871
|
+
Registry: 'registries',
|
|
872
|
+
Adapter: 'adapters'
|
|
873
|
+
}
|
|
874
|
+
}],
|
|
875
|
+
|
|
876
|
+
// Type location rule: Types must be in types folder
|
|
877
|
+
'standard-conventions/type-location': 'error',
|
|
878
|
+
|
|
879
|
+
// One class per file rule
|
|
880
|
+
'standard-conventions/one-class-per-file': 'error',
|
|
881
|
+
|
|
882
|
+
// Repository CQRS enforcement
|
|
883
|
+
'standard-conventions/repository-cqrs': 'error',
|
|
884
|
+
|
|
885
|
+
// Repository 'ById' naming enforcement
|
|
886
|
+
'standard-conventions/repository-by-id': 'error',
|
|
887
|
+
|
|
888
|
+
// No 'utils' folder enforcement
|
|
889
|
+
'standard-conventions/no-utils-folder': 'error',
|
|
890
|
+
|
|
891
|
+
// Enforce separation of schemas, types, and constants from class files
|
|
892
|
+
'standard-conventions/no-schemas-in-class-files': 'error',
|
|
893
|
+
'standard-conventions/no-types-in-class-files': 'error',
|
|
894
|
+
'standard-conventions/no-constants-in-class-files': 'error',
|
|
895
|
+
|
|
896
|
+
'unicorn/filename-case': ['error', {
|
|
897
|
+
cases: {
|
|
898
|
+
camelCase: true,
|
|
899
|
+
pascalCase: true
|
|
900
|
+
}
|
|
901
|
+
}],
|
|
902
|
+
// Enforce Type prefix OR Interface suffix strictly
|
|
903
|
+
// TypeXXX for data types, XXXInterface for class contracts
|
|
904
|
+
'@typescript-eslint/naming-convention': [
|
|
905
|
+
'error',
|
|
906
|
+
{
|
|
907
|
+
selector: 'interface',
|
|
908
|
+
format: ['PascalCase'],
|
|
909
|
+
custom: {
|
|
910
|
+
regex: '(^Type[A-Z]|Interface$)',
|
|
911
|
+
match: true
|
|
912
|
+
}
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
selector: 'typeAlias',
|
|
916
|
+
format: ['PascalCase'],
|
|
917
|
+
custom: {
|
|
918
|
+
regex: '^Type[A-Z]',
|
|
919
|
+
match: true
|
|
920
|
+
}
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
selector: 'class',
|
|
924
|
+
format: ['PascalCase']
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
selector: 'enum',
|
|
928
|
+
format: ['PascalCase'],
|
|
929
|
+
custom: {
|
|
930
|
+
regex: '^Enum[A-Z]',
|
|
931
|
+
match: true
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
]
|
|
935
|
+
} : {}),
|
|
936
|
+
|
|
846
937
|
// Allow custom rules to be added
|
|
847
938
|
...rules
|
|
848
939
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dmitryrechkin/eslint-standard",
|
|
3
3
|
"description": "This package provides a shared ESLint configuration which includes TypeScript support and a set of specific linting rules designed to ensure high-quality and consistent code style across projects.",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.9",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "eslint.config.mjs",
|
|
6
7
|
"exports": {
|
|
7
8
|
".": "./eslint.config.mjs"
|
|
@@ -31,7 +32,8 @@
|
|
|
31
32
|
"test:spacing": "node tests/test-spacing-rules.mjs",
|
|
32
33
|
"test:switch-case": "node tests/test-switch-case-simple.mjs",
|
|
33
34
|
"test:cli": "node tests/test-cli.mjs",
|
|
34
|
-
"test:install": "node tests/test-install-simulation.mjs"
|
|
35
|
+
"test:install": "node tests/test-install-simulation.mjs",
|
|
36
|
+
"test:strict": "node tests/test-strict-rules.mjs"
|
|
35
37
|
},
|
|
36
38
|
"keywords": [],
|
|
37
39
|
"author": "",
|
|
@@ -65,4 +67,4 @@
|
|
|
65
67
|
"prettier": "^3.0.0",
|
|
66
68
|
"typescript": "^5.0.0"
|
|
67
69
|
}
|
|
68
|
-
}
|
|
70
|
+
}
|
package/src/cli/check-deps.mjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readFileSync } from 'fs';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
import { dirname, join
|
|
6
|
-
import { spawn } from 'child_process';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
7
6
|
|
|
8
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
8
|
const __dirname = dirname(__filename);
|
|
@@ -17,18 +16,8 @@ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
|
17
16
|
const peerDeps = packageJson.peerDependencies || {};
|
|
18
17
|
|
|
19
18
|
// Check if running from node_modules and find project root
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (isInNodeModules) {
|
|
24
|
-
// Handle different package manager structures
|
|
25
|
-
// pnpm: .pnpm/@org+pkg@version_deps/node_modules/@org/pkg
|
|
26
|
-
// npm/yarn: node_modules/@org/pkg
|
|
27
|
-
const parts = __dirname.split('node_modules');
|
|
28
|
-
projectRoot = parts[0].replace(/[\\/]$/, ''); // Remove trailing slash
|
|
29
|
-
} else {
|
|
30
|
-
projectRoot = process.cwd();
|
|
31
|
-
}
|
|
19
|
+
// We primarily use process.cwd() as this is a CLI tool meant to run in the target project
|
|
20
|
+
const projectRoot = process.cwd();
|
|
32
21
|
|
|
33
22
|
// Read project's package.json
|
|
34
23
|
let projectPackageJson;
|
|
@@ -36,6 +25,7 @@ try {
|
|
|
36
25
|
projectPackageJson = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8'));
|
|
37
26
|
} catch (error) {
|
|
38
27
|
console.error('❌ Could not read project package.json');
|
|
28
|
+
console.error(`Checked in: ${projectRoot}`);
|
|
39
29
|
process.exit(1);
|
|
40
30
|
}
|
|
41
31
|
|
|
@@ -69,13 +59,13 @@ if (missingDeps.length === 0 && outdatedDeps.length === 0) {
|
|
|
69
59
|
if (missingDeps.length > 0) {
|
|
70
60
|
console.log(`\n❌ Missing ${missingDeps.length} dependencies:`);
|
|
71
61
|
missingDeps.forEach(dep => console.log(` - ${dep}`));
|
|
72
|
-
|
|
62
|
+
|
|
73
63
|
if (shouldInstall) {
|
|
74
64
|
console.log('\n🔧 Auto-installing missing dependencies...\n');
|
|
75
|
-
|
|
65
|
+
|
|
76
66
|
// Import and run the install-deps script
|
|
77
67
|
try {
|
|
78
|
-
|
|
68
|
+
await import('./install-deps.mjs');
|
|
79
69
|
// The install-deps script will handle the installation
|
|
80
70
|
} catch (error) {
|
|
81
71
|
console.error('❌ Failed to auto-install dependencies:', error.message);
|
|
@@ -91,7 +81,7 @@ if (missingDeps.length === 0 && outdatedDeps.length === 0) {
|
|
|
91
81
|
console.log(`\n⚠️ ${outdatedDeps.length} dependencies may be outdated:`);
|
|
92
82
|
outdatedDeps.forEach(dep => console.log(` - ${dep}`));
|
|
93
83
|
}
|
|
94
|
-
|
|
84
|
+
|
|
95
85
|
if (!shouldInstall && missingDeps.length > 0) {
|
|
96
86
|
process.exit(1);
|
|
97
87
|
}
|
|
@@ -99,4 +89,4 @@ if (missingDeps.length === 0 && outdatedDeps.length === 0) {
|
|
|
99
89
|
|
|
100
90
|
export default function checkDeps() {
|
|
101
91
|
// Export for programmatic use
|
|
102
|
-
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
|
|
4
|
+
const args = process.argv.slice(3); // First 3 are node, script, command
|
|
5
|
+
|
|
6
|
+
console.log('Running formatting...');
|
|
7
|
+
|
|
8
|
+
// Determine eslint command (npx eslint or just eslint if in path)
|
|
9
|
+
// We inject --fix to enable auto-fixing/formatting
|
|
10
|
+
const eslint = spawn('npx', ['eslint', '--fix', ...args], {
|
|
11
|
+
stdio: 'inherit',
|
|
12
|
+
shell: true
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
eslint.on('close', (code) => {
|
|
16
|
+
if (code !== 0) {
|
|
17
|
+
console.log('\n\x1b[33m%s\x1b[0m', 'formatting completed with issues. please address remaining errors and warnings manually, make sure everything builds and all tests pass after modifying the code');
|
|
18
|
+
process.exit(code);
|
|
19
|
+
} else {
|
|
20
|
+
console.log('\n✅ Formatting passed');
|
|
21
|
+
}
|
|
22
|
+
});
|
package/src/cli/index.mjs
CHANGED
|
@@ -9,6 +9,12 @@ switch (command) {
|
|
|
9
9
|
case 'check-deps':
|
|
10
10
|
await import('./check-deps.mjs');
|
|
11
11
|
break;
|
|
12
|
+
case 'lint':
|
|
13
|
+
await import('./lint.mjs');
|
|
14
|
+
break;
|
|
15
|
+
case 'format':
|
|
16
|
+
await import('./format.mjs');
|
|
17
|
+
break;
|
|
12
18
|
case 'help':
|
|
13
19
|
case '--help':
|
|
14
20
|
case '-h':
|
|
@@ -23,12 +29,16 @@ Commands:
|
|
|
23
29
|
install-deps Install all peer dependencies
|
|
24
30
|
check-deps Check if all peer dependencies are installed
|
|
25
31
|
check-deps --install Auto-install missing dependencies if any
|
|
32
|
+
lint Run eslint with custom error message
|
|
33
|
+
format Run eslint --fix with custom error message
|
|
26
34
|
help Show this help message
|
|
27
35
|
|
|
28
36
|
Examples:
|
|
29
37
|
npx @dmitryrechkin/eslint-standard install-deps
|
|
30
38
|
npx @dmitryrechkin/eslint-standard check-deps
|
|
31
39
|
npx @dmitryrechkin/eslint-standard check-deps --install
|
|
40
|
+
npx @dmitryrechkin/eslint-standard lint .
|
|
41
|
+
npx @dmitryrechkin/eslint-standard format .
|
|
32
42
|
`);
|
|
33
43
|
break;
|
|
34
44
|
default:
|
package/src/cli/install-deps.mjs
CHANGED
|
@@ -1,49 +1,67 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
7
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = dirname(__filename);
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
// Get peer dependencies from our package.json
|
|
12
|
+
const packageJsonPath = join(__dirname, '../../package.json');
|
|
13
|
+
let peerDeps = {};
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
17
|
+
peerDeps = packageJson.peerDependencies || {};
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error('❌ Could not read package.json:', error.message);
|
|
20
|
+
process.exit(1);
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
const depsToInstall = Object.entries(peerDeps).map(([dep, version]) => `${dep}@${version}`);
|
|
24
|
+
|
|
25
|
+
if (depsToInstall.length === 0) {
|
|
26
|
+
console.log('✅ No peer dependencies to install.');
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
24
29
|
|
|
25
|
-
//
|
|
26
|
-
const
|
|
27
|
-
|
|
30
|
+
// Check for lockfiles to determine package manager
|
|
31
|
+
const projectRoot = process.cwd();
|
|
32
|
+
let packageManager = 'npm';
|
|
33
|
+
let installArgs = ['install', '--save-dev'];
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
if (existsSync(join(projectRoot, 'pnpm-lock.yaml'))) {
|
|
36
|
+
packageManager = 'pnpm';
|
|
37
|
+
installArgs = ['add', '-D'];
|
|
38
|
+
} else if (existsSync(join(projectRoot, 'yarn.lock'))) {
|
|
39
|
+
packageManager = 'yarn';
|
|
40
|
+
installArgs = ['add', '-D'];
|
|
41
|
+
} else if (existsSync(join(projectRoot, 'bun.lockb'))) {
|
|
42
|
+
packageManager = 'bun';
|
|
43
|
+
installArgs = ['add', '-D'];
|
|
44
|
+
}
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
console.log(`📦 Installing peer dependencies using ${packageManager}...`);
|
|
47
|
+
console.log(` ${depsToInstall.join(' ')}\n`);
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
// Use shell: true on Windows compatibility or if packageManager is a .cmd/.bat file,
|
|
50
|
+
// but generally spawnSync handles this better than execSync.
|
|
51
|
+
// However, 'npm' often needs a shell on Windows or when run via npx.
|
|
52
|
+
// Using shell: true is standard for cross-platform npm execution unless using .cmd explicitly on Windows.
|
|
53
|
+
const result = spawnSync(packageManager, [...installArgs, ...depsToInstall], {
|
|
54
|
+
stdio: 'inherit',
|
|
55
|
+
shell: true
|
|
56
|
+
});
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
if (result.status !== 0) {
|
|
59
|
+
console.error('❌ Installation failed');
|
|
60
|
+
process.exit(result.status || 1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log('\n✅ Peer dependencies installed successfully!');
|
|
64
|
+
|
|
65
|
+
export default function installDeps() {
|
|
66
|
+
// Export for programmatic use
|
|
67
|
+
}
|
package/src/cli/lint.mjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
|
|
4
|
+
const args = process.argv.slice(3); // First 3 are node, script, command
|
|
5
|
+
|
|
6
|
+
console.log('Running linting...');
|
|
7
|
+
|
|
8
|
+
// Determine eslint command (npx eslint or just eslint if in path)
|
|
9
|
+
const eslint = spawn('npx', ['eslint', ...args], {
|
|
10
|
+
stdio: 'inherit',
|
|
11
|
+
shell: true
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
eslint.on('close', (code) => {
|
|
15
|
+
if (code !== 0) {
|
|
16
|
+
console.log('\n\x1b[33m%s\x1b[0m', 'please address all the errors and warnings and re-run linting after, make sure everything builds and all tests pass after modifying the code');
|
|
17
|
+
process.exit(code);
|
|
18
|
+
} else {
|
|
19
|
+
console.log('\n✅ Linting passed');
|
|
20
|
+
}
|
|
21
|
+
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readFileSync, existsSync } from 'fs';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
import { dirname, join } from 'path';
|
|
6
|
-
import { execSync } from 'child_process';
|
|
3
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
7
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = dirname(__filename);
|
package/src/cli/postinstall.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import { dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
5
|
|
|
6
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = dirname(__filename);
|
|
@@ -52,7 +52,7 @@ if (autoInstall) {
|
|
|
52
52
|
|
|
53
53
|
try {
|
|
54
54
|
// Run check-deps with --install flag
|
|
55
|
-
const { execSync } = await import('child_process');
|
|
55
|
+
const { execSync } = await import('node:child_process');
|
|
56
56
|
execSync('node ' + join(__dirname, 'index.mjs') + ' check-deps --install', {
|
|
57
57
|
stdio: 'inherit',
|
|
58
58
|
cwd: process.cwd()
|