lint-rules-alvin 1.2.1 → 2.0.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/.vscode/settings.json +8 -1
- package/eslint/configs/{astro.js → astro.ts} +2 -3
- package/eslint/configs/{base.js → base.ts} +2 -3
- package/eslint/configs/{custom.js → custom.ts} +10 -12
- package/eslint/configs/{importX.js → importX.ts} +13 -3
- package/eslint/configs/index.d.ts +11 -13
- package/eslint/configs/index.ts +10 -0
- package/eslint/configs/json.ts +7 -0
- package/eslint/configs/{markdown.js → markdown.ts} +2 -4
- package/eslint/configs/{reactHooks.js → reactHooks.ts} +2 -3
- package/eslint/configs/{stylistic.js → stylistic.ts} +2 -4
- package/eslint/configs/{sveltekit.js → sveltekit.ts} +12 -7
- package/eslint/configs/{typescript.js → typescript.ts} +4 -4
- package/eslint/custom_rules/{destructure-newline.js → destructure-newline.ts} +19 -14
- package/eslint/custom_rules/jsx-multiline-prop-newline.ts +245 -0
- package/eslint/custom_rules/max-chain-per-line.ts +320 -0
- package/eslint/custom_rules/multiline-array-accessor-newline.ts +249 -0
- package/eslint/custom_rules/{multiline-paren-newline.js → multiline-paren-newline.ts} +130 -65
- package/eslint/custom_rules/{newline-between-imports.js → newline-between-imports.ts} +16 -14
- package/eslint/custom_rules/unnamed-imports-last.ts +105 -0
- package/eslint/presets/{astroReactTs.js → astroReactPreset.ts} +13 -11
- package/eslint/presets/index.d.ts +3 -11
- package/eslint/presets/index.ts +2 -0
- package/eslint/presets/sveltekitPreset.ts +34 -0
- package/eslint.config.ts +19 -0
- package/package.json +51 -23
- package/test/eslint/destructure-newline.test.ts +21 -0
- package/test/eslint/jsx-multiline-prop-newline.test.ts +203 -0
- package/test/eslint/max-chain-per-line.test.ts +238 -0
- package/test/eslint/multiline-array-accessor-newline.test.ts +37 -0
- package/test/eslint/multiline-paren-newline.test.ts +87 -0
- package/test/eslint/newline-between-imports.test.ts +20 -0
- package/test/eslint/test-bootstrap.mjs +28 -0
- package/test/eslint/unnamed-imports-last.test.ts +25 -0
- package/tsconfig.json +25 -0
- package/tsconfig.test.json +18 -0
- package/eslint/configs/importXTs.js +0 -24
- package/eslint/configs/index.js +0 -11
- package/eslint/configs/json.js +0 -25
- package/eslint/custom_rules/jsx-multiline-prop-newline.js +0 -208
- package/eslint/custom_rules/max-chain-per-line.js +0 -253
- package/eslint/custom_rules/multiline-array-accessor-newline.js +0 -193
- package/eslint/custom_rules/unnamed-imports-last.js +0 -82
- package/eslint/presets/index.js +0 -2
- package/eslint/presets/sveltekitTs.js +0 -30
- package/eslint.config.js +0 -17
package/.vscode/settings.json
CHANGED
|
@@ -5,5 +5,12 @@
|
|
|
5
5
|
"eslint.codeAction.showDocumentation": {
|
|
6
6
|
"enable": true
|
|
7
7
|
},
|
|
8
|
-
"eslint.enable": true
|
|
8
|
+
"eslint.enable": true,
|
|
9
|
+
"eslint.validate": [
|
|
10
|
+
"javascript",
|
|
11
|
+
"typescript",
|
|
12
|
+
"javascriptreact",
|
|
13
|
+
"typescriptreact"
|
|
14
|
+
],
|
|
15
|
+
"typescript.tsdk": "node_modules\\typescript\\lib"
|
|
9
16
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
1
2
|
import eslintPluginAstro from 'eslint-plugin-astro';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The ESLint Astro config. Extends `configs['flat/base']` and configures all rules.
|
|
5
|
-
*
|
|
6
|
-
* @type {import('eslint').Linter.Config}
|
|
7
6
|
*/
|
|
8
|
-
export const astro = [
|
|
7
|
+
export const astro: TSESLint.FlatConfig.ConfigArray = [
|
|
9
8
|
...eslintPluginAstro.configs['flat/base'],
|
|
10
9
|
{
|
|
11
10
|
name: 'eslint-plugin-astro',
|
|
@@ -3,14 +3,13 @@ import { includeIgnoreFile } from '@eslint/compat';
|
|
|
3
3
|
import eslintJs from '@eslint/js';
|
|
4
4
|
import { globalIgnores } from 'eslint/config';
|
|
5
5
|
import globals from 'globals';
|
|
6
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* The ESLint base config. Includes base rules, ignore files and directives,
|
|
9
10
|
* and the recommended eslintJs rules.
|
|
10
|
-
*
|
|
11
|
-
* @type {import('eslint').Linter.Config}
|
|
12
11
|
*/
|
|
13
|
-
export const base = [
|
|
12
|
+
export const base: TSESLint.FlatConfig.ConfigArray = [
|
|
14
13
|
includeIgnoreFile(
|
|
15
14
|
path.join(
|
|
16
15
|
process.cwd(),
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
import { newlineBetweenImportsRule } from '../custom_rules/newline-between-imports';
|
|
3
|
+
import { unnamedImportsLastRule } from '../custom_rules/unnamed-imports-last';
|
|
4
|
+
import { jsxMultilinePropNewlineRule } from '../custom_rules/jsx-multiline-prop-newline';
|
|
5
|
+
import { maxChainPerLineRule } from '../custom_rules/max-chain-per-line';
|
|
6
|
+
import { multilineParenNewlineRule } from '../custom_rules/multiline-paren-newline';
|
|
7
|
+
import { multilineArrayAccessorNewlineRule } from '../custom_rules/multiline-array-accessor-newline';
|
|
8
|
+
import { destructureNewlineRule } from '../custom_rules/destructure-newline';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Provides a config that creates a plugin and configures custom rules.
|
|
11
|
-
*
|
|
12
|
-
* @type {import('eslint').Linter.Config}
|
|
13
12
|
*/
|
|
14
|
-
export const custom = {
|
|
13
|
+
export const custom: TSESLint.FlatConfig.Config = {
|
|
15
14
|
plugins: {
|
|
16
15
|
custom: {
|
|
17
16
|
rules: {
|
|
@@ -42,8 +41,7 @@ export const custom = {
|
|
|
42
41
|
'error',
|
|
43
42
|
{
|
|
44
43
|
maxChain: 2,
|
|
45
|
-
enforceSingleLine: true
|
|
46
|
-
checkSingleLink: true
|
|
44
|
+
enforceSingleLine: true
|
|
47
45
|
}
|
|
48
46
|
],
|
|
49
47
|
'custom/multiline-paren-newline': [
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
1
2
|
import { importX as eslintPluginImportX } from 'eslint-plugin-import-x';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The ESLint import config. Configures all rules.
|
|
5
|
-
*
|
|
6
|
-
* @type {import('eslint').Linter.Config}
|
|
7
6
|
*/
|
|
8
|
-
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
8
|
+
export const importX: TSESLint.FlatConfig.Config = {
|
|
9
9
|
name: 'eslint-plugin-import-x',
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
10
12
|
plugins: { 'import-x': eslintPluginImportX },
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
14
|
+
...eslintPluginImportX.flatConfigs.typescript,
|
|
15
|
+
settings: {
|
|
16
|
+
'import-x/resolver': {
|
|
17
|
+
typescript: true,
|
|
18
|
+
node: true
|
|
19
|
+
}
|
|
20
|
+
},
|
|
11
21
|
rules: {
|
|
12
22
|
'import-x/export': 'error',
|
|
13
23
|
'import-x/no-deprecated': 'error',
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
export
|
|
11
|
-
export
|
|
12
|
-
export declare const sveltekit: Linter.Config[];
|
|
13
|
-
export declare const typescript: Linter.Config;
|
|
1
|
+
import type { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
export const astro: TSESLint.FlatConfig.ConfigArray;
|
|
3
|
+
export const base: TSESLint.FlatConfig.ConfigArray;
|
|
4
|
+
export const custom: TSESLint.FlatConfig.Config;
|
|
5
|
+
export const importX: TSESLint.FlatConfig.Config;
|
|
6
|
+
export const json: TSESLint.FlatConfig.ConfigArray;
|
|
7
|
+
export const markdown: TSESLint.FlatConfig.Config;
|
|
8
|
+
export const reactHooks: TSESLint.FlatConfig.Config;
|
|
9
|
+
export const stylistic: TSESLint.FlatConfig.Config;
|
|
10
|
+
export const sveltekit: TSESLint.FlatConfig.ConfigArray;
|
|
11
|
+
export const typescript: TSESLint.FlatConfig.Config;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { astro } from './astro';
|
|
2
|
+
export { base } from './base';
|
|
3
|
+
export { custom } from './custom';
|
|
4
|
+
export { importX } from './importX';
|
|
5
|
+
export { json } from './json';
|
|
6
|
+
export { markdown } from './markdown';
|
|
7
|
+
export { reactHooks } from './reactHooks';
|
|
8
|
+
export { stylistic } from './stylistic';
|
|
9
|
+
export { sveltekit } from './sveltekit';
|
|
10
|
+
export { typescript } from './typescript';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
import eslintPluginJsonc from 'eslint-plugin-jsonc';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The ESLint JSON config. Extends `flat/recommended-with-json`.
|
|
6
|
+
*/
|
|
7
|
+
export const json: TSESLint.FlatConfig.ConfigArray = [ ...eslintPluginJsonc.configs['flat/recommended-with-json'] ];
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
1
2
|
import eslintPluginMarkdown from 'eslint-plugin-markdown';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The ESLint markdown config. Extends `configs.recommended` only for `md` files.
|
|
5
|
-
*
|
|
6
|
-
* @type {import('eslint').Linter.Config}
|
|
7
|
-
*
|
|
8
6
|
*/
|
|
9
|
-
export const markdown = {
|
|
7
|
+
export const markdown: TSESLint.FlatConfig.Config = {
|
|
10
8
|
name: 'eslint-plugin-markdown',
|
|
11
9
|
files: [ '**/*.{md}' ],
|
|
12
10
|
...eslintPluginMarkdown.configs.recommended
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
1
2
|
import eslintPluginReactHooks from 'eslint-plugin-react-hooks';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The ESLint `react-hooks` config. Extends `configs.flat.recommended` only for `jsx` and `tsx` files.
|
|
5
|
-
*
|
|
6
|
-
* @type {import('eslint').Linter.Config}
|
|
7
6
|
*/
|
|
8
|
-
export const reactHooks = {
|
|
7
|
+
export const reactHooks: TSESLint.FlatConfig.Config = {
|
|
9
8
|
name: 'eslint-plugin-react-hooks',
|
|
10
9
|
files: [ '**/*.{jsx,tsx}' ],
|
|
11
10
|
...eslintPluginReactHooks
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
// eslint-disable-next-line import-x/no-deprecated, import-x/namespace, import-x/default
|
|
2
1
|
import eslintPluginStylistic from '@stylistic/eslint-plugin';
|
|
2
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The ESLint `stylistic` config. Extends `configs.recommended` and overrides all rules.
|
|
6
|
-
*
|
|
7
|
-
* @type {import('eslint').Linter.Config}
|
|
8
6
|
*/
|
|
9
|
-
export const stylistic = {
|
|
7
|
+
export const stylistic: TSESLint.FlatConfig.Config = {
|
|
10
8
|
name: 'eslint-plugin-stylistic',
|
|
11
9
|
...eslintPluginStylistic.configs.recommended,
|
|
12
10
|
plugins: { '@stylistic': eslintPluginStylistic },
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
1
2
|
import eslintPluginSvelte from 'eslint-plugin-svelte';
|
|
3
|
+
import tsEslintParser from '@typescript-eslint/parser';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* The ESLint `stylistic` config. Extends `configs.recommended` and overrides all rules.
|
|
5
|
-
*
|
|
6
|
-
* @type {import('eslint').Linter.Config[]}
|
|
7
7
|
*/
|
|
8
|
-
export const sveltekit = [
|
|
8
|
+
export const sveltekit: TSESLint.FlatConfig.ConfigArray = [
|
|
9
9
|
...eslintPluginSvelte.configs['flat/base'],
|
|
10
10
|
{
|
|
11
|
+
name: 'sveltekit-config',
|
|
11
12
|
files: [
|
|
12
13
|
'**/*.svelte',
|
|
13
14
|
'**/*.svelte.ts',
|
|
14
15
|
'**/*.svelte.js'
|
|
15
16
|
],
|
|
17
|
+
languageOptions: {
|
|
18
|
+
parserOptions: {
|
|
19
|
+
parser: tsEslintParser,
|
|
20
|
+
projectService: true,
|
|
21
|
+
extraFileExtensions: [ '.svelte' ]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
16
24
|
rules: {
|
|
17
25
|
|
|
18
26
|
// Svelte-specific
|
|
@@ -117,10 +125,7 @@ export const sveltekit = [
|
|
|
117
125
|
}
|
|
118
126
|
}
|
|
119
127
|
],
|
|
120
|
-
'svelte/html-self-closing':
|
|
121
|
-
'error',
|
|
122
|
-
'default'
|
|
123
|
-
],
|
|
128
|
+
'svelte/html-self-closing': 'error', // leave it on 'default'
|
|
124
129
|
'indent': 'off', // eslint-disable-line @stylistic/quote-props
|
|
125
130
|
'@stylistic/indent': 'off',
|
|
126
131
|
'svelte/indent': [
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import eslintTs from '@typescript-eslint/eslint-plugin';
|
|
2
2
|
import eslintTsParser from '@typescript-eslint/parser';
|
|
3
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The ESLint `typescript` config. Extends `configs.base`,
|
|
6
7
|
* enables `languageOptions.parserOptions.projectService` and configures all rules,
|
|
7
8
|
* only for `ts`, `tsx`, `mts` and `cts` files.
|
|
8
|
-
*
|
|
9
|
-
* @type {import('eslint').Linter.Config}
|
|
10
9
|
*/
|
|
11
|
-
export const typescript = {
|
|
10
|
+
export const typescript: TSESLint.FlatConfig.Config = {
|
|
12
11
|
name: 'typescript',
|
|
13
12
|
files: [
|
|
14
13
|
'**/*.ts',
|
|
@@ -16,7 +15,8 @@ export const typescript = {
|
|
|
16
15
|
'**/*.mts',
|
|
17
16
|
'**/*.cts'
|
|
18
17
|
],
|
|
19
|
-
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
19
|
+
...eslintTs.configs['flat/base'] as TSESLint.FlatConfig.Config,
|
|
20
20
|
languageOptions: {
|
|
21
21
|
parser: eslintTsParser,
|
|
22
22
|
parserOptions: { projectService: true },
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
AST_NODE_TYPES,
|
|
3
|
+
ESLintUtils
|
|
4
|
+
} from '@typescript-eslint/utils';
|
|
5
|
+
|
|
6
|
+
export const destructureNewlineRule = ESLintUtils.RuleCreator.withoutDocs({
|
|
5
7
|
meta: {
|
|
6
8
|
type: 'layout',
|
|
7
|
-
docs: {
|
|
8
|
-
description: 'Enforce newlines between properties in destructuring assignments',
|
|
9
|
-
category: 'Stylistic Issues',
|
|
10
|
-
recommended: false
|
|
11
|
-
},
|
|
9
|
+
docs: { description: 'Enforce newlines between properties in destructuring assignments' },
|
|
12
10
|
fixable: 'whitespace',
|
|
13
11
|
schema: [
|
|
14
12
|
{
|
|
@@ -24,16 +22,16 @@ export const destructureNewlineRule = {
|
|
|
24
22
|
],
|
|
25
23
|
messages: { error: 'There should be a newline between destructuring properties.' }
|
|
26
24
|
},
|
|
27
|
-
|
|
28
|
-
create(context) {
|
|
25
|
+
defaultOptions: [ { minItems: 2 } ],
|
|
26
|
+
create(context, options) {
|
|
29
27
|
|
|
30
28
|
const sourceCode = context.sourceCode;
|
|
31
|
-
const { minItems
|
|
29
|
+
const { minItems } = options[0];
|
|
32
30
|
|
|
33
31
|
return {
|
|
34
32
|
VariableDeclarator(node) {
|
|
35
33
|
|
|
36
|
-
if (node.id.type !==
|
|
34
|
+
if (node.id.type !== AST_NODE_TYPES.ObjectPattern) {
|
|
37
35
|
|
|
38
36
|
return;
|
|
39
37
|
|
|
@@ -54,6 +52,13 @@ export const destructureNewlineRule = {
|
|
|
54
52
|
const lastTokenOfCurrent = sourceCode.getLastToken(currentProperty);
|
|
55
53
|
const firstTokenOfNext = sourceCode.getFirstToken(nextProperty);
|
|
56
54
|
|
|
55
|
+
// Token not found
|
|
56
|
+
if (!lastTokenOfCurrent || !firstTokenOfNext) {
|
|
57
|
+
|
|
58
|
+
continue;
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
if (
|
|
58
63
|
lastTokenOfCurrent
|
|
59
64
|
.loc
|
|
@@ -85,4 +90,4 @@ export const destructureNewlineRule = {
|
|
|
85
90
|
};
|
|
86
91
|
|
|
87
92
|
}
|
|
88
|
-
};
|
|
93
|
+
});
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AST_NODE_TYPES,
|
|
3
|
+
ESLintUtils,
|
|
4
|
+
TSESTree
|
|
5
|
+
} from '@typescript-eslint/utils';
|
|
6
|
+
|
|
7
|
+
export const jsxMultilinePropNewlineRule = ESLintUtils.RuleCreator.withoutDocs({
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'layout',
|
|
10
|
+
docs: { description: 'Enforces newline consistency for JSX properties.' },
|
|
11
|
+
fixable: 'code',
|
|
12
|
+
schema: [
|
|
13
|
+
{
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
enforceSingleLine: { type: 'boolean' },
|
|
17
|
+
allowSingleExpression: { type: 'boolean' }
|
|
18
|
+
},
|
|
19
|
+
additionalProperties: false
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
messages: {
|
|
23
|
+
collapse: 'This expression should not be surrounded by newlines.',
|
|
24
|
+
expand: 'This multiline expression should be surrounded by newlines.'
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
defaultOptions: [
|
|
28
|
+
{
|
|
29
|
+
enforceSingleLine: false,
|
|
30
|
+
allowSingleExpression: true
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
create(context, options) {
|
|
34
|
+
|
|
35
|
+
const sourceCode = context.sourceCode;
|
|
36
|
+
const {
|
|
37
|
+
enforceSingleLine,
|
|
38
|
+
allowSingleExpression
|
|
39
|
+
} = options[0];
|
|
40
|
+
|
|
41
|
+
function isMultiline(node: TSESTree.Node): boolean {
|
|
42
|
+
|
|
43
|
+
return node
|
|
44
|
+
.loc
|
|
45
|
+
.start
|
|
46
|
+
.line !== node
|
|
47
|
+
.loc
|
|
48
|
+
.end
|
|
49
|
+
.line;
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isSingleObjectOrArrayExpression(node: TSESTree.Node): boolean {
|
|
54
|
+
|
|
55
|
+
return node.type === AST_NODE_TYPES.ObjectExpression || node.type === AST_NODE_TYPES.ArrayExpression;
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function checkContainer(node: TSESTree.JSXExpressionContainer) {
|
|
60
|
+
|
|
61
|
+
// Only enforce for JSX props (attributes), not for children
|
|
62
|
+
/* eslint-disable-next-line
|
|
63
|
+
@typescript-eslint/no-unnecessary-condition,
|
|
64
|
+
@typescript-eslint/strict-boolean-expressions */
|
|
65
|
+
if (!node?.parent || node.parent.type !== AST_NODE_TYPES.JSXAttribute)
|
|
66
|
+
return;
|
|
67
|
+
|
|
68
|
+
const expr = node.expression;
|
|
69
|
+
/* eslint-disable-next-line
|
|
70
|
+
@typescript-eslint/no-unnecessary-condition,
|
|
71
|
+
@typescript-eslint/strict-boolean-expressions */
|
|
72
|
+
if (!expr)
|
|
73
|
+
return;
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* Find the surrounding braces around the expression
|
|
77
|
+
* Use tokenBefore/After relative to the inner expression to avoid TS/estree mismatches
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
// Use the expression container's outer tokens to find the real surrounding braces
|
|
81
|
+
|
|
82
|
+
const openBrace = sourceCode.getFirstToken(node);
|
|
83
|
+
|
|
84
|
+
const closeBrace = sourceCode.getLastToken(node);
|
|
85
|
+
if (!openBrace || !closeBrace)
|
|
86
|
+
return;
|
|
87
|
+
|
|
88
|
+
const firstTokenOfExpr = sourceCode.getFirstToken(expr);
|
|
89
|
+
|
|
90
|
+
const lastTokenOfExpr = sourceCode.getLastToken(expr);
|
|
91
|
+
if (!firstTokenOfExpr || !lastTokenOfExpr)
|
|
92
|
+
return;
|
|
93
|
+
|
|
94
|
+
const exprIsMultiline = isMultiline(expr);
|
|
95
|
+
const exceptionApplies = allowSingleExpression && isSingleObjectOrArrayExpression(expr);
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* If configured to prefer compact braces for a single object/array expression,
|
|
99
|
+
* collapse surrounding whitespace/newlines regardless of multiline/single-line.
|
|
100
|
+
*/
|
|
101
|
+
if (exceptionApplies) {
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
openBrace
|
|
105
|
+
.loc
|
|
106
|
+
.end
|
|
107
|
+
.line !== firstTokenOfExpr
|
|
108
|
+
.loc
|
|
109
|
+
.start
|
|
110
|
+
.line
|
|
111
|
+
) {
|
|
112
|
+
|
|
113
|
+
context.report({
|
|
114
|
+
node: openBrace,
|
|
115
|
+
messageId: 'collapse',
|
|
116
|
+
fix: (fixer) => fixer.removeRange([
|
|
117
|
+
openBrace.range[1],
|
|
118
|
+
firstTokenOfExpr.range[0]
|
|
119
|
+
])
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
if (
|
|
124
|
+
lastTokenOfExpr
|
|
125
|
+
.loc
|
|
126
|
+
.end
|
|
127
|
+
.line !== closeBrace
|
|
128
|
+
.loc
|
|
129
|
+
.start
|
|
130
|
+
.line
|
|
131
|
+
) {
|
|
132
|
+
|
|
133
|
+
context.report({
|
|
134
|
+
node: closeBrace,
|
|
135
|
+
messageId: 'collapse',
|
|
136
|
+
fix: (fixer) => fixer.removeRange([
|
|
137
|
+
lastTokenOfExpr.range[1],
|
|
138
|
+
closeBrace.range[0]
|
|
139
|
+
])
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (exprIsMultiline) {
|
|
148
|
+
|
|
149
|
+
// Require newlines after '{' and before '}' for multiline expressions
|
|
150
|
+
if (
|
|
151
|
+
openBrace
|
|
152
|
+
.loc
|
|
153
|
+
.end
|
|
154
|
+
.line === firstTokenOfExpr
|
|
155
|
+
.loc
|
|
156
|
+
.start
|
|
157
|
+
.line
|
|
158
|
+
) {
|
|
159
|
+
|
|
160
|
+
context.report({
|
|
161
|
+
node: openBrace,
|
|
162
|
+
messageId: 'expand',
|
|
163
|
+
fix: (fixer) => fixer.insertTextAfter(
|
|
164
|
+
openBrace,
|
|
165
|
+
'\n'
|
|
166
|
+
)
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
}
|
|
170
|
+
if (
|
|
171
|
+
lastTokenOfExpr
|
|
172
|
+
.loc
|
|
173
|
+
.end
|
|
174
|
+
.line === closeBrace
|
|
175
|
+
.loc
|
|
176
|
+
.start
|
|
177
|
+
.line
|
|
178
|
+
) {
|
|
179
|
+
|
|
180
|
+
context.report({
|
|
181
|
+
node: closeBrace,
|
|
182
|
+
messageId: 'expand',
|
|
183
|
+
fix: (fixer) => fixer.insertTextBefore(
|
|
184
|
+
closeBrace,
|
|
185
|
+
'\n'
|
|
186
|
+
)
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Single-line expression: optionally enforce collapse (no surrounding newlines)
|
|
195
|
+
if (enforceSingleLine) {
|
|
196
|
+
|
|
197
|
+
if (
|
|
198
|
+
openBrace
|
|
199
|
+
.loc
|
|
200
|
+
.end
|
|
201
|
+
.line !== firstTokenOfExpr
|
|
202
|
+
.loc
|
|
203
|
+
.start
|
|
204
|
+
.line
|
|
205
|
+
) {
|
|
206
|
+
|
|
207
|
+
context.report({
|
|
208
|
+
node: openBrace,
|
|
209
|
+
messageId: 'collapse',
|
|
210
|
+
fix: (fixer) => fixer.removeRange([
|
|
211
|
+
openBrace.range[1],
|
|
212
|
+
firstTokenOfExpr.range[0]
|
|
213
|
+
])
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
}
|
|
217
|
+
if (
|
|
218
|
+
lastTokenOfExpr
|
|
219
|
+
.loc
|
|
220
|
+
.end
|
|
221
|
+
.line !== closeBrace
|
|
222
|
+
.loc
|
|
223
|
+
.start
|
|
224
|
+
.line
|
|
225
|
+
) {
|
|
226
|
+
|
|
227
|
+
context.report({
|
|
228
|
+
node: closeBrace,
|
|
229
|
+
messageId: 'collapse',
|
|
230
|
+
fix: (fixer) => fixer.removeRange([
|
|
231
|
+
lastTokenOfExpr.range[1],
|
|
232
|
+
closeBrace.range[0]
|
|
233
|
+
])
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return { JSXExpressionContainer: checkContainer };
|
|
243
|
+
|
|
244
|
+
}
|
|
245
|
+
});
|