ts-const-value-transformer 0.9.0 → 0.10.0
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/CHANGELOG.md +10 -0
- package/README.md +33 -0
- package/dist/TsProxy.d.mts +105 -0
- package/dist/TsProxy.mjs +1 -0
- package/dist/createPortalTransformer.mjs +2 -1
- package/dist/createPortalTransformerWithTsLs.d.mts +59 -0
- package/dist/createPortalTransformerWithTsLs.mjs +159 -0
- package/dist/createTransformer.mjs +2 -1
- package/dist/getCustomTransformers.d.mts +1 -5
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +4 -2
- package/dist/lsp/SyncLspClient.d.mts +25 -0
- package/dist/lsp/SyncLspClient.mjs +234 -0
- package/dist/lsp/TsLspClient.d.mts +29 -0
- package/dist/lsp/TsLspClient.mjs +271 -0
- package/dist/lsp/lspTransformerUtil.d.mts +1 -0
- package/dist/lsp/lspTransformerUtil.mjs +536 -0
- package/dist/lspTransformer.d.mts +9 -0
- package/dist/lspTransformer.mjs +226 -0
- package/dist/transform.d.mts +8 -5
- package/dist/transform.mjs +105 -298
- package/dist/tscTransformer.d.mts +8 -0
- package/dist/tscTransformer.mjs +377 -0
- package/dist/version.d.mts +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +16 -3
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
const HOVER_CACHE_SYMBOL = Symbol('hover-cache');
|
|
3
|
+
const TYPE_CACHE_SYMBOL = Symbol('type-cache');
|
|
4
|
+
const READONLY_CACHE_SYMBOL = Symbol('read-only-cache');
|
|
5
|
+
const DEFINITION_CACHE_SYMBOL = Symbol('definition-cache');
|
|
6
|
+
function positionToLineAndColumn(sourceFile, pos) {
|
|
7
|
+
const p = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
8
|
+
return { line: p.line, column: p.character };
|
|
9
|
+
}
|
|
10
|
+
function addToPositionByText(pos, text) {
|
|
11
|
+
const r = {
|
|
12
|
+
line: pos.line,
|
|
13
|
+
column: pos.column,
|
|
14
|
+
};
|
|
15
|
+
const textLines = text.split(/\r?\n/g);
|
|
16
|
+
if (textLines.length > 1) {
|
|
17
|
+
r.line += textLines.length - 1;
|
|
18
|
+
r.column = textLines[textLines.length - 1].length;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
r.column += text.length;
|
|
22
|
+
}
|
|
23
|
+
return r;
|
|
24
|
+
}
|
|
25
|
+
function getNodeFromPosition(sourceFile, line, pos) {
|
|
26
|
+
const lines = sourceFile.getLineStarts();
|
|
27
|
+
if (line >= lines.length) {
|
|
28
|
+
line = lines.length - 1;
|
|
29
|
+
}
|
|
30
|
+
const nodePos = pos + lines[line];
|
|
31
|
+
let foundNode;
|
|
32
|
+
visit(sourceFile, undefined);
|
|
33
|
+
return foundNode;
|
|
34
|
+
function visit(node, parent) {
|
|
35
|
+
node.parent = parent;
|
|
36
|
+
if (node.pos <= nodePos && node.end >= nodePos) {
|
|
37
|
+
foundNode = node;
|
|
38
|
+
}
|
|
39
|
+
ts.visitEachChild(node, (n) => visit(n, node), undefined);
|
|
40
|
+
return node;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function isParentNode(target, child, stopNode) {
|
|
44
|
+
if (target === child) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
48
|
+
while (child.parent) {
|
|
49
|
+
if (child.parent === target) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (child.parent === stopNode) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
child = child.parent;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
function getNodeLineAndColumn(node, sourceFile, tsInstance) {
|
|
60
|
+
return positionToLineAndColumn(sourceFile,
|
|
61
|
+
// For call expression, use end of position of expression (excluding arguments range)
|
|
62
|
+
tsInstance.isCallExpression(node) ? node.expression.end : node.end);
|
|
63
|
+
}
|
|
64
|
+
function hoverResultToCode(result) {
|
|
65
|
+
let code = result[1];
|
|
66
|
+
if (result[0]) {
|
|
67
|
+
const ra = /```(`*)(?:ts|tsx|typescript|js|jsx|javascript)\s+([\s\S]*?)\s+```\1/.exec(code);
|
|
68
|
+
if (ra) {
|
|
69
|
+
code = ra[2];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return code.trim();
|
|
73
|
+
}
|
|
74
|
+
function getHoverFromNode(node, sourceFile, tsInstance, client) {
|
|
75
|
+
if (node[HOVER_CACHE_SYMBOL] != null) {
|
|
76
|
+
return node[HOVER_CACHE_SYMBOL];
|
|
77
|
+
}
|
|
78
|
+
const pos = getNodeLineAndColumn(node, sourceFile, tsInstance);
|
|
79
|
+
const o = client.hoverForPosition(sourceFile.fileName, pos.line, pos.column);
|
|
80
|
+
const code = hoverResultToCode(o);
|
|
81
|
+
node[HOVER_CACHE_SYMBOL] = code;
|
|
82
|
+
return code;
|
|
83
|
+
}
|
|
84
|
+
function isFunctionLike(hover) {
|
|
85
|
+
return (hover.startsWith('function') ||
|
|
86
|
+
hover.startsWith('(local function)') ||
|
|
87
|
+
hover.startsWith('(method)'));
|
|
88
|
+
}
|
|
89
|
+
function hoverToTypeString(node, hover, tsInstance) {
|
|
90
|
+
if (hover.startsWith('(enum member)')) {
|
|
91
|
+
hover = hover.slice(13);
|
|
92
|
+
const s = tsInstance.createScanner(tsInstance.ScriptTarget.Latest, false);
|
|
93
|
+
s.setText(hover);
|
|
94
|
+
for (;;) {
|
|
95
|
+
const t = s.scan();
|
|
96
|
+
if (t === tsInstance.SyntaxKind.EqualsToken) {
|
|
97
|
+
return hover.slice(s.getTokenEnd()).trim();
|
|
98
|
+
}
|
|
99
|
+
else if (t === tsInstance.SyntaxKind.EndOfFileToken) {
|
|
100
|
+
return '';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (node && tsInstance.isIdentifier(node) && isFunctionLike(hover)) {
|
|
105
|
+
if (hover.startsWith('function')) {
|
|
106
|
+
hover = hover.slice(8);
|
|
107
|
+
}
|
|
108
|
+
else if (hover.startsWith('(local function)')) {
|
|
109
|
+
hover = hover.slice(16);
|
|
110
|
+
}
|
|
111
|
+
else if (hover.startsWith('(method)')) {
|
|
112
|
+
hover = hover.slice(8);
|
|
113
|
+
}
|
|
114
|
+
return hover.trim();
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const s = tsInstance.createScanner(tsInstance.ScriptTarget.Latest, false);
|
|
118
|
+
s.setText(hover);
|
|
119
|
+
const tokenStack = [];
|
|
120
|
+
for (;;) {
|
|
121
|
+
const t = s.scan();
|
|
122
|
+
if (tokenStack.length > 0) {
|
|
123
|
+
if (tokenStack[tokenStack.length - 1] === t) {
|
|
124
|
+
tokenStack.pop();
|
|
125
|
+
}
|
|
126
|
+
else if (t === tsInstance.SyntaxKind.EndOfFileToken) {
|
|
127
|
+
// Unexpected
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (t === tsInstance.SyntaxKind.ColonToken) {
|
|
133
|
+
return hover.slice(s.getTokenEnd()).trim();
|
|
134
|
+
}
|
|
135
|
+
else if (t === tsInstance.SyntaxKind.EndOfFileToken) {
|
|
136
|
+
// Including indexed access
|
|
137
|
+
return '';
|
|
138
|
+
}
|
|
139
|
+
else if (t === tsInstance.SyntaxKind.LessThanToken) {
|
|
140
|
+
tokenStack.push(tsInstance.SyntaxKind.GreaterThanToken);
|
|
141
|
+
}
|
|
142
|
+
else if (t === tsInstance.SyntaxKind.OpenBraceToken) {
|
|
143
|
+
tokenStack.push(tsInstance.SyntaxKind.CloseBraceToken);
|
|
144
|
+
}
|
|
145
|
+
else if (t === tsInstance.SyntaxKind.OpenBracketToken) {
|
|
146
|
+
tokenStack.push(tsInstance.SyntaxKind.CloseBracketToken);
|
|
147
|
+
}
|
|
148
|
+
else if (t === tsInstance.SyntaxKind.OpenParenToken) {
|
|
149
|
+
tokenStack.push(tsInstance.SyntaxKind.CloseParenToken);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function hoverToFunctionReturnTypeString(node, hover, tsInstance) {
|
|
155
|
+
const t = hoverToTypeString(node, hover, tsInstance);
|
|
156
|
+
if (isFunctionLike(hover)) {
|
|
157
|
+
return t;
|
|
158
|
+
}
|
|
159
|
+
if (t[0] !== '(') {
|
|
160
|
+
return '';
|
|
161
|
+
}
|
|
162
|
+
// Search type string after '=>'
|
|
163
|
+
const s = tsInstance.createScanner(tsInstance.ScriptTarget.Latest, false);
|
|
164
|
+
s.setText(t);
|
|
165
|
+
const tokenStack = [];
|
|
166
|
+
for (;;) {
|
|
167
|
+
const t = s.scan();
|
|
168
|
+
if (tokenStack.length > 0) {
|
|
169
|
+
if (tokenStack[tokenStack.length - 1] === t) {
|
|
170
|
+
tokenStack.pop();
|
|
171
|
+
}
|
|
172
|
+
else if (t === tsInstance.SyntaxKind.EndOfFileToken) {
|
|
173
|
+
// Unexpected
|
|
174
|
+
return '';
|
|
175
|
+
}
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (t === tsInstance.SyntaxKind.EqualsGreaterThanToken) {
|
|
179
|
+
return hover.slice(s.getTokenEnd()).trim();
|
|
180
|
+
}
|
|
181
|
+
else if (t === tsInstance.SyntaxKind.EndOfFileToken) {
|
|
182
|
+
// Unexpected
|
|
183
|
+
return '';
|
|
184
|
+
}
|
|
185
|
+
else if (t === tsInstance.SyntaxKind.LessThanToken) {
|
|
186
|
+
tokenStack.push(tsInstance.SyntaxKind.GreaterThanToken);
|
|
187
|
+
}
|
|
188
|
+
else if (t === tsInstance.SyntaxKind.OpenBraceToken) {
|
|
189
|
+
tokenStack.push(tsInstance.SyntaxKind.CloseBraceToken);
|
|
190
|
+
}
|
|
191
|
+
else if (t === tsInstance.SyntaxKind.OpenBracketToken) {
|
|
192
|
+
tokenStack.push(tsInstance.SyntaxKind.CloseBracketToken);
|
|
193
|
+
}
|
|
194
|
+
else if (t === tsInstance.SyntaxKind.OpenParenToken) {
|
|
195
|
+
tokenStack.push(tsInstance.SyntaxKind.CloseParenToken);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function retrieveActualTypeString(fileName, pos, tsIntance, client) {
|
|
200
|
+
const def = client.getTypeDefitition(fileName, pos.line, pos.column);
|
|
201
|
+
if (!def) {
|
|
202
|
+
return ['', ''];
|
|
203
|
+
}
|
|
204
|
+
const o = client.hoverForPosition(def.fileName, def.lineEnd, def.posEnd);
|
|
205
|
+
const code = hoverResultToCode(o);
|
|
206
|
+
return [code, hoverToTypeString(null, code, tsIntance)];
|
|
207
|
+
}
|
|
208
|
+
function typeStringToLiteralType(typeString, tsInstance) {
|
|
209
|
+
typeString = typeString.trim();
|
|
210
|
+
if (typeString === '') {
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
if (typeString === 'null') {
|
|
214
|
+
return tsInstance.TypeFlags.Null;
|
|
215
|
+
}
|
|
216
|
+
if (typeString === 'undefined') {
|
|
217
|
+
return tsInstance.TypeFlags.Undefined;
|
|
218
|
+
}
|
|
219
|
+
if (typeString === 'true' || typeString === 'false') {
|
|
220
|
+
return tsInstance.TypeFlags.BooleanLiteral;
|
|
221
|
+
}
|
|
222
|
+
// string literal
|
|
223
|
+
if (/^(["']).*\1$/.test(typeString)) {
|
|
224
|
+
return tsInstance.TypeFlags.StringLiteral;
|
|
225
|
+
}
|
|
226
|
+
// number / bigint
|
|
227
|
+
const ra = /^-?[0-9]+(?:\.[0-9]*)?(n?)$/.exec(typeString);
|
|
228
|
+
if (ra) {
|
|
229
|
+
return ra[1] === 'n'
|
|
230
|
+
? tsInstance.TypeFlags.BigIntLiteral
|
|
231
|
+
: tsInstance.TypeFlags.NumberLiteral;
|
|
232
|
+
}
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
function performActualPropertyForElementAccessExpression(node, sourceFile, tsInstance, client, perform) {
|
|
236
|
+
const a = node.argumentExpression;
|
|
237
|
+
let replacedRange;
|
|
238
|
+
if (!tsInstance.isLiteralExpression(a)) {
|
|
239
|
+
// Temporary replace
|
|
240
|
+
const rStart = positionToLineAndColumn(sourceFile, a.pos);
|
|
241
|
+
const rEnd = positionToLineAndColumn(sourceFile, a.end);
|
|
242
|
+
const t = nodeToTypeString(a, sourceFile, tsInstance, client);
|
|
243
|
+
const flags = typeStringToLiteralType(t, tsInstance);
|
|
244
|
+
if (flags !== 0) {
|
|
245
|
+
client.sendReplaceText(sourceFile.fileName, rStart.line, rStart.column, rEnd.line, rEnd.column, t);
|
|
246
|
+
replacedRange = {
|
|
247
|
+
start: rStart,
|
|
248
|
+
end: addToPositionByText(rStart, t),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const targetPos = replacedRange
|
|
253
|
+
? replacedRange.end
|
|
254
|
+
: positionToLineAndColumn(sourceFile, a.end);
|
|
255
|
+
const r = perform(targetPos);
|
|
256
|
+
if (replacedRange) {
|
|
257
|
+
// Restore replacement
|
|
258
|
+
client.sendReplaceText(sourceFile.fileName, replacedRange.start.line, replacedRange.start.column, replacedRange.end.line, replacedRange.end.column, a.getFullText(sourceFile));
|
|
259
|
+
}
|
|
260
|
+
return r;
|
|
261
|
+
}
|
|
262
|
+
function elementAccessExpressionToTypeString(node, sourceFile, tsInstance, client) {
|
|
263
|
+
if (node[TYPE_CACHE_SYMBOL] != null) {
|
|
264
|
+
return node[TYPE_CACHE_SYMBOL][0];
|
|
265
|
+
}
|
|
266
|
+
const typeString = performActualPropertyForElementAccessExpression(node, sourceFile, tsInstance, client, (pos) => {
|
|
267
|
+
const result = client.hoverForPosition(sourceFile.fileName, pos.line, pos.column);
|
|
268
|
+
const hover = hoverResultToCode(result);
|
|
269
|
+
node[HOVER_CACHE_SYMBOL] = hover;
|
|
270
|
+
let typeString = hoverToTypeString(node, hover, tsInstance);
|
|
271
|
+
let isEnum = hover.startsWith('(enum member)');
|
|
272
|
+
if (typeStringToLiteralType(typeString, tsInstance) === 0) {
|
|
273
|
+
const t = retrieveActualTypeString(sourceFile.fileName, pos, tsInstance, client);
|
|
274
|
+
if (t[1] !== '') {
|
|
275
|
+
isEnum = t[0].startsWith('(enum member)');
|
|
276
|
+
typeString = t[1];
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
node[TYPE_CACHE_SYMBOL] = [
|
|
280
|
+
typeString,
|
|
281
|
+
isEnum,
|
|
282
|
+
];
|
|
283
|
+
return typeString;
|
|
284
|
+
});
|
|
285
|
+
return typeString;
|
|
286
|
+
}
|
|
287
|
+
function templateExpressionToTypeString(node, sourceFile, tsInstance, client) {
|
|
288
|
+
if (node[TYPE_CACHE_SYMBOL] != null) {
|
|
289
|
+
return node[TYPE_CACHE_SYMBOL][0];
|
|
290
|
+
}
|
|
291
|
+
// hack to retrieve actual literal type by using arrow function
|
|
292
|
+
// - before: `foo ${'bar'}`
|
|
293
|
+
// - after: (()=>(`foo ${'bar'}`) as const)()
|
|
294
|
+
const text = node.getFullText(sourceFile);
|
|
295
|
+
const rStart = positionToLineAndColumn(sourceFile, node.pos);
|
|
296
|
+
const rEnd = positionToLineAndColumn(sourceFile, node.end);
|
|
297
|
+
const replacedText = `(()=>(${text}) as const)()`;
|
|
298
|
+
client.sendReplaceText(sourceFile.fileName, rStart.line, rStart.column, rEnd.line, rEnd.column, replacedText);
|
|
299
|
+
const result = client.hoverForPosition(sourceFile.fileName, rStart.line, rStart.column + 4 // middle of arrow '=>' position
|
|
300
|
+
);
|
|
301
|
+
const hover = hoverResultToCode(result);
|
|
302
|
+
node[HOVER_CACHE_SYMBOL] = hover;
|
|
303
|
+
const typeString = hoverToTypeString(node, hover, tsInstance);
|
|
304
|
+
const rNewEnd = addToPositionByText(rStart, replacedText);
|
|
305
|
+
// Restore replacement
|
|
306
|
+
client.sendReplaceText(sourceFile.fileName, rStart.line, rStart.column, rNewEnd.line, rNewEnd.column, text);
|
|
307
|
+
node[TYPE_CACHE_SYMBOL] = [
|
|
308
|
+
typeString,
|
|
309
|
+
false,
|
|
310
|
+
];
|
|
311
|
+
return typeString;
|
|
312
|
+
}
|
|
313
|
+
// @internal
|
|
314
|
+
export function nodeToTypeString(node, sourceFile, tsInstance, client) {
|
|
315
|
+
if (node[TYPE_CACHE_SYMBOL] != null) {
|
|
316
|
+
return node[TYPE_CACHE_SYMBOL][0];
|
|
317
|
+
}
|
|
318
|
+
if (tsInstance.isElementAccessExpression(node)) {
|
|
319
|
+
return elementAccessExpressionToTypeString(node, sourceFile, tsInstance, client);
|
|
320
|
+
}
|
|
321
|
+
let typeString;
|
|
322
|
+
let isEnum = false;
|
|
323
|
+
if (tsInstance.isAsExpression(node)) {
|
|
324
|
+
typeString = node.type.getText(sourceFile);
|
|
325
|
+
if (typeString === 'const') {
|
|
326
|
+
nodeToTypeString(node.expression, sourceFile, tsInstance, client);
|
|
327
|
+
// use cached value instead of return value
|
|
328
|
+
[typeString, isEnum] = node.expression[TYPE_CACHE_SYMBOL];
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
else if (tsInstance.isStringLiteral(node)) {
|
|
332
|
+
typeString = JSON.stringify(node.text);
|
|
333
|
+
}
|
|
334
|
+
else if (tsInstance.isNumericLiteral(node) ||
|
|
335
|
+
tsInstance.isBigIntLiteral(node)) {
|
|
336
|
+
typeString = node.text;
|
|
337
|
+
}
|
|
338
|
+
else if (tsInstance.isTemplateExpression(node)) {
|
|
339
|
+
typeString = templateExpressionToTypeString(node, sourceFile, tsInstance, client);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
const hover = getHoverFromNode(node, sourceFile, tsInstance, client);
|
|
343
|
+
if (tsInstance.isCallExpression(node)) {
|
|
344
|
+
typeString = hoverToFunctionReturnTypeString(node, hover, tsInstance);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
typeString = hoverToTypeString(node, hover, tsInstance);
|
|
348
|
+
}
|
|
349
|
+
isEnum = hover.startsWith('(enum member)');
|
|
350
|
+
}
|
|
351
|
+
if (typeStringToLiteralType(typeString, tsInstance) === 0) {
|
|
352
|
+
const pos = getNodeLineAndColumn(node, sourceFile, tsInstance);
|
|
353
|
+
const t = retrieveActualTypeString(sourceFile.fileName, pos, tsInstance, client);
|
|
354
|
+
if (t[1] !== '') {
|
|
355
|
+
isEnum = t[0].startsWith('(enum member)');
|
|
356
|
+
typeString = t[1];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
node[TYPE_CACHE_SYMBOL] = [typeString, isEnum];
|
|
360
|
+
return typeString;
|
|
361
|
+
}
|
|
362
|
+
// @internal
|
|
363
|
+
export function nodeToTypeFlags(node, sourceFile, tsInstance, client) {
|
|
364
|
+
const typeString = nodeToTypeString(node, sourceFile, tsInstance, client);
|
|
365
|
+
let flags = typeStringToLiteralType(typeString, tsInstance);
|
|
366
|
+
if (node[TYPE_CACHE_SYMBOL][1]) {
|
|
367
|
+
flags |= tsInstance.TypeFlags.EnumLiteral;
|
|
368
|
+
}
|
|
369
|
+
return flags;
|
|
370
|
+
}
|
|
371
|
+
function determineIfPropertyIsReadonly(node, sourceFile, tsInstance, client, getSourceFile) {
|
|
372
|
+
if (!getSourceFile) {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
let def;
|
|
376
|
+
if (ts.isElementAccessExpression(node)) {
|
|
377
|
+
def = performActualPropertyForElementAccessExpression(node, sourceFile, tsInstance, client, (pos) => client.getDefinition(sourceFile.fileName, pos.line, pos.column));
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
const pos = positionToLineAndColumn(sourceFile, node.end);
|
|
381
|
+
def = client.getDefinition(sourceFile.fileName, pos.line, pos.column);
|
|
382
|
+
}
|
|
383
|
+
if (!def) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
const defSource = getSourceFile(def.fileName);
|
|
387
|
+
const expr = getNodeFromPosition(defSource, def.lineEnd, def.posEnd);
|
|
388
|
+
if (!expr) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
let parent = expr.parent;
|
|
392
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
393
|
+
while (parent) {
|
|
394
|
+
// For interface/type literal
|
|
395
|
+
if (ts.isPropertySignature(parent)) {
|
|
396
|
+
if (parent.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.ReadonlyKeyword)) {
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
else if (ts.isAsExpression(parent)) {
|
|
402
|
+
// For definition, 'as const' should be readonly
|
|
403
|
+
if (parent.type.getText(sourceFile) === 'const') {
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
else if (ts.isEnumMember(parent)) {
|
|
408
|
+
// Enum member is readonly
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
else if (ts.isVariableDeclaration(parent)) {
|
|
412
|
+
if (isParentNode(parent.name, expr, parent)) {
|
|
413
|
+
const p = parent.parent;
|
|
414
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
415
|
+
if (p && ts.isVariableDeclarationList(p)) {
|
|
416
|
+
return (p.flags & ts.NodeFlags.Const) !== 0;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// Reached to top level
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
if (parent.parent === parent) {
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
parent = parent.parent;
|
|
426
|
+
}
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
// @internal
|
|
430
|
+
export function isExpressionReadonly(node, sourceFile, tsInstance, client, getSourceFile) {
|
|
431
|
+
if (node[READONLY_CACHE_SYMBOL] != null) {
|
|
432
|
+
return node[READONLY_CACHE_SYMBOL];
|
|
433
|
+
}
|
|
434
|
+
if (!ts.isIdentifier(node) &&
|
|
435
|
+
!ts.isPropertyAccessExpression(node) &&
|
|
436
|
+
!ts.isElementAccessExpression(node)) {
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
if (ts.isIdentifier(node)) {
|
|
440
|
+
if (
|
|
441
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
442
|
+
node.parent &&
|
|
443
|
+
isUndefinedIdentifier(node, node.parent, sourceFile, tsInstance, client)) {
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
let hover = getHoverFromNode(node, sourceFile, tsInstance, client);
|
|
447
|
+
if (hover.startsWith('(alias)')) {
|
|
448
|
+
hover = hover.slice(7).trim();
|
|
449
|
+
const lines = hover.split(/\r?\n/g);
|
|
450
|
+
if (lines.length >= 2 && /^import\b/.test(lines[1].trim())) {
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
const isReadonly = /^const\b/.test(hover);
|
|
455
|
+
node[READONLY_CACHE_SYMBOL] =
|
|
456
|
+
isReadonly;
|
|
457
|
+
return isReadonly;
|
|
458
|
+
}
|
|
459
|
+
return determineIfPropertyIsReadonly(node, sourceFile, tsInstance, client, getSourceFile);
|
|
460
|
+
// const typeString = nodeToTypeString(node, sourceFile, tsInstance, client);
|
|
461
|
+
// if (typeStringToLiteralType(typeString, tsInstance) === 0) {
|
|
462
|
+
// return null;
|
|
463
|
+
// }
|
|
464
|
+
// const diagBefore = client.receiveDiagnostics(sourceFile.fileName);
|
|
465
|
+
// // Replace expression with `(expression = typeString)` which should be error if expression is read-only
|
|
466
|
+
// const text = node.getFullText(sourceFile);
|
|
467
|
+
// const replacedText = `(${text} = ${typeString})`;
|
|
468
|
+
// const rStart = positionToLineAndColumn(sourceFile, node.pos);
|
|
469
|
+
// const rEnd = positionToLineAndColumn(sourceFile, node.end);
|
|
470
|
+
// client.sendReplaceText(
|
|
471
|
+
// sourceFile.fileName,
|
|
472
|
+
// rStart.line,
|
|
473
|
+
// rStart.column,
|
|
474
|
+
// rEnd.line,
|
|
475
|
+
// rEnd.column,
|
|
476
|
+
// replacedText
|
|
477
|
+
// );
|
|
478
|
+
// const diagAfter = client.receiveDiagnostics(sourceFile.fileName);
|
|
479
|
+
// const isReadonly = diagAfter.length > diagBefore.length;
|
|
480
|
+
// // Restore text
|
|
481
|
+
// const rNewEnd = addToPositionByText(rStart, replacedText);
|
|
482
|
+
// client.sendReplaceText(
|
|
483
|
+
// sourceFile.fileName,
|
|
484
|
+
// rStart.line,
|
|
485
|
+
// rStart.column,
|
|
486
|
+
// rNewEnd.line,
|
|
487
|
+
// rNewEnd.column,
|
|
488
|
+
// text
|
|
489
|
+
// );
|
|
490
|
+
// (node as ts.Node as NodeWithReadonlyCache)[READONLY_CACHE_SYMBOL] =
|
|
491
|
+
// isReadonly;
|
|
492
|
+
// return isReadonly;
|
|
493
|
+
}
|
|
494
|
+
// @internal
|
|
495
|
+
export function isExternalReference(node, sourceFile, tsInstance, client, externalNames) {
|
|
496
|
+
if (node[DEFINITION_CACHE_SYMBOL] === undefined) {
|
|
497
|
+
const pos = getNodeLineAndColumn(node, sourceFile, tsInstance);
|
|
498
|
+
const def = client.getDefinition(sourceFile.fileName, pos.line, pos.column);
|
|
499
|
+
node[DEFINITION_CACHE_SYMBOL] =
|
|
500
|
+
def != null ? def.fileName : null;
|
|
501
|
+
}
|
|
502
|
+
const defFile = node[DEFINITION_CACHE_SYMBOL];
|
|
503
|
+
if (defFile == null) {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
if (externalNames.length === 0) {
|
|
507
|
+
return /[\\/]node_modules[\\/]/.test(defFile);
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
return externalNames.some((part) => {
|
|
511
|
+
if (typeof part === 'string') {
|
|
512
|
+
return defFile.replace(/\\/g, '/').includes(part);
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
return part.test(defFile);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
// @internal
|
|
521
|
+
export function isUndefinedIdentifier(node, parent, sourceFile, tsInstance, client) {
|
|
522
|
+
if (tsInstance.isPropertyAccessExpression(parent) ||
|
|
523
|
+
tsInstance.isElementAccessExpression(parent)) {
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
if (node.getText(sourceFile)?.trim() !== 'undefined') {
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
const typeFlags = nodeToTypeFlags(node, sourceFile, tsInstance, client);
|
|
530
|
+
return (typeFlags & tsInstance.TypeFlags.Undefined) !== 0;
|
|
531
|
+
}
|
|
532
|
+
// @internal
|
|
533
|
+
export function isEnumAccess(node, sourceFile, tsInstance, client) {
|
|
534
|
+
const hover = getHoverFromNode(node, sourceFile, tsInstance, client);
|
|
535
|
+
return hover.startsWith('(enum member)');
|
|
536
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type * as sourceMap from 'source-map';
|
|
2
|
+
import type * as ts from 'typescript';
|
|
3
|
+
import type TsLspClient from './lsp/TsLspClient.mjs';
|
|
4
|
+
import { type TransformOptions } from './transform.mjs';
|
|
5
|
+
export declare function printSource(sourceFile: ts.SourceFile, tsInstance?: typeof ts): string;
|
|
6
|
+
export declare function printSourceWithMap(sourceFile: ts.SourceFile, originalSourceName: string, startOfSourceMap?: sourceMap.RawSourceMap, tsInstance?: typeof ts): [string, sourceMap.RawSourceMap];
|
|
7
|
+
export declare function transformSource(sourceFile: ts.SourceFile, lspClient: TsLspClient, getSourceFile: (fileName: string) => ts.SourceFile, options?: TransformOptions): ts.SourceFile;
|
|
8
|
+
export declare function transformAndPrintSource(sourceFile: ts.SourceFile, lspClient: TsLspClient, getSourceFile: (fileName: string) => ts.SourceFile, options?: TransformOptions): string;
|
|
9
|
+
export declare function transformAndPrintSourceWithMap(sourceFile: ts.SourceFile, lspClient: TsLspClient, getSourceFile: (fileName: string) => ts.SourceFile, originalSourceName: string, options?: TransformOptions, startOfSourceMap?: sourceMap.RawSourceMap): [string, sourceMap.RawSourceMap];
|