Skip to content

Commit 5391a46

Browse files
committed
first pass at a svelte checker
1 parent df4917e commit 5391a46

File tree

6 files changed

+413
-5
lines changed

6 files changed

+413
-5
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"rimraf": "^3.0.2",
5656
"semver": "^7.3.5",
5757
"strip-ansi": "^7.0.0",
58+
"svelte-language-server": "^0.14.7",
5859
"ts-jest": "^27.0.3",
5960
"typescript": "^4.2.2",
6061
"vite": "^2.3.8",
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import os from 'os'
2+
import path from 'path'
3+
import * as fs from 'fs'
4+
import invariant from 'tiny-invariant'
5+
import { URI } from 'vscode-uri'
6+
import { watch } from 'chokidar'
7+
import { SvelteCheck } from 'svelte-language-server'
8+
import { parentPort } from 'worker_threads'
9+
10+
import { Checker } from '../../Checker'
11+
import {
12+
diagnosticToTerminalLog,
13+
diagnosticToViteError,
14+
normalizeLspDiagnostic,
15+
} from '../../logger'
16+
17+
import type { CreateDiagnostic } from '../../types'
18+
19+
// based off the class in svelte-check/src/index.ts
20+
class DiagnosticsWatcher {
21+
private updateDiagnostics: any
22+
private svelteCheck: SvelteCheck
23+
24+
constructor(root: string, private overlay: boolean) {
25+
this.svelteCheck = new SvelteCheck(root, {
26+
compilerWarnings: {},
27+
diagnosticSources: ['js', 'svelte', 'css'],
28+
})
29+
30+
watch(`${root}/**/*.{svelte,d.ts,ts,js}`, {
31+
ignored: ['node_modules'].map((ignore) => path.join(root, ignore)),
32+
ignoreInitial: false,
33+
})
34+
.on('add', (path) => this.updateDocument(path, true))
35+
.on('unlink', (path) => this.removeDocument(path))
36+
.on('change', (path) => this.updateDocument(path, false))
37+
}
38+
39+
private async updateDocument(path: string, isNew: boolean) {
40+
const text = fs.readFileSync(path, 'utf-8')
41+
await this.svelteCheck.upsertDocument({ text, uri: URI.file(path).toString() }, isNew)
42+
this.scheduleDiagnostics()
43+
}
44+
45+
private async removeDocument(path: string) {
46+
await this.svelteCheck.removeDocument(URI.file(path).toString())
47+
this.scheduleDiagnostics()
48+
}
49+
50+
private scheduleDiagnostics() {
51+
clearTimeout(this.updateDiagnostics)
52+
this.updateDiagnostics = setTimeout(async () => {
53+
let logChunk = ''
54+
try {
55+
const ds = await this.svelteCheck.getDiagnostics()
56+
let currErr = null
57+
58+
for (const { filePath, text, diagnostics } of ds) {
59+
for (const diagnostic of diagnostics) {
60+
const formattedDiagnostic = normalizeLspDiagnostic({
61+
diagnostic,
62+
absFilePath: filePath,
63+
fileText: text,
64+
checker: 'svelte',
65+
})
66+
67+
if (currErr == null) {
68+
currErr = diagnosticToViteError(formattedDiagnostic)
69+
}
70+
logChunk += os.EOL + diagnosticToTerminalLog(formattedDiagnostic, 'svelte')
71+
}
72+
}
73+
74+
if (this.overlay) {
75+
parentPort?.postMessage({
76+
type: 'ERROR',
77+
payload: {
78+
type: 'error',
79+
err: currErr,
80+
},
81+
})
82+
}
83+
84+
console.log(logChunk)
85+
} catch (err) {
86+
if (this.overlay) {
87+
parentPort?.postMessage({
88+
type: 'ERROR',
89+
payload: {
90+
type: 'error',
91+
err: err.message,
92+
},
93+
})
94+
}
95+
console.error(err.message)
96+
}
97+
}, 1000)
98+
}
99+
}
100+
101+
const createDiagnostic: CreateDiagnostic<'svelte'> = (pluginConfig) => {
102+
let overlay = true // Vite defaults to true
103+
104+
return {
105+
config: ({ hmr }) => {
106+
const viteOverlay = !(typeof hmr === 'object' && hmr.overlay === false)
107+
108+
if (pluginConfig.overlay === false || !viteOverlay) {
109+
overlay = false
110+
}
111+
},
112+
configureServer({ root }) {
113+
invariant(pluginConfig.svelte, 'config.svelte should be `false`')
114+
115+
if (pluginConfig.svelte !== true && pluginConfig.svelte.root) {
116+
root = pluginConfig.svelte.root
117+
}
118+
119+
let watcher = new DiagnosticsWatcher(root, overlay)
120+
return watcher
121+
},
122+
}
123+
}
124+
125+
export class SvelteChecker extends Checker<'svelte'> {
126+
public constructor() {
127+
super({
128+
name: 'svelte',
129+
absFilePath: __filename,
130+
build: {
131+
buildBin: (_config) => {
132+
return ['svelte-check', []]
133+
},
134+
},
135+
createDiagnostic,
136+
})
137+
}
138+
139+
public init() {
140+
const createServeAndBuild = super.initMainThread()
141+
module.exports.createServeAndBuild = createServeAndBuild
142+
143+
super.initWorkerThread()
144+
}
145+
}
146+
147+
const svelteChecker = new SvelteChecker()
148+
svelteChecker.prepare()
149+
svelteChecker.init()

packages/vite-plugin-checker/src/logger.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export enum DiagnosticLevel {
5353

5454
export function diagnosticToTerminalLog(
5555
d: NormalizedDiagnostic,
56-
name?: 'TypeScript' | 'vue-tsc' | 'VLS' | 'ESLint'
56+
name?: 'TypeScript' | 'vue-tsc' | 'VLS' | 'ESLint' | 'svelte'
5757
): string {
5858
const nameInLabel = name ? `(${name})` : ''
5959
const boldBlack = chalk.bold.rgb(0, 0, 0)
@@ -180,10 +180,12 @@ export function normalizeLspDiagnostic({
180180
diagnostic,
181181
absFilePath,
182182
fileText,
183+
checker = 'VLS',
183184
}: {
184185
diagnostic: LspDiagnostic
185186
absFilePath: string
186187
fileText: string
188+
checker?: string
187189
}): NormalizedDiagnostic {
188190
let level = DiagnosticLevel.Error
189191
const loc = lspRange2Location(diagnostic.range)
@@ -210,7 +212,7 @@ export function normalizeLspDiagnostic({
210212
codeFrame,
211213
stripedCodeFrame: codeFrame && strip(codeFrame),
212214
id: absFilePath,
213-
checker: 'VLS',
215+
checker,
214216
loc,
215217
level,
216218
}

packages/vite-plugin-checker/src/main.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ export * from './codeFrame'
1919
export * from './worker'
2020

2121
const sharedConfigKeys: (keyof SharedConfig)[] = ['enableBuild', 'overlay']
22-
const buildInCheckerKeys: BuildInCheckerNames[] = ['typescript', 'vueTsc', 'vls', 'eslint']
22+
const buildInCheckerKeys: BuildInCheckerNames[] = [
23+
'typescript',
24+
'vueTsc',
25+
'vls',
26+
'eslint',
27+
'svelte',
28+
]
2329

2430
function createCheckers(userConfig: UserPluginConfig, env: ConfigEnv): ServeAndBuildChecker[] {
2531
const serveAndBuildCheckers: ServeAndBuildChecker[] = []

packages/vite-plugin-checker/src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ export type EslintConfig =
4545
// watchDelay?: number
4646
}
4747

48+
export type SvelteConfig =
49+
| boolean
50+
| Partial<{
51+
root?: string
52+
// TODO: support svelte config
53+
}>
54+
4855
/** checkers shared configuration */
4956
export interface SharedConfig {
5057
/**
@@ -65,6 +72,7 @@ export interface BuildInCheckers {
6572
vueTsc: VueTscConfig
6673
vls: VlsConfig
6774
eslint: EslintConfig
75+
svelte: SvelteConfig
6876
}
6977

7078
export type BuildInCheckerNames = keyof BuildInCheckers

0 commit comments

Comments
 (0)