@mikey-pro/eslint-config-vue 8.0.3 → 9.0.0

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.
Files changed (5) hide show
  1. package/README.md +18 -3
  2. package/flat.js +3 -0
  3. package/index.js +147 -307
  4. package/legacy.js +64 -0
  5. package/package.json +36 -21
package/README.md CHANGED
@@ -18,14 +18,29 @@ A preset ESLint Vue configuration
18
18
  npm i -D mikey-pro @mikey-pro/eslint-config-vue
19
19
  ```
20
20
 
21
- ### Configure
21
+ ### Configure (Flat ESLint v9+ Default)
22
22
 
23
- Extend to ESLint in `package.json`:
23
+ ```js
24
+ // eslint.config.js
25
+ import vueConfig from '@mikey-pro/eslint-config-vue';
26
+
27
+ export default vueConfig;
28
+ ```
29
+
30
+ ### Legacy (eslintrc) Configuration (still supported)
31
+
32
+ Legacy consumers should explicitly import the legacy entry:
24
33
 
25
34
  ```json
26
35
  {
27
36
  "eslintConfig": {
28
- "extends": ["@mikey-pro/eslint-config-vue"]
37
+ "extends": ["@mikey-pro/eslint-config-vue/legacy"]
29
38
  }
30
39
  }
31
40
  ```
41
+
42
+ Or via direct require:
43
+
44
+ ```js
45
+ module.exports = require('@mikey-pro/eslint-config-vue/legacy');
46
+ ```
package/flat.js ADDED
@@ -0,0 +1,3 @@
1
+ // Modern Vue ESLint 9 Flat Configuration for Mikey Pro
2
+
3
+ export { default } from './index.js';
package/index.js CHANGED
@@ -1,316 +1,156 @@
1
- import baseConfig from '@mikey-pro/eslint-config';
2
- import vuePlugin from 'eslint-plugin-vue';
3
- import vueRecommended from 'eslint-plugin-vue/lib/configs/vue3-recommended.js';
4
-
5
- const vueConfig = [
6
- ...baseConfig,
7
- {
8
- files: ['*.vue'],
9
- languageOptions: {
10
- parser: 'vue-eslint-parser',
11
- parserOptions: {
12
- parser: {
13
- js: '@babel/eslint-parser',
14
- ts: '@typescript-eslint/parser',
15
- '<template>': 'espree'
16
- },
17
- babelOptions: {
18
- plugins: [
19
- [
20
- '@babel/plugin-transform-react-jsx',
21
- {
22
- pragma: 'h',
23
- pragmaFrag: 'Fragment',
24
- runtime: 'automatic',
25
- },
26
- ],
27
- ],
28
- presets: [
29
- [
30
- '@babel/preset-env',
31
- {
32
- targets: {
33
- node: 'current',
34
- },
35
- },
36
- ],
37
- ],
38
- },
39
- ecmaVersion: 'latest',
40
- requireConfigFile: false,
41
- sourceType: 'module',
42
- extraFileExtensions: ['.vue'],
43
- vueFeatures: {
44
- filter: true,
45
- interpolationAsNonHTML: false,
46
- styleCSSVariableInjection: true
47
- }
1
+ // Modern Vue ESLint configuration for Mikey Pro
2
+ import { baseConfig } from '@mikey-pro/eslint-config/base-config.js';
3
+ import { baseOverrides } from '@mikey-pro/eslint-config/overrides.js';
4
+ import vue from 'eslint-plugin-vue';
5
+ import vueParser from 'vue-eslint-parser';
6
+
7
+ // Vue-specific configuration
8
+ const vueConfig = {
9
+ files: ['**/*.vue'],
10
+ languageOptions: {
11
+ parser: vueParser,
12
+ parserOptions: {
13
+ ecmaFeatures: {
14
+ jsx: true,
48
15
  },
16
+ ecmaVersion: 'latest',
17
+ sourceType: 'module',
49
18
  },
50
- plugins: {
51
- vue: vuePlugin,
52
- },
53
- rules: {
54
- ...vueRecommended.rules,
55
-
56
- // Vue 3 Composition API
57
- 'vue/define-macros-order': ['warn', {
58
- order: ['defineProps', 'defineEmits']
59
- }],
60
- 'vue/define-props-declaration': ['error', 'type-based'],
61
- 'vue/component-api-style': ['error', ['script-setup', 'composition']],
62
- 'vue/no-ref-object-destructure': 'error',
63
- 'vue/no-setup-props-destructure': 'error',
64
-
65
- // Best Practices
66
- 'vue/block-order': ['error', {
67
- order: ['script', 'template', 'style']
68
- }],
69
- 'vue/component-name-in-template-casing': ['error', 'PascalCase'],
70
- 'vue/no-duplicate-attr-inheritance': 'error',
71
- 'vue/no-useless-v-bind': 'error',
72
- 'vue/prefer-separate-static-class': 'error',
73
-
74
- // Performance
75
- 'vue/no-async-in-computed-properties': 'error',
76
- 'vue/no-side-effects-in-computed-properties': 'error',
77
- 'vue/no-static-inline-styles': 'warn',
78
-
79
- // Type Safety
80
- 'vue/component-definition-name-casing': ['error', 'PascalCase'],
81
- 'vue/match-component-file-name': ['error', {
82
- extensions: ['vue'],
83
- shouldMatchCase: true
84
- }],
85
- 'vue/component-options-name-casing': ['error', 'PascalCase'],
86
-
87
- // Best Practices
88
- 'vue/prefer-import-from-vue': 'error',
89
- 'vue/no-unused-refs': 'warn',
90
- 'vue/no-required-prop-with-default': 'error',
91
- 'vue/require-typed-ref': 'error',
92
- 'vue/valid-define-props': 'error',
93
-
94
- // Existing rules...
95
- 'vue/component-tags-order': [
96
- 'warn',
97
- {
98
- order: [['script', 'template'], 'style'],
99
- },
100
- ],
101
- 'vue/html-self-closing': [
102
- 'warn',
103
- {
104
- html: {
105
- component: 'always',
106
- normal: 'always',
107
- void: 'always',
108
- },
109
- math: 'always',
110
- svg: 'always',
19
+ },
20
+ plugins: {
21
+ vue,
22
+ },
23
+ rules: {
24
+ // Vue rules
25
+ ...vue.configs['vue3-recommended'].rules,
26
+
27
+ // Basic Vue-specific overrides
28
+ 'vue/attribute-hyphenation': ['error', 'always'],
29
+ 'vue/component-definition-name-casing': ['error', 'PascalCase'],
30
+ 'vue/component-name-in-template-casing': ['error', 'PascalCase'],
31
+ 'vue/custom-event-name-casing': ['error', 'camelCase'],
32
+ 'vue/define-emits-declaration': ['error', 'type-based'],
33
+ 'vue/define-props-declaration': ['error', 'type-based'],
34
+ 'vue/html-button-has-type': 'error',
35
+ 'vue/html-indent': ['error', 2],
36
+ 'vue/html-self-closing': [
37
+ 'error',
38
+ {
39
+ html: {
40
+ component: 'always',
41
+ normal: 'always',
42
+ void: 'always',
111
43
  },
112
- ],
113
- 'vue/singleline-html-element-content-newline': 'off',
114
- 'prettier/prettier': ['warn', { parser: 'vue' }],
115
-
116
- // Enhanced Type Safety
117
- 'vue/define-emits-declaration': ['error', 'type-based'],
118
- 'vue/no-export-in-script-setup': 'error',
119
-
120
- // Script Setup
121
- 'vue/script-setup-uses-vars': 'error',
122
- 'vue/no-undef-components': ['error', {
123
- ignorePatterns: ['router-view', 'router-link'],
124
- }],
125
-
126
- // Performance
127
- 'vue/no-unused-properties': ['warn', {
128
- groups: ['props', 'data', 'computed', 'methods', 'setup'],
129
- }],
130
-
131
- // Composables
132
- 'vue/define-macros-order': ['error', {
133
- order: [
134
- 'defineOptions',
135
- 'defineProps',
136
- 'defineEmits',
137
- 'defineSlots',
138
- 'withDefaults'
139
- ]
140
- }],
141
- 'vue/custom-event-name-casing': ['error', 'camelCase', {
142
- ignore: []
143
- }],
144
-
145
- // Security
146
- 'vue/no-unescaped-html': 'error',
147
- 'vue/no-potential-component-option-typo': 'error',
148
- 'vue/no-restricted-component-options': ['error', {
149
- name: 'data',
150
- message: 'Use setup() instead'
151
- }],
152
- 'vue/no-restricted-v-bind': ['error', '/^v-/'],
153
- 'vue/no-restricted-html-elements': [
154
- 'error',
155
- 'iframe',
156
- 'object',
157
- 'embed'
158
- ],
159
- 'vue/no-use-v-if-with-v-for': ['error', {
160
- allowUsingIterationVar: false
161
- }],
162
-
163
- // Optimization
164
- 'vue/no-unused-properties': ['error', {
165
- groups: ['props', 'data', 'computed', 'methods', 'setup'],
166
- deepData: true
167
- }],
168
- 'vue/no-unused-refs': 'error',
169
- 'vue/valid-next-tick': 'error',
170
-
171
- // Performance
172
- 'vue/no-parsing-error': ['error', {
173
- 'invalid-first-character-of-tag-name': false
174
- }],
175
- 'vue/no-unused-components': ['error', {
176
- ignoreWhenBindingPresent: true
177
- }],
178
- 'vue/valid-v-slot': ['error', {
179
- allowModifiers: true
180
- }],
181
-
182
- // Advanced Vue 3 Features
183
- 'vue/prefer-true-attribute-shorthand': ['error', 'always'],
184
- 'vue/v-on-function-call': ['error', 'never'],
185
- 'vue/no-empty-component-block': 'error',
186
- 'vue/multi-word-component-names': ['error', {
187
- ignores: ['index', 'default']
188
- }],
189
-
190
- // Performance Optimizations
191
- 'vue/no-deprecated-filter': 'error',
192
- 'vue/prefer-template': 'error',
193
- 'vue/no-useless-mustaches': 'error',
194
- 'vue/no-empty-pattern': 'error',
195
-
196
- // Type Safety
197
- 'vue/block-tag-newline': ['error', {
198
- singleline: 'always',
199
- multiline: 'always',
200
- maxEmptyLines: 1
201
- }],
202
-
203
- // Enhanced Performance
204
- 'vue/no-child-content': ['error', {
205
- 'ignoreSlots': ['default']
206
- }],
207
- 'vue/no-duplicate-attr-inheritance': ['error', {
208
- 'ignoreKeys': ['class', 'style']
209
- }],
210
- 'vue/prefer-define-options': 'error',
211
-
212
- // Better Type Safety
213
- 'vue/define-props-declaration': ['error', {
214
- 'type-based': true,
215
- 'required': true
216
- }],
217
- 'vue/define-emits-declaration': ['error', {
218
- 'type-based': true
219
- }],
220
-
221
- // Component Organization
222
- 'vue/block-order': ['error', {
223
- 'order': [
224
- 'script:not([setup])',
225
- 'script[setup]',
226
- 'template',
227
- 'style:not([scoped])',
228
- 'style[scoped]'
229
- ]
230
- }],
231
-
232
- // Vue 3.4+ Features
233
- 'vue/next-tick-style': ['error', 'promise'],
234
- 'vue/prefer-prop-type-inference': 'error',
235
- 'vue/require-typed-object-prop': 'error',
236
- 'vue/no-deprecated-model-definition': ['error', {
237
- allowVue3Compat: false
238
- }],
239
-
240
- // Memory Optimization
241
- 'vue/no-ref-as-operand': 'error',
242
- 'vue/prefer-import-from-vue': ['error', {
243
- disallowVuePath: true
244
- }],
245
- 'vue/no-side-effects-in-computed-properties': ['error', {
246
- enforceForEventHandlers: true
247
- }],
248
-
249
- // Strict Mode Features
250
- 'vue/component-name-in-template-casing': ['error', 'PascalCase', {
251
- registeredComponentsOnly: true,
252
- ignores: []
253
- }],
254
- 'vue/define-macros-order': ['error', {
255
- order: [
256
- 'defineOptions',
257
- 'defineSlots',
258
- 'defineEmits',
259
- 'defineModel',
260
- 'defineProps'
261
- ]
262
- }],
44
+ math: 'always',
45
+ svg: 'always',
46
+ },
47
+ ],
48
+ 'vue/max-attributes-per-line': [
49
+ 'error',
50
+ {
51
+ multiline: { max: 1 },
52
+ singleline: { max: 1 },
53
+ },
54
+ ],
55
+ 'vue/multi-word-component-names': 'error',
56
+ 'vue/mustache-interpolation-spacing': ['error', 'always'],
57
+ 'vue/no-multiple-template-root': 'off', // Vue 3 allows multiple roots
58
+ 'vue/no-mutating-props': 'error',
59
+ 'vue/no-unused-components': 'error',
60
+ 'vue/no-unused-vars': 'error',
61
+ 'vue/no-use-v-if-with-v-for': 'error',
62
+ 'vue/no-v-html': 'error',
63
+ 'vue/no-v-model-argument': 'off', // Vue 3 supports v-model arguments
64
+ 'vue/object-curly-spacing': ['error', 'always'],
65
+ 'vue/require-component-is': 'error',
66
+ 'vue/require-default-prop': 'error',
67
+ 'vue/require-prop-types': 'error',
68
+ 'vue/require-v-for-key': 'error',
69
+ 'vue/script-indent': ['error', 2, { baseIndent: 0 }],
70
+ 'vue/this-in-template': 'error',
71
+ 'vue/use-v-on-exact': 'error',
72
+ 'vue/v-for-delimiter-style': ['error', 'in'],
73
+ 'vue/v-on-function-call': 'error',
74
+ 'vue/valid-define-emits': 'error',
75
+ 'vue/valid-define-props': 'error',
76
+ 'vue/valid-next-tick': 'error',
77
+ 'vue/valid-template-root': 'off', // Vue 3 allows multiple roots
78
+ 'vue/valid-v-bind-sync': 'error',
79
+ 'vue/valid-v-cloak': 'error',
80
+ 'vue/valid-v-else': 'error',
81
+ 'vue/valid-v-else-if': 'error',
82
+ 'vue/valid-v-for': 'error',
83
+ 'vue/valid-v-html': 'error',
84
+ 'vue/valid-v-if': 'error',
85
+ 'vue/valid-v-is': 'error',
86
+ 'vue/valid-v-model': 'error',
87
+ 'vue/valid-v-on': 'error',
88
+ 'vue/valid-v-once': 'error',
89
+ 'vue/valid-v-pre': 'error',
90
+ 'vue/valid-v-show': 'error',
91
+ 'vue/valid-v-slot': 'error',
92
+ 'vue/valid-v-text': 'error',
93
+ },
94
+ settings: {
95
+ 'import/resolver': {
96
+ typescript: {
97
+ alwaysTryTypes: true,
98
+ },
99
+ },
100
+ },
101
+ };
263
102
 
264
- // Performance Optimizations
265
- 'vue/no-static-style': 'error',
266
- 'vue/no-undef-properties': ['error', {
267
- ignores: ['/^\\$/']
268
- }],
269
- 'vue/valid-define-options': 'error',
103
+ // Export the complete Vue configuration
104
+ export default [
105
+ // Global ignores
106
+ {
107
+ ignores: [
108
+ '**/dist/**/*',
109
+ '**/vendor/**/*',
110
+ '**/node_modules/**/*',
111
+ '**/coverage/**/*',
112
+ '**/.next/**/*',
113
+ '**/.nuxt/**/*',
114
+ '**/.output/**/*',
115
+ '**/.vite/**/*',
116
+ '**/build/**/*',
117
+ '**/out/**/*',
118
+ '*.properties',
119
+ '*.cclibs',
120
+ '*.svg',
121
+ '*.png',
122
+ '*.jpg',
123
+ '*.jpeg',
124
+ '*.gif',
125
+ '*.ico',
126
+ '*.webp',
127
+ '*.aco',
128
+ '*.psd',
129
+ '*.ai',
130
+ '*.ase',
131
+ '*.sh',
132
+ '*.bat',
133
+ '*.cmd',
134
+ 'package-lock.json',
135
+ 'yarn.lock',
136
+ 'pnpm-lock.yaml',
137
+ 'LICENSE',
138
+ 'CNAME',
139
+ '*.min.js',
140
+ '*.min.css',
141
+ ],
142
+ },
270
143
 
271
- // Script Setup Safety
272
- 'vue/script-setup-uses-vars': ['error', {
273
- checkTemplate: true
274
- }],
275
- 'vue/valid-define-options': ['error', {
276
- presetName: 'script-setup'
277
- }],
144
+ // Base configuration
145
+ baseConfig,
278
146
 
279
- // Performance Optimizations
280
- 'vue/prefer-define-options': ['error', {
281
- presets: ['composition-api', 'script-setup']
282
- }],
283
- 'vue/no-duplicate-attr-inheritance': ['error', {
284
- ignoreKeys: ['class', 'style', 'key', 'ref']
285
- }],
147
+ // Vue-specific configuration
148
+ vueConfig,
286
149
 
287
- // Type Safety
288
- 'vue/no-setup-props-reactivity-loss': 'error',
289
- 'vue/no-watch-after-await': 'error',
290
- 'vue/prefer-prop-type-inference': ['error', {
291
- allowAny: false
292
- }]
293
- },
294
- settings: {
295
- ...baseConfig.settings,
296
- 'import/resolver': {
297
- typescript: {
298
- alwaysTryTypes: true,
299
- project: ['./tsconfig.json', './tsconfig.*.json']
300
- }
301
- },
302
- 'import/core-modules': ['vue', '@vue/composition-api', '@vue/runtime-core', '@vue/runtime-dom'],
303
- 'import/parsers': {
304
- '@typescript-eslint/parser': ['.ts', '.tsx', '.vue', '.d.ts'],
305
- },
306
- 'vue': {
307
- version: '3.4'
308
- }
309
- }
310
- },
150
+ // File-specific overrides
151
+ ...baseOverrides,
311
152
  ];
312
153
 
313
- export default vueConfig;
314
- if (typeof module !== 'undefined' && module.exports) {
315
- module.exports = vueConfig;
316
- }
154
+ // Export individual components for advanced usage
155
+ export { baseConfig } from '@mikey-pro/eslint-config/base-config.js';
156
+ export { baseOverrides } from '@mikey-pro/eslint-config/overrides.js';
package/legacy.js ADDED
@@ -0,0 +1,64 @@
1
+ // Legacy (eslintrc-style) config preserved under /legacy entry point.
2
+ const baseConfig = require('@mikey-pro/eslint-config');
3
+
4
+ module.exports = {
5
+ ...baseConfig,
6
+ overrides: [
7
+ ...baseConfig.overrides,
8
+ {
9
+ extends: ['plugin:vue/vue3-recommended'],
10
+ files: ['*.vue'],
11
+ parser: 'vue-eslint-parser',
12
+ parserOptions: {
13
+ babelOptions: {
14
+ plugins: [
15
+ 'eslint-plugin-vue',
16
+ [
17
+ '@babel/plugin-transform-react-jsx',
18
+ {
19
+ pragma: 'h',
20
+ pragmaFrag: 'Fragment',
21
+ runtime: 'automatic',
22
+ },
23
+ ],
24
+ ],
25
+ presets: [
26
+ [
27
+ '@babel/preset-env',
28
+ {
29
+ targets: {
30
+ node: 'current',
31
+ },
32
+ },
33
+ ],
34
+ ],
35
+ },
36
+ ecmaVersion: 'latest',
37
+ requireConfigFile: false,
38
+ sourceType: 'module',
39
+ },
40
+ rules: {
41
+ 'vue/component-tags-order': [
42
+ 'warn',
43
+ {
44
+ order: [['script', 'template'], 'style'],
45
+ },
46
+ ],
47
+ 'vue/html-self-closing': [
48
+ 'warn',
49
+ {
50
+ html: {
51
+ component: 'always',
52
+ normal: 'always',
53
+ void: 'always',
54
+ },
55
+ math: 'always',
56
+ svg: 'always',
57
+ },
58
+ ],
59
+ 'vue/singleline-html-element-content-newline': 'off',
60
+ 'prettier/prettier': ['warn', { parser: 'vue' }],
61
+ },
62
+ },
63
+ ],
64
+ };
package/package.json CHANGED
@@ -1,27 +1,21 @@
1
1
  {
2
2
  "name": "@mikey-pro/eslint-config-vue",
3
- "version": "8.0.3",
4
- "description": "Mikey Pro ESLint Vue configuration",
3
+ "version": "9.0.0",
4
+ "description": "Mikey Pro ESLint Vue configuration - Ultimate Vue coding style guide",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "dependencies": {
8
- "@babel/core": "^7.26.7",
9
- "@babel/eslint-parser": "^7.26.5",
10
- "@babel/plugin-transform-react-jsx": "^7.25.9",
11
- "@babel/preset-env": "^7.26.7",
12
- "@mikey-pro/eslint-config": "^8.0.3",
13
- "@typescript-eslint/parser": "^8.22.0",
14
- "eslint-plugin-vue": "^9.32.0",
15
- "vue-eslint-parser": "^9.4.3"
8
+ "@babel/core": "^7.28.4",
9
+ "@babel/eslint-parser": "^7.28.4",
10
+ "@babel/plugin-transform-react-jsx": "^7.28.0",
11
+ "@babel/preset-env": "^7.28.3",
12
+ "@mikey-pro/eslint-config": "^9.0.0",
13
+ "@typescript-eslint/parser": "^8.43.0",
14
+ "eslint-plugin-vue": "^10.4.0",
15
+ "vue-eslint-parser": "^10.2.0"
16
16
  },
17
17
  "peerDependencies": {
18
- "@mikey-pro/eslint-config": "^8.0.3",
19
- "@types/node": ">=18.0.0",
20
- "@vue/compiler-sfc": "^3.4.0",
21
- "eslint": "^8.57.0",
22
- "prettier": "^3.2.0",
23
- "typescript": ">=5.0.0",
24
- "vue": "^3.4.0"
18
+ "@mikey-pro/eslint-config": "^9.0.0"
25
19
  },
26
20
  "peerDependenciesMeta": {
27
21
  "@types/node": {
@@ -33,6 +27,8 @@
33
27
  },
34
28
  "files": [
35
29
  "index.js",
30
+ "flat.js",
31
+ "legacy.js",
36
32
  "README.md",
37
33
  "LICENSE"
38
34
  ],
@@ -46,7 +42,7 @@
46
42
  },
47
43
  "license": "MIT",
48
44
  "keywords": [
49
- "@mikey-pro/eslint-config",
45
+ "@mikey-pro/eslint-config-vue",
50
46
  "@mikey-pro",
51
47
  "mikey-pro",
52
48
  "mikey",
@@ -55,16 +51,35 @@
55
51
  "eslintconfig",
56
52
  "eslint",
57
53
  "config",
54
+ "vue",
58
55
  "style-guide",
59
56
  "style",
60
57
  "guide",
61
- "vue"
58
+ "eslint-9",
59
+ "flat-config"
62
60
  ],
63
61
  "author": "Mikl Wolfe <wolfe@mikl.io> (https://mikl.io)",
64
62
  "engines": {
65
- "node": ">=18.0.0"
63
+ "node": ">=18.0.0",
64
+ "npm": ">=9.0.0",
65
+ "pnpm": ">=8.0.0",
66
+ "yarn": ">=4.0.0"
66
67
  },
67
68
  "browserslist": [
68
69
  "defaults"
69
- ]
70
+ ],
71
+ "exports": {
72
+ ".": {
73
+ "import": "./index.js",
74
+ "require": "./index.js"
75
+ },
76
+ "./flat": {
77
+ "import": "./flat.js",
78
+ "require": "./flat.js"
79
+ },
80
+ "./legacy": {
81
+ "import": "./legacy.js",
82
+ "require": "./legacy.js"
83
+ }
84
+ }
70
85
  }