Skip to content

Commit 1d657db

Browse files
committed
feat!: initial flat config support
BREAKING CHANGES: - Dropped legacy `.eslintrc*` support - Dropped support for Vue 2 - Airbnb and Standard style guides are not yet supported It satisfies the use case for basic JavaScript / TypeScript projects with Vue 3. The formatting of `additionalConfigs` still needs to be improved to be able to be used in `create-vue`. The CLI functionality is too basic and needs to be improved.
1 parent 9df38b7 commit 1d657db

13 files changed

+1196
-3525
lines changed

.editorconfig

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}]
2+
charset = utf-8
3+
indent_size = 2
4+
indent_style = space
5+
insert_final_newline = true
6+
trim_trailing_whitespace = true

.prettierrc.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "https://json.schemastore.org/prettierrc",
3+
"semi": false,
4+
"singleQuote": true,
5+
"arrowParens": "avoid"
6+
}

bin/create-eslint-config.js

+58-153
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import path from 'node:path'
55
import process from 'node:process'
66
import { bold, blue, yellow, red, green, dim } from 'kolorist'
77

8-
import createConfig, { deepMerge, CREATE_ALIAS_SETTING_PLACEHOLDER } from '../index.js'
8+
import createConfig, { deepMerge } from '../index.js'
99

1010
const require = createRequire(import.meta.url)
1111
const Enquirer = require('enquirer')
@@ -43,6 +43,7 @@ const pkg = JSON.parse(rawPkgJson)
4343

4444
// 1. check for existing config files
4545
// `.eslintrc.*`, `eslintConfig` in `package.json`
46+
// FIXME: `eslint.config.*`
4647
// ask if wanna overwrite?
4748

4849
// https://eslint.org/docs/latest/user-guide/configuring/configuration-files#configuration-file-formats
@@ -89,55 +90,20 @@ if (pkg.eslintConfig) {
8990
}
9091

9192
// 2. Check Vue
92-
// Not detected? Choose from Vue 2 or 3
93-
// TODO: better support for 2.7 and vue-demi
9493
let vueVersion
94+
// Not detected? Abort
95+
// Vue 2? Abort because this tool only supports Vue 3
9596
try {
9697
vueVersion = requireInCwd('vue/package.json').version
9798
console.info(dim(`Detected Vue.js version: ${vueVersion}`))
9899
} catch (e) {
99-
const anwsers = await prompt({
100-
type: 'select',
101-
name: 'vueVersion',
102-
message: 'Which Vue.js version do you use in the project?',
103-
choices: [
104-
'3.x',
105-
'2.x'
106-
]
107-
})
108-
vueVersion = anwsers.vueVersion
100+
// FIXME: warning that this only support Vue 3
109101
}
110102

111-
// 3. Choose a style guide
112-
// - Error Prevention (ESLint Recommended)
113-
// - Standard
114-
// - Airbnb
115-
const { styleGuide } = await prompt({
116-
type: 'select',
117-
name: 'styleGuide',
118-
message: 'Which style guide do you want to follow?',
119-
choices: [
120-
{
121-
name: 'default',
122-
message: 'ESLint Recommended (Error-Prevention-Only)'
123-
},
124-
{
125-
name: 'airbnb',
126-
message: `Airbnb ${dim('(https://airbnb.io/javascript/)')}`
127-
},
128-
{
129-
name: 'standard',
130-
message: `Standard ${dim('(https://standardjs.com/)')}`
131-
}
132-
]
133-
})
134-
135103
// 4. Check TypeScript
136104
// 4.1 Allow JS?
137-
// 4.2 Allow JS in Vue?
138-
// 4.3 Allow JSX (TSX, if answered no in 4.1) in Vue?
105+
// 4.2 Allow JS in Vue? Allow JSX (TSX, if answered no in 4.1) in Vue?
139106
let hasTypeScript = false
140-
const additionalConfig = {}
141107
try {
142108
const tsVersion = requireInCwd('typescript/package.json').version
143109
console.info(dim(`Detected TypeScript version: ${tsVersion}`))
@@ -154,108 +120,54 @@ try {
154120
hasTypeScript = anwsers.hasTypeScript
155121
}
156122

157-
// TODO: we don't have more fine-grained sub-rulsets in `@vue/eslint-config-typescript` yet
158-
if (hasTypeScript && styleGuide !== 'default') {
159-
const { allowJsInVue } = await prompt({
160-
type: 'toggle',
161-
disabled: 'No',
162-
enabled: 'Yes',
163-
name: 'allowJsInVue',
164-
message: `Do you use plain ${yellow('<script>')}s (without ${blue('lang="ts"')}) in ${green('.vue')} files?`,
165-
initial: false
166-
})
167-
168-
if (allowJsInVue) {
169-
const { allowJsxInVue } = await prompt({
170-
type: 'toggle',
171-
disabled: 'No',
172-
enabled: 'Yes',
173-
name: 'allowJsxInVue',
174-
message: `Do you use ${yellow('<script lang="jsx">')}s in ${green('.vue')} files (not recommended)?`,
175-
initial: false
176-
})
177-
178-
additionalConfig.extends = [
179-
`@vue/eslint-config-${styleGuide}-with-typescript/${
180-
allowJsxInVue
181-
? 'allow-jsx-in-vue'
182-
: 'allow-js-in-vue'
183-
}`
184-
]
185-
} else {
186-
const { allowTsxInVue } = await prompt({
187-
type: 'toggle',
188-
disabled: 'No',
189-
enabled: 'Yes',
190-
name: 'allowTsxInVue',
191-
message: `Do you use ${yellow('<script lang="tsx">')}s in ${green('.vue')} files (not recommended)?`,
192-
initial: false
193-
})
194-
195-
if (allowTsxInVue) {
196-
additionalConfig.extends = [
197-
`@vue/eslint-config-${styleGuide}-with-typescript/allow-tsx-in-vue`
198-
]
199-
}
200-
}
201-
}
202-
203-
// 5. If Airbnb && !TypeScript
204-
// Does your project use any path aliases?
205-
// Show [snippet prompts](https://github.com/enquirer/enquirer#snippet-prompt) for the user to input aliases
206-
if (styleGuide === 'airbnb' && !hasTypeScript) {
207-
const { hasAlias } = await prompt({
208-
type: 'toggle',
209-
disabled: 'No',
210-
enabled: 'Yes',
211-
name: 'hasAlias',
212-
message: 'Does your project use any path aliases?',
213-
initial: false
214-
})
215-
216-
if (hasAlias) {
217-
console.info()
218-
console.info(`Please input your alias configurations (press ${bold(green('<Enter>'))} to skip):`)
219-
220-
const aliases = {}
221-
while (true) {
222-
console.info()
223-
224-
const { prefix } = await prompt({
225-
type: 'input',
226-
name: 'prefix',
227-
message: 'Alias prefix',
228-
validate: (val) => {
229-
if (Object.hasOwn(aliases, val)) {
230-
return red(`${green(val)} has already been aliased to ${green(aliases[val])}`)
231-
}
232-
233-
return true
234-
}
235-
})
236-
237-
if (!prefix) {
238-
break
239-
}
240-
241-
const { replacement } = await prompt({
242-
type: 'input',
243-
name: 'replacement',
244-
message: `Path replacement for the prefix ${green(prefix)}`,
245-
validate: (value) => value !== ''
246-
})
247-
248-
aliases[prefix] = replacement
249-
}
250-
if (Object.keys(aliases).length > 0) {
251-
additionalConfig.settings = { [CREATE_ALIAS_SETTING_PLACEHOLDER]: aliases }
252-
}
253-
254-
console.info()
255-
}
256-
}
257-
258-
// 6. Do you need Prettier to format your codebase?
123+
const supportedScriptLangs = {}
124+
// FIXME: Use a multi-select prompt
125+
// if (hasTypeScript) {
126+
// const { allowJsInVue } = await prompt({
127+
// type: 'toggle',
128+
// disabled: 'No',
129+
// enabled: 'Yes',
130+
// name: 'allowJsInVue',
131+
// message: `Do you use plain ${yellow('<script>')}s (without ${blue('lang="ts"')}) in ${green('.vue')} files?`,
132+
// initial: false
133+
// })
134+
135+
// if (allowJsInVue) {
136+
// const { allowJsxInVue } = await prompt({
137+
// type: 'toggle',
138+
// disabled: 'No',
139+
// enabled: 'Yes',
140+
// name: 'allowJsxInVue',
141+
// message: `Do you use ${yellow('<script lang="jsx">')}s in ${green('.vue')} files (not recommended)?`,
142+
// initial: false
143+
// })
144+
145+
// additionalConfig.extends = [
146+
// `@vue/eslint-config-${styleGuide}-with-typescript/${
147+
// allowJsxInVue
148+
// ? 'allow-jsx-in-vue'
149+
// : 'allow-js-in-vue'
150+
// }`
151+
// ]
152+
// } else {
153+
// const { allowTsxInVue } = await prompt({
154+
// type: 'toggle',
155+
// disabled: 'No',
156+
// enabled: 'Yes',
157+
// name: 'allowTsxInVue',
158+
// message: `Do you use ${yellow('<script lang="tsx">')}s in ${green('.vue')} files (not recommended)?`,
159+
// initial: false
160+
// })
161+
162+
// if (allowTsxInVue) {
163+
// additionalConfig.extends = [
164+
// `@vue/eslint-config-${styleGuide}-with-typescript/allow-tsx-in-vue`
165+
// ]
166+
// }
167+
// }
168+
// }
169+
170+
// 5. Do you need Prettier to format your codebase?
259171
const { needsPrettier } = await prompt({
260172
type: 'toggle',
261173
disabled: 'No',
@@ -265,30 +177,23 @@ const { needsPrettier } = await prompt({
265177
})
266178

267179
const { pkg: pkgToExtend, files } = createConfig({
268-
vueVersion,
269-
styleGuide,
270180
hasTypeScript,
181+
supportedScriptLangs,
271182
needsPrettier,
272-
273-
additionalConfig
274183
})
275184

276185
// TODO:
277186
// Add `lint` command to package.json
278-
// - eslint ... (extensions vary based on the language)
279-
// - note: for Vue CLI, remove the @vue/cli-plugin-eslint and override its lint command, they are too outdated.
280187
// Add a `format` command to package.json when prettier is used
281-
// TODO:
282-
// Add a note about that Vue CLI projects may need a `tsconfig.eslint.json`
283188

284189
deepMerge(pkg, pkgToExtend)
285190

286191
// Write `package.json` back
287-
writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, indent) + '\n', 'utf-8')
192+
writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, indent) + '\n', 'utf8')
288193
// Write files back
289194
for (const [name, content] of Object.entries(files)) {
290195
const fullPath = path.resolve(cwd, name)
291-
writeFileSync(fullPath, content, 'utf-8')
196+
writeFileSync(fullPath, content, 'utf8')
292197
}
293198

294199
// Prompt: Run `npm install` or `yarn` or `pnpm install`
@@ -300,7 +205,7 @@ const lintCommand = packageManager === 'npm' ? 'npm run lint' : `${packageManage
300205

301206
console.info(
302207
'\n' +
303-
`${bold(yellow('package.json'))} and ${bold(blue('.eslintrc.cjs'))} have been updated.\n` +
208+
`${bold(yellow('package.json'))} and ${bold(blue('eslint.config.mjs'))} have been updated.\n` +
304209
`Now please run ${bold(green(installCommand))} to re-install the dependencies.\n` +
305210
`Then you can run ${bold(green(lintCommand))} to lint your files.`
306211
)

eslint.config.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pluginVue from 'eslint-plugin-vue'
2+
// Use the TS config here because the default js parser doesn't support import attributes
3+
import vueTsEslintConfig from '@vue/eslint-config-typescript'
4+
5+
export default [
6+
{
7+
name: 'app/files-to-lint',
8+
files: ['**/*.{js,mjs,jsx,ts,mts,tsx,vue}'],
9+
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
10+
},
11+
...pluginVue.configs['flat/essential'],
12+
...vueTsEslintConfig(),
13+
]

0 commit comments

Comments
 (0)