blob: 863878682465f8e3ae7bcf6812c9510036a26dae [file] [log] [blame]
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# 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/
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(, line_number, line, regex, message)
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 = []
presubmit_path = self.input_api.PresubmitLocalPath()
for f in affected_js_files:
os_path.relpath(f.AbsoluteLocalPath(), presubmit_path))
args = ['--color', '--format', format, '--ignore-pattern \'!.eslintrc.js\'']
args += affected_js_files_paths
import eslint
output = eslint.Run(os_path=os_path, args=args)
return [self.output_api.PresubmitError(output)] if output else []
def VariableNameCheck(self, i, line):
"""See the style guide."""
return self.RegexCheck(i, line,
r"(?:var|let|const) (?!g_\w+)(_?[a-z][a-zA-Z]*[_$][\w_$]*)(?<! \$)",
"Please use variable namesLikeThis <>")
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
results = []
affected_files = self.input_api.AffectedFiles(file_filter=self.file_filter,
affected_js_files = filter(lambda f: f.LocalPath().endswith('.js'),
if affected_js_files:
results += self.RunEsLintChecks(affected_js_files)
for f in affected_js_files:
error_lines = []
for i, line in enumerate(f.NewContents(), start=1):
error_lines += filter(None, [
self.ChromeSendCheck(i, line),
self.CommentIfAndIncludeCheck(i, line),
self.EndJsDocCommentCheck(i, line),
self.ExtraDotInGenericCheck(i, line),
self.InheritDocCheck(i, line),
self.PolymerLocalIdCheck(i, line),
self.VariableNameCheck(i, line),
if error_lines:
error_lines = [
'Found JavaScript style violations in %s:' %
f.LocalPath()] + error_lines
if results:
'See the JavaScript style guide at '
return results