Skip to content

Commit

Permalink
feat: build for release
Browse files Browse the repository at this point in the history
github-actions[bot] committed Jul 12, 2020
0 parents commit 1d43a71
Showing 23 changed files with 215,110 additions and 0 deletions.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020 dolittle

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# GitHub Action - Release TypeScript Library
This GitHub action releases a TypeScript/JavaScript library by updating its package version(s) and publishing it to npm

![Github JavaScript Actions CI/CD](https://github.com/dolittle/release-typescript-lib-action/workflows/Github%20JavaScript%20Actions%20CI/CD/badge.svg)

### Pre requisites
Create a workflow `.yml` file in your `.github/workflows` directory. An [example workflow](#example-workflow) is available below.

For more information, reference the GitHub Help Documentation for [Creating a workflow file](https://help.github.com/en/articles/configuring-a-workflow#creating-a-workflow-file)

### Inputs
- `version`: The version
- `root` (optional): The relative path to the root of the TypeScript project. default = root of the repository

### Example Workflow
```yaml
on:
push:
branches:
- '**'
pull_request:
types: [closed]

name: GitHub action workflow name

jobs:
context:
name: Job name
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Name here
uses: dolittle/action-repository-here@tag-to-use

```
## Contributing
We're always open for contributions and bug fixes!
### Pre requisites
node <= 12
yarn
git
14 changes: 14 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Release TypeScript Lib
description: Releases a typescript library
inputs:
version:
description: The version to release
required: true
root:
description: The relative path to the root of the TypeScript project. default = root of the repository
required: true
default: ''

runs:
using: 'node12'
main: 'release/index.js'
31 changes: 31 additions & 0 deletions release/eslint-all.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @fileoverview Config to enable all rules.
* @author Robert Fletcher
*/

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const builtInRules = require("../lib/rules");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const allRules = {};

for (const [ruleId, rule] of builtInRules) {
if (!rule.meta.deprecated) {
allRules[ruleId] = "error";
}
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

/** @type {import("../lib/shared/types").ConfigData} */
module.exports = { rules: allRules };
69 changes: 69 additions & 0 deletions release/eslint-recommended.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* @fileoverview Configuration applied when a user configuration extends from
* eslint:recommended.
* @author Nicholas C. Zakas
*/

"use strict";

/* eslint sort-keys: ["error", "asc"] */

/** @type {import("../lib/shared/types").ConfigData} */
module.exports = {
rules: {
"constructor-super": "error",
"for-direction": "error",
"getter-return": "error",
"no-async-promise-executor": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-const-assign": "error",
"no-constant-condition": "error",
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-dupe-args": "error",
"no-dupe-class-members": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-empty": "error",
"no-empty-character-class": "error",
"no-empty-pattern": "error",
"no-ex-assign": "error",
"no-extra-boolean-cast": "error",
"no-extra-semi": "error",
"no-fallthrough": "error",
"no-func-assign": "error",
"no-global-assign": "error",
"no-inner-declarations": "error",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-misleading-character-class": "error",
"no-mixed-spaces-and-tabs": "error",
"no-new-symbol": "error",
"no-obj-calls": "error",
"no-octal": "error",
"no-prototype-builtins": "error",
"no-redeclare": "error",
"no-regex-spaces": "error",
"no-self-assign": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-this-before-super": "error",
"no-undef": "error",
"no-unexpected-multiline": "error",
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unused-labels": "error",
"no-unused-vars": "error",
"no-useless-catch": "error",
"no-useless-escape": "error",
"no-with": "error",
"require-yield": "error",
"use-isnan": "error",
"valid-typeof": "error"
}
};
177 changes: 177 additions & 0 deletions release/espree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/**
* @fileoverview Main Espree file that converts Acorn into Esprima output.
*
* This file contains code from the following MIT-licensed projects:
* 1. Acorn
* 2. Babylon
* 3. Babel-ESLint
*
* This file also contains code from Esprima, which is BSD licensed.
*
* Acorn is Copyright 2012-2015 Acorn Contributors (https://github.com/marijnh/acorn/blob/master/AUTHORS)
* Babylon is Copyright 2014-2015 various contributors (https://github.com/babel/babel/blob/master/packages/babylon/AUTHORS)
* Babel-ESLint is Copyright 2014-2015 Sebastian McKenzie <sebmck@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Esprima is Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* eslint no-undefined:0, no-use-before-define: 0 */

"use strict";

const acorn = require("acorn");
const jsx = require("acorn-jsx");
const astNodeTypes = require("./lib/ast-node-types");
const espree = require("./lib/espree");
const { getLatestEcmaVersion, getSupportedEcmaVersions } = require("./lib/options");

// To initialize lazily.
const parsers = {
_regular: null,
_jsx: null,

get regular() {
if (this._regular === null) {
this._regular = acorn.Parser.extend(espree());
}
return this._regular;
},

get jsx() {
if (this._jsx === null) {
this._jsx = acorn.Parser.extend(jsx(), espree());
}
return this._jsx;
},

get(options) {
const useJsx = Boolean(
options &&
options.ecmaFeatures &&
options.ecmaFeatures.jsx
);

return useJsx ? this.jsx : this.regular;
}
};

//------------------------------------------------------------------------------
// Tokenizer
//------------------------------------------------------------------------------

/**
* Tokenizes the given code.
* @param {string} code The code to tokenize.
* @param {Object} options Options defining how to tokenize.
* @returns {Token[]} An array of tokens.
* @throws {SyntaxError} If the input code is invalid.
* @private
*/
function tokenize(code, options) {
const Parser = parsers.get(options);

// Ensure to collect tokens.
if (!options || options.tokens !== true) {
options = Object.assign({}, options, { tokens: true }); // eslint-disable-line no-param-reassign
}

return new Parser(options, code).tokenize();
}

//------------------------------------------------------------------------------
// Parser
//------------------------------------------------------------------------------

/**
* Parses the given code.
* @param {string} code The code to tokenize.
* @param {Object} options Options defining how to tokenize.
* @returns {ASTNode} The "Program" AST node.
* @throws {SyntaxError} If the input code is invalid.
*/
function parse(code, options) {
const Parser = parsers.get(options);

return new Parser(options, code).parse();
}

//------------------------------------------------------------------------------
// Public
//------------------------------------------------------------------------------

exports.version = require("./package.json").version;

exports.tokenize = tokenize;

exports.parse = parse;

// Deep copy.
/* istanbul ignore next */
exports.Syntax = (function() {
let name,
types = {};

if (typeof Object.create === "function") {
types = Object.create(null);
}

for (name in astNodeTypes) {
if (Object.hasOwnProperty.call(astNodeTypes, name)) {
types[name] = astNodeTypes[name];
}
}

if (typeof Object.freeze === "function") {
Object.freeze(types);
}

return types;
}());

/* istanbul ignore next */
exports.VisitorKeys = (function() {
return require("eslint-visitor-keys").KEYS;
}());

exports.latestEcmaVersion = getLatestEcmaVersion();

exports.supportedEcmaVersions = getSupportedEcmaVersions();
60 changes: 60 additions & 0 deletions release/formatters/checkstyle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* @fileoverview CheckStyle XML reporter
* @author Ian Christian Myers
*/
"use strict";

const xmlEscape = require("../xml-escape");

//------------------------------------------------------------------------------
// Helper Functions
//------------------------------------------------------------------------------

/**
* Returns the severity of warning or error
* @param {Object} message message object to examine
* @returns {string} severity level
* @private
*/
function getMessageType(message) {
if (message.fatal || message.severity === 2) {
return "error";
}
return "warning";

}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {

let output = "";

output += "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
output += "<checkstyle version=\"4.3\">";

results.forEach(result => {
const messages = result.messages;

output += `<file name="${xmlEscape(result.filePath)}">`;

messages.forEach(message => {
output += [
`<error line="${xmlEscape(message.line)}"`,
`column="${xmlEscape(message.column)}"`,
`severity="${xmlEscape(getMessageType(message))}"`,
`message="${xmlEscape(message.message)}${message.ruleId ? ` (${message.ruleId})` : ""}"`,
`source="${message.ruleId ? xmlEscape(`eslint.rules.${message.ruleId}`) : ""}" />`
].join(" ");
});

output += "</file>";

});

output += "</checkstyle>";

return output;
};
138 changes: 138 additions & 0 deletions release/formatters/codeframe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* @fileoverview Codeframe reporter
* @author Vitor Balocco
*/
"use strict";

const chalk = require("chalk");
const { codeFrameColumns } = require("@babel/code-frame");
const path = require("path");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Given a word and a count, append an s if count is not one.
* @param {string} word A word in its singular form.
* @param {number} count A number controlling whether word should be pluralized.
* @returns {string} The original word with an s on the end if count is not one.
*/
function pluralize(word, count) {
return (count === 1 ? word : `${word}s`);
}

/**
* Gets a formatted relative file path from an absolute path and a line/column in the file.
* @param {string} filePath The absolute file path to format.
* @param {number} line The line from the file to use for formatting.
* @param {number} column The column from the file to use for formatting.
* @returns {string} The formatted file path.
*/
function formatFilePath(filePath, line, column) {
let relPath = path.relative(process.cwd(), filePath);

if (line && column) {
relPath += `:${line}:${column}`;
}

return chalk.green(relPath);
}

/**
* Gets the formatted output for a given message.
* @param {Object} message The object that represents this message.
* @param {Object} parentResult The result object that this message belongs to.
* @returns {string} The formatted output.
*/
function formatMessage(message, parentResult) {
const type = (message.fatal || message.severity === 2) ? chalk.red("error") : chalk.yellow("warning");
const msg = `${chalk.bold(message.message.replace(/([^ ])\.$/u, "$1"))}`;
const ruleId = message.fatal ? "" : chalk.dim(`(${message.ruleId})`);
const filePath = formatFilePath(parentResult.filePath, message.line, message.column);
const sourceCode = parentResult.output ? parentResult.output : parentResult.source;

const firstLine = [
`${type}:`,
`${msg}`,
ruleId ? `${ruleId}` : "",
sourceCode ? `at ${filePath}:` : `at ${filePath}`
].filter(String).join(" ");

const result = [firstLine];

if (sourceCode) {
result.push(
codeFrameColumns(sourceCode, { start: { line: message.line, column: message.column } }, { highlightCode: false })
);
}

return result.join("\n");
}

/**
* Gets the formatted output summary for a given number of errors and warnings.
* @param {number} errors The number of errors.
* @param {number} warnings The number of warnings.
* @param {number} fixableErrors The number of fixable errors.
* @param {number} fixableWarnings The number of fixable warnings.
* @returns {string} The formatted output summary.
*/
function formatSummary(errors, warnings, fixableErrors, fixableWarnings) {
const summaryColor = errors > 0 ? "red" : "yellow";
const summary = [];
const fixablesSummary = [];

if (errors > 0) {
summary.push(`${errors} ${pluralize("error", errors)}`);
}

if (warnings > 0) {
summary.push(`${warnings} ${pluralize("warning", warnings)}`);
}

if (fixableErrors > 0) {
fixablesSummary.push(`${fixableErrors} ${pluralize("error", fixableErrors)}`);
}

if (fixableWarnings > 0) {
fixablesSummary.push(`${fixableWarnings} ${pluralize("warning", fixableWarnings)}`);
}

let output = chalk[summaryColor].bold(`${summary.join(" and ")} found.`);

if (fixableErrors || fixableWarnings) {
output += chalk[summaryColor].bold(`\n${fixablesSummary.join(" and ")} potentially fixable with the \`--fix\` option.`);
}

return output;
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {
let errors = 0;
let warnings = 0;
let fixableErrors = 0;
let fixableWarnings = 0;

const resultsWithMessages = results.filter(result => result.messages.length > 0);

let output = resultsWithMessages.reduce((resultsOutput, result) => {
const messages = result.messages.map(message => `${formatMessage(message, result)}\n\n`);

errors += result.errorCount;
warnings += result.warningCount;
fixableErrors += result.fixableErrorCount;
fixableWarnings += result.fixableWarningCount;

return resultsOutput.concat(messages);
}, []).join("\n");

output += "\n";
output += formatSummary(errors, warnings, fixableErrors, fixableWarnings);

return (errors + warnings) > 0 ? output : "";
};
60 changes: 60 additions & 0 deletions release/formatters/compact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* @fileoverview Compact reporter
* @author Nicholas C. Zakas
*/
"use strict";

//------------------------------------------------------------------------------
// Helper Functions
//------------------------------------------------------------------------------

/**
* Returns the severity of warning or error
* @param {Object} message message object to examine
* @returns {string} severity level
* @private
*/
function getMessageType(message) {
if (message.fatal || message.severity === 2) {
return "Error";
}
return "Warning";

}


//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {

let output = "",
total = 0;

results.forEach(result => {

const messages = result.messages;

total += messages.length;

messages.forEach(message => {

output += `${result.filePath}: `;
output += `line ${message.line || 0}`;
output += `, col ${message.column || 0}`;
output += `, ${getMessageType(message)}`;
output += ` - ${message.message}`;
output += message.ruleId ? ` (${message.ruleId})` : "";
output += "\n";

});

});

if (total > 0) {
output += `\n${total} problem${total !== 1 ? "s" : ""}`;
}

return output;
};
8 changes: 8 additions & 0 deletions release/formatters/html-template-message.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<tr style="display:none" class="f-<%= parentIndex %>">
<td><%= lineNumber %>:<%= columnNumber %></td>
<td class="clr-<%= severityNumber %>"><%= severityName %></td>
<td><%- message %></td>
<td>
<a href="<%= ruleUrl %>" target="_blank" rel="noopener noreferrer"><%= ruleId %></a>
</td>
</tr>
115 changes: 115 additions & 0 deletions release/formatters/html-template-page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ESLint Report</title>
<style>
body {
font-family:Arial, "Helvetica Neue", Helvetica, sans-serif;
font-size:16px;
font-weight:normal;
margin:0;
padding:0;
color:#333
}
#overview {
padding:20px 30px
}
td, th {
padding:5px 10px
}
h1 {
margin:0
}
table {
margin:30px;
width:calc(100% - 60px);
max-width:1000px;
border-radius:5px;
border:1px solid #ddd;
border-spacing:0px;
}
th {
font-weight:400;
font-size:medium;
text-align:left;
cursor:pointer
}
td.clr-1, td.clr-2, th span {
font-weight:700
}
th span {
float:right;
margin-left:20px
}
th span:after {
content:"";
clear:both;
display:block
}
tr:last-child td {
border-bottom:none
}
tr td:first-child, tr td:last-child {
color:#9da0a4
}
#overview.bg-0, tr.bg-0 th {
color:#468847;
background:#dff0d8;
border-bottom:1px solid #d6e9c6
}
#overview.bg-1, tr.bg-1 th {
color:#f0ad4e;
background:#fcf8e3;
border-bottom:1px solid #fbeed5
}
#overview.bg-2, tr.bg-2 th {
color:#b94a48;
background:#f2dede;
border-bottom:1px solid #eed3d7
}
td {
border-bottom:1px solid #ddd
}
td.clr-1 {
color:#f0ad4e
}
td.clr-2 {
color:#b94a48
}
td a {
color:#3a33d1;
text-decoration:none
}
td a:hover {
color:#272296;
text-decoration:underline
}
</style>
</head>
<body>
<div id="overview" class="bg-<%= reportColor %>">
<h1>ESLint Report</h1>
<div>
<span><%= reportSummary %></span> - Generated on <%= date %>
</div>
</div>
<table>
<tbody>
<%= results %>
</tbody>
</table>
<script type="text/javascript">
var groups = document.querySelectorAll("tr[data-group]");
for (i = 0; i < groups.length; i++) {
groups[i].addEventListener("click", function() {
var inGroup = document.getElementsByClassName(this.getAttribute("data-group"));
this.innerHTML = (this.innerHTML.indexOf("+") > -1) ? this.innerHTML.replace("+", "-") : this.innerHTML.replace("-", "+");
for (var j = 0; j < inGroup.length; j++) {
inGroup[j].style.display = (inGroup[j].style.display !== "none") ? "none" : "table-row";
}
});
}
</script>
</body>
</html>
6 changes: 6 additions & 0 deletions release/formatters/html-template-result.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<tr class="bg-<%- color %>" data-group="f-<%- index %>">
<th colspan="4">
[+] <%- filePath %>
<span><%- summary %></span>
</th>
</tr>
140 changes: 140 additions & 0 deletions release/formatters/html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/**
* @fileoverview HTML reporter
* @author Julian Laval
*/
"use strict";

const lodash = require("lodash");
const fs = require("fs");
const path = require("path");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const pageTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-page.html"), "utf-8"));
const messageTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-message.html"), "utf-8"));
const resultTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-result.html"), "utf-8"));

/**
* Given a word and a count, append an s if count is not one.
* @param {string} word A word in its singular form.
* @param {int} count A number controlling whether word should be pluralized.
* @returns {string} The original word with an s on the end if count is not one.
*/
function pluralize(word, count) {
return (count === 1 ? word : `${word}s`);
}

/**
* Renders text along the template of x problems (x errors, x warnings)
* @param {string} totalErrors Total errors
* @param {string} totalWarnings Total warnings
* @returns {string} The formatted string, pluralized where necessary
*/
function renderSummary(totalErrors, totalWarnings) {
const totalProblems = totalErrors + totalWarnings;
let renderedText = `${totalProblems} ${pluralize("problem", totalProblems)}`;

if (totalProblems !== 0) {
renderedText += ` (${totalErrors} ${pluralize("error", totalErrors)}, ${totalWarnings} ${pluralize("warning", totalWarnings)})`;
}
return renderedText;
}

/**
* Get the color based on whether there are errors/warnings...
* @param {string} totalErrors Total errors
* @param {string} totalWarnings Total warnings
* @returns {int} The color code (0 = green, 1 = yellow, 2 = red)
*/
function renderColor(totalErrors, totalWarnings) {
if (totalErrors !== 0) {
return 2;
}
if (totalWarnings !== 0) {
return 1;
}
return 0;
}

/**
* Get HTML (table rows) describing the messages.
* @param {Array} messages Messages.
* @param {int} parentIndex Index of the parent HTML row.
* @param {Object} rulesMeta Dictionary containing metadata for each rule executed by the analysis.
* @returns {string} HTML (table rows) describing the messages.
*/
function renderMessages(messages, parentIndex, rulesMeta) {

/**
* Get HTML (table row) describing a message.
* @param {Object} message Message.
* @returns {string} HTML (table row) describing a message.
*/
return lodash.map(messages, message => {
const lineNumber = message.line || 0;
const columnNumber = message.column || 0;
let ruleUrl;

if (rulesMeta) {
const meta = rulesMeta[message.ruleId];

ruleUrl = lodash.get(meta, "docs.url", null);
}

return messageTemplate({
parentIndex,
lineNumber,
columnNumber,
severityNumber: message.severity,
severityName: message.severity === 1 ? "Warning" : "Error",
message: message.message,
ruleId: message.ruleId,
ruleUrl
});
}).join("\n");
}

// eslint-disable-next-line jsdoc/require-description
/**
* @param {Array} results Test results.
* @param {Object} rulesMeta Dictionary containing metadata for each rule executed by the analysis.
* @returns {string} HTML string describing the results.
*/
function renderResults(results, rulesMeta) {
return lodash.map(results, (result, index) => resultTemplate({
index,
color: renderColor(result.errorCount, result.warningCount),
filePath: result.filePath,
summary: renderSummary(result.errorCount, result.warningCount)

}) + renderMessages(result.messages, index, rulesMeta)).join("\n");
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results, data) {
let totalErrors,
totalWarnings;

const metaData = data ? data.rulesMeta : {};

totalErrors = 0;
totalWarnings = 0;

// Iterate over results to get totals
results.forEach(result => {
totalErrors += result.errorCount;
totalWarnings += result.warningCount;
});

return pageTemplate({
date: new Date(),
reportColor: renderColor(totalErrors, totalWarnings),
reportSummary: renderSummary(totalErrors, totalWarnings),
results: renderResults(results, metaData)
});
};
41 changes: 41 additions & 0 deletions release/formatters/jslint-xml.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* @fileoverview JSLint XML reporter
* @author Ian Christian Myers
*/
"use strict";

const xmlEscape = require("../xml-escape");

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {

let output = "";

output += "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
output += "<jslint>";

results.forEach(result => {
const messages = result.messages;

output += `<file name="${result.filePath}">`;

messages.forEach(message => {
output += [
`<issue line="${message.line}"`,
`char="${message.column}"`,
`evidence="${xmlEscape(message.source || "")}"`,
`reason="${xmlEscape(message.message || "")}${message.ruleId ? ` (${message.ruleId})` : ""}" />`
].join(" ");
});

output += "</file>";

});

output += "</jslint>";

return output;
};
16 changes: 16 additions & 0 deletions release/formatters/json-with-metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* @fileoverview JSON reporter, including rules metadata
* @author Chris Meyer
*/
"use strict";

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results, data) {
return JSON.stringify({
results,
metadata: data
});
};
13 changes: 13 additions & 0 deletions release/formatters/json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @fileoverview JSON reporter
* @author Burak Yigit Kaya aka BYK
*/
"use strict";

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {
return JSON.stringify(results);
};
82 changes: 82 additions & 0 deletions release/formatters/junit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* @fileoverview jUnit Reporter
* @author Jamund Ferguson
*/
"use strict";

const xmlEscape = require("../xml-escape");
const path = require("path");

//------------------------------------------------------------------------------
// Helper Functions
//------------------------------------------------------------------------------

/**
* Returns the severity of warning or error
* @param {Object} message message object to examine
* @returns {string} severity level
* @private
*/
function getMessageType(message) {
if (message.fatal || message.severity === 2) {
return "Error";
}
return "Warning";

}

/**
* Returns a full file path without extension
* @param {string} filePath input file path
* @returns {string} file path without extension
* @private
*/
function pathWithoutExt(filePath) {
return path.posix.join(path.posix.dirname(filePath), path.basename(filePath, path.extname(filePath)));
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {

let output = "";

output += "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
output += "<testsuites>\n";

results.forEach(result => {

const messages = result.messages;
const classname = pathWithoutExt(result.filePath);

if (messages.length > 0) {
output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
messages.forEach(message => {
const type = message.fatal ? "error" : "failure";

output += `<testcase time="0" name="org.eslint.${message.ruleId || "unknown"}" classname="${classname}">`;
output += `<${type} message="${xmlEscape(message.message || "")}">`;
output += "<![CDATA[";
output += `line ${message.line || 0}, col `;
output += `${message.column || 0}, ${getMessageType(message)}`;
output += ` - ${xmlEscape(message.message || "")}`;
output += (message.ruleId ? ` (${message.ruleId})` : "");
output += "]]>";
output += `</${type}>`;
output += "</testcase>\n";
});
output += "</testsuite>\n";
} else {
output += `<testsuite package="org.eslint" time="0" tests="1" errors="0" name="${result.filePath}">\n`;
output += `<testcase time="0" name="${result.filePath}" classname="${classname}" />\n`;
output += "</testsuite>\n";
}

});

output += "</testsuites>\n";

return output;
};
101 changes: 101 additions & 0 deletions release/formatters/stylish.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* @fileoverview Stylish reporter
* @author Sindre Sorhus
*/
"use strict";

const chalk = require("chalk"),
stripAnsi = require("strip-ansi"),
table = require("text-table");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Given a word and a count, append an s if count is not one.
* @param {string} word A word in its singular form.
* @param {int} count A number controlling whether word should be pluralized.
* @returns {string} The original word with an s on the end if count is not one.
*/
function pluralize(word, count) {
return (count === 1 ? word : `${word}s`);
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {

let output = "\n",
errorCount = 0,
warningCount = 0,
fixableErrorCount = 0,
fixableWarningCount = 0,
summaryColor = "yellow";

results.forEach(result => {
const messages = result.messages;

if (messages.length === 0) {
return;
}

errorCount += result.errorCount;
warningCount += result.warningCount;
fixableErrorCount += result.fixableErrorCount;
fixableWarningCount += result.fixableWarningCount;

output += `${chalk.underline(result.filePath)}\n`;

output += `${table(
messages.map(message => {
let messageType;
if (message.fatal || message.severity === 2) {
messageType = chalk.red("error");
summaryColor = "red";
} else {
messageType = chalk.yellow("warning");
}
return [
"",
message.line || 0,
message.column || 0,
messageType,
message.message.replace(/([^ ])\.$/u, "$1"),
chalk.dim(message.ruleId || "")
];
}),
{
align: ["", "r", "l"],
stringLength(str) {
return stripAnsi(str).length;
}
}
).split("\n").map(el => el.replace(/(\d+)\s+(\d+)/u, (m, p1, p2) => chalk.dim(`${p1}:${p2}`))).join("\n")}\n\n`;
});

const total = errorCount + warningCount;

if (total > 0) {
output += chalk[summaryColor].bold([
"\u2716 ", total, pluralize(" problem", total),
" (", errorCount, pluralize(" error", errorCount), ", ",
warningCount, pluralize(" warning", warningCount), ")\n"
].join(""));

if (fixableErrorCount > 0 || fixableWarningCount > 0) {
output += chalk[summaryColor].bold([
" ", fixableErrorCount, pluralize(" error", fixableErrorCount), " and ",
fixableWarningCount, pluralize(" warning", fixableWarningCount),
" potentially fixable with the `--fix` option.\n"
].join(""));
}
}

// Resets output color, for prevent change on top level
return total > 0 ? chalk.reset(output) : "";
};
159 changes: 159 additions & 0 deletions release/formatters/table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/**
* @fileoverview "table reporter.
* @author Gajus Kuizinas <gajus@gajus.com>
*/
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const chalk = require("chalk"),
table = require("table").table;

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Given a word and a count, append an "s" if count is not one.
* @param {string} word A word.
* @param {number} count Quantity.
* @returns {string} The original word with an s on the end if count is not one.
*/
function pluralize(word, count) {
return (count === 1 ? word : `${word}s`);
}

/**
* Draws text table.
* @param {Array<Object>} messages Error messages relating to a specific file.
* @returns {string} A text table.
*/
function drawTable(messages) {
const rows = [];

if (messages.length === 0) {
return "";
}

rows.push([
chalk.bold("Line"),
chalk.bold("Column"),
chalk.bold("Type"),
chalk.bold("Message"),
chalk.bold("Rule ID")
]);

messages.forEach(message => {
let messageType;

if (message.fatal || message.severity === 2) {
messageType = chalk.red("error");
} else {
messageType = chalk.yellow("warning");
}

rows.push([
message.line || 0,
message.column || 0,
messageType,
message.message,
message.ruleId || ""
]);
});

return table(rows, {
columns: {
0: {
width: 8,
wrapWord: true
},
1: {
width: 8,
wrapWord: true
},
2: {
width: 8,
wrapWord: true
},
3: {
paddingRight: 5,
width: 50,
wrapWord: true
},
4: {
width: 20,
wrapWord: true
}
},
drawHorizontalLine(index) {
return index === 1;
}
});
}

/**
* Draws a report (multiple tables).
* @param {Array} results Report results for every file.
* @returns {string} A column of text tables.
*/
function drawReport(results) {
let files;

files = results.map(result => {
if (!result.messages.length) {
return "";
}

return `\n${result.filePath}\n\n${drawTable(result.messages)}`;
});

files = files.filter(content => content.trim());

return files.join("");
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(report) {
let result,
errorCount,
warningCount;

result = "";
errorCount = 0;
warningCount = 0;

report.forEach(fileReport => {
errorCount += fileReport.errorCount;
warningCount += fileReport.warningCount;
});

if (errorCount || warningCount) {
result = drawReport(report);
}

result += `\n${table([
[
chalk.red(pluralize(`${errorCount} Error`, errorCount))
],
[
chalk.yellow(pluralize(`${warningCount} Warning`, warningCount))
]
], {
columns: {
0: {
width: 110,
wrapWord: true
}
},
drawHorizontalLine() {
return true;
}
})}`;

return result;
};
95 changes: 95 additions & 0 deletions release/formatters/tap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* @fileoverview TAP reporter
* @author Jonathan Kingston
*/
"use strict";

const yaml = require("js-yaml");

//------------------------------------------------------------------------------
// Helper Functions
//------------------------------------------------------------------------------

/**
* Returns a canonical error level string based upon the error message passed in.
* @param {Object} message Individual error message provided by eslint
* @returns {string} Error level string
*/
function getMessageType(message) {
if (message.fatal || message.severity === 2) {
return "error";
}
return "warning";
}

/**
* Takes in a JavaScript object and outputs a TAP diagnostics string
* @param {Object} diagnostic JavaScript object to be embedded as YAML into output.
* @returns {string} diagnostics string with YAML embedded - TAP version 13 compliant
*/
function outputDiagnostics(diagnostic) {
const prefix = " ";
let output = `${prefix}---\n`;

output += prefix + yaml.safeDump(diagnostic).split("\n").join(`\n${prefix}`);
output += "...\n";
return output;
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {
let output = `TAP version 13\n1..${results.length}\n`;

results.forEach((result, id) => {
const messages = result.messages;
let testResult = "ok";
let diagnostics = {};

if (messages.length > 0) {
messages.forEach(message => {
const severity = getMessageType(message);
const diagnostic = {
message: message.message,
severity,
data: {
line: message.line || 0,
column: message.column || 0,
ruleId: message.ruleId || ""
}
};

// This ensures a warning message is not flagged as error
if (severity === "error") {
testResult = "not ok";
}

/*
* If we have multiple messages place them under a messages key
* The first error will be logged as message key
* This is to adhere to TAP 13 loosely defined specification of having a message key
*/
if ("message" in diagnostics) {
if (typeof diagnostics.messages === "undefined") {
diagnostics.messages = [];
}
diagnostics.messages.push(diagnostic);
} else {
diagnostics = diagnostic;
}
});
}

output += `${testResult} ${id + 1} - ${result.filePath}\n`;

// If we have an error include diagnostics
if (messages.length > 0) {
output += outputDiagnostics(diagnostics);
}

});

return output;
};
58 changes: 58 additions & 0 deletions release/formatters/unix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @fileoverview unix-style formatter.
* @author oshi-shinobu
*/
"use strict";

//------------------------------------------------------------------------------
// Helper Functions
//------------------------------------------------------------------------------

/**
* Returns a canonical error level string based upon the error message passed in.
* @param {Object} message Individual error message provided by eslint
* @returns {string} Error level string
*/
function getMessageType(message) {
if (message.fatal || message.severity === 2) {
return "Error";
}
return "Warning";

}


//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {

let output = "",
total = 0;

results.forEach(result => {

const messages = result.messages;

total += messages.length;

messages.forEach(message => {

output += `${result.filePath}:`;
output += `${message.line || 0}:`;
output += `${message.column || 0}:`;
output += ` ${message.message} `;
output += `[${getMessageType(message)}${message.ruleId ? `/${message.ruleId}` : ""}]`;
output += "\n";

});

});

if (total > 0) {
output += `\n${total} problem${total !== 1 ? "s" : ""}`;
}

return output;
};
63 changes: 63 additions & 0 deletions release/formatters/visualstudio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @fileoverview Visual Studio compatible formatter
* @author Ronald Pijnacker
*/

"use strict";

//------------------------------------------------------------------------------
// Helper Functions
//------------------------------------------------------------------------------

/**
* Returns the severity of warning or error
* @param {Object} message message object to examine
* @returns {string} severity level
* @private
*/
function getMessageType(message) {
if (message.fatal || message.severity === 2) {
return "error";
}
return "warning";

}


//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {

let output = "",
total = 0;

results.forEach(result => {

const messages = result.messages;

total += messages.length;

messages.forEach(message => {

output += result.filePath;
output += `(${message.line || 0}`;
output += message.column ? `,${message.column}` : "";
output += `): ${getMessageType(message)}`;
output += message.ruleId ? ` ${message.ruleId}` : "";
output += ` : ${message.message}`;
output += "\n";

});

});

if (total === 0) {
output += "no problems";
} else {
output += `\n${total} problem${total !== 1 ? "s" : ""}`;
}

return output;
};
213,600 changes: 213,600 additions & 0 deletions release/index.js

Large diffs are not rendered by default.

0 comments on commit 1d43a71

Please sign in to comment.