forked from babel/babel
/
index.js
127 lines (111 loc) 路 2.75 KB
/
index.js
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
import jsTokens, { matchToToken } from "js-tokens";
import { isReservedWord, isKeyword } from "@babel/helper-validator-identifier";
import Chalk from "chalk";
/**
* Chalk styles for token types.
*/
function getDefs(chalk) {
return {
keyword: chalk.cyan,
capitalized: chalk.yellow,
jsx_tag: chalk.yellow,
punctuator: chalk.yellow,
// bracket: intentionally omitted.
number: chalk.magenta,
string: chalk.green,
regex: chalk.magenta,
comment: chalk.grey,
invalid: chalk.white.bgRed.bold,
};
}
/**
* RegExp to test for newlines in terminal.
*/
const NEWLINE = /\r\n|[\n\r\u2028\u2029]/;
/**
* RegExp to test for what seems to be a JSX tag name.
*/
const JSX_TAG = /^[a-z][\w-]*$/i;
/**
* RegExp to test for the three types of brackets.
*/
const BRACKET = /^[()[\]{}]$/;
/**
* Get the type of token, specifying punctuator type.
*/
function getTokenType(match) {
const [offset, text] = match.slice(-2);
const token = matchToToken(match);
if (token.type === "name") {
if (isKeyword(token.value) || isReservedWord(token.value)) {
return "keyword";
}
if (
JSX_TAG.test(token.value) &&
(text[offset - 1] === "<" || text.substr(offset - 2, 2) == "</")
) {
return "jsx_tag";
}
if (token.value[0] !== token.value[0].toLowerCase()) {
return "capitalized";
}
}
if (token.type === "punctuator" && BRACKET.test(token.value)) {
return "bracket";
}
if (
token.type === "invalid" &&
(token.value === "@" || token.value === "#")
) {
return "punctuator";
}
return token.type;
}
/**
* Highlight `text` using the token definitions in `defs`.
*/
function highlightTokens(defs: Object, text: string) {
return text.replace(jsTokens, function(...args) {
const type = getTokenType(args);
const colorize = defs[type];
if (colorize) {
return args[0]
.split(NEWLINE)
.map(str => colorize(str))
.join("\n");
} else {
return args[0];
}
});
}
type Options = {
forceColor?: boolean,
};
/**
* Whether the code should be highlighted given the passed options.
*/
export function shouldHighlight(options: Options): boolean {
return Chalk.supportsColor || options.forceColor;
}
/**
* The Chalk instance that should be used given the passed options.
*/
export function getChalk(options: Options) {
let chalk = Chalk;
if (options.forceColor) {
chalk = new Chalk.constructor({ enabled: true, level: 1 });
}
return chalk;
}
/**
* Highlight `code`.
*/
export default function highlight(code: string, options: Options = {}): string {
if (shouldHighlight(options)) {
const chalk = getChalk(options);
const defs = getDefs(chalk);
return highlightTokens(defs, code);
} else {
return code;
}
}