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

A basic svelte checker #73

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"strip-ansi": "^7.0.0",
"svelte-language-server": "^0.14.7",
"ts-jest": "^27.0.3",
"typescript": "^4.2.2",
"vite": "^2.3.8",
Expand Down
152 changes: 152 additions & 0 deletions packages/vite-plugin-checker/src/checkers/svelte/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import os from 'os'
import path from 'path'
import * as fs from 'fs'
import invariant from 'tiny-invariant'
import { URI } from 'vscode-uri'
import { watch } from 'chokidar'
import { SvelteCheck } from 'svelte-language-server'
import { parentPort } from 'worker_threads'

import { Checker } from '../../Checker'
import {
diagnosticToTerminalLog,
diagnosticToViteError,
normalizeLspDiagnostic,
} from '../../logger'

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

// based off the class in svelte-check/src/index.ts
class DiagnosticsWatcher {
private updateDiagnostics: any
private svelteCheck: SvelteCheck
private overlay: boolean

public constructor(root: string, overlay: boolean) {
this.overlay = overlay
this.svelteCheck = new SvelteCheck(root, {
compilerWarnings: {},
diagnosticSources: ['js', 'svelte', 'css'],
})

watch(`${root}/**/*.{svelte,d.ts,ts,js}`, {
ignored: ['node_modules'].map((ignore) => path.join(root, ignore)),
ignoreInitial: false,
})
.on('add', (path) => this.updateDocument(path, true))
.on('unlink', (path) => this.removeDocument(path))
.on('change', (path) => this.updateDocument(path, false))
}

private async updateDocument(path: string, isNew: boolean) {
const text = fs.readFileSync(path, 'utf-8')
await this.svelteCheck.upsertDocument({ text, uri: URI.file(path).toString() }, isNew)
this.scheduleDiagnostics()
}

private async removeDocument(path: string) {
await this.svelteCheck.removeDocument(URI.file(path).toString())
this.scheduleDiagnostics()
}

private scheduleDiagnostics() {
clearTimeout(this.updateDiagnostics)
this.updateDiagnostics = setTimeout(async () => {
let logChunk = ''
try {
const ds = await this.svelteCheck.getDiagnostics()
let currErr = null

for (const { filePath, text, diagnostics } of ds) {
for (const diagnostic of diagnostics) {
const formattedDiagnostic = normalizeLspDiagnostic({
diagnostic,
absFilePath: filePath,
fileText: text,
checker: 'svelte',
})

if (currErr === null) {
currErr = diagnosticToViteError(formattedDiagnostic)
}
logChunk += os.EOL + diagnosticToTerminalLog(formattedDiagnostic, 'svelte')
}
}

if (this.overlay) {
parentPort?.postMessage({
type: 'ERROR',
payload: {
type: 'error',
err: currErr,
},
})
}

console.log(logChunk)
} catch (err) {
if (this.overlay) {
parentPort?.postMessage({
type: 'ERROR',
payload: {
type: 'error',
err: err.message,
},
})
}
console.error(err.message)
}
}, 1000)
}
}

const createDiagnostic: CreateDiagnostic<'svelte'> = (pluginConfig) => {
let overlay = true // Vite defaults to true

return {
config: ({ hmr }) => {
const viteOverlay = !(typeof hmr === 'object' && hmr.overlay === false)

if (pluginConfig.overlay === false || !viteOverlay) {
overlay = false
}
},
configureServer({ root }) {
invariant(pluginConfig.svelte, 'config.svelte should be `false`')
let svelteRoot = root

if (pluginConfig.svelte !== true && pluginConfig.svelte.root) {
svelteRoot = pluginConfig.svelte.root
}

let watcher = new DiagnosticsWatcher(svelteRoot, overlay)
return watcher
},
}
}

export class SvelteChecker extends Checker<'svelte'> {
public constructor() {
super({
name: 'svelte',
absFilePath: __filename,
build: {
buildBin: (_config) => {
return ['svelte-check', []]
},
},
createDiagnostic,
})
}

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

super.initWorkerThread()
}
}

const svelteChecker = new SvelteChecker()
svelteChecker.prepare()
svelteChecker.init()
6 changes: 4 additions & 2 deletions packages/vite-plugin-checker/src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export enum DiagnosticLevel {

export function diagnosticToTerminalLog(
d: NormalizedDiagnostic,
name?: 'TypeScript' | 'vue-tsc' | 'VLS' | 'ESLint'
name?: 'TypeScript' | 'vue-tsc' | 'VLS' | 'ESLint' | 'svelte'
): string {
const nameInLabel = name ? `(${name})` : ''
const boldBlack = chalk.bold.rgb(0, 0, 0)
Expand Down Expand Up @@ -180,10 +180,12 @@ export function normalizeLspDiagnostic({
diagnostic,
absFilePath,
fileText,
checker = 'VLS',
}: {
diagnostic: LspDiagnostic
absFilePath: string
fileText: string
checker?: string
}): NormalizedDiagnostic {
let level = DiagnosticLevel.Error
const loc = lspRange2Location(diagnostic.range)
Expand All @@ -210,7 +212,7 @@ export function normalizeLspDiagnostic({
codeFrame,
stripedCodeFrame: codeFrame && strip(codeFrame),
id: absFilePath,
checker: 'VLS',
checker,
loc,
level,
}
Expand Down
8 changes: 7 additions & 1 deletion packages/vite-plugin-checker/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ export * from './codeFrame'
export * from './worker'

const sharedConfigKeys: (keyof SharedConfig)[] = ['enableBuild', 'overlay']
const buildInCheckerKeys: BuildInCheckerNames[] = ['typescript', 'vueTsc', 'vls', 'eslint']
const buildInCheckerKeys: BuildInCheckerNames[] = [
'typescript',
'vueTsc',
'vls',
'eslint',
'svelte',
]

function createCheckers(userConfig: UserPluginConfig, env: ConfigEnv): ServeAndBuildChecker[] {
const serveAndBuildCheckers: ServeAndBuildChecker[] = []
Expand Down
8 changes: 8 additions & 0 deletions packages/vite-plugin-checker/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ export type EslintConfig =
// watchDelay?: number
}

export type SvelteConfig =
| boolean
| Partial<{
root?: string
// TODO: support svelte config
}>

/** checkers shared configuration */
export interface SharedConfig {
/**
Expand All @@ -65,6 +72,7 @@ export interface BuildInCheckers {
vueTsc: VueTscConfig
vls: VlsConfig
eslint: EslintConfig
svelte: SvelteConfig
}

export type BuildInCheckerNames = keyof BuildInCheckers
Expand Down
Loading