@astrojs/ts-plugin 0.2.1 → 0.4.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.
Files changed (54) hide show
  1. package/.turbo/turbo-build.log +5 -2
  2. package/CHANGELOG.md +16 -0
  3. package/LICENSE +36 -0
  4. package/README.md +1 -1
  5. package/dist/astro-snapshots.js +48 -47
  6. package/dist/astro-sys.js +22 -35
  7. package/dist/astro2tsx.js +17 -41
  8. package/dist/index.js +38 -39
  9. package/dist/language-service/completions.js +42 -42
  10. package/dist/language-service/definition.js +22 -40
  11. package/dist/language-service/diagnostics.js +15 -18
  12. package/dist/language-service/file-references.js +52 -0
  13. package/dist/language-service/find-references.js +20 -38
  14. package/dist/language-service/implementation.js +18 -37
  15. package/dist/language-service/index.js +26 -42
  16. package/dist/language-service/line-column-offset.js +44 -0
  17. package/dist/language-service/rename.js +24 -37
  18. package/dist/logger.js +18 -5
  19. package/dist/module-loader.js +28 -22
  20. package/dist/project-astro-files.js +125 -0
  21. package/dist/utils.js +34 -19
  22. package/dist/workers/TSXService.js +39 -0
  23. package/dist/workers/TSXWorker.js +9 -0
  24. package/package.json +36 -27
  25. package/src/astro-snapshots.ts +270 -291
  26. package/src/astro-sys.ts +22 -22
  27. package/src/astro2tsx.ts +5 -50
  28. package/src/index.ts +49 -60
  29. package/src/language-service/completions.ts +56 -64
  30. package/src/language-service/definition.ts +35 -42
  31. package/src/language-service/diagnostics.ts +32 -33
  32. package/src/language-service/file-references.ts +31 -0
  33. package/src/language-service/find-references.ts +67 -76
  34. package/src/language-service/implementation.ts +27 -34
  35. package/src/language-service/index.ts +17 -32
  36. package/src/language-service/line-column-offset.ts +21 -0
  37. package/src/language-service/rename.ts +38 -48
  38. package/src/logger.ts +32 -36
  39. package/src/module-loader.ts +124 -124
  40. package/src/project-astro-files.ts +130 -0
  41. package/src/utils.ts +54 -45
  42. package/src/workers/TSXService.ts +11 -0
  43. package/src/workers/TSXWorker.ts +8 -0
  44. package/test/fixtures/MyAstroComponent.astro +5 -0
  45. package/test/fixtures/script.ts +9 -0
  46. package/test/fixtures/tsconfig.json +3 -0
  47. package/test/runTest.js +41 -0
  48. package/test/suite/extension.test.js +68 -0
  49. package/test/suite/index.js +38 -0
  50. package/test/tsconfig.json +14 -0
  51. package/test/utils.js +65 -0
  52. package/tsconfig.json +2 -1
  53. package/dist/source-mapper.js +0 -114
  54. package/src/source-mapper.ts +0 -123
@@ -1,297 +1,276 @@
1
- import { astro2tsx } from './astro2tsx.js';
1
+ import { EncodedSourceMap, originalPositionFor, TraceMap } from '@jridgewell/trace-mapping';
2
2
  import type ts from 'typescript/lib/tsserverlibrary';
3
- import { Logger } from './logger.js';
4
- import { SourceMapper } from './source-mapper.js';
5
- import { isNoTextSpanInGeneratedCode, isAstroFilePath } from './utils.js';
3
+ import { astro2tsx } from './astro2tsx.js';
4
+ import type { Logger } from './logger.js';
5
+ import { isAstroFilePath } from './utils.js';
6
6
 
7
7
  export class AstroSnapshot {
8
- private scriptInfo?: ts.server.ScriptInfo;
9
- private lineOffsets?: number[];
10
- private convertInternalCodePositions = false;
11
-
12
- constructor(
13
- private typescript: typeof ts,
14
- private fileName: string,
15
- private astroCode: string,
16
- private mapper: SourceMapper,
17
- private logger: Logger,
18
- public readonly isTsFile: boolean
19
- ) {}
20
-
21
- update(astroCode: string, mapper: SourceMapper) {
22
- this.astroCode = astroCode;
23
- this.mapper = mapper;
24
- this.lineOffsets = undefined;
25
- this.log('Updated Snapshot');
26
- }
27
-
28
- getOriginalTextSpan(textSpan: ts.TextSpan): ts.TextSpan | null {
29
- if (!isNoTextSpanInGeneratedCode(this.getText(), textSpan)) {
30
- return null;
31
- }
32
-
33
- const start = this.getOriginalOffset(textSpan.start);
34
- if (start === -1) {
35
- return null;
36
- }
37
-
38
- // Assumption: We don't change identifiers itself, so we don't change ranges.
39
- return {
40
- start,
41
- length: textSpan.length
42
- };
43
- }
44
-
45
- getOriginalOffset(generatedOffset: number) {
46
- if (!this.scriptInfo) {
47
- return generatedOffset;
48
- }
49
-
50
- this.toggleMappingMode(true);
51
- const lineOffset = this.scriptInfo.positionToLineOffset(generatedOffset);
52
- this.debug('try convert offset', generatedOffset, '/', lineOffset);
53
- const original = this.mapper.getOriginalPosition({
54
- line: lineOffset.line - 1,
55
- character: lineOffset.offset - 1
56
- });
57
- this.toggleMappingMode(false);
58
- if (original.line === -1) {
59
- return -1;
60
- }
61
-
62
- const originalOffset = this.scriptInfo.lineOffsetToPosition(
63
- original.line + 1,
64
- original.character + 1
65
- );
66
- this.debug('converted offset to', original, '/', originalOffset);
67
- return originalOffset;
68
- }
69
-
70
- setAndPatchScriptInfo(scriptInfo: ts.server.ScriptInfo) {
71
- // @ts-expect-error
72
- scriptInfo.scriptKind = this.typescript.ScriptKind.TSX;
73
-
74
- const positionToLineOffset = scriptInfo.positionToLineOffset.bind(scriptInfo);
75
- scriptInfo.positionToLineOffset = (position) => {
76
- if (this.convertInternalCodePositions) {
77
- const lineOffset = positionToLineOffset(position);
78
- this.debug('positionToLineOffset for generated code', position, lineOffset);
79
- return lineOffset;
80
- }
81
-
82
- const lineOffset = this.positionAt(position);
83
- this.debug('positionToLineOffset for original code', position, lineOffset);
84
- return { line: lineOffset.line + 1, offset: lineOffset.character + 1 };
85
- };
86
-
87
- const lineOffsetToPosition = scriptInfo.lineOffsetToPosition.bind(scriptInfo);
88
- scriptInfo.lineOffsetToPosition = (line, offset) => {
89
- if (this.convertInternalCodePositions) {
90
- const position = lineOffsetToPosition(line, offset);
91
- this.debug('lineOffsetToPosition for generated code', { line, offset }, position);
92
- return position;
93
- }
94
-
95
- const position = this.offsetAt({ line: line - 1, character: offset - 1 });
96
- this.debug('lineOffsetToPosition for original code', { line, offset }, position);
97
- return position;
98
- };
99
-
100
- this.scriptInfo = scriptInfo;
101
- this.log('patched scriptInfo');
102
- }
103
-
104
- /**
105
- * Get the line and character based on the offset
106
- * @param offset The index of the position
107
- */
108
- positionAt(offset: number): ts.LineAndCharacter {
109
- offset = this.clamp(offset, 0, this.astroCode.length);
110
-
111
- const lineOffsets = this.getLineOffsets();
112
- let low = 0;
113
- let high = lineOffsets.length;
114
- if (high === 0) {
115
- return { line: 0, character: offset };
116
- }
117
-
118
- while (low < high) {
119
- const mid = Math.floor((low + high) / 2);
120
- if (lineOffsets[mid] > offset) {
121
- high = mid;
122
- } else {
123
- low = mid + 1;
124
- }
125
- }
126
-
127
- // low is the least x for which the line offset is larger than the current offset
128
- // or array.length if no line offset is larger than the current offset
129
- const line = low - 1;
130
-
131
- return { line, character: offset - lineOffsets[line] };
132
- }
133
-
134
- /**
135
- * Get the index of the line and character position
136
- * @param position Line and character position
137
- */
138
- offsetAt(position: ts.LineAndCharacter): number {
139
- const lineOffsets = this.getLineOffsets();
140
-
141
- if (position.line >= lineOffsets.length) {
142
- return this.astroCode.length;
143
- } else if (position.line < 0) {
144
- return 0;
145
- }
146
-
147
- const lineOffset = lineOffsets[position.line];
148
- const nextLineOffset =
149
- position.line + 1 < lineOffsets.length
150
- ? lineOffsets[position.line + 1]
151
- : this.astroCode.length;
152
-
153
- return this.clamp(nextLineOffset, lineOffset, lineOffset + position.character);
154
- }
155
-
156
- private getLineOffsets() {
157
- if (this.lineOffsets) {
158
- return this.lineOffsets;
159
- }
160
-
161
- const lineOffsets = [];
162
- const text = this.astroCode;
163
- let isLineStart = true;
164
-
165
- for (let i = 0; i < text.length; i++) {
166
- if (isLineStart) {
167
- lineOffsets.push(i);
168
- isLineStart = false;
169
- }
170
- const ch = text.charAt(i);
171
- isLineStart = ch === '\r' || ch === '\n';
172
- if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') {
173
- i++;
174
- }
175
- }
176
-
177
- if (isLineStart && text.length > 0) {
178
- lineOffsets.push(text.length);
179
- }
180
-
181
- this.lineOffsets = lineOffsets;
182
- return lineOffsets;
183
- }
184
-
185
- private clamp(num: number, min: number, max: number): number {
186
- return Math.max(min, Math.min(max, num));
187
- }
188
-
189
- private log(...args: any[]) {
190
- this.logger.log('AstroSnapshot:', this.fileName, '-', ...args);
191
- }
192
-
193
- private debug(...args: any[]) {
194
- this.logger.debug('AstroSnapshot:', this.fileName, '-', ...args);
195
- }
196
-
197
- private toggleMappingMode(convertInternalCodePositions: boolean) {
198
- this.convertInternalCodePositions = convertInternalCodePositions;
199
- }
200
-
201
- private getText() {
202
- const snapshot = this.scriptInfo?.getSnapshot();
203
- if (!snapshot) {
204
- return '';
205
- }
206
- return snapshot.getText(0, snapshot.getLength());
207
- }
8
+ private scriptInfo?: ts.server.ScriptInfo;
9
+ private lineOffsets?: number[];
10
+ private convertInternalCodePositions = false;
11
+
12
+ constructor(
13
+ private typescript: typeof ts,
14
+ private fileName: string,
15
+ private astroCode: string,
16
+ private traceMap: TraceMap,
17
+ private logger: Logger
18
+ ) {}
19
+
20
+ update(astroCode: string, traceMap: TraceMap) {
21
+ this.astroCode = astroCode;
22
+ this.traceMap = traceMap;
23
+ this.lineOffsets = undefined;
24
+ this.log('Updated Snapshot');
25
+ }
26
+
27
+ getOriginalTextSpan(textSpan: ts.TextSpan): ts.TextSpan | null {
28
+ const start = this.getOriginalOffset(textSpan.start);
29
+ if (start === -1) {
30
+ return null;
31
+ }
32
+
33
+ // Assumption: We don't change identifiers itself, so we don't change ranges.
34
+ return {
35
+ start,
36
+ length: textSpan.length,
37
+ };
38
+ }
39
+
40
+ getOriginalOffset(generatedOffset: number) {
41
+ if (!this.scriptInfo) {
42
+ return generatedOffset;
43
+ }
44
+
45
+ this.toggleMappingMode(true);
46
+ const lineOffset = this.scriptInfo.positionToLineOffset(generatedOffset);
47
+ this.debug('try convert offset', generatedOffset, '/', lineOffset);
48
+ const original = originalPositionFor(this.traceMap, {
49
+ line: lineOffset.line,
50
+ column: lineOffset.offset,
51
+ });
52
+ this.toggleMappingMode(false);
53
+
54
+ if (!original.line) {
55
+ return -1;
56
+ }
57
+
58
+ const originalOffset = this.scriptInfo.lineOffsetToPosition(original.line, original.column);
59
+ this.debug('converted offset to', original, '/', originalOffset);
60
+ return originalOffset;
61
+ }
62
+
63
+ setAndPatchScriptInfo(scriptInfo: ts.server.ScriptInfo) {
64
+ // @ts-expect-error
65
+ scriptInfo.scriptKind = this.typescript.ScriptKind.TSX;
66
+
67
+ const positionToLineOffset = scriptInfo.positionToLineOffset.bind(scriptInfo);
68
+ scriptInfo.positionToLineOffset = (position) => {
69
+ if (this.convertInternalCodePositions) {
70
+ const lineOffset = positionToLineOffset(position);
71
+ this.debug('positionToLineOffset for generated code', position, lineOffset);
72
+ return lineOffset;
73
+ }
74
+
75
+ const lineOffset = this.positionAt(position);
76
+ this.debug('positionToLineOffset for original code', position, lineOffset);
77
+ return { line: lineOffset.line + 1, offset: lineOffset.character + 1 };
78
+ };
79
+
80
+ const lineOffsetToPosition = scriptInfo.lineOffsetToPosition.bind(scriptInfo);
81
+ scriptInfo.lineOffsetToPosition = (line, offset) => {
82
+ if (this.convertInternalCodePositions) {
83
+ const position = lineOffsetToPosition(line, offset);
84
+ this.debug('lineOffsetToPosition for generated code', { line, offset }, position);
85
+ return position;
86
+ }
87
+
88
+ const position = this.offsetAt({ line: line - 1, character: offset - 1 });
89
+ this.debug('lineOffsetToPosition for original code', { line, offset }, position);
90
+ return position;
91
+ };
92
+
93
+ this.scriptInfo = scriptInfo;
94
+ this.log('patched scriptInfo');
95
+ }
96
+
97
+ /**
98
+ * Get the line and character based on the offset
99
+ * @param offset The index of the position
100
+ */
101
+ positionAt(offset: number): ts.LineAndCharacter {
102
+ offset = this.clamp(offset, 0, this.astroCode.length);
103
+
104
+ const lineOffsets = this.getLineOffsets();
105
+ let low = 0;
106
+ let high = lineOffsets.length;
107
+ if (high === 0) {
108
+ return { line: 0, character: offset };
109
+ }
110
+
111
+ while (low < high) {
112
+ const mid = Math.floor((low + high) / 2);
113
+ if (lineOffsets[mid] > offset) {
114
+ high = mid;
115
+ } else {
116
+ low = mid + 1;
117
+ }
118
+ }
119
+
120
+ // low is the least x for which the line offset is larger than the current offset
121
+ // or array.length if no line offset is larger than the current offset
122
+ const line = low - 1;
123
+
124
+ return { line, character: offset - lineOffsets[line] };
125
+ }
126
+
127
+ /**
128
+ * Get the index of the line and character position
129
+ * @param position Line and character position
130
+ */
131
+ offsetAt(position: ts.LineAndCharacter): number {
132
+ const lineOffsets = this.getLineOffsets();
133
+
134
+ if (position.line >= lineOffsets.length) {
135
+ return this.astroCode.length;
136
+ } else if (position.line < 0) {
137
+ return 0;
138
+ }
139
+
140
+ const lineOffset = lineOffsets[position.line];
141
+ const nextLineOffset =
142
+ position.line + 1 < lineOffsets.length ? lineOffsets[position.line + 1] : this.astroCode.length;
143
+
144
+ return this.clamp(nextLineOffset, lineOffset, lineOffset + position.character);
145
+ }
146
+
147
+ private getLineOffsets() {
148
+ if (this.lineOffsets) {
149
+ return this.lineOffsets;
150
+ }
151
+
152
+ const lineOffsets = [];
153
+ const text = this.astroCode;
154
+ let isLineStart = true;
155
+
156
+ for (let i = 0; i < text.length; i++) {
157
+ if (isLineStart) {
158
+ lineOffsets.push(i);
159
+ isLineStart = false;
160
+ }
161
+ const ch = text.charAt(i);
162
+ isLineStart = ch === '\r' || ch === '\n';
163
+ if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') {
164
+ i++;
165
+ }
166
+ }
167
+
168
+ if (isLineStart && text.length > 0) {
169
+ lineOffsets.push(text.length);
170
+ }
171
+
172
+ this.lineOffsets = lineOffsets;
173
+ return lineOffsets;
174
+ }
175
+
176
+ private clamp(num: number, min: number, max: number): number {
177
+ return Math.max(min, Math.min(max, num));
178
+ }
179
+
180
+ private log(...args: any[]) {
181
+ this.logger.log('AstroSnapshot:', this.fileName, '-', ...args);
182
+ }
183
+
184
+ private debug(...args: any[]) {
185
+ this.logger.debug('AstroSnapshot:', this.fileName, '-', ...args);
186
+ }
187
+
188
+ private toggleMappingMode(convertInternalCodePositions: boolean) {
189
+ this.convertInternalCodePositions = convertInternalCodePositions;
190
+ }
191
+
192
+ private getText() {
193
+ const snapshot = this.scriptInfo?.getSnapshot();
194
+ if (!snapshot) {
195
+ return '';
196
+ }
197
+ return snapshot.getText(0, snapshot.getLength());
198
+ }
208
199
  }
209
200
 
210
201
  export class AstroSnapshotManager {
211
- private snapshots = new Map<string, AstroSnapshot>();
212
-
213
- constructor(
214
- private typescript: typeof ts,
215
- private projectService: ts.server.ProjectService,
216
- private logger: Logger
217
- ) {
218
- this.patchProjectServiceReadFile();
219
- }
220
-
221
- get(fileName: string) {
222
- return this.snapshots.get(fileName);
223
- }
224
-
225
- create(fileName: string): AstroSnapshot | undefined {
226
- if (this.snapshots.has(fileName)) {
227
- return this.snapshots.get(fileName)!;
228
- }
229
-
230
- // This will trigger projectService.host.readFile which is patched below
231
- const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(
232
- this.typescript.server.toNormalizedPath(fileName),
233
- false
234
- );
235
- if (!scriptInfo) {
236
- this.logger.log('Was not able get snapshot for', fileName);
237
- return;
238
- }
239
-
240
- try {
241
- scriptInfo.getSnapshot(); // needed to trigger readFile
242
- } catch (e) {
243
- this.logger.log('Loading Snapshot failed', fileName);
244
- }
245
- const snapshot = this.snapshots.get(fileName);
246
- if (!snapshot) {
247
- this.logger.log(
248
- 'Astro snapshot was not found after trying to load script snapshot for',
249
- fileName
250
- );
251
- return; // should never get here
252
- }
253
- snapshot.setAndPatchScriptInfo(scriptInfo);
254
- this.snapshots.set(fileName, snapshot);
255
- return snapshot;
256
- }
257
-
258
- private patchProjectServiceReadFile() {
259
- const readFile = this.projectService.host.readFile;
260
- this.projectService.host.readFile = (path: string) => {
261
- if (isAstroFilePath(path)) {
262
- this.logger.debug('Read Astro file:', path);
263
- const astroCode = readFile(path) || '';
264
- try {
265
- const isTsFile = true;
266
- const result = astro2tsx(astroCode, {
267
- filename: path.split('/').pop(),
268
- isTsFile
269
- });
270
- const existingSnapshot = this.snapshots.get(path);
271
- if (existingSnapshot) {
272
- existingSnapshot.update(astroCode, new SourceMapper(result.map.mappings));
273
- } else {
274
- this.snapshots.set(
275
- path,
276
- new AstroSnapshot(
277
- this.typescript,
278
- path,
279
- astroCode,
280
- new SourceMapper(result.map.mappings),
281
- this.logger,
282
- isTsFile
283
- )
284
- );
285
- }
286
- this.logger.log('Successfully read Astro file contents of', path);
287
- return result.code;
288
- } catch (e) {
289
- this.logger.log('Error loading Astro file:', path);
290
- this.logger.debug('Error:', e);
291
- }
292
- } else {
293
- return readFile(path);
294
- }
295
- };
296
- }
297
- }
202
+ private snapshots = new Map<string, AstroSnapshot>();
203
+
204
+ constructor(private typescript: typeof ts, private projectService: ts.server.ProjectService, private logger: Logger) {
205
+ this.patchProjectServiceReadFile();
206
+ }
207
+
208
+ get(fileName: string) {
209
+ return this.snapshots.get(fileName);
210
+ }
211
+
212
+ create(fileName: string): AstroSnapshot | undefined {
213
+ if (this.snapshots.has(fileName)) {
214
+ return this.snapshots.get(fileName)!;
215
+ }
216
+
217
+ // This will trigger projectService.host.readFile which is patched below
218
+ const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(
219
+ this.typescript.server.toNormalizedPath(fileName),
220
+ false
221
+ );
222
+ if (!scriptInfo) {
223
+ this.logger.log('Was not able get snapshot for', fileName);
224
+ return;
225
+ }
226
+
227
+ try {
228
+ scriptInfo.getSnapshot(); // needed to trigger readFile
229
+ } catch (e) {
230
+ this.logger.log('Loading Snapshot failed', fileName);
231
+ }
232
+ const snapshot = this.snapshots.get(fileName);
233
+ if (!snapshot) {
234
+ this.logger.log('Astro snapshot was not found after trying to load script snapshot for', fileName);
235
+ return; // should never get here
236
+ }
237
+ snapshot.setAndPatchScriptInfo(scriptInfo);
238
+ this.snapshots.set(fileName, snapshot);
239
+ return snapshot;
240
+ }
241
+
242
+ private patchProjectServiceReadFile() {
243
+ const readFile = this.projectService.host.readFile;
244
+ this.projectService.host.readFile = (path: string) => {
245
+ if (isAstroFilePath(path)) {
246
+ this.logger.debug('Read Astro file:', path);
247
+ const astroCode = readFile(path) || '';
248
+ try {
249
+ const result = astro2tsx(astroCode, path);
250
+ const existingSnapshot = this.snapshots.get(path);
251
+ if (existingSnapshot) {
252
+ existingSnapshot.update(astroCode, new TraceMap(result.map as EncodedSourceMap));
253
+ } else {
254
+ this.snapshots.set(
255
+ path,
256
+ new AstroSnapshot(
257
+ this.typescript,
258
+ path,
259
+ astroCode,
260
+ new TraceMap(result.map as EncodedSourceMap),
261
+ this.logger
262
+ )
263
+ );
264
+ }
265
+ this.logger.log('Successfully read Astro file contents of', path);
266
+ return result.code;
267
+ } catch (e) {
268
+ this.logger.log('Error loading Astro file:', path);
269
+ this.logger.debug('Error:', e);
270
+ }
271
+ } else {
272
+ return readFile(path);
273
+ }
274
+ };
275
+ }
276
+ }
package/src/astro-sys.ts CHANGED
@@ -1,32 +1,32 @@
1
1
  import ts from 'typescript';
2
- import { Logger } from './logger.js';
2
+ import type { Logger } from './logger.js';
3
3
  import { ensureRealAstroFilePath, isVirtualAstroFilePath, toRealAstroFilePath } from './utils.js';
4
4
 
5
5
  /**
6
6
  * This should only be accessed by TS astro module resolution.
7
7
  */
8
8
  export function createAstroSys(logger: Logger) {
9
- const astroSys: ts.System = {
10
- ...ts.sys,
11
- fileExists(path: string) {
12
- return ts.sys.fileExists(ensureRealAstroFilePath(path));
13
- },
14
- readDirectory(path, extensions, exclude, include, depth) {
15
- const extensionsWithAstro = (extensions ?? []).concat('.astro');
9
+ const astroSys: ts.System = {
10
+ ...ts.sys,
11
+ fileExists(path: string) {
12
+ return ts.sys.fileExists(ensureRealAstroFilePath(path));
13
+ },
14
+ readDirectory(path, extensions, exclude, include, depth) {
15
+ const extensionsWithAstro = (extensions ?? []).concat('.astro');
16
16
 
17
- return ts.sys.readDirectory(path, extensionsWithAstro, exclude, include, depth);
18
- }
19
- };
17
+ return ts.sys.readDirectory(path, extensionsWithAstro, exclude, include, depth);
18
+ },
19
+ };
20
20
 
21
- if (ts.sys.realpath) {
22
- const realpath = ts.sys.realpath;
23
- astroSys.realpath = function (path) {
24
- if (isVirtualAstroFilePath(path)) {
25
- return realpath(toRealAstroFilePath(path)) + '.ts';
26
- }
27
- return realpath(path);
28
- };
29
- }
21
+ if (ts.sys.realpath) {
22
+ const realpath = ts.sys.realpath;
23
+ astroSys.realpath = function (path) {
24
+ if (isVirtualAstroFilePath(path)) {
25
+ return realpath(toRealAstroFilePath(path)) + '.ts';
26
+ }
27
+ return realpath(path);
28
+ };
29
+ }
30
30
 
31
- return astroSys;
32
- }
31
+ return astroSys;
32
+ }