index.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. 'use strict';
  2. function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
  3. var path = _interopDefault(require('path'));
  4. var through = _interopDefault(require('through2'));
  5. var PluginError = _interopDefault(require('plugin-error'));
  6. /*! *****************************************************************************
  7. Copyright (c) Microsoft Corporation.
  8. Permission to use, copy, modify, and/or distribute this software for any
  9. purpose with or without fee is hereby granted.
  10. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  11. REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  12. AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  13. INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  14. LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  15. OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  16. PERFORMANCE OF THIS SOFTWARE.
  17. ***************************************************************************** */
  18. /* global Reflect, Promise */
  19. var extendStatics = function(d, b) {
  20. extendStatics = Object.setPrototypeOf ||
  21. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  22. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  23. return extendStatics(d, b);
  24. };
  25. function __extends(d, b) {
  26. extendStatics(d, b);
  27. function __() { this.constructor = d; }
  28. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  29. }
  30. function __values(o) {
  31. var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
  32. if (m) return m.call(o);
  33. if (o && typeof o.length === "number") return {
  34. next: function () {
  35. if (o && i >= o.length) o = void 0;
  36. return { value: o && o[i++], done: !o };
  37. }
  38. };
  39. throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
  40. }
  41. function __read(o, n) {
  42. var m = typeof Symbol === "function" && o[Symbol.iterator];
  43. if (!m) return o;
  44. var i = m.call(o), r, ar = [], e;
  45. try {
  46. while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
  47. }
  48. catch (error) { e = { error: error }; }
  49. finally {
  50. try {
  51. if (r && !r.done && (m = i["return"])) m.call(i);
  52. }
  53. finally { if (e) throw e.error; }
  54. }
  55. return ar;
  56. }
  57. function __spread() {
  58. for (var ar = [], i = 0; i < arguments.length; i++)
  59. ar = ar.concat(__read(arguments[i]));
  60. return ar;
  61. }
  62. var CharCodes;
  63. (function (CharCodes) {
  64. CharCodes[CharCodes["LEFT_CURLY_BRACE"] = 123] = "LEFT_CURLY_BRACE";
  65. CharCodes[CharCodes["RIGHT_CURLY_BRACE"] = 125] = "RIGHT_CURLY_BRACE";
  66. CharCodes[CharCodes["SINGLE_QUOTE"] = 39] = "SINGLE_QUOTE";
  67. CharCodes[CharCodes["DOUBLE_QUOTE"] = 34] = "DOUBLE_QUOTE";
  68. CharCodes[CharCodes["BACK_QUOTE"] = 96] = "BACK_QUOTE";
  69. CharCodes[CharCodes["BACK_SLASH"] = 92] = "BACK_SLASH";
  70. CharCodes[CharCodes["SLASH"] = 47] = "SLASH";
  71. CharCodes[CharCodes["LESS_THAN"] = 60] = "LESS_THAN";
  72. CharCodes[CharCodes["GREATER_THAN"] = 62] = "GREATER_THAN";
  73. CharCodes[CharCodes["MINUS"] = 45] = "MINUS";
  74. CharCodes[CharCodes["UNDER_LINE"] = 95] = "UNDER_LINE";
  75. CharCodes[CharCodes["EQUALS"] = 61] = "EQUALS";
  76. CharCodes[CharCodes["EXCLAMATION"] = 33] = "EXCLAMATION";
  77. CharCodes[CharCodes["COLON"] = 58] = "COLON";
  78. CharCodes[CharCodes["LEFT_PAREN"] = 40] = "LEFT_PAREN";
  79. CharCodes[CharCodes["RIGHT_PAREN"] = 41] = "RIGHT_PAREN";
  80. CharCodes[CharCodes["DOT"] = 46] = "DOT";
  81. CharCodes[CharCodes["ASTERISK"] = 42] = "ASTERISK";
  82. CharCodes[CharCodes["TAB"] = 9] = "TAB";
  83. CharCodes[CharCodes["LINE_FEED"] = 10] = "LINE_FEED";
  84. CharCodes[CharCodes["CARRIAGE_RETURN"] = 13] = "CARRIAGE_RETURN";
  85. CharCodes[CharCodes["SPACE"] = 32] = "SPACE";
  86. CharCodes[CharCodes["UPPER_A"] = 65] = "UPPER_A";
  87. CharCodes[CharCodes["UPPER_Z"] = 90] = "UPPER_Z";
  88. CharCodes[CharCodes["LOWER_A"] = 97] = "LOWER_A";
  89. CharCodes[CharCodes["LOWER_Z"] = 122] = "LOWER_Z";
  90. CharCodes[CharCodes["_0"] = 48] = "_0";
  91. CharCodes[CharCodes["_9"] = 57] = "_9";
  92. CharCodes[CharCodes["$"] = 36] = "$";
  93. })(CharCodes || (CharCodes = {}));
  94. var WXS_LITERAL = 'wxs';
  95. function isWhitespace(code) {
  96. // TODO: other whitespaces
  97. return CharCodes.SPACE === code ||
  98. CharCodes.TAB === code ||
  99. CharCodes.LINE_FEED === code ||
  100. CharCodes.CARRIAGE_RETURN === code;
  101. }
  102. function isLetter(code) {
  103. return (code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z) || // A-Z
  104. (code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z) || // a-z
  105. code === CharCodes.MINUS || // -
  106. code === CharCodes.UNDER_LINE; // _
  107. }
  108. function isNumber(code) {
  109. return code >= CharCodes._0 && code <= CharCodes._9; // 0-9
  110. }
  111. function isValidFunctionLiteralChar(code) {
  112. return isNumber(code) ||
  113. (code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z) ||
  114. (code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z) ||
  115. code === CharCodes.UNDER_LINE ||
  116. code === CharCodes.$;
  117. }
  118. var Parser = /** @class */ (function () {
  119. function Parser(source, fileName) {
  120. this.source = source;
  121. this.fileName = fileName;
  122. this.pos = 0;
  123. this.line = 1;
  124. this.column = 1;
  125. }
  126. Parser.prototype.consumeChar = function () {
  127. var ch = this.source.charCodeAt(this.pos);
  128. this.advance();
  129. return ch;
  130. };
  131. Parser.prototype.consumeQuoteString = function (resultRef) {
  132. if (resultRef === void 0) { resultRef = {}; }
  133. var start = this.pos;
  134. if (this.match(CharCodes.SINGLE_QUOTE) || this.match(CharCodes.DOUBLE_QUOTE) || this.match(CharCodes.BACK_QUOTE)) {
  135. var quoteType = this.consumeChar();
  136. while (!this.eof() && !this.match(quoteType)) {
  137. if (this.match(CharCodes.BACK_SLASH) && this.match(quoteType, this.pos + 1)) {
  138. this.advance(2);
  139. }
  140. else
  141. this.advance();
  142. }
  143. if (this.match(quoteType))
  144. this.advance();
  145. resultRef.result = this.source.substring(start, this.pos);
  146. return true;
  147. }
  148. return false;
  149. };
  150. Parser.prototype.advance = function (step) {
  151. var _this = this;
  152. var _advanceOnce = function () {
  153. if (_this.source[_this.pos] === '\n') {
  154. _this.column = 1;
  155. _this.line++;
  156. }
  157. else {
  158. _this.column++;
  159. }
  160. _this.pos++;
  161. };
  162. if (!step) {
  163. _advanceOnce();
  164. }
  165. else {
  166. while (step-- > 0)
  167. _advanceOnce();
  168. }
  169. };
  170. Parser.prototype.eof = function () {
  171. return this.pos >= this.source.length;
  172. };
  173. Parser.prototype.match = function (code, pos) {
  174. return this.source.charCodeAt(pos && pos !== -1 ? pos : this.pos) === code;
  175. };
  176. Parser.prototype.currentChar = function () {
  177. return this.source[this.pos];
  178. };
  179. Parser.prototype.peekCharCode = function () {
  180. return this.source.charCodeAt(this.pos);
  181. };
  182. Parser.prototype.consumeWhitespace = function () {
  183. this.consumeWhile(isWhitespace);
  184. };
  185. Parser.prototype.consumeWhile = function (checkFunc) {
  186. var result = [];
  187. while (!this.eof() && checkFunc(this.source.charCodeAt(this.pos))) {
  188. var ch = this.source[this.pos];
  189. this.advance();
  190. result.push(ch);
  191. }
  192. return result.join('');
  193. };
  194. Parser.prototype.currentContext = function () {
  195. var MAX_NEXT_LINE = 2;
  196. var answer = '';
  197. var _pos = this.pos - 1, accumulateNextLine = 0;
  198. while (_pos >= 0 && accumulateNextLine < MAX_NEXT_LINE + 2) {
  199. if (this.source[_pos] === '\n') {
  200. accumulateNextLine++;
  201. }
  202. answer = this.source[_pos] + answer;
  203. _pos--;
  204. }
  205. if (this.pos < this.source.length) {
  206. answer += this.source[this.pos];
  207. }
  208. _pos = this.pos + 1, accumulateNextLine = 0;
  209. while (_pos < this.source.length && accumulateNextLine < MAX_NEXT_LINE) {
  210. if (this.source[_pos] === '\n') {
  211. accumulateNextLine++;
  212. }
  213. answer += this.source[_pos];
  214. _pos++;
  215. }
  216. return answer;
  217. };
  218. return Parser;
  219. }());
  220. var Expression = /** @class */ (function () {
  221. function Expression(start, end, expression) {
  222. if (start === void 0) { start = 0; }
  223. if (end === void 0) { end = 0; }
  224. if (expression === void 0) { expression = ''; }
  225. this.start = start;
  226. this.end = end;
  227. this.expression = expression;
  228. }
  229. return Expression;
  230. }());
  231. var CallExpression = /** @class */ (function (_super) {
  232. __extends(CallExpression, _super);
  233. function CallExpression(start, end, expression, functionNameStart, functionNameEnd, parameters, childFunctionExpressions) {
  234. if (start === void 0) { start = 0; }
  235. if (end === void 0) { end = 0; }
  236. if (expression === void 0) { expression = ''; }
  237. if (functionNameStart === void 0) { functionNameStart = 0; }
  238. if (functionNameEnd === void 0) { functionNameEnd = 0; }
  239. if (parameters === void 0) { parameters = ''; }
  240. if (childFunctionExpressions === void 0) { childFunctionExpressions = []; }
  241. var _this = _super.call(this, start, end, expression) || this;
  242. _this.functionNameStart = functionNameStart;
  243. _this.functionNameEnd = functionNameEnd;
  244. _this.parameters = parameters;
  245. _this.childFunctionExpressions = childFunctionExpressions;
  246. return _this;
  247. }
  248. return CallExpression;
  249. }(Expression));
  250. /**
  251. * ExpressionParser helps parsing expressions inside wxml interpolation block
  252. */
  253. var ExpressionParser = /** @class */ (function (_super) {
  254. __extends(ExpressionParser, _super);
  255. function ExpressionParser(source, fileName) {
  256. if (fileName === void 0) { fileName = ''; }
  257. var _this = _super.call(this, source, fileName) || this;
  258. _this.source = source;
  259. _this.fileName = fileName;
  260. _this.blockStart = -1;
  261. _this.expressions = [];
  262. _this.callExpressions = [];
  263. return _this;
  264. }
  265. ExpressionParser.prototype.parse = function () {
  266. try {
  267. var expr = this._parse();
  268. return expr;
  269. }
  270. catch (e) {
  271. if (this.fileName) {
  272. e.message += "\nfile: " + this.fileName;
  273. }
  274. e.message += "\nline: " + this.line + ", column: " + this.column + "\n\n" + this.currentContext();
  275. throw e;
  276. }
  277. };
  278. ExpressionParser.prototype._parse = function () {
  279. while (!this.eof()) {
  280. if (this.match(CharCodes.LEFT_CURLY_BRACE) && this.match(CharCodes.LEFT_CURLY_BRACE, this.pos + 1)) {
  281. this.advance(2);
  282. this.enterInterpolationBlock();
  283. // TODO: parse function calls and pass it to transfomers
  284. this.parseInterpolationExpression();
  285. continue;
  286. }
  287. this.advance();
  288. }
  289. return { expression: this.expressions, callExpressions: this.callExpressions };
  290. };
  291. /**
  292. * Parse expressions in wxml, it only cares about function calls
  293. * and ignore other expressions since it's trivial for i18n
  294. */
  295. ExpressionParser.prototype.parseInterpolationExpression = function () {
  296. while (!this.eof()) {
  297. this.consumeQuoteString();
  298. if (this.match(CharCodes.RIGHT_CURLY_BRACE) && this.match(CharCodes.RIGHT_CURLY_BRACE, this.pos + 1)) {
  299. var _a = this.exitInterpolationBlock(), start = _a.start, end = _a.end, block = _a.block;
  300. this.advance(2);
  301. if (end > start && start !== -1) {
  302. this.expressions.push(new Expression(start, end, block));
  303. }
  304. return;
  305. }
  306. // maybe function call expression
  307. if (this.match(CharCodes.LEFT_PAREN)) {
  308. var start = this.isFunctionCallExpression(this.pos);
  309. if (start !== -1) {
  310. var exprs = this.parseFunctionCallExpression(start);
  311. this.callExpressions.push(exprs);
  312. continue;
  313. }
  314. }
  315. this.advance();
  316. }
  317. };
  318. ExpressionParser.prototype.parseObjectDecl = function () {
  319. var callFunctions = [];
  320. while (!this.eof()) {
  321. this.consumeQuoteString();
  322. if (this.match(CharCodes.RIGHT_CURLY_BRACE)) {
  323. this.advance();
  324. return callFunctions;
  325. }
  326. if (this.match(CharCodes.LEFT_PAREN)) {
  327. var start = this.isFunctionCallExpression(this.pos);
  328. if (start !== -1) {
  329. var expr = this.parseFunctionCallExpression(start);
  330. callFunctions.push(expr);
  331. }
  332. }
  333. if (this.match(CharCodes.LEFT_CURLY_BRACE)) {
  334. this.advance();
  335. callFunctions.push.apply(callFunctions, __spread(this.parseObjectDecl()));
  336. continue;
  337. }
  338. this.advance();
  339. }
  340. return callFunctions;
  341. };
  342. ExpressionParser.prototype.parseFunctionCallExpression = function (start) {
  343. var childFunctions = [];
  344. var functionNameEnd = this.pos;
  345. if (this.consumeChar() !== CharCodes.LEFT_PAREN) {
  346. throw new Error('expected a left paren for a function call');
  347. }
  348. while (!this.eof()) {
  349. this.consumeQuoteString();
  350. if (this.match(CharCodes.LEFT_CURLY_BRACE)) {
  351. // JavaScript block should be ignored
  352. this.advance();
  353. var expr = this.parseObjectDecl();
  354. childFunctions.push.apply(childFunctions, __spread(expr));
  355. }
  356. if (this.consumeChar() === CharCodes.RIGHT_PAREN) {
  357. break;
  358. }
  359. }
  360. return new CallExpression(start, this.pos, this.source.substring(start, functionNameEnd), start, functionNameEnd, this.source.substring(functionNameEnd + 1, this.pos - 1).trim(), childFunctions);
  361. };
  362. ExpressionParser.prototype.enterInterpolationBlock = function () {
  363. // Already in an translation block, this must not be
  364. // valid translation block
  365. if (this.blockStart !== -1)
  366. return;
  367. this.blockStart = this.pos;
  368. };
  369. ExpressionParser.prototype.exitInterpolationBlock = function () {
  370. var start = this.blockStart;
  371. var end = this.pos;
  372. var block = this.source.substring(start, end);
  373. this.blockStart = -1;
  374. return { start: start, end: end, block: block };
  375. };
  376. ExpressionParser.prototype.matchNextChar = function (code) {
  377. return this.source.charCodeAt(++this.pos) === code;
  378. };
  379. ExpressionParser.prototype.isFunctionCallExpression = function (pos) {
  380. while (--pos >= 0) {
  381. // maybe wxs call {{ a.b() }}
  382. if (this.match(CharCodes.DOT, pos))
  383. return -1;
  384. if (!isValidFunctionLiteralChar(this.source.charCodeAt(pos)))
  385. break;
  386. }
  387. return pos + 1;
  388. };
  389. return ExpressionParser;
  390. }(Parser));
  391. var parse = require('format-message-parse');
  392. var Node = /** @class */ (function () {
  393. function Node(tagName) {
  394. this.tagName = tagName;
  395. }
  396. Node.prototype.dump = function () {
  397. return { type: this.tagName };
  398. };
  399. return Node;
  400. }());
  401. var AttributeValue = /** @class */ (function () {
  402. function AttributeValue(value, start, end) {
  403. this.value = value;
  404. this.start = start;
  405. this.end = end;
  406. }
  407. return AttributeValue;
  408. }());
  409. var Element = /** @class */ (function (_super) {
  410. __extends(Element, _super);
  411. function Element(tagName, attributes, children) {
  412. var _this = _super.call(this, tagName) || this;
  413. _this.attributes = attributes;
  414. _this.children = children;
  415. return _this;
  416. }
  417. Element.prototype.dump = function () {
  418. var e_1, _a;
  419. var attributes = {};
  420. if (this.attributes) {
  421. try {
  422. for (var _b = __values(this.attributes), _c = _b.next(); !_c.done; _c = _b.next()) {
  423. var _d = __read(_c.value, 2), key = _d[0], value = _d[1];
  424. if (value && value.value) {
  425. attributes[key] = value.value;
  426. }
  427. else {
  428. attributes[key] = null;
  429. }
  430. }
  431. }
  432. catch (e_1_1) { e_1 = { error: e_1_1 }; }
  433. finally {
  434. try {
  435. if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
  436. }
  437. finally { if (e_1) throw e_1.error; }
  438. }
  439. }
  440. var children = this.children.map(function (child) { return child.dump(); });
  441. return { type: this.tagName, attributes: attributes, children: children };
  442. };
  443. return Element;
  444. }(Node));
  445. var Text = /** @class */ (function (_super) {
  446. __extends(Text, _super);
  447. function Text(content, start, end) {
  448. var _this = _super.call(this, Text.tagName) || this;
  449. _this.content = content;
  450. _this.start = start;
  451. _this.end = end;
  452. return _this;
  453. }
  454. Text.prototype.dump = function () {
  455. return { type: this.tagName, content: this.content };
  456. };
  457. Text.tagName = 'text';
  458. return Text;
  459. }(Node));
  460. var WxmlState;
  461. (function (WxmlState) {
  462. WxmlState[WxmlState["NORMAL"] = 1] = "NORMAL";
  463. WxmlState[WxmlState["WXS"] = 2] = "WXS";
  464. WxmlState[WxmlState["INT"] = 4] = "INT";
  465. })(WxmlState || (WxmlState = {}));
  466. /**
  467. * Simple wxml parser
  468. */
  469. var WxmlParser = /** @class */ (function (_super) {
  470. __extends(WxmlParser, _super);
  471. function WxmlParser(source, fileName) {
  472. if (fileName === void 0) { fileName = ''; }
  473. var _this = _super.call(this, source, fileName) || this;
  474. _this.source = source;
  475. _this.fileName = fileName;
  476. _this.state = WxmlState.NORMAL;
  477. return _this;
  478. }
  479. WxmlParser.prototype.parse = function () {
  480. try {
  481. var nodes = this._parse();
  482. return nodes;
  483. }
  484. catch (e) {
  485. if (this.fileName) {
  486. e.message += "\nfile: " + this.fileName;
  487. }
  488. e.message += "\nline: " + this.line + ", column: " + this.column + "\n\n" + this.currentContext();
  489. throw e;
  490. }
  491. };
  492. WxmlParser.prototype._parse = function () {
  493. var nodes = [];
  494. while (!this.eof()) {
  495. this.consumeWhitespace();
  496. if (this.match(CharCodes.LESS_THAN) && this.match(CharCodes.SLASH, this.pos + 1)) {
  497. break;
  498. }
  499. // Ignore comments
  500. if (this.match(CharCodes.LESS_THAN, this.pos) &&
  501. this.match(CharCodes.EXCLAMATION, this.pos + 1) &&
  502. this.match(CharCodes.MINUS, this.pos + 2) &&
  503. this.match(CharCodes.MINUS, this.pos + 3)) {
  504. this.advance(4);
  505. this.parseComments();
  506. continue;
  507. }
  508. if (this.match(CharCodes.LESS_THAN)) {
  509. var node = this.parseWxmlTag();
  510. nodes.push(node);
  511. continue;
  512. }
  513. var textNode = this.parseText();
  514. if (textNode.content.length > 0)
  515. nodes.push(textNode);
  516. }
  517. return nodes;
  518. };
  519. WxmlParser.prototype.parseWxmlTag = function () {
  520. if (this.consumeChar() !== CharCodes.LESS_THAN) {
  521. throw new Error('unexpected character for wxml start tag');
  522. }
  523. this.consumeWhitespace();
  524. var tagName = this.parseTagName();
  525. if (!tagName || tagName.length === 0) {
  526. throw new Error("unexpected tag name " + this.currentChar());
  527. }
  528. var attributes = this.parseAttributes();
  529. // self-closing tag
  530. if (this.match(CharCodes.SLASH)) {
  531. this.advance();
  532. if (this.consumeChar() !== CharCodes.GREATER_THAN) {
  533. throw new Error('unexpected character ' + this.currentChar());
  534. }
  535. return new Element(tagName, attributes, []);
  536. }
  537. if (this.consumeChar() !== CharCodes.GREATER_THAN) {
  538. throw new Error('expected character > to close a tag');
  539. }
  540. if (tagName.toLowerCase() === WXS_LITERAL) {
  541. this.state = WxmlState.WXS;
  542. }
  543. var childNodes = this.parse();
  544. if (this.consumeChar() !== CharCodes.LESS_THAN) {
  545. throw new Error('expected char ' + String.fromCharCode(CharCodes.LESS_THAN) + ' but got ' + this.currentChar());
  546. }
  547. if (this.consumeChar() !== CharCodes.SLASH) {
  548. throw new Error('expected char ' + String.fromCharCode(CharCodes.SLASH) + ' but got ' + this.currentChar());
  549. }
  550. this.consumeWhitespace();
  551. var endTagName = this.parseTagName();
  552. if (endTagName !== tagName) {
  553. throw new Error("expected tag name " + tagName + " but got " + endTagName);
  554. }
  555. this.consumeWhitespace();
  556. if (!this.match(CharCodes.GREATER_THAN)) {
  557. throw new Error('expected char ' + String.fromCharCode(CharCodes.GREATER_THAN) + ' but got ' + this.currentChar());
  558. }
  559. this.advance();
  560. if (tagName.toLowerCase() === WXS_LITERAL) {
  561. this.state = WxmlState.NORMAL;
  562. }
  563. return new Element(tagName, attributes, childNodes);
  564. };
  565. WxmlParser.prototype.parseText = function () {
  566. var start = this.pos;
  567. if (this.state === WxmlState.WXS) {
  568. while (!this.eof() && !(this.match(CharCodes.LESS_THAN) && this.match(CharCodes.SLASH, this.pos + 1))) {
  569. if (!this.consumeQuoteString() && !this.consumeWXSComments()) {
  570. this.advance();
  571. }
  572. }
  573. return new Text(this.source.substring(start, this.pos), start, this.pos);
  574. }
  575. return new Text(this.parseTextContents(), start, this.pos);
  576. };
  577. WxmlParser.prototype.parseTextContents = function () {
  578. var result = [];
  579. while (!this.eof() && (!this.match(CharCodes.LESS_THAN) || this.state === WxmlState.INT)) {
  580. if (this.match(CharCodes.LEFT_CURLY_BRACE) && this.match(CharCodes.LEFT_CURLY_BRACE, this.pos + 1)) {
  581. this.state = WxmlState.INT;
  582. }
  583. if (this.match(CharCodes.RIGHT_CURLY_BRACE) && this.match(CharCodes.RIGHT_CURLY_BRACE, this.pos + 1)) {
  584. this.state = WxmlState.NORMAL;
  585. }
  586. var quoteStringRef = { result: '' };
  587. if (this.consumeQuoteString(quoteStringRef)) {
  588. result.push(quoteStringRef.result);
  589. continue;
  590. }
  591. var ch = this.source[this.pos];
  592. result.push(ch);
  593. this.advance();
  594. }
  595. return result.join('');
  596. };
  597. /**
  598. * Ignore comments
  599. */
  600. WxmlParser.prototype.parseComments = function () {
  601. while (!this.eof()) {
  602. if (this.match(CharCodes.MINUS, this.pos) &&
  603. this.match(CharCodes.MINUS, this.pos + 1) &&
  604. this.match(CharCodes.GREATER_THAN, this.pos + 2)) {
  605. this.advance(3);
  606. return;
  607. }
  608. this.advance();
  609. }
  610. };
  611. WxmlParser.prototype.parseTagName = function () {
  612. // loosy check
  613. // TODO: prevent number as first letter
  614. return this.consumeWhile(function (c) { return isLetter(c) || isNumber(c); });
  615. };
  616. WxmlParser.prototype.parseAttributeName = function () {
  617. // loosy check
  618. // TODO: prevent number as first letter
  619. // Note: can have colon (:) in between
  620. return this.consumeWhile(function (c) { return isLetter(c) || isNumber(c) || c === CharCodes.COLON; });
  621. };
  622. WxmlParser.prototype.parseAttributes = function () {
  623. var attrs = new Map();
  624. while (!this.eof()) {
  625. this.consumeWhitespace();
  626. if (this.match(CharCodes.SLASH) || this.match(CharCodes.GREATER_THAN))
  627. break;
  628. if (!isLetter(this.peekCharCode()) && !this.match(CharCodes.COLON))
  629. break;
  630. var _a = this.parseAttribute(), name = _a.name, value = _a.value;
  631. attrs.set(name, value);
  632. }
  633. return attrs;
  634. };
  635. WxmlParser.prototype.parseAttribute = function () {
  636. var name = this.parseAttributeName();
  637. this.consumeWhitespace();
  638. if (!this.match(CharCodes.EQUALS)) {
  639. return { name: name, value: null };
  640. }
  641. this.advance();
  642. this.consumeWhitespace();
  643. var value = this.parseAttrValue();
  644. return { name: name, value: value };
  645. };
  646. WxmlParser.prototype.parseAttrValue = function () {
  647. var leftQuote = this.consumeChar();
  648. var start = this.pos;
  649. if (leftQuote !== CharCodes.SINGLE_QUOTE && leftQuote !== CharCodes.DOUBLE_QUOTE) {
  650. throw new Error("expected char " + String.fromCharCode(CharCodes.SINGLE_QUOTE) + " or " + String.fromCharCode(CharCodes.DOUBLE_QUOTE) + " " +
  651. ("but got " + String.fromCharCode(leftQuote)));
  652. }
  653. var value = this.consumeWhile(function (ch) { return ch !== leftQuote; });
  654. var end = this.pos;
  655. if (this.consumeChar() !== leftQuote) {
  656. throw new Error('expected char ' + String.fromCharCode(leftQuote) + ' to close an attribute');
  657. }
  658. var attribute = new AttributeValue(value, start, end);
  659. return attribute;
  660. };
  661. WxmlParser.prototype.consumeWXSComments = function () {
  662. if (this.match(CharCodes.SLASH) && this.match(CharCodes.SLASH, this.pos + 1)) {
  663. while (!this.eof()) {
  664. if (this.match(CharCodes.LINE_FEED)) {
  665. this.advance();
  666. break;
  667. }
  668. if (this.match(CharCodes.CARRIAGE_RETURN) && this.match(CharCodes.LINE_FEED)) {
  669. this.advance(2);
  670. break;
  671. }
  672. // If no line end is met we should end at this point
  673. if (this.match(CharCodes.LESS_THAN) && this.match(CharCodes.SLASH, this.pos + 1)) {
  674. return false;
  675. }
  676. this.advance();
  677. }
  678. return true;
  679. }
  680. else if (this.match(CharCodes.SLASH) && this.match(CharCodes.ASTERISK, this.pos + 1)) {
  681. while (!this.eof()) {
  682. if (this.match(CharCodes.ASTERISK) && this.match(CharCodes.SLASH, this.pos + 1)) {
  683. this.advance(2);
  684. break;
  685. }
  686. this.advance();
  687. }
  688. return true;
  689. }
  690. return false;
  691. };
  692. return WxmlParser;
  693. }(Parser));
  694. var TranslationFunction;
  695. (function (TranslationFunction) {
  696. TranslationFunction["default"] = "t";
  697. })(TranslationFunction || (TranslationFunction = {}));
  698. var I18nModuleName;
  699. (function (I18nModuleName) {
  700. I18nModuleName["default"] = "i18n";
  701. })(I18nModuleName || (I18nModuleName = {}));
  702. var LocaleVariable;
  703. (function (LocaleVariable) {
  704. LocaleVariable["default"] = "$_locale";
  705. })(LocaleVariable || (LocaleVariable = {}));
  706. var BLOCK_DELIMITER_START = '{{';
  707. /**
  708. * Transform miniprogram templates (WXML) to import i18n data and transform i18n invocations
  709. * i18n data will be imported via WXS mechanism while i18n invocations will be transformed
  710. * to normal WXS calls, e.g. t(key, val) will be transformed to something like $i18n.t(key, val)
  711. */
  712. var TranslationFunctionTransformer = /** @class */ (function () {
  713. function TranslationFunctionTransformer(translationFunctionName, i18nModuleName, currentLocaleVariableName) {
  714. if (translationFunctionName === void 0) { translationFunctionName = TranslationFunction.default; }
  715. if (i18nModuleName === void 0) { i18nModuleName = I18nModuleName.default; }
  716. if (currentLocaleVariableName === void 0) { currentLocaleVariableName = LocaleVariable.default; }
  717. this.translationFunctionName = translationFunctionName;
  718. this.i18nModuleName = i18nModuleName;
  719. this.currentLocaleVariableName = currentLocaleVariableName;
  720. this.source = '';
  721. }
  722. /**
  723. * Given a piece of wxml source code, transform its i18n function calls into normal wxs function calls
  724. */
  725. TranslationFunctionTransformer.prototype.transform = function (source, fileName) {
  726. if (fileName === void 0) { fileName = ''; }
  727. this.source = source;
  728. var parser = new WxmlParser(source, fileName);
  729. var nodes = parser.parse();
  730. // Transform function call expressions in place
  731. this.transformNodes(nodes, fileName);
  732. return this.source;
  733. };
  734. TranslationFunctionTransformer.prototype.transformNodes = function (nodes, fileName) {
  735. var e_1, _a;
  736. // walk through wxml nodes to pick up interpolation blocks
  737. for (var i = nodes.length - 1; i >= 0; i--) {
  738. var node = nodes[i];
  739. if (node instanceof Text) {
  740. if (node.content.includes(BLOCK_DELIMITER_START)) {
  741. var _b = this.transformFunctionCallExpr(node.content, fileName), content = _b.content, transformed = _b.transformed;
  742. if (transformed) {
  743. var head = this.source.substring(0, node.start);
  744. var rear = this.source.substring(node.end);
  745. this.source = head + content + rear;
  746. }
  747. }
  748. }
  749. else if (node instanceof Element) {
  750. this.transformNodes(node.children, fileName);
  751. var attributes = this.sortAttributesByStartPos(node.attributes);
  752. try {
  753. for (var attributes_1 = (e_1 = void 0, __values(attributes)), attributes_1_1 = attributes_1.next(); !attributes_1_1.done; attributes_1_1 = attributes_1.next()) {
  754. var attrValue = attributes_1_1.value;
  755. if (attrValue && attrValue.value.includes(BLOCK_DELIMITER_START)) {
  756. var _c = this.transformFunctionCallExpr(attrValue.value, fileName), content = _c.content, transformed = _c.transformed;
  757. if (transformed) {
  758. var head = this.source.substring(0, attrValue.start);
  759. var rear = this.source.substring(attrValue.end);
  760. this.source = head + content + rear;
  761. }
  762. }
  763. }
  764. }
  765. catch (e_1_1) { e_1 = { error: e_1_1 }; }
  766. finally {
  767. try {
  768. if (attributes_1_1 && !attributes_1_1.done && (_a = attributes_1.return)) _a.call(attributes_1);
  769. }
  770. finally { if (e_1) throw e_1.error; }
  771. }
  772. }
  773. }
  774. };
  775. TranslationFunctionTransformer.prototype.transformFunctionCallExpr = function (source, fileName) {
  776. var parser = new ExpressionParser(source, fileName);
  777. var expr = parser.parse();
  778. return this.transformFunctionCallExprRecursively(source, expr.callExpressions);
  779. };
  780. TranslationFunctionTransformer.prototype.transformFunctionCallExprRecursively = function (source, callExpressions) {
  781. var transformed = false;
  782. for (var i = callExpressions.length - 1; i >= 0; i--) {
  783. var callExpr = callExpressions[i];
  784. if (callExpr.expression === this.translationFunctionName) {
  785. // Inject currentLocale variable
  786. var end = callExpr.end - 1;
  787. var localeVarDecl = callExpr.parameters.length > 0 ? ", " + this.currentLocaleVariableName : this.currentLocaleVariableName;
  788. source = source.substring(0, end) + localeVarDecl + source.substring(end);
  789. var child = this.transformFunctionCallExprRecursively(source, callExpr.childFunctionExpressions);
  790. transformed = transformed || child.transformed;
  791. source = child.content;
  792. var head = source.substring(0, callExpr.functionNameStart);
  793. var rear = source.substring(callExpr.functionNameEnd);
  794. source = head + this.i18nModuleName + '.' + TranslationFunction.default + rear;
  795. transformed = true;
  796. }
  797. }
  798. return { transformed: transformed, content: source };
  799. };
  800. TranslationFunctionTransformer.prototype.sortAttributesByStartPos = function (attributes) {
  801. // descendent
  802. return Array.from(attributes.values()).filter(function (a) { return !!a; }).sort(function (a, b) { return b.start - a.start; });
  803. };
  804. return TranslationFunctionTransformer;
  805. }());
  806. var PLUGIN_NAME = '@miniprogram-i18n/gulp-i18n-wxml';
  807. // Assume source folder is src
  808. var DEFAULT_WXS_PATH = path.resolve('src/i18n/locales.wxs');
  809. var getWxsTag = function (path$$1, moduleName) { return "<wxs src=\"" + path$$1 + "\" module=\"" + moduleName + "\" />\n"; };
  810. var gulpI18nWxmlTransformer = function (options) { return through.obj(function (file, _, cb) {
  811. var opts = options || { wxsPath: '', wxsModuleName: '', i18nFunctionName: '' };
  812. var wxsPath = opts.wxsPath || DEFAULT_WXS_PATH;
  813. var wxsModuleName = opts.wxsModuleName || I18nModuleName.default;
  814. var i18nFunctionName = opts.i18nFunctionName || TranslationFunction.default;
  815. // Ignore empty file or not wxml files
  816. if (file.isNull() || path.extname(file.path) !== '.wxml') {
  817. return cb(null, file);
  818. }
  819. if (file.isStream()) {
  820. return cb(new PluginError(PLUGIN_NAME, 'Streaming data is not supported'));
  821. }
  822. // TODO: read functionn name from options
  823. var transfomer = new TranslationFunctionTransformer(i18nFunctionName, wxsModuleName);
  824. if (!file.contents) {
  825. return cb(null, file);
  826. }
  827. try {
  828. var transformedContents = transfomer.transform(file.contents.toString('utf-8'), file.path);
  829. var relativeWxsPath = path.relative(path.dirname(file.path), wxsPath);
  830. if (process.platform === 'win32') {
  831. relativeWxsPath = relativeWxsPath.replace(/\\/g, '/');
  832. }
  833. var wxsTag = getWxsTag(relativeWxsPath, wxsModuleName);
  834. if (transformedContents.indexOf(wxsTag) !== -1) {
  835. // has already write wxs tag into wxml.
  836. file.contents = Buffer.from(transformedContents);
  837. return cb(null, file);
  838. }
  839. file.contents = Buffer.concat([Buffer.from(wxsTag), Buffer.from(transformedContents)]);
  840. }
  841. catch (err) {
  842. console.log('error:', err);
  843. return cb(new PluginError(PLUGIN_NAME, err));
  844. }
  845. return cb(null, file);
  846. }); };
  847. module.exports = gulpI18nWxmlTransformer;