Skip to content

Commit 9127b52

Browse files
committed
first pass at a svelte checker
1 parent df4917e commit 9127b52

File tree

6 files changed

+416
-5
lines changed

6 files changed

+416
-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: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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+
private overlay: boolean
24+
25+
public constructor(root: string, overlay: boolean) {
26+
this.overlay = overlay
27+
this.svelteCheck = new SvelteCheck(root, {
28+
compilerWarnings: {},
29+
diagnosticSources: ['js', 'svelte', 'css'],
30+
})
31+
32+
watch(`${root}/**/*.{svelte,d.ts,ts,js}`, {
33+
ignored: ['node_modules'].map((ignore) => path.join(root, ignore)),
34+
ignoreInitial: false,
35+
})
36+
.on('add', (path) => this.updateDocument(path, true))
37+
.on('unlink', (path) => this.removeDocument(path))
38+
.on('change', (path) => this.updateDocument(path, false))
39+
}
40+
41+
private async updateDocument(path: string, isNew: boolean) {
42+
const text = fs.readFileSync(path, 'utf-8')
43+
await this.svelteCheck.upsertDocument({ text, uri: URI.file(path).toString() }, isNew)
44+
this.scheduleDiagnostics()
45+
}
46+
47+
private async removeDocument(path: string) {
48+
await this.svelteCheck.removeDocument(URI.file(path).toString())
49+
this.scheduleDiagnostics()
50+
}
51+
52+
private scheduleDiagnostics() {
53+
clearTimeout(this.updateDiagnostics)
54+
this.updateDiagnostics = setTimeout(async () => {
55+
let logChunk = ''
56+
try {
57+
const ds = await this.svelteCheck.getDiagnostics()
58+
let currErr = null
59+
60+
for (const { filePath, text, diagnostics } of ds) {
61+
for (const diagnostic of diagnostics) {
62+
const formattedDiagnostic = normalizeLspDiagnostic({
63+
diagnostic,
64+
absFilePath: filePath,
65+
fileText: text,
66+
checker: 'svelte',
67+
})
68+
69+
if (currErr === null) {
70+
currErr = diagnosticToViteError(formattedDiagnostic)
71+
}
72+
logChunk += os.EOL + diagnosticToTerminalLog(formattedDiagnostic, 'svelte')
73+
}
74+
}
75+
76+
if (this.overlay) {
77+
parentPort?.postMessage({
78+
type: 'ERROR',
79+
payload: {
80+
type: 'error',
81+
err: currErr,
82+
},
83+
})
84+
}
85+
86+
console.log(logChunk)
87+
} catch (err) {
88+
if (this.overlay) {
89+
parentPort?.postMessage({
90+
type: 'ERROR',
91+
payload: {
92+
type: 'error',
93+
err: err.message,
94+
},
95+
})
96+
}
97+
console.error(err.message)
98+
}
99+
}, 1000)
100+
}
101+
}
102+
103+
const createDiagnostic: CreateDiagnostic<'svelte'> = (pluginConfig) => {
104+
let overlay = true // Vite defaults to true
105+
106+
return {
107+
config: ({ hmr }) => {
108+
const viteOverlay = !(typeof hmr === 'object' && hmr.overlay === false)
109+
110+
if (pluginConfig.overlay === false || !viteOverlay) {
111+
overlay = false
112+
}
113+
},
114+
configureServer({ root }) {
115+
invariant(pluginConfig.svelte, 'config.svelte should be `false`')
116+
let svelteRoot = root
117+
118+
if (pluginConfig.svelte !== true && pluginConfig.svelte.root) {
119+
svelteRoot = pluginConfig.svelte.root
120+
}
121+
122+
let watcher = new DiagnosticsWatcher(svelteRoot, overlay)
123+
return watcher
124+
},
125+
}
126+
}
127+
128+
export class SvelteChecker extends Checker<'svelte'> {
129+
public constructor() {
130+
super({
131+
name: 'svelte',
132+
absFilePath: __filename,
133+
build: {
134+
buildBin: (_config) => {
135+
return ['svelte-check', []]
136+
},
137+
},
138+
createDiagnostic,
139+
})
140+
}
141+
142+
public init() {
143+
const createServeAndBuild = super.initMainThread()
144+
module.exports.createServeAndBuild = createServeAndBuild
145+
146+
super.initWorkerThread()
147+
}
148+
}
149+
150+
const svelteChecker = new SvelteChecker()
151+
svelteChecker.prepare()
152+
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)