From c8f0071c4f5336dfe0efc5d3c218ab49f2401264 Mon Sep 17 00:00:00 2001 From: Elena Vilchik Date: Wed, 18 Dec 2019 17:10:10 +0100 Subject: Rely on NodeJS API of Stylelint to execute CSS rules (#221) --- sonar-css-plugin/css-bundle/src/server.ts | 116 ++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 sonar-css-plugin/css-bundle/src/server.ts (limited to 'sonar-css-plugin/css-bundle/src') diff --git a/sonar-css-plugin/css-bundle/src/server.ts b/sonar-css-plugin/css-bundle/src/server.ts new file mode 100644 index 0000000..05405c8 --- /dev/null +++ b/sonar-css-plugin/css-bundle/src/server.ts @@ -0,0 +1,116 @@ +import { Server } from "http"; +import * as express from "express"; +import { AddressInfo } from "net"; +import * as stylelint from "stylelint"; +import * as fs from "fs"; +import * as bodyParser from "body-parser"; + +// for testing purposes +let log = console.log; +let logError = console.error; + +export function setLogHandlersForTests( + logHandler: typeof console.log, + errorHandler: typeof console.error +) { + log = logHandler; + logError = errorHandler; +} + +export function start(port = 0): Promise { + return new Promise(resolve => { + log("DEBUG starting stylelint-bridge server at port", port); + const app = express(); + app.use(bodyParser.json()); + app.post("/analyze", analyzeWithStylelint); + app.get("/status", (_: express.Request, resp: express.Response) => + resp.send("OK!") + ); + + // every time something is wrong we log error and send empty response (with 0 issues) + // it's important to keep this call last in configuring "app" + app.use( + ( + error: any, + _req: express.Request, + response: express.Response, + _next: any + ) => processError(error, response) + ); + + const server = app.listen(port, () => { + log( + "DEBUG stylelint-bridge server is running at port", + (server.address() as AddressInfo).port + ); + resolve(server); + }); + }); +} + +function analyzeWithStylelint( + request: express.Request, + response: express.Response +) { + const parsedRequest = request.body as AnalysisInput; + const { filePath, configFile } = parsedRequest; + const code = getFileContent(filePath); + + const options = { + code, + codeFilename: filePath, + configFile + }; + + stylelint + .lint(options) + .then(result => response.json(toIssues(result.results, filePath))) + .catch(error => processError(error, response)); +} + +function processError(error: any, response: express.Response) { + logError(error); + response.json([]); +} + +function toIssues(results: stylelint.LintResult[], filePath: string): Issue[] { + const analysisResponse: Issue[] = []; + // we should have only one element in 'results' as we are analyzing only 1 file + results.forEach(result => { + // to avoid reporting on "fake" source like + if (result.source !== filePath) { + log( + `DEBUG For file [${filePath}] received issues with [${result.source}] as a source. They will not be reported.` + ); + return; + } + result.warnings.forEach(warning => + analysisResponse.push({ + line: warning.line, + text: warning.text, + rule: warning.rule + }) + ); + }); + return analysisResponse; +} + +function getFileContent(filePath: string) { + const fileContent = fs.readFileSync(filePath, { encoding: "utf8" }); + // strip BOM + if (fileContent.charCodeAt(0) === 0xfeff) { + return fileContent.slice(1); + } + return fileContent; +} + +export interface AnalysisInput { + filePath: string; + configFile: string; +} + +export interface Issue { + line: number; + rule: string; + text: string; +} -- cgit v1.2.3