From 2785184cbefaec82f9f4ce901119bef2d71e6c0c Mon Sep 17 00:00:00 2001 From: Jesse Tatasciore Date: Mon, 16 Dec 2024 13:31:19 -0500 Subject: [PATCH] fix: Use relative paths with stylelint (#448) --- lint/BUILD.bazel | 6 ++ lint/js/BUILD.bazel | 6 ++ lint/js/stylelint.compactFormatter.mjs | 138 +++++++++++++++++++++++++ lint/stylelint.bzl | 25 +++-- 4 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 lint/js/stylelint.compactFormatter.mjs diff --git a/lint/BUILD.bazel b/lint/BUILD.bazel index 04129a01..451e24a6 100644 --- a/lint/BUILD.bazel +++ b/lint/BUILD.bazel @@ -130,6 +130,12 @@ alias( visibility = ["//visibility:public"], ) +alias( + name = "stylelint.compact-formatter", + actual = "//lint/js:stylelint.compact-formatter", + visibility = ["//visibility:public"], +) + bzl_library( name = "eslint", srcs = ["eslint.bzl"], diff --git a/lint/js/BUILD.bazel b/lint/js/BUILD.bazel index 680bdd2b..2f2ba450 100644 --- a/lint/js/BUILD.bazel +++ b/lint/js/BUILD.bazel @@ -18,3 +18,9 @@ js_library( visibility = ["//visibility:public"], ) +js_library( + name = "stylelint.compact-formatter", + srcs = ["stylelint.compactFormatter.mjs"], + visibility = ["//visibility:public"], +) + diff --git a/lint/js/stylelint.compactFormatter.mjs b/lint/js/stylelint.compactFormatter.mjs new file mode 100644 index 00000000..a491c906 --- /dev/null +++ b/lint/js/stylelint.compactFormatter.mjs @@ -0,0 +1,138 @@ +// Fork of 'compactFormatter' plugin, changed so that it prints relative paths. +// 3 files have been combined into 1 +// https://github.com/stylelint/stylelint/blob/b2c99cef764643f3bd9539b34cdec58af882db88/lib/formatters/compactFormatter.mjs +// https://github.com/stylelint/stylelint/blob/b2c99cef764643f3bd9539b34cdec58af882db88/lib/formatters/preprocessWarnings.mjs +// https://github.com/stylelint/stylelint/blob/b2c99cef764643f3bd9539b34cdec58af882db88/lib/constants.mjs + + +/*************************************************************************************************************** + * The following is vendored from: + * https://github.com/stylelint/stylelint/blob/b2c99cef764643f3bd9539b34cdec58af882db88/lib/constants.mjs + ***************************************************************************************************************/ +import { relative, sep } from 'node:path'; + +export const DEFAULT_CACHE_LOCATION = './.stylelintcache'; +export const CACHE_STRATEGY_METADATA = 'metadata'; +export const CACHE_STRATEGY_CONTENT = 'content'; +export const DEFAULT_CACHE_STRATEGY = CACHE_STRATEGY_METADATA; + +export const DEFAULT_IGNORE_FILENAME = '.stylelintignore'; + +export const DEFAULT_FORMATTER = 'string'; + +// NOTE: Partially based on `sysexits.h`. +export const EXIT_CODE_SUCCESS = 0; +export const EXIT_CODE_FATAL_ERROR = 1; +export const EXIT_CODE_LINT_PROBLEM = 2; +export const EXIT_CODE_INVALID_USAGE = 64; +export const EXIT_CODE_INVALID_CONFIG = 78; + +export const RULE_NAME_ALL = 'all'; + +export const SEVERITY_ERROR = 'error'; +export const SEVERITY_WARNING = 'warning'; +export const DEFAULT_SEVERITY = SEVERITY_ERROR; + +/*************************************************************************************************************** + * The following is vendored from: + * https://github.com/stylelint/stylelint/blob/b2c99cef764643f3bd9539b34cdec58af882db88/lib/formatters/preprocessWarnings.mjs + ***************************************************************************************************************/ +/** @import {LintResult} from 'stylelint' */ +/** @typedef {LintResult['parseErrors'][0]} ParseError */ +/** @typedef {LintResult['warnings'][0]} Warning */ +/** @typedef {Warning['severity']} Severity */ + +/** + * Preprocess warnings in a given lint result. + * Note that this function has a side-effect. + * + * @param {LintResult} result + * @returns {LintResult} + */ +export function preprocessWarnings(result) { + for (const error of result.parseErrors || []) { + result.warnings.push(parseErrorToWarning(error)); + } + + for (const warning of result.warnings) { + warning.severity = normalizeSeverity(warning); + } + + result.warnings.sort(byLocationOrder); + + return result; +} + +/** + * @param {ParseError} error + * @returns {Warning} + */ +function parseErrorToWarning(error) { + return { + line: error.line, + column: error.column, + rule: error.stylelintType, + severity: SEVERITY_ERROR, + text: `${error.text} (${error.stylelintType})`, + }; +} + +/** + * @param {Warning} warning + * @returns {Severity} + */ +function normalizeSeverity(warning) { + // NOTE: Plugins may add a warning without severity, for example, + // by directly using the PostCSS `Result#warn()` API. + return warning.severity || DEFAULT_SEVERITY; +} + +/** + * @param {Warning} a + * @param {Warning} b + * @returns {number} + */ +function byLocationOrder(a, b) { + // positionless first + if (!a.line && b.line) return -1; + + // positionless first + if (a.line && !b.line) return 1; + + if (a.line < b.line) return -1; + + if (a.line > b.line) return 1; + + if (a.column < b.column) return -1; + + if (a.column > b.column) return 1; + + return 0; +} + +/*************************************************************************************************************** + * The following is vendored from: + * https://github.com/stylelint/stylelint/blob/b2c99cef764643f3bd9539b34cdec58af882db88/lib/formatters/compactFormatter.mjs + ***************************************************************************************************************/ +/** + * @type {import('stylelint').Formatter} + * @param {import('stylelint').Warning[]} messages + * @param {string} source + * @param {string} cwd + */ +export default function compactFormatter(results, returnValue) { + return results + .flatMap((result) => { + const { warnings } = preprocessWarnings(result); + + return warnings.map( + (warning) => + `${relative((returnValue && returnValue.cwd) || process.cwd(), result.source).split(sep).join('/')}: ` + + `line ${warning.line}, ` + + `col ${warning.column}, ` + + `${warning.severity} - ` + + `${warning.text}`, + ); + }) + .join('\n'); +} \ No newline at end of file diff --git a/lint/stylelint.bzl b/lint/stylelint.bzl index 728d6555..4593ca22 100644 --- a/lint/stylelint.bzl +++ b/lint/stylelint.bzl @@ -43,14 +43,14 @@ load("//lint/private:lint_aspect.bzl", "LintOptionsInfo", "filter_srcs", "output _MNEMONIC = "AspectRulesLintStylelint" -def _gather_inputs(ctx, srcs): +def _gather_inputs(ctx, srcs, files = []): inputs = copy_files_to_bin_actions(ctx, srcs) # Add the config file along with any deps it has on npm packages if "gather_files_from_js_providers" in dir(js_lib_helpers): # rules_js 1.x js_inputs = js_lib_helpers.gather_files_from_js_providers( - [ctx.attr._config_file], + [ctx.attr._config_file] + files, include_transitive_sources = True, include_declarations = False, include_npm_linked_packages = True, @@ -58,7 +58,7 @@ def _gather_inputs(ctx, srcs): else: # rules_js 2.x js_inputs = js_lib_helpers.gather_files_from_js_infos( - [ctx.attr._config_file], + [ctx.attr._config_file] + files, include_sources = True, include_transitive_sources = True, include_types = False, @@ -67,7 +67,7 @@ def _gather_inputs(ctx, srcs): ) return depset(inputs, transitive = [js_inputs]) -def stylelint_action(ctx, executable, srcs, stderr, exit_code = None, env = {}, options = []): +def stylelint_action(ctx, executable, srcs, stderr, exit_code = None, env = {}, options = [], format = None): """Spawn stylelint as a Bazel action Args: @@ -85,6 +85,7 @@ def stylelint_action(ctx, executable, srcs, stderr, exit_code = None, env = {}, 78 - invalid configuration file env: environment variables for stylelint options: additional command-line arguments + format: a formatter to add as a command line argument """ outputs = [stderr] @@ -100,8 +101,15 @@ def stylelint_action(ctx, executable, srcs, stderr, exit_code = None, env = {}, # Create empty file on success, as Bazel expects one command = "{stylelint} $@ && touch {stderr}" + file_inputs = [] + if type(format) == "string": + args.add_all(["--formatter", format]) + elif format != None: + args.add_all(["--custom-formatter", "../../../" + format.files.to_list()[0].path]) + file_inputs.append(format) + ctx.actions.run_shell( - inputs = _gather_inputs(ctx, srcs), + inputs = _gather_inputs(ctx, srcs, file_inputs), outputs = outputs, command = command.format(stylelint = executable._stylelint.path, stderr = stderr.path), arguments = [args], @@ -180,7 +188,7 @@ def _stylelint_aspect_impl(target, ctx): stylelint_action(ctx, ctx.executable, files_to_lint, outputs.human.out, outputs.human.exit_code, options = color_options) # TODO(alex): if we run with --fix, this will report the issues that were fixed. Does a machine reader want to know about them? - stylelint_action(ctx, ctx.executable, files_to_lint, outputs.machine.out, outputs.machine.exit_code, options = ["--formatter", "compact"]) + stylelint_action(ctx, ctx.executable, files_to_lint, outputs.machine.out, outputs.machine.exit_code, format = ctx.attr._compact_formatter) return [info] @@ -215,6 +223,11 @@ def lint_stylelint_aspect(binary, config, rule_kinds = ["css_library"], filegrou default = config, allow_files = True, ), + "_compact_formatter": attr.label( + default = "@aspect_rules_lint//lint:stylelint.compact-formatter", + allow_single_file = True, + cfg = "exec", + ), "_patcher": attr.label( default = "@aspect_rules_lint//lint/private:patcher", executable = True,