index.js 16 KB

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