Skip to content

Commit

Permalink
JavaScript: add basic support for template strings
Browse files Browse the repository at this point in the history
  • Loading branch information
akx committed Jan 18, 2016
1 parent afb1769 commit 7357d3d
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 6 deletions.
5 changes: 4 additions & 1 deletion babel/messages/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,8 @@ def extract_javascript(fileobj, keywords, comment_tags, options):
:param options: a dictionary of additional options (optional)
Supported options are:
* `jsx` -- set to false to disable JSX/E4X support.
* `template_string` -- set to false to disable ES6
template string support.
"""
from babel.messages.jslexer import tokenize, unquote_string
funcname = message_lineno = None
Expand All @@ -522,6 +524,7 @@ def extract_javascript(fileobj, keywords, comment_tags, options):
for token in tokenize(
fileobj.read().decode(encoding),
jsx=options.get("jsx", True),
template_string=options.get("template_string", True),
dotted=dotted
):
if token.type == 'operator' and token.value == '(':
Expand Down Expand Up @@ -583,7 +586,7 @@ def extract_javascript(fileobj, keywords, comment_tags, options):
messages = []
call_stack = -1

elif token.type == 'string':
elif token.type in ('string', 'template_string'):
new_value = unquote_string(token.value)
if concatenate_next:
last_argument = (last_argument or '') + new_value
Expand Down
14 changes: 9 additions & 5 deletions babel/messages/jslexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,15 @@
)''')),
('jsx_tag', re.compile(r'<(?:/?)\w+.+?>', re.I)), # May be mangled in `get_rules`
('operator', re.compile(r'(%s)' % '|'.join(map(re.escape, operators)))),
('template_string', re.compile(r'''`(?:[^`\\]*(?:\\.[^`\\]*)*)`''', re.UNICODE)),
('string', re.compile(r'''(?xs)(
'(?:[^'\\]*(?:\\.[^'\\]*)*)' |
"(?:[^"\\]*(?:\\.[^"\\]*)*)"
)'''))
]


def get_rules(jsx, dotted):
def get_rules(jsx, dotted, template_string):
"""
Get a tokenization rule list given the passed syntax options.
Expand All @@ -64,6 +65,8 @@ def get_rules(jsx, dotted):
for token_type, rule in _rules:
if not jsx and token_type and 'jsx' in token_type:
continue
if not template_string and token_type == 'template_string':
continue
if token_type == 'dotted_name':
if not dotted:
continue
Expand All @@ -83,9 +86,9 @@ def indicates_division(token):

def unquote_string(string):
"""Unquote a string with JavaScript rules. The string has to start with
string delimiters (``'`` or ``"``.)
string delimiters (``'``, ``"`` or the backquote (for template strings).)

This comment has been minimized.

Copy link
@ehaivala

ehaivala Jan 29, 2016

Nitpick: It's called a backtick.

Edit. Except it seems to be called backquote in some cases. Backquote sounds weird and backtick is more correct anyway.

"""
assert string and string[0] == string[-1] and string[0] in '"\'', \
assert string and string[0] == string[-1] and string[0] in '"\'`', \
'string provided is not properly delimited'
string = line_join_re.sub('\\1', string[1:-1])
result = []
Expand Down Expand Up @@ -137,18 +140,19 @@ def unquote_string(string):
return u''.join(result)


def tokenize(source, jsx=True, dotted=True):
def tokenize(source, jsx=True, dotted=True, template_string=True):
"""
Tokenize JavaScript/JSX source. Returns a generator of tokens.
:param jsx: Enable (limited) JSX parsing.
:param dotted: Read dotted names as single name token.
:param template_string: Support ES6 template strings
"""
may_divide = False
pos = 0
lineno = 1
end = len(source)
rules = get_rules(jsx=jsx, dotted=dotted)
rules = get_rules(jsx=jsx, dotted=dotted, template_string=template_string)

while pos < end:
# handle regular rules first
Expand Down
9 changes: 9 additions & 0 deletions tests/messages/test_js_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,12 @@ def test_dotted_keyword_extract():
)

assert messages == [(1, 'Insert coin to continue', [], None)]


def test_template_string_standard_usage():
buf = BytesIO(b"msg1 = gettext(`Very template, wow`)")
messages = list(
extract.extract('javascript', buf, {"gettext": None}, [], {})
)

assert messages == [(1, 'Very template, wow', [], None)]
7 changes: 7 additions & 0 deletions tests/messages/test_jslexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,10 @@ def test_dotted_name_end():
assert list(jslexer.tokenize("foo.bar", dotted=True)) == [
('name', 'foo.bar', 1),
]


def test_template_string():
assert list(jslexer.tokenize("gettext `foo\"bar\"p`", template_string=True)) == [
('name', 'gettext', 1),
('template_string', '`foo"bar"p`', 1)
]

0 comments on commit 7357d3d

Please sign in to comment.