index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. // Copyright 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Simon Lydell
  2. // License: MIT.
  3. var HashbangComment, Identifier, JSXIdentifier, JSXPunctuator, JSXString, JSXText, KeywordsWithExpressionAfter, KeywordsWithNoLineTerminatorAfter, LineTerminatorSequence, MultiLineComment, Newline, NumericLiteral, Punctuator, RegularExpressionLiteral, SingleLineComment, StringLiteral, Template, TokensNotPrecedingObjectLiteral, TokensPrecedingExpression, WhiteSpace, jsTokens;
  4. RegularExpressionLiteral = /\/(?![*\/])(?:\[(?:[^\]\\\n\r\u2028\u2029]+|\\.)*\]?|[^\/[\\\n\r\u2028\u2029]+|\\.)*(\/[$_\u200C\u200D\p{ID_Continue}]*|\\)?/yu;
  5. Punctuator = /--|\+\+|=>|\.{3}|\??\.(?!\d)|(?:&&|\|\||\?\?|[+\-%&|^]|\*{1,2}|<{1,2}|>{1,3}|!=?|={1,2}|\/(?![\/*]))=?|[?~,:;[\](){}]/y;
  6. Identifier = /(\x23?)(?=[$_\p{ID_Start}\\])(?:[$_\u200C\u200D\p{ID_Continue}]+|\\u[\da-fA-F]{4}|\\u\{[\da-fA-F]+\})+/yu;
  7. StringLiteral = /(['"])(?:[^'"\\\n\r]+|(?!\1)['"]|\\(?:\r\n|[^]))*(\1)?/y;
  8. NumericLiteral = /(?:0[xX][\da-fA-F](?:_?[\da-fA-F])*|0[oO][0-7](?:_?[0-7])*|0[bB][01](?:_?[01])*)n?|0n|[1-9](?:_?\d)*n|(?:(?:0(?!\d)|0\d*[89]\d*|[1-9](?:_?\d)*)(?:\.(?:\d(?:_?\d)*)?)?|\.\d(?:_?\d)*)(?:[eE][+-]?\d(?:_?\d)*)?|0[0-7]+/y;
  9. Template = /[`}](?:[^`\\$]+|\\[^]|\$(?!\{))*(`|\$\{)?/y;
  10. WhiteSpace = /[\t\v\f\ufeff\p{Zs}]+/yu;
  11. LineTerminatorSequence = /\r?\n|[\r\u2028\u2029]/y;
  12. MultiLineComment = /\/\*(?:[^*]+|\*(?!\/))*(\*\/)?/y;
  13. SingleLineComment = /\/\/.*/y;
  14. HashbangComment = /^#!.*/;
  15. JSXPunctuator = /[<>.:={}]|\/(?![\/*])/y;
  16. JSXIdentifier = /[$_\p{ID_Start}][$_\u200C\u200D\p{ID_Continue}-]*/yu;
  17. JSXString = /(['"])(?:[^'"]+|(?!\1)['"])*(\1)?/y;
  18. JSXText = /[^<>{}]+/y;
  19. TokensPrecedingExpression = /^(?:[\/+-]|\.{3}|\?(?:InterpolationIn(?:JSX|Template)|NoLineTerminatorHere|NonExpressionParenEnd|UnaryIncDec))?$|[{}([,;<>=*%&|^!~?:]$/;
  20. TokensNotPrecedingObjectLiteral = /^(?:=>|[;\]){}]|else|\?(?:NoLineTerminatorHere|NonExpressionParenEnd))?$/;
  21. KeywordsWithExpressionAfter = /^(?:await|case|default|delete|do|else|instanceof|new|return|throw|typeof|void|yield)$/;
  22. KeywordsWithNoLineTerminatorAfter = /^(?:return|throw|yield)$/;
  23. Newline = RegExp(LineTerminatorSequence.source);
  24. module.exports = jsTokens = function*(input, {jsx = false} = {}) {
  25. var braces, firstCodePoint, isExpression, lastIndex, lastSignificantToken, length, match, mode, nextLastIndex, nextLastSignificantToken, parenNesting, postfixIncDec, punctuator, stack;
  26. ({length} = input);
  27. lastIndex = 0;
  28. lastSignificantToken = "";
  29. stack = [
  30. {tag: "JS"}
  31. ];
  32. braces = [];
  33. parenNesting = 0;
  34. postfixIncDec = false;
  35. if (match = HashbangComment.exec(input)) {
  36. yield ({
  37. type: "HashbangComment",
  38. value: match[0]
  39. });
  40. lastIndex = match[0].length;
  41. }
  42. while (lastIndex < length) {
  43. mode = stack[stack.length - 1];
  44. switch (mode.tag) {
  45. case "JS":
  46. case "JSNonExpressionParen":
  47. case "InterpolationInTemplate":
  48. case "InterpolationInJSX":
  49. if (input[lastIndex] === "/" && (TokensPrecedingExpression.test(lastSignificantToken) || KeywordsWithExpressionAfter.test(lastSignificantToken))) {
  50. RegularExpressionLiteral.lastIndex = lastIndex;
  51. if (match = RegularExpressionLiteral.exec(input)) {
  52. lastIndex = RegularExpressionLiteral.lastIndex;
  53. lastSignificantToken = match[0];
  54. postfixIncDec = true;
  55. yield ({
  56. type: "RegularExpressionLiteral",
  57. value: match[0],
  58. closed: match[1] !== void 0 && match[1] !== "\\"
  59. });
  60. continue;
  61. }
  62. }
  63. Punctuator.lastIndex = lastIndex;
  64. if (match = Punctuator.exec(input)) {
  65. punctuator = match[0];
  66. nextLastIndex = Punctuator.lastIndex;
  67. nextLastSignificantToken = punctuator;
  68. switch (punctuator) {
  69. case "(":
  70. if (lastSignificantToken === "?NonExpressionParenKeyword") {
  71. stack.push({
  72. tag: "JSNonExpressionParen",
  73. nesting: parenNesting
  74. });
  75. }
  76. parenNesting++;
  77. postfixIncDec = false;
  78. break;
  79. case ")":
  80. parenNesting--;
  81. postfixIncDec = true;
  82. if (mode.tag === "JSNonExpressionParen" && parenNesting === mode.nesting) {
  83. stack.pop();
  84. nextLastSignificantToken = "?NonExpressionParenEnd";
  85. postfixIncDec = false;
  86. }
  87. break;
  88. case "{":
  89. Punctuator.lastIndex = 0;
  90. isExpression = !TokensNotPrecedingObjectLiteral.test(lastSignificantToken) && (TokensPrecedingExpression.test(lastSignificantToken) || KeywordsWithExpressionAfter.test(lastSignificantToken));
  91. braces.push(isExpression);
  92. postfixIncDec = false;
  93. break;
  94. case "}":
  95. switch (mode.tag) {
  96. case "InterpolationInTemplate":
  97. if (braces.length === mode.nesting) {
  98. Template.lastIndex = lastIndex;
  99. match = Template.exec(input);
  100. lastIndex = Template.lastIndex;
  101. lastSignificantToken = match[0];
  102. if (match[1] === "${") {
  103. lastSignificantToken = "?InterpolationInTemplate";
  104. postfixIncDec = false;
  105. yield ({
  106. type: "TemplateMiddle",
  107. value: match[0]
  108. });
  109. } else {
  110. stack.pop();
  111. postfixIncDec = true;
  112. yield ({
  113. type: "TemplateTail",
  114. value: match[0],
  115. closed: match[1] === "`"
  116. });
  117. }
  118. continue;
  119. }
  120. break;
  121. case "InterpolationInJSX":
  122. if (braces.length === mode.nesting) {
  123. stack.pop();
  124. lastIndex += 1;
  125. lastSignificantToken = "}";
  126. yield ({
  127. type: "JSXPunctuator",
  128. value: "}"
  129. });
  130. continue;
  131. }
  132. }
  133. postfixIncDec = braces.pop();
  134. nextLastSignificantToken = postfixIncDec ? "?ExpressionBraceEnd" : "}";
  135. break;
  136. case "]":
  137. postfixIncDec = true;
  138. break;
  139. case "++":
  140. case "--":
  141. nextLastSignificantToken = postfixIncDec ? "?PostfixIncDec" : "?UnaryIncDec";
  142. break;
  143. case "<":
  144. if (jsx && (TokensPrecedingExpression.test(lastSignificantToken) || KeywordsWithExpressionAfter.test(lastSignificantToken))) {
  145. stack.push({tag: "JSXTag"});
  146. lastIndex += 1;
  147. lastSignificantToken = "<";
  148. yield ({
  149. type: "JSXPunctuator",
  150. value: punctuator
  151. });
  152. continue;
  153. }
  154. postfixIncDec = false;
  155. break;
  156. default:
  157. postfixIncDec = false;
  158. }
  159. lastIndex = nextLastIndex;
  160. lastSignificantToken = nextLastSignificantToken;
  161. yield ({
  162. type: "Punctuator",
  163. value: punctuator
  164. });
  165. continue;
  166. }
  167. Identifier.lastIndex = lastIndex;
  168. if (match = Identifier.exec(input)) {
  169. lastIndex = Identifier.lastIndex;
  170. nextLastSignificantToken = match[0];
  171. switch (match[0]) {
  172. case "for":
  173. case "if":
  174. case "while":
  175. case "with":
  176. if (lastSignificantToken !== "." && lastSignificantToken !== "?.") {
  177. nextLastSignificantToken = "?NonExpressionParenKeyword";
  178. }
  179. }
  180. lastSignificantToken = nextLastSignificantToken;
  181. postfixIncDec = !KeywordsWithExpressionAfter.test(match[0]);
  182. yield ({
  183. type: match[1] === "#" ? "PrivateIdentifier" : "IdentifierName",
  184. value: match[0]
  185. });
  186. continue;
  187. }
  188. StringLiteral.lastIndex = lastIndex;
  189. if (match = StringLiteral.exec(input)) {
  190. lastIndex = StringLiteral.lastIndex;
  191. lastSignificantToken = match[0];
  192. postfixIncDec = true;
  193. yield ({
  194. type: "StringLiteral",
  195. value: match[0],
  196. closed: match[2] !== void 0
  197. });
  198. continue;
  199. }
  200. NumericLiteral.lastIndex = lastIndex;
  201. if (match = NumericLiteral.exec(input)) {
  202. lastIndex = NumericLiteral.lastIndex;
  203. lastSignificantToken = match[0];
  204. postfixIncDec = true;
  205. yield ({
  206. type: "NumericLiteral",
  207. value: match[0]
  208. });
  209. continue;
  210. }
  211. Template.lastIndex = lastIndex;
  212. if (match = Template.exec(input)) {
  213. lastIndex = Template.lastIndex;
  214. lastSignificantToken = match[0];
  215. if (match[1] === "${") {
  216. lastSignificantToken = "?InterpolationInTemplate";
  217. stack.push({
  218. tag: "InterpolationInTemplate",
  219. nesting: braces.length
  220. });
  221. postfixIncDec = false;
  222. yield ({
  223. type: "TemplateHead",
  224. value: match[0]
  225. });
  226. } else {
  227. postfixIncDec = true;
  228. yield ({
  229. type: "NoSubstitutionTemplate",
  230. value: match[0],
  231. closed: match[1] === "`"
  232. });
  233. }
  234. continue;
  235. }
  236. break;
  237. case "JSXTag":
  238. case "JSXTagEnd":
  239. JSXPunctuator.lastIndex = lastIndex;
  240. if (match = JSXPunctuator.exec(input)) {
  241. lastIndex = JSXPunctuator.lastIndex;
  242. nextLastSignificantToken = match[0];
  243. switch (match[0]) {
  244. case "<":
  245. stack.push({tag: "JSXTag"});
  246. break;
  247. case ">":
  248. stack.pop();
  249. if (lastSignificantToken === "/" || mode.tag === "JSXTagEnd") {
  250. nextLastSignificantToken = "?JSX";
  251. postfixIncDec = true;
  252. } else {
  253. stack.push({tag: "JSXChildren"});
  254. }
  255. break;
  256. case "{":
  257. stack.push({
  258. tag: "InterpolationInJSX",
  259. nesting: braces.length
  260. });
  261. nextLastSignificantToken = "?InterpolationInJSX";
  262. postfixIncDec = false;
  263. break;
  264. case "/":
  265. if (lastSignificantToken === "<") {
  266. stack.pop();
  267. if (stack[stack.length - 1].tag === "JSXChildren") {
  268. stack.pop();
  269. }
  270. stack.push({tag: "JSXTagEnd"});
  271. }
  272. }
  273. lastSignificantToken = nextLastSignificantToken;
  274. yield ({
  275. type: "JSXPunctuator",
  276. value: match[0]
  277. });
  278. continue;
  279. }
  280. JSXIdentifier.lastIndex = lastIndex;
  281. if (match = JSXIdentifier.exec(input)) {
  282. lastIndex = JSXIdentifier.lastIndex;
  283. lastSignificantToken = match[0];
  284. yield ({
  285. type: "JSXIdentifier",
  286. value: match[0]
  287. });
  288. continue;
  289. }
  290. JSXString.lastIndex = lastIndex;
  291. if (match = JSXString.exec(input)) {
  292. lastIndex = JSXString.lastIndex;
  293. lastSignificantToken = match[0];
  294. yield ({
  295. type: "JSXString",
  296. value: match[0],
  297. closed: match[2] !== void 0
  298. });
  299. continue;
  300. }
  301. break;
  302. case "JSXChildren":
  303. JSXText.lastIndex = lastIndex;
  304. if (match = JSXText.exec(input)) {
  305. lastIndex = JSXText.lastIndex;
  306. lastSignificantToken = match[0];
  307. yield ({
  308. type: "JSXText",
  309. value: match[0]
  310. });
  311. continue;
  312. }
  313. switch (input[lastIndex]) {
  314. case "<":
  315. stack.push({tag: "JSXTag"});
  316. lastIndex++;
  317. lastSignificantToken = "<";
  318. yield ({
  319. type: "JSXPunctuator",
  320. value: "<"
  321. });
  322. continue;
  323. case "{":
  324. stack.push({
  325. tag: "InterpolationInJSX",
  326. nesting: braces.length
  327. });
  328. lastIndex++;
  329. lastSignificantToken = "?InterpolationInJSX";
  330. postfixIncDec = false;
  331. yield ({
  332. type: "JSXPunctuator",
  333. value: "{"
  334. });
  335. continue;
  336. }
  337. }
  338. WhiteSpace.lastIndex = lastIndex;
  339. if (match = WhiteSpace.exec(input)) {
  340. lastIndex = WhiteSpace.lastIndex;
  341. yield ({
  342. type: "WhiteSpace",
  343. value: match[0]
  344. });
  345. continue;
  346. }
  347. LineTerminatorSequence.lastIndex = lastIndex;
  348. if (match = LineTerminatorSequence.exec(input)) {
  349. lastIndex = LineTerminatorSequence.lastIndex;
  350. postfixIncDec = false;
  351. if (KeywordsWithNoLineTerminatorAfter.test(lastSignificantToken)) {
  352. lastSignificantToken = "?NoLineTerminatorHere";
  353. }
  354. yield ({
  355. type: "LineTerminatorSequence",
  356. value: match[0]
  357. });
  358. continue;
  359. }
  360. MultiLineComment.lastIndex = lastIndex;
  361. if (match = MultiLineComment.exec(input)) {
  362. lastIndex = MultiLineComment.lastIndex;
  363. if (Newline.test(match[0])) {
  364. postfixIncDec = false;
  365. if (KeywordsWithNoLineTerminatorAfter.test(lastSignificantToken)) {
  366. lastSignificantToken = "?NoLineTerminatorHere";
  367. }
  368. }
  369. yield ({
  370. type: "MultiLineComment",
  371. value: match[0],
  372. closed: match[1] !== void 0
  373. });
  374. continue;
  375. }
  376. SingleLineComment.lastIndex = lastIndex;
  377. if (match = SingleLineComment.exec(input)) {
  378. lastIndex = SingleLineComment.lastIndex;
  379. postfixIncDec = false;
  380. yield ({
  381. type: "SingleLineComment",
  382. value: match[0]
  383. });
  384. continue;
  385. }
  386. firstCodePoint = String.fromCodePoint(input.codePointAt(lastIndex));
  387. lastIndex += firstCodePoint.length;
  388. lastSignificantToken = firstCodePoint;
  389. postfixIncDec = false;
  390. yield ({
  391. type: mode.tag.startsWith("JSX") ? "JSXInvalid" : "Invalid",
  392. value: firstCodePoint
  393. });
  394. }
  395. return void 0;
  396. };