1 |
|
2 | import {TokenType as tt} from "../parser/tokenizer/types";
|
3 |
|
4 | import Transformer from "./Transformer";
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | export default class OptionalChainingNullishTransformer extends Transformer {
|
16 | constructor( tokens, nameManager) {
|
17 | super();this.tokens = tokens;this.nameManager = nameManager;;
|
18 | }
|
19 |
|
20 | process() {
|
21 | if (this.tokens.matches1(tt.nullishCoalescing)) {
|
22 | const token = this.tokens.currentToken();
|
23 | if (this.tokens.tokens[token.nullishStartIndex].isAsyncOperation) {
|
24 | this.tokens.replaceTokenTrimmingLeftWhitespace(", async () => (");
|
25 | } else {
|
26 | this.tokens.replaceTokenTrimmingLeftWhitespace(", () => (");
|
27 | }
|
28 | return true;
|
29 | }
|
30 | if (this.tokens.matches1(tt._delete)) {
|
31 | const nextToken = this.tokens.tokenAtRelativeIndex(1);
|
32 | if (nextToken.isOptionalChainStart) {
|
33 | this.tokens.removeInitialToken();
|
34 | return true;
|
35 | }
|
36 | }
|
37 | const token = this.tokens.currentToken();
|
38 | const chainStart = token.subscriptStartIndex;
|
39 | if (
|
40 | chainStart != null &&
|
41 | this.tokens.tokens[chainStart].isOptionalChainStart &&
|
42 |
|
43 |
|
44 | this.tokens.tokenAtRelativeIndex(-1).type !== tt._super
|
45 | ) {
|
46 | const param = this.nameManager.claimFreeName("_");
|
47 | let arrowStartSnippet;
|
48 | if (
|
49 | chainStart > 0 &&
|
50 | this.tokens.matches1AtIndex(chainStart - 1, tt._delete) &&
|
51 | this.isLastSubscriptInChain()
|
52 | ) {
|
53 |
|
54 |
|
55 |
|
56 | arrowStartSnippet = `${param} => delete ${param}`;
|
57 | } else {
|
58 | arrowStartSnippet = `${param} => ${param}`;
|
59 | }
|
60 | if (this.tokens.tokens[chainStart].isAsyncOperation) {
|
61 | arrowStartSnippet = `async ${arrowStartSnippet}`;
|
62 | }
|
63 | if (
|
64 | this.tokens.matches2(tt.questionDot, tt.parenL) ||
|
65 | this.tokens.matches2(tt.questionDot, tt.lessThan)
|
66 | ) {
|
67 | if (this.justSkippedSuper()) {
|
68 | this.tokens.appendCode(".bind(this)");
|
69 | }
|
70 | this.tokens.replaceTokenTrimmingLeftWhitespace(`, 'optionalCall', ${arrowStartSnippet}`);
|
71 | } else if (this.tokens.matches2(tt.questionDot, tt.bracketL)) {
|
72 | this.tokens.replaceTokenTrimmingLeftWhitespace(`, 'optionalAccess', ${arrowStartSnippet}`);
|
73 | } else if (this.tokens.matches1(tt.questionDot)) {
|
74 | this.tokens.replaceTokenTrimmingLeftWhitespace(`, 'optionalAccess', ${arrowStartSnippet}.`);
|
75 | } else if (this.tokens.matches1(tt.dot)) {
|
76 | this.tokens.replaceTokenTrimmingLeftWhitespace(`, 'access', ${arrowStartSnippet}.`);
|
77 | } else if (this.tokens.matches1(tt.bracketL)) {
|
78 | this.tokens.replaceTokenTrimmingLeftWhitespace(`, 'access', ${arrowStartSnippet}[`);
|
79 | } else if (this.tokens.matches1(tt.parenL)) {
|
80 | if (this.justSkippedSuper()) {
|
81 | this.tokens.appendCode(".bind(this)");
|
82 | }
|
83 | this.tokens.replaceTokenTrimmingLeftWhitespace(`, 'call', ${arrowStartSnippet}(`);
|
84 | } else {
|
85 | throw new Error("Unexpected subscript operator in optional chain.");
|
86 | }
|
87 | return true;
|
88 | }
|
89 | return false;
|
90 | }
|
91 |
|
92 | |
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 | isLastSubscriptInChain() {
|
102 | let depth = 0;
|
103 | for (let i = this.tokens.currentIndex() + 1; ; i++) {
|
104 | if (i >= this.tokens.tokens.length) {
|
105 | throw new Error("Reached the end of the code while finding the end of the access chain.");
|
106 | }
|
107 | if (this.tokens.tokens[i].isOptionalChainStart) {
|
108 | depth++;
|
109 | } else if (this.tokens.tokens[i].isOptionalChainEnd) {
|
110 | depth--;
|
111 | }
|
112 | if (depth < 0) {
|
113 | return true;
|
114 | }
|
115 |
|
116 |
|
117 | if (depth === 0 && this.tokens.tokens[i].subscriptStartIndex != null) {
|
118 | return false;
|
119 | }
|
120 | }
|
121 | }
|
122 |
|
123 | |
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 | justSkippedSuper() {
|
131 | let depth = 0;
|
132 | let index = this.tokens.currentIndex() - 1;
|
133 | while (true) {
|
134 | if (index < 0) {
|
135 | throw new Error(
|
136 | "Reached the start of the code while finding the start of the access chain.",
|
137 | );
|
138 | }
|
139 | if (this.tokens.tokens[index].isOptionalChainStart) {
|
140 | depth--;
|
141 | } else if (this.tokens.tokens[index].isOptionalChainEnd) {
|
142 | depth++;
|
143 | }
|
144 | if (depth < 0) {
|
145 | return false;
|
146 | }
|
147 |
|
148 |
|
149 | if (depth === 0 && this.tokens.tokens[index].subscriptStartIndex != null) {
|
150 | return this.tokens.tokens[index - 1].type === tt._super;
|
151 | }
|
152 | index--;
|
153 | }
|
154 | }
|
155 | }
|