message-compiler.esm-bundler.js 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324
  1. /*!
  2. * @intlify/message-compiler v9.1.9
  3. * (c) 2021 kazuya kawaguchi
  4. * Released under the MIT License.
  5. */
  6. import { format, assign, isString } from '@intlify/shared';
  7. /** @internal */
  8. const errorMessages = {
  9. // tokenizer error messages
  10. [0 /* EXPECTED_TOKEN */]: `Expected token: '{0}'`,
  11. [1 /* INVALID_TOKEN_IN_PLACEHOLDER */]: `Invalid token in placeholder: '{0}'`,
  12. [2 /* UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER */]: `Unterminated single quote in placeholder`,
  13. [3 /* UNKNOWN_ESCAPE_SEQUENCE */]: `Unknown escape sequence: \\{0}`,
  14. [4 /* INVALID_UNICODE_ESCAPE_SEQUENCE */]: `Invalid unicode escape sequence: {0}`,
  15. [5 /* UNBALANCED_CLOSING_BRACE */]: `Unbalanced closing brace`,
  16. [6 /* UNTERMINATED_CLOSING_BRACE */]: `Unterminated closing brace`,
  17. [7 /* EMPTY_PLACEHOLDER */]: `Empty placeholder`,
  18. [8 /* NOT_ALLOW_NEST_PLACEHOLDER */]: `Not allowed nest placeholder`,
  19. [9 /* INVALID_LINKED_FORMAT */]: `Invalid linked format`,
  20. // parser error messages
  21. [10 /* MUST_HAVE_MESSAGES_IN_PLURAL */]: `Plural must have messages`,
  22. [11 /* UNEXPECTED_EMPTY_LINKED_MODIFIER */]: `Unexpected empty linked modifier`,
  23. [12 /* UNEXPECTED_EMPTY_LINKED_KEY */]: `Unexpected empty linked key`,
  24. [13 /* UNEXPECTED_LEXICAL_ANALYSIS */]: `Unexpected lexical analysis in token: '{0}'`
  25. };
  26. function createCompileError(code, loc, options = {}) {
  27. const { domain, messages, args } = options;
  28. const msg = (process.env.NODE_ENV !== 'production')
  29. ? format((messages || errorMessages)[code] || '', ...(args || []))
  30. : code;
  31. const error = new SyntaxError(String(msg));
  32. error.code = code;
  33. if (loc) {
  34. error.location = loc;
  35. }
  36. error.domain = domain;
  37. return error;
  38. }
  39. /** @internal */
  40. function defaultOnError(error) {
  41. throw error;
  42. }
  43. const LocationStub = {
  44. start: { line: 1, column: 1, offset: 0 },
  45. end: { line: 1, column: 1, offset: 0 }
  46. };
  47. function createPosition(line, column, offset) {
  48. return { line, column, offset };
  49. }
  50. function createLocation(start, end, source) {
  51. const loc = { start, end };
  52. if (source != null) {
  53. loc.source = source;
  54. }
  55. return loc;
  56. }
  57. const CHAR_SP = ' ';
  58. const CHAR_CR = '\r';
  59. const CHAR_LF = '\n';
  60. const CHAR_LS = String.fromCharCode(0x2028);
  61. const CHAR_PS = String.fromCharCode(0x2029);
  62. function createScanner(str) {
  63. const _buf = str;
  64. let _index = 0;
  65. let _line = 1;
  66. let _column = 1;
  67. let _peekOffset = 0;
  68. const isCRLF = (index) => _buf[index] === CHAR_CR && _buf[index + 1] === CHAR_LF;
  69. const isLF = (index) => _buf[index] === CHAR_LF;
  70. const isPS = (index) => _buf[index] === CHAR_PS;
  71. const isLS = (index) => _buf[index] === CHAR_LS;
  72. const isLineEnd = (index) => isCRLF(index) || isLF(index) || isPS(index) || isLS(index);
  73. const index = () => _index;
  74. const line = () => _line;
  75. const column = () => _column;
  76. const peekOffset = () => _peekOffset;
  77. const charAt = (offset) => isCRLF(offset) || isPS(offset) || isLS(offset) ? CHAR_LF : _buf[offset];
  78. const currentChar = () => charAt(_index);
  79. const currentPeek = () => charAt(_index + _peekOffset);
  80. function next() {
  81. _peekOffset = 0;
  82. if (isLineEnd(_index)) {
  83. _line++;
  84. _column = 0;
  85. }
  86. if (isCRLF(_index)) {
  87. _index++;
  88. }
  89. _index++;
  90. _column++;
  91. return _buf[_index];
  92. }
  93. function peek() {
  94. if (isCRLF(_index + _peekOffset)) {
  95. _peekOffset++;
  96. }
  97. _peekOffset++;
  98. return _buf[_index + _peekOffset];
  99. }
  100. function reset() {
  101. _index = 0;
  102. _line = 1;
  103. _column = 1;
  104. _peekOffset = 0;
  105. }
  106. function resetPeek(offset = 0) {
  107. _peekOffset = offset;
  108. }
  109. function skipToPeek() {
  110. const target = _index + _peekOffset;
  111. // eslint-disable-next-line no-unmodified-loop-condition
  112. while (target !== _index) {
  113. next();
  114. }
  115. _peekOffset = 0;
  116. }
  117. return {
  118. index,
  119. line,
  120. column,
  121. peekOffset,
  122. charAt,
  123. currentChar,
  124. currentPeek,
  125. next,
  126. peek,
  127. reset,
  128. resetPeek,
  129. skipToPeek
  130. };
  131. }
  132. const EOF = undefined;
  133. const LITERAL_DELIMITER = "'";
  134. const ERROR_DOMAIN$1 = 'tokenizer';
  135. function createTokenizer(source, options = {}) {
  136. const location = options.location !== false;
  137. const _scnr = createScanner(source);
  138. const currentOffset = () => _scnr.index();
  139. const currentPosition = () => createPosition(_scnr.line(), _scnr.column(), _scnr.index());
  140. const _initLoc = currentPosition();
  141. const _initOffset = currentOffset();
  142. const _context = {
  143. currentType: 14 /* EOF */,
  144. offset: _initOffset,
  145. startLoc: _initLoc,
  146. endLoc: _initLoc,
  147. lastType: 14 /* EOF */,
  148. lastOffset: _initOffset,
  149. lastStartLoc: _initLoc,
  150. lastEndLoc: _initLoc,
  151. braceNest: 0,
  152. inLinked: false,
  153. text: ''
  154. };
  155. const context = () => _context;
  156. const { onError } = options;
  157. function emitError(code, pos, offset, ...args) {
  158. const ctx = context();
  159. pos.column += offset;
  160. pos.offset += offset;
  161. if (onError) {
  162. const loc = createLocation(ctx.startLoc, pos);
  163. const err = createCompileError(code, loc, {
  164. domain: ERROR_DOMAIN$1,
  165. args
  166. });
  167. onError(err);
  168. }
  169. }
  170. function getToken(context, type, value) {
  171. context.endLoc = currentPosition();
  172. context.currentType = type;
  173. const token = { type };
  174. if (location) {
  175. token.loc = createLocation(context.startLoc, context.endLoc);
  176. }
  177. if (value != null) {
  178. token.value = value;
  179. }
  180. return token;
  181. }
  182. const getEndToken = (context) => getToken(context, 14 /* EOF */);
  183. function eat(scnr, ch) {
  184. if (scnr.currentChar() === ch) {
  185. scnr.next();
  186. return ch;
  187. }
  188. else {
  189. emitError(0 /* EXPECTED_TOKEN */, currentPosition(), 0, ch);
  190. return '';
  191. }
  192. }
  193. function peekSpaces(scnr) {
  194. let buf = '';
  195. while (scnr.currentPeek() === CHAR_SP || scnr.currentPeek() === CHAR_LF) {
  196. buf += scnr.currentPeek();
  197. scnr.peek();
  198. }
  199. return buf;
  200. }
  201. function skipSpaces(scnr) {
  202. const buf = peekSpaces(scnr);
  203. scnr.skipToPeek();
  204. return buf;
  205. }
  206. function isIdentifierStart(ch) {
  207. if (ch === EOF) {
  208. return false;
  209. }
  210. const cc = ch.charCodeAt(0);
  211. return ((cc >= 97 && cc <= 122) || // a-z
  212. (cc >= 65 && cc <= 90) || // A-Z
  213. cc === 95 // _
  214. );
  215. }
  216. function isNumberStart(ch) {
  217. if (ch === EOF) {
  218. return false;
  219. }
  220. const cc = ch.charCodeAt(0);
  221. return cc >= 48 && cc <= 57; // 0-9
  222. }
  223. function isNamedIdentifierStart(scnr, context) {
  224. const { currentType } = context;
  225. if (currentType !== 2 /* BraceLeft */) {
  226. return false;
  227. }
  228. peekSpaces(scnr);
  229. const ret = isIdentifierStart(scnr.currentPeek());
  230. scnr.resetPeek();
  231. return ret;
  232. }
  233. function isListIdentifierStart(scnr, context) {
  234. const { currentType } = context;
  235. if (currentType !== 2 /* BraceLeft */) {
  236. return false;
  237. }
  238. peekSpaces(scnr);
  239. const ch = scnr.currentPeek() === '-' ? scnr.peek() : scnr.currentPeek();
  240. const ret = isNumberStart(ch);
  241. scnr.resetPeek();
  242. return ret;
  243. }
  244. function isLiteralStart(scnr, context) {
  245. const { currentType } = context;
  246. if (currentType !== 2 /* BraceLeft */) {
  247. return false;
  248. }
  249. peekSpaces(scnr);
  250. const ret = scnr.currentPeek() === LITERAL_DELIMITER;
  251. scnr.resetPeek();
  252. return ret;
  253. }
  254. function isLinkedDotStart(scnr, context) {
  255. const { currentType } = context;
  256. if (currentType !== 8 /* LinkedAlias */) {
  257. return false;
  258. }
  259. peekSpaces(scnr);
  260. const ret = scnr.currentPeek() === "." /* LinkedDot */;
  261. scnr.resetPeek();
  262. return ret;
  263. }
  264. function isLinkedModifierStart(scnr, context) {
  265. const { currentType } = context;
  266. if (currentType !== 9 /* LinkedDot */) {
  267. return false;
  268. }
  269. peekSpaces(scnr);
  270. const ret = isIdentifierStart(scnr.currentPeek());
  271. scnr.resetPeek();
  272. return ret;
  273. }
  274. function isLinkedDelimiterStart(scnr, context) {
  275. const { currentType } = context;
  276. if (!(currentType === 8 /* LinkedAlias */ ||
  277. currentType === 12 /* LinkedModifier */)) {
  278. return false;
  279. }
  280. peekSpaces(scnr);
  281. const ret = scnr.currentPeek() === ":" /* LinkedDelimiter */;
  282. scnr.resetPeek();
  283. return ret;
  284. }
  285. function isLinkedReferStart(scnr, context) {
  286. const { currentType } = context;
  287. if (currentType !== 10 /* LinkedDelimiter */) {
  288. return false;
  289. }
  290. const fn = () => {
  291. const ch = scnr.currentPeek();
  292. if (ch === "{" /* BraceLeft */) {
  293. return isIdentifierStart(scnr.peek());
  294. }
  295. else if (ch === "@" /* LinkedAlias */ ||
  296. ch === "%" /* Modulo */ ||
  297. ch === "|" /* Pipe */ ||
  298. ch === ":" /* LinkedDelimiter */ ||
  299. ch === "." /* LinkedDot */ ||
  300. ch === CHAR_SP ||
  301. !ch) {
  302. return false;
  303. }
  304. else if (ch === CHAR_LF) {
  305. scnr.peek();
  306. return fn();
  307. }
  308. else {
  309. // other characters
  310. return isIdentifierStart(ch);
  311. }
  312. };
  313. const ret = fn();
  314. scnr.resetPeek();
  315. return ret;
  316. }
  317. function isPluralStart(scnr) {
  318. peekSpaces(scnr);
  319. const ret = scnr.currentPeek() === "|" /* Pipe */;
  320. scnr.resetPeek();
  321. return ret;
  322. }
  323. function isTextStart(scnr, reset = true) {
  324. const fn = (hasSpace = false, prev = '', detectModulo = false) => {
  325. const ch = scnr.currentPeek();
  326. if (ch === "{" /* BraceLeft */) {
  327. return prev === "%" /* Modulo */ ? false : hasSpace;
  328. }
  329. else if (ch === "@" /* LinkedAlias */ || !ch) {
  330. return prev === "%" /* Modulo */ ? true : hasSpace;
  331. }
  332. else if (ch === "%" /* Modulo */) {
  333. scnr.peek();
  334. return fn(hasSpace, "%" /* Modulo */, true);
  335. }
  336. else if (ch === "|" /* Pipe */) {
  337. return prev === "%" /* Modulo */ || detectModulo
  338. ? true
  339. : !(prev === CHAR_SP || prev === CHAR_LF);
  340. }
  341. else if (ch === CHAR_SP) {
  342. scnr.peek();
  343. return fn(true, CHAR_SP, detectModulo);
  344. }
  345. else if (ch === CHAR_LF) {
  346. scnr.peek();
  347. return fn(true, CHAR_LF, detectModulo);
  348. }
  349. else {
  350. return true;
  351. }
  352. };
  353. const ret = fn();
  354. reset && scnr.resetPeek();
  355. return ret;
  356. }
  357. function takeChar(scnr, fn) {
  358. const ch = scnr.currentChar();
  359. if (ch === EOF) {
  360. return EOF;
  361. }
  362. if (fn(ch)) {
  363. scnr.next();
  364. return ch;
  365. }
  366. return null;
  367. }
  368. function takeIdentifierChar(scnr) {
  369. const closure = (ch) => {
  370. const cc = ch.charCodeAt(0);
  371. return ((cc >= 97 && cc <= 122) || // a-z
  372. (cc >= 65 && cc <= 90) || // A-Z
  373. (cc >= 48 && cc <= 57) || // 0-9
  374. cc === 95 || // _
  375. cc === 36 // $
  376. );
  377. };
  378. return takeChar(scnr, closure);
  379. }
  380. function takeDigit(scnr) {
  381. const closure = (ch) => {
  382. const cc = ch.charCodeAt(0);
  383. return cc >= 48 && cc <= 57; // 0-9
  384. };
  385. return takeChar(scnr, closure);
  386. }
  387. function takeHexDigit(scnr) {
  388. const closure = (ch) => {
  389. const cc = ch.charCodeAt(0);
  390. return ((cc >= 48 && cc <= 57) || // 0-9
  391. (cc >= 65 && cc <= 70) || // A-F
  392. (cc >= 97 && cc <= 102)); // a-f
  393. };
  394. return takeChar(scnr, closure);
  395. }
  396. function getDigits(scnr) {
  397. let ch = '';
  398. let num = '';
  399. while ((ch = takeDigit(scnr))) {
  400. num += ch;
  401. }
  402. return num;
  403. }
  404. function readText(scnr) {
  405. let buf = '';
  406. while (true) {
  407. const ch = scnr.currentChar();
  408. if (ch === "{" /* BraceLeft */ ||
  409. ch === "}" /* BraceRight */ ||
  410. ch === "@" /* LinkedAlias */ ||
  411. ch === "|" /* Pipe */ ||
  412. !ch) {
  413. break;
  414. }
  415. else if (ch === "%" /* Modulo */) {
  416. if (isTextStart(scnr)) {
  417. buf += ch;
  418. scnr.next();
  419. }
  420. else {
  421. break;
  422. }
  423. }
  424. else if (ch === CHAR_SP || ch === CHAR_LF) {
  425. if (isTextStart(scnr)) {
  426. buf += ch;
  427. scnr.next();
  428. }
  429. else if (isPluralStart(scnr)) {
  430. break;
  431. }
  432. else {
  433. buf += ch;
  434. scnr.next();
  435. }
  436. }
  437. else {
  438. buf += ch;
  439. scnr.next();
  440. }
  441. }
  442. return buf;
  443. }
  444. function readNamedIdentifier(scnr) {
  445. skipSpaces(scnr);
  446. let ch = '';
  447. let name = '';
  448. while ((ch = takeIdentifierChar(scnr))) {
  449. name += ch;
  450. }
  451. if (scnr.currentChar() === EOF) {
  452. emitError(6 /* UNTERMINATED_CLOSING_BRACE */, currentPosition(), 0);
  453. }
  454. return name;
  455. }
  456. function readListIdentifier(scnr) {
  457. skipSpaces(scnr);
  458. let value = '';
  459. if (scnr.currentChar() === '-') {
  460. scnr.next();
  461. value += `-${getDigits(scnr)}`;
  462. }
  463. else {
  464. value += getDigits(scnr);
  465. }
  466. if (scnr.currentChar() === EOF) {
  467. emitError(6 /* UNTERMINATED_CLOSING_BRACE */, currentPosition(), 0);
  468. }
  469. return value;
  470. }
  471. function readLiteral(scnr) {
  472. skipSpaces(scnr);
  473. eat(scnr, `\'`);
  474. let ch = '';
  475. let literal = '';
  476. const fn = (x) => x !== LITERAL_DELIMITER && x !== CHAR_LF;
  477. while ((ch = takeChar(scnr, fn))) {
  478. if (ch === '\\') {
  479. literal += readEscapeSequence(scnr);
  480. }
  481. else {
  482. literal += ch;
  483. }
  484. }
  485. const current = scnr.currentChar();
  486. if (current === CHAR_LF || current === EOF) {
  487. emitError(2 /* UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER */, currentPosition(), 0);
  488. // TODO: Is it correct really?
  489. if (current === CHAR_LF) {
  490. scnr.next();
  491. eat(scnr, `\'`);
  492. }
  493. return literal;
  494. }
  495. eat(scnr, `\'`);
  496. return literal;
  497. }
  498. function readEscapeSequence(scnr) {
  499. const ch = scnr.currentChar();
  500. switch (ch) {
  501. case '\\':
  502. case `\'`:
  503. scnr.next();
  504. return `\\${ch}`;
  505. case 'u':
  506. return readUnicodeEscapeSequence(scnr, ch, 4);
  507. case 'U':
  508. return readUnicodeEscapeSequence(scnr, ch, 6);
  509. default:
  510. emitError(3 /* UNKNOWN_ESCAPE_SEQUENCE */, currentPosition(), 0, ch);
  511. return '';
  512. }
  513. }
  514. function readUnicodeEscapeSequence(scnr, unicode, digits) {
  515. eat(scnr, unicode);
  516. let sequence = '';
  517. for (let i = 0; i < digits; i++) {
  518. const ch = takeHexDigit(scnr);
  519. if (!ch) {
  520. emitError(4 /* INVALID_UNICODE_ESCAPE_SEQUENCE */, currentPosition(), 0, `\\${unicode}${sequence}${scnr.currentChar()}`);
  521. break;
  522. }
  523. sequence += ch;
  524. }
  525. return `\\${unicode}${sequence}`;
  526. }
  527. function readInvalidIdentifier(scnr) {
  528. skipSpaces(scnr);
  529. let ch = '';
  530. let identifiers = '';
  531. const closure = (ch) => ch !== "{" /* BraceLeft */ &&
  532. ch !== "}" /* BraceRight */ &&
  533. ch !== CHAR_SP &&
  534. ch !== CHAR_LF;
  535. while ((ch = takeChar(scnr, closure))) {
  536. identifiers += ch;
  537. }
  538. return identifiers;
  539. }
  540. function readLinkedModifier(scnr) {
  541. let ch = '';
  542. let name = '';
  543. while ((ch = takeIdentifierChar(scnr))) {
  544. name += ch;
  545. }
  546. return name;
  547. }
  548. function readLinkedRefer(scnr) {
  549. const fn = (detect = false, buf) => {
  550. const ch = scnr.currentChar();
  551. if (ch === "{" /* BraceLeft */ ||
  552. ch === "%" /* Modulo */ ||
  553. ch === "@" /* LinkedAlias */ ||
  554. ch === "|" /* Pipe */ ||
  555. !ch) {
  556. return buf;
  557. }
  558. else if (ch === CHAR_SP) {
  559. return buf;
  560. }
  561. else if (ch === CHAR_LF) {
  562. buf += ch;
  563. scnr.next();
  564. return fn(detect, buf);
  565. }
  566. else {
  567. buf += ch;
  568. scnr.next();
  569. return fn(true, buf);
  570. }
  571. };
  572. return fn(false, '');
  573. }
  574. function readPlural(scnr) {
  575. skipSpaces(scnr);
  576. const plural = eat(scnr, "|" /* Pipe */);
  577. skipSpaces(scnr);
  578. return plural;
  579. }
  580. // TODO: We need refactoring of token parsing ...
  581. function readTokenInPlaceholder(scnr, context) {
  582. let token = null;
  583. const ch = scnr.currentChar();
  584. switch (ch) {
  585. case "{" /* BraceLeft */:
  586. if (context.braceNest >= 1) {
  587. emitError(8 /* NOT_ALLOW_NEST_PLACEHOLDER */, currentPosition(), 0);
  588. }
  589. scnr.next();
  590. token = getToken(context, 2 /* BraceLeft */, "{" /* BraceLeft */);
  591. skipSpaces(scnr);
  592. context.braceNest++;
  593. return token;
  594. case "}" /* BraceRight */:
  595. if (context.braceNest > 0 &&
  596. context.currentType === 2 /* BraceLeft */) {
  597. emitError(7 /* EMPTY_PLACEHOLDER */, currentPosition(), 0);
  598. }
  599. scnr.next();
  600. token = getToken(context, 3 /* BraceRight */, "}" /* BraceRight */);
  601. context.braceNest--;
  602. context.braceNest > 0 && skipSpaces(scnr);
  603. if (context.inLinked && context.braceNest === 0) {
  604. context.inLinked = false;
  605. }
  606. return token;
  607. case "@" /* LinkedAlias */:
  608. if (context.braceNest > 0) {
  609. emitError(6 /* UNTERMINATED_CLOSING_BRACE */, currentPosition(), 0);
  610. }
  611. token = readTokenInLinked(scnr, context) || getEndToken(context);
  612. context.braceNest = 0;
  613. return token;
  614. default:
  615. let validNamedIdentifier = true;
  616. let validListIdentifier = true;
  617. let validLiteral = true;
  618. if (isPluralStart(scnr)) {
  619. if (context.braceNest > 0) {
  620. emitError(6 /* UNTERMINATED_CLOSING_BRACE */, currentPosition(), 0);
  621. }
  622. token = getToken(context, 1 /* Pipe */, readPlural(scnr));
  623. // reset
  624. context.braceNest = 0;
  625. context.inLinked = false;
  626. return token;
  627. }
  628. if (context.braceNest > 0 &&
  629. (context.currentType === 5 /* Named */ ||
  630. context.currentType === 6 /* List */ ||
  631. context.currentType === 7 /* Literal */)) {
  632. emitError(6 /* UNTERMINATED_CLOSING_BRACE */, currentPosition(), 0);
  633. context.braceNest = 0;
  634. return readToken(scnr, context);
  635. }
  636. if ((validNamedIdentifier = isNamedIdentifierStart(scnr, context))) {
  637. token = getToken(context, 5 /* Named */, readNamedIdentifier(scnr));
  638. skipSpaces(scnr);
  639. return token;
  640. }
  641. if ((validListIdentifier = isListIdentifierStart(scnr, context))) {
  642. token = getToken(context, 6 /* List */, readListIdentifier(scnr));
  643. skipSpaces(scnr);
  644. return token;
  645. }
  646. if ((validLiteral = isLiteralStart(scnr, context))) {
  647. token = getToken(context, 7 /* Literal */, readLiteral(scnr));
  648. skipSpaces(scnr);
  649. return token;
  650. }
  651. if (!validNamedIdentifier && !validListIdentifier && !validLiteral) {
  652. // TODO: we should be re-designed invalid cases, when we will extend message syntax near the future ...
  653. token = getToken(context, 13 /* InvalidPlace */, readInvalidIdentifier(scnr));
  654. emitError(1 /* INVALID_TOKEN_IN_PLACEHOLDER */, currentPosition(), 0, token.value);
  655. skipSpaces(scnr);
  656. return token;
  657. }
  658. break;
  659. }
  660. return token;
  661. }
  662. // TODO: We need refactoring of token parsing ...
  663. function readTokenInLinked(scnr, context) {
  664. const { currentType } = context;
  665. let token = null;
  666. const ch = scnr.currentChar();
  667. if ((currentType === 8 /* LinkedAlias */ ||
  668. currentType === 9 /* LinkedDot */ ||
  669. currentType === 12 /* LinkedModifier */ ||
  670. currentType === 10 /* LinkedDelimiter */) &&
  671. (ch === CHAR_LF || ch === CHAR_SP)) {
  672. emitError(9 /* INVALID_LINKED_FORMAT */, currentPosition(), 0);
  673. }
  674. switch (ch) {
  675. case "@" /* LinkedAlias */:
  676. scnr.next();
  677. token = getToken(context, 8 /* LinkedAlias */, "@" /* LinkedAlias */);
  678. context.inLinked = true;
  679. return token;
  680. case "." /* LinkedDot */:
  681. skipSpaces(scnr);
  682. scnr.next();
  683. return getToken(context, 9 /* LinkedDot */, "." /* LinkedDot */);
  684. case ":" /* LinkedDelimiter */:
  685. skipSpaces(scnr);
  686. scnr.next();
  687. return getToken(context, 10 /* LinkedDelimiter */, ":" /* LinkedDelimiter */);
  688. default:
  689. if (isPluralStart(scnr)) {
  690. token = getToken(context, 1 /* Pipe */, readPlural(scnr));
  691. // reset
  692. context.braceNest = 0;
  693. context.inLinked = false;
  694. return token;
  695. }
  696. if (isLinkedDotStart(scnr, context) ||
  697. isLinkedDelimiterStart(scnr, context)) {
  698. skipSpaces(scnr);
  699. return readTokenInLinked(scnr, context);
  700. }
  701. if (isLinkedModifierStart(scnr, context)) {
  702. skipSpaces(scnr);
  703. return getToken(context, 12 /* LinkedModifier */, readLinkedModifier(scnr));
  704. }
  705. if (isLinkedReferStart(scnr, context)) {
  706. skipSpaces(scnr);
  707. if (ch === "{" /* BraceLeft */) {
  708. // scan the placeholder
  709. return readTokenInPlaceholder(scnr, context) || token;
  710. }
  711. else {
  712. return getToken(context, 11 /* LinkedKey */, readLinkedRefer(scnr));
  713. }
  714. }
  715. if (currentType === 8 /* LinkedAlias */) {
  716. emitError(9 /* INVALID_LINKED_FORMAT */, currentPosition(), 0);
  717. }
  718. context.braceNest = 0;
  719. context.inLinked = false;
  720. return readToken(scnr, context);
  721. }
  722. }
  723. // TODO: We need refactoring of token parsing ...
  724. function readToken(scnr, context) {
  725. let token = { type: 14 /* EOF */ };
  726. if (context.braceNest > 0) {
  727. return readTokenInPlaceholder(scnr, context) || getEndToken(context);
  728. }
  729. if (context.inLinked) {
  730. return readTokenInLinked(scnr, context) || getEndToken(context);
  731. }
  732. const ch = scnr.currentChar();
  733. switch (ch) {
  734. case "{" /* BraceLeft */:
  735. return readTokenInPlaceholder(scnr, context) || getEndToken(context);
  736. case "}" /* BraceRight */:
  737. emitError(5 /* UNBALANCED_CLOSING_BRACE */, currentPosition(), 0);
  738. scnr.next();
  739. return getToken(context, 3 /* BraceRight */, "}" /* BraceRight */);
  740. case "@" /* LinkedAlias */:
  741. return readTokenInLinked(scnr, context) || getEndToken(context);
  742. default:
  743. if (isPluralStart(scnr)) {
  744. token = getToken(context, 1 /* Pipe */, readPlural(scnr));
  745. // reset
  746. context.braceNest = 0;
  747. context.inLinked = false;
  748. return token;
  749. }
  750. if (isTextStart(scnr)) {
  751. return getToken(context, 0 /* Text */, readText(scnr));
  752. }
  753. if (ch === "%" /* Modulo */) {
  754. scnr.next();
  755. return getToken(context, 4 /* Modulo */, "%" /* Modulo */);
  756. }
  757. break;
  758. }
  759. return token;
  760. }
  761. function nextToken() {
  762. const { currentType, offset, startLoc, endLoc } = _context;
  763. _context.lastType = currentType;
  764. _context.lastOffset = offset;
  765. _context.lastStartLoc = startLoc;
  766. _context.lastEndLoc = endLoc;
  767. _context.offset = currentOffset();
  768. _context.startLoc = currentPosition();
  769. if (_scnr.currentChar() === EOF) {
  770. return getToken(_context, 14 /* EOF */);
  771. }
  772. return readToken(_scnr, _context);
  773. }
  774. return {
  775. nextToken,
  776. currentOffset,
  777. currentPosition,
  778. context
  779. };
  780. }
  781. const ERROR_DOMAIN = 'parser';
  782. // Backslash backslash, backslash quote, uHHHH, UHHHHHH.
  783. const KNOWN_ESCAPES = /(?:\\\\|\\'|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;
  784. function fromEscapeSequence(match, codePoint4, codePoint6) {
  785. switch (match) {
  786. case `\\\\`:
  787. return `\\`;
  788. case `\\\'`:
  789. return `\'`;
  790. default: {
  791. const codePoint = parseInt(codePoint4 || codePoint6, 16);
  792. if (codePoint <= 0xd7ff || codePoint >= 0xe000) {
  793. return String.fromCodePoint(codePoint);
  794. }
  795. // invalid ...
  796. // Replace them with U+FFFD REPLACEMENT CHARACTER.
  797. return '�';
  798. }
  799. }
  800. }
  801. function createParser(options = {}) {
  802. const location = options.location !== false;
  803. const { onError } = options;
  804. function emitError(tokenzer, code, start, offset, ...args) {
  805. const end = tokenzer.currentPosition();
  806. end.offset += offset;
  807. end.column += offset;
  808. if (onError) {
  809. const loc = createLocation(start, end);
  810. const err = createCompileError(code, loc, {
  811. domain: ERROR_DOMAIN,
  812. args
  813. });
  814. onError(err);
  815. }
  816. }
  817. function startNode(type, offset, loc) {
  818. const node = {
  819. type,
  820. start: offset,
  821. end: offset
  822. };
  823. if (location) {
  824. node.loc = { start: loc, end: loc };
  825. }
  826. return node;
  827. }
  828. function endNode(node, offset, pos, type) {
  829. node.end = offset;
  830. if (type) {
  831. node.type = type;
  832. }
  833. if (location && node.loc) {
  834. node.loc.end = pos;
  835. }
  836. }
  837. function parseText(tokenizer, value) {
  838. const context = tokenizer.context();
  839. const node = startNode(3 /* Text */, context.offset, context.startLoc);
  840. node.value = value;
  841. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  842. return node;
  843. }
  844. function parseList(tokenizer, index) {
  845. const context = tokenizer.context();
  846. const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
  847. const node = startNode(5 /* List */, offset, loc);
  848. node.index = parseInt(index, 10);
  849. tokenizer.nextToken(); // skip brach right
  850. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  851. return node;
  852. }
  853. function parseNamed(tokenizer, key) {
  854. const context = tokenizer.context();
  855. const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
  856. const node = startNode(4 /* Named */, offset, loc);
  857. node.key = key;
  858. tokenizer.nextToken(); // skip brach right
  859. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  860. return node;
  861. }
  862. function parseLiteral(tokenizer, value) {
  863. const context = tokenizer.context();
  864. const { lastOffset: offset, lastStartLoc: loc } = context; // get brace left loc
  865. const node = startNode(9 /* Literal */, offset, loc);
  866. node.value = value.replace(KNOWN_ESCAPES, fromEscapeSequence);
  867. tokenizer.nextToken(); // skip brach right
  868. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  869. return node;
  870. }
  871. function parseLinkedModifier(tokenizer) {
  872. const token = tokenizer.nextToken();
  873. const context = tokenizer.context();
  874. const { lastOffset: offset, lastStartLoc: loc } = context; // get linked dot loc
  875. const node = startNode(8 /* LinkedModifier */, offset, loc);
  876. if (token.type !== 12 /* LinkedModifier */) {
  877. // empty modifier
  878. emitError(tokenizer, 11 /* UNEXPECTED_EMPTY_LINKED_MODIFIER */, context.lastStartLoc, 0);
  879. node.value = '';
  880. endNode(node, offset, loc);
  881. return {
  882. nextConsumeToken: token,
  883. node
  884. };
  885. }
  886. // check token
  887. if (token.value == null) {
  888. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, getTokenCaption(token));
  889. }
  890. node.value = token.value || '';
  891. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  892. return {
  893. node
  894. };
  895. }
  896. function parseLinkedKey(tokenizer, value) {
  897. const context = tokenizer.context();
  898. const node = startNode(7 /* LinkedKey */, context.offset, context.startLoc);
  899. node.value = value;
  900. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  901. return node;
  902. }
  903. function parseLinked(tokenizer) {
  904. const context = tokenizer.context();
  905. const linkedNode = startNode(6 /* Linked */, context.offset, context.startLoc);
  906. let token = tokenizer.nextToken();
  907. if (token.type === 9 /* LinkedDot */) {
  908. const parsed = parseLinkedModifier(tokenizer);
  909. linkedNode.modifier = parsed.node;
  910. token = parsed.nextConsumeToken || tokenizer.nextToken();
  911. }
  912. // asset check token
  913. if (token.type !== 10 /* LinkedDelimiter */) {
  914. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, getTokenCaption(token));
  915. }
  916. token = tokenizer.nextToken();
  917. // skip brace left
  918. if (token.type === 2 /* BraceLeft */) {
  919. token = tokenizer.nextToken();
  920. }
  921. switch (token.type) {
  922. case 11 /* LinkedKey */:
  923. if (token.value == null) {
  924. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, getTokenCaption(token));
  925. }
  926. linkedNode.key = parseLinkedKey(tokenizer, token.value || '');
  927. break;
  928. case 5 /* Named */:
  929. if (token.value == null) {
  930. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, getTokenCaption(token));
  931. }
  932. linkedNode.key = parseNamed(tokenizer, token.value || '');
  933. break;
  934. case 6 /* List */:
  935. if (token.value == null) {
  936. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, getTokenCaption(token));
  937. }
  938. linkedNode.key = parseList(tokenizer, token.value || '');
  939. break;
  940. case 7 /* Literal */:
  941. if (token.value == null) {
  942. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, getTokenCaption(token));
  943. }
  944. linkedNode.key = parseLiteral(tokenizer, token.value || '');
  945. break;
  946. default:
  947. // empty key
  948. emitError(tokenizer, 12 /* UNEXPECTED_EMPTY_LINKED_KEY */, context.lastStartLoc, 0);
  949. const nextContext = tokenizer.context();
  950. const emptyLinkedKeyNode = startNode(7 /* LinkedKey */, nextContext.offset, nextContext.startLoc);
  951. emptyLinkedKeyNode.value = '';
  952. endNode(emptyLinkedKeyNode, nextContext.offset, nextContext.startLoc);
  953. linkedNode.key = emptyLinkedKeyNode;
  954. endNode(linkedNode, nextContext.offset, nextContext.startLoc);
  955. return {
  956. nextConsumeToken: token,
  957. node: linkedNode
  958. };
  959. }
  960. endNode(linkedNode, tokenizer.currentOffset(), tokenizer.currentPosition());
  961. return {
  962. node: linkedNode
  963. };
  964. }
  965. function parseMessage(tokenizer) {
  966. const context = tokenizer.context();
  967. const startOffset = context.currentType === 1 /* Pipe */
  968. ? tokenizer.currentOffset()
  969. : context.offset;
  970. const startLoc = context.currentType === 1 /* Pipe */
  971. ? context.endLoc
  972. : context.startLoc;
  973. const node = startNode(2 /* Message */, startOffset, startLoc);
  974. node.items = [];
  975. let nextToken = null;
  976. do {
  977. const token = nextToken || tokenizer.nextToken();
  978. nextToken = null;
  979. switch (token.type) {
  980. case 0 /* Text */:
  981. if (token.value == null) {
  982. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, getTokenCaption(token));
  983. }
  984. node.items.push(parseText(tokenizer, token.value || ''));
  985. break;
  986. case 6 /* List */:
  987. if (token.value == null) {
  988. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, getTokenCaption(token));
  989. }
  990. node.items.push(parseList(tokenizer, token.value || ''));
  991. break;
  992. case 5 /* Named */:
  993. if (token.value == null) {
  994. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, getTokenCaption(token));
  995. }
  996. node.items.push(parseNamed(tokenizer, token.value || ''));
  997. break;
  998. case 7 /* Literal */:
  999. if (token.value == null) {
  1000. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, getTokenCaption(token));
  1001. }
  1002. node.items.push(parseLiteral(tokenizer, token.value || ''));
  1003. break;
  1004. case 8 /* LinkedAlias */:
  1005. const parsed = parseLinked(tokenizer);
  1006. node.items.push(parsed.node);
  1007. nextToken = parsed.nextConsumeToken || null;
  1008. break;
  1009. }
  1010. } while (context.currentType !== 14 /* EOF */ &&
  1011. context.currentType !== 1 /* Pipe */);
  1012. // adjust message node loc
  1013. const endOffset = context.currentType === 1 /* Pipe */
  1014. ? context.lastOffset
  1015. : tokenizer.currentOffset();
  1016. const endLoc = context.currentType === 1 /* Pipe */
  1017. ? context.lastEndLoc
  1018. : tokenizer.currentPosition();
  1019. endNode(node, endOffset, endLoc);
  1020. return node;
  1021. }
  1022. function parsePlural(tokenizer, offset, loc, msgNode) {
  1023. const context = tokenizer.context();
  1024. let hasEmptyMessage = msgNode.items.length === 0;
  1025. const node = startNode(1 /* Plural */, offset, loc);
  1026. node.cases = [];
  1027. node.cases.push(msgNode);
  1028. do {
  1029. const msg = parseMessage(tokenizer);
  1030. if (!hasEmptyMessage) {
  1031. hasEmptyMessage = msg.items.length === 0;
  1032. }
  1033. node.cases.push(msg);
  1034. } while (context.currentType !== 14 /* EOF */);
  1035. if (hasEmptyMessage) {
  1036. emitError(tokenizer, 10 /* MUST_HAVE_MESSAGES_IN_PLURAL */, loc, 0);
  1037. }
  1038. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  1039. return node;
  1040. }
  1041. function parseResource(tokenizer) {
  1042. const context = tokenizer.context();
  1043. const { offset, startLoc } = context;
  1044. const msgNode = parseMessage(tokenizer);
  1045. if (context.currentType === 14 /* EOF */) {
  1046. return msgNode;
  1047. }
  1048. else {
  1049. return parsePlural(tokenizer, offset, startLoc, msgNode);
  1050. }
  1051. }
  1052. function parse(source) {
  1053. const tokenizer = createTokenizer(source, assign({}, options));
  1054. const context = tokenizer.context();
  1055. const node = startNode(0 /* Resource */, context.offset, context.startLoc);
  1056. if (location && node.loc) {
  1057. node.loc.source = source;
  1058. }
  1059. node.body = parseResource(tokenizer);
  1060. // assert whether achieved to EOF
  1061. if (context.currentType !== 14 /* EOF */) {
  1062. emitError(tokenizer, 13 /* UNEXPECTED_LEXICAL_ANALYSIS */, context.lastStartLoc, 0, source[context.offset] || '');
  1063. }
  1064. endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
  1065. return node;
  1066. }
  1067. return { parse };
  1068. }
  1069. function getTokenCaption(token) {
  1070. if (token.type === 14 /* EOF */) {
  1071. return 'EOF';
  1072. }
  1073. const name = (token.value || '').replace(/\r?\n/gu, '\\n');
  1074. return name.length > 10 ? name.slice(0, 9) + '…' : name;
  1075. }
  1076. function createTransformer(ast, options = {} // eslint-disable-line
  1077. ) {
  1078. const _context = {
  1079. ast,
  1080. helpers: new Set()
  1081. };
  1082. const context = () => _context;
  1083. const helper = (name) => {
  1084. _context.helpers.add(name);
  1085. return name;
  1086. };
  1087. return { context, helper };
  1088. }
  1089. function traverseNodes(nodes, transformer) {
  1090. for (let i = 0; i < nodes.length; i++) {
  1091. traverseNode(nodes[i], transformer);
  1092. }
  1093. }
  1094. function traverseNode(node, transformer) {
  1095. // TODO: if we need pre-hook of transform, should be implemented to here
  1096. switch (node.type) {
  1097. case 1 /* Plural */:
  1098. traverseNodes(node.cases, transformer);
  1099. transformer.helper("plural" /* PLURAL */);
  1100. break;
  1101. case 2 /* Message */:
  1102. traverseNodes(node.items, transformer);
  1103. break;
  1104. case 6 /* Linked */:
  1105. const linked = node;
  1106. traverseNode(linked.key, transformer);
  1107. transformer.helper("linked" /* LINKED */);
  1108. break;
  1109. case 5 /* List */:
  1110. transformer.helper("interpolate" /* INTERPOLATE */);
  1111. transformer.helper("list" /* LIST */);
  1112. break;
  1113. case 4 /* Named */:
  1114. transformer.helper("interpolate" /* INTERPOLATE */);
  1115. transformer.helper("named" /* NAMED */);
  1116. break;
  1117. }
  1118. // TODO: if we need post-hook of transform, should be implemented to here
  1119. }
  1120. // transform AST
  1121. function transform(ast, options = {} // eslint-disable-line
  1122. ) {
  1123. const transformer = createTransformer(ast);
  1124. transformer.helper("normalize" /* NORMALIZE */);
  1125. // traverse
  1126. ast.body && traverseNode(ast.body, transformer);
  1127. // set meta information
  1128. const context = transformer.context();
  1129. ast.helpers = Array.from(context.helpers);
  1130. }
  1131. function createCodeGenerator(ast, options) {
  1132. const { sourceMap, filename, breakLineCode, needIndent: _needIndent } = options;
  1133. const _context = {
  1134. source: ast.loc.source,
  1135. filename,
  1136. code: '',
  1137. column: 1,
  1138. line: 1,
  1139. offset: 0,
  1140. map: undefined,
  1141. breakLineCode,
  1142. needIndent: _needIndent,
  1143. indentLevel: 0
  1144. };
  1145. const context = () => _context;
  1146. function push(code, node) {
  1147. _context.code += code;
  1148. }
  1149. function _newline(n, withBreakLine = true) {
  1150. const _breakLineCode = withBreakLine ? breakLineCode : '';
  1151. push(_needIndent ? _breakLineCode + ` `.repeat(n) : _breakLineCode);
  1152. }
  1153. function indent(withNewLine = true) {
  1154. const level = ++_context.indentLevel;
  1155. withNewLine && _newline(level);
  1156. }
  1157. function deindent(withNewLine = true) {
  1158. const level = --_context.indentLevel;
  1159. withNewLine && _newline(level);
  1160. }
  1161. function newline() {
  1162. _newline(_context.indentLevel);
  1163. }
  1164. const helper = (key) => `_${key}`;
  1165. const needIndent = () => _context.needIndent;
  1166. return {
  1167. context,
  1168. push,
  1169. indent,
  1170. deindent,
  1171. newline,
  1172. helper,
  1173. needIndent
  1174. };
  1175. }
  1176. function generateLinkedNode(generator, node) {
  1177. const { helper } = generator;
  1178. generator.push(`${helper("linked" /* LINKED */)}(`);
  1179. generateNode(generator, node.key);
  1180. if (node.modifier) {
  1181. generator.push(`, `);
  1182. generateNode(generator, node.modifier);
  1183. }
  1184. generator.push(`)`);
  1185. }
  1186. function generateMessageNode(generator, node) {
  1187. const { helper, needIndent } = generator;
  1188. generator.push(`${helper("normalize" /* NORMALIZE */)}([`);
  1189. generator.indent(needIndent());
  1190. const length = node.items.length;
  1191. for (let i = 0; i < length; i++) {
  1192. generateNode(generator, node.items[i]);
  1193. if (i === length - 1) {
  1194. break;
  1195. }
  1196. generator.push(', ');
  1197. }
  1198. generator.deindent(needIndent());
  1199. generator.push('])');
  1200. }
  1201. function generatePluralNode(generator, node) {
  1202. const { helper, needIndent } = generator;
  1203. if (node.cases.length > 1) {
  1204. generator.push(`${helper("plural" /* PLURAL */)}([`);
  1205. generator.indent(needIndent());
  1206. const length = node.cases.length;
  1207. for (let i = 0; i < length; i++) {
  1208. generateNode(generator, node.cases[i]);
  1209. if (i === length - 1) {
  1210. break;
  1211. }
  1212. generator.push(', ');
  1213. }
  1214. generator.deindent(needIndent());
  1215. generator.push(`])`);
  1216. }
  1217. }
  1218. function generateResource(generator, node) {
  1219. if (node.body) {
  1220. generateNode(generator, node.body);
  1221. }
  1222. else {
  1223. generator.push('null');
  1224. }
  1225. }
  1226. function generateNode(generator, node) {
  1227. const { helper } = generator;
  1228. switch (node.type) {
  1229. case 0 /* Resource */:
  1230. generateResource(generator, node);
  1231. break;
  1232. case 1 /* Plural */:
  1233. generatePluralNode(generator, node);
  1234. break;
  1235. case 2 /* Message */:
  1236. generateMessageNode(generator, node);
  1237. break;
  1238. case 6 /* Linked */:
  1239. generateLinkedNode(generator, node);
  1240. break;
  1241. case 8 /* LinkedModifier */:
  1242. generator.push(JSON.stringify(node.value), node);
  1243. break;
  1244. case 7 /* LinkedKey */:
  1245. generator.push(JSON.stringify(node.value), node);
  1246. break;
  1247. case 5 /* List */:
  1248. generator.push(`${helper("interpolate" /* INTERPOLATE */)}(${helper("list" /* LIST */)}(${node.index}))`, node);
  1249. break;
  1250. case 4 /* Named */:
  1251. generator.push(`${helper("interpolate" /* INTERPOLATE */)}(${helper("named" /* NAMED */)}(${JSON.stringify(node.key)}))`, node);
  1252. break;
  1253. case 9 /* Literal */:
  1254. generator.push(JSON.stringify(node.value), node);
  1255. break;
  1256. case 3 /* Text */:
  1257. generator.push(JSON.stringify(node.value), node);
  1258. break;
  1259. default:
  1260. if ((process.env.NODE_ENV !== 'production')) {
  1261. throw new Error(`unhandled codegen node type: ${node.type}`);
  1262. }
  1263. }
  1264. }
  1265. // generate code from AST
  1266. const generate = (ast, options = {} // eslint-disable-line
  1267. ) => {
  1268. const mode = isString(options.mode) ? options.mode : 'normal';
  1269. const filename = isString(options.filename)
  1270. ? options.filename
  1271. : 'message.intl';
  1272. const sourceMap = !!options.sourceMap;
  1273. // prettier-ignore
  1274. const breakLineCode = options.breakLineCode != null
  1275. ? options.breakLineCode
  1276. : mode === 'arrow'
  1277. ? ';'
  1278. : '\n';
  1279. const needIndent = options.needIndent ? options.needIndent : mode !== 'arrow';
  1280. const helpers = ast.helpers || [];
  1281. const generator = createCodeGenerator(ast, {
  1282. mode,
  1283. filename,
  1284. sourceMap,
  1285. breakLineCode,
  1286. needIndent
  1287. });
  1288. generator.push(mode === 'normal' ? `function __msg__ (ctx) {` : `(ctx) => {`);
  1289. generator.indent(needIndent);
  1290. if (helpers.length > 0) {
  1291. generator.push(`const { ${helpers.map(s => `${s}: _${s}`).join(', ')} } = ctx`);
  1292. generator.newline();
  1293. }
  1294. generator.push(`return `);
  1295. generateNode(generator, ast);
  1296. generator.deindent(needIndent);
  1297. generator.push(`}`);
  1298. const { code, map } = generator.context();
  1299. return {
  1300. ast,
  1301. code,
  1302. map: map ? map.toJSON() : undefined // eslint-disable-line @typescript-eslint/no-explicit-any
  1303. };
  1304. };
  1305. function baseCompile(source, options = {}) {
  1306. const assignedOptions = assign({}, options);
  1307. // parse source codes
  1308. const parser = createParser(assignedOptions);
  1309. const ast = parser.parse(source);
  1310. // transform ASTs
  1311. transform(ast, assignedOptions);
  1312. // generate javascript codes
  1313. return generate(ast, assignedOptions);
  1314. }
  1315. export { ERROR_DOMAIN, LocationStub, baseCompile, createCompileError, createLocation, createParser, createPosition, defaultOnError, errorMessages };