mirror of https://github.com/zulip/zulip.git
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:
parent
15f46a142e
commit
a904222947
|
@ -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()
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue