index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _xhtml = require("./xhtml");
  7. var _types = require("../../tokenizer/types");
  8. var _context = require("../../tokenizer/context");
  9. var _identifier = require("../../util/identifier");
  10. var _whitespace = require("../../util/whitespace");
  11. var _parseError = require("../../parse-error");
  12. const JsxErrors = (0, _parseError.ParseErrorEnum)`jsx`({
  13. AttributeIsEmpty: "JSX attributes must only be assigned a non-empty expression.",
  14. MissingClosingTagElement: ({
  15. openingTagName
  16. }) => `Expected corresponding JSX closing tag for <${openingTagName}>.`,
  17. MissingClosingTagFragment: "Expected corresponding JSX closing tag for <>.",
  18. UnexpectedSequenceExpression: "Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?",
  19. UnexpectedToken: ({
  20. unexpected,
  21. HTMLEntity
  22. }) => `Unexpected token \`${unexpected}\`. Did you mean \`${HTMLEntity}\` or \`{'${unexpected}'}\`?`,
  23. UnsupportedJsxValue: "JSX value should be either an expression or a quoted JSX text.",
  24. UnterminatedJsxContent: "Unterminated JSX contents.",
  25. UnwrappedAdjacentJSXElements: "Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>?"
  26. });
  27. function isFragment(object) {
  28. return object ? object.type === "JSXOpeningFragment" || object.type === "JSXClosingFragment" : false;
  29. }
  30. function getQualifiedJSXName(object) {
  31. if (object.type === "JSXIdentifier") {
  32. return object.name;
  33. }
  34. if (object.type === "JSXNamespacedName") {
  35. return object.namespace.name + ":" + object.name.name;
  36. }
  37. if (object.type === "JSXMemberExpression") {
  38. return getQualifiedJSXName(object.object) + "." + getQualifiedJSXName(object.property);
  39. }
  40. throw new Error("Node had unexpected type: " + object.type);
  41. }
  42. var _default = superClass => class JSXParserMixin extends superClass {
  43. jsxReadToken() {
  44. let out = "";
  45. let chunkStart = this.state.pos;
  46. for (;;) {
  47. if (this.state.pos >= this.length) {
  48. throw this.raise(JsxErrors.UnterminatedJsxContent, {
  49. at: this.state.startLoc
  50. });
  51. }
  52. const ch = this.input.charCodeAt(this.state.pos);
  53. switch (ch) {
  54. case 60:
  55. case 123:
  56. if (this.state.pos === this.state.start) {
  57. if (ch === 60 && this.state.canStartJSXElement) {
  58. ++this.state.pos;
  59. this.finishToken(140);
  60. } else {
  61. super.getTokenFromCode(ch);
  62. }
  63. return;
  64. }
  65. out += this.input.slice(chunkStart, this.state.pos);
  66. this.finishToken(139, out);
  67. return;
  68. case 38:
  69. out += this.input.slice(chunkStart, this.state.pos);
  70. out += this.jsxReadEntity();
  71. chunkStart = this.state.pos;
  72. break;
  73. case 62:
  74. case 125:
  75. ;
  76. default:
  77. if ((0, _whitespace.isNewLine)(ch)) {
  78. out += this.input.slice(chunkStart, this.state.pos);
  79. out += this.jsxReadNewLine(true);
  80. chunkStart = this.state.pos;
  81. } else {
  82. ++this.state.pos;
  83. }
  84. }
  85. }
  86. }
  87. jsxReadNewLine(normalizeCRLF) {
  88. const ch = this.input.charCodeAt(this.state.pos);
  89. let out;
  90. ++this.state.pos;
  91. if (ch === 13 && this.input.charCodeAt(this.state.pos) === 10) {
  92. ++this.state.pos;
  93. out = normalizeCRLF ? "\n" : "\r\n";
  94. } else {
  95. out = String.fromCharCode(ch);
  96. }
  97. ++this.state.curLine;
  98. this.state.lineStart = this.state.pos;
  99. return out;
  100. }
  101. jsxReadString(quote) {
  102. let out = "";
  103. let chunkStart = ++this.state.pos;
  104. for (;;) {
  105. if (this.state.pos >= this.length) {
  106. throw this.raise(_parseError.Errors.UnterminatedString, {
  107. at: this.state.startLoc
  108. });
  109. }
  110. const ch = this.input.charCodeAt(this.state.pos);
  111. if (ch === quote) break;
  112. if (ch === 38) {
  113. out += this.input.slice(chunkStart, this.state.pos);
  114. out += this.jsxReadEntity();
  115. chunkStart = this.state.pos;
  116. } else if ((0, _whitespace.isNewLine)(ch)) {
  117. out += this.input.slice(chunkStart, this.state.pos);
  118. out += this.jsxReadNewLine(false);
  119. chunkStart = this.state.pos;
  120. } else {
  121. ++this.state.pos;
  122. }
  123. }
  124. out += this.input.slice(chunkStart, this.state.pos++);
  125. this.finishToken(131, out);
  126. }
  127. jsxReadEntity() {
  128. const startPos = ++this.state.pos;
  129. if (this.codePointAtPos(this.state.pos) === 35) {
  130. ++this.state.pos;
  131. let radix = 10;
  132. if (this.codePointAtPos(this.state.pos) === 120) {
  133. radix = 16;
  134. ++this.state.pos;
  135. }
  136. const codePoint = this.readInt(radix, undefined, false, "bail");
  137. if (codePoint !== null && this.codePointAtPos(this.state.pos) === 59) {
  138. ++this.state.pos;
  139. return String.fromCodePoint(codePoint);
  140. }
  141. } else {
  142. let count = 0;
  143. let semi = false;
  144. while (count++ < 10 && this.state.pos < this.length && !(semi = this.codePointAtPos(this.state.pos) == 59)) {
  145. ++this.state.pos;
  146. }
  147. if (semi) {
  148. const desc = this.input.slice(startPos, this.state.pos);
  149. const entity = _xhtml.default[desc];
  150. ++this.state.pos;
  151. if (entity) {
  152. return entity;
  153. }
  154. }
  155. }
  156. this.state.pos = startPos;
  157. return "&";
  158. }
  159. jsxReadWord() {
  160. let ch;
  161. const start = this.state.pos;
  162. do {
  163. ch = this.input.charCodeAt(++this.state.pos);
  164. } while ((0, _identifier.isIdentifierChar)(ch) || ch === 45);
  165. this.finishToken(138, this.input.slice(start, this.state.pos));
  166. }
  167. jsxParseIdentifier() {
  168. const node = this.startNode();
  169. if (this.match(138)) {
  170. node.name = this.state.value;
  171. } else if ((0, _types.tokenIsKeyword)(this.state.type)) {
  172. node.name = (0, _types.tokenLabelName)(this.state.type);
  173. } else {
  174. this.unexpected();
  175. }
  176. this.next();
  177. return this.finishNode(node, "JSXIdentifier");
  178. }
  179. jsxParseNamespacedName() {
  180. const startLoc = this.state.startLoc;
  181. const name = this.jsxParseIdentifier();
  182. if (!this.eat(14)) return name;
  183. const node = this.startNodeAt(startLoc);
  184. node.namespace = name;
  185. node.name = this.jsxParseIdentifier();
  186. return this.finishNode(node, "JSXNamespacedName");
  187. }
  188. jsxParseElementName() {
  189. const startLoc = this.state.startLoc;
  190. let node = this.jsxParseNamespacedName();
  191. if (node.type === "JSXNamespacedName") {
  192. return node;
  193. }
  194. while (this.eat(16)) {
  195. const newNode = this.startNodeAt(startLoc);
  196. newNode.object = node;
  197. newNode.property = this.jsxParseIdentifier();
  198. node = this.finishNode(newNode, "JSXMemberExpression");
  199. }
  200. return node;
  201. }
  202. jsxParseAttributeValue() {
  203. let node;
  204. switch (this.state.type) {
  205. case 5:
  206. node = this.startNode();
  207. this.setContext(_context.types.brace);
  208. this.next();
  209. node = this.jsxParseExpressionContainer(node, _context.types.j_oTag);
  210. if (node.expression.type === "JSXEmptyExpression") {
  211. this.raise(JsxErrors.AttributeIsEmpty, {
  212. at: node
  213. });
  214. }
  215. return node;
  216. case 140:
  217. case 131:
  218. return this.parseExprAtom();
  219. default:
  220. throw this.raise(JsxErrors.UnsupportedJsxValue, {
  221. at: this.state.startLoc
  222. });
  223. }
  224. }
  225. jsxParseEmptyExpression() {
  226. const node = this.startNodeAt(this.state.lastTokEndLoc);
  227. return this.finishNodeAt(node, "JSXEmptyExpression", this.state.startLoc);
  228. }
  229. jsxParseSpreadChild(node) {
  230. this.next();
  231. node.expression = this.parseExpression();
  232. this.setContext(_context.types.j_expr);
  233. this.state.canStartJSXElement = true;
  234. this.expect(8);
  235. return this.finishNode(node, "JSXSpreadChild");
  236. }
  237. jsxParseExpressionContainer(node, previousContext) {
  238. if (this.match(8)) {
  239. node.expression = this.jsxParseEmptyExpression();
  240. } else {
  241. const expression = this.parseExpression();
  242. ;
  243. node.expression = expression;
  244. }
  245. this.setContext(previousContext);
  246. this.state.canStartJSXElement = true;
  247. this.expect(8);
  248. return this.finishNode(node, "JSXExpressionContainer");
  249. }
  250. jsxParseAttribute() {
  251. const node = this.startNode();
  252. if (this.match(5)) {
  253. this.setContext(_context.types.brace);
  254. this.next();
  255. this.expect(21);
  256. node.argument = this.parseMaybeAssignAllowIn();
  257. this.setContext(_context.types.j_oTag);
  258. this.state.canStartJSXElement = true;
  259. this.expect(8);
  260. return this.finishNode(node, "JSXSpreadAttribute");
  261. }
  262. node.name = this.jsxParseNamespacedName();
  263. node.value = this.eat(29) ? this.jsxParseAttributeValue() : null;
  264. return this.finishNode(node, "JSXAttribute");
  265. }
  266. jsxParseOpeningElementAt(startLoc) {
  267. const node = this.startNodeAt(startLoc);
  268. if (this.eat(141)) {
  269. return this.finishNode(node, "JSXOpeningFragment");
  270. }
  271. node.name = this.jsxParseElementName();
  272. return this.jsxParseOpeningElementAfterName(node);
  273. }
  274. jsxParseOpeningElementAfterName(node) {
  275. const attributes = [];
  276. while (!this.match(56) && !this.match(141)) {
  277. attributes.push(this.jsxParseAttribute());
  278. }
  279. node.attributes = attributes;
  280. node.selfClosing = this.eat(56);
  281. this.expect(141);
  282. return this.finishNode(node, "JSXOpeningElement");
  283. }
  284. jsxParseClosingElementAt(startLoc) {
  285. const node = this.startNodeAt(startLoc);
  286. if (this.eat(141)) {
  287. return this.finishNode(node, "JSXClosingFragment");
  288. }
  289. node.name = this.jsxParseElementName();
  290. this.expect(141);
  291. return this.finishNode(node, "JSXClosingElement");
  292. }
  293. jsxParseElementAt(startLoc) {
  294. const node = this.startNodeAt(startLoc);
  295. const children = [];
  296. const openingElement = this.jsxParseOpeningElementAt(startLoc);
  297. let closingElement = null;
  298. if (!openingElement.selfClosing) {
  299. contents: for (;;) {
  300. switch (this.state.type) {
  301. case 140:
  302. startLoc = this.state.startLoc;
  303. this.next();
  304. if (this.eat(56)) {
  305. closingElement = this.jsxParseClosingElementAt(startLoc);
  306. break contents;
  307. }
  308. children.push(this.jsxParseElementAt(startLoc));
  309. break;
  310. case 139:
  311. children.push(this.parseExprAtom());
  312. break;
  313. case 5:
  314. {
  315. const node = this.startNode();
  316. this.setContext(_context.types.brace);
  317. this.next();
  318. if (this.match(21)) {
  319. children.push(this.jsxParseSpreadChild(node));
  320. } else {
  321. children.push(this.jsxParseExpressionContainer(node, _context.types.j_expr));
  322. }
  323. break;
  324. }
  325. default:
  326. this.unexpected();
  327. }
  328. }
  329. if (isFragment(openingElement) && !isFragment(closingElement) && closingElement !== null) {
  330. this.raise(JsxErrors.MissingClosingTagFragment, {
  331. at: closingElement
  332. });
  333. } else if (!isFragment(openingElement) && isFragment(closingElement)) {
  334. this.raise(JsxErrors.MissingClosingTagElement, {
  335. at: closingElement,
  336. openingTagName: getQualifiedJSXName(openingElement.name)
  337. });
  338. } else if (!isFragment(openingElement) && !isFragment(closingElement)) {
  339. if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) {
  340. this.raise(JsxErrors.MissingClosingTagElement, {
  341. at: closingElement,
  342. openingTagName: getQualifiedJSXName(openingElement.name)
  343. });
  344. }
  345. }
  346. }
  347. if (isFragment(openingElement)) {
  348. node.openingFragment = openingElement;
  349. node.closingFragment = closingElement;
  350. } else {
  351. node.openingElement = openingElement;
  352. node.closingElement = closingElement;
  353. }
  354. node.children = children;
  355. if (this.match(47)) {
  356. throw this.raise(JsxErrors.UnwrappedAdjacentJSXElements, {
  357. at: this.state.startLoc
  358. });
  359. }
  360. return isFragment(openingElement) ? this.finishNode(node, "JSXFragment") : this.finishNode(node, "JSXElement");
  361. }
  362. jsxParseElement() {
  363. const startLoc = this.state.startLoc;
  364. this.next();
  365. return this.jsxParseElementAt(startLoc);
  366. }
  367. setContext(newContext) {
  368. const {
  369. context
  370. } = this.state;
  371. context[context.length - 1] = newContext;
  372. }
  373. parseExprAtom(refExpressionErrors) {
  374. if (this.match(139)) {
  375. return this.parseLiteral(this.state.value, "JSXText");
  376. } else if (this.match(140)) {
  377. return this.jsxParseElement();
  378. } else if (this.match(47) && this.input.charCodeAt(this.state.pos) !== 33) {
  379. this.replaceToken(140);
  380. return this.jsxParseElement();
  381. } else {
  382. return super.parseExprAtom(refExpressionErrors);
  383. }
  384. }
  385. skipSpace() {
  386. const curContext = this.curContext();
  387. if (!curContext.preserveSpace) super.skipSpace();
  388. }
  389. getTokenFromCode(code) {
  390. const context = this.curContext();
  391. if (context === _context.types.j_expr) {
  392. this.jsxReadToken();
  393. return;
  394. }
  395. if (context === _context.types.j_oTag || context === _context.types.j_cTag) {
  396. if ((0, _identifier.isIdentifierStart)(code)) {
  397. this.jsxReadWord();
  398. return;
  399. }
  400. if (code === 62) {
  401. ++this.state.pos;
  402. this.finishToken(141);
  403. return;
  404. }
  405. if ((code === 34 || code === 39) && context === _context.types.j_oTag) {
  406. this.jsxReadString(code);
  407. return;
  408. }
  409. }
  410. if (code === 60 && this.state.canStartJSXElement && this.input.charCodeAt(this.state.pos + 1) !== 33) {
  411. ++this.state.pos;
  412. this.finishToken(140);
  413. return;
  414. }
  415. super.getTokenFromCode(code);
  416. }
  417. updateContext(prevType) {
  418. const {
  419. context,
  420. type
  421. } = this.state;
  422. if (type === 56 && prevType === 140) {
  423. context.splice(-2, 2, _context.types.j_cTag);
  424. this.state.canStartJSXElement = false;
  425. } else if (type === 140) {
  426. context.push(_context.types.j_oTag);
  427. } else if (type === 141) {
  428. const out = context[context.length - 1];
  429. if (out === _context.types.j_oTag && prevType === 56 || out === _context.types.j_cTag) {
  430. context.pop();
  431. this.state.canStartJSXElement = context[context.length - 1] === _context.types.j_expr;
  432. } else {
  433. this.setContext(_context.types.j_expr);
  434. this.state.canStartJSXElement = true;
  435. }
  436. } else {
  437. this.state.canStartJSXElement = (0, _types.tokenComesBeforeExpression)(type);
  438. }
  439. }
  440. };
  441. exports.default = _default;
  442. //# sourceMappingURL=index.js.map