Skip to content

Commit

Permalink
Add JSX lexer
Browse files Browse the repository at this point in the history
  • Loading branch information
fcurella committed Sep 25, 2023
1 parent c097c92 commit 230ff09
Show file tree
Hide file tree
Showing 11 changed files with 633 additions and 0 deletions.
1 change: 1 addition & 0 deletions pygments/lexers/_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@
'JsonLexer': ('pygments.lexers.data', 'JSON', ('json', 'json-object'), ('*.json', '*.jsonl', '*.ndjson', 'Pipfile.lock'), ('application/json', 'application/json-object', 'application/x-ndjson', 'application/jsonl', 'application/json-seq')),
'JsonnetLexer': ('pygments.lexers.jsonnet', 'Jsonnet', ('jsonnet',), ('*.jsonnet', '*.libsonnet'), ()),
'JspLexer': ('pygments.lexers.templates', 'Java Server Page', ('jsp',), ('*.jsp',), ('application/x-jsp',)),
'JsxLexer': ('pygments.lexers.jsx', 'react', ('jsx', 'react'), ('*.jsx', '*.react'), ('text/jsx', 'text/typescript-jsx')),
'JuliaConsoleLexer': ('pygments.lexers.julia', 'Julia console', ('jlcon', 'julia-repl'), (), ()),
'JuliaLexer': ('pygments.lexers.julia', 'Julia', ('julia', 'jl'), ('*.jl',), ('text/x-julia', 'application/x-julia')),
'JuttleLexer': ('pygments.lexers.javascript', 'Juttle', ('juttle',), ('*.juttle',), ('application/juttle', 'application/x-juttle', 'text/x-juttle', 'text/juttle')),
Expand Down
64 changes: 64 additions & 0 deletions pygments/lexers/jsx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import re

from pygments.lexer import bygroups, default, include, inherit
from pygments.lexers.javascript import JavascriptLexer
from pygments.token import Name, Operator, Punctuation, String, Text

__all__ = ['JsxLexer']

# Use same tokens as `JavascriptLexer`, but with tags and attributes support
TOKENS = {
"root": [
include("jsx"),
inherit,
],
"jsx": [
(
r"(<)(/?)(>)",
bygroups(Punctuation, Punctuation, Punctuation),
), # JSXFragment <>|</>
(r"(<)([\w]+)(\.?)", bygroups(Punctuation, Name.Tag, Punctuation), "tag"),
(
r"(<)(/)([\w]+)(>)",
bygroups(Punctuation, Punctuation, Name.Tag, Punctuation),
),
(
r"(<)(/)([\w]+)",
bygroups(Punctuation, Punctuation, Name.Tag),
"fragment",
), # Same for React.Context
],
"tag": [
(r"\s+", Text),
(r"([\w-]+\s*)(=)(\s*)", bygroups(Name.Attribute, Operator, Text), "attr"),
(r"[{}]+", Punctuation),
(r"[\w\.]+", Name.Attribute),
(r"(/?)(\s*)(>)", bygroups(Punctuation, Text, Punctuation), "#pop"),
],
"fragment": [
(r"(.)([\w]+)", bygroups(Punctuation, Name.Attribute)),
(r"(>)", bygroups(Punctuation), "#pop"),
],
"attr": [
("{", Punctuation, "expression"),
('".*?"', String, "#pop"),
("'.*?'", String, "#pop"),
default("#pop"),
],
"expression": [
("{", Punctuation, "#push"),
("}", Punctuation, "#pop"),
include("root"),
],
}


class JsxLexer(JavascriptLexer):
name = "react"
aliases = ["jsx", "react"]
filenames = ["*.jsx", "*.react"]
mimetypes = ["text/jsx", "text/typescript-jsx"]

flags = re.MULTILINE | re.DOTALL | re.UNICODE

tokens = TOKENS
54 changes: 54 additions & 0 deletions tests/examplefiles/jsx/general.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const isOldEnough = (value, ownProps) => {
if (parseInt(value, 10) < 14) {
return "Only 14yo and older can register to the site."
}
};

// functional component
const BlogTitle = ({ children }) => (
<h3>{children}</h3>
);

// class component
class BlogPost extends React.Component {
renderTitle(title) {
return <BlogTitle>{title}</BlogTitle>
};
render() {
return (
<div className="blog-body">
{this.renderTitle(this.props.title)}
<p>{this.props.body}</p>
<CustomComponent>text</CustomComponent>
<input type="text" {...props.inputProps} />
<button aria-label="Submit">Submit</button>
</div>
);
}
}

const body = "Hello World!";
const blogNode = <BlogPost title="What's going on?" body={body} />;
// some comment. Tags shouldn't be lexed in here
// <div class="blog-body">
// <h3>What's going on?</h3>
// <p>Hello World!</p>
// </div>

/*
Some comment. Tags shouldn't be lexed in here either
<div class="blog-body">
<h3>What's going on?</h3>
<p>Hello World!</p>
</div>
*/

const shortSyntaxfragmentEmptyBody = <></>;

const shortSyntaxfragmentFullBody = <><div/></>;

const reactDotFragment = <React.Fragment><div/></React.Fragment>;

const reactDotContext = <Context.Provider><div/></Context.Provider>;

const reactDotContextValue = <Context.Provider value="Hello"><div/></Context.Provider>;

0 comments on commit 230ff09

Please sign in to comment.