1 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
2 # vim: set filetype=python:
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
14 sys
.path
.append(os
.path
.join(os
.path
.dirname(__file__
), "eslint"))
15 from eslint
import setup_helper
16 from mozbuild
.nodeutil
import find_node_executable
17 from mozlint
import result
19 STYLELINT_ERROR_MESSAGE
= """
20 An error occurred running stylelint. Please check the following error messages:
25 STYLELINT_NOT_FOUND_MESSAGE
= """
26 Could not find stylelint! We looked at the --binary option, at the STYLELINT
27 environment variable, and then at your local node_modules path. Please install
28 eslint, stylelint and needed plugins with:
35 FILE_EXT_REGEX
= re
.compile(r
"\.[a-z0-9_]{2,10}$", re
.IGNORECASE
)
38 def setup(root
, **lintargs
):
39 setup_helper
.set_project_root(root
)
41 if not setup_helper
.check_node_executables_valid():
44 return setup_helper
.eslint_maybe_setup()
47 def lint(paths
, config
, binary
=None, fix
=None, rules
=[], setup
=None, **lintargs
):
50 setup_helper
.set_project_root(lintargs
["root"])
51 module_path
= setup_helper
.get_project_root()
54 exts
= "*.{" + ",".join(config
["extensions"]) + "}"
57 filepath
, fileext
= os
.path
.splitext(path
)
59 modified_paths
+= [path
]
61 joined_path
= os
.path
.join(path
, "**", exts
)
63 joined_path
= joined_path
.replace("\\", "/")
64 modified_paths
.append(joined_path
)
67 # - Any provided by the binary argument.
68 # - Any pointed at by the STYLELINT environmental variable.
69 # - Those provided by |mach lint --setup|.
72 binary
, _
= find_node_executable()
75 print(STYLELINT_NOT_FOUND_MESSAGE
)
78 extra_args
= lintargs
.get("extra_args") or []
80 for path
in config
.get("exclude", []):
82 ["--ignore-pattern", os
.path
.relpath(path
, lintargs
["root"])]
85 # Default to $topsrcdir/.stylelintrc.js, but allow override in stylelint.yml
86 stylelint_rc
= config
.get("stylelint-rc", ".stylelintrc.js")
93 module_path
, "node_modules", "stylelint", "bin", "stylelint.mjs"
97 "--allow-empty-input",
99 os
.path
.join(lintargs
["root"], stylelint_rc
),
107 cmd_args
.append("--fix")
109 log
.debug("Stylelint command: {}".format(" ".join(cmd_args
)))
111 result
= run(cmd_args
, config
, fix
)
118 def run(cmd_args
, config
, fix
):
121 # The stylelint binary needs to be run from a shell with msys
125 orig
= signal
.signal(signal
.SIGINT
, signal
.SIG_IGN
)
126 proc
= subprocess
.Popen(
127 cmd_args
, shell
=shell
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
129 signal
.signal(signal
.SIGINT
, orig
)
132 _
, errors
= proc
.communicate()
133 except KeyboardInterrupt:
135 return {"results": [], "fixed": 0}
138 errors
= errors
.decode(encoding
, "replace")
140 # 0 is success, 2 is there was at least 1 rule violation. Anything else
142 if proc
.returncode
!= 0 and proc
.returncode
!= 2:
143 if proc
.returncode
== 78:
144 print("Stylelint reported an issue with its configuration file.")
149 return {"results": [], "fixed": 0}
152 jsonresult
= json
.loads(errors
)
154 print(STYLELINT_ERROR_MESSAGE
.format(errors
))
159 for obj
in jsonresult
:
160 errors
= obj
["warnings"] + obj
["parseErrors"]
161 # This will return a number of fixed files, as that's the only thing
162 # stylelint gives us. Note that it also seems to sometimes list files
163 # like this where it finds nothing and fixes nothing. It's not clear
164 # why... but this is why we also check if we were even trying to fix
166 if fix
and not errors
and not obj
.get("ignored"):
170 msg
= err
.get("text")
172 # stylelint includes the rule id in the error message.
173 # All mozlint formatters that include the error message also already
174 # separately include the rule id, so that leads to duplication. Fix:
175 msg
= msg
.replace("(" + err
.get("rule") + ")", "").strip()
179 "level": err
.get("severity") or "error",
180 "lineno": err
.get("line") or 0,
181 "path": obj
["source"],
182 "rule": err
.get("rule") or "parseError",
185 results
.append(result
.from_config(config
, **err
))
187 return {"results": results
, "fixed": fixed
}
192 os
.environ
.get("MSYSTEM") in ("MINGW32", "MINGW64")
193 or "MOZILLABUILD" in os
.environ