github.com/vanstinator/golangci-lint@v0.0.0-20240223191551-cc572f00d9d1/pkg/printers/html.go (about)

     1  package printers
     2  
     3  import (
     4  	"fmt"
     5  	"html/template"
     6  	"io"
     7  	"strings"
     8  
     9  	"github.com/vanstinator/golangci-lint/pkg/result"
    10  )
    11  
    12  const templateContent = `<!doctype html>
    13  <html lang="en">
    14  <head>
    15      <meta charset="utf-8">
    16      <title>golangci-lint</title>
    17      <link rel="shortcut icon" type="image/png" href="https://golangci-lint.run/favicon-32x32.png">
    18      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.2/css/bulma.min.css"
    19            integrity="sha512-byErQdWdTqREz6DLAA9pCnLbdoGGhXfU6gm1c8bkf7F51JVmUBlayGe2A31VpXWQP+eiJ3ilTAZHCR3vmMyybA=="
    20            crossorigin="anonymous" referrerpolicy="no-referrer"/>
    21      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/styles/default.min.css"
    22            integrity="sha512-kZqGbhf9JTB4bVJ0G8HCkqmaPcRgo88F0dneK30yku5Y/dep7CZfCnNml2Je/sY4lBoqoksXz4PtVXS4GHSUzQ=="
    23            crossorigin="anonymous" referrerpolicy="no-referrer"/>
    24      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/highlight.min.js"
    25              integrity="sha512-s+tOYYcC3Jybgr9mVsdAxsRYlGNq4mlAurOrfNuGMQ/SCofNPu92tjE7YRZCsdEtWL1yGkqk15fU/ark206YTg=="
    26              crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    27      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/languages/go.min.js"
    28              integrity="sha512-+UYV2NyyynWEQcZ4sMTKmeppyV331gqvMOGZ61/dqc89Tn1H40lF05ACd03RSD9EWwGutNwKj256mIR8waEJBQ=="
    29              crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    30      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"
    31              integrity="sha512-qlzIeUtTg7eBpmEaS12NZgxz52YYZVF5myj89mjJEesBd/oE9UPsYOX2QAXzvOAZYEvQohKdcY8zKE02ifXDmA=="
    32              crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    33      <script type="text/javascript"
    34              src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"
    35              integrity="sha512-9jGNr5Piwe8nzLLYTk8QrEMPfjGU0px80GYzKZUxi7lmCfrBjtyCc1V5kkS5vxVwwIB7Qpzc7UxLiQxfAN30dw=="
    36              crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    37      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"
    38              integrity="sha512-kp7YHLxuJDJcOzStgd6vtpxr4ZU9kjn77e6dBsivSz+pUuAuMlE2UTdKB7jjsWT84qbS8kdCWHPETnP/ctrFsA=="
    39              crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    40  </head>
    41  <body>
    42  <section class="section">
    43      <div class="container">
    44          <div id="content"></div>
    45      </div>
    46  </section>
    47  <script>
    48      const data = {{ . }};
    49  </script>
    50  <script type="text/babel">
    51    class Highlight extends React.Component {
    52      componentDidMount() {
    53        hljs.highlightElement(ReactDOM.findDOMNode(this));
    54      }
    55  
    56      render() {
    57        return <pre className="go"><code>{this.props.code}</code></pre>;
    58      }
    59    }
    60  
    61    class Issue extends React.Component {
    62      render() {
    63        return (
    64          <div className="issue box">
    65            <div>
    66              <div className="columns">
    67                <div className="column is-four-fifths">
    68                  <h5 className="title is-5 has-text-danger-dark">{this.props.data.Title}</h5>
    69                </div>
    70                <div className="column is-one-fifth">
    71                  <h6 className="title is-6">{this.props.data.Linter}</h6>
    72                </div>
    73              </div>
    74              <strong>{this.props.data.Pos}</strong>
    75            </div>
    76            <div className="highlight">
    77              <Highlight code={this.props.data.Code}/>
    78            </div>
    79          </div>
    80        );
    81      }
    82    }
    83  
    84    class Issues extends React.Component {
    85      render() {
    86        if (!this.props.data.Issues || this.props.data.Issues.length === 0) {
    87          return (
    88            <div>
    89              <div className="notification">
    90                No issues found!
    91              </div>
    92            </div>
    93          );
    94        }
    95  
    96        return (
    97          <div className="issues">
    98            {this.props.data.Issues.map(issue => (<Issue data={issue}/>))}
    99          </div>
   100        );
   101      }
   102    }
   103  
   104    ReactDOM.render(
   105      <div className="content">
   106        <div className="columns is-centered">
   107          <div className="column is-three-quarters">
   108            <Issues data={data}/>
   109          </div>
   110        </div>
   111      </div>,
   112      document.getElementById("content")
   113    );
   114  </script>
   115  </body>
   116  </html>`
   117  
   118  type htmlIssue struct {
   119  	Title  string
   120  	Pos    string
   121  	Linter string
   122  	Code   string
   123  }
   124  
   125  type HTML struct {
   126  	w io.Writer
   127  }
   128  
   129  func NewHTML(w io.Writer) *HTML {
   130  	return &HTML{w: w}
   131  }
   132  
   133  func (p HTML) Print(issues []result.Issue) error {
   134  	var htmlIssues []htmlIssue
   135  
   136  	for i := range issues {
   137  		pos := fmt.Sprintf("%s:%d", issues[i].FilePath(), issues[i].Line())
   138  		if issues[i].Pos.Column != 0 {
   139  			pos += fmt.Sprintf(":%d", issues[i].Pos.Column)
   140  		}
   141  
   142  		htmlIssues = append(htmlIssues, htmlIssue{
   143  			Title:  strings.TrimSpace(issues[i].Text),
   144  			Pos:    pos,
   145  			Linter: issues[i].FromLinter,
   146  			Code:   strings.Join(issues[i].SourceLines, "\n"),
   147  		})
   148  	}
   149  
   150  	t, err := template.New("golangci-lint").Parse(templateContent)
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	return t.Execute(p.w, struct{ Issues []htmlIssue }{Issues: htmlIssues})
   156  }