diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..ecea360f1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/LICENSE b/LICENSE index b5a7c2b0f..fea80e3de 100644 --- a/LICENSE +++ b/LICENSE @@ -447,33 +447,4 @@ Repository: git://github.com/mde/ejs.git > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > See the License for the specific language governing permissions and > limitations under the License. -> - -## @vue/create-eslint-config - -License: MIT -By: Haoqun Jiang -Repository: git+https://github.com/vuejs/create-eslint-config.git - -> MIT License -> -> Copyright (c) 2022 vuejs -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in all -> copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -> SOFTWARE. > \ No newline at end of file diff --git a/__test__/renderEslint.spec.ts b/__test__/renderEslint.spec.ts deleted file mode 100644 index 4deaf6c6e..000000000 --- a/__test__/renderEslint.spec.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { it, describe, expect } from 'vitest' -import { getAdditionalConfigs } from '../utils/renderEslint' - -describe('renderEslint', () => { - it('should get additional dependencies and config with no test flags', () => { - const additionalConfigs = getAdditionalConfigs({ - needsTypeScript: false, - needsVitest: false, - needsCypress: false, - needsCypressCT: false, - needsPlaywright: false, - }) - expect(additionalConfigs).toStrictEqual([]) - }) - - it('should get additional dependencies and config with for vitest', () => { - const additionalConfigs = getAdditionalConfigs({ - needsTypeScript: false, - needsVitest: true, - needsCypress: false, - needsCypressCT: false, - needsPlaywright: false, - }) - expect(additionalConfigs).toHaveLength(1) - const [additionalVitestConfig] = additionalConfigs - expect(additionalVitestConfig.devDependencies['@vitest/eslint-plugin']).not.toBeUndefined() - expect(additionalVitestConfig.afterVuePlugin).toHaveLength(1) - const [additionalVitestPlugin] = additionalVitestConfig.afterVuePlugin! - expect(additionalVitestPlugin.importer).toBe(`import pluginVitest from '@vitest/eslint-plugin'`) - expect(additionalVitestPlugin.content).toContain('...pluginVitest.configs.recommended') - expect(additionalVitestPlugin.content).toContain("files: ['src/**/__tests__/*']") - }) - - it('should get additional dependencies and config with for cypress', () => { - const additionalConfigs = getAdditionalConfigs({ - needsTypeScript: false, - needsVitest: false, - needsCypress: true, - needsCypressCT: false, - needsPlaywright: false, - }) - expect(additionalConfigs).toHaveLength(1) - const [additionalCypressConfig] = additionalConfigs - expect(additionalCypressConfig.devDependencies['eslint-plugin-cypress']).not.toBeUndefined() - expect(additionalCypressConfig.afterVuePlugin).toHaveLength(1) - const [additionalCypressPlugin] = additionalCypressConfig.afterVuePlugin! - expect(additionalCypressPlugin.importer).toBe( - "import pluginCypress from 'eslint-plugin-cypress'", - ) - expect(additionalCypressPlugin.content).toContain('...pluginCypress.configs.recommended') - expect(additionalCypressPlugin.content).toContain( - "'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}'", - ) - expect(additionalCypressPlugin.content).toContain("'cypress/support/**/*.{js,ts,jsx,tsx}'") - }) - - it('should get additional dependencies and config with for cypress with component testing', () => { - const additionalConfigs = getAdditionalConfigs({ - needsTypeScript: false, - needsVitest: false, - needsCypress: true, - needsCypressCT: true, - needsPlaywright: false, - }) - expect(additionalConfigs).toHaveLength(1) - const [additionalCypressConfig] = additionalConfigs - expect(additionalCypressConfig.devDependencies['eslint-plugin-cypress']).not.toBeUndefined() - expect(additionalCypressConfig.afterVuePlugin).toHaveLength(1) - const [additionalCypressPlugin] = additionalCypressConfig.afterVuePlugin! - expect(additionalCypressPlugin.importer).toBe( - "import pluginCypress from 'eslint-plugin-cypress'", - ) - expect(additionalCypressPlugin.content).toContain('...pluginCypress.configs.recommended') - expect(additionalCypressPlugin.content).toContain("'**/__tests__/*.{cy,spec}.{js,ts,jsx,tsx}'") - expect(additionalCypressPlugin.content).toContain( - "'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}'", - ) - expect(additionalCypressPlugin.content).toContain("'cypress/support/**/*.{js,ts,jsx,tsx}'") - }) - - it('should get additional dependencies and config with for playwright', () => { - const additionalConfigs = getAdditionalConfigs({ - needsTypeScript: false, - needsVitest: false, - needsCypress: false, - needsCypressCT: false, - needsPlaywright: true, - }) - expect(additionalConfigs).toHaveLength(1) - const [additionalPlaywrightConfig] = additionalConfigs - expect( - additionalPlaywrightConfig.devDependencies['eslint-plugin-playwright'], - ).not.toBeUndefined() - expect(additionalPlaywrightConfig.afterVuePlugin).toHaveLength(1) - const [additionalPlaywrightPlugin] = additionalPlaywrightConfig.afterVuePlugin! - expect(additionalPlaywrightPlugin.importer).toBe( - "import pluginPlaywright from 'eslint-plugin-playwright'", - ) - expect(additionalPlaywrightPlugin.content).toContain( - "...pluginPlaywright.configs['flat/recommended']", - ) - expect(additionalPlaywrightPlugin.content).toContain( - "files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}']", - ) - }) -}) diff --git a/index.ts b/index.ts index 4d77d0904..a006f966f 100755 --- a/index.ts +++ b/index.ts @@ -20,7 +20,6 @@ import { import generateReadme from './utils/generateReadme' import getCommand from './utils/getCommand' import getLanguage from './utils/getLanguage' -import renderEslint from './utils/renderEslint' import { trimBoilerplate, removeCSSImport, emptyRouterConfig } from './utils/trimBoilerplate' import cliPackageJson from './package.json' with { type: 'json' } @@ -511,25 +510,40 @@ async function init() { } // Render ESLint config - if (needsEslint || needsOxlint) { - renderEslint(root, { - needsTypeScript, - needsOxlint, - needsVitest, - needsCypress, - needsCypressCT, - needsPrettier, - needsPlaywright, - }) - render('config/eslint') - } + if (needsEslint) { + render('linting/base') - if (needsOxlint) { - render('config/oxlint') + if (needsTypeScript) { + render('linting/core/ts') + } else { + render('linting/core/js') + } + + if (needsCypress) { + render('linting/cypress') + } + if (needsCypressCT) { + render('linting/cypress-ct') + } + if (needsPlaywright) { + render('linting/playwright') + } + if (needsVitest) { + render('linting/vitest') + } + + // These configs only disable rules, so they should be applied last. + if (needsPrettier) { + render('linting/prettier') + } + if (needsOxlint) { + render('linting/oxlint') + } } if (needsPrettier) { - render('config/prettier') + render('formatting/prettier') + // TODO: add oxfmt option in the next PR } // use rolldown-vite if the feature is enabled @@ -615,7 +629,7 @@ async function init() { root, () => {}, (filepath) => { - if (filepath.endsWith('.js') && !filepath.endsWith('eslint.config.js')) { + if (filepath.endsWith('.js')) { const tsFilePath = filepath.replace(/\.js$/, '.ts') if (fs.existsSync(tsFilePath)) { fs.unlinkSync(filepath) @@ -694,7 +708,7 @@ async function init() { if (!dotGitDirectoryState.hasDotGitDirectory) { outroMessage += ` ${dim('|')} ${language.infos.optionalGitCommand} - + ${bold(green('git init && git add -A && git commit -m "initial commit"'))}` } diff --git a/package.json b/package.json index f66863f5a..0dab801af 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "@types/eslint": "^9.6.1", "@types/node": "^24.10.4", "@types/prompts": "^2.4.9", - "@vue/create-eslint-config": "^0.14.1", "@vue/tsconfig": "^0.8.1", "ejs": "^3.1.10", "husky": "^9.1.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1100d92ba..0c248feba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,9 +23,6 @@ importers: '@types/prompts': specifier: ^2.4.9 version: 2.4.9 - '@vue/create-eslint-config': - specifier: ^0.14.1 - version: 0.14.1 '@vue/tsconfig': specifier: ^0.8.1 version: 0.8.1(typescript@5.9.2)(vue@3.5.25(typescript@5.9.2)) @@ -157,12 +154,6 @@ importers: specifier: ^1.57.0 version: 1.57.0 - template/config/prettier: - devDependencies: - prettier: - specifier: 3.7.4 - version: 3.7.4 - template/config/router: dependencies: vue: @@ -1529,11 +1520,6 @@ packages: '@vue/compiler-ssr@3.5.25': resolution: {integrity: sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==} - '@vue/create-eslint-config@0.14.1': - resolution: {integrity: sha512-EiEc2Kl1stBZ68HEYmjlk1uIbQP8MlvRUg5CqvbMZfCBH8+xF2HhXcRZhmtd/p0AHcV8b9HdN3Uq3BIxOwPZYQ==} - engines: {node: ^16.14.0 || >= 18.0.0} - hasBin: true - '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} @@ -5608,12 +5594,6 @@ snapshots: '@vue/compiler-dom': 3.5.25 '@vue/shared': 3.5.25 - '@vue/create-eslint-config@0.14.1': - dependencies: - ejs: 3.1.10 - enquirer: 2.4.1 - kolorist: 1.8.0 - '@vue/devtools-api@6.6.4': {} '@vue/devtools-api@7.7.8': diff --git a/rolldown.config.ts b/rolldown.config.ts index 6ee4e497f..b1c17e20a 100644 --- a/rolldown.config.ts +++ b/rolldown.config.ts @@ -1,4 +1,3 @@ -import fs from 'node:fs' import path from 'node:path' import { defineConfig, RolldownPlugin } from 'rolldown' import license from 'rollup-plugin-license' @@ -200,34 +199,5 @@ export default defineConfig({ }, }, }) as RolldownPlugin, - - { - name: '@vue/create-eslint-config fix', - transform: { - filter: { - id: /@vue.create-eslint-config.renderEjsFile\.js$/, - }, - handler(_code, id) { - const pkgDir = path.dirname(id) - const templatesDir = path.resolve(pkgDir, './templates') - - const allTemplateFileNames = fs.readdirSync(templatesDir) - const templateFiles = Object.fromEntries( - allTemplateFileNames.map((fileName) => { - const content = fs.readFileSync(path.resolve(templatesDir, fileName), 'utf8') - return [`./templates/${fileName}`, content] - }), - ) - - return ` - import ejs from 'ejs' - const templates = ${JSON.stringify(templateFiles)} - export default function renderEjsFile(filePath, data) { - return ejs.render(templates[filePath], data, {}) - } - ` - }, - }, - }, ], }) diff --git a/template/eslint/package.json b/template/eslint/package.json deleted file mode 100644 index 07cefb97d..000000000 --- a/template/eslint/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "devDependencies": { - "@vitest/eslint-plugin": "^1.5.2", - "eslint-plugin-cypress": "^5.2.0", - "eslint-plugin-playwright": "^2.4.0" - } -} diff --git a/template/config/oxlint/.vscode/extensions.json b/template/formatting/oxfmt/.vscode/extensions.json similarity index 100% rename from template/config/oxlint/.vscode/extensions.json rename to template/formatting/oxfmt/.vscode/extensions.json diff --git a/template/formatting/oxfmt/.vscode/settings.json b/template/formatting/oxfmt/.vscode/settings.json new file mode 100644 index 000000000..16f965cfb --- /dev/null +++ b/template/formatting/oxfmt/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "[javascript]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + } +} diff --git a/template/formatting/oxfmt/_oxfmtrc.jsonc b/template/formatting/oxfmt/_oxfmtrc.jsonc new file mode 100644 index 000000000..2de1f92b0 --- /dev/null +++ b/template/formatting/oxfmt/_oxfmtrc.jsonc @@ -0,0 +1,5 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "semi": false, + "singleQuote": true, +} diff --git a/template/formatting/oxfmt/_prettierrc.json b/template/formatting/oxfmt/_prettierrc.json new file mode 100644 index 000000000..a84e0edfb --- /dev/null +++ b/template/formatting/oxfmt/_prettierrc.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "singleQuote": true, + "printWidth": 100, + "plugins": ["@prettier/plugin-oxc"] +} diff --git a/template/formatting/oxfmt/package.json b/template/formatting/oxfmt/package.json new file mode 100644 index 000000000..2fd791ca4 --- /dev/null +++ b/template/formatting/oxfmt/package.json @@ -0,0 +1,14 @@ +{ + "scripts": { + "format:oxfmt": "oxfmt src/", + "format:prettier": "prettier --write --experimental-cli src/ '!**/*.{js,jsx,ts,tsx}'", + "format": "run-p format:oxfmt format:prettier" + }, + "devDependencies": { + "@vue/eslint-config-prettier": "^10.2.0", + "@prettier/plugin-oxc": "^0.1.3", + "npm-run-all2": "^8.0.4", + "prettier": "3.7.4", + "oxfmt": "^0.18.0" + } +} diff --git a/template/config/prettier/.vscode/extensions.json b/template/formatting/prettier/.vscode/extensions.json similarity index 100% rename from template/config/prettier/.vscode/extensions.json rename to template/formatting/prettier/.vscode/extensions.json diff --git a/template/config/prettier/.vscode/settings.json b/template/formatting/prettier/.vscode/settings.json similarity index 100% rename from template/config/prettier/.vscode/settings.json rename to template/formatting/prettier/.vscode/settings.json diff --git a/template/config/prettier/_prettierrc.json b/template/formatting/prettier/_prettierrc.json similarity index 100% rename from template/config/prettier/_prettierrc.json rename to template/formatting/prettier/_prettierrc.json diff --git a/template/config/prettier/package.json b/template/formatting/prettier/package.json similarity index 74% rename from template/config/prettier/package.json rename to template/formatting/prettier/package.json index 8d9ab5711..1751645e7 100644 --- a/template/config/prettier/package.json +++ b/template/formatting/prettier/package.json @@ -3,6 +3,7 @@ "format": "prettier --write --experimental-cli src/" }, "devDependencies": { + "@vue/eslint-config-prettier": "^10.2.0", "prettier": "3.7.4" } } diff --git a/template/config/eslint/.vscode/extensions.json b/template/linting/base/.vscode/extensions.json similarity index 100% rename from template/config/eslint/.vscode/extensions.json rename to template/linting/base/.vscode/extensions.json diff --git a/template/config/eslint/.vscode/settings.json b/template/linting/base/.vscode/settings.json similarity index 100% rename from template/config/eslint/.vscode/settings.json rename to template/linting/base/.vscode/settings.json diff --git a/template/linting/base/_editorconfig b/template/linting/base/_editorconfig new file mode 100644 index 000000000..3b510aa68 --- /dev/null +++ b/template/linting/base/_editorconfig @@ -0,0 +1,8 @@ +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +max_line_length = 100 diff --git a/template/config/prettier/_gitattributes b/template/linting/base/_gitattributes similarity index 100% rename from template/config/prettier/_gitattributes rename to template/linting/base/_gitattributes diff --git a/template/linting/base/package.json b/template/linting/base/package.json new file mode 100644 index 000000000..9f1b863fd --- /dev/null +++ b/template/linting/base/package.json @@ -0,0 +1,9 @@ +{ + "scripts": { + "lint": "eslint . --fix --cache" + }, + "devDependencies": { + "eslint-plugin-vue": "~10.6.2", + "eslint": "^9.39.2" + } +} diff --git a/template/linting/core/js/eslint.config.js.data.mjs b/template/linting/core/js/eslint.config.js.data.mjs new file mode 100644 index 000000000..62e48a504 --- /dev/null +++ b/template/linting/core/js/eslint.config.js.data.mjs @@ -0,0 +1,15 @@ +export default function getData() { + return { + configs: [ + { + importer: `import js from '@eslint/js'`, + content: ` js.configs.recommended,`, + }, + + { + importer: `import pluginVue from 'eslint-plugin-vue'`, + content: ` ...pluginVue.configs['flat/essential'],`, + }, + ], + } +} diff --git a/template/linting/core/js/eslint.config.js.ejs b/template/linting/core/js/eslint.config.js.ejs new file mode 100644 index 000000000..8d9971cb7 --- /dev/null +++ b/template/linting/core/js/eslint.config.js.ejs @@ -0,0 +1,27 @@ +import { defineConfig, globalIgnores } from 'eslint/config' +import globals from 'globals' +<%_ for (const { importer } of configs) { _%> +<%_ if (importer) { _%><%- importer %> +<%_ } _%> +<%_ } _%> + +export default defineConfig([ + { + name: 'app/files-to-lint', + files: ['**/*.{vue,js,mjs,jsx}'], + }, + + globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), + + { + languageOptions: { + globals: { + ...globals.browser, + }, + }, + }, + +<%_ for (const { content } of configs) { _%> +<%- content %> +<%_ } _%> +]) diff --git a/template/linting/core/js/package.json b/template/linting/core/js/package.json new file mode 100644 index 000000000..1bdd664b0 --- /dev/null +++ b/template/linting/core/js/package.json @@ -0,0 +1,8 @@ +{ + "devDependencies": { + "@eslint/js": "^9.39.2", + "eslint-plugin-vue": "~10.6.2", + "eslint": "^9.39.2", + "globals": "^16.5.0" + } +} diff --git a/template/linting/core/ts/eslint.config.js.data.mjs b/template/linting/core/ts/eslint.config.js.data.mjs new file mode 100644 index 000000000..486b1c723 --- /dev/null +++ b/template/linting/core/ts/eslint.config.js.data.mjs @@ -0,0 +1,15 @@ +export default function getData() { + return { + configs: [ + { + importer: `import pluginVue from 'eslint-plugin-vue'`, + content: ` ...pluginVue.configs['flat/essential'],`, + }, + + { + // skipped importer for this one because it was already there + content: ` vueTsConfigs.recommended,`, + }, + ], + } +} diff --git a/template/linting/core/ts/eslint.config.js.ejs b/template/linting/core/ts/eslint.config.js.ejs new file mode 100644 index 000000000..9378b68fd --- /dev/null +++ b/template/linting/core/ts/eslint.config.js.ejs @@ -0,0 +1,24 @@ +import { globalIgnores } from 'eslint/config' +import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' +<%_ for (const { importer } of configs) { _%> +<%_ if (importer) { _%><%- importer %> +<%_ } _%> +<%_ } _%> + +// To allow more languages other than `ts` in `.vue` files, uncomment the following lines: +// import { configureVueProject } from '@vue/eslint-config-typescript' +// configureVueProject({ scriptLangs: ['ts', 'tsx'] }) +// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup + +export default defineConfigWithVueTs( + { + name: 'app/files-to-lint', + files: ['**/*.{vue,ts,mts,tsx}'], + }, + + globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), + +<%_ for (const { content } of configs) { _%> +<%- content %> +<%_ } _%> +) diff --git a/template/linting/core/ts/package.json b/template/linting/core/ts/package.json new file mode 100644 index 000000000..adc7f2366 --- /dev/null +++ b/template/linting/core/ts/package.json @@ -0,0 +1,9 @@ +{ + "devDependencies": { + "@vue/eslint-config-typescript": "^14.6.0", + "eslint": "^9.39.2", + "eslint-plugin-vue": "~10.6.2", + "jiti": "^2.6.1", + "typescript": "~5.9.0" + } +} diff --git a/template/linting/cypress-ct/eslint.config.js.data.mjs b/template/linting/cypress-ct/eslint.config.js.data.mjs new file mode 100644 index 000000000..2e4ccf2b2 --- /dev/null +++ b/template/linting/cypress-ct/eslint.config.js.data.mjs @@ -0,0 +1,25 @@ +export default function getData({ oldData }) { + return { + ...oldData, + configs: oldData.configs.map((c) => { + if (!c.content.includes('pluginCypress')) { + return c + } + + return { + ...c, + + // modify the files pattern to include component testing files + content: ` + { + ...pluginCypress.configs.recommended, + files: [ + '**/__tests__/*.{cy,spec}.{js,ts,jsx,tsx}', + 'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}', + 'cypress/support/**/*.{js,ts,jsx,tsx}', + ], + },`, + } + }), + } +} diff --git a/template/linting/cypress/eslint.config.js.data.mjs b/template/linting/cypress/eslint.config.js.data.mjs new file mode 100644 index 000000000..58b37de32 --- /dev/null +++ b/template/linting/cypress/eslint.config.js.data.mjs @@ -0,0 +1,19 @@ +export default function getData({ oldData }) { + return { + ...oldData, + configs: [ + ...oldData.configs, + { + importer: `import pluginCypress from 'eslint-plugin-cypress'`, + content: ` + { + ...pluginCypress.configs.recommended, + files: [ + 'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}', + 'cypress/support/**/*.{js,ts,jsx,tsx}', + ], + },`, + }, + ], + } +} diff --git a/template/linting/cypress/package.json b/template/linting/cypress/package.json new file mode 100644 index 000000000..f3638c936 --- /dev/null +++ b/template/linting/cypress/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "eslint-plugin-cypress": "^5.2.0" + } +} diff --git a/template/linting/oxlint/.vscode/extensions.json b/template/linting/oxlint/.vscode/extensions.json new file mode 100644 index 000000000..99e2f7ddf --- /dev/null +++ b/template/linting/oxlint/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["oxc.oxc-vscode"] +} diff --git a/template/linting/oxlint/_ oxlintrc.json b/template/linting/oxlint/_ oxlintrc.json new file mode 100644 index 000000000..d5648b966 --- /dev/null +++ b/template/linting/oxlint/_ oxlintrc.json @@ -0,0 +1,10 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["eslint", "typescript", "unicorn", "oxc", "vue"], + "env": { + "browser": true + }, + "categories": { + "correctness": "error" + } +} diff --git a/template/linting/oxlint/eslint.config.js.data.mjs b/template/linting/oxlint/eslint.config.js.data.mjs new file mode 100644 index 000000000..4601d74c8 --- /dev/null +++ b/template/linting/oxlint/eslint.config.js.data.mjs @@ -0,0 +1,12 @@ +export default function getData({ oldData }) { + return { + ...oldData, + configs: [ + ...oldData.configs, + { + importer: `import pluginOxlint from 'eslint-plugin-oxlint'`, + content: `\n ...pluginOxlint.configs['flat/recommended'],`, + }, + ], + } +} diff --git a/template/linting/oxlint/package.json b/template/linting/oxlint/package.json new file mode 100644 index 000000000..07c9542ba --- /dev/null +++ b/template/linting/oxlint/package.json @@ -0,0 +1,12 @@ +{ + "scripts": { + "lint:oxlint": "oxlint . --fix", + "lint:eslint": "eslint . --fix --cache", + "lint": "run-s lint:*" + }, + "devDependencies": { + "eslint-plugin-oxlint": "~1.33.0", + "npm-run-all2": "^8.0.4", + "oxlint": "~1.33.0" + } +} diff --git a/template/linting/playwright/eslint.config.js.data.mjs b/template/linting/playwright/eslint.config.js.data.mjs new file mode 100644 index 000000000..1900c753a --- /dev/null +++ b/template/linting/playwright/eslint.config.js.data.mjs @@ -0,0 +1,16 @@ +export default function getData({ oldData }) { + return { + ...oldData, + configs: [ + ...oldData.configs, + { + importer: "import pluginPlaywright from 'eslint-plugin-playwright'", + content: ` + { + ...pluginPlaywright.configs['flat/recommended'], + files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}'], + },`, + }, + ], + } +} diff --git a/template/linting/playwright/package.json b/template/linting/playwright/package.json new file mode 100644 index 000000000..6c3f127f2 --- /dev/null +++ b/template/linting/playwright/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "eslint-plugin-playwright": "^2.4.0" + } +} diff --git a/template/linting/prettier/eslint.config.js.data.mjs b/template/linting/prettier/eslint.config.js.data.mjs new file mode 100644 index 000000000..81db21fb1 --- /dev/null +++ b/template/linting/prettier/eslint.config.js.data.mjs @@ -0,0 +1,12 @@ +export default function getData({ oldData }) { + return { + ...oldData, + configs: [ + ...oldData.configs, + { + importer: `import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'`, + content: `\n skipFormatting,`, + }, + ], + } +} diff --git a/template/linting/prettier/package.json b/template/linting/prettier/package.json new file mode 100644 index 000000000..16577b374 --- /dev/null +++ b/template/linting/prettier/package.json @@ -0,0 +1,6 @@ +{ + "devDependencies": { + "@vue/eslint-config-prettier": "^10.2.0", + "prettier": "3.7.4" + } +} diff --git a/template/linting/vitest/eslint.config.js.data.mjs b/template/linting/vitest/eslint.config.js.data.mjs new file mode 100644 index 000000000..953217685 --- /dev/null +++ b/template/linting/vitest/eslint.config.js.data.mjs @@ -0,0 +1,16 @@ +export default function getData({ oldData }) { + return { + ...oldData, + configs: [ + ...oldData.configs, + { + importer: `import pluginVitest from '@vitest/eslint-plugin'`, + content: ` + { + ...pluginVitest.configs.recommended, + files: ['src/**/__tests__/*'], + },`, + }, + ], + } +} diff --git a/template/linting/vitest/package.json b/template/linting/vitest/package.json new file mode 100644 index 000000000..914975751 --- /dev/null +++ b/template/linting/vitest/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "@vitest/eslint-plugin": "^1.5.2" + } +} diff --git a/utils/renderEslint.ts b/utils/renderEslint.ts deleted file mode 100644 index 823543851..000000000 --- a/utils/renderEslint.ts +++ /dev/null @@ -1,144 +0,0 @@ -import * as fs from 'node:fs' -import * as path from 'node:path' - -import createESLintConfig from '@vue/create-eslint-config' - -import sortDependencies from './sortDependencies' -import deepMerge from './deepMerge' - -import eslintTemplatePackage from '../template/eslint/package.json' with { type: 'json' } -const eslintDeps = eslintTemplatePackage.devDependencies - -export default function renderEslint( - rootDir, - { - needsTypeScript, - needsVitest, - needsCypress, - needsCypressCT, - needsOxlint, - needsPrettier, - needsPlaywright, - }, -) { - const additionalConfigs = getAdditionalConfigs({ - needsTypeScript, - needsVitest, - needsCypress, - needsCypressCT, - needsPlaywright, - }) - - const { pkg, files } = createESLintConfig({ - styleGuide: 'default', - hasTypeScript: needsTypeScript, - needsOxlint, - // Theoretically, we could add Prettier without requring ESLint. - // But it doesn't seem to be a good practice, so we just let createESLintConfig handle it. - needsPrettier, - additionalConfigs, - }) - - // update package.json - const packageJsonPath = path.resolve(rootDir, 'package.json') - const existingPkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) - const updatedPkg = sortDependencies(deepMerge(existingPkg, pkg)) - fs.writeFileSync(packageJsonPath, JSON.stringify(updatedPkg, null, 2) + '\n', 'utf8') - - // write to eslint.config.js, .prettierrc.json, .editorconfig, etc. - for (const [fileName, content] of Object.entries(files)) { - const fullPath = path.resolve(rootDir, fileName) - fs.writeFileSync(fullPath, content as string, 'utf8') - } -} - -type ConfigItemInESLintTemplate = { - importer: string - content: string -} -type AdditionalConfig = { - devDependencies: Record - beforeVuePlugin?: Array - afterVuePlugin?: Array -} -type AdditionalConfigArray = Array - -// visible for testing -export function getAdditionalConfigs({ - needsTypeScript, - needsVitest, - needsCypress, - needsCypressCT, - needsPlaywright, -}) { - const additionalConfigs: AdditionalConfigArray = [] - - if (needsVitest) { - additionalConfigs.push({ - devDependencies: { - '@vitest/eslint-plugin': eslintDeps['@vitest/eslint-plugin'], - }, - afterVuePlugin: [ - { - importer: `import pluginVitest from '@vitest/eslint-plugin'`, - content: ` - { - ...pluginVitest.configs.recommended, - files: ['src/**/__tests__/*'], - },`, - }, - ], - }) - } - - if (needsCypress) { - additionalConfigs.push({ - devDependencies: { - 'eslint-plugin-cypress': eslintDeps['eslint-plugin-cypress'], - }, - afterVuePlugin: [ - { - importer: - (needsTypeScript - ? `// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n` + - `// @ts-ignore\n` - : '') + "import pluginCypress from 'eslint-plugin-cypress'", - content: ` - { - ...pluginCypress.configs.recommended, - files: [ - ${[ - ...(needsCypressCT ? ['**/__tests__/*.{cy,spec}.{js,ts,jsx,tsx}'] : []), - 'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}', - 'cypress/support/**/*.{js,ts,jsx,tsx}', - ] - .map(JSON.stringify.bind(JSON)) - .join(',\n ') - .replace(/"/g, "'" /* use single quotes as in the other configs */)} - ], - },`, - }, - ], - }) - } - - if (needsPlaywright) { - additionalConfigs.push({ - devDependencies: { - 'eslint-plugin-playwright': eslintDeps['eslint-plugin-playwright'], - }, - afterVuePlugin: [ - { - importer: "import pluginPlaywright from 'eslint-plugin-playwright'", - content: ` - { - ...pluginPlaywright.configs['flat/recommended'], - files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}'], - },`, - }, - ], - }) - } - - return additionalConfigs -}