Skip to content

Commit 90dbe09

Browse files
committed
feat: support ESLint Flat Config
1 parent cd20177 commit 90dbe09

File tree

5 files changed

+118
-89
lines changed

5 files changed

+118
-89
lines changed

LICENSE

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -270,35 +270,6 @@ Repository: git://github.com/mde/ejs.git
270270
> limitations under the License.
271271
>
272272

273-
## javascript-stringify
274-
275-
License: MIT
276-
By: Blake Embrey
277-
Repository: git+https://github.com/blakeembrey/javascript-stringify.git
278-
279-
> The MIT License (MIT)
280-
>
281-
> Copyright (c) 2013 Blake Embrey ([email protected])
282-
>
283-
> Permission is hereby granted, free of charge, to any person obtaining a copy
284-
> of this software and associated documentation files (the "Software"), to deal
285-
> in the Software without restriction, including without limitation the rights
286-
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
287-
> copies of the Software, and to permit persons to whom the Software is
288-
> furnished to do so, subject to the following conditions:
289-
>
290-
> The above copyright notice and this permission notice shall be included in
291-
> all copies or substantial portions of the Software.
292-
>
293-
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
294-
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
295-
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
296-
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
297-
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
298-
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
299-
> THE SOFTWARE.
300-
>
301-
302273
## kleur
303274

304275
License: MIT

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"@types/eslint": "^9.6.1",
4242
"@types/node": "^20.16.10",
4343
"@types/prompts": "^2.4.9",
44-
"@vue/create-eslint-config": "^0.3.3",
44+
"@vue/create-eslint-config": "0.4.0",
4545
"@vue/tsconfig": "^0.5.1",
4646
"ejs": "^3.1.10",
4747
"esbuild": "^0.18.20",

pnpm-lock.yaml

Lines changed: 6 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/build.mjs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fs from 'node:fs'
12
import * as esbuild from 'esbuild'
23
import esbuildPluginLicense from 'esbuild-plugin-license'
34

@@ -48,6 +49,48 @@ await esbuild.build({
4849
})
4950
}
5051
},
52+
53+
{
54+
name: '@vue/create-eslint-config fix',
55+
setup(build) {
56+
// Update esbuild to support the import attributes syntax in this PR is too risky.
57+
// TODO: update esbuild and remove the hack.
58+
build.onLoad({ filter: /@vue\/create-eslint-config\/index.js$/ }, (args) => {
59+
const text = fs.readFileSync(args.path, 'utf8')
60+
return {
61+
contents: text.replace(`with { type: 'json' }`, ''),
62+
loader: 'js'
63+
}
64+
})
65+
66+
// The renderEjsFile.js module uses file system APIs therefore after bundling it will not work.
67+
// So we need to preprocess it to remove the file system APIs.
68+
build.onLoad({ filter: /@vue\/create-eslint-config\/renderEjsFile.js$/ }, (args) => {
69+
const pkgDir = path.dirname(args.path)
70+
const templatesDir = path.resolve(pkgDir, './templates')
71+
72+
const allTemplateFileNames = fs.readdirSync(templatesDir)
73+
const templateFiles = Object.fromEntries(
74+
allTemplateFileNames.map((fileName) => {
75+
const content = fs.readFileSync(path.resolve(templatesDir, fileName), 'utf8')
76+
return [`./templates/${fileName}`, content]
77+
})
78+
)
79+
80+
return {
81+
contents: `
82+
import ejs from 'ejs'
83+
const templates = ${JSON.stringify(templateFiles)}
84+
export default function renderEjsFile(filePath, data) {
85+
return ejs.render(templates[filePath], data, {})
86+
}
87+
`,
88+
loader: 'js'
89+
}
90+
})
91+
}
92+
},
93+
5194
esbuildPluginLicense({
5295
thirdParty: {
5396
includePrivate: false,

utils/renderEslint.ts

Lines changed: 68 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import * as fs from 'node:fs'
22
import * as path from 'node:path'
33

4-
import type { Linter } from 'eslint'
5-
64
import createESLintConfig from '@vue/create-eslint-config'
75

86
import sortDependencies from './sortDependencies'
@@ -15,29 +13,23 @@ export default function renderEslint(
1513
rootDir,
1614
{ needsTypeScript, needsVitest, needsCypress, needsCypressCT, needsPrettier, needsPlaywright }
1715
) {
18-
const { additionalConfig, additionalDependencies } = getAdditionalConfigAndDependencies({
16+
const additionalConfigs = getAdditionalConfigs({
1917
needsVitest,
2018
needsCypress,
2119
needsCypressCT,
2220
needsPlaywright
2321
})
2422

2523
const { pkg, files } = createESLintConfig({
26-
vueVersion: '3.x',
27-
// we currently don't support other style guides
2824
styleGuide: 'default',
2925
hasTypeScript: needsTypeScript,
3026
needsPrettier,
3127

32-
additionalConfig,
33-
additionalDependencies
28+
additionalConfigs
3429
})
3530

3631
const scripts: Record<string, string> = {
37-
// Note that we reuse .gitignore here to avoid duplicating the configuration
38-
lint: needsTypeScript
39-
? 'eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore'
40-
: 'eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore'
32+
lint: 'eslint . --fix'
4133
}
4234

4335
// Theoretically, we could add Prettier without requring ESLint.
@@ -54,62 +46,90 @@ export default function renderEslint(
5446
const packageJsonPath = path.resolve(rootDir, 'package.json')
5547
const existingPkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
5648
const updatedPkg = sortDependencies(deepMerge(deepMerge(existingPkg, pkg), { scripts }))
57-
fs.writeFileSync(packageJsonPath, JSON.stringify(updatedPkg, null, 2) + '\n', 'utf-8')
49+
fs.writeFileSync(packageJsonPath, JSON.stringify(updatedPkg, null, 2) + '\n', 'utf8')
5850

59-
// write to .eslintrc.cjs, .prettierrc.json, etc.
51+
// write to eslint.config.mjs, .prettierrc.json, .editorconfig, etc.
6052
for (const [fileName, content] of Object.entries(files)) {
6153
const fullPath = path.resolve(rootDir, fileName)
62-
fs.writeFileSync(fullPath, content as string, 'utf-8')
54+
fs.writeFileSync(fullPath, content as string, 'utf8')
6355
}
6456
}
6557

58+
type ConfigItemInESLintTemplate = {
59+
importer: string
60+
content: string
61+
}
62+
type AdditionalConfig = {
63+
devDependencies: Record<string, string>
64+
beforeVuePlugin?: Array<ConfigItemInESLintTemplate>
65+
afterVuePlugin?: Array<ConfigItemInESLintTemplate>
66+
}
67+
type AdditionalConfigArray = Array<AdditionalConfig>
68+
6669
// visible for testing
67-
export function getAdditionalConfigAndDependencies({
70+
export function getAdditionalConfigs({
6871
needsVitest,
6972
needsCypress,
7073
needsCypressCT,
7174
needsPlaywright
7275
}) {
73-
const additionalConfig: Linter.Config = {}
74-
const additionalDependencies = {}
76+
const additionalConfigs: AdditionalConfigArray = []
7577

7678
if (needsVitest) {
77-
additionalConfig.overrides = [
78-
{
79-
files: ['src/**/*.{test,spec}.{js,ts,jsx,tsx}'],
80-
extends: ['plugin:@vitest/legacy-recommended']
81-
}
82-
]
83-
84-
additionalDependencies['@vitest/eslint-plugin'] = eslintDeps['@vitest/eslint-plugin']
79+
additionalConfigs.push({
80+
devDependencies: { '@vitest/eslint-plugin': eslintDeps['@vitest/eslint-plugin'] },
81+
afterVuePlugin: [
82+
{
83+
importer: `import pluginVitest from '@vitest/eslint-plugin'`,
84+
content: `
85+
{
86+
...pluginVitest.configs['recommended'],
87+
files: ['src/**/__tests__/*'],
88+
},`
89+
}
90+
]
91+
})
8592
}
8693

8794
if (needsCypress) {
88-
additionalConfig.overrides = [
89-
{
90-
files: needsCypressCT
91-
? [
92-
'**/__tests__/*.{cy,spec}.{js,ts,jsx,tsx}',
93-
'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}',
94-
'cypress/support/**/*.{js,ts,jsx,tsx}'
95-
]
96-
: ['cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}', 'cypress/support/**/*.{js,ts,jsx,tsx}'],
97-
extends: ['plugin:cypress/recommended']
98-
}
99-
]
100-
101-
additionalDependencies['eslint-plugin-cypress'] = eslintDeps['eslint-plugin-cypress']
95+
additionalConfigs.push({
96+
devDependencies: { 'eslint-plugin-cypress': eslintDeps['eslint-plugin-cypress'] },
97+
afterVuePlugin: [
98+
{
99+
importer: "import pluginCypress from 'eslint-plugin-cypress/flat'",
100+
content: `
101+
{
102+
...pluginCypress.configs.recommended,
103+
files: [
104+
${[
105+
...(needsCypressCT ? ["'**/__tests__/*.{cy,spec}.{js,ts,jsx,tsx}',"] : []),
106+
'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}',
107+
'cypress/support/**/*.{js,ts,jsx,tsx}'
108+
]
109+
.map(JSON.stringify.bind(JSON))
110+
.join(',\n ')}
111+
],
112+
},`
113+
}
114+
]
115+
})
102116
}
103117

104118
if (needsPlaywright) {
105-
additionalConfig.overrides = [
106-
{
107-
files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}'],
108-
extends: ['plugin:playwright/recommended']
109-
}
110-
]
111-
112-
additionalDependencies['eslint-plugin-playwright'] = eslintDeps['eslint-plugin-playwright']
119+
additionalConfigs.push({
120+
devDependencies: { 'eslint-plugin-playwright': eslintDeps['eslint-plugin-playwright'] },
121+
afterVuePlugin: [
122+
{
123+
importer: "import pluginPlaywright from 'eslint-plugin-playwright'",
124+
content: `
125+
{
126+
...pluginPlaywright.configs['flat/recommended'],
127+
files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}'],
128+
},`
129+
}
130+
]
131+
})
113132
}
114-
return { additionalConfig, additionalDependencies }
133+
134+
return additionalConfigs
115135
}

0 commit comments

Comments
 (0)