@makano/rew 1.2.55 → 1.2.57

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,2316 +0,0 @@
1
- // Generated by CoffeeScript 2.7.0
2
- (function () {
3
- // The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
4
- // matches against the beginning of the source code. When a match is found,
5
- // a token is produced, we consume the match, and start again. Tokens are in the
6
- // form:
7
-
8
- // [tag, value, locationData]
9
-
10
- // where locationData is {first_line, first_column, last_line, last_column, last_line_exclusive, last_column_exclusive}, which is a
11
- // format that can be fed directly into [Jison](https://github.com/zaach/jison). These
12
- // are read by jison in the `parser.lexer` function defined in coffeescript.coffee.
13
- var BOM,
14
- BOOL,
15
- CALLABLE,
16
- CODE,
17
- COFFEE_ALIASES,
18
- COFFEE_ALIAS_MAP,
19
- COFFEE_KEYWORDS,
20
- COMMENT,
21
- COMPARABLE_LEFT_SIDE,
22
- COMPARE,
23
- COMPOUND_ASSIGN,
24
- HERECOMMENT_ILLEGAL,
25
- HEREDOC_DOUBLE,
26
- HEREDOC_INDENT,
27
- HEREDOC_SINGLE,
28
- HEREGEX,
29
- HEREGEX_COMMENT,
30
- HERE_JSTOKEN,
31
- IDENTIFIER,
32
- INDENTABLE_CLOSERS,
33
- INDEXABLE,
34
- INSIDE_JSX,
35
- INVERSES,
36
- JSTOKEN,
37
- JSX_ATTRIBUTE,
38
- JSX_FRAGMENT_IDENTIFIER,
39
- JSX_IDENTIFIER,
40
- JSX_IDENTIFIER_PART,
41
- JSX_INTERPOLATION,
42
- JS_KEYWORDS,
43
- LINE_BREAK,
44
- LINE_CONTINUER,
45
- Lexer,
46
- MATH,
47
- MULTI_DENT,
48
- NOT_REGEX,
49
- NUMBER,
50
- OPERATOR,
51
- POSSIBLY_DIVISION,
52
- REGEX,
53
- REGEX_FLAGS,
54
- REGEX_ILLEGAL,
55
- REGEX_INVALID_ESCAPE,
56
- RELATION,
57
- RESERVED,
58
- Rewriter,
59
- SHIFT,
60
- STRICT_PROSCRIBED,
61
- STRING_DOUBLE,
62
- STRING_INVALID_ESCAPE,
63
- STRING_SINGLE,
64
- STRING_START,
65
- TRAILING_SPACES,
66
- UNARY,
67
- UNARY_MATH,
68
- UNFINISHED,
69
- VALID_FLAGS,
70
- WHITESPACE,
71
- addTokenData,
72
- attachCommentsToNode,
73
- compact,
74
- count,
75
- flatten,
76
- invertLiterate,
77
- isForFrom,
78
- isUnassignable,
79
- key,
80
- locationDataToString,
81
- merge,
82
- parseNumber,
83
- repeat,
84
- replaceUnicodeCodePointEscapes,
85
- starts,
86
- throwSyntaxError,
87
- indexOf = [].indexOf,
88
- slice = [].slice;
89
-
90
- ({ Rewriter, INVERSES, UNFINISHED } = require('./rewriter'));
91
-
92
- // Import the helpers we need.
93
- ({
94
- count,
95
- starts,
96
- compact,
97
- repeat,
98
- invertLiterate,
99
- merge,
100
- attachCommentsToNode,
101
- locationDataToString,
102
- throwSyntaxError,
103
- replaceUnicodeCodePointEscapes,
104
- flatten,
105
- parseNumber,
106
- } = require('./helpers'));
107
-
108
- // The Lexer Class
109
- // ---------------
110
-
111
- // The Lexer class reads a stream of CoffeeScript and divvies it up into tagged
112
- // tokens. Some potential ambiguity in the grammar has been avoided by
113
- // pushing some extra smarts into the Lexer.
114
- exports.Lexer = Lexer = class Lexer {
115
- constructor() {
116
- // Throws an error at either a given offset from the current chunk or at the
117
- // location of a token (`token[2]`).
118
- this.error = this.error.bind(this);
119
- }
120
-
121
- // **tokenize** is the Lexer's main method. Scan by attempting to match tokens
122
- // one at a time, using a regular expression anchored at the start of the
123
- // remaining code, or a custom recursive token-matching method
124
- // (for interpolations). When the next token has been recorded, we move forward
125
- // within the code past the token, and begin again.
126
-
127
- // Each tokenizing method is responsible for returning the number of characters
128
- // it has consumed.
129
-
130
- // Before returning the token stream, run it through the [Rewriter](rewriter.html).
131
- tokenize(code, opts = {}) {
132
- var consumed, end, i, ref;
133
- this.literate = opts.literate; // Are we lexing literate CoffeeScript?
134
- this.indent = 0; // The current indentation level.
135
- this.baseIndent = 0; // The overall minimum indentation level.
136
- this.continuationLineAdditionalIndent = 0; // The over-indentation at the current level.
137
- this.outdebt = 0; // The under-outdentation at the current level.
138
- this.indents = []; // The stack of all current indentation levels.
139
- this.indentLiteral = ''; // The indentation.
140
- this.ends = []; // The stack for pairing up tokens.
141
- this.tokens = []; // Stream of parsed tokens in the form `['TYPE', value, location data]`.
142
- this.seenFor = false; // Used to recognize `FORIN`, `FOROF` and `FORFROM` tokens.
143
- this.seenImport = false; // Used to recognize `IMPORT FROM? AS?` tokens.
144
- this.seenExport = false; // Used to recognize `EXPORT FROM? AS?` tokens.
145
- this.importSpecifierList = false; // Used to identify when in an `IMPORT {...} FROM? ...`.
146
- this.exportSpecifierList = false; // Used to identify when in an `EXPORT {...} FROM? ...`.
147
- this.jsxDepth = 0; // Used to optimize JSX checks, how deep in JSX we are.
148
- this.jsxObjAttribute = {}; // Used to detect if JSX attributes is wrapped in {} (<div {props...} />).
149
- this.chunkLine = opts.line || 0; // The start line for the current @chunk.
150
- this.chunkColumn = opts.column || 0; // The start column of the current @chunk.
151
- this.chunkOffset = opts.offset || 0; // The start offset for the current @chunk.
152
- this.locationDataCompensations = opts.locationDataCompensations || {};
153
- code = this.clean(code); // The stripped, cleaned original source code.
154
-
155
- // At every position, run through this list of attempted matches,
156
- // short-circuiting if any of them succeed. Their order determines precedence:
157
- // `@literalToken` is the fallback catch-all.
158
- i = 0;
159
- while ((this.chunk = code.slice(i))) {
160
- consumed =
161
- this.identifierToken() ||
162
- this.commentToken() ||
163
- this.whitespaceToken() ||
164
- this.lineToken() ||
165
- this.stringToken() ||
166
- this.numberToken() ||
167
- this.jsxToken() ||
168
- this.regexToken() ||
169
- this.jsToken() ||
170
- this.literalToken();
171
- // Update position.
172
- [this.chunkLine, this.chunkColumn, this.chunkOffset] = this.getLineAndColumnFromChunk(consumed);
173
- i += consumed;
174
- if (opts.untilBalanced && this.ends.length === 0) {
175
- return {
176
- tokens: this.tokens,
177
- index: i,
178
- };
179
- }
180
- }
181
- this.closeIndentation();
182
- if ((end = this.ends.pop())) {
183
- this.error(`missing ${end.tag}`, ((ref = end.origin) != null ? ref : end)[2]);
184
- }
185
- if (opts.rewrite === false) {
186
- return this.tokens;
187
- }
188
- return new Rewriter().rewrite(this.tokens);
189
- }
190
-
191
- // Preprocess the code to remove leading and trailing whitespace, carriage
192
- // returns, etc. If we’re lexing literate CoffeeScript, strip external Markdown
193
- // by removing all lines that aren’t indented by at least four spaces or a tab.
194
- clean(code) {
195
- var base, thusFar;
196
- thusFar = 0;
197
- if (code.charCodeAt(0) === BOM) {
198
- code = code.slice(1);
199
- this.locationDataCompensations[0] = 1;
200
- thusFar += 1;
201
- }
202
- if (WHITESPACE.test(code)) {
203
- code = `\n${code}`;
204
- this.chunkLine--;
205
- if ((base = this.locationDataCompensations)[0] == null) {
206
- base[0] = 0;
207
- }
208
- this.locationDataCompensations[0] -= 1;
209
- }
210
- code = code
211
- .replace(/\r/g, (match, offset) => {
212
- this.locationDataCompensations[thusFar + offset] = 1;
213
- return '';
214
- })
215
- .replace(TRAILING_SPACES, '');
216
- if (this.literate) {
217
- code = invertLiterate(code);
218
- }
219
- return code;
220
- }
221
-
222
- // Tokenizers
223
- // ----------
224
-
225
- // Matches identifying literals: variables, keywords, method names, etc.
226
- // Check to ensure that JavaScript reserved words aren’t being used as
227
- // identifiers. Because CoffeeScript reserves a handful of keywords that are
228
- // allowed in JavaScript, we’re careful not to tag them as keywords when
229
- // referenced as property names here, so you can still do `jQuery.is()` even
230
- // though `is` means `===` otherwise.
231
- identifierToken() {
232
- var alias,
233
- colon,
234
- colonOffset,
235
- colonToken,
236
- id,
237
- idLength,
238
- inJSXTag,
239
- input,
240
- match,
241
- poppedToken,
242
- prev,
243
- prevprev,
244
- ref,
245
- ref1,
246
- ref10,
247
- ref11,
248
- ref12,
249
- ref2,
250
- ref3,
251
- ref4,
252
- ref5,
253
- ref6,
254
- ref7,
255
- ref8,
256
- ref9,
257
- regExSuper,
258
- regex,
259
- sup,
260
- tag,
261
- tagToken,
262
- tokenData;
263
- inJSXTag = this.atJSXTag();
264
- regex = inJSXTag ? JSX_ATTRIBUTE : IDENTIFIER;
265
- if (!(match = regex.exec(this.chunk))) {
266
- return 0;
267
- }
268
- [input, id, colon] = match;
269
- // Preserve length of id for location data
270
- idLength = id.length;
271
- poppedToken = void 0;
272
- if (id === 'own' && this.tag() === 'FOR') {
273
- this.token('OWN', id);
274
- return id.length;
275
- }
276
- if (id === 'from' && this.tag() === 'YIELD') {
277
- this.token('FROM', id);
278
- return id.length;
279
- }
280
- if (id === 'as' && this.seenImport) {
281
- if (this.value() === '*') {
282
- this.tokens[this.tokens.length - 1][0] = 'IMPORT_ALL';
283
- } else if (((ref = this.value(true)), indexOf.call(COFFEE_KEYWORDS, ref) >= 0)) {
284
- prev = this.prev();
285
- [prev[0], prev[1]] = ['IDENTIFIER', this.value(true)];
286
- }
287
- if ((ref1 = this.tag()) === 'DEFAULT' || ref1 === 'IMPORT_ALL' || ref1 === 'IDENTIFIER') {
288
- this.token('AS', id);
289
- return id.length;
290
- }
291
- }
292
- if (id === 'as' && this.seenExport) {
293
- if ((ref2 = this.tag()) === 'IDENTIFIER' || ref2 === 'DEFAULT') {
294
- this.token('AS', id);
295
- return id.length;
296
- }
297
- if (((ref3 = this.value(true)), indexOf.call(COFFEE_KEYWORDS, ref3) >= 0)) {
298
- prev = this.prev();
299
- [prev[0], prev[1]] = ['IDENTIFIER', this.value(true)];
300
- this.token('AS', id);
301
- return id.length;
302
- }
303
- }
304
- if (id === 'default' && this.seenExport && ((ref4 = this.tag()) === 'EXPORT' || ref4 === 'AS')) {
305
- this.token('DEFAULT', id);
306
- return id.length;
307
- }
308
- if (id === 'assert' && (this.seenImport || this.seenExport) && this.tag() === 'STRING') {
309
- this.token('ASSERT', id);
310
- return id.length;
311
- }
312
- if (id === 'do' && (regExSuper = /^(\s*super)(?!\(\))/.exec(this.chunk.slice(3)))) {
313
- this.token('SUPER', 'super');
314
- this.token('CALL_START', '(');
315
- this.token('CALL_END', ')');
316
- [input, sup] = regExSuper;
317
- return sup.length + 3;
318
- }
319
- prev = this.prev();
320
- tag =
321
- colon ||
322
- (prev != null &&
323
- ((ref5 = prev[0]) === '.' || ref5 === '?.' || ref5 === '::' || ref5 === '?::' || (!prev.spaced && prev[0] === '@')))
324
- ? 'PROPERTY'
325
- : 'IDENTIFIER';
326
- tokenData = {};
327
- if (
328
- tag === 'IDENTIFIER' &&
329
- (indexOf.call(JS_KEYWORDS, id) >= 0 || indexOf.call(COFFEE_KEYWORDS, id) >= 0) &&
330
- !(this.exportSpecifierList && indexOf.call(COFFEE_KEYWORDS, id) >= 0)
331
- ) {
332
- tag = id.toUpperCase();
333
- if (tag === 'WHEN' && ((ref6 = this.tag()), indexOf.call(LINE_BREAK, ref6) >= 0)) {
334
- tag = 'LEADING_WHEN';
335
- } else if (tag === 'FOR') {
336
- this.seenFor = {
337
- endsLength: this.ends.length,
338
- };
339
- } else if (tag === 'UNLESS') {
340
- tag = 'IF';
341
- } else if (tag === 'IMPORT') {
342
- this.seenImport = true;
343
- } else if (tag === 'EXPORT') {
344
- this.seenExport = true;
345
- } else if (indexOf.call(UNARY, tag) >= 0) {
346
- tag = 'UNARY';
347
- } else if (indexOf.call(RELATION, tag) >= 0) {
348
- if (tag !== 'INSTANCEOF' && this.seenFor) {
349
- tag = 'FOR' + tag;
350
- this.seenFor = false;
351
- } else {
352
- tag = 'RELATION';
353
- if (this.value() === '!') {
354
- poppedToken = this.tokens.pop();
355
- tokenData.invert = (ref7 = (ref8 = poppedToken.data) != null ? ref8.original : void 0) != null ? ref7 : poppedToken[1];
356
- }
357
- }
358
- }
359
- } else if (tag === 'IDENTIFIER' && this.seenFor && id === 'from' && isForFrom(prev)) {
360
- tag = 'FORFROM';
361
- this.seenFor = false;
362
- // Throw an error on attempts to use `get` or `set` as keywords, or
363
- // what CoffeeScript would normally interpret as calls to functions named
364
- // `get` or `set`, i.e. `get({foo: function () {}})`.
365
- } else if (tag === 'PROPERTY' && prev) {
366
- if (
367
- prev.spaced &&
368
- ((ref9 = prev[0]), indexOf.call(CALLABLE, ref9) >= 0) &&
369
- /^[gs]et$/.test(prev[1]) &&
370
- this.tokens.length > 1 &&
371
- (ref10 = this.tokens[this.tokens.length - 2][0]) !== '.' &&
372
- ref10 !== '?.' &&
373
- ref10 !== '@'
374
- ) {
375
- this.error(`'${prev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prev[2]);
376
- } else if (
377
- prev[0] === '.' &&
378
- this.tokens.length > 1 &&
379
- (prevprev = this.tokens[this.tokens.length - 2])[0] === 'UNARY' &&
380
- prevprev[1] === 'new'
381
- ) {
382
- prevprev[0] = 'NEW_TARGET';
383
- } else if (
384
- prev[0] === '.' &&
385
- this.tokens.length > 1 &&
386
- (prevprev = this.tokens[this.tokens.length - 2])[0] === 'IMPORT' &&
387
- prevprev[1] === 'import'
388
- ) {
389
- this.seenImport = false;
390
- prevprev[0] = 'IMPORT_META';
391
- } else if (this.tokens.length > 2) {
392
- prevprev = this.tokens[this.tokens.length - 2];
393
- if (
394
- ((ref11 = prev[0]) === '@' || ref11 === 'THIS') &&
395
- prevprev &&
396
- prevprev.spaced &&
397
- /^[gs]et$/.test(prevprev[1]) &&
398
- (ref12 = this.tokens[this.tokens.length - 3][0]) !== '.' &&
399
- ref12 !== '?.' &&
400
- ref12 !== '@'
401
- ) {
402
- this.error(`'${prevprev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prevprev[2]);
403
- }
404
- }
405
- }
406
- if (tag === 'IDENTIFIER' && indexOf.call(RESERVED, id) >= 0 && !inJSXTag) {
407
- this.error(`reserved word '${id}'`, {
408
- length: id.length,
409
- });
410
- }
411
- if (!(tag === 'PROPERTY' || this.exportSpecifierList || this.importSpecifierList)) {
412
- if (indexOf.call(COFFEE_ALIASES, id) >= 0) {
413
- alias = id;
414
- id = COFFEE_ALIAS_MAP[id];
415
- tokenData.original = alias;
416
- }
417
- tag = (function () {
418
- switch (id) {
419
- case '!':
420
- return 'UNARY';
421
- case '==':
422
- case '!=':
423
- return 'COMPARE';
424
- case 'true':
425
- case 'false':
426
- return 'BOOL';
427
- case 'break':
428
- case 'continue':
429
- case 'debugger':
430
- return 'STATEMENT';
431
- case '&&':
432
- case '||':
433
- return id;
434
- default:
435
- return tag;
436
- }
437
- })();
438
- }
439
- tagToken = this.token(tag, id, {
440
- length: idLength,
441
- data: tokenData,
442
- });
443
- if (alias) {
444
- tagToken.origin = [tag, alias, tagToken[2]];
445
- }
446
- if (poppedToken) {
447
- [tagToken[2].first_line, tagToken[2].first_column, tagToken[2].range[0]] = [
448
- poppedToken[2].first_line,
449
- poppedToken[2].first_column,
450
- poppedToken[2].range[0],
451
- ];
452
- }
453
- if (colon) {
454
- colonOffset = input.lastIndexOf(inJSXTag ? '=' : ':');
455
- colonToken = this.token(':', ':', {
456
- offset: colonOffset,
457
- });
458
- if (inJSXTag) {
459
- // used by rewriter
460
- colonToken.jsxColon = true;
461
- }
462
- }
463
- if (inJSXTag && tag === 'IDENTIFIER' && prev[0] !== ':') {
464
- this.token(',', ',', {
465
- length: 0,
466
- origin: tagToken,
467
- generated: true,
468
- });
469
- }
470
- return input.length;
471
- }
472
-
473
- // Matches numbers, including decimals, hex, and exponential notation.
474
- // Be careful not to interfere with ranges in progress.
475
- numberToken() {
476
- var lexedLength, match, number, parsedValue, tag, tokenData;
477
- if (!(match = NUMBER.exec(this.chunk))) {
478
- return 0;
479
- }
480
- number = match[0];
481
- lexedLength = number.length;
482
- switch (false) {
483
- case !/^0[BOX]/.test(number):
484
- this.error(`radix prefix in '${number}' must be lowercase`, {
485
- offset: 1,
486
- });
487
- break;
488
- case !/^0\d*[89]/.test(number):
489
- this.error(`decimal literal '${number}' must not be prefixed with '0'`, {
490
- length: lexedLength,
491
- });
492
- break;
493
- case !/^0\d+/.test(number):
494
- this.error(`octal literal '${number}' must be prefixed with '0o'`, {
495
- length: lexedLength,
496
- });
497
- }
498
- parsedValue = parseNumber(number);
499
- tokenData = { parsedValue };
500
- tag = parsedValue === 2e308 ? 'INFINITY' : 'NUMBER';
501
- if (tag === 'INFINITY') {
502
- tokenData.original = number;
503
- }
504
- this.token(tag, number, {
505
- length: lexedLength,
506
- data: tokenData,
507
- });
508
- return lexedLength;
509
- }
510
-
511
- // Matches strings, including multiline strings, as well as heredocs, with or without
512
- // interpolation.
513
- stringToken() {
514
- var attempt, delimiter, doc, end, heredoc, i, indent, match, prev, quote, ref, regex, token, tokens;
515
- [quote] = STRING_START.exec(this.chunk) || [];
516
- if (!quote) {
517
- return 0;
518
- }
519
- // If the preceding token is `from` and this is an import or export statement,
520
- // properly tag the `from`.
521
- prev = this.prev();
522
- if (prev && this.value() === 'from' && (this.seenImport || this.seenExport)) {
523
- prev[0] = 'FROM';
524
- }
525
- regex = (function () {
526
- switch (quote) {
527
- case "'":
528
- return STRING_SINGLE;
529
- case '"':
530
- return STRING_DOUBLE;
531
- case "'''":
532
- return HEREDOC_SINGLE;
533
- case '"""':
534
- return HEREDOC_DOUBLE;
535
- }
536
- })();
537
- ({ tokens, index: end } = this.matchWithInterpolations(regex, quote));
538
- heredoc = quote.length === 3;
539
- if (heredoc) {
540
- // Find the smallest indentation. It will be removed from all lines later.
541
- indent = null;
542
- doc = (function () {
543
- var j, len, results;
544
- results = [];
545
- for (i = j = 0, len = tokens.length; j < len; i = ++j) {
546
- token = tokens[i];
547
- if (token[0] === 'NEOSTRING') {
548
- results.push(token[1]);
549
- }
550
- }
551
- return results;
552
- })().join('#{}');
553
- while ((match = HEREDOC_INDENT.exec(doc))) {
554
- attempt = match[1];
555
- if (indent === null || (0 < (ref = attempt.length) && ref < indent.length)) {
556
- indent = attempt;
557
- }
558
- }
559
- }
560
- delimiter = quote.charAt(0);
561
- this.mergeInterpolationTokens(
562
- tokens,
563
- {
564
- quote,
565
- indent,
566
- endOffset: end,
567
- },
568
- (value) => {
569
- return this.validateUnicodeCodePointEscapes(value, {
570
- delimiter: quote,
571
- });
572
- },
573
- );
574
- if (this.atJSXTag()) {
575
- this.token(',', ',', {
576
- length: 0,
577
- origin: this.prev,
578
- generated: true,
579
- });
580
- }
581
- return end;
582
- }
583
-
584
- // Matches and consumes comments. The comments are taken out of the token
585
- // stream and saved for later, to be reinserted into the output after
586
- // everything has been parsed and the JavaScript code generated.
587
- commentToken(chunk = this.chunk, { heregex, returnCommentTokens = false, offsetInChunk = 0 } = {}) {
588
- var commentAttachment,
589
- commentAttachments,
590
- commentWithSurroundingWhitespace,
591
- content,
592
- contents,
593
- getIndentSize,
594
- hasSeenFirstCommentLine,
595
- hereComment,
596
- hereLeadingWhitespace,
597
- hereTrailingWhitespace,
598
- i,
599
- indentSize,
600
- leadingNewline,
601
- leadingNewlineOffset,
602
- leadingNewlines,
603
- leadingWhitespace,
604
- length,
605
- lineComment,
606
- match,
607
- matchIllegal,
608
- noIndent,
609
- nonInitial,
610
- placeholderToken,
611
- precededByBlankLine,
612
- precedingNonCommentLines,
613
- prev;
614
- if (!(match = chunk.match(COMMENT))) {
615
- return 0;
616
- }
617
- [commentWithSurroundingWhitespace, hereLeadingWhitespace, hereComment, hereTrailingWhitespace, lineComment] = match;
618
- contents = null;
619
- // Does this comment follow code on the same line?
620
- leadingNewline = /^\s*\n+\s*#/.test(commentWithSurroundingWhitespace);
621
- if (hereComment) {
622
- matchIllegal = HERECOMMENT_ILLEGAL.exec(hereComment);
623
- if (matchIllegal) {
624
- this.error(`block comments cannot contain ${matchIllegal[0]}`, {
625
- offset: '###'.length + matchIllegal.index,
626
- length: matchIllegal[0].length,
627
- });
628
- }
629
- // Parse indentation or outdentation as if this block comment didn’t exist.
630
- chunk = chunk.replace(`###${hereComment}###`, '');
631
- // Remove leading newlines, like `Rewriter::removeLeadingNewlines`, to
632
- // avoid the creation of unwanted `TERMINATOR` tokens.
633
- chunk = chunk.replace(/^\n+/, '');
634
- this.lineToken({ chunk });
635
- // Pull out the ###-style comment’s content, and format it.
636
- content = hereComment;
637
- contents = [
638
- {
639
- content,
640
- length: commentWithSurroundingWhitespace.length - hereLeadingWhitespace.length - hereTrailingWhitespace.length,
641
- leadingWhitespace: hereLeadingWhitespace,
642
- },
643
- ];
644
- } else {
645
- // The `COMMENT` regex captures successive line comments as one token.
646
- // Remove any leading newlines before the first comment, but preserve
647
- // blank lines between line comments.
648
- leadingNewlines = '';
649
- content = lineComment.replace(/^(\n*)/, function (leading) {
650
- leadingNewlines = leading;
651
- return '';
652
- });
653
- precedingNonCommentLines = '';
654
- hasSeenFirstCommentLine = false;
655
- contents = content
656
- .split('\n')
657
- .map(function (line, index) {
658
- var comment, leadingWhitespace;
659
- if (!(line.indexOf('#') > -1)) {
660
- precedingNonCommentLines += `\n${line}`;
661
- return;
662
- }
663
- leadingWhitespace = '';
664
- content = line.replace(/^([ |\t]*)#/, function (_, whitespace) {
665
- leadingWhitespace = whitespace;
666
- return '';
667
- });
668
- comment = {
669
- content,
670
- length: '#'.length + content.length,
671
- leadingWhitespace: `${!hasSeenFirstCommentLine ? leadingNewlines : ''}${precedingNonCommentLines}${leadingWhitespace}`,
672
- precededByBlankLine: !!precedingNonCommentLines,
673
- };
674
- hasSeenFirstCommentLine = true;
675
- precedingNonCommentLines = '';
676
- return comment;
677
- })
678
- .filter(function (comment) {
679
- return comment;
680
- });
681
- }
682
- getIndentSize = function ({ leadingWhitespace, nonInitial }) {
683
- var lastNewlineIndex;
684
- lastNewlineIndex = leadingWhitespace.lastIndexOf('\n');
685
- if (hereComment != null || !nonInitial) {
686
- if (!(lastNewlineIndex > -1)) {
687
- return null;
688
- }
689
- } else {
690
- if (lastNewlineIndex == null) {
691
- lastNewlineIndex = -1;
692
- }
693
- }
694
- return leadingWhitespace.length - 1 - lastNewlineIndex;
695
- };
696
- commentAttachments = function () {
697
- var j, len, results;
698
- results = [];
699
- for (i = j = 0, len = contents.length; j < len; i = ++j) {
700
- ({ content, length, leadingWhitespace, precededByBlankLine } = contents[i]);
701
- nonInitial = i !== 0;
702
- leadingNewlineOffset = nonInitial ? 1 : 0;
703
- offsetInChunk += leadingNewlineOffset + leadingWhitespace.length;
704
- indentSize = getIndentSize({ leadingWhitespace, nonInitial });
705
- noIndent = indentSize == null || indentSize === -1;
706
- commentAttachment = {
707
- content,
708
- here: hereComment != null,
709
- newLine: leadingNewline || nonInitial, // Line comments after the first one start new lines, by definition.
710
- locationData: this.makeLocationData({ offsetInChunk, length }),
711
- precededByBlankLine,
712
- indentSize,
713
- indented: !noIndent && indentSize > this.indent,
714
- outdented: !noIndent && indentSize < this.indent,
715
- };
716
- if (heregex) {
717
- commentAttachment.heregex = true;
718
- }
719
- offsetInChunk += length;
720
- results.push(commentAttachment);
721
- }
722
- return results;
723
- }.call(this);
724
- prev = this.prev();
725
- if (!prev) {
726
- // If there’s no previous token, create a placeholder token to attach
727
- // this comment to; and follow with a newline.
728
- commentAttachments[0].newLine = true;
729
- this.lineToken({
730
- chunk: this.chunk.slice(commentWithSurroundingWhitespace.length),
731
- offset: commentWithSurroundingWhitespace.length, // Set the indent.
732
- });
733
- placeholderToken = this.makeToken('JS', '', {
734
- offset: commentWithSurroundingWhitespace.length,
735
- generated: true,
736
- });
737
- placeholderToken.comments = commentAttachments;
738
- this.tokens.push(placeholderToken);
739
- this.newlineToken(commentWithSurroundingWhitespace.length);
740
- } else {
741
- attachCommentsToNode(commentAttachments, prev);
742
- }
743
- if (returnCommentTokens) {
744
- return commentAttachments;
745
- }
746
- return commentWithSurroundingWhitespace.length;
747
- }
748
-
749
- // Matches JavaScript interpolated directly into the source via backticks.
750
- jsToken() {
751
- var length, match, matchedHere, script;
752
- if (!(this.chunk.charAt(0) === '`' && (match = (matchedHere = HERE_JSTOKEN.exec(this.chunk)) || JSTOKEN.exec(this.chunk)))) {
753
- return 0;
754
- }
755
- // Convert escaped backticks to backticks, and escaped backslashes
756
- // just before escaped backticks to backslashes
757
- script = match[1];
758
- ({ length } = match[0]);
759
- this.token('JS', script, {
760
- length,
761
- data: {
762
- here: !!matchedHere,
763
- },
764
- });
765
- return length;
766
- }
767
-
768
- // Matches regular expression literals, as well as multiline extended ones.
769
- // Lexing regular expressions is difficult to distinguish from division, so we
770
- // borrow some basic heuristics from JavaScript and Ruby.
771
- regexToken() {
772
- var body,
773
- closed,
774
- comment,
775
- commentIndex,
776
- commentOpts,
777
- commentTokens,
778
- comments,
779
- delimiter,
780
- end,
781
- flags,
782
- fullMatch,
783
- index,
784
- leadingWhitespace,
785
- match,
786
- matchedComment,
787
- origin,
788
- prev,
789
- ref,
790
- ref1,
791
- regex,
792
- tokens;
793
- switch (false) {
794
- case !(match = REGEX_ILLEGAL.exec(this.chunk)):
795
- this.error(`regular expressions cannot begin with ${match[2]}`, {
796
- offset: match.index + match[1].length,
797
- });
798
- break;
799
- case !(match = this.matchWithInterpolations(HEREGEX, '///')):
800
- ({ tokens, index } = match);
801
- comments = [];
802
- while ((matchedComment = HEREGEX_COMMENT.exec(this.chunk.slice(0, index)))) {
803
- ({ index: commentIndex } = matchedComment);
804
- [fullMatch, leadingWhitespace, comment] = matchedComment;
805
- comments.push({
806
- comment,
807
- offsetInChunk: commentIndex + leadingWhitespace.length,
808
- });
809
- }
810
- commentTokens = flatten(
811
- function () {
812
- var j, len, results;
813
- results = [];
814
- for (j = 0, len = comments.length; j < len; j++) {
815
- commentOpts = comments[j];
816
- results.push(
817
- this.commentToken(
818
- commentOpts.comment,
819
- Object.assign(commentOpts, {
820
- heregex: true,
821
- returnCommentTokens: true,
822
- }),
823
- ),
824
- );
825
- }
826
- return results;
827
- }.call(this),
828
- );
829
- break;
830
- case !(match = REGEX.exec(this.chunk)):
831
- [regex, body, closed] = match;
832
- this.validateEscapes(body, {
833
- isRegex: true,
834
- offsetInChunk: 1,
835
- });
836
- index = regex.length;
837
- prev = this.prev();
838
- if (prev) {
839
- if (prev.spaced && ((ref = prev[0]), indexOf.call(CALLABLE, ref) >= 0)) {
840
- if (!closed || POSSIBLY_DIVISION.test(regex)) {
841
- return 0;
842
- }
843
- } else if (((ref1 = prev[0]), indexOf.call(NOT_REGEX, ref1) >= 0)) {
844
- return 0;
845
- }
846
- }
847
- if (!closed) {
848
- this.error('missing / (unclosed regex)');
849
- }
850
- break;
851
- default:
852
- return 0;
853
- }
854
- [flags] = REGEX_FLAGS.exec(this.chunk.slice(index));
855
- end = index + flags.length;
856
- origin = this.makeToken('REGEX', null, {
857
- length: end,
858
- });
859
- switch (false) {
860
- case !!VALID_FLAGS.test(flags):
861
- this.error(`invalid regular expression flags ${flags}`, {
862
- offset: index,
863
- length: flags.length,
864
- });
865
- break;
866
- case !(regex || tokens.length === 1):
867
- delimiter = body ? '/' : '///';
868
- if (body == null) {
869
- body = tokens[0][1];
870
- }
871
- this.validateUnicodeCodePointEscapes(body, { delimiter });
872
- this.token('REGEX', `/${body}/${flags}`, {
873
- length: end,
874
- origin,
875
- data: { delimiter },
876
- });
877
- break;
878
- default:
879
- this.token('REGEX_START', '(', {
880
- length: 0,
881
- origin,
882
- generated: true,
883
- });
884
- this.token('IDENTIFIER', 'RegExp', {
885
- length: 0,
886
- generated: true,
887
- });
888
- this.token('CALL_START', '(', {
889
- length: 0,
890
- generated: true,
891
- });
892
- this.mergeInterpolationTokens(
893
- tokens,
894
- {
895
- double: true,
896
- heregex: { flags },
897
- endOffset: end - flags.length,
898
- quote: '///',
899
- },
900
- (str) => {
901
- return this.validateUnicodeCodePointEscapes(str, { delimiter });
902
- },
903
- );
904
- if (flags) {
905
- this.token(',', ',', {
906
- offset: index - 1,
907
- length: 0,
908
- generated: true,
909
- });
910
- this.token('STRING', '"' + flags + '"', {
911
- offset: index,
912
- length: flags.length,
913
- });
914
- }
915
- this.token(')', ')', {
916
- offset: end,
917
- length: 0,
918
- generated: true,
919
- });
920
- this.token('REGEX_END', ')', {
921
- offset: end,
922
- length: 0,
923
- generated: true,
924
- });
925
- }
926
- // Explicitly attach any heregex comments to the REGEX/REGEX_END token.
927
- if (commentTokens != null ? commentTokens.length : void 0) {
928
- addTokenData(this.tokens[this.tokens.length - 1], {
929
- heregexCommentTokens: commentTokens,
930
- });
931
- }
932
- return end;
933
- }
934
-
935
- // Matches newlines, indents, and outdents, and determines which is which.
936
- // If we can detect that the current line is continued onto the next line,
937
- // then the newline is suppressed:
938
-
939
- // elements
940
- // .each( ... )
941
- // .map( ... )
942
-
943
- // Keeps track of the level of indentation, because a single outdent token
944
- // can close multiple indents, so we need to know how far in we happen to be.
945
- lineToken({ chunk = this.chunk, offset = 0 } = {}) {
946
- var backslash, diff, endsContinuationLineIndentation, indent, match, minLiteralLength, newIndentLiteral, noNewlines, prev, ref, size;
947
- if (!(match = MULTI_DENT.exec(chunk))) {
948
- return 0;
949
- }
950
- indent = match[0];
951
- prev = this.prev();
952
- backslash = (prev != null ? prev[0] : void 0) === '\\';
953
- if (!((backslash || ((ref = this.seenFor) != null ? ref.endsLength : void 0) < this.ends.length) && this.seenFor)) {
954
- this.seenFor = false;
955
- }
956
- if (!((backslash && this.seenImport) || this.importSpecifierList)) {
957
- this.seenImport = false;
958
- }
959
- if (!((backslash && this.seenExport) || this.exportSpecifierList)) {
960
- this.seenExport = false;
961
- }
962
- size = indent.length - 1 - indent.lastIndexOf('\n');
963
- noNewlines = this.unfinished();
964
- newIndentLiteral = size > 0 ? indent.slice(-size) : '';
965
- if (!/^(.?)\1*$/.exec(newIndentLiteral)) {
966
- this.error('mixed indentation', {
967
- offset: indent.length,
968
- });
969
- return indent.length;
970
- }
971
- minLiteralLength = Math.min(newIndentLiteral.length, this.indentLiteral.length);
972
- if (newIndentLiteral.slice(0, minLiteralLength) !== this.indentLiteral.slice(0, minLiteralLength)) {
973
- this.error('indentation mismatch', {
974
- offset: indent.length,
975
- });
976
- return indent.length;
977
- }
978
- if (size - this.continuationLineAdditionalIndent === this.indent) {
979
- if (noNewlines) {
980
- this.suppressNewlines();
981
- } else {
982
- this.newlineToken(offset);
983
- }
984
- return indent.length;
985
- }
986
- if (size > this.indent) {
987
- if (noNewlines) {
988
- if (!backslash) {
989
- this.continuationLineAdditionalIndent = size - this.indent;
990
- }
991
- if (this.continuationLineAdditionalIndent) {
992
- prev.continuationLineIndent = this.indent + this.continuationLineAdditionalIndent;
993
- }
994
- this.suppressNewlines();
995
- return indent.length;
996
- }
997
- if (!this.tokens.length) {
998
- this.baseIndent = this.indent = size;
999
- this.indentLiteral = newIndentLiteral;
1000
- return indent.length;
1001
- }
1002
- diff = size - this.indent + this.outdebt;
1003
- this.token('INDENT', diff, {
1004
- offset: offset + indent.length - size,
1005
- length: size,
1006
- });
1007
- this.indents.push(diff);
1008
- this.ends.push({
1009
- tag: 'OUTDENT',
1010
- });
1011
- this.outdebt = this.continuationLineAdditionalIndent = 0;
1012
- this.indent = size;
1013
- this.indentLiteral = newIndentLiteral;
1014
- } else if (size < this.baseIndent) {
1015
- this.error('missing indentation', {
1016
- offset: offset + indent.length,
1017
- });
1018
- } else {
1019
- endsContinuationLineIndentation = this.continuationLineAdditionalIndent > 0;
1020
- this.continuationLineAdditionalIndent = 0;
1021
- this.outdentToken({
1022
- moveOut: this.indent - size,
1023
- noNewlines,
1024
- outdentLength: indent.length,
1025
- offset,
1026
- indentSize: size,
1027
- endsContinuationLineIndentation,
1028
- });
1029
- }
1030
- return indent.length;
1031
- }
1032
-
1033
- // Record an outdent token or multiple tokens, if we happen to be moving back
1034
- // inwards past several recorded indents. Sets new @indent value.
1035
- outdentToken({ moveOut, noNewlines, outdentLength = 0, offset = 0, indentSize, endsContinuationLineIndentation }) {
1036
- var decreasedIndent, dent, lastIndent, ref, terminatorToken;
1037
- decreasedIndent = this.indent - moveOut;
1038
- while (moveOut > 0) {
1039
- lastIndent = this.indents[this.indents.length - 1];
1040
- if (!lastIndent) {
1041
- this.outdebt = moveOut = 0;
1042
- } else if (this.outdebt && moveOut <= this.outdebt) {
1043
- this.outdebt -= moveOut;
1044
- moveOut = 0;
1045
- } else {
1046
- dent = this.indents.pop() + this.outdebt;
1047
- if (outdentLength && ((ref = this.chunk[outdentLength]), indexOf.call(INDENTABLE_CLOSERS, ref) >= 0)) {
1048
- decreasedIndent -= dent - moveOut;
1049
- moveOut = dent;
1050
- }
1051
- this.outdebt = 0;
1052
- // pair might call outdentToken, so preserve decreasedIndent
1053
- this.pair('OUTDENT');
1054
- this.token('OUTDENT', moveOut, {
1055
- length: outdentLength,
1056
- indentSize: indentSize + moveOut - dent,
1057
- });
1058
- moveOut -= dent;
1059
- }
1060
- }
1061
- if (dent) {
1062
- this.outdebt -= moveOut;
1063
- }
1064
- this.suppressSemicolons();
1065
- if (!(this.tag() === 'TERMINATOR' || noNewlines)) {
1066
- terminatorToken = this.token('TERMINATOR', '\n', {
1067
- offset: offset + outdentLength,
1068
- length: 0,
1069
- });
1070
- if (endsContinuationLineIndentation) {
1071
- terminatorToken.endsContinuationLineIndentation = {
1072
- preContinuationLineIndent: this.indent,
1073
- };
1074
- }
1075
- }
1076
- this.indent = decreasedIndent;
1077
- this.indentLiteral = this.indentLiteral.slice(0, decreasedIndent);
1078
- return this;
1079
- }
1080
-
1081
- // Matches and consumes non-meaningful whitespace. Tag the previous token
1082
- // as being “spaced”, because there are some cases where it makes a difference.
1083
- whitespaceToken() {
1084
- var match, nline, prev;
1085
- if (!((match = WHITESPACE.exec(this.chunk)) || (nline = this.chunk.charAt(0) === '\n'))) {
1086
- return 0;
1087
- }
1088
- prev = this.prev();
1089
- if (prev) {
1090
- prev[match ? 'spaced' : 'newLine'] = true;
1091
- }
1092
- if (match) {
1093
- return match[0].length;
1094
- } else {
1095
- return 0;
1096
- }
1097
- }
1098
-
1099
- // Generate a newline token. Consecutive newlines get merged together.
1100
- newlineToken(offset) {
1101
- this.suppressSemicolons();
1102
- if (this.tag() !== 'TERMINATOR') {
1103
- this.token('TERMINATOR', '\n', {
1104
- offset,
1105
- length: 0,
1106
- });
1107
- }
1108
- return this;
1109
- }
1110
-
1111
- // Use a `\` at a line-ending to suppress the newline.
1112
- // The slash is removed here once its job is done.
1113
- suppressNewlines() {
1114
- var prev;
1115
- prev = this.prev();
1116
- if (prev[1] === '\\') {
1117
- if (prev.comments && this.tokens.length > 1) {
1118
- // `@tokens.length` should be at least 2 (some code, then `\`).
1119
- // If something puts a `\` after nothing, they deserve to lose any
1120
- // comments that trail it.
1121
- attachCommentsToNode(prev.comments, this.tokens[this.tokens.length - 2]);
1122
- }
1123
- this.tokens.pop();
1124
- }
1125
- return this;
1126
- }
1127
-
1128
- jsxToken() {
1129
- var afterTag,
1130
- end,
1131
- endToken,
1132
- firstChar,
1133
- fullId,
1134
- fullTagName,
1135
- id,
1136
- input,
1137
- j,
1138
- jsxTag,
1139
- len,
1140
- match,
1141
- offset,
1142
- openingTagToken,
1143
- prev,
1144
- prevChar,
1145
- properties,
1146
- property,
1147
- ref,
1148
- tagToken,
1149
- token,
1150
- tokens;
1151
- firstChar = this.chunk[0];
1152
- // Check the previous token to detect if attribute is spread.
1153
- prevChar = this.tokens.length > 0 ? this.tokens[this.tokens.length - 1][0] : '';
1154
- if (firstChar === '<') {
1155
- match = JSX_IDENTIFIER.exec(this.chunk.slice(1)) || JSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(1));
1156
- // Not the right hand side of an unspaced comparison (i.e. `a<b`).
1157
- if (
1158
- !(
1159
- match &&
1160
- (this.jsxDepth > 0 || !(prev = this.prev()) || prev.spaced || ((ref = prev[0]), indexOf.call(COMPARABLE_LEFT_SIDE, ref) < 0))
1161
- )
1162
- ) {
1163
- return 0;
1164
- }
1165
- [input, id] = match;
1166
- fullId = id;
1167
- if (indexOf.call(id, '.') >= 0) {
1168
- [id, ...properties] = id.split('.');
1169
- } else {
1170
- properties = [];
1171
- }
1172
- tagToken = this.token('JSX_TAG', id, {
1173
- length: id.length + 1,
1174
- data: {
1175
- openingBracketToken: this.makeToken('<', '<'),
1176
- tagNameToken: this.makeToken('IDENTIFIER', id, {
1177
- offset: 1,
1178
- }),
1179
- },
1180
- });
1181
- offset = id.length + 1;
1182
- for (j = 0, len = properties.length; j < len; j++) {
1183
- property = properties[j];
1184
- this.token('.', '.', { offset });
1185
- offset += 1;
1186
- this.token('PROPERTY', property, { offset });
1187
- offset += property.length;
1188
- }
1189
- this.token('CALL_START', '(', {
1190
- generated: true,
1191
- });
1192
- this.token('[', '[', {
1193
- generated: true,
1194
- });
1195
- this.ends.push({
1196
- tag: '/>',
1197
- origin: tagToken,
1198
- name: id,
1199
- properties,
1200
- });
1201
- this.jsxDepth++;
1202
- return fullId.length + 1;
1203
- } else if ((jsxTag = this.atJSXTag())) {
1204
- if (this.chunk.slice(0, 2) === '/>') {
1205
- // Self-closing tag.
1206
- this.pair('/>');
1207
- this.token(']', ']', {
1208
- length: 2,
1209
- generated: true,
1210
- });
1211
- this.token('CALL_END', ')', {
1212
- length: 2,
1213
- generated: true,
1214
- data: {
1215
- selfClosingSlashToken: this.makeToken('/', '/'),
1216
- closingBracketToken: this.makeToken('>', '>', {
1217
- offset: 1,
1218
- }),
1219
- },
1220
- });
1221
- this.jsxDepth--;
1222
- return 2;
1223
- } else if (firstChar === '{') {
1224
- if (prevChar === ':') {
1225
- // This token represents the start of a JSX attribute value
1226
- // that’s an expression (e.g. the `{b}` in `<div a={b} />`).
1227
- // Our grammar represents the beginnings of expressions as `(`
1228
- // tokens, so make this into a `(` token that displays as `{`.
1229
- token = this.token('(', '{');
1230
- this.jsxObjAttribute[this.jsxDepth] = false;
1231
- // tag attribute name as JSX
1232
- addTokenData(this.tokens[this.tokens.length - 3], {
1233
- jsx: true,
1234
- });
1235
- } else {
1236
- token = this.token('{', '{');
1237
- this.jsxObjAttribute[this.jsxDepth] = true;
1238
- }
1239
- this.ends.push({
1240
- tag: '}',
1241
- origin: token,
1242
- });
1243
- return 1;
1244
- } else if (firstChar === '>') {
1245
- // end of opening tag
1246
- ({
1247
- // Ignore terminators inside a tag.
1248
- origin: openingTagToken,
1249
- } = this.pair('/>')); // As if the current tag was self-closing.
1250
- this.token(']', ']', {
1251
- generated: true,
1252
- data: {
1253
- closingBracketToken: this.makeToken('>', '>'),
1254
- },
1255
- });
1256
- this.token(',', 'JSX_COMMA', {
1257
- generated: true,
1258
- });
1259
- ({ tokens, index: end } = this.matchWithInterpolations(INSIDE_JSX, '>', '</', JSX_INTERPOLATION));
1260
- this.mergeInterpolationTokens(
1261
- tokens,
1262
- {
1263
- endOffset: end,
1264
- jsx: true,
1265
- },
1266
- (value) => {
1267
- return this.validateUnicodeCodePointEscapes(value, {
1268
- delimiter: '>',
1269
- });
1270
- },
1271
- );
1272
- match = JSX_IDENTIFIER.exec(this.chunk.slice(end)) || JSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(end));
1273
- if (
1274
- !match ||
1275
- match[1] !==
1276
- `${jsxTag.name}${(function () {
1277
- var k, len1, ref1, results;
1278
- ref1 = jsxTag.properties;
1279
- results = [];
1280
- for (k = 0, len1 = ref1.length; k < len1; k++) {
1281
- property = ref1[k];
1282
- results.push(`.${property}`);
1283
- }
1284
- return results;
1285
- })().join('')}`
1286
- ) {
1287
- this.error(`expected corresponding JSX closing tag for ${jsxTag.name}`, jsxTag.origin.data.tagNameToken[2]);
1288
- }
1289
- [, fullTagName] = match;
1290
- afterTag = end + fullTagName.length;
1291
- if (this.chunk[afterTag] !== '>') {
1292
- this.error('missing closing > after tag name', {
1293
- offset: afterTag,
1294
- length: 1,
1295
- });
1296
- }
1297
- // -2/+2 for the opening `</` and +1 for the closing `>`.
1298
- endToken = this.token('CALL_END', ')', {
1299
- offset: end - 2,
1300
- length: fullTagName.length + 3,
1301
- generated: true,
1302
- data: {
1303
- closingTagOpeningBracketToken: this.makeToken('<', '<', {
1304
- offset: end - 2,
1305
- }),
1306
- closingTagSlashToken: this.makeToken('/', '/', {
1307
- offset: end - 1,
1308
- }),
1309
- // TODO: individual tokens for complex tag name? eg < / A . B >
1310
- closingTagNameToken: this.makeToken('IDENTIFIER', fullTagName, {
1311
- offset: end,
1312
- }),
1313
- closingTagClosingBracketToken: this.makeToken('>', '>', {
1314
- offset: end + fullTagName.length,
1315
- }),
1316
- },
1317
- });
1318
- // make the closing tag location data more easily accessible to the grammar
1319
- addTokenData(openingTagToken, endToken.data);
1320
- this.jsxDepth--;
1321
- return afterTag + 1;
1322
- } else {
1323
- return 0;
1324
- }
1325
- } else if (this.atJSXTag(1)) {
1326
- if (firstChar === '}') {
1327
- this.pair(firstChar);
1328
- if (this.jsxObjAttribute[this.jsxDepth]) {
1329
- this.token('}', '}');
1330
- this.jsxObjAttribute[this.jsxDepth] = false;
1331
- } else {
1332
- this.token(')', '}');
1333
- }
1334
- this.token(',', ',', {
1335
- generated: true,
1336
- });
1337
- return 1;
1338
- } else {
1339
- return 0;
1340
- }
1341
- } else {
1342
- return 0;
1343
- }
1344
- }
1345
-
1346
- atJSXTag(depth = 0) {
1347
- var i, last, ref;
1348
- if (this.jsxDepth === 0) {
1349
- return false;
1350
- }
1351
- i = this.ends.length - 1;
1352
- while (((ref = this.ends[i]) != null ? ref.tag : void 0) === 'OUTDENT' || depth-- > 0) {
1353
- // Ignore indents.
1354
- i--;
1355
- }
1356
- last = this.ends[i];
1357
- return (last != null ? last.tag : void 0) === '/>' && last;
1358
- }
1359
-
1360
- // We treat all other single characters as a token. E.g.: `( ) , . !`
1361
- // Multi-character operators are also literal tokens, so that Jison can assign
1362
- // the proper order of operations. There are some symbols that we tag specially
1363
- // here. `;` and newlines are both treated as a `TERMINATOR`, we distinguish
1364
- // parentheses that indicate a method call from regular parentheses, and so on.
1365
- literalToken() {
1366
- var match, message, origin, prev, ref, ref1, ref2, ref3, ref4, ref5, skipToken, tag, token, value;
1367
- if ((match = OPERATOR.exec(this.chunk))) {
1368
- [value] = match;
1369
- if (CODE.test(value)) {
1370
- this.tagParameters();
1371
- }
1372
- } else {
1373
- value = this.chunk.charAt(0);
1374
- }
1375
- tag = value;
1376
- prev = this.prev();
1377
- if (prev && indexOf.call(['=', ...COMPOUND_ASSIGN], value) >= 0) {
1378
- skipToken = false;
1379
- if (value === '=' && ((ref = prev[1]) === '||' || ref === '&&') && !prev.spaced) {
1380
- prev[0] = 'COMPOUND_ASSIGN';
1381
- prev[1] += '=';
1382
- if ((ref1 = prev.data) != null ? ref1.original : void 0) {
1383
- prev.data.original += '=';
1384
- }
1385
- prev[2].range = [prev[2].range[0], prev[2].range[1] + 1];
1386
- prev[2].last_column += 1;
1387
- prev[2].last_column_exclusive += 1;
1388
- prev = this.tokens[this.tokens.length - 2];
1389
- skipToken = true;
1390
- }
1391
- if (prev && prev[0] !== 'PROPERTY') {
1392
- origin = (ref2 = prev.origin) != null ? ref2 : prev;
1393
- message = isUnassignable(prev[1], origin[1]);
1394
- if (message) {
1395
- this.error(message, origin[2]);
1396
- }
1397
- }
1398
- if (skipToken) {
1399
- return value.length;
1400
- }
1401
- }
1402
- if (value === '(' && (prev != null ? prev[0] : void 0) === 'IMPORT') {
1403
- prev[0] = 'DYNAMIC_IMPORT';
1404
- }
1405
- if (value === '{' && this.seenImport) {
1406
- this.importSpecifierList = true;
1407
- } else if (this.importSpecifierList && value === '}') {
1408
- this.importSpecifierList = false;
1409
- } else if (value === '{' && (prev != null ? prev[0] : void 0) === 'EXPORT') {
1410
- this.exportSpecifierList = true;
1411
- } else if (this.exportSpecifierList && value === '}') {
1412
- this.exportSpecifierList = false;
1413
- }
1414
- if (value === ';') {
1415
- if (((ref3 = prev != null ? prev[0] : void 0), indexOf.call(['=', ...UNFINISHED], ref3) >= 0)) {
1416
- this.error('unexpected ;');
1417
- }
1418
- this.seenFor = this.seenImport = this.seenExport = false;
1419
- tag = 'TERMINATOR';
1420
- } else if (value === '*' && (prev != null ? prev[0] : void 0) === 'EXPORT') {
1421
- tag = 'EXPORT_ALL';
1422
- } else if (indexOf.call(MATH, value) >= 0) {
1423
- tag = 'MATH';
1424
- } else if (indexOf.call(COMPARE, value) >= 0) {
1425
- tag = 'COMPARE';
1426
- } else if (indexOf.call(COMPOUND_ASSIGN, value) >= 0) {
1427
- tag = 'COMPOUND_ASSIGN';
1428
- } else if (indexOf.call(UNARY, value) >= 0) {
1429
- tag = 'UNARY';
1430
- } else if (indexOf.call(UNARY_MATH, value) >= 0) {
1431
- tag = 'UNARY_MATH';
1432
- } else if (indexOf.call(SHIFT, value) >= 0) {
1433
- tag = 'SHIFT';
1434
- } else if (value === '?' && (prev != null ? prev.spaced : void 0)) {
1435
- tag = 'BIN?';
1436
- } else if (prev) {
1437
- if (value === '(' && !prev.spaced && ((ref4 = prev[0]), indexOf.call(CALLABLE, ref4) >= 0)) {
1438
- if (prev[0] === '?') {
1439
- prev[0] = 'FUNC_EXIST';
1440
- }
1441
- tag = 'CALL_START';
1442
- } else if (value === '[' && ((((ref5 = prev[0]), indexOf.call(INDEXABLE, ref5) >= 0) && !prev.spaced) || prev[0] === '::')) {
1443
- // `.prototype` can’t be a method you can call.
1444
- tag = 'INDEX_START';
1445
- switch (prev[0]) {
1446
- case '?':
1447
- prev[0] = 'INDEX_SOAK';
1448
- }
1449
- }
1450
- }
1451
- token = this.makeToken(tag, value);
1452
- switch (value) {
1453
- case '(':
1454
- case '{':
1455
- case '[':
1456
- this.ends.push({
1457
- tag: INVERSES[value],
1458
- origin: token,
1459
- });
1460
- break;
1461
- case ')':
1462
- case '}':
1463
- case ']':
1464
- this.pair(value);
1465
- }
1466
- this.tokens.push(this.makeToken(tag, value));
1467
- return value.length;
1468
- }
1469
-
1470
- // Token Manipulators
1471
- // ------------------
1472
-
1473
- // A source of ambiguity in our grammar used to be parameter lists in function
1474
- // definitions versus argument lists in function calls. Walk backwards, tagging
1475
- // parameters specially in order to make things easier for the parser.
1476
- tagParameters() {
1477
- var i, paramEndToken, stack, tok, tokens;
1478
- if (this.tag() !== ')') {
1479
- return this.tagDoIife();
1480
- }
1481
- stack = [];
1482
- ({ tokens } = this);
1483
- i = tokens.length;
1484
- paramEndToken = tokens[--i];
1485
- paramEndToken[0] = 'PARAM_END';
1486
- while ((tok = tokens[--i])) {
1487
- switch (tok[0]) {
1488
- case ')':
1489
- stack.push(tok);
1490
- break;
1491
- case '(':
1492
- case 'CALL_START':
1493
- if (stack.length) {
1494
- stack.pop();
1495
- } else if (tok[0] === '(') {
1496
- tok[0] = 'PARAM_START';
1497
- return this.tagDoIife(i - 1);
1498
- } else {
1499
- paramEndToken[0] = 'CALL_END';
1500
- return this;
1501
- }
1502
- }
1503
- }
1504
- return this;
1505
- }
1506
-
1507
- // Tag `do` followed by a function differently than `do` followed by eg an
1508
- // identifier to allow for different grammar precedence
1509
- tagDoIife(tokenIndex) {
1510
- var tok;
1511
- tok = this.tokens[tokenIndex != null ? tokenIndex : this.tokens.length - 1];
1512
- if ((tok != null ? tok[0] : void 0) !== 'DO') {
1513
- return this;
1514
- }
1515
- tok[0] = 'DO_IIFE';
1516
- return this;
1517
- }
1518
-
1519
- // Close up all remaining open blocks at the end of the file.
1520
- closeIndentation() {
1521
- return this.outdentToken({
1522
- moveOut: this.indent,
1523
- indentSize: 0,
1524
- });
1525
- }
1526
-
1527
- // Match the contents of a delimited token and expand variables and expressions
1528
- // inside it using Ruby-like notation for substitution of arbitrary
1529
- // expressions.
1530
-
1531
- // "Hello #{name.capitalize()}."
1532
-
1533
- // If it encounters an interpolation, this method will recursively create a new
1534
- // Lexer and tokenize until the `{` of `#{` is balanced with a `}`.
1535
-
1536
- // - `regex` matches the contents of a token (but not `delimiter`, and not
1537
- // `#{` if interpolations are desired).
1538
- // - `delimiter` is the delimiter of the token. Examples are `'`, `"`, `'''`,
1539
- // `"""` and `///`.
1540
- // - `closingDelimiter` is different from `delimiter` only in JSX
1541
- // - `interpolators` matches the start of an interpolation, for JSX it's both
1542
- // `{` and `<` (i.e. nested JSX tag)
1543
-
1544
- // This method allows us to have strings within interpolations within strings,
1545
- // ad infinitum.
1546
- matchWithInterpolations(regex, delimiter, closingDelimiter = delimiter, interpolators = /^#\{/) {
1547
- var braceInterpolator,
1548
- close,
1549
- column,
1550
- index,
1551
- interpolationOffset,
1552
- interpolator,
1553
- line,
1554
- match,
1555
- nested,
1556
- offset,
1557
- offsetInChunk,
1558
- open,
1559
- ref,
1560
- ref1,
1561
- rest,
1562
- str,
1563
- strPart,
1564
- tokens;
1565
- tokens = [];
1566
- offsetInChunk = delimiter.length;
1567
- if (this.chunk.slice(0, offsetInChunk) !== delimiter) {
1568
- return null;
1569
- }
1570
- str = this.chunk.slice(offsetInChunk);
1571
- while (true) {
1572
- [strPart] = regex.exec(str);
1573
- this.validateEscapes(strPart, {
1574
- isRegex: delimiter.charAt(0) === '/',
1575
- offsetInChunk,
1576
- });
1577
- // Push a fake `'NEOSTRING'` token, which will get turned into a real string later.
1578
- tokens.push(
1579
- this.makeToken('NEOSTRING', strPart, {
1580
- offset: offsetInChunk,
1581
- }),
1582
- );
1583
- str = str.slice(strPart.length);
1584
- offsetInChunk += strPart.length;
1585
- if (!(match = interpolators.exec(str))) {
1586
- break;
1587
- }
1588
- [interpolator] = match;
1589
- // To remove the `#` in `#{`.
1590
- interpolationOffset = interpolator.length - 1;
1591
- [line, column, offset] = this.getLineAndColumnFromChunk(offsetInChunk + interpolationOffset);
1592
- rest = str.slice(interpolationOffset);
1593
- ({ tokens: nested, index } = new Lexer().tokenize(rest, {
1594
- line,
1595
- column,
1596
- offset,
1597
- untilBalanced: true,
1598
- locationDataCompensations: this.locationDataCompensations,
1599
- }));
1600
- // Account for the `#` in `#{`.
1601
- index += interpolationOffset;
1602
- braceInterpolator = str[index - 1] === '}';
1603
- if (braceInterpolator) {
1604
- // Turn the leading and trailing `{` and `}` into parentheses. Unnecessary
1605
- // parentheses will be removed later.
1606
- ([open] = nested), ([close] = slice.call(nested, -1));
1607
- open[0] = 'INTERPOLATION_START';
1608
- open[1] = '(';
1609
- open[2].first_column -= interpolationOffset;
1610
- open[2].range = [open[2].range[0] - interpolationOffset, open[2].range[1]];
1611
- close[0] = 'INTERPOLATION_END';
1612
- close[1] = ')';
1613
- close.origin = ['', 'end of interpolation', close[2]];
1614
- }
1615
- if (((ref = nested[1]) != null ? ref[0] : void 0) === 'TERMINATOR') {
1616
- // Remove leading `'TERMINATOR'` (if any).
1617
- nested.splice(1, 1);
1618
- }
1619
- if (((ref1 = nested[nested.length - 3]) != null ? ref1[0] : void 0) === 'INDENT' && nested[nested.length - 2][0] === 'OUTDENT') {
1620
- // Remove trailing `'INDENT'/'OUTDENT'` pair (if any).
1621
- nested.splice(-3, 2);
1622
- }
1623
- if (!braceInterpolator) {
1624
- // We are not using `{` and `}`, so wrap the interpolated tokens instead.
1625
- open = this.makeToken('INTERPOLATION_START', '(', {
1626
- offset: offsetInChunk,
1627
- length: 0,
1628
- generated: true,
1629
- });
1630
- close = this.makeToken('INTERPOLATION_END', ')', {
1631
- offset: offsetInChunk + index,
1632
- length: 0,
1633
- generated: true,
1634
- });
1635
- nested = [open, ...nested, close];
1636
- }
1637
- // Push a fake `'TOKENS'` token, which will get turned into real tokens later.
1638
- tokens.push(['TOKENS', nested]);
1639
- str = str.slice(index);
1640
- offsetInChunk += index;
1641
- }
1642
- if (str.slice(0, closingDelimiter.length) !== closingDelimiter) {
1643
- this.error(`missing ${closingDelimiter}`, {
1644
- length: delimiter.length,
1645
- });
1646
- }
1647
- return {
1648
- tokens,
1649
- index: offsetInChunk + closingDelimiter.length,
1650
- };
1651
- }
1652
-
1653
- // Merge the array `tokens` of the fake token types `'TOKENS'` and `'NEOSTRING'`
1654
- // (as returned by `matchWithInterpolations`) into the token stream. The value
1655
- // of `'NEOSTRING'`s are converted using `fn` and turned into strings using
1656
- // `options` first.
1657
- mergeInterpolationTokens(tokens, options, fn) {
1658
- var $,
1659
- converted,
1660
- double,
1661
- endOffset,
1662
- firstIndex,
1663
- heregex,
1664
- i,
1665
- indent,
1666
- j,
1667
- jsx,
1668
- k,
1669
- lastToken,
1670
- len,
1671
- len1,
1672
- locationToken,
1673
- lparen,
1674
- placeholderToken,
1675
- quote,
1676
- ref,
1677
- ref1,
1678
- rparen,
1679
- tag,
1680
- token,
1681
- tokensToPush,
1682
- val,
1683
- value;
1684
- ({ quote, indent, double, heregex, endOffset, jsx } = options);
1685
- if (tokens.length > 1) {
1686
- lparen = this.token('STRING_START', '(', {
1687
- length: (ref = quote != null ? quote.length : void 0) != null ? ref : 0,
1688
- data: { quote },
1689
- generated: !(quote != null ? quote.length : void 0),
1690
- });
1691
- }
1692
- firstIndex = this.tokens.length;
1693
- $ = tokens.length - 1;
1694
- for (i = j = 0, len = tokens.length; j < len; i = ++j) {
1695
- token = tokens[i];
1696
- [tag, value] = token;
1697
- switch (tag) {
1698
- case 'TOKENS':
1699
- // There are comments (and nothing else) in this interpolation.
1700
- if (value.length === 2 && (value[0].comments || value[1].comments)) {
1701
- placeholderToken = this.makeToken('JS', '', {
1702
- generated: true,
1703
- });
1704
- // Use the same location data as the first parenthesis.
1705
- placeholderToken[2] = value[0][2];
1706
- for (k = 0, len1 = value.length; k < len1; k++) {
1707
- val = value[k];
1708
- if (!val.comments) {
1709
- continue;
1710
- }
1711
- if (placeholderToken.comments == null) {
1712
- placeholderToken.comments = [];
1713
- }
1714
- placeholderToken.comments.push(...val.comments);
1715
- }
1716
- value.splice(1, 0, placeholderToken);
1717
- }
1718
- // Push all the tokens in the fake `'TOKENS'` token. These already have
1719
- // sane location data.
1720
- locationToken = value[0];
1721
- tokensToPush = value;
1722
- break;
1723
- case 'NEOSTRING':
1724
- // Convert `'NEOSTRING'` into `'STRING'`.
1725
- converted = fn.call(this, token[1], i);
1726
- if (i === 0) {
1727
- addTokenData(token, {
1728
- initialChunk: true,
1729
- });
1730
- }
1731
- if (i === $) {
1732
- addTokenData(token, {
1733
- finalChunk: true,
1734
- });
1735
- }
1736
- addTokenData(token, { indent, quote, double });
1737
- if (heregex) {
1738
- addTokenData(token, { heregex });
1739
- }
1740
- if (jsx) {
1741
- addTokenData(token, { jsx });
1742
- }
1743
- token[0] = 'STRING';
1744
- token[1] = '"' + converted + '"';
1745
- if (tokens.length === 1 && quote != null) {
1746
- token[2].first_column -= quote.length;
1747
- if (token[1].substr(-2, 1) === '\n') {
1748
- token[2].last_line += 1;
1749
- token[2].last_column = quote.length - 1;
1750
- } else {
1751
- token[2].last_column += quote.length;
1752
- if (token[1].length === 2) {
1753
- token[2].last_column -= 1;
1754
- }
1755
- }
1756
- token[2].last_column_exclusive += quote.length;
1757
- token[2].range = [token[2].range[0] - quote.length, token[2].range[1] + quote.length];
1758
- }
1759
- locationToken = token;
1760
- tokensToPush = [token];
1761
- }
1762
- this.tokens.push(...tokensToPush);
1763
- }
1764
- if (lparen) {
1765
- [lastToken] = slice.call(tokens, -1);
1766
- lparen.origin = [
1767
- 'STRING',
1768
- null,
1769
- {
1770
- first_line: lparen[2].first_line,
1771
- first_column: lparen[2].first_column,
1772
- last_line: lastToken[2].last_line,
1773
- last_column: lastToken[2].last_column,
1774
- last_line_exclusive: lastToken[2].last_line_exclusive,
1775
- last_column_exclusive: lastToken[2].last_column_exclusive,
1776
- range: [lparen[2].range[0], lastToken[2].range[1]],
1777
- },
1778
- ];
1779
- if (!(quote != null ? quote.length : void 0)) {
1780
- lparen[2] = lparen.origin[2];
1781
- }
1782
- return (rparen = this.token('STRING_END', ')', {
1783
- offset: endOffset - (quote != null ? quote : '').length,
1784
- length: (ref1 = quote != null ? quote.length : void 0) != null ? ref1 : 0,
1785
- generated: !(quote != null ? quote.length : void 0),
1786
- }));
1787
- }
1788
- }
1789
-
1790
- // Pairs up a closing token, ensuring that all listed pairs of tokens are
1791
- // correctly balanced throughout the course of the token stream.
1792
- pair(tag) {
1793
- var lastIndent, prev, ref, ref1, wanted;
1794
- (ref = this.ends), ([prev] = slice.call(ref, -1));
1795
- if (tag !== (wanted = prev != null ? prev.tag : void 0)) {
1796
- if ('OUTDENT' !== wanted) {
1797
- this.error(`unmatched ${tag}`);
1798
- }
1799
- // Auto-close `INDENT` to support syntax like this:
1800
-
1801
- // el.click((event) ->
1802
- // el.hide())
1803
-
1804
- (ref1 = this.indents), ([lastIndent] = slice.call(ref1, -1));
1805
- this.outdentToken({
1806
- moveOut: lastIndent,
1807
- noNewlines: true,
1808
- });
1809
- return this.pair(tag);
1810
- }
1811
- return this.ends.pop();
1812
- }
1813
-
1814
- // Helpers
1815
- // -------
1816
-
1817
- // Compensate for the things we strip out initially (e.g. carriage returns)
1818
- // so that location data stays accurate with respect to the original source file.
1819
- getLocationDataCompensation(start, end) {
1820
- var compensation, current, initialEnd, totalCompensation;
1821
- totalCompensation = 0;
1822
- initialEnd = end;
1823
- current = start;
1824
- while (current <= end) {
1825
- if (current === end && start !== initialEnd) {
1826
- break;
1827
- }
1828
- compensation = this.locationDataCompensations[current];
1829
- if (compensation != null) {
1830
- totalCompensation += compensation;
1831
- end += compensation;
1832
- }
1833
- current++;
1834
- }
1835
- return totalCompensation;
1836
- }
1837
-
1838
- // Returns the line and column number from an offset into the current chunk.
1839
-
1840
- // `offset` is a number of characters into `@chunk`.
1841
- getLineAndColumnFromChunk(offset) {
1842
- var column, columnCompensation, compensation, lastLine, lineCount, previousLinesCompensation, ref, string;
1843
- compensation = this.getLocationDataCompensation(this.chunkOffset, this.chunkOffset + offset);
1844
- if (offset === 0) {
1845
- return [this.chunkLine, this.chunkColumn + compensation, this.chunkOffset + compensation];
1846
- }
1847
- if (offset >= this.chunk.length) {
1848
- string = this.chunk;
1849
- } else {
1850
- string = this.chunk.slice(0, +(offset - 1) + 1 || 9e9);
1851
- }
1852
- lineCount = count(string, '\n');
1853
- column = this.chunkColumn;
1854
- if (lineCount > 0) {
1855
- (ref = string.split('\n')), ([lastLine] = slice.call(ref, -1));
1856
- column = lastLine.length;
1857
- previousLinesCompensation = this.getLocationDataCompensation(this.chunkOffset, this.chunkOffset + offset - column);
1858
- if (previousLinesCompensation < 0) {
1859
- // Don't recompensate for initially inserted newline.
1860
- previousLinesCompensation = 0;
1861
- }
1862
- columnCompensation = this.getLocationDataCompensation(
1863
- this.chunkOffset + offset + previousLinesCompensation - column,
1864
- this.chunkOffset + offset + previousLinesCompensation,
1865
- );
1866
- } else {
1867
- column += string.length;
1868
- columnCompensation = compensation;
1869
- }
1870
- return [this.chunkLine + lineCount, column + columnCompensation, this.chunkOffset + offset + compensation];
1871
- }
1872
-
1873
- makeLocationData({ offsetInChunk, length }) {
1874
- var endOffset, lastCharacter, locationData;
1875
- locationData = {
1876
- range: [],
1877
- };
1878
- [locationData.first_line, locationData.first_column, locationData.range[0]] = this.getLineAndColumnFromChunk(offsetInChunk);
1879
- // Use length - 1 for the final offset - we’re supplying the last_line and the last_column,
1880
- // so if last_column == first_column, then we’re looking at a character of length 1.
1881
- lastCharacter = length > 0 ? length - 1 : 0;
1882
- [locationData.last_line, locationData.last_column, endOffset] = this.getLineAndColumnFromChunk(offsetInChunk + lastCharacter);
1883
- [locationData.last_line_exclusive, locationData.last_column_exclusive] = this.getLineAndColumnFromChunk(
1884
- offsetInChunk + lastCharacter + (length > 0 ? 1 : 0),
1885
- );
1886
- locationData.range[1] = length > 0 ? endOffset + 1 : endOffset;
1887
- return locationData;
1888
- }
1889
-
1890
- // Same as `token`, except this just returns the token without adding it
1891
- // to the results.
1892
- makeToken(tag, value, { offset: offsetInChunk = 0, length = value.length, origin, generated, indentSize } = {}) {
1893
- var token;
1894
- token = [tag, value, this.makeLocationData({ offsetInChunk, length })];
1895
- if (origin) {
1896
- token.origin = origin;
1897
- }
1898
- if (generated) {
1899
- token.generated = true;
1900
- }
1901
- if (indentSize != null) {
1902
- token.indentSize = indentSize;
1903
- }
1904
- return token;
1905
- }
1906
-
1907
- // Add a token to the results.
1908
- // `offset` is the offset into the current `@chunk` where the token starts.
1909
- // `length` is the length of the token in the `@chunk`, after the offset. If
1910
- // not specified, the length of `value` will be used.
1911
-
1912
- // Returns the new token.
1913
- token(tag, value, { offset, length, origin, data, generated, indentSize } = {}) {
1914
- var token;
1915
- token = this.makeToken(tag, value, { offset, length, origin, generated, indentSize });
1916
- if (data) {
1917
- addTokenData(token, data);
1918
- }
1919
- this.tokens.push(token);
1920
- return token;
1921
- }
1922
-
1923
- // Peek at the last tag in the token stream.
1924
- tag() {
1925
- var ref, token;
1926
- (ref = this.tokens), ([token] = slice.call(ref, -1));
1927
- return token != null ? token[0] : void 0;
1928
- }
1929
-
1930
- // Peek at the last value in the token stream.
1931
- value(useOrigin = false) {
1932
- var ref, token;
1933
- (ref = this.tokens), ([token] = slice.call(ref, -1));
1934
- if (useOrigin && (token != null ? token.origin : void 0) != null) {
1935
- return token.origin[1];
1936
- } else {
1937
- return token != null ? token[1] : void 0;
1938
- }
1939
- }
1940
-
1941
- // Get the previous token in the token stream.
1942
- prev() {
1943
- return this.tokens[this.tokens.length - 1];
1944
- }
1945
-
1946
- // Are we in the midst of an unfinished expression?
1947
- unfinished() {
1948
- var ref;
1949
- return LINE_CONTINUER.test(this.chunk) || ((ref = this.tag()), indexOf.call(UNFINISHED, ref) >= 0);
1950
- }
1951
-
1952
- validateUnicodeCodePointEscapes(str, options) {
1953
- return replaceUnicodeCodePointEscapes(str, merge(options, { error: this.error }));
1954
- }
1955
-
1956
- // Validates escapes in strings and regexes.
1957
- validateEscapes(str, options = {}) {
1958
- var before, hex, invalidEscape, invalidEscapeRegex, match, message, octal, ref, unicode, unicodeCodePoint;
1959
- invalidEscapeRegex = options.isRegex ? REGEX_INVALID_ESCAPE : STRING_INVALID_ESCAPE;
1960
- match = invalidEscapeRegex.exec(str);
1961
- if (!match) {
1962
- return;
1963
- }
1964
- match[0], (before = match[1]), (octal = match[2]), (hex = match[3]), (unicodeCodePoint = match[4]), (unicode = match[5]);
1965
- message = octal ? 'octal escape sequences are not allowed' : 'invalid escape sequence';
1966
- invalidEscape = `\\${octal || hex || unicodeCodePoint || unicode}`;
1967
- return this.error(`${message} ${invalidEscape}`, {
1968
- offset: ((ref = options.offsetInChunk) != null ? ref : 0) + match.index + before.length,
1969
- length: invalidEscape.length,
1970
- });
1971
- }
1972
-
1973
- suppressSemicolons() {
1974
- var ref, ref1, results;
1975
- results = [];
1976
- while (this.value() === ';') {
1977
- this.tokens.pop();
1978
- if (((ref = (ref1 = this.prev()) != null ? ref1[0] : void 0), indexOf.call(['=', ...UNFINISHED], ref) >= 0)) {
1979
- results.push(this.error('unexpected ;'));
1980
- } else {
1981
- results.push(void 0);
1982
- }
1983
- }
1984
- return results;
1985
- }
1986
-
1987
- error(message, options = {}) {
1988
- var first_column, first_line, location, ref, ref1;
1989
- location =
1990
- 'first_line' in options
1991
- ? options
1992
- : (([first_line, first_column] = this.getLineAndColumnFromChunk((ref = options.offset) != null ? ref : 0)),
1993
- {
1994
- first_line,
1995
- first_column,
1996
- last_column: first_column + ((ref1 = options.length) != null ? ref1 : 1) - 1,
1997
- });
1998
- return throwSyntaxError(message, location);
1999
- }
2000
- };
2001
-
2002
- // Helper functions
2003
- // ----------------
2004
- isUnassignable = function (name, displayName = name) {
2005
- switch (false) {
2006
- case indexOf.call([...JS_KEYWORDS, ...COFFEE_KEYWORDS], name) < 0:
2007
- return `keyword '${displayName}' can't be assigned`;
2008
- case indexOf.call(STRICT_PROSCRIBED, name) < 0:
2009
- return `'${displayName}' can't be assigned`;
2010
- case indexOf.call(RESERVED, name) < 0:
2011
- return `reserved word '${displayName}' can't be assigned`;
2012
- default:
2013
- return false;
2014
- }
2015
- };
2016
-
2017
- exports.isUnassignable = isUnassignable;
2018
-
2019
- // `from` isn’t a CoffeeScript keyword, but it behaves like one in `import` and
2020
- // `export` statements (handled above) and in the declaration line of a `for`
2021
- // loop. Try to detect when `from` is a variable identifier and when it is this
2022
- // “sometimes” keyword.
2023
- isForFrom = function (prev) {
2024
- var ref;
2025
- // `for i from iterable`
2026
- if (prev[0] === 'IDENTIFIER') {
2027
- return true;
2028
- // `for from…`
2029
- } else if (prev[0] === 'FOR') {
2030
- return false;
2031
- // `for {from}…`, `for [from]…`, `for {a, from}…`, `for {a: from}…`
2032
- } else if ((ref = prev[1]) === '{' || ref === '[' || ref === ',' || ref === ':') {
2033
- return false;
2034
- } else {
2035
- return true;
2036
- }
2037
- };
2038
-
2039
- addTokenData = function (token, data) {
2040
- return Object.assign(token.data != null ? token.data : (token.data = {}), data);
2041
- };
2042
-
2043
- // Constants
2044
- // ---------
2045
-
2046
- // Keywords that CoffeeScript shares in common with JavaScript.
2047
- JS_KEYWORDS = [
2048
- 'true',
2049
- 'false',
2050
- 'null',
2051
- 'this',
2052
- 'new',
2053
- 'delete',
2054
- 'typeof',
2055
- 'in',
2056
- 'instanceof',
2057
- 'return',
2058
- 'throw',
2059
- 'break',
2060
- 'continue',
2061
- 'debugger',
2062
- 'yield',
2063
- 'await',
2064
- 'if',
2065
- 'else',
2066
- 'switch',
2067
- 'for',
2068
- 'while',
2069
- 'do',
2070
- 'try',
2071
- 'catch',
2072
- 'finally',
2073
- 'class',
2074
- 'extends',
2075
- 'super',
2076
- 'import',
2077
- 'export',
2078
- 'default',
2079
- ];
2080
-
2081
- // CoffeeScript-only keywords.
2082
- COFFEE_KEYWORDS = ['undefined', 'Infinity', 'NaN', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when'];
2083
-
2084
- COFFEE_ALIAS_MAP = {
2085
- and: '&&',
2086
- or: '||',
2087
- is: '==',
2088
- isnt: '!=',
2089
- not: '!',
2090
- yes: 'true',
2091
- no: 'false',
2092
- on: 'true',
2093
- off: 'false',
2094
- };
2095
-
2096
- COFFEE_ALIASES = (function () {
2097
- var results;
2098
- results = [];
2099
- for (key in COFFEE_ALIAS_MAP) {
2100
- results.push(key);
2101
- }
2102
- return results;
2103
- })();
2104
-
2105
- COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat(COFFEE_ALIASES);
2106
-
2107
- // The list of keywords that are reserved by JavaScript, but not used, or are
2108
- // used by CoffeeScript internally. We throw an error when these are encountered,
2109
- // to avoid having a JavaScript error at runtime.
2110
- RESERVED = [
2111
- 'case',
2112
- 'function',
2113
- 'var',
2114
- 'void',
2115
- 'with',
2116
- 'const',
2117
- 'let',
2118
- 'enum',
2119
- 'native',
2120
- 'implements',
2121
- 'interface',
2122
- 'package',
2123
- 'private',
2124
- 'protected',
2125
- 'public',
2126
- 'static',
2127
- ];
2128
-
2129
- STRICT_PROSCRIBED = ['arguments', 'eval'];
2130
-
2131
- // The superset of both JavaScript keywords and reserved words, none of which may
2132
- // be used as identifiers or properties.
2133
- exports.JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED);
2134
-
2135
- // The character code of the nasty Microsoft madness otherwise known as the BOM.
2136
- BOM = 65279;
2137
-
2138
- // Token matching regexes.
2139
- IDENTIFIER = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+)([^\n\S]*:(?!:))?/; // Is this a property name?
2140
-
2141
- // Like `IDENTIFIER`, but includes `-`s
2142
- JSX_IDENTIFIER_PART = /(?:(?!\s)[\-$\w\x7f-\uffff])+/.source;
2143
-
2144
- // In https://facebook.github.io/jsx/ spec, JSXElementName can be
2145
- // JSXIdentifier, JSXNamespacedName (JSXIdentifier : JSXIdentifier), or
2146
- // JSXMemberExpression (two or more JSXIdentifier connected by `.`s).
2147
- JSX_IDENTIFIER = RegExp(
2148
- `^(?![\\d<])(${
2149
- JSX_IDENTIFIER_PART // Must not start with `<`.
2150
- // JSXNamespacedName
2151
- // JSXMemberExpression
2152
- }(?:\\s*:\\s*${JSX_IDENTIFIER_PART}|(?:\\s*\\.\\s*${JSX_IDENTIFIER_PART})+)?)`,
2153
- );
2154
-
2155
- // Fragment: <></>
2156
- JSX_FRAGMENT_IDENTIFIER = /^()>/; // Ends immediately with `>`.
2157
-
2158
- // In https://facebook.github.io/jsx/ spec, JSXAttributeName can be either
2159
- // JSXIdentifier or JSXNamespacedName which is JSXIdentifier : JSXIdentifier
2160
- JSX_ATTRIBUTE = RegExp(
2161
- `^(?!\\d)(${
2162
- JSX_IDENTIFIER_PART // JSXNamespacedName
2163
- // Is this an attribute with a value?
2164
- }(?:\\s*:\\s*${JSX_IDENTIFIER_PART})?)([^\\S]*=(?!=))?`,
2165
- );
2166
-
2167
- NUMBER =
2168
- /^0b[01](?:_?[01])*n?|^0o[0-7](?:_?[0-7])*n?|^0x[\da-f](?:_?[\da-f])*n?|^\d+(?:_\d+)*n|^(?:\d+(?:_\d+)*)?\.?\d+(?:_\d+)*(?:e[+-]?\d+(?:_\d+)*)?/i; // binary
2169
- // octal
2170
- // hex
2171
- // decimal bigint
2172
- // decimal
2173
- // decimal without support for numeric literal separators for reference:
2174
- // \d*\.?\d+ (?:e[+-]?\d+)?
2175
-
2176
- OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?(\.|::)|\.{2,3})/; // function
2177
- // compound assign / compare
2178
- // zero-fill right shift
2179
- // doubles
2180
- // logic / shift / power / floor division / modulo
2181
- // soak access
2182
- // range or splat
2183
-
2184
- WHITESPACE = /^[^\n\S]+/;
2185
-
2186
- COMMENT = /^(\s*)###([^#][\s\S]*?)(?:###([^\n\S]*)|###$)|^((?:\s*#(?!##[^#]).*)+)/;
2187
-
2188
- CODE = /^[-=]>/;
2189
-
2190
- MULTI_DENT = /^(?:\n[^\n\S]*)+/;
2191
-
2192
- JSTOKEN = /^`(?!``)((?:[^`\\]|\\[\s\S])*)`/;
2193
-
2194
- HERE_JSTOKEN = /^```((?:[^`\\]|\\[\s\S]|`(?!``))*)```/;
2195
-
2196
- // String-matching-regexes.
2197
- STRING_START = /^(?:'''|"""|'|")/;
2198
-
2199
- STRING_SINGLE = /^(?:[^\\']|\\[\s\S])*/;
2200
-
2201
- STRING_DOUBLE = /^(?:[^\\"#]|\\[\s\S]|\#(?!\{))*/;
2202
-
2203
- HEREDOC_SINGLE = /^(?:[^\\']|\\[\s\S]|'(?!''))*/;
2204
-
2205
- HEREDOC_DOUBLE = /^(?:[^\\"#]|\\[\s\S]|"(?!"")|\#(?!\{))*/;
2206
-
2207
- INSIDE_JSX = /^(?:[^\{<])*/; // Start of CoffeeScript interpolation. // Similar to `HEREDOC_DOUBLE` but there is no escaping.
2208
- // Maybe JSX tag (`<` not allowed even if bare).
2209
-
2210
- JSX_INTERPOLATION = /^(?:\{|<(?!\/))/; // CoffeeScript interpolation.
2211
- // JSX opening tag.
2212
-
2213
- HEREDOC_INDENT = /\n+([^\n\S]*)(?=\S)/g;
2214
-
2215
- // Regex-matching-regexes.
2216
- REGEX = /^\/(?!\/)((?:[^[\/\n\\]|\\[^\n]|\[(?:\\[^\n]|[^\]\n\\])*\])*)(\/)?/; // Every other thing.
2217
- // Anything but newlines escaped.
2218
- // Character class.
2219
-
2220
- REGEX_FLAGS = /^\w*/;
2221
-
2222
- VALID_FLAGS = /^(?!.*(.).*\1)[gimsuy]*$/;
2223
-
2224
- HEREGEX = /^(?:[^\\\/#\s]|\\[\s\S]|\/(?!\/\/)|\#(?!\{)|\s+(?:#(?!\{).*)?)*/; // Match any character, except those that need special handling below.
2225
- // Match `\` followed by any character.
2226
- // Match any `/` except `///`.
2227
- // Match `#` which is not part of interpolation, e.g. `#{}`.
2228
- // Comments consume everything until the end of the line, including `///`.
2229
-
2230
- HEREGEX_COMMENT = /(\s+)(#(?!{).*)/gm;
2231
-
2232
- REGEX_ILLEGAL = /^(\/|\/{3}\s*)(\*)/;
2233
-
2234
- POSSIBLY_DIVISION = /^\/=?\s/;
2235
-
2236
- // Other regexes.
2237
- HERECOMMENT_ILLEGAL = /\*\//;
2238
-
2239
- LINE_CONTINUER = /^\s*(?:,|\??\.(?![.\d])|\??::)/;
2240
-
2241
- STRING_INVALID_ESCAPE =
2242
- /((?:^|[^\\])(?:\\\\)*)\\(?:(0\d|[1-7])|(x(?![\da-fA-F]{2}).{0,2})|(u\{(?![\da-fA-F]{1,}\})[^}]*\}?)|(u(?!\{|[\da-fA-F]{4}).{0,4}))/; // Make sure the escape isn’t escaped.
2243
- // octal escape
2244
- // hex escape
2245
- // unicode code point escape
2246
- // unicode escape
2247
-
2248
- REGEX_INVALID_ESCAPE =
2249
- /((?:^|[^\\])(?:\\\\)*)\\(?:(0\d)|(x(?![\da-fA-F]{2}).{0,2})|(u\{(?![\da-fA-F]{1,}\})[^}]*\}?)|(u(?!\{|[\da-fA-F]{4}).{0,4}))/; // Make sure the escape isn’t escaped.
2250
- // octal escape
2251
- // hex escape
2252
- // unicode code point escape
2253
- // unicode escape
2254
-
2255
- TRAILING_SPACES = /\s+$/;
2256
-
2257
- // Compound assignment tokens.
2258
- COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=', '**=', '//=', '%%='];
2259
-
2260
- // Unary tokens.
2261
- UNARY = ['NEW', 'TYPEOF', 'DELETE'];
2262
-
2263
- UNARY_MATH = ['!', '~'];
2264
-
2265
- // Bit-shifting tokens.
2266
- SHIFT = ['<<', '>>', '>>>'];
2267
-
2268
- // Comparison tokens.
2269
- COMPARE = ['==', '!=', '<', '>', '<=', '>='];
2270
-
2271
- // Mathematical tokens.
2272
- MATH = ['*', '/', '%', '//', '%%'];
2273
-
2274
- // Relational tokens that are negatable with `not` prefix.
2275
- RELATION = ['IN', 'OF', 'INSTANCEOF'];
2276
-
2277
- // Boolean tokens.
2278
- BOOL = ['TRUE', 'FALSE'];
2279
-
2280
- // Tokens which could legitimately be invoked or indexed. An opening
2281
- // parentheses or bracket following these tokens will be recorded as the start
2282
- // of a function invocation or indexing operation.
2283
- CALLABLE = ['IDENTIFIER', 'PROPERTY', ')', ']', '?', '@', 'THIS', 'SUPER', 'DYNAMIC_IMPORT'];
2284
-
2285
- INDEXABLE = CALLABLE.concat([
2286
- 'NUMBER',
2287
- 'INFINITY',
2288
- 'NAN',
2289
- 'STRING',
2290
- 'STRING_END',
2291
- 'REGEX',
2292
- 'REGEX_END',
2293
- 'BOOL',
2294
- 'NULL',
2295
- 'UNDEFINED',
2296
- '}',
2297
- '::',
2298
- ]);
2299
-
2300
- // Tokens which can be the left-hand side of a less-than comparison, i.e. `a<b`.
2301
- COMPARABLE_LEFT_SIDE = ['IDENTIFIER', ')', ']', 'NUMBER'];
2302
-
2303
- // Tokens which a regular expression will never immediately follow (except spaced
2304
- // CALLABLEs in some cases), but which a division operator can.
2305
-
2306
- // See: http://www-archive.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
2307
- NOT_REGEX = INDEXABLE.concat(['++', '--']);
2308
-
2309
- // Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
2310
- // occurs at the start of a line. We disambiguate these from trailing whens to
2311
- // avoid an ambiguity in the grammar.
2312
- LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
2313
-
2314
- // Additional indent in front of these is ignored.
2315
- INDENTABLE_CLOSERS = [')', '}', ']'];
2316
- }).call(this);