Skip to content

Commit

Permalink
feat: add Stylelint basic support (#158)
Browse files Browse the repository at this point in the history
* feat: add stylelint basic support

* fix: rename variables

* chore: {packages} -> packages

* feat: match upstream changes
  • Loading branch information
ModyQyW authored Oct 13, 2022
1 parent 140f96c commit 333ba13
Show file tree
Hide file tree
Showing 15 changed files with 796 additions and 13 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Visit [documentation](https://vite-plugin-checker.netlify.app) for usage.
# Visit [documentation](https://vite-plugin-checker.netlify.app) for usage

A Vite plugin that can run TypeScript, VLS, vue-tsc, ESLint in worker thread.
A Vite plugin that can run TypeScript, VLS, vue-tsc, ESLint, Stylelint in worker thread.

<p align="center">
<img alt="screenshot" src="https://user-images.githubusercontent.com/12322740/152739742-7444ee62-9ca7-4379-8f02-495c612ecc5c.png">
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ function sidebar() {
{ text: 'vue-tsc', link: '/checkers/vue-tsc' },
{ text: 'ESLint', link: '/checkers/eslint' },
{ text: 'VLS', link: '/checkers/vls' },
{ text: 'Stylelint', link: '/checkers/stylelint' },
],
},
{
Expand Down
4 changes: 2 additions & 2 deletions docs/checkers/overview.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Checkers overview

vite-plugin-checkers provide built-in checkers. For now, it provides [TypeScript](/checkers/typescript), [ESLint](/checkers/eslint), [vue-tsc](/checkers/vue-tsc), [VLS](/checkers/vls).
vite-plugin-checkers provide built-in checkers. For now, it provides [TypeScript](/checkers/typescript), [ESLint](/checkers/eslint), [vue-tsc](/checkers/vue-tsc), [VLS](/checkers/vls), [Stylelint](/checkers/stylelint).

## How to add a checker

- Set to `true` to use a checker with its default value (except ESLint).
- Set to `true` to use a checker with its default value (except ESLint and Stylelint).
- Leave the field blank or `false` to disable the checker.
- Make sure to install the peer dependencies indicated of each checker.
- Checker can be enabled with an advanced object config.
35 changes: 35 additions & 0 deletions docs/checkers/stylelint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Stylelint

## Installation

1. Make sure [stylelint](https://www.npmjs.com/package/stylelint) and related plugins for your `stylelintrc` are installed as peer dependencies.

::: warning
**(Optional but highly recommended)** Install `meow@^9.0.0` with your package manager. It's needed because of Stylelint dependents on it. It's probably working fine even it's not installed as it's accessed as a phantom dependency. But when you set `hoist=false` of pnpm. It won't be accessible anymore without explicit installation.

:::

2. Add `stylelint` field to plugin config and `options.stylelint.lintCommand` is required. The `lintCommand` is the same as the lint command of your project. The default root of the command uses Vite's [root](https://vitejs.dev/config/#root).

```js
// e.g.
export default {
plugins: [
checker({
stylelint: {
lintCommand: 'stylelint ./src/**/*.{css,vue}', // for example, lint .css & .vue
},
}),
],
}
```

## Configuration

Advanced object configuration table of `options.stylelint`

| field | Type | Default value | Description |
| :----------------- | -------------------------------------------------------------------------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| lintCommand | `string` | This value is required | `lintCommand` will be executed at build mode, and will also be used as default config for dev mode when `stylelint.dev.stylelint` is nullable. |
| dev.overrideConfig | [`Stylelint.LinterOptions`](https://github.com/stylelint/stylelint/blob/main/types/stylelint/index.d.ts) | `undefined` | **(Only in dev mode)** You can override the options of the translated from `lintCommand`. Config priority: `stylelint.lint({ cwd: root, ...translatedOptions, ...pluginConfig.stylelint.dev?.overrideConfig, })`. |
| dev.logLevel | `('error' \| 'warning')[]` | `['error', 'warning']` | **(Only in dev mode)** Which level of Stylelint should be emitted to terminal and overlay in dev mode |
2 changes: 1 addition & 1 deletion docs/introduction/introduction.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# About vite-plugin-checker

A Vite plugin that can run TypeScript, VLS, vue-tsc, ESLint in worker thread.
A Vite plugin that can run TypeScript, VLS, vue-tsc, ESLint, Stylelint in worker thread.

<div :style="{ 'display': 'flex' }">
<a href="https://www.npmjs.com/package/vite-plugin-checker" :style="{ 'margin-right': '4px' }"><img src="https://img.shields.io/npm/v/vite-plugin-checker" /></a>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"pre-commit": "pnpm exec lint-staged"
},
"lint-staged": {
"{packages}/**/*.{js,ts}": [
"packages/**/*.{js,ts}": [
"eslint --fix",
"prettier --write",
"git add"
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/src/components/Diagnostic.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
ESLint: '#7b7fe3',
VLS: '#64b587',
'vue-tsc': '#64b587',
Stylelint: '#ffffff',
}
const fileRE = /(?:[a-zA-Z]:\\|\/).*(:\d+:\d+)?/g
Expand Down
10 changes: 10 additions & 0 deletions packages/vite-plugin-checker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
},
"peerDependencies": {
"eslint": ">=7",
"meow": "^9.0.0",
"stylelint": ">=13",
"typescript": "*",
"vite": "^2.0.0 || ^3.0.0-0",
"vls": "*",
Expand All @@ -69,6 +71,12 @@
"eslint": {
"optional": true
},
"meow": {
"optional": true
},
"stylelint": {
"optional": true
},
"typescript": {
"optional": true
},
Expand All @@ -88,8 +96,10 @@
"@types/lodash.pick": "^4.4.6",
"@volar/vue-typescript": "^0.33.0",
"esbuild": "^0.14.27",
"meow": "^9.0.0",
"npm-run-all": "^4.1.5",
"optionator": "^0.9.1",
"stylelint": "^14.0.0",
"tsup": "^6.2.2",
"typescript": "~4.5.5",
"vls": "^0.7.6",
Expand Down
8 changes: 6 additions & 2 deletions packages/vite-plugin-checker/src/Checker.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import invariant from 'tiny-invariant'
import { isInVitestEntryThread, isMainThread } from './utils.js'

import type { ServeAndBuildChecker, BuildInCheckerNames } from './types.js'
import { createScript, Script } from './worker.js'

// still an only issue https://github.com/microsoft/TypeScript/issues/29808#issuecomment-829750974
import type {} from 'vite'
import type { CreateDiagnostic, BuildInCheckers } from './types.js'
import type {
CreateDiagnostic,
BuildInCheckers,
ServeAndBuildChecker,
BuildInCheckerNames,
} from './types.js'

if (!(isMainThread || isInVitestEntryThread)) {
process.stdout.isTTY = true
Expand Down
145 changes: 145 additions & 0 deletions packages/vite-plugin-checker/src/checkers/stylelint/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import chokidar from 'chokidar'
import stylelint from 'stylelint'
import translateOptions from './options'
import path from 'path'
import { fileURLToPath } from 'url'
import { parentPort } from 'worker_threads'

import { Checker } from '../../Checker.js'
import { FileDiagnosticManager } from '../../FileDiagnosticManager.js'
import {
composeCheckerSummary,
consoleLog,
diagnosticToRuntimeError,
diagnosticToTerminalLog,
filterLogLevel,
normalizeStylelintDiagnostic,
toViteCustomPayload,
} from '../../logger.js'
import { ACTION_TYPES, DiagnosticLevel } from '../../types.js'

const manager = new FileDiagnosticManager()

import type { CreateDiagnostic } from '../../types.js'

const __filename = fileURLToPath(import.meta.url)

const createDiagnostic: CreateDiagnostic<'stylelint'> = (pluginConfig) => {
let overlay = true
let terminal = true

return {
config: async ({ enableOverlay, enableTerminal }) => {
overlay = enableOverlay
terminal = enableTerminal
},
async configureServer({ root }) {
if (!pluginConfig.stylelint) return

const translatedOptions = translateOptions(pluginConfig.stylelint.lintCommand)

const logLevel = (() => {
if (typeof pluginConfig.stylelint !== 'object') return undefined
const userLogLevel = pluginConfig.stylelint.dev?.logLevel
if (!userLogLevel) return undefined
const map = {
error: DiagnosticLevel.Error,
warning: DiagnosticLevel.Warning,
} as const

return userLogLevel.map((l) => map[l])
})()

const dispatchDiagnostics = () => {
const diagnostics = filterLogLevel(manager.getDiagnostics(), logLevel)

if (terminal) {
diagnostics.forEach((d) => {
consoleLog(diagnosticToTerminalLog(d, 'Stylelint'))
})
const errorCount = diagnostics.filter((d) => d.level === DiagnosticLevel.Error).length
const warningCount = diagnostics.filter((d) => d.level === DiagnosticLevel.Warning).length
consoleLog(composeCheckerSummary('Stylelint', errorCount, warningCount))
}

if (overlay) {
parentPort?.postMessage({
type: ACTION_TYPES.overlayError,
payload: toViteCustomPayload(
'stylelint',
diagnostics.map((d) => diagnosticToRuntimeError(d))
),
})
}
}

const handleFileChange = async (filePath: string, type: 'change' | 'unlink') => {
const absPath = path.resolve(root, filePath)

if (type === 'unlink') {
manager.updateByFileId(absPath, [])
} else if (type === 'change') {
const { results: diagnosticsOfChangedFile } = await stylelint.lint({ files: filePath })
const newDiagnostics = diagnosticsOfChangedFile
.map((d) => normalizeStylelintDiagnostic(d))
.flat(1)
manager.updateByFileId(absPath, newDiagnostics)
}

dispatchDiagnostics()
}

// initial lint
const { results: diagnostics } = await stylelint.lint({
cwd: root,
...translatedOptions,
...pluginConfig.stylelint.dev?.overrideConfig,
})

manager.initWith(diagnostics.map((p) => normalizeStylelintDiagnostic(p)).flat(1))
dispatchDiagnostics()

// watch lint
const watcher = chokidar.watch([], {
cwd: root,
ignored: (path: string) => path.includes('node_modules'),
})
watcher.add(translatedOptions.files as string)
watcher.on('change', async (filePath) => {
handleFileChange(filePath, 'change')
})
watcher.on('unlink', async (filePath) => {
handleFileChange(filePath, 'unlink')
})
},
}
}

export class StylelintChecker extends Checker<'stylelint'> {
public constructor() {
super({
name: 'stylelint',
absFilePath: __filename,
build: {
buildBin: (pluginConfig) => {
if (pluginConfig.stylelint) {
const { lintCommand } = pluginConfig.stylelint
return ['stylelint', lintCommand.split(' ').slice(1)]
}
return ['stylelint', ['']]
},
},
createDiagnostic,
})
}

public init() {
const createServeAndBuild = super.initMainThread()
module.exports.createServeAndBuild = createServeAndBuild
super.initWorkerThread()
}
}

const stylelintChecker = new StylelintChecker()
stylelintChecker.prepare()
stylelintChecker.init()
Loading

0 comments on commit 333ba13

Please sign in to comment.