@astrojs/ts-plugin 0.2.1 → 0.3.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/dist/astro-snapshots.js +1 -1
- package/dist/astro-sys.js +1 -1
- package/dist/astro2tsx.js +2 -22
- package/dist/index.js +2 -2
- package/dist/language-service/completions.js +1 -1
- package/dist/language-service/definition.js +1 -1
- package/dist/language-service/diagnostics.js +1 -1
- package/dist/language-service/find-references.js +1 -1
- package/dist/language-service/implementation.js +1 -1
- package/dist/language-service/index.js +1 -1
- package/dist/language-service/rename.js +1 -1
- package/dist/logger.js +1 -1
- package/dist/module-loader.js +1 -1
- package/dist/source-mapper.js +2 -6
- package/dist/utils.js +1 -1
- package/package.json +25 -26
- package/src/astro-snapshots.ts +277 -289
- package/src/astro-sys.ts +21 -21
- package/src/astro2tsx.ts +27 -31
- package/src/index.ts +42 -53
- package/src/language-service/completions.ts +44 -63
- package/src/language-service/definition.ts +37 -39
- package/src/language-service/diagnostics.ts +31 -31
- package/src/language-service/find-references.ts +66 -75
- package/src/language-service/implementation.ts +29 -31
- package/src/language-service/index.ts +27 -27
- package/src/language-service/rename.ts +37 -47
- package/src/logger.ts +32 -36
- package/src/module-loader.ts +102 -110
- package/src/source-mapper.ts +83 -99
- package/src/utils.ts +39 -39
|
@@ -1,38 +1,36 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { Logger } from '../logger.js';
|
|
3
2
|
import { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
4
|
-
import {
|
|
3
|
+
import { Logger } from '../logger.js';
|
|
4
|
+
import { isAstroFilePath, isNotNullOrUndefined } from '../utils.js';
|
|
5
5
|
|
|
6
6
|
export function decorateGetImplementation(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
ls: ts.LanguageService,
|
|
8
|
+
snapshotManager: AstroSnapshotManager,
|
|
9
|
+
logger: Logger
|
|
10
10
|
): void {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
const getImplementationAtPosition = ls.getImplementationAtPosition;
|
|
12
|
+
ls.getImplementationAtPosition = (fileName, position) => {
|
|
13
|
+
const implementation = getImplementationAtPosition(fileName, position);
|
|
14
|
+
return implementation
|
|
15
|
+
?.map((impl) => {
|
|
16
|
+
if (!isAstroFilePath(impl.fileName)) {
|
|
17
|
+
return impl;
|
|
18
|
+
}
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
20
|
+
const textSpan = snapshotManager.get(impl.fileName)?.getOriginalTextSpan(impl.textSpan);
|
|
21
|
+
if (!textSpan) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
25
|
+
return {
|
|
26
|
+
...impl,
|
|
27
|
+
textSpan,
|
|
28
|
+
// Spare the work for now
|
|
29
|
+
contextSpan: undefined,
|
|
30
|
+
originalTextSpan: undefined,
|
|
31
|
+
originalContextSpan: undefined,
|
|
32
|
+
};
|
|
33
|
+
})
|
|
34
|
+
.filter(isNotNullOrUndefined);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { Logger } from '../logger';
|
|
3
2
|
import { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
+
import { Logger } from '../logger';
|
|
4
4
|
import { isAstroFilePath } from '../utils.js';
|
|
5
5
|
import { decorateCompletions } from './completions.js';
|
|
6
6
|
import { decorateGetDefinition } from './definition.js';
|
|
@@ -10,34 +10,34 @@ import { decorateGetImplementation } from './implementation.js';
|
|
|
10
10
|
import { decorateRename } from './rename.js';
|
|
11
11
|
|
|
12
12
|
export function decorateLanguageService(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
ls: ts.LanguageService,
|
|
14
|
+
snapshotManager: AstroSnapshotManager,
|
|
15
|
+
logger: Logger
|
|
16
16
|
): ts.LanguageService {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
patchLineColumnOffset(ls, snapshotManager);
|
|
18
|
+
decorateRename(ls, snapshotManager, logger);
|
|
19
|
+
decorateDiagnostics(ls, logger);
|
|
20
|
+
decorateFindReferences(ls, snapshotManager, logger);
|
|
21
|
+
decorateCompletions(ls, logger);
|
|
22
|
+
decorateGetDefinition(ls, snapshotManager, logger);
|
|
23
|
+
decorateGetImplementation(ls, snapshotManager, logger);
|
|
24
|
+
return ls;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function patchLineColumnOffset(ls: ts.LanguageService, snapshotManager: AstroSnapshotManager) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
if (!ls.toLineColumnOffset) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
32
|
+
// We need to patch this because (according to source, only) getDefinition uses this
|
|
33
|
+
const toLineColumnOffset = ls.toLineColumnOffset;
|
|
34
|
+
ls.toLineColumnOffset = (fileName, position) => {
|
|
35
|
+
if (isAstroFilePath(fileName)) {
|
|
36
|
+
const snapshot = snapshotManager.get(fileName);
|
|
37
|
+
if (snapshot) {
|
|
38
|
+
return snapshot.positionAt(position);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return toLineColumnOffset(fileName, position);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -1,52 +1,42 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { Logger } from '../logger.js';
|
|
3
2
|
import { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
4
|
-
import {
|
|
3
|
+
import { Logger } from '../logger.js';
|
|
4
|
+
import { isAstroFilePath, isNotNullOrUndefined } from '../utils.js';
|
|
5
5
|
|
|
6
|
-
export function decorateRename(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
findInStrings,
|
|
23
|
-
findInComments,
|
|
24
|
-
providePrefixAndSuffixTextForRename
|
|
25
|
-
);
|
|
26
|
-
return renameLocations
|
|
27
|
-
?.map((renameLocation) => {
|
|
28
|
-
const snapshot = snapshotManager.get(renameLocation.fileName);
|
|
29
|
-
if (!isAstroFilePath(renameLocation.fileName) || !snapshot) {
|
|
30
|
-
return renameLocation;
|
|
31
|
-
}
|
|
6
|
+
export function decorateRename(ls: ts.LanguageService, snapshotManager: AstroSnapshotManager, logger: Logger): void {
|
|
7
|
+
const findRenameLocations = ls.findRenameLocations;
|
|
8
|
+
ls.findRenameLocations = (fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename) => {
|
|
9
|
+
const renameLocations = findRenameLocations(
|
|
10
|
+
fileName,
|
|
11
|
+
position,
|
|
12
|
+
findInStrings,
|
|
13
|
+
findInComments,
|
|
14
|
+
providePrefixAndSuffixTextForRename
|
|
15
|
+
);
|
|
16
|
+
return renameLocations
|
|
17
|
+
?.map((renameLocation) => {
|
|
18
|
+
const snapshot = snapshotManager.get(renameLocation.fileName);
|
|
19
|
+
if (!isAstroFilePath(renameLocation.fileName) || !snapshot) {
|
|
20
|
+
return renameLocation;
|
|
21
|
+
}
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
// TODO more needed to filter invalid locations, see RenameProvider
|
|
24
|
+
const textSpan = snapshot.getOriginalTextSpan(renameLocation.textSpan);
|
|
25
|
+
if (!textSpan) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
38
28
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
29
|
+
const converted = {
|
|
30
|
+
...renameLocation,
|
|
31
|
+
textSpan,
|
|
32
|
+
};
|
|
33
|
+
if (converted.contextSpan) {
|
|
34
|
+
// Not important, spare the work
|
|
35
|
+
converted.contextSpan = undefined;
|
|
36
|
+
}
|
|
37
|
+
logger.debug('Converted rename location ', converted);
|
|
38
|
+
return converted;
|
|
39
|
+
})
|
|
40
|
+
.filter(isNotNullOrUndefined);
|
|
41
|
+
};
|
|
42
|
+
}
|
package/src/logger.ts
CHANGED
|
@@ -1,41 +1,37 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
2
|
|
|
3
3
|
export class Logger {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
4
|
+
constructor(private tsLogService: ts.server.Logger, suppressNonAstroLogs = false, private logDebug = false) {
|
|
5
|
+
if (suppressNonAstroLogs) {
|
|
6
|
+
const log = this.tsLogService.info.bind(this.tsLogService);
|
|
7
|
+
this.tsLogService.info = (s: string) => {
|
|
8
|
+
if (s.startsWith('-Astro Plugin-')) {
|
|
9
|
+
log(s);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
15
|
+
log(...args: any[]) {
|
|
16
|
+
const str = args
|
|
17
|
+
.map((arg) => {
|
|
18
|
+
if (typeof arg === 'object') {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.stringify(arg);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return '[object that cannot by stringified]';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return arg;
|
|
26
|
+
})
|
|
27
|
+
.join(' ');
|
|
28
|
+
this.tsLogService.info('-Astro Plugin- ' + str);
|
|
29
|
+
}
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
31
|
+
debug(...args: any[]) {
|
|
32
|
+
if (!this.logDebug) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.log(...args);
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/module-loader.ts
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { Logger } from './logger.js';
|
|
3
2
|
import { AstroSnapshotManager } from './astro-snapshots.js';
|
|
4
3
|
import { createAstroSys } from './astro-sys.js';
|
|
4
|
+
import { Logger } from './logger.js';
|
|
5
5
|
import { ensureRealAstroFilePath, isVirtualAstroFilePath } from './utils.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Caches resolved modules.
|
|
9
9
|
*/
|
|
10
10
|
class ModuleResolutionCache {
|
|
11
|
-
|
|
11
|
+
private cache = new Map<string, ts.ResolvedModule>();
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Tries to get a cached module.
|
|
15
|
+
*/
|
|
16
|
+
get(moduleName: string, containingFile: string): ts.ResolvedModule | undefined {
|
|
17
|
+
return this.cache.get(this.getKey(moduleName, containingFile));
|
|
18
|
+
}
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Caches resolved module, if it is not undefined.
|
|
22
|
+
*/
|
|
23
|
+
set(moduleName: string, containingFile: string, resolvedModule: ts.ResolvedModule | undefined) {
|
|
24
|
+
if (!resolvedModule) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.cache.set(this.getKey(moduleName, containingFile), resolvedModule);
|
|
28
|
+
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Deletes module from cache. Call this if a file was deleted.
|
|
32
|
+
* @param resolvedModuleName full path of the module
|
|
33
|
+
*/
|
|
34
|
+
delete(resolvedModuleName: string): void {
|
|
35
|
+
this.cache.forEach((val, key) => {
|
|
36
|
+
if (val.resolvedFileName === resolvedModuleName) {
|
|
37
|
+
this.cache.delete(key);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
private getKey(moduleName: string, containingFile: string) {
|
|
43
|
+
return containingFile + ':::' + ensureRealAstroFilePath(moduleName);
|
|
44
|
+
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
@@ -54,92 +54,84 @@ class ModuleResolutionCache {
|
|
|
54
54
|
* In order to fix this, we need to wrap typescript's module resolution and reroute all `.astro.ts` file lookups to .astro.
|
|
55
55
|
*/
|
|
56
56
|
export function patchModuleLoader(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
logger: Logger,
|
|
58
|
+
snapshotManager: AstroSnapshotManager,
|
|
59
|
+
typescript: typeof ts,
|
|
60
|
+
lsHost: ts.LanguageServiceHost,
|
|
61
|
+
project: ts.server.Project
|
|
62
62
|
): void {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
const astroSys = createAstroSys(logger);
|
|
64
|
+
const moduleCache = new ModuleResolutionCache();
|
|
65
|
+
const origResolveModuleNames = lsHost.resolveModuleNames?.bind(lsHost);
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
lsHost.resolveModuleNames = resolveModuleNames;
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
const origRemoveFile = project.removeFile.bind(project);
|
|
70
|
+
project.removeFile = (info, fileExists, detachFromProject) => {
|
|
71
|
+
logger.log('File is being removed. Delete from cache: ', info.fileName);
|
|
72
|
+
moduleCache.delete(info.fileName);
|
|
73
|
+
return origRemoveFile(info, fileExists, detachFromProject);
|
|
74
|
+
};
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
containingFile,
|
|
92
|
-
reusedNames,
|
|
93
|
-
redirectedReference,
|
|
94
|
-
compilerOptions
|
|
95
|
-
) || Array.from<undefined>(Array(moduleNames.length));
|
|
76
|
+
function resolveModuleNames(
|
|
77
|
+
moduleNames: string[],
|
|
78
|
+
containingFile: string,
|
|
79
|
+
reusedNames: string[] | undefined,
|
|
80
|
+
redirectedReference: ts.ResolvedProjectReference | undefined,
|
|
81
|
+
compilerOptions: ts.CompilerOptions
|
|
82
|
+
): Array<ts.ResolvedModule | undefined> {
|
|
83
|
+
logger.log('Resolving modules names for ' + containingFile);
|
|
84
|
+
// Try resolving all module names with the original method first.
|
|
85
|
+
// The ones that are undefined will be re-checked if they are a
|
|
86
|
+
// astro file and if so, are resolved, too. This way we can defer
|
|
87
|
+
// all module resolving logic except for astro files to TypeScript.
|
|
88
|
+
const resolved =
|
|
89
|
+
origResolveModuleNames?.(moduleNames, containingFile, reusedNames, redirectedReference, compilerOptions) ||
|
|
90
|
+
Array.from<undefined>(Array(moduleNames.length));
|
|
96
91
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
return resolved.map((moduleName, idx) => {
|
|
93
|
+
const fileName = moduleNames[idx];
|
|
94
|
+
if (moduleName || !ensureRealAstroFilePath(fileName).endsWith('.astro')) {
|
|
95
|
+
return moduleName;
|
|
96
|
+
}
|
|
102
97
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
98
|
+
const cachedModule = moduleCache.get(fileName, containingFile);
|
|
99
|
+
if (cachedModule) {
|
|
100
|
+
return cachedModule;
|
|
101
|
+
}
|
|
107
102
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
103
|
+
const resolvedModule = resolveModuleName(fileName, containingFile, compilerOptions);
|
|
104
|
+
moduleCache.set(fileName, containingFile, resolvedModule);
|
|
105
|
+
return resolvedModule;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
113
108
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
) {
|
|
129
|
-
return astroResolvedModule;
|
|
130
|
-
}
|
|
109
|
+
function resolveModuleName(
|
|
110
|
+
name: string,
|
|
111
|
+
containingFile: string,
|
|
112
|
+
compilerOptions: ts.CompilerOptions
|
|
113
|
+
): ts.ResolvedModule | undefined {
|
|
114
|
+
const astroResolvedModule = typescript.resolveModuleName(
|
|
115
|
+
name,
|
|
116
|
+
containingFile,
|
|
117
|
+
compilerOptions,
|
|
118
|
+
astroSys
|
|
119
|
+
).resolvedModule;
|
|
120
|
+
if (!astroResolvedModule || !isVirtualAstroFilePath(astroResolvedModule.resolvedFileName)) {
|
|
121
|
+
return astroResolvedModule;
|
|
122
|
+
}
|
|
131
123
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
124
|
+
const resolvedFileName = ensureRealAstroFilePath(astroResolvedModule.resolvedFileName);
|
|
125
|
+
logger.log('Resolved', name, 'to astro file', resolvedFileName);
|
|
126
|
+
const snapshot = snapshotManager.create(resolvedFileName);
|
|
127
|
+
if (!snapshot) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
138
130
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
131
|
+
const resolvedAstroModule: ts.ResolvedModuleFull = {
|
|
132
|
+
extension: snapshot.isTsFile ? typescript.Extension.Tsx : typescript.Extension.Jsx,
|
|
133
|
+
resolvedFileName,
|
|
134
|
+
};
|
|
135
|
+
return resolvedAstroModule;
|
|
136
|
+
}
|
|
137
|
+
}
|