message-compiler.esm-browser.js 56 KB

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