@intlify/message-compiler 11.1.2 → 12.0.0-alpha.2

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