xo icon indicating copy to clipboard operation
xo copied to clipboard

How to extend flat config?

Open GeoffreyBooth opened this issue 4 months ago • 2 comments

Related to https://github.com/xojs/xo/issues/843, it’s not obvious to me how one should go about integrating eslint plugins for additional file types. In my case I want to use eslint-plugin-svelte to add support for Svelte files, with its Svelte-specific rules added to the default rules that XO defines for general JavaScript. Based on https://github.com/sveltejs/eslint-plugin-svelte#usage, this is the cleanest I could come up with:

import xo from 'xo';
import svelte from 'eslint-plugin-svelte';
import svelteParser from 'svelte-eslint-parser';
import typescriptParser from '@typescript-eslint/parser';

const baseConfigs = xo.xoToEslintConfig([]);
const mergedPlugins = Object.fromEntries(baseConfigs.flatMap(config => config.plugins ? Object.entries(config.plugins) : []));
const mergedRules = Object.fromEntries(baseConfigs.flatMap(config => config.rules ? Object.entries(config.rules) : []));

const svelteConfig = {
	files: ['**/*.svelte'],
	plugins: {
		...mergedPlugins,
		svelte,
	},
	languageOptions: {
		parser: svelteParser,
		parserOptions: {
			parser: typescriptParser,
			extraFileExtensions: ['.svelte'],
			project: true,
		},
	},
	rules: {
		...mergedRules,
		...svelte.configs.recommended.rules,
	},
};

const xoConfig = [
	...baseConfigs,
	svelteConfig,
];

export default xoConfig;

Reproduction: xo-svelte.zip

Even this feels overly verbose and pretty far from the zero-config goal of XO. This took a few hours of fiddling to come up with, and I’m still not sure there’s not a better version possible; I haven’t tested if this supports Svelte files with <script lang="ts">, or .svelte.js/.svelte.ts files. And it suffers from #843 where XO needs to be run via xo '**/*.{js,mjs,cjs,ts,mts,cts,svelte}' for this to work.

Should a suggested configuration for Svelte be added to the readme? A wiki page? Or perhaps it should be baked into XO proper, like XO already has built-in support for TypeScript and JSX/TSX? There seems to be one canonical eslint plugin for Svelte, so perhaps adding it to XO core is the best solution; and the **/*.svelte glob shouldn’t match anything in non-Svelte projects and so it shouldn’t degrade performance. Likewise for Vue and .vue files.

GeoffreyBooth avatar Oct 05 '25 23:10 GeoffreyBooth

XO just uses ESLint configs, so refer to ESLint docs.


The config could be much simpler:

// xo.config.js
import svelte from 'eslint-plugin-svelte';
import svelteParser from 'svelte-eslint-parser';
import typescriptParser from '@typescript-eslint/parser';

export default [
	{
		files: ['**/*.svelte'],
		plugins: { svelte },
		languageOptions: {
			parser: svelteParser,
			parserOptions: {
				parser: typescriptParser,
				extraFileExtensions: ['.svelte'],
			},
		},
		rules: {
			...svelte.configs.recommended.rules,
		},
	},
];

sindresorhus avatar Oct 06 '25 02:10 sindresorhus

The config could be much simpler:

Using that config means that only the rules defined in svelte.configs.recommended.rules are applied to Svelte files. So in a Svelte file like

<script>
	console.log("this should trigger no double quotes");
</script>

{@html '<span>This should trigger svelte/no-at-html-tags</span>'}

The simpler config triggers the svelte/no-at-html-tags rule but not the @stylistic/quotes rule. To get XO’s JavaScript rules to apply to the script block, those rules need to be passed into the rules block in the config file. Therefore I need to somehow get XO’s rules that apply to JavaScript to pass into there, hence my version’s xo.xoToEslintConfig() and transforming that result into pieces that can be used here.

If XO provided equivalents of svelte.configs.recommended.rules as exports, like js.configs.recommended.rules and ts.configs.recommended.rules, then that part would be easier. I also found that I needed to pass in XO’s plugins, as the rules included things like @stylistic/quotes that require the Stylistic plugin to be passed in; so XO would also need to export js.configs.recommended.plugins and ts.configs.recommended.plugins. And at this point we’re almost at the complexity of the config in the top post, with the only difference being that the mergedPlugins and mergedRules lines are replaced by imports.

If there’s a simpler alternative, I’m open to suggestions. I think https://github.com/xojs/xo/pull/845 is great; it feels like the natural follow-up would be to have default included config blocks for files: ['**/*.svelte'] and files: ['**/*.vue'] and files: ['**/*.astro'] with the most popular eslint plugins for each file type, and with XO’s JavaScript and TypeScript rules and plugins included for them.

GeoffreyBooth avatar Oct 06 '25 15:10 GeoffreyBooth