Add tools/find-add-class to tools/lint-all.

The find-add-class tool, when in lint mode, verifies that we can
understand all calls to addClass from our JS code.

When in non-lint mode, i.e. verbose mode, the tool prints out a
list of tuples of (fn, class) that we can use as we wish in other
tools.
This commit is contained in:
Steve Howell 2016-08-07 08:56:15 -07:00 committed by Tim Abbott
parent 15f46a142e
commit a904222947
3 changed files with 151 additions and 0 deletions

47
tools/find-add-class Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import print_function
from lib.find_add_class import display, find
import glob
import optparse
import sys
from six.moves import filter
try:
import lister
from typing import cast, Callable, Dict, Iterable, List
except ImportError as e:
print("ImportError: {}".format(e))
print("You need to run the Zulip linters inside a Zulip dev environment.")
print("If you are using Vagrant, you can `vagrant ssh` to enter the Vagrant guest.")
sys.exit(1)
def process_files():
# type: () -> None
usage = '''
Use this tool to find HTML classes that we use in our JS code.
This looks for calls to addClass, and if you use the -v option,
you will get a display of (fn, html_class) tuples that
represent addClass calls.
If you call it with no options, the tool acts as a linter, and
it will complain if it can't resolve the class for an addClass()
call.
'''
parser = optparse.OptionParser(usage=usage)
parser.add_option('--verbose', '-v',
action='store_true', default=False,
help='Show where calls are.')
(options, _) = parser.parse_args()
fns = glob.glob('static/js/*.js')
if options.verbose:
display(fns)
else:
find(fns)
if __name__ == '__main__':
process_files()

View File

@ -0,0 +1,98 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Set, Tuple
import os
import re
GENERIC_KEYWORDS = [
'active',
'alert',
'danger',
'condensed',
'disabled',
'error',
'expanded',
'hide',
'notdisplayed',
'popover',
'success',
'text-error',
'warning',
]
def raise_error(fn, i, line):
# type: (str, int, str) -> None
error = '''
In %s line %d there is the following line of code:
%s
Our tools want to be able to identify which modules
add which HTML/CSS classes, and we need two things to
happen:
- The code must explicitly name the class.
- Only one module can refer to that class (unless
it is something generic like an alert class).
If you get this error, you can usually address it by
refactoring your code to be more explicit, or you can
move the common code that sets the class to a library
module. If neither of those applies, you need to
modify %s
''' % (fn, i, line, __file__)
raise Exception(error)
def generic(html_class):
# type: (str) -> bool
for kw in GENERIC_KEYWORDS:
if kw in html_class:
return True
def display(fns):
# type: (List[str]) -> None
for tup in find(fns):
# this format is for code generation purposes
print(' ' * 8 + repr(tup) + ',')
def find(fns):
# type: (List[str]) -> List[Tuple[str, str]]
encountered = set() # type: Set[str]
tups = [] # type: List[Tuple[str, str]]
for fn in fns:
lines = list(open(fn))
fn = os.path.basename(fn)
module_classes = set() # type: Set[str]
for i, line in enumerate(lines):
if 'addClass' in line:
html_classes = [] # type: List[str]
m = re.search('addClass\([\'"](.*?)[\'"]', line)
if m:
html_classes = [m.group(1)]
if not html_classes:
if 'bar-success' in line:
html_classes = ['bar-success', 'bar-danger']
elif 'color_class' in line:
continue
elif 'stream_dark' in line:
continue
elif fn == 'signup.js' and 'class_to_add' in line:
html_classes = ['error', 'success']
elif fn == 'ui.js' and 'status_classes' in line:
html_classes = ['alert']
if not html_classes:
raise_error(fn, i, line)
for html_class in html_classes:
if generic(html_class):
continue
if html_class in module_classes:
continue
if html_class in encountered:
raise_error(fn, i, line)
tups.append((fn, html_class))
module_classes.add(html_class)
encountered.add(html_class)
return tups

View File

@ -401,6 +401,12 @@ try:
result = subprocess.call(args)
return result
@lint
def add_class():
# type: () -> int
result = subprocess.call(['tools/find-add-class'])
return result
@lint
def css():
# type: () -> int