@frontify/eslint-config-basic 1.0.13 → 1.0.14
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 +139 -68
- package/package.json +16 -14
- package/rules/import-order.test.mjs +192 -0
package/eslint.config.mjs
CHANGED
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
// @ts-check
|
|
4
4
|
|
|
5
5
|
import eslint from '@eslint/js';
|
|
6
|
+
import eslintPluginMarkdown from '@eslint/markdown';
|
|
6
7
|
import eslintPluginComments from '@eslint-community/eslint-plugin-eslint-comments/configs';
|
|
7
8
|
import eslintPluginStylistic from '@stylistic/eslint-plugin';
|
|
8
9
|
import { defineConfig } from 'eslint/config';
|
|
9
|
-
import
|
|
10
|
+
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript';
|
|
11
|
+
import { createNodeResolver, default as eslintPluginImportX } from 'eslint-plugin-import-x';
|
|
10
12
|
import eslintPluginJsonc from 'eslint-plugin-jsonc';
|
|
11
13
|
// @ts-expect-error No types available
|
|
12
14
|
import eslintPluginLodash from 'eslint-plugin-lodash';
|
|
13
15
|
// @ts-expect-error No types available
|
|
14
|
-
import eslintPluginMarkdown from 'eslint-plugin-markdown';
|
|
15
|
-
// @ts-expect-error No types available
|
|
16
16
|
import eslintPluginNoOnlyTests from 'eslint-plugin-no-only-tests';
|
|
17
17
|
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
18
18
|
// @ts-expect-error No types available
|
|
@@ -22,23 +22,131 @@ import eslintPluginYml from 'eslint-plugin-yml';
|
|
|
22
22
|
import globals from 'globals';
|
|
23
23
|
import tseslint from 'typescript-eslint';
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* eslint-plugin-jsonc / eslint-plugin-yml flat presets end with `{ rules }` without `files`, which would apply to every file.
|
|
27
|
+
* Spreading the scoped preset into `defineConfig` matches `{ files, ...preset }` for a single merged object.
|
|
28
|
+
*
|
|
29
|
+
* @param {import('eslint').Linter.Config[]} preset
|
|
30
|
+
* @param {{ files: string[], ignores?: string[], language?: string }} scope
|
|
31
|
+
*/
|
|
32
|
+
function scopeFlatPreset(preset, scope) {
|
|
33
|
+
const { files, ignores, language } = scope;
|
|
34
|
+
return preset.map((config) => {
|
|
35
|
+
if (config.rules !== undefined && config.files === undefined) {
|
|
36
|
+
return {
|
|
37
|
+
...config,
|
|
38
|
+
files,
|
|
39
|
+
...(ignores?.length ? { ignores } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (language !== undefined && 'language' in config && Array.isArray(config.files)) {
|
|
43
|
+
return {
|
|
44
|
+
...config,
|
|
45
|
+
files,
|
|
46
|
+
...(ignores?.length ? { ignores } : {}),
|
|
47
|
+
language,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return config;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const jsonWithCommentsPatterns = ['**/*.jsonc', '**/.vscode/**/*.json', '**/tsconfig.json', '**/tsconfig.*.json'];
|
|
55
|
+
|
|
56
|
+
const strictJsonScope = {
|
|
57
|
+
files: ['**/*.json'],
|
|
58
|
+
ignores: ['**/package-lock.json', ...jsonWithCommentsPatterns],
|
|
59
|
+
language: 'jsonc/json',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const yamlScope = {
|
|
63
|
+
files: ['**/*.yaml', '**/*.yml'],
|
|
64
|
+
ignores: ['**/pnpm-lock.yaml', '**/yarn.lock'],
|
|
65
|
+
language: 'yml/yaml',
|
|
66
|
+
};
|
|
67
|
+
|
|
25
68
|
export default defineConfig(
|
|
26
|
-
|
|
69
|
+
{
|
|
70
|
+
...eslint.configs.recommended,
|
|
71
|
+
files: ['**/*.{js,mjs,cjs,jsx}'],
|
|
72
|
+
},
|
|
27
73
|
tseslint.configs.recommendedTypeChecked,
|
|
28
74
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
29
75
|
eslintPluginPromise.configs['flat/recommended'],
|
|
30
76
|
eslintPluginComments.recommended,
|
|
31
|
-
eslintPluginJsonc.configs['flat/recommended-with-json'],
|
|
32
|
-
eslintPluginJsonc.configs['flat/prettier'],
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
77
|
+
...scopeFlatPreset(eslintPluginJsonc.configs['flat/recommended-with-json'], strictJsonScope),
|
|
78
|
+
...scopeFlatPreset(eslintPluginJsonc.configs['flat/prettier'], strictJsonScope),
|
|
79
|
+
{
|
|
80
|
+
...strictJsonScope,
|
|
81
|
+
extends: [tseslint.configs.disableTypeChecked],
|
|
82
|
+
rules: {
|
|
83
|
+
quotes: ['error', 'double'],
|
|
84
|
+
'quote-props': ['error', 'always'],
|
|
85
|
+
'comma-dangle': ['error', 'never'],
|
|
86
|
+
semi: 'off',
|
|
87
|
+
'jsonc/array-bracket-spacing': ['error', 'never'],
|
|
88
|
+
'jsonc/comma-dangle': ['error', 'never'],
|
|
89
|
+
'jsonc/comma-style': ['error', 'last'],
|
|
90
|
+
'jsonc/key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
|
91
|
+
'jsonc/no-octal-escape': 'error',
|
|
92
|
+
'jsonc/object-curly-newline': ['error', { multiline: true, consistent: true }],
|
|
93
|
+
'jsonc/object-curly-spacing': ['error', 'always'],
|
|
94
|
+
'jsonc/object-property-newline': ['error', { allowMultiplePropertiesPerLine: true }],
|
|
95
|
+
'jsonc/no-dupe-keys': 'error',
|
|
96
|
+
'jsonc/sort-keys': ['warn', 'asc'],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
...scopeFlatPreset(eslintPluginJsonc.configs['flat/recommended-with-jsonc'], {
|
|
100
|
+
files: jsonWithCommentsPatterns,
|
|
101
|
+
language: 'jsonc/jsonc',
|
|
102
|
+
}),
|
|
103
|
+
...scopeFlatPreset(eslintPluginJsonc.configs['flat/prettier'], {
|
|
104
|
+
files: jsonWithCommentsPatterns,
|
|
105
|
+
language: 'jsonc/jsonc',
|
|
106
|
+
}),
|
|
36
107
|
{
|
|
37
|
-
|
|
108
|
+
files: jsonWithCommentsPatterns,
|
|
109
|
+
language: 'jsonc/jsonc',
|
|
110
|
+
extends: [tseslint.configs.disableTypeChecked],
|
|
111
|
+
},
|
|
112
|
+
...scopeFlatPreset(eslintPluginJsonc.configs['flat/recommended-with-json5'], {
|
|
113
|
+
files: ['**/*.json5'],
|
|
114
|
+
language: 'jsonc/json5',
|
|
115
|
+
}),
|
|
116
|
+
...scopeFlatPreset(eslintPluginJsonc.configs['flat/prettier'], {
|
|
117
|
+
files: ['**/*.json5'],
|
|
118
|
+
language: 'jsonc/json5',
|
|
119
|
+
}),
|
|
120
|
+
{
|
|
121
|
+
files: ['**/*.json5'],
|
|
122
|
+
language: 'jsonc/json5',
|
|
123
|
+
extends: [tseslint.configs.disableTypeChecked],
|
|
124
|
+
},
|
|
125
|
+
...scopeFlatPreset(eslintPluginYml.configs['flat/recommended'], yamlScope),
|
|
126
|
+
...scopeFlatPreset(eslintPluginYml.configs['flat/prettier'], yamlScope),
|
|
127
|
+
{
|
|
128
|
+
...yamlScope,
|
|
129
|
+
extends: [tseslint.configs.disableTypeChecked],
|
|
130
|
+
rules: {
|
|
131
|
+
'yml/quotes': ['error', { prefer: 'single', avoidEscape: false }],
|
|
132
|
+
'yml/no-empty-document': 'off',
|
|
133
|
+
'yml/indent': ['error', 4, { indicatorValueIndent: 2 }],
|
|
134
|
+
'@stylistic/spaced-comment': 'off',
|
|
135
|
+
'yml/no-empty-mapping-value': 'off',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
files: ['**/*.md'],
|
|
140
|
+
plugins: { markdown: eslintPluginMarkdown },
|
|
141
|
+
extends: ['markdown/recommended'],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
// General configuration for JS/TS source (not JSON, YAML, Markdown, etc.)
|
|
145
|
+
files: ['**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts}'],
|
|
38
146
|
linterOptions: {
|
|
39
147
|
reportUnusedDisableDirectives: true,
|
|
40
148
|
},
|
|
41
|
-
extends: [
|
|
149
|
+
extends: [eslintPluginImportX.flatConfigs.typescript],
|
|
42
150
|
plugins: {
|
|
43
151
|
// Where we want to customize few part and not use recommended
|
|
44
152
|
unicorn: eslintPluginUnicorn,
|
|
@@ -58,10 +166,7 @@ export default defineConfig(
|
|
|
58
166
|
},
|
|
59
167
|
},
|
|
60
168
|
settings: {
|
|
61
|
-
'import/resolver':
|
|
62
|
-
typescript: true,
|
|
63
|
-
node: true,
|
|
64
|
-
},
|
|
169
|
+
'import-x/resolver-next': [createTypeScriptImportResolver(), createNodeResolver()],
|
|
65
170
|
},
|
|
66
171
|
rules: {
|
|
67
172
|
// Eslint (base)
|
|
@@ -145,16 +250,16 @@ export default defineConfig(
|
|
|
145
250
|
],
|
|
146
251
|
|
|
147
252
|
// Eslint Import
|
|
148
|
-
'import/no-unresolved': 'off',
|
|
149
|
-
'import/no-internal-modules': 'off',
|
|
150
|
-
'import/no-relative-parent-imports': 'off',
|
|
151
|
-
'import/no-named-as-default': 'off',
|
|
152
|
-
'import/exports-last': 'off',
|
|
153
|
-
'import/no-namespace': 'off',
|
|
154
|
-
'import/extensions': 'off',
|
|
155
|
-
'import/namespace': 'off',
|
|
156
|
-
'import/default': 'off',
|
|
157
|
-
'import/order': [
|
|
253
|
+
'import-x/no-unresolved': 'off',
|
|
254
|
+
'import-x/no-internal-modules': 'off',
|
|
255
|
+
'import-x/no-relative-parent-imports': 'off',
|
|
256
|
+
'import-x/no-named-as-default': 'off',
|
|
257
|
+
'import-x/exports-last': 'off',
|
|
258
|
+
'import-x/no-namespace': 'off',
|
|
259
|
+
'import-x/extensions': 'off',
|
|
260
|
+
'import-x/namespace': 'off',
|
|
261
|
+
'import-x/default': 'off',
|
|
262
|
+
'import-x/order': [
|
|
158
263
|
'error',
|
|
159
264
|
{
|
|
160
265
|
groups: [
|
|
@@ -179,14 +284,14 @@ export default defineConfig(
|
|
|
179
284
|
},
|
|
180
285
|
},
|
|
181
286
|
],
|
|
182
|
-
'import/prefer-default-export': 'off',
|
|
183
|
-
'import/max-dependencies': 'off',
|
|
184
|
-
'import/no-unassigned-import': 'off',
|
|
185
|
-
'import/no-default-export': 'off',
|
|
186
|
-
'import/no-named-export': 'off',
|
|
187
|
-
'import/group-exports': 'off',
|
|
188
|
-
'import/no-duplicates': ['error', { 'prefer-inline': true }],
|
|
189
|
-
'import/consistent-type-specifier-style': ['error', 'prefer-inline'],
|
|
287
|
+
'import-x/prefer-default-export': 'off',
|
|
288
|
+
'import-x/max-dependencies': 'off',
|
|
289
|
+
'import-x/no-unassigned-import': 'off',
|
|
290
|
+
'import-x/no-default-export': 'off',
|
|
291
|
+
'import-x/no-named-export': 'off',
|
|
292
|
+
'import-x/group-exports': 'off',
|
|
293
|
+
'import-x/no-duplicates': ['error', { 'prefer-inline': true }],
|
|
294
|
+
'import-x/consistent-type-specifier-style': ['error', 'prefer-inline'],
|
|
190
295
|
|
|
191
296
|
// Unicorn
|
|
192
297
|
// Pass error message when throwing errors
|
|
@@ -268,40 +373,6 @@ export default defineConfig(
|
|
|
268
373
|
'@typescript-eslint/no-misused-promises': [2, { checksVoidReturn: { attributes: false } }],
|
|
269
374
|
},
|
|
270
375
|
},
|
|
271
|
-
{
|
|
272
|
-
// JSON files
|
|
273
|
-
files: ['**/*.json'],
|
|
274
|
-
extends: [tseslint.configs.disableTypeChecked],
|
|
275
|
-
rules: {
|
|
276
|
-
quotes: ['error', 'double'],
|
|
277
|
-
'quote-props': ['error', 'always'],
|
|
278
|
-
'comma-dangle': ['error', 'never'],
|
|
279
|
-
semi: 'off',
|
|
280
|
-
'jsonc/array-bracket-spacing': ['error', 'never'],
|
|
281
|
-
'jsonc/comma-dangle': ['error', 'never'],
|
|
282
|
-
'jsonc/comma-style': ['error', 'last'],
|
|
283
|
-
'jsonc/key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
|
284
|
-
'jsonc/no-octal-escape': 'error',
|
|
285
|
-
'jsonc/object-curly-newline': ['error', { multiline: true, consistent: true }],
|
|
286
|
-
'jsonc/object-curly-spacing': ['error', 'always'],
|
|
287
|
-
'jsonc/object-property-newline': ['error', { allowMultiplePropertiesPerLine: true }],
|
|
288
|
-
'jsonc/no-dupe-keys': 'error',
|
|
289
|
-
'jsonc/sort-keys': ['warn', 'asc'],
|
|
290
|
-
},
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
// YAML files rules
|
|
294
|
-
files: ['**/*.yaml', '**/*.yml'],
|
|
295
|
-
extends: [tseslint.configs.disableTypeChecked],
|
|
296
|
-
rules: {
|
|
297
|
-
'spaced-comment': 'off',
|
|
298
|
-
'yml/quotes': ['error', { prefer: 'single', avoidEscape: false }],
|
|
299
|
-
'yml/no-empty-document': 'off',
|
|
300
|
-
'yml/indent': ['error', 4, { indicatorValueIndent: 2 }],
|
|
301
|
-
'@stylistic/spaced-comment': 'off', // False positive in yaml/yml files
|
|
302
|
-
'yml/no-empty-mapping-value': 'off', // Breaks comments on GitHub Actions
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
376
|
{
|
|
306
377
|
// package.json rules
|
|
307
378
|
files: ['**/package.json'],
|
|
@@ -369,7 +440,7 @@ export default defineConfig(
|
|
|
369
440
|
// d.ts files
|
|
370
441
|
files: ['**/*.d.ts'],
|
|
371
442
|
rules: {
|
|
372
|
-
'import/no-duplicates': 'off',
|
|
443
|
+
'import-x/no-duplicates': 'off',
|
|
373
444
|
},
|
|
374
445
|
},
|
|
375
446
|
{
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontify/eslint-config-basic",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.14",
|
|
5
5
|
"author": "Frontify Developers <developers@frontify.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -11,44 +11,46 @@
|
|
|
11
11
|
},
|
|
12
12
|
"main": "eslint.config.mjs",
|
|
13
13
|
"files": [
|
|
14
|
-
"eslint.config.mjs"
|
|
14
|
+
"eslint.config.mjs",
|
|
15
|
+
"rules"
|
|
15
16
|
],
|
|
16
17
|
"peerDependencies": {
|
|
17
18
|
"eslint": "^9.0.0 || ^10.0.0",
|
|
18
19
|
"prettier": "^3.0.0",
|
|
19
|
-
"typescript": "^5.0.0"
|
|
20
|
+
"typescript": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
23
|
"@eslint-community/eslint-plugin-eslint-comments": "^4.7.1",
|
|
23
24
|
"@eslint/js": "^10.0.1",
|
|
24
|
-
"@
|
|
25
|
-
"@
|
|
26
|
-
"@typescript-eslint/
|
|
25
|
+
"@eslint/markdown": "^8.0.0",
|
|
26
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^8.57.2",
|
|
28
|
+
"@typescript-eslint/parser": "^8.57.2",
|
|
27
29
|
"eslint-config-prettier": "^10.1.8",
|
|
28
30
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
29
|
-
"eslint-plugin-import": "^
|
|
30
|
-
"eslint-plugin-jsonc": "^3.1.
|
|
31
|
+
"eslint-plugin-import-x": "^4.16.2",
|
|
32
|
+
"eslint-plugin-jsonc": "^3.1.2",
|
|
31
33
|
"eslint-plugin-lodash": "^8.0.0",
|
|
32
|
-
"eslint-plugin-markdown": "^5.1.0",
|
|
33
34
|
"eslint-plugin-no-only-tests": "^3.3.0",
|
|
34
35
|
"eslint-plugin-prettier": "^5.5.5",
|
|
35
36
|
"eslint-plugin-promise": "^7.2.1",
|
|
36
|
-
"eslint-plugin-unicorn": "^
|
|
37
|
-
"eslint-plugin-yml": "^3.3.
|
|
37
|
+
"eslint-plugin-unicorn": "^64.0.0",
|
|
38
|
+
"eslint-plugin-yml": "^3.3.1",
|
|
38
39
|
"globals": "^17.4.0",
|
|
39
|
-
"typescript-eslint": "^8.
|
|
40
|
+
"typescript-eslint": "^8.57.2",
|
|
40
41
|
"yaml-eslint-parser": "^2.0.0"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"eslint": "^10.0
|
|
44
|
+
"eslint": "^10.1.0",
|
|
44
45
|
"prettier": "^3.8.1",
|
|
45
|
-
"typescript": "^
|
|
46
|
+
"typescript": "^6.0.2"
|
|
46
47
|
},
|
|
47
48
|
"publishConfig": {
|
|
48
49
|
"access": "public"
|
|
49
50
|
},
|
|
50
51
|
"scripts": {
|
|
51
52
|
"lint": "eslint .",
|
|
53
|
+
"test": "node --test 'rules/**/*.test.mjs'",
|
|
52
54
|
"typecheck": "tsc --noEmit"
|
|
53
55
|
}
|
|
54
56
|
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/* (c) Copyright Frontify Ltd., all rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { RuleTester } from 'eslint';
|
|
4
|
+
import eslintPluginImportX from 'eslint-plugin-import-x';
|
|
5
|
+
|
|
6
|
+
import config from '../eslint.config.mjs';
|
|
7
|
+
|
|
8
|
+
const rule = eslintPluginImportX.rules['order'];
|
|
9
|
+
|
|
10
|
+
const importOrderEntry = config.find((c) => c?.rules?.['import-x/order'] !== undefined);
|
|
11
|
+
if (!importOrderEntry) {
|
|
12
|
+
throw new Error("Could not find 'import-x/order' rule in eslint.config.mjs");
|
|
13
|
+
}
|
|
14
|
+
const [, orderOptions] = /** @type {[string, import('eslint-plugin-import-x').RuleOptions['order'][1]]} */ (
|
|
15
|
+
importOrderEntry.rules['import-x/order']
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const ruleTester = new RuleTester({
|
|
19
|
+
languageOptions: {
|
|
20
|
+
ecmaVersion: 'latest',
|
|
21
|
+
sourceType: 'module',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
ruleTester.run('import-x/order', rule, {
|
|
26
|
+
valid: [
|
|
27
|
+
{
|
|
28
|
+
name: 'single external import',
|
|
29
|
+
code: "import foo from 'foo';",
|
|
30
|
+
options: [orderOptions],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'builtin before external with blank line',
|
|
34
|
+
code: ["import path from 'node:path';", '', "import foo from 'foo';"].join('\n'),
|
|
35
|
+
options: [orderOptions],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'external imports alphabetized',
|
|
39
|
+
code: ["import alpha from 'alpha';", "import beta from 'beta';", "import zeta from 'zeta';"].join('\n'),
|
|
40
|
+
options: [orderOptions],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'external before parent with blank line',
|
|
44
|
+
code: ["import foo from 'foo';", '', "import bar from '../bar';"].join('\n'),
|
|
45
|
+
options: [orderOptions],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'external before sibling with blank line',
|
|
49
|
+
code: ["import foo from 'foo';", '', "import bar from './bar';"].join('\n'),
|
|
50
|
+
options: [orderOptions],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'all groups in order with blank lines between each',
|
|
54
|
+
code: [
|
|
55
|
+
"import path from 'node:path';",
|
|
56
|
+
'',
|
|
57
|
+
"import foo from 'foo';",
|
|
58
|
+
'',
|
|
59
|
+
"import bar from '../bar';",
|
|
60
|
+
'',
|
|
61
|
+
"import baz from './baz';",
|
|
62
|
+
].join('\n'),
|
|
63
|
+
options: [orderOptions],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'scoped externals alphabetized (@frontify before @tanstack)',
|
|
67
|
+
code: [
|
|
68
|
+
"import { Button } from '@frontify/fondue';",
|
|
69
|
+
"import { useQuery } from '@tanstack/react-query';",
|
|
70
|
+
].join('\n'),
|
|
71
|
+
options: [orderOptions],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'realistic file: node builtin, scoped externals, deep parent, sibling',
|
|
75
|
+
code: [
|
|
76
|
+
"import path from 'node:path';",
|
|
77
|
+
'',
|
|
78
|
+
"import { Button } from '@frontify/fondue';",
|
|
79
|
+
"import { useQuery } from '@tanstack/react-query';",
|
|
80
|
+
'',
|
|
81
|
+
"import { formatDate } from '../../../utils/date';",
|
|
82
|
+
"import { getUser } from '../../services/user';",
|
|
83
|
+
'',
|
|
84
|
+
"import { MyComponent } from './MyComponent';",
|
|
85
|
+
"import styles from './styles.module.css';",
|
|
86
|
+
].join('\n'),
|
|
87
|
+
options: [orderOptions],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'multiple sibling imports alphabetized',
|
|
91
|
+
code: [
|
|
92
|
+
"import { Avatar } from './Avatar';",
|
|
93
|
+
"import { Button } from './Button';",
|
|
94
|
+
"import { Input } from './Input';",
|
|
95
|
+
].join('\n'),
|
|
96
|
+
options: [orderOptions],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'deep parent imports (../../../) are treated as parent group',
|
|
100
|
+
code: [
|
|
101
|
+
"import { useQuery } from '@tanstack/react-query';",
|
|
102
|
+
'',
|
|
103
|
+
"import { formatDate } from '../../../utils/date';",
|
|
104
|
+
].join('\n'),
|
|
105
|
+
options: [orderOptions],
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
invalid: [
|
|
109
|
+
{
|
|
110
|
+
name: 'missing blank line between builtin and external',
|
|
111
|
+
code: ["import path from 'node:path';", "import foo from 'foo';"].join('\n'),
|
|
112
|
+
output: ["import path from 'node:path';", '', "import foo from 'foo';"].join('\n'),
|
|
113
|
+
options: [orderOptions],
|
|
114
|
+
errors: [{ messageId: 'oneLineBetweenGroups' }],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'missing blank line between external and parent',
|
|
118
|
+
code: ["import foo from 'foo';", "import bar from '../bar';"].join('\n'),
|
|
119
|
+
output: ["import foo from 'foo';", '', "import bar from '../bar';"].join('\n'),
|
|
120
|
+
options: [orderOptions],
|
|
121
|
+
errors: [{ messageId: 'oneLineBetweenGroups' }],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'missing blank line between external and sibling',
|
|
125
|
+
code: ["import foo from 'foo';", "import bar from './bar';"].join('\n'),
|
|
126
|
+
output: ["import foo from 'foo';", '', "import bar from './bar';"].join('\n'),
|
|
127
|
+
options: [orderOptions],
|
|
128
|
+
errors: [{ messageId: 'oneLineBetweenGroups' }],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'external imports not alphabetized',
|
|
132
|
+
code: ["import zeta from 'zeta';", "import alpha from 'alpha';"].join('\n'),
|
|
133
|
+
output: ["import alpha from 'alpha';", "import zeta from 'zeta';", ''].join('\n'),
|
|
134
|
+
options: [orderOptions],
|
|
135
|
+
errors: [{ messageId: 'order' }],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'external before builtin (wrong group order)',
|
|
139
|
+
code: ["import foo from 'foo';", '', "import path from 'node:path';"].join('\n'),
|
|
140
|
+
// One-pass fix swaps the imports; the blank line between groups is added by a second pass.
|
|
141
|
+
output: "import path from 'node:path';\nimport foo from 'foo';\n\n",
|
|
142
|
+
options: [orderOptions],
|
|
143
|
+
errors: [{ messageId: 'order' }],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'sibling before parent (wrong group order)',
|
|
147
|
+
code: ["import baz from './baz';", '', "import bar from '../bar';"].join('\n'),
|
|
148
|
+
// One-pass fix swaps the imports; the blank line between groups is added by a second pass.
|
|
149
|
+
output: "import bar from '../bar';\nimport baz from './baz';\n\n",
|
|
150
|
+
options: [orderOptions],
|
|
151
|
+
errors: [{ messageId: 'order' }],
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'extra blank line within the same group',
|
|
155
|
+
code: ["import alpha from 'alpha';", '', "import beta from 'beta';"].join('\n'),
|
|
156
|
+
output: ["import alpha from 'alpha';", "import beta from 'beta';"].join('\n'),
|
|
157
|
+
options: [orderOptions],
|
|
158
|
+
errors: [{ messageId: 'noLineWithinGroup' }],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: '@tanstack before @frontify (wrong alphabetical order)',
|
|
162
|
+
code: [
|
|
163
|
+
"import { useQuery } from '@tanstack/react-query';",
|
|
164
|
+
"import { Button } from '@frontify/fondue';",
|
|
165
|
+
].join('\n'),
|
|
166
|
+
output: [
|
|
167
|
+
"import { Button } from '@frontify/fondue';",
|
|
168
|
+
"import { useQuery } from '@tanstack/react-query';",
|
|
169
|
+
'',
|
|
170
|
+
].join('\n'),
|
|
171
|
+
options: [orderOptions],
|
|
172
|
+
errors: [{ messageId: 'order' }],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'missing blank line between scoped external and deep parent',
|
|
176
|
+
code: ["import { Button } from '@frontify/fondue';", "import utils from '../../../utils';"].join('\n'),
|
|
177
|
+
output: ["import { Button } from '@frontify/fondue';", '', "import utils from '../../../utils';"].join(
|
|
178
|
+
'\n',
|
|
179
|
+
),
|
|
180
|
+
options: [orderOptions],
|
|
181
|
+
errors: [{ messageId: 'oneLineBetweenGroups' }],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'sibling (./Button) before deep parent (../../../utils) — wrong group order',
|
|
185
|
+
code: ["import Button from './Button';", '', "import utils from '../../../utils';"].join('\n'),
|
|
186
|
+
// One-pass fix swaps the imports; the blank line between groups is added by a second pass.
|
|
187
|
+
output: "import utils from '../../../utils';\nimport Button from './Button';\n\n",
|
|
188
|
+
options: [orderOptions],
|
|
189
|
+
errors: [{ messageId: 'order' }],
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
});
|