1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
# Copyright 2012 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Presubmit script for Chromium JS resources.
See chrome/browser/PRESUBMIT.py
"""
from . import regex_check
class JSChecker(object):
def __init__(self, input_api, output_api, file_filter=None):
self.input_api = input_api
self.output_api = output_api
self.file_filter = file_filter
def RegexCheck(self, line_number, line, regex, message):
return regex_check.RegexCheck(
self.input_api.re, line_number, line, regex, message)
def BindThisCheck(self, i, line):
"""Checks for usages of bind(this) with inlined functions."""
return self.RegexCheck(i, line, r"\)(\.bind\(this)[^)]*\)",
"Prefer arrow (=>) functions over bind(this)")
def ChromeSendCheck(self, i, line):
"""Checks for a particular misuse of "chrome.send"."""
return self.RegexCheck(i, line, r"chrome\.send\('[^']+'\s*(, \[\])\)",
"Passing an empty array to chrome.send is unnecessary")
def CommentIfAndIncludeCheck(self, line_number, line):
return self.RegexCheck(line_number, line, r"(?<!\/\/ )(<if|<include) ",
"<if> or <include> should be in a single line comment with a space " +
"after the slashes. Examples:\n" +
' // <include src="...">\n' +
' // <if expr="chromeos">\n' +
" // </if>\n")
def EndJsDocCommentCheck(self, i, line):
msg = "End JSDoc comments with */ instead of **/"
def _check(regex):
return self.RegexCheck(i, line, regex, msg)
return _check(r"^\s*(\*\*/)\s*$") or _check(r"/\*\* @[a-zA-Z]+.* (\*\*/)")
def ExtraDotInGenericCheck(self, i, line):
return self.RegexCheck(i, line, r"((?:Array|Object|Promise)\.<)",
"Don't use a dot after generics (Object.<T> should be Object<T>).")
def InheritDocCheck(self, i, line):
"""Checks for use of "@inheritDoc" instead of "@override"."""
return self.RegexCheck(i, line, r"\* (@inheritDoc)",
"@inheritDoc is deprecated, use @override instead")
def PolymerLocalIdCheck(self, i, line):
"""Checks for use of element.$.localId."""
return self.RegexCheck(i, line, r"(?<!this)(\.\$)[\[\.](?![a-zA-Z]+\()",
"Please only use this.$.localId, not element.$.localId")
def RunEsLintChecks(self, affected_js_files, format="stylish"):
"""Runs lint checks using ESLint. The ESLint rules being applied are defined
in the .eslintrc.js configuration file.
"""
os_path = self.input_api.os_path
# Extract paths to be passed to ESLint.
affected_js_files_paths = []
for f in affected_js_files:
affected_js_files_paths.append(f.AbsoluteLocalPath())
from os import isatty as os_isatty
parameters = ["--color"] if os_isatty(
self.input_api.sys.stdout.fileno()) else []
parameters += ["--format", format, "--ignore-pattern", "!.eslintrc.js"]
from . import eslint
# When running git cl presubmit --all this presubmit may be asked to check
# ~1,100 files, leading to a command line that is about 92,000 characters.
# This goes past the Windows 8191 character cmd.exe limit and causes cryptic
# failures. To avoid these we break the command up into smaller pieces. The
# non-Windows limit is chosen so that the code that splits up commands will
# get some exercise on other platforms.
# Depending on how long the command is on Windows the error may be:
# The command line is too long.
# Or it may be:
# OSError: Execution failed with error: [WinError 206] The filename or
# extension is too long.
# The latter error comes from CreateProcess hitting its 32768 character
# limit.
files_per_command = 25 if self.input_api.is_windows else 1000
results = []
for i in range(0, len(affected_js_files_paths), files_per_command):
args = parameters + affected_js_files_paths[i:i + files_per_command]
try:
output = eslint.Run(os_path=os_path, args=args)
except RuntimeError as err:
results.append(self.output_api.PresubmitError(str(err)))
return results
def VariableNameCheck(self, i, line):
"""See the style guide. http://goo.gl/eQiXVW"""
return self.RegexCheck(i, line,
r"(?:var|let|const) (?!g_\w+)(_?[a-z][a-zA-Z]*[_$][\w_$]*)(?<! \$)",
"Please use variable namesLikeThis <https://goo.gl/eQiXVW>")
def _GetErrorHighlight(self, start, length):
"""Takes a start position and a length, and produces a row of "^"s to
highlight the corresponding part of a string.
"""
return start * " " + length * "^"
def RunChecks(self):
"""Check for violations of the Chromium JavaScript style guide. See
https://chromium.googlesource.com/chromium/src/+/main/styleguide/web/web.md#JavaScript
"""
results = []
affected_files = self.input_api.AffectedFiles(file_filter=self.file_filter,
include_deletes=False)
affected_js_files = [
f for f in affected_files if f.LocalPath().endswith((".js", ".ts"))
]
if affected_js_files:
results += self.RunEsLintChecks(affected_js_files)
for f in affected_js_files:
error_lines = []
for i, line in f.ChangedContents():
error_lines += [
_f for _f in [
self.BindThisCheck(i, line),
self.ChromeSendCheck(i, line),
self.EndJsDocCommentCheck(i, line),
self.ExtraDotInGenericCheck(i, line),
self.InheritDocCheck(i, line),
self.PolymerLocalIdCheck(i, line),
self.VariableNameCheck(i, line),
] if _f
]
if not f.LocalPath().endswith((".html.js", ".html.ts")):
# Exclude JS/TS files holding HTML strings from
# CommentIfAndIncludeCheck().
for i, line in f.ChangedContents():
error_lines += [
_f for _f in [
self.CommentIfAndIncludeCheck(i, line),
] if _f
]
if error_lines:
error_lines = [
"Found JavaScript style violations in %s:" %
f.LocalPath()] + error_lines
results.append(self.output_api.PresubmitError("\n".join(error_lines)))
if results:
results.append(self.output_api.PresubmitNotifyResult(
"See the JavaScript style guide at "
"https://chromium.googlesource.com/chromium/src/+/main/styleguide/web/web.md#JavaScript"))
return results
|