@dmitryrechkin/eslint-standard 1.1.1 → 1.1.2

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/eslint.config.mjs CHANGED
@@ -6,8 +6,8 @@ import stylisticPlugin from '@stylistic/eslint-plugin';
6
6
  import jsdocPlugin from 'eslint-plugin-jsdoc';
7
7
  import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort';
8
8
  import perfectionistPlugin from 'eslint-plugin-perfectionist';
9
- import jsdocIndentPlugin from './eslint-plugin-jsdoc-indent.mjs';
10
- import interfaceBracePlugin from './eslint-plugin-interface-brace.mjs';
9
+ import jsdocIndentPlugin from './src/plugins/jsdoc-indent.mjs';
10
+ import interfaceBracePlugin from './src/plugins/interface-brace.mjs';
11
11
 
12
12
  export default function ({
13
13
  tsconfigPath = './tsconfig.json',
package/package.json CHANGED
@@ -1,16 +1,19 @@
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.1.1",
4
+ "version": "1.1.2",
5
5
  "main": "eslint.config.mjs",
6
+ "bin": {
7
+ "eslint-standard": "./src/cli/index.mjs"
8
+ },
6
9
  "files": [
7
10
  "eslint.config.mjs",
8
- "eslint-plugin-jsdoc-indent.mjs",
11
+ "src/",
9
12
  "README.md",
10
13
  "LICENSE"
11
14
  ],
12
15
  "scripts": {
13
- "postinstall": "echo 'Remember to install peer dependencies:\n npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-unused-imports @stylistic/eslint-plugin eslint-plugin-jsdoc --save-dev\n bun add eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-unused-imports @stylistic/eslint-plugin eslint-plugin-jsdoc --dev'",
16
+ "postinstall": "node src/cli/postinstall.mjs",
14
17
  "package:publish": "npm publish --access public",
15
18
  "test": "npm run test:formatting",
16
19
  "test:formatting": "node tests/test-runner.js"
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join, resolve } from 'path';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ // Get peer dependencies
11
+ const packageJsonPath = join(__dirname, '../../package.json');
12
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
13
+ const peerDeps = packageJson.peerDependencies || {};
14
+
15
+ // Check if running from node_modules
16
+ const isInNodeModules = __dirname.includes('node_modules');
17
+ const projectRoot = isInNodeModules
18
+ ? resolve(__dirname, '../../../../')
19
+ : process.cwd();
20
+
21
+ // Read project's package.json
22
+ let projectPackageJson;
23
+ try {
24
+ projectPackageJson = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8'));
25
+ } catch (error) {
26
+ console.error('❌ Could not read project package.json');
27
+ process.exit(1);
28
+ }
29
+
30
+ const allDeps = {
31
+ ...projectPackageJson.dependencies || {},
32
+ ...projectPackageJson.devDependencies || {}
33
+ };
34
+
35
+ console.log('🔍 Checking ESLint Standard peer dependencies...\n');
36
+
37
+ let missingDeps = [];
38
+ let outdatedDeps = [];
39
+
40
+ for (const [dep, requiredVersion] of Object.entries(peerDeps)) {
41
+ if (!allDeps[dep]) {
42
+ missingDeps.push(`${dep}@${requiredVersion}`);
43
+ console.log(`❌ Missing: ${dep} (required: ${requiredVersion})`);
44
+ } else {
45
+ console.log(`✅ Found: ${dep}@${allDeps[dep]}`);
46
+ // Simple version check - could be improved
47
+ if (!allDeps[dep].includes('^') && !allDeps[dep].includes('~') && allDeps[dep] !== requiredVersion) {
48
+ outdatedDeps.push(`${dep} (installed: ${allDeps[dep]}, required: ${requiredVersion})`);
49
+ }
50
+ }
51
+ }
52
+
53
+ console.log('\n📊 Summary:');
54
+ if (missingDeps.length === 0 && outdatedDeps.length === 0) {
55
+ console.log('✅ All peer dependencies are satisfied!');
56
+ } else {
57
+ if (missingDeps.length > 0) {
58
+ console.log(`\n❌ Missing ${missingDeps.length} dependencies:`);
59
+ console.log('Run: npx @dmitryrechkin/eslint-standard install-deps');
60
+ }
61
+ if (outdatedDeps.length > 0) {
62
+ console.log(`\n⚠️ ${outdatedDeps.length} dependencies may be outdated:`);
63
+ outdatedDeps.forEach(dep => console.log(` - ${dep}`));
64
+ }
65
+ process.exit(1);
66
+ }
67
+
68
+ export default function checkDeps() {
69
+ // Export for programmatic use
70
+ }
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+
3
+ const command = process.argv[2];
4
+
5
+ switch (command) {
6
+ case 'install-deps':
7
+ await import('./install-deps.mjs');
8
+ break;
9
+ case 'check-deps':
10
+ await import('./check-deps.mjs');
11
+ break;
12
+ case 'help':
13
+ case '--help':
14
+ case '-h':
15
+ case undefined:
16
+ console.log(`
17
+ @dmitryrechkin/eslint-standard CLI
18
+
19
+ Usage:
20
+ npx @dmitryrechkin/eslint-standard <command>
21
+
22
+ Commands:
23
+ install-deps Install all peer dependencies
24
+ check-deps Check if all peer dependencies are installed
25
+ help Show this help message
26
+
27
+ Examples:
28
+ npx @dmitryrechkin/eslint-standard install-deps
29
+ npx @dmitryrechkin/eslint-standard check-deps
30
+ `);
31
+ break;
32
+ default:
33
+ console.error(`Unknown command: ${command}`);
34
+ console.log('Run "npx @dmitryrechkin/eslint-standard help" for usage information');
35
+ process.exit(1);
36
+ }
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from 'child_process';
4
+ import { readFileSync, existsSync } from 'fs';
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'path';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ // Detect package manager
12
+ function detectPackageManager() {
13
+ if (existsSync('pnpm-lock.yaml')) return 'pnpm';
14
+ if (existsSync('yarn.lock')) return 'yarn';
15
+ if (existsSync('package-lock.json')) return 'npm';
16
+ if (existsSync('bun.lockb')) return 'bun';
17
+ return 'npm'; // default
18
+ }
19
+
20
+ // Get peer dependencies from package.json
21
+ const packageJsonPath = join(__dirname, '../../package.json');
22
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
23
+ const peerDeps = packageJson.peerDependencies || {};
24
+
25
+ // Build install command
26
+ const packageManager = detectPackageManager();
27
+ const deps = Object.entries(peerDeps).map(([name, version]) => `${name}@${version}`).join(' ');
28
+
29
+ const installCommands = {
30
+ npm: `npm install --save-dev ${deps}`,
31
+ pnpm: `pnpm add -D ${deps}`,
32
+ yarn: `yarn add -D ${deps}`,
33
+ bun: `bun add -d ${deps}`
34
+ };
35
+
36
+ const command = installCommands[packageManager];
37
+
38
+ console.log(`🔧 Installing ESLint Standard peer dependencies...`);
39
+ console.log(`📦 Detected package manager: ${packageManager}`);
40
+ console.log(`📋 Running: ${command}\n`);
41
+
42
+ try {
43
+ execSync(command, { stdio: 'inherit' });
44
+ console.log('\n✅ All peer dependencies installed successfully!');
45
+ } catch (error) {
46
+ console.error('\n❌ Failed to install dependencies. Please run manually:');
47
+ console.error(command);
48
+ process.exit(1);
49
+ }
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname } from 'path';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+
9
+ // Don't run during local development
10
+ if (!__dirname.includes('node_modules')) {
11
+ process.exit(0);
12
+ }
13
+
14
+ console.log(`
15
+ ╔════════════════════════════════════════════════════════════════╗
16
+ ║ ║
17
+ ║ 🎉 Thanks for installing @dmitryrechkin/eslint-standard! ║
18
+ ║ ║
19
+ ╚════════════════════════════════════════════════════════════════╝
20
+
21
+ 📦 This package requires peer dependencies to work properly.
22
+
23
+ 🚀 Quick install (auto-detects your package manager):
24
+ ${'\x1b[36m'}npx @dmitryrechkin/eslint-standard install-deps${'\x1b[0m'}
25
+
26
+ 📋 Or install manually:
27
+ ${'\x1b[90m'}npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-unused-imports @stylistic/eslint-plugin eslint-plugin-jsdoc eslint-plugin-simple-import-sort eslint-plugin-perfectionist${'\x1b[0m'}
28
+
29
+ 🔍 To check if all dependencies are installed:
30
+ ${'\x1b[36m'}npx @dmitryrechkin/eslint-standard check-deps${'\x1b[0m'}
31
+
32
+ 📚 Documentation: https://github.com/dmitryrechkin/eslint-standard
33
+ `);
34
+
35
+ // Run check-deps to show current status
36
+ try {
37
+ await import('./check-deps.mjs');
38
+ } catch (error) {
39
+ // Silent fail - don't break installation
40
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Custom ESLint plugin to enforce Allman brace style for TypeScript interfaces
3
+ */
4
+
5
+ const interfaceBraceRule = {
6
+ meta: {
7
+ type: 'layout',
8
+ docs: {
9
+ description: 'Enforce Allman brace style for TypeScript interfaces',
10
+ category: 'Stylistic Issues',
11
+ recommended: false
12
+ },
13
+ fixable: 'whitespace',
14
+ schema: []
15
+ },
16
+
17
+ create(context) {
18
+ const sourceCode = context.sourceCode || context.getSourceCode();
19
+
20
+ return {
21
+ TSInterfaceDeclaration(node) {
22
+ // Get the interface name token
23
+ const interfaceId = node.id;
24
+ const typeParams = node.typeParameters;
25
+ const extendsClause = node.extends;
26
+
27
+ // Find the opening brace
28
+ let tokenBeforeBrace;
29
+ if (node.heritage && node.heritage.length > 0) {
30
+ // Interface extends something
31
+ tokenBeforeBrace = node.heritage[node.heritage.length - 1];
32
+ } else if (extendsClause && extendsClause.length > 0) {
33
+ // Interface extends something (newer AST)
34
+ tokenBeforeBrace = extendsClause[extendsClause.length - 1];
35
+ } else if (typeParams) {
36
+ // Interface has type parameters
37
+ tokenBeforeBrace = typeParams;
38
+ } else {
39
+ // Simple interface
40
+ tokenBeforeBrace = interfaceId;
41
+ }
42
+
43
+ // Get the opening brace token
44
+ const openingBrace = sourceCode.getTokenAfter(tokenBeforeBrace, token => token.type === 'Punctuator' && token.value === '{');
45
+
46
+ if (!openingBrace) return;
47
+
48
+ // Check if there's a newline before the opening brace
49
+ const tokenBefore = sourceCode.getTokenBefore(openingBrace);
50
+ const textBetween = sourceCode.text.slice(tokenBefore.range[1], openingBrace.range[0]);
51
+
52
+ // Check if brace is on the same line
53
+ if (!textBetween.includes('\n')) {
54
+ context.report({
55
+ node: openingBrace,
56
+ message: 'Opening brace should be on a new line (Allman style)',
57
+ fix(fixer) {
58
+ // Get the base indentation of the interface declaration
59
+ const interfaceLine = sourceCode.getLines()[node.loc.start.line - 1];
60
+ const baseIndentMatch = interfaceLine.match(/^(\s*)/);
61
+ const baseIndent = baseIndentMatch ? baseIndentMatch[1] : '';
62
+
63
+ // Replace the space before the brace with a newline and proper indentation
64
+ return fixer.replaceTextRange(
65
+ [tokenBefore.range[1], openingBrace.range[0]],
66
+ '\n' + baseIndent
67
+ );
68
+ }
69
+ });
70
+ }
71
+ },
72
+
73
+ // Also handle TSTypeAliasDeclaration with object type
74
+ TSTypeAliasDeclaration(node) {
75
+ if (node.typeAnnotation && node.typeAnnotation.type === 'TSTypeLiteral') {
76
+ const typeParams = node.typeParameters;
77
+ const tokenBeforeBrace = typeParams ? typeParams : node.id;
78
+
79
+ // Find the equals sign
80
+ const equalsToken = sourceCode.getTokenAfter(tokenBeforeBrace, token => token.type === 'Punctuator' && token.value === '=');
81
+
82
+ if (!equalsToken) return;
83
+
84
+ // Get the opening brace token
85
+ const openingBrace = sourceCode.getTokenAfter(equalsToken, token => token.type === 'Punctuator' && token.value === '{');
86
+
87
+ if (!openingBrace) return;
88
+
89
+ // Check if there's a newline before the opening brace
90
+ const textBetween = sourceCode.text.slice(equalsToken.range[1], openingBrace.range[0]);
91
+
92
+ // Check if brace is on the same line
93
+ if (!textBetween.includes('\n')) {
94
+ context.report({
95
+ node: openingBrace,
96
+ message: 'Opening brace should be on a new line (Allman style)',
97
+ fix(fixer) {
98
+ // Get the base indentation
99
+ const typeLine = sourceCode.getLines()[node.loc.start.line - 1];
100
+ const baseIndentMatch = typeLine.match(/^(\s*)/);
101
+ const baseIndent = baseIndentMatch ? baseIndentMatch[1] : '';
102
+
103
+ // Replace the space before the brace with a newline and proper indentation
104
+ return fixer.replaceTextRange(
105
+ [equalsToken.range[1], openingBrace.range[0]],
106
+ ' =\n' + baseIndent
107
+ );
108
+ }
109
+ });
110
+ }
111
+ }
112
+ }
113
+ };
114
+ }
115
+ };
116
+
117
+ export default {
118
+ rules: {
119
+ 'interface-brace-style': interfaceBraceRule
120
+ }
121
+ };