@gi-tcg/gts-transpiler 0.3.2 → 0.3.3
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.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +214 -384
- package/package.json +6 -4
- package/src/parse/gts_plugin.ts +2 -2
- package/src/parse/index.ts +2 -0
- package/src/parse/loose_plugin.ts +1 -1
- package/src/parse/record_call_lparen_plugin.ts +17 -20
- package/src/transform/gts.ts +15 -1
- package/src/transform/index.ts +3 -3
- package/src/transform/volar/collect_tokens.ts +28 -34
- package/src/transform/volar/index.ts +46 -46
- package/src/transform/volar/mappings.ts +5 -352
- package/src/transform/volar/printer.ts +101 -117
- package/src/transform/volar/replacements.ts +75 -47
- package/src/transform/volar/walker.ts +87 -68
- package/src/types.ts +3 -9
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { decode } from "@jridgewell/sourcemap-codec";
|
|
2
1
|
import type { CodeInformation, CodeMapping } from "@volar/language-core";
|
|
3
|
-
import type { LeafToken } from "./collect_tokens.ts";
|
|
4
2
|
|
|
5
3
|
export interface VolarMappingResult {
|
|
6
4
|
code: string;
|
|
7
5
|
mappings: CodeMapping[];
|
|
8
6
|
}
|
|
9
7
|
|
|
10
|
-
const DEFAULT_VOLAR_MAPPING_DATA: CodeInformation = {
|
|
8
|
+
export const DEFAULT_VOLAR_MAPPING_DATA: CodeInformation = {
|
|
11
9
|
completion: true,
|
|
12
10
|
format: true,
|
|
13
11
|
navigation: true,
|
|
@@ -15,354 +13,9 @@ const DEFAULT_VOLAR_MAPPING_DATA: CodeInformation = {
|
|
|
15
13
|
structure: true,
|
|
16
14
|
verification: true,
|
|
17
15
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
mappings: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface CodePosition {
|
|
24
|
-
line: number;
|
|
25
|
-
column: number;
|
|
26
|
-
end_line: number;
|
|
27
|
-
end_column: number;
|
|
28
|
-
code: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
type LineOffsets = number[];
|
|
32
|
-
|
|
33
|
-
type CodeToGeneratedMap = Map<string, CodePosition[]>;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Convert byte offset to line/column
|
|
37
|
-
* @param offset
|
|
38
|
-
* @param line_offsets
|
|
39
|
-
* @returns */
|
|
40
|
-
export const offset_to_line_col = (
|
|
41
|
-
offset: number,
|
|
42
|
-
line_offsets: LineOffsets,
|
|
43
|
-
): { line: number; column: number } => {
|
|
44
|
-
// Binary search
|
|
45
|
-
let left = 0;
|
|
46
|
-
let right = line_offsets.length - 1;
|
|
47
|
-
let line = 1;
|
|
48
|
-
|
|
49
|
-
while (left <= right) {
|
|
50
|
-
const mid = Math.floor((left + right) / 2);
|
|
51
|
-
if (
|
|
52
|
-
offset >= line_offsets[mid] &&
|
|
53
|
-
(mid === line_offsets.length - 1 || offset < line_offsets[mid + 1])
|
|
54
|
-
) {
|
|
55
|
-
line = mid + 1;
|
|
56
|
-
break;
|
|
57
|
-
} else if (offset < line_offsets[mid]) {
|
|
58
|
-
right = mid - 1;
|
|
59
|
-
} else {
|
|
60
|
-
left = mid + 1;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const column = offset - line_offsets[line - 1];
|
|
65
|
-
return { line, column };
|
|
16
|
+
export const VERIFICATION_ONLY_MAPPING_DATA: CodeInformation = {
|
|
17
|
+
verification: true,
|
|
66
18
|
};
|
|
67
19
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
* Applies post-processing adjustments during map building for efficiency
|
|
71
|
-
* @param source_map - The source map object from esrap (v3 format)
|
|
72
|
-
* @param line_offsets - Pre-computed line offsets array
|
|
73
|
-
* @param generated_code - The final generated code (after post-processing)
|
|
74
|
-
* @returns source-to-generated map
|
|
75
|
-
*/
|
|
76
|
-
export function buildSrcToGenMap(
|
|
77
|
-
source_map: SourceMap,
|
|
78
|
-
line_offsets: LineOffsets,
|
|
79
|
-
generated_code: string,
|
|
80
|
-
): CodeToGeneratedMap {
|
|
81
|
-
const map: CodeToGeneratedMap = new Map();
|
|
82
|
-
|
|
83
|
-
// Decode the VLQ-encoded mappings string
|
|
84
|
-
const decoded = decode(source_map.mappings);
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Convert line/column position to byte offset
|
|
88
|
-
* @param {number} line - 1-based line number
|
|
89
|
-
* @param {number} column - 0-based column number
|
|
90
|
-
* @returns {number} Byte offset
|
|
91
|
-
*/
|
|
92
|
-
const line_col_to_byte_offset = (line: number, column: number) => {
|
|
93
|
-
return line_offsets[line - 1] + column;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Apply post-processing adjustments to all segments first
|
|
97
|
-
const adjusted_segments: {
|
|
98
|
-
line: number;
|
|
99
|
-
column: number;
|
|
100
|
-
sourceLine: number;
|
|
101
|
-
sourceColumn: number;
|
|
102
|
-
}[][] = [];
|
|
103
|
-
|
|
104
|
-
for (
|
|
105
|
-
let generated_line = 0;
|
|
106
|
-
generated_line < decoded.length;
|
|
107
|
-
generated_line++
|
|
108
|
-
) {
|
|
109
|
-
const line = decoded[generated_line];
|
|
110
|
-
adjusted_segments[generated_line] = [];
|
|
111
|
-
|
|
112
|
-
for (const segment of line) {
|
|
113
|
-
if (segment.length >= 4) {
|
|
114
|
-
let adjusted_line = generated_line + 1;
|
|
115
|
-
let adjusted_column = segment[0];
|
|
116
|
-
adjusted_segments[generated_line].push({
|
|
117
|
-
line: adjusted_line,
|
|
118
|
-
column: adjusted_column,
|
|
119
|
-
sourceLine: segment[2]!,
|
|
120
|
-
sourceColumn: segment[3]!,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Now build the map using adjusted positions
|
|
127
|
-
for (let line_idx = 0; line_idx < adjusted_segments.length; line_idx++) {
|
|
128
|
-
const line_segments = adjusted_segments[line_idx];
|
|
129
|
-
|
|
130
|
-
for (let seg_idx = 0; seg_idx < line_segments.length; seg_idx++) {
|
|
131
|
-
const segment = line_segments[seg_idx];
|
|
132
|
-
const line = segment.line;
|
|
133
|
-
const column = segment.column;
|
|
134
|
-
|
|
135
|
-
// Determine end position using next segment
|
|
136
|
-
let end_line = line;
|
|
137
|
-
let end_column = column;
|
|
138
|
-
|
|
139
|
-
// Look for next segment to determine end position
|
|
140
|
-
if (seg_idx + 1 < line_segments.length) {
|
|
141
|
-
// Next segment on same line
|
|
142
|
-
const next_segment = line_segments[seg_idx + 1];
|
|
143
|
-
end_line = next_segment.line;
|
|
144
|
-
end_column = next_segment.column;
|
|
145
|
-
} else if (
|
|
146
|
-
line_idx + 1 < adjusted_segments.length &&
|
|
147
|
-
adjusted_segments[line_idx + 1].length > 0
|
|
148
|
-
) {
|
|
149
|
-
// Look at first segment of next line
|
|
150
|
-
const next_segment = adjusted_segments[line_idx + 1][0];
|
|
151
|
-
end_line = next_segment.line;
|
|
152
|
-
end_column = next_segment.column;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Extract code snippet
|
|
156
|
-
const start_offset = line_col_to_byte_offset(line, column);
|
|
157
|
-
const end_offset = line_col_to_byte_offset(end_line, end_column);
|
|
158
|
-
const code_snippet = generated_code.slice(start_offset, end_offset);
|
|
159
|
-
|
|
160
|
-
// Create key from source position (1-indexed line, 0-indexed column)
|
|
161
|
-
segment.sourceLine += 1;
|
|
162
|
-
const key = `${segment.sourceLine}:${segment.sourceColumn}`;
|
|
163
|
-
|
|
164
|
-
// Store adjusted generated position with code snippet
|
|
165
|
-
const gen_pos = {
|
|
166
|
-
line,
|
|
167
|
-
column,
|
|
168
|
-
end_line,
|
|
169
|
-
end_column,
|
|
170
|
-
code: code_snippet,
|
|
171
|
-
metadata: {},
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
if (!map.has(key)) {
|
|
175
|
-
map.set(key, []);
|
|
176
|
-
}
|
|
177
|
-
map.get(key)!.push(gen_pos);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return map;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Look up generated position for a given source position
|
|
186
|
-
* @param src_line - 1-based line number in source
|
|
187
|
-
* @param src_column - 0-based column number in source
|
|
188
|
-
* @param srcToGenMap - Lookup map
|
|
189
|
-
* @returns Generated position
|
|
190
|
-
*/
|
|
191
|
-
export function getGeneratedPosition(
|
|
192
|
-
src_line: number,
|
|
193
|
-
src_column: number,
|
|
194
|
-
srcToGenMap: CodeToGeneratedMap,
|
|
195
|
-
): CodePosition[] {
|
|
196
|
-
const key = `${src_line}:${src_column}`;
|
|
197
|
-
const positions = srcToGenMap.get(key);
|
|
198
|
-
|
|
199
|
-
// If multiple generated positions map to same source, return the first
|
|
200
|
-
return positions || [];
|
|
201
|
-
}
|
|
202
|
-
// Helper to create a line-to-offset lookup table
|
|
203
|
-
function createLineOffsets(content: string): number[] {
|
|
204
|
-
const lines = content.split("\n");
|
|
205
|
-
const offsets: number[] = [];
|
|
206
|
-
let currentOffset = 0;
|
|
207
|
-
|
|
208
|
-
for (const line of lines) {
|
|
209
|
-
offsets.push(currentOffset);
|
|
210
|
-
// +1 for the newline character (handle \r\n vs \n if necessary)
|
|
211
|
-
currentOffset += line.length + 1;
|
|
212
|
-
}
|
|
213
|
-
return offsets;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Convert line/column to byte offset
|
|
218
|
-
* @param line
|
|
219
|
-
* @param column
|
|
220
|
-
* @param line_offsets
|
|
221
|
-
* @returns
|
|
222
|
-
*/
|
|
223
|
-
function locToOffset(
|
|
224
|
-
line: number,
|
|
225
|
-
column: number,
|
|
226
|
-
line_offsets: number[],
|
|
227
|
-
): number {
|
|
228
|
-
if (line < 1 || line > line_offsets.length) {
|
|
229
|
-
// throw new Error(
|
|
230
|
-
// `Location line or line offsets length is out of bounds, line: ${line}, line offsets length: ${line_offsets.length}`
|
|
231
|
-
// );
|
|
232
|
-
}
|
|
233
|
-
return line_offsets[line - 1] + column;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export function convertToVolarMappings(
|
|
237
|
-
generated: string,
|
|
238
|
-
source: string,
|
|
239
|
-
sourceMap: SourceMap,
|
|
240
|
-
tokens: LeafToken[],
|
|
241
|
-
extraMappings: Map<string, string>,
|
|
242
|
-
): CodeMapping[] {
|
|
243
|
-
const sourceLineOffsets = createLineOffsets(source);
|
|
244
|
-
const generatedLineOffsets = createLineOffsets(generated);
|
|
245
|
-
const srcToGenMap = buildSrcToGenMap(
|
|
246
|
-
sourceMap,
|
|
247
|
-
generatedLineOffsets,
|
|
248
|
-
generated,
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
const mappings: CodeMapping[] = [];
|
|
252
|
-
|
|
253
|
-
for (const token of tokens) {
|
|
254
|
-
let sourceStart = locToOffset(
|
|
255
|
-
token.loc.start.line,
|
|
256
|
-
token.loc.start.column,
|
|
257
|
-
sourceLineOffsets,
|
|
258
|
-
);
|
|
259
|
-
sourceStart += token.sourceStartOffset ?? 0;
|
|
260
|
-
|
|
261
|
-
const sourceEnd = locToOffset(
|
|
262
|
-
token.loc.end.line,
|
|
263
|
-
token.loc.end.column,
|
|
264
|
-
sourceLineOffsets,
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
let sourceLength = token.sourceLength ?? sourceEnd - sourceStart;
|
|
268
|
-
|
|
269
|
-
sourceLength += token.sourceLengthOffset ?? 0;
|
|
270
|
-
const [genLineCol] = getGeneratedPosition(
|
|
271
|
-
token.loc.start.line,
|
|
272
|
-
token.loc.start.column,
|
|
273
|
-
srcToGenMap,
|
|
274
|
-
);
|
|
275
|
-
if (!genLineCol) {
|
|
276
|
-
// No mapping found for this token - skip it
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
let genStart = locToOffset(
|
|
280
|
-
genLineCol.line,
|
|
281
|
-
genLineCol.column,
|
|
282
|
-
generatedLineOffsets,
|
|
283
|
-
);
|
|
284
|
-
if (token.isDummy) {
|
|
285
|
-
// A dummy token might be generated for a missing property / argument.
|
|
286
|
-
// Notice that when facing this scenario, the parser tries to 'defer' and step through
|
|
287
|
-
// all whitespaces and insert the invalid node just before the next token.
|
|
288
|
-
// But in a mapping context, we need the caret next to the previous token (commonly the `.` dot)
|
|
289
|
-
// to allow triggering completion correctly. So we adjust the sourceStart and sourceLength accordingly.
|
|
290
|
-
// After adjustment, the mapping will include all whitespaces as the invalid node and maps to an empty string.
|
|
291
|
-
while (sourceStart > 0 && /\s/.test(source[sourceStart - 1])) {
|
|
292
|
-
sourceStart--;
|
|
293
|
-
sourceLength++;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
const generatedLength = token.generatedLength ?? sourceLength;
|
|
297
|
-
|
|
298
|
-
if (
|
|
299
|
-
typeof token.generatedStartOffset === "number" &&
|
|
300
|
-
token.generatedStartOffset > 0
|
|
301
|
-
) {
|
|
302
|
-
// maps verification back to the start of source
|
|
303
|
-
mappings.push({
|
|
304
|
-
sourceOffsets: [sourceStart],
|
|
305
|
-
generatedOffsets: [genStart],
|
|
306
|
-
lengths: [0],
|
|
307
|
-
generatedLengths: [generatedLength],
|
|
308
|
-
data: {
|
|
309
|
-
verification: true,
|
|
310
|
-
},
|
|
311
|
-
});
|
|
312
|
-
genStart += token.generatedStartOffset;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
mappings.push({
|
|
316
|
-
sourceOffsets: [sourceStart],
|
|
317
|
-
generatedOffsets: [genStart],
|
|
318
|
-
lengths: [sourceLength],
|
|
319
|
-
generatedLengths: [generatedLength],
|
|
320
|
-
data: DEFAULT_VOLAR_MAPPING_DATA,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Handle extra mappings that purely generated and wants a diagnostic
|
|
325
|
-
// that appears on the top of file.
|
|
326
|
-
// We'd add these mappings at walker that they will have a `loc` to `1:0`
|
|
327
|
-
// so find them by looking up a source position of `1:0` will work
|
|
328
|
-
const locMapsToTop = getGeneratedPosition(1, 0, srcToGenMap);
|
|
329
|
-
for (const loc of locMapsToTop) {
|
|
330
|
-
const offset = locToOffset(loc.line, loc.column, generatedLineOffsets);
|
|
331
|
-
mappings.push({
|
|
332
|
-
sourceOffsets: [0],
|
|
333
|
-
generatedOffsets: [offset],
|
|
334
|
-
lengths: [0],
|
|
335
|
-
generatedLengths: [0],
|
|
336
|
-
data: {
|
|
337
|
-
verification: true,
|
|
338
|
-
},
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
for (const [loc, codeSnippet] of extraMappings) {
|
|
343
|
-
const generatedStart = generated.indexOf(codeSnippet);
|
|
344
|
-
if (generatedStart === -1) {
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
const [lineStr, columnStr] = loc.split(":");
|
|
348
|
-
const line = Number(lineStr);
|
|
349
|
-
const column = Number(columnStr);
|
|
350
|
-
const sourceStart = locToOffset(line, column, sourceLineOffsets);
|
|
351
|
-
const sourceLength = 1;
|
|
352
|
-
const generatedLength = codeSnippet.length;
|
|
353
|
-
mappings.push({
|
|
354
|
-
sourceOffsets: [sourceStart],
|
|
355
|
-
generatedOffsets: [generatedStart],
|
|
356
|
-
lengths: [sourceLength],
|
|
357
|
-
generatedLengths: [generatedLength],
|
|
358
|
-
data: {
|
|
359
|
-
verification: true,
|
|
360
|
-
},
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Sort mappings by source offset // Sort mappings by source offset
|
|
365
|
-
mappings.sort((a, b) => a.sourceOffsets[0] - b.sourceOffsets[0]);
|
|
366
|
-
|
|
367
|
-
return mappings;
|
|
368
|
-
}
|
|
20
|
+
Object.freeze(DEFAULT_VOLAR_MAPPING_DATA);
|
|
21
|
+
Object.freeze(VERIFICATION_ONLY_MAPPING_DATA);
|
|
@@ -1,123 +1,107 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type {
|
|
2
|
+
Identifier,
|
|
3
|
+
NewExpression,
|
|
4
|
+
Node,
|
|
5
|
+
SimpleCallExpression,
|
|
6
|
+
} from "estree";
|
|
7
|
+
import {
|
|
8
|
+
type PrintOptions,
|
|
9
|
+
type AST as EspolarAST,
|
|
10
|
+
defaultPrinters,
|
|
11
|
+
} from "espolar";
|
|
12
|
+
import type { CodeInformation } from "@volar/language-core";
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_VOLAR_MAPPING_DATA,
|
|
15
|
+
VERIFICATION_ONLY_MAPPING_DATA,
|
|
16
|
+
} from "./mappings.ts";
|
|
17
|
+
import type { TypingTranspileState } from "./walker.ts";
|
|
5
18
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
loc: node.lParenLoc!,
|
|
45
|
-
} as AST.Node;
|
|
46
|
-
|
|
47
|
-
// Map the print of `(` with the fake lParen node.
|
|
48
|
-
// The print of `(` can be happened in two area:
|
|
49
|
-
// 1. The wrapped parenthesis towards callee, which follows a `context.visit` to the callee;
|
|
50
|
-
// 2. The argument list (what we should map), which follows a `context.append` call
|
|
51
|
-
// so we defer the write of `(` to the next call of `context.visit|append`.
|
|
52
|
-
// If it is `context.append`, then make `context.write("(")` happens with our fake node
|
|
53
|
-
// and mapping will be added to the final code-mapping. Otherwise, keep original write.
|
|
54
|
-
|
|
55
|
-
const patchedWrite = (text: string, node?: AST.Node) => {
|
|
56
|
-
if (text === "(") {
|
|
57
|
-
hasDeferredWrite = true;
|
|
58
|
-
} else {
|
|
59
|
-
context.write(text, node);
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
const patchedVisit = (node: AST.Node) => {
|
|
63
|
-
if (hasDeferredWrite) {
|
|
64
|
-
context.write("(");
|
|
65
|
-
}
|
|
66
|
-
return context.visit(node);
|
|
67
|
-
};
|
|
68
|
-
const patchedAppend = (subcontext: Context) => {
|
|
69
|
-
if (hasDeferredWrite) {
|
|
70
|
-
context.write("(", lParenFakeNode);
|
|
71
|
-
// console.log("Inserted fake [LPAREN] for node at ", lParenFakeNode.loc);
|
|
72
|
-
interceptionDone = true;
|
|
73
|
-
}
|
|
74
|
-
return context.append(subcontext);
|
|
75
|
-
};
|
|
76
|
-
const proxiedContext = new Proxy(context, {
|
|
77
|
-
get(target, prop) {
|
|
78
|
-
if (!interceptionDone) {
|
|
79
|
-
if (prop === "write") {
|
|
80
|
-
return patchedWrite;
|
|
19
|
+
export function getPrintOptions(
|
|
20
|
+
source: string,
|
|
21
|
+
state: TypingTranspileState,
|
|
22
|
+
): PrintOptions<CodeInformation> {
|
|
23
|
+
return {
|
|
24
|
+
source,
|
|
25
|
+
isUntouched: (node) => {
|
|
26
|
+
if (node.type === "Identifier" && (node as Identifier).isDummy) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return state.sourceNodes.has(node as Node);
|
|
30
|
+
},
|
|
31
|
+
getLeadingComments: (node) => (node as Node).leadingComments,
|
|
32
|
+
getTrailingComments: (node) => (node as Node).trailingComments,
|
|
33
|
+
getMappingData: () => DEFAULT_VOLAR_MAPPING_DATA,
|
|
34
|
+
printers: {
|
|
35
|
+
// Make the print of dummy identifier print nothing.
|
|
36
|
+
// Exception: if GTS attribute list's last argument is dummy, e.g.
|
|
37
|
+
// foo bar, ;
|
|
38
|
+
// ^~ here
|
|
39
|
+
// Then the printed JS will be `foo(bar, )` which WILL NOT be syntax error in ES6.
|
|
40
|
+
// So we mark the lastArg manually and print an additional comma
|
|
41
|
+
// for this dummy identifier, i.e. `foo(bar,,)` and TypeScript will recognize the error.
|
|
42
|
+
Identifier(node, context) {
|
|
43
|
+
const identifier = node as Identifier;
|
|
44
|
+
if (identifier.isDummy && identifier.range) {
|
|
45
|
+
const text = state.lastArgNodes.has(identifier) ? "," : "";
|
|
46
|
+
// extend the source mapping of dummy id to the before next token
|
|
47
|
+
let firstNonWhiteSpaceIndex = context.source
|
|
48
|
+
.slice(identifier.range[1])
|
|
49
|
+
.search(/\S/);
|
|
50
|
+
const rangeEnd =
|
|
51
|
+
firstNonWhiteSpaceIndex === -1
|
|
52
|
+
? context.source.length
|
|
53
|
+
: identifier.range[1] + firstNonWhiteSpaceIndex;
|
|
54
|
+
context.writeMapped(text, identifier.range[0], rangeEnd);
|
|
55
|
+
} else {
|
|
56
|
+
return defaultPrinters.Identifier(node, context);
|
|
81
57
|
}
|
|
82
|
-
|
|
83
|
-
|
|
58
|
+
},
|
|
59
|
+
Literal(node, context) {
|
|
60
|
+
const generatedStart = context.generatedOffset;
|
|
61
|
+
if (state.literalFromIdentifier.has(node) && node.range) {
|
|
62
|
+
const text = JSON.stringify(node.value);
|
|
63
|
+
context.write('"');
|
|
64
|
+
context.writeMapped(text.slice(1, -1), node.range[0], node.range[1]);
|
|
65
|
+
context.write('"');
|
|
66
|
+
} else {
|
|
67
|
+
defaultPrinters.Literal(node, context);
|
|
84
68
|
}
|
|
85
|
-
|
|
86
|
-
|
|
69
|
+
// For generated `import xxx from "yyy"`, add mappings from xxx and yyy
|
|
70
|
+
// to the top-of-file for diagnostics around missing / wrong imports.
|
|
71
|
+
if (state.diagnosticsOnTopNodes.has(node)) {
|
|
72
|
+
const generatedEnd = context.generatedOffset;
|
|
73
|
+
context.createExtraMapping(
|
|
74
|
+
{ start: 0, end: 1 },
|
|
75
|
+
generatedStart,
|
|
76
|
+
generatedEnd,
|
|
77
|
+
VERIFICATION_ONLY_MAPPING_DATA,
|
|
78
|
+
);
|
|
87
79
|
}
|
|
80
|
+
},
|
|
81
|
+
ImportDefaultSpecifier(node, context) {
|
|
82
|
+
const generatedStart = context.generatedOffset;
|
|
83
|
+
defaultPrinters.ImportDefaultSpecifier(node, context);
|
|
84
|
+
if (state.diagnosticsOnTopNodes.has(node as Node)) {
|
|
85
|
+
const generatedEnd = context.generatedOffset;
|
|
86
|
+
context.createExtraMapping(
|
|
87
|
+
{ start: 0, end: 1 },
|
|
88
|
+
generatedStart,
|
|
89
|
+
generatedEnd,
|
|
90
|
+
VERIFICATION_ONLY_MAPPING_DATA,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
// Enable triggering signature completion
|
|
96
|
+
experimentalGetLeftParenSourceRange: (node) => {
|
|
97
|
+
const callLike = node as SimpleCallExpression | NewExpression;
|
|
98
|
+
if (callLike.lParenRange) {
|
|
99
|
+
return {
|
|
100
|
+
start: callLike.lParenRange[0],
|
|
101
|
+
end: callLike.lParenRange[1],
|
|
102
|
+
};
|
|
88
103
|
}
|
|
89
|
-
|
|
90
|
-
if (typeof value === "function") {
|
|
91
|
-
return value.bind(target);
|
|
92
|
-
}
|
|
93
|
-
return value;
|
|
104
|
+
return state.namedAttributeCalleeLParenRange.get(callLike.callee);
|
|
94
105
|
},
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
};
|
|
98
|
-
printer.CallExpression = newCallNewExpression;
|
|
99
|
-
printer.NewExpression = newCallNewExpression;
|
|
100
|
-
|
|
101
|
-
// Handle node with `diagnosticsOnTop`. These nodes and their children will be
|
|
102
|
-
// printed with an extra `loc` that point to the beginning of the source file,
|
|
103
|
-
// so the source-mapping will include them with a 0:1 -> generated position entry.
|
|
104
|
-
|
|
105
|
-
const prevWildcard = printer._!;
|
|
106
|
-
printer._ = (node: Node, context, visit) => {
|
|
107
|
-
const contextProto = Object.getPrototypeOf(context);
|
|
108
|
-
const prevContextWrite: typeof context.write = contextProto.write;
|
|
109
|
-
if ("diagnosticsOnTop" in node && node.diagnosticsOnTop) {
|
|
110
|
-
contextProto.write = function (text: string, node?: AST.Node) {
|
|
111
|
-
node ??= {} as AST.Node;
|
|
112
|
-
node.loc ??= {
|
|
113
|
-
start: { line: 1, column: 0 },
|
|
114
|
-
end: { line: 1, column: 0 },
|
|
115
|
-
};
|
|
116
|
-
return prevContextWrite.call(this, text, node);
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
prevWildcard(node, context, visit);
|
|
120
|
-
contextProto.write = prevContextWrite;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
export const patchedPrinter: Visitors<AST.Node> = printer;
|
|
106
|
+
};
|
|
107
|
+
}
|