Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: eslint-plugin #2140

Merged
merged 7 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ interactive/guides/vendor/*.md
interactive/data/guides.ts
defaultConfig.ts
packages/preset-icons/src/collections.json
packages/eslint-plugin/fixtures
61 changes: 56 additions & 5 deletions packages/core/src/utils/variantGroup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type MagicString from 'magic-string'
import { notNull } from '../utils'

const regexCache: Record<string, RegExp> = {}

Expand All @@ -10,19 +11,22 @@ export function makeRegexClassGroup(separators = ['-', ':']) {
return regexCache[key]
}

export function expandVariantGroup(str: string, separators?: string[], depth?: number): string
export function expandVariantGroup(str: MagicString, separators?: string[], depth?: number): MagicString
export function expandVariantGroup(str: string | MagicString, separators = ['-', ':'], depth = 5) {
export function parseVariantGroup(str: string, separators = ['-', ':'], depth = 5) {
const regexClassGroup = makeRegexClassGroup(separators)
let hasChanged = false
let content = str.toString()
const prefixes = new Set<string>()

do {
const before = content
content = content.replace(
regexClassGroup,
(from, pre, sep, body: string) => {
if (!separators.includes(sep))
return from

prefixes.add(pre + sep)

return body
.split(/\s/g)
.filter(Boolean)
Expand All @@ -34,12 +38,59 @@ export function expandVariantGroup(str: string | MagicString, separators = ['-',
depth -= 1
} while (hasChanged && depth)

return {
prefixes: Array.from(prefixes),
expanded: content,
hasChanged,
}
}

export function collapseVariantGroup(str: string, prefixes: string[]): string {
const collection = new Map<string, string[]>()

const sortedPrefix = prefixes.sort((a, b) => b.length - a.length)

return str.split(/\s+/g).map((part) => {
const prefix = sortedPrefix.find(prefix => part.startsWith(prefix))
if (!prefix)
return part

const body = part.slice(prefix.length)
if (collection.has(prefix)) {
collection.get(prefix)!.push(body)
return null
}
else {
const items = [body]
collection.set(prefix, items)
return {
prefix,
items,
}
}
})
.filter(notNull)
.map((i) => {
if (typeof i === 'string')
return i
return `${i.prefix}(${i.items.join(' ')})`
})
.join(' ')
}

export function expandVariantGroup(str: string, separators?: string[], depth?: number): string
export function expandVariantGroup(str: MagicString, separators?: string[], depth?: number): MagicString
export function expandVariantGroup(str: string | MagicString, separators = ['-', ':'], depth = 5) {
const {
expanded,
} = parseVariantGroup(str.toString(), separators, depth)

if (typeof str === 'string') {
return content
return expanded
}
else {
return str.length()
? str.overwrite(0, str.length(), content)
? str.overwrite(0, str.length(), expanded)
: str
}
}
29 changes: 29 additions & 0 deletions packages/eslint-config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# @unocss/eslint-config

ESLint config for UnoCSS.

**Currently working in progress, breaking changes may NOT follow semver.**

## Installation

```bash
npm i -D @unocss/eslint-config
```

In `.eslintrc`:

```json
{
"extends": [
"@unocss"
]
}
```

## Rules

- `@unocss/order` - Enforce a specific order for class and attribute selectors.

## Prior Arts

Thanks to [eslint-plugin-unocss](https://github.com/devunt/eslint-plugin-unocss) by [@devunt](https://github.com/devunt).
12 changes: 12 additions & 0 deletions packages/eslint-config/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
entries: [
'src/index',
],
clean: true,
declaration: true,
rollup: {
emitCJS: true,
},
})
42 changes: 42 additions & 0 deletions packages/eslint-config/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "@unocss/eslint-config",
"version": "0.0.0",
"description": "ESLint config for UnoCSS",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/unocss/unocss/tree/main/packages/eslint-config#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/unocss/unocss.git",
"directory": "packages/eslint-config"
},
"bugs": {
"url": "https://github.com/unocss/unocss/issues"
},
"keywords": ["eslint-config", "eslint"],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.cjs",
"import": "./dist/index.mjs"
}
},
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"engines": {
"node": ">=14"
},
"scripts": {
"build": "unbuild",
"stub": "unbuild --stub"
},
"dependencies": {
"@unocss/eslint-plugin": "workspace:*"
}
}
5 changes: 5 additions & 0 deletions packages/eslint-config/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
extends: [
'plugin:@unocss/recommended',
],
}
9 changes: 9 additions & 0 deletions packages/eslint-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# @unocss/eslint-plugin

ESLint plugin for UnoCSS.

**Currently working in progress, breaking changes may NOT follow semver.**

## Installation

Please refer to [@unocss/eslint-config](../eslint-config/) for installation.
14 changes: 14 additions & 0 deletions packages/eslint-plugin/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
entries: [
'src/dirs',
'src/index',
'src/worker-sort',
],
clean: true,
declaration: true,
rollup: {
emitCJS: true,
},
})
1 change: 1 addition & 0 deletions packages/eslint-plugin/fixtures/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.eslintrc.json
6 changes: 6 additions & 0 deletions packages/eslint-plugin/fixtures/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": [
"@antfu",
"plugin:@unocss/recommended"
]
}
23 changes: 23 additions & 0 deletions packages/eslint-plugin/fixtures/src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<template>
<div flex="~ gap-1 cols">
<div class="hover:text-red text-4xl mx1 m2">
<div i-carbon-campsite inline-block />
</div>
<p>
<a rel="noreferrer" href="https://github.com/antfu/vitesse" target="_blank">
Vitesse
</a>
</p>
<p>
<em text-sm opacity-75 class="foo" hover:text-red text-4xl mx1 m2>{{ t('intro.desc') }}</em>
</p>

<div class="hover:(text-red text-4xl) hover:mx1 mx1 m2">Variant group</div>

<div class="
foo bar
hover:(text-red text-4xl)
hover:mx1 mx1 m2
"></div>
</div>
</template>
7 changes: 7 additions & 0 deletions packages/eslint-plugin/fixtures/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function App() {
return (
<h1 className="text-3xl hover:text-red m1 font-bold underline">
Hello world!
</h1>
)
}
25 changes: 25 additions & 0 deletions packages/eslint-plugin/fixtures/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": [
"src"
]
}
11 changes: 11 additions & 0 deletions packages/eslint-plugin/fixtures/uno.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// eslint-disable-next-line no-restricted-imports
import { defineConfig, presetUno, transformerVariantGroup } from 'unocss'

export default defineConfig({
presets: [
presetUno(),
],
transformers: [
transformerVariantGroup(),
],
})
52 changes: 52 additions & 0 deletions packages/eslint-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "@unocss/eslint-plugin",
"version": "0.0.0",
"description": "ESLint plugin for UnoCSS",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/unocss/unocss/tree/main/packages/esling-plugin#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/unocss/unocss.git",
"directory": "packages/esling-plugin"
},
"bugs": {
"url": "https://github.com/unocss/unocss/issues"
},
"keywords": [
"eslint-plugin",
"eslint"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.cjs",
"import": "./dist/index.mjs"
}
},
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"engines": {
"node": ">=14"
},
"scripts": {
"build": "unbuild",
"lint": "nr build && cd ./fixtures && eslint ./src"
},
"dependencies": {
"@typescript-eslint/utils": "^5.50.0",
"@unocss/config": "workspace:^0.49.2",
"@unocss/core": "workspace:*",
"magic-string": "^0.27.0",
"synckit": "^0.8.5"
},
"devDependencies": {
"@unocss/eslint-plugin": "workspace:*"
}
}
7 changes: 7 additions & 0 deletions packages/eslint-plugin/src/configs/recommended.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
plugins: ['@unocss'],
rules: {
'@unocss/order': 'warn',
'@unocss/order-attributify': 'warn',
},
}
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const CLASS_FIELDS = ['class', 'classname']
3 changes: 3 additions & 0 deletions packages/eslint-plugin/src/dirs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { fileURLToPath } from 'url'

export const distDir = fileURLToPath(new URL('../dist', import.meta.url))
13 changes: 13 additions & 0 deletions packages/eslint-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import order from './rules/order'
import orderAttributify from './rules/order-attributify'
import configsRecommended from './configs/recommended'

export default {
rules: {
order,
'order-attributify': orderAttributify,
},
configs: {
recommended: configsRecommended,
},
}