Skip to content

Commit

Permalink
first pass at a svelte checker
Browse files Browse the repository at this point in the history
  • Loading branch information
SohumB committed Aug 27, 2021
1 parent df4917e commit 9d684bb
Show file tree
Hide file tree
Showing 6 changed files with 407 additions and 8 deletions.
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
146 changes: 146 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,146 @@
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;

constructor(root: string, private overlay: boolean) {
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`')

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

let watcher = new DiagnosticsWatcher(root, 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()
12 changes: 7 additions & 5 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
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
2 changes: 1 addition & 1 deletion packages/vite-plugin-checker/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ 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

0 comments on commit 9d684bb

Please sign in to comment.