@dmitryrechkin/eslint-standard 1.1.1 → 1.1.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/eslint.config.mjs +2 -2
- package/package.json +10 -5
- package/src/cli/check-deps.mjs +102 -0
- package/src/cli/index.mjs +38 -0
- package/src/cli/install-deps.mjs +49 -0
- package/src/cli/postinstall-check.mjs +54 -0
- package/src/cli/postinstall.mjs +64 -0
- package/src/plugins/interface-brace.mjs +121 -0
- /package/{eslint-plugin-jsdoc-indent.mjs → src/plugins/jsdoc-indent.mjs} +0 -0
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 './
|
|
10
|
-
import interfaceBracePlugin from './
|
|
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,19 +1,24 @@
|
|
|
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.
|
|
4
|
+
"version": "1.1.3",
|
|
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
|
-
"
|
|
11
|
+
"src/",
|
|
9
12
|
"README.md",
|
|
10
13
|
"LICENSE"
|
|
11
14
|
],
|
|
12
15
|
"scripts": {
|
|
13
|
-
"postinstall": "
|
|
16
|
+
"postinstall": "node src/cli/postinstall.mjs",
|
|
14
17
|
"package:publish": "npm publish --access public",
|
|
15
|
-
"test": "npm run test:formatting",
|
|
16
|
-
"test:formatting": "node tests/test-runner.js"
|
|
18
|
+
"test": "npm run test:formatting && npm run test:cli",
|
|
19
|
+
"test:formatting": "node tests/test-runner.js",
|
|
20
|
+
"test:cli": "node tests/test-cli.js",
|
|
21
|
+
"test:install": "node tests/test-install-simulation.js"
|
|
17
22
|
},
|
|
18
23
|
"keywords": [],
|
|
19
24
|
"author": "",
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, join, resolve } from 'path';
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
|
|
11
|
+
// Check for --install flag
|
|
12
|
+
const shouldInstall = process.argv.includes('--install');
|
|
13
|
+
|
|
14
|
+
// Get peer dependencies
|
|
15
|
+
const packageJsonPath = join(__dirname, '../../package.json');
|
|
16
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
17
|
+
const peerDeps = packageJson.peerDependencies || {};
|
|
18
|
+
|
|
19
|
+
// Check if running from node_modules and find project root
|
|
20
|
+
const isInNodeModules = __dirname.includes('node_modules');
|
|
21
|
+
let projectRoot;
|
|
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
|
+
}
|
|
32
|
+
|
|
33
|
+
// Read project's package.json
|
|
34
|
+
let projectPackageJson;
|
|
35
|
+
try {
|
|
36
|
+
projectPackageJson = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8'));
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error('❌ Could not read project package.json');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const allDeps = {
|
|
43
|
+
...projectPackageJson.dependencies || {},
|
|
44
|
+
...projectPackageJson.devDependencies || {}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
console.log('🔍 Checking ESLint Standard peer dependencies...\n');
|
|
48
|
+
|
|
49
|
+
let missingDeps = [];
|
|
50
|
+
let outdatedDeps = [];
|
|
51
|
+
|
|
52
|
+
for (const [dep, requiredVersion] of Object.entries(peerDeps)) {
|
|
53
|
+
if (!allDeps[dep]) {
|
|
54
|
+
missingDeps.push(`${dep}@${requiredVersion}`);
|
|
55
|
+
console.log(`❌ Missing: ${dep} (required: ${requiredVersion})`);
|
|
56
|
+
} else {
|
|
57
|
+
console.log(`✅ Found: ${dep}@${allDeps[dep]}`);
|
|
58
|
+
// Simple version check - could be improved
|
|
59
|
+
if (!allDeps[dep].includes('^') && !allDeps[dep].includes('~') && allDeps[dep] !== requiredVersion) {
|
|
60
|
+
outdatedDeps.push(`${dep} (installed: ${allDeps[dep]}, required: ${requiredVersion})`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log('\n📊 Summary:');
|
|
66
|
+
if (missingDeps.length === 0 && outdatedDeps.length === 0) {
|
|
67
|
+
console.log('✅ All peer dependencies are satisfied!');
|
|
68
|
+
} else {
|
|
69
|
+
if (missingDeps.length > 0) {
|
|
70
|
+
console.log(`\n❌ Missing ${missingDeps.length} dependencies:`);
|
|
71
|
+
missingDeps.forEach(dep => console.log(` - ${dep}`));
|
|
72
|
+
|
|
73
|
+
if (shouldInstall) {
|
|
74
|
+
console.log('\n🔧 Auto-installing missing dependencies...\n');
|
|
75
|
+
|
|
76
|
+
// Import and run the install-deps script
|
|
77
|
+
try {
|
|
78
|
+
const installDepsModule = await import('./install-deps.mjs');
|
|
79
|
+
// The install-deps script will handle the installation
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error('❌ Failed to auto-install dependencies:', error.message);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
console.log('\n💡 To install missing dependencies:');
|
|
86
|
+
console.log(' npx @dmitryrechkin/eslint-standard install-deps');
|
|
87
|
+
console.log(' or run this command with --install flag');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (outdatedDeps.length > 0) {
|
|
91
|
+
console.log(`\n⚠️ ${outdatedDeps.length} dependencies may be outdated:`);
|
|
92
|
+
outdatedDeps.forEach(dep => console.log(` - ${dep}`));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!shouldInstall && missingDeps.length > 0) {
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export default function checkDeps() {
|
|
101
|
+
// Export for programmatic use
|
|
102
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
check-deps --install Auto-install missing dependencies if any
|
|
26
|
+
help Show this help message
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
npx @dmitryrechkin/eslint-standard install-deps
|
|
30
|
+
npx @dmitryrechkin/eslint-standard check-deps
|
|
31
|
+
npx @dmitryrechkin/eslint-standard check-deps --install
|
|
32
|
+
`);
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
console.error(`Unknown command: ${command}`);
|
|
36
|
+
console.log('Run "npx @dmitryrechkin/eslint-standard help" for usage information');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
@@ -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,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync, existsSync } from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, join } from 'path';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
|
|
11
|
+
// Don't run during local development
|
|
12
|
+
if (!__dirname.includes('node_modules')) {
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Check if we're in CI environment
|
|
17
|
+
const isCI = process.env.CI || process.env.CONTINUOUS_INTEGRATION || process.env.GITHUB_ACTIONS;
|
|
18
|
+
|
|
19
|
+
// Check if scripts are disabled
|
|
20
|
+
const npmConfigIgnoreScripts = process.env.npm_config_ignore_scripts === 'true';
|
|
21
|
+
|
|
22
|
+
if (isCI || npmConfigIgnoreScripts) {
|
|
23
|
+
// Silent exit in CI or when scripts are disabled
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Simple check for missing dependencies
|
|
28
|
+
try {
|
|
29
|
+
const packageJsonPath = join(__dirname, '../../package.json');
|
|
30
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
31
|
+
const peerDeps = Object.keys(packageJson.peerDependencies || {});
|
|
32
|
+
|
|
33
|
+
// Quick check if any peer deps are missing
|
|
34
|
+
let missingCount = 0;
|
|
35
|
+
for (const dep of peerDeps) {
|
|
36
|
+
try {
|
|
37
|
+
// Try to resolve the dependency
|
|
38
|
+
require.resolve(dep, { paths: [process.cwd()] });
|
|
39
|
+
} catch {
|
|
40
|
+
missingCount++;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (missingCount > 0) {
|
|
45
|
+
console.log(`
|
|
46
|
+
⚠️ @dmitryrechkin/eslint-standard: ${missingCount} peer dependencies are missing!
|
|
47
|
+
|
|
48
|
+
Run this command to check and install them:
|
|
49
|
+
👉 ${'\x1b[36m'}npx @dmitryrechkin/eslint-standard check-deps --install${'\x1b[0m'}
|
|
50
|
+
`);
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// Silent fail - don't break installation
|
|
54
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
// Check environment variables for auto-install preference
|
|
15
|
+
const autoInstall = process.env.ESLINT_STANDARD_AUTO_INSTALL === 'true';
|
|
16
|
+
const skipInstall = process.env.ESLINT_STANDARD_SKIP_INSTALL === 'true';
|
|
17
|
+
|
|
18
|
+
// Check if we're in CI environment
|
|
19
|
+
const isCI = process.env.CI || process.env.CONTINUOUS_INTEGRATION || process.env.GITHUB_ACTIONS;
|
|
20
|
+
|
|
21
|
+
if (skipInstall || isCI) {
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(`
|
|
26
|
+
╔════════════════════════════════════════════════════════════════╗
|
|
27
|
+
║ ║
|
|
28
|
+
║ 🎉 Thanks for installing @dmitryrechkin/eslint-standard! ║
|
|
29
|
+
║ ║
|
|
30
|
+
╚════════════════════════════════════════════════════════════════╝
|
|
31
|
+
|
|
32
|
+
📦 This package requires peer dependencies to work properly.
|
|
33
|
+
|
|
34
|
+
🚀 Quick install (auto-detects your package manager):
|
|
35
|
+
${'\x1b[36m'}npx @dmitryrechkin/eslint-standard check-deps --install${'\x1b[0m'}
|
|
36
|
+
|
|
37
|
+
📋 Or install all dependencies directly:
|
|
38
|
+
${'\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'}
|
|
39
|
+
|
|
40
|
+
🔍 To check if all dependencies are installed:
|
|
41
|
+
${'\x1b[36m'}npx @dmitryrechkin/eslint-standard check-deps${'\x1b[0m'}
|
|
42
|
+
|
|
43
|
+
📚 Documentation: https://github.com/dmitryrechkin/eslint-standard
|
|
44
|
+
`);
|
|
45
|
+
|
|
46
|
+
// Don't run check-deps in postinstall to avoid errors
|
|
47
|
+
// Users can run it manually with: npx @dmitryrechkin/eslint-standard check-deps
|
|
48
|
+
|
|
49
|
+
if (autoInstall) {
|
|
50
|
+
console.log('\n🤖 Auto-install enabled via ESLINT_STANDARD_AUTO_INSTALL=true');
|
|
51
|
+
console.log('📦 Checking and installing peer dependencies...\n');
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
// Run check-deps with --install flag
|
|
55
|
+
const { execSync } = await import('child_process');
|
|
56
|
+
execSync('node ' + join(__dirname, 'index.mjs') + ' check-deps --install', {
|
|
57
|
+
stdio: 'inherit',
|
|
58
|
+
cwd: process.cwd()
|
|
59
|
+
});
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error('❌ Auto-install failed. Please run manually:');
|
|
62
|
+
console.error(' npx @dmitryrechkin/eslint-standard check-deps --install');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -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
|
+
};
|
|
File without changes
|