Skip to content

Commit

Permalink
feat: support features.formatters to format CSS and HTML (#520)
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu authored Nov 16, 2024
1 parent b413fbd commit f6d8bb6
Show file tree
Hide file tree
Showing 12 changed files with 595 additions and 16 deletions.
6 changes: 1 addition & 5 deletions docs/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,7 @@ watch(projectsSectionVisible, () => {
top: 25vh;
width: 100%;
height: 30vh;
background: radial-gradient(
50% 50% at 50% 50%,
#00dc82 0%,
rgba(0, 220, 130, 0) 100%
);
background: radial-gradient(50% 50% at 50% 50%, #00dc82 0%, rgba(0, 220, 130, 0) 100%);
filter: blur(180px);
opacity: 0.6;
z-index: -1;
Expand Down
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default createConfigForNuxt({
features: {
stylistic: true,
tooling: true,
formatters: true,
},
dirs: {
src: [
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@vueuse/nuxt": "catalog:",
"bumpp": "catalog:",
"eslint": "catalog:",
"eslint-plugin-format": "^0.1.2",
"fast-glob": "catalog:",
"nuxt": "catalog:",
"nuxt-og-image": "catalog:",
Expand Down
12 changes: 11 additions & 1 deletion packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,17 @@
"prepack": "pnpm run build"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0"
"eslint": "^8.57.0 || ^9.0.0",
"eslint-plugin-format": "*"
},
"peerDependenciesMeta": {
"eslint-plugin-format": {
"optional": true
}
},
"dependencies": {
"@antfu/install-pkg": "^0.4.1",
"@clack/prompts": "^0.8.1",
"@eslint/js": "catalog:",
"@nuxt/eslint-plugin": "workspace:*",
"@stylistic/eslint-plugin": "catalog:",
Expand All @@ -63,6 +71,8 @@
"eslint-plugin-regexp": "catalog:",
"eslint-plugin-unicorn": "catalog:",
"eslint-plugin-vue": "catalog:",
"eslint-processor-vue-blocks": "^0.1.2",
"eslint-merge-processors": "^0.1.0",
"globals": "catalog:",
"local-pkg": "catalog:",
"pathe": "catalog:",
Expand Down
258 changes: 258 additions & 0 deletions packages/eslint-config/src/flat/configs/formatters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import type { StylisticCustomizeOptions } from '@stylistic/eslint-plugin'
import type { Linter } from 'eslint'
import { isPackageExists } from 'local-pkg'
import type { OptionsFormatters } from '../types'
import { ensurePackages, interopDefault, parserPlain } from '../utils'
import { GLOB_CSS, GLOB_GRAPHQL, GLOB_HTML, GLOB_LESS, GLOB_MARKDOWN, GLOB_POSTCSS, GLOB_SCSS, GLOB_SVG, GLOB_XML } from '../globs'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type VendoredPrettierOptions = any

function mergePrettierOptions(
options: VendoredPrettierOptions,
overrides: VendoredPrettierOptions = {},
): VendoredPrettierOptions {
return {
...options,
...overrides,
plugins: [
...(overrides.plugins || []),
...(options.plugins || []),
],
}
}

export async function formatters(
options: OptionsFormatters | boolean = {},
stylistic: StylisticCustomizeOptions<true>,
): Promise<Linter.Config[]> {
if (!options)
return []

if (options === true) {
const isPrettierPluginXmlInScope = isPackageExists('@prettier/plugin-xml')
options = {
css: true,
graphql: true,
html: true,
// Markdown is disabled by default as many Nuxt projects use MDC with @nuxt/content,
// where Prettier doesn't fully understand.
markdown: false,
svg: isPrettierPluginXmlInScope,
xml: isPrettierPluginXmlInScope,
}
}

await ensurePackages([
'eslint-plugin-format',
(options.xml || options.svg) ? '@prettier/plugin-xml' : undefined,
])

const {
indent,
quotes,
semi,
} = {
indent: 2,
quotes: 'single',
semi: false,
...stylistic,
}

const prettierOptions: VendoredPrettierOptions = Object.assign(
{
endOfLine: 'auto',
printWidth: 120,
semi,
singleQuote: quotes === 'single',
tabWidth: typeof indent === 'number' ? indent : 2,
trailingComma: 'all',
useTabs: indent === 'tab',
} satisfies VendoredPrettierOptions,
options.prettierOptions || {},
)

const prettierXmlOptions: VendoredPrettierOptions = {
xmlQuoteAttributes: 'double',
xmlSelfClosingSpace: true,
xmlSortAttributesByKey: false,
xmlWhitespaceSensitivity: 'ignore',
}

const dprintOptions = Object.assign(
{
indentWidth: typeof indent === 'number' ? indent : 2,
quoteStyle: quotes === 'single' ? 'preferSingle' : 'preferDouble',
useTabs: indent === 'tab',
},
options.dprintOptions || {},
)

const pluginFormat = await interopDefault(import('eslint-plugin-format'))

const configs: Linter.Config[] = [
{
name: 'nuxt/formatter/setup',
plugins: {
format: pluginFormat,
},
},
]

if (options.css) {
configs.push(
{
files: [GLOB_CSS, GLOB_POSTCSS],
languageOptions: {
parser: parserPlain,
},
name: 'nuxt/formatter/css',
rules: {
'format/prettier': [
'error',
mergePrettierOptions(prettierOptions, {
parser: 'css',
}),
],
},
},
{
files: [GLOB_SCSS],
languageOptions: {
parser: parserPlain,
},
name: 'nuxt/formatter/scss',
rules: {
'format/prettier': [
'error',
mergePrettierOptions(prettierOptions, {
parser: 'scss',
}),
],
},
},
{
files: [GLOB_LESS],
languageOptions: {
parser: parserPlain,
},
name: 'nuxt/formatter/less',
rules: {
'format/prettier': [
'error',
mergePrettierOptions(prettierOptions, {
parser: 'less',
}),
],
},
},
)
}

if (options.html) {
configs.push({
files: [GLOB_HTML],
languageOptions: {
parser: parserPlain,
},
name: 'nuxt/formatter/html',
rules: {
'format/prettier': [
'error',
mergePrettierOptions(prettierOptions, {
parser: 'html',
}),
],
},
})
}

if (options.xml) {
configs.push({
files: [GLOB_XML],
languageOptions: {
parser: parserPlain,
},
name: 'nuxt/formatter/xml',
rules: {
'format/prettier': [
'error',
mergePrettierOptions({ ...prettierXmlOptions, ...prettierOptions }, {
parser: 'xml',
plugins: [
'@prettier/plugin-xml',
],
}),
],
},
})
}
if (options.svg) {
configs.push({
files: [GLOB_SVG],
languageOptions: {
parser: parserPlain,
},
name: 'nuxt/formatter/svg',
rules: {
'format/prettier': [
'error',
mergePrettierOptions({ ...prettierXmlOptions, ...prettierOptions }, {
parser: 'xml',
plugins: [
'@prettier/plugin-xml',
],
}),
],
},
})
}

if (options.markdown) {
const formater = options.markdown === true
? 'prettier'
: options.markdown

configs.push({
files: [GLOB_MARKDOWN],
languageOptions: {
parser: parserPlain,
},
name: 'nuxt/formatter/markdown',
rules: {
[`format/${formater}`]: [
'error',
formater === 'prettier'
? mergePrettierOptions(prettierOptions, {
embeddedLanguageFormatting: 'off',
parser: 'markdown',
})
: {
...dprintOptions,
language: 'markdown',
},
],
},
})
}

if (options.graphql) {
configs.push({
files: [GLOB_GRAPHQL],
languageOptions: {
parser: parserPlain,
},
name: 'nuxt/formatter/graphql',
rules: {
'format/prettier': [
'error',
mergePrettierOptions(prettierOptions, {
parser: 'graphql',
}),
],
},
})
}

return configs
}
1 change: 0 additions & 1 deletion packages/eslint-config/src/flat/configs/javascript.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-expect-error missing types
import pluginESLint from '@eslint/js'
import type { Linter } from 'eslint'
import globals from 'globals'
Expand Down
27 changes: 22 additions & 5 deletions packages/eslint-config/src/flat/configs/vue.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as parserVue from 'vue-eslint-parser'
import pluginVue from 'eslint-plugin-vue'
import processorVueBlocks from 'eslint-processor-vue-blocks'
import type { Linter } from 'eslint'
import { mergeProcessors } from 'eslint-merge-processors'
import type { NuxtESLintConfigOptions } from '../types'
import { removeUndefined, resolveOptions } from '../utils'

Expand All @@ -20,7 +22,7 @@ export default async function vue(options: NuxtESLintConfigOptions): Promise<Lin
commaDangle = 'always-multiline',
} = typeof resolved.features.stylistic === 'boolean' ? {} : resolved.features.stylistic

return [
const configs: Linter.Config[] = [
{
name: 'nuxt/vue/setup',
plugins: {
Expand Down Expand Up @@ -65,9 +67,17 @@ export default async function vue(options: NuxtESLintConfigOptions): Promise<Lin
languageOptions: {
parser: parserVue,
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
processor: pluginVue.processors['.vue'] as any,
rules: removeUndefined({
processor: options.features?.formatters
? mergeProcessors([
pluginVue.processors['.vue'],
processorVueBlocks({
blocks: {
styles: true,
},
}),
])
: pluginVue.processors['.vue'],
rules: {
...pluginVue.configs.base.rules,
...pluginVue.configs['vue3-essential'].rules,
...pluginVue.configs['vue3-strongly-recommended'].rules,
Expand Down Expand Up @@ -134,7 +144,14 @@ export default async function vue(options: NuxtESLintConfigOptions): Promise<Lin
'vue/no-spaces-around-equal-signs-in-attribute': undefined,
'vue/singleline-html-element-content-newline': undefined,
}),
}),
},
},
]

for (const config of configs) {
if (config.rules)
config.rules = removeUndefined(config.rules)
}

return configs
}
Loading

0 comments on commit f6d8bb6

Please sign in to comment.