Skip to content

Commit 9d684bb

Browse files
committed
first pass at a svelte checker
1 parent df4917e commit 9d684bb

File tree

6 files changed

+407
-8
lines changed

6 files changed

+407
-8
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: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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, absFilePath: filePath, fileText: text, checker: 'svelte'
62+
})
63+
64+
if (currErr == null) {
65+
currErr = diagnosticToViteError(formattedDiagnostic)
66+
}
67+
logChunk += os.EOL + diagnosticToTerminalLog(formattedDiagnostic, 'svelte')
68+
}
69+
}
70+
71+
if (this.overlay) {
72+
parentPort?.postMessage({
73+
type: 'ERROR',
74+
payload: {
75+
type: 'error',
76+
err: currErr,
77+
},
78+
})
79+
}
80+
81+
console.log(logChunk)
82+
} catch (err) {
83+
if (this.overlay) {
84+
parentPort?.postMessage({
85+
type: 'ERROR',
86+
payload: {
87+
type: 'error',
88+
err: err.message,
89+
},
90+
})
91+
}
92+
console.error(err.message)
93+
}
94+
}, 1000);
95+
}
96+
}
97+
98+
const createDiagnostic: CreateDiagnostic<'svelte'> = (pluginConfig) => {
99+
let overlay = true // Vite defaults to true
100+
101+
return {
102+
config: ({ hmr }) => {
103+
const viteOverlay = !(typeof hmr === 'object' && hmr.overlay === false)
104+
105+
if (pluginConfig.overlay === false || !viteOverlay) {
106+
overlay = false
107+
}
108+
},
109+
configureServer({ root }) {
110+
invariant(pluginConfig.svelte, 'config.svelte should be `false`')
111+
112+
if (pluginConfig.svelte !== true && pluginConfig.svelte.root) {
113+
root = pluginConfig.svelte.root
114+
}
115+
116+
let watcher = new DiagnosticsWatcher(root, overlay);
117+
return watcher;
118+
},
119+
}
120+
}
121+
122+
export class SvelteChecker extends Checker<'svelte'> {
123+
public constructor() {
124+
super({
125+
name: 'svelte',
126+
absFilePath: __filename,
127+
build: {
128+
buildBin: (_config) => {
129+
return ['svelte-check', []]
130+
},
131+
},
132+
createDiagnostic,
133+
})
134+
}
135+
136+
public init() {
137+
const createServeAndBuild = super.initMainThread()
138+
module.exports.createServeAndBuild = createServeAndBuild
139+
140+
super.initWorkerThread()
141+
}
142+
}
143+
144+
const svelteChecker = new SvelteChecker()
145+
svelteChecker.prepare()
146+
svelteChecker.init()

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

Lines changed: 7 additions & 5 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
}: {
184-
diagnostic: LspDiagnostic
185-
absFilePath: string
186-
fileText: string
185+
diagnostic: LspDiagnostic,
186+
absFilePath: string,
187+
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ 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[] = ['typescript', 'vueTsc', 'vls', 'eslint', 'svelte']
2323

2424
function createCheckers(userConfig: UserPluginConfig, env: ConfigEnv): ServeAndBuildChecker[] {
2525
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)