File: js_checker.py

package info (click to toggle)
chromium 138.0.7204.157-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 6,071,864 kB
  • sloc: cpp: 34,936,859; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,967; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (166 lines) | stat: -rw-r--r-- 6,578 bytes parent folder | download | duplicates (3)
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