diff options
Diffstat (limited to 'sonar-css-plugin/css-bundle/src')
| -rw-r--r-- | sonar-css-plugin/css-bundle/src/server.ts | 116 | 
1 files changed, 116 insertions, 0 deletions
| 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<Server> { +  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 <input ccs 1> +    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; +} | 
