@astrojs/ts-plugin 1.0.9 → 1.0.10
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/astro-snapshots.js +234 -0
- package/dist/astro-sys.js +30 -0
- package/dist/astro2tsx.js +26 -0
- package/dist/index.js +50 -0
- package/dist/language-service/completions.js +45 -0
- package/dist/language-service/definition.js +38 -0
- package/dist/language-service/diagnostics.js +62 -0
- package/dist/language-service/file-references.js +30 -0
- package/dist/language-service/find-references.js +66 -0
- package/dist/language-service/implementation.js +30 -0
- package/dist/language-service/index.js +44 -0
- package/dist/language-service/line-column-offset.js +21 -0
- package/dist/language-service/rename.js +34 -0
- package/dist/logger.js +40 -0
- package/dist/module-loader.js +146 -0
- package/dist/project-astro-files.js +102 -0
- package/dist/utils.js +61 -0
- package/package.json +5 -1
- package/.turbo/turbo-build.log +0 -4
- package/CHANGELOG.md +0 -113
- package/src/astro-snapshots.ts +0 -285
- package/src/astro-sys.ts +0 -35
- package/src/astro2tsx.ts +0 -26
- package/src/index.ts +0 -84
- package/src/language-service/completions.ts +0 -73
- package/src/language-service/definition.ts +0 -42
- package/src/language-service/diagnostics.ts +0 -73
- package/src/language-service/file-references.ts +0 -34
- package/src/language-service/find-references.ts +0 -90
- package/src/language-service/implementation.ts +0 -34
- package/src/language-service/index.ts +0 -56
- package/src/language-service/line-column-offset.ts +0 -24
- package/src/language-service/rename.ts +0 -46
- package/src/logger.ts +0 -41
- package/src/module-loader.ts +0 -219
- package/src/project-astro-files.ts +0 -138
- package/src/utils.ts +0 -77
- package/test/fixtures/MyAstroComponent.astro +0 -5
- package/test/fixtures/script.ts +0 -9
- package/test/fixtures/tsconfig.json +0 -3
- package/test/runTest.js +0 -43
- package/test/suite/extension.test.js +0 -78
- package/test/suite/index.js +0 -38
- package/test/tsconfig.json +0 -14
- package/test/utils.js +0 -69
- package/tsconfig.json +0 -13
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import type { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
-
import type { Logger } from '../logger';
|
|
4
|
-
import { isAstroFilePath, isNotNullOrUndefined } from '../utils.js';
|
|
5
|
-
|
|
6
|
-
export function decorateFindReferences(
|
|
7
|
-
ls: ts.LanguageService,
|
|
8
|
-
snapshotManager: AstroSnapshotManager,
|
|
9
|
-
logger: Logger
|
|
10
|
-
): void {
|
|
11
|
-
decorateGetReferencesAtPosition(ls, snapshotManager, logger);
|
|
12
|
-
_decorateFindReferences(ls, snapshotManager, logger);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function _decorateFindReferences(
|
|
16
|
-
ls: ts.LanguageService,
|
|
17
|
-
snapshotManager: AstroSnapshotManager,
|
|
18
|
-
logger: Logger
|
|
19
|
-
) {
|
|
20
|
-
const findReferences = ls.findReferences;
|
|
21
|
-
ls.findReferences = (fileName, position) => {
|
|
22
|
-
const references = findReferences(fileName, position);
|
|
23
|
-
return references
|
|
24
|
-
?.map((reference) => {
|
|
25
|
-
const snapshot = snapshotManager.get(reference.definition.fileName);
|
|
26
|
-
if (!isAstroFilePath(reference.definition.fileName) || !snapshot) {
|
|
27
|
-
return reference;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const textSpan = snapshot.getOriginalTextSpan(reference.definition.textSpan);
|
|
31
|
-
if (!textSpan) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return {
|
|
36
|
-
definition: {
|
|
37
|
-
...reference.definition,
|
|
38
|
-
textSpan,
|
|
39
|
-
// Spare the work for now
|
|
40
|
-
originalTextSpan: undefined,
|
|
41
|
-
},
|
|
42
|
-
references: mapReferences(reference.references, snapshotManager, logger),
|
|
43
|
-
};
|
|
44
|
-
})
|
|
45
|
-
.filter(isNotNullOrUndefined);
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function decorateGetReferencesAtPosition(
|
|
50
|
-
ls: ts.LanguageService,
|
|
51
|
-
snapshotManager: AstroSnapshotManager,
|
|
52
|
-
logger: Logger
|
|
53
|
-
) {
|
|
54
|
-
const getReferencesAtPosition = ls.getReferencesAtPosition;
|
|
55
|
-
ls.getReferencesAtPosition = (fileName, position) => {
|
|
56
|
-
const references = getReferencesAtPosition(fileName, position);
|
|
57
|
-
return references && mapReferences(references, snapshotManager, logger);
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function mapReferences(
|
|
62
|
-
references: ts.ReferenceEntry[],
|
|
63
|
-
snapshotManager: AstroSnapshotManager,
|
|
64
|
-
logger: Logger
|
|
65
|
-
): ts.ReferenceEntry[] {
|
|
66
|
-
return references
|
|
67
|
-
.map((reference) => {
|
|
68
|
-
const snapshot = snapshotManager.get(reference.fileName);
|
|
69
|
-
if (!isAstroFilePath(reference.fileName) || !snapshot) {
|
|
70
|
-
return reference;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const textSpan = snapshot.getOriginalTextSpan(reference.textSpan);
|
|
74
|
-
if (!textSpan) {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
logger.debug('Find references; map textSpan: changed', reference.textSpan, 'to', textSpan);
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
...reference,
|
|
82
|
-
textSpan,
|
|
83
|
-
// Spare the work for now
|
|
84
|
-
contextSpan: undefined,
|
|
85
|
-
originalTextSpan: undefined,
|
|
86
|
-
originalContextSpan: undefined,
|
|
87
|
-
};
|
|
88
|
-
})
|
|
89
|
-
.filter(isNotNullOrUndefined);
|
|
90
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import type { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
-
import { isAstroFilePath, isNotNullOrUndefined } from '../utils.js';
|
|
4
|
-
|
|
5
|
-
export function decorateGetImplementation(
|
|
6
|
-
ls: ts.LanguageService,
|
|
7
|
-
snapshotManager: AstroSnapshotManager
|
|
8
|
-
): void {
|
|
9
|
-
const getImplementationAtPosition = ls.getImplementationAtPosition;
|
|
10
|
-
ls.getImplementationAtPosition = (fileName, position) => {
|
|
11
|
-
const implementation = getImplementationAtPosition(fileName, position);
|
|
12
|
-
return implementation
|
|
13
|
-
?.map((impl) => {
|
|
14
|
-
if (!isAstroFilePath(impl.fileName)) {
|
|
15
|
-
return impl;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const textSpan = snapshotManager.get(impl.fileName)?.getOriginalTextSpan(impl.textSpan);
|
|
19
|
-
if (!textSpan) {
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
...impl,
|
|
25
|
-
textSpan,
|
|
26
|
-
// Spare the work for now
|
|
27
|
-
contextSpan: undefined,
|
|
28
|
-
originalTextSpan: undefined,
|
|
29
|
-
originalContextSpan: undefined,
|
|
30
|
-
};
|
|
31
|
-
})
|
|
32
|
-
.filter(isNotNullOrUndefined);
|
|
33
|
-
};
|
|
34
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import type { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
-
import type { Logger } from '../logger';
|
|
4
|
-
import { decorateCompletions } from './completions.js';
|
|
5
|
-
import { decorateGetDefinition } from './definition.js';
|
|
6
|
-
import { decorateDiagnostics } from './diagnostics.js';
|
|
7
|
-
import { decorateGetFileReferences } from './file-references.js';
|
|
8
|
-
import { decorateFindReferences } from './find-references.js';
|
|
9
|
-
import { decorateGetImplementation } from './implementation.js';
|
|
10
|
-
import { decorateLineColumnOffset } from './line-column-offset.js';
|
|
11
|
-
import { decorateRename } from './rename.js';
|
|
12
|
-
|
|
13
|
-
const astroPluginPatchSymbol = Symbol('astroPluginPatchSymbol');
|
|
14
|
-
|
|
15
|
-
export function isPatched(ls: ts.LanguageService) {
|
|
16
|
-
return (ls as any)[astroPluginPatchSymbol] === true;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function decorateLanguageService(
|
|
20
|
-
ls: ts.LanguageService,
|
|
21
|
-
snapshotManager: AstroSnapshotManager,
|
|
22
|
-
ts: typeof import('typescript/lib/tsserverlibrary'),
|
|
23
|
-
logger: Logger
|
|
24
|
-
): ts.LanguageService {
|
|
25
|
-
const proxy = new Proxy(ls, createProxyHandler());
|
|
26
|
-
|
|
27
|
-
decorateLineColumnOffset(proxy, snapshotManager);
|
|
28
|
-
decorateRename(proxy, snapshotManager, logger);
|
|
29
|
-
decorateDiagnostics(proxy, ts);
|
|
30
|
-
decorateFindReferences(proxy, snapshotManager, logger);
|
|
31
|
-
decorateCompletions(proxy, logger);
|
|
32
|
-
decorateGetDefinition(proxy, snapshotManager);
|
|
33
|
-
decorateGetImplementation(proxy, snapshotManager);
|
|
34
|
-
decorateGetFileReferences(proxy, snapshotManager);
|
|
35
|
-
|
|
36
|
-
return proxy;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function createProxyHandler(): ProxyHandler<ts.LanguageService> {
|
|
40
|
-
const decorated: Partial<ts.LanguageService> = {};
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
get(target, p) {
|
|
44
|
-
if (p === astroPluginPatchSymbol) {
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return decorated[p as keyof ts.LanguageService] ?? target[p as keyof ts.LanguageService];
|
|
49
|
-
},
|
|
50
|
-
set(_, p, value) {
|
|
51
|
-
decorated[p as keyof ts.LanguageService] = value;
|
|
52
|
-
|
|
53
|
-
return true;
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import type { AstroSnapshotManager } from '../astro-snapshots';
|
|
3
|
-
import { isAstroFilePath } from '../utils';
|
|
4
|
-
|
|
5
|
-
export function decorateLineColumnOffset(
|
|
6
|
-
ls: ts.LanguageService,
|
|
7
|
-
snapshotManager: AstroSnapshotManager
|
|
8
|
-
) {
|
|
9
|
-
if (!ls.toLineColumnOffset) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// We need to patch this because (according to source, only) getDefinition uses this
|
|
14
|
-
const toLineColumnOffset = ls.toLineColumnOffset;
|
|
15
|
-
ls.toLineColumnOffset = (fileName, position) => {
|
|
16
|
-
if (isAstroFilePath(fileName)) {
|
|
17
|
-
const snapshot = snapshotManager.get(fileName);
|
|
18
|
-
if (snapshot) {
|
|
19
|
-
return snapshot.positionAt(position);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return toLineColumnOffset(fileName, position);
|
|
23
|
-
};
|
|
24
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import type { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
-
import type { Logger } from '../logger.js';
|
|
4
|
-
import { isAstroFilePath, isNotNullOrUndefined } from '../utils.js';
|
|
5
|
-
|
|
6
|
-
export function decorateRename(
|
|
7
|
-
ls: ts.LanguageService,
|
|
8
|
-
snapshotManager: AstroSnapshotManager,
|
|
9
|
-
logger: Logger
|
|
10
|
-
): void {
|
|
11
|
-
const findRenameLocations = ls.findRenameLocations;
|
|
12
|
-
ls.findRenameLocations = (fileName, position, findInStrings, findInComments, preferences) => {
|
|
13
|
-
const renameLocations = findRenameLocations(
|
|
14
|
-
fileName,
|
|
15
|
-
position,
|
|
16
|
-
findInStrings,
|
|
17
|
-
findInComments,
|
|
18
|
-
preferences as ts.UserPreferences
|
|
19
|
-
);
|
|
20
|
-
return renameLocations
|
|
21
|
-
?.map((renameLocation) => {
|
|
22
|
-
const snapshot = snapshotManager.get(renameLocation.fileName);
|
|
23
|
-
if (!isAstroFilePath(renameLocation.fileName) || !snapshot) {
|
|
24
|
-
return renameLocation;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// TODO more needed to filter invalid locations, see RenameProvider
|
|
28
|
-
const textSpan = snapshot.getOriginalTextSpan(renameLocation.textSpan);
|
|
29
|
-
if (!textSpan) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const converted = {
|
|
34
|
-
...renameLocation,
|
|
35
|
-
textSpan,
|
|
36
|
-
};
|
|
37
|
-
if (converted.contextSpan) {
|
|
38
|
-
// Not important, spare the work
|
|
39
|
-
converted.contextSpan = undefined;
|
|
40
|
-
}
|
|
41
|
-
logger.debug('Converted rename location ', converted);
|
|
42
|
-
return converted;
|
|
43
|
-
})
|
|
44
|
-
.filter(isNotNullOrUndefined);
|
|
45
|
-
};
|
|
46
|
-
}
|
package/src/logger.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
|
|
3
|
-
export class Logger {
|
|
4
|
-
constructor(
|
|
5
|
-
private tsLogService: ts.server.Logger,
|
|
6
|
-
suppressNonAstroLogs = false,
|
|
7
|
-
private logDebug = false
|
|
8
|
-
) {
|
|
9
|
-
if (suppressNonAstroLogs) {
|
|
10
|
-
const log = this.tsLogService.info.bind(this.tsLogService);
|
|
11
|
-
this.tsLogService.info = (s: string) => {
|
|
12
|
-
if (s.startsWith('-Astro Plugin-')) {
|
|
13
|
-
log(s);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
log(...args: any[]) {
|
|
20
|
-
const str = args
|
|
21
|
-
.map((arg) => {
|
|
22
|
-
if (typeof arg === 'object') {
|
|
23
|
-
try {
|
|
24
|
-
return JSON.stringify(arg);
|
|
25
|
-
} catch (e) {
|
|
26
|
-
return '[object that cannot by stringified]';
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return arg;
|
|
30
|
-
})
|
|
31
|
-
.join(' ');
|
|
32
|
-
this.tsLogService.info('-Astro Plugin- ' + str);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
debug(...args: any[]) {
|
|
36
|
-
if (!this.logDebug) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
this.log(...args);
|
|
40
|
-
}
|
|
41
|
-
}
|
package/src/module-loader.ts
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import type { AstroSnapshotManager } from './astro-snapshots.js';
|
|
3
|
-
import { createAstroSys } from './astro-sys.js';
|
|
4
|
-
import type { Logger } from './logger.js';
|
|
5
|
-
import { ensureRealAstroFilePath, isVirtualAstroFilePath } from './utils.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Caches resolved modules.
|
|
9
|
-
*/
|
|
10
|
-
class ModuleResolutionCache {
|
|
11
|
-
private cache = new Map<string, ts.ResolvedModuleFull>();
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Tries to get a cached module.
|
|
15
|
-
*/
|
|
16
|
-
get(moduleName: string, containingFile: string): ts.ResolvedModuleFull | undefined {
|
|
17
|
-
return this.cache.get(this.getKey(moduleName, containingFile));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Caches resolved module, if it is not undefined.
|
|
22
|
-
*/
|
|
23
|
-
set(
|
|
24
|
-
moduleName: string,
|
|
25
|
-
containingFile: string,
|
|
26
|
-
resolvedModule: ts.ResolvedModuleFull | undefined
|
|
27
|
-
) {
|
|
28
|
-
if (!resolvedModule) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
this.cache.set(this.getKey(moduleName, containingFile), resolvedModule);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Deletes module from cache. Call this if a file was deleted.
|
|
36
|
-
* @param resolvedModuleName full path of the module
|
|
37
|
-
*/
|
|
38
|
-
delete(resolvedModuleName: string): void {
|
|
39
|
-
this.cache.forEach((val, key) => {
|
|
40
|
-
if (val.resolvedFileName === resolvedModuleName) {
|
|
41
|
-
this.cache.delete(key);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
private getKey(moduleName: string, containingFile: string) {
|
|
47
|
-
return containingFile + ':::' + ensureRealAstroFilePath(moduleName);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Creates a module loader than can also resolve `.astro` files.
|
|
53
|
-
*
|
|
54
|
-
* The typescript language service tries to look up other files that are referenced in the currently open astro file.
|
|
55
|
-
* For `.ts`/`.js` files this works, for `.astro` files it does not by default.
|
|
56
|
-
* Reason: The typescript language service does not know about the `.astro` file ending,
|
|
57
|
-
* so it assumes it's a normal typescript file and searches for files like `../Component.astro.ts`, which is wrong.
|
|
58
|
-
* In order to fix this, we need to wrap typescript's module resolution and reroute all `.astro.ts` file lookups to .astro.
|
|
59
|
-
*/
|
|
60
|
-
export function patchModuleLoader(
|
|
61
|
-
logger: Logger,
|
|
62
|
-
snapshotManager: AstroSnapshotManager,
|
|
63
|
-
typescript: typeof ts,
|
|
64
|
-
lsHost: ts.LanguageServiceHost,
|
|
65
|
-
project: ts.server.Project
|
|
66
|
-
): void {
|
|
67
|
-
const astroSys = createAstroSys(logger, typescript);
|
|
68
|
-
const moduleCache = new ModuleResolutionCache();
|
|
69
|
-
const origResolveModuleNames = lsHost.resolveModuleNames?.bind(lsHost);
|
|
70
|
-
const origResolveModuleNamLiterals = lsHost.resolveModuleNameLiterals?.bind(lsHost);
|
|
71
|
-
|
|
72
|
-
if (lsHost.resolveModuleNameLiterals) {
|
|
73
|
-
lsHost.resolveModuleNameLiterals = resolveModuleNameLiterals;
|
|
74
|
-
} else {
|
|
75
|
-
lsHost.resolveModuleNames = resolveModuleNames;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const origRemoveFile = project.removeFile.bind(project);
|
|
79
|
-
project.removeFile = (info, fileExists, detachFromProject) => {
|
|
80
|
-
logger.log('File is being removed. Delete from cache: ', info.fileName);
|
|
81
|
-
moduleCache.delete(info.fileName);
|
|
82
|
-
return origRemoveFile(info, fileExists, detachFromProject);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// Patch readDirectory so we get completions for .astro files
|
|
86
|
-
const origReadDirectory = project.readDirectory.bind(project);
|
|
87
|
-
project.readDirectory = (path, extensions, exclude, include, depth) => {
|
|
88
|
-
const extensionsWithAstro = (extensions ?? []).concat('.astro', '.md', '.mdx');
|
|
89
|
-
return origReadDirectory(path, extensionsWithAstro, exclude, include, depth);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
function resolveModuleNames(
|
|
93
|
-
moduleNames: string[],
|
|
94
|
-
containingFile: string,
|
|
95
|
-
reusedNames: string[] | undefined,
|
|
96
|
-
redirectedReference: ts.ResolvedProjectReference | undefined,
|
|
97
|
-
compilerOptions: ts.CompilerOptions
|
|
98
|
-
): Array<ts.ResolvedModule | undefined> {
|
|
99
|
-
// logger.log('Resolving modules names for ' + containingFile);
|
|
100
|
-
|
|
101
|
-
// Try resolving all module names with the original method first.
|
|
102
|
-
// The ones that are undefined will be re-checked if they are a
|
|
103
|
-
// astro file and if so, are resolved, too. This way we can defer
|
|
104
|
-
// all module resolving logic except for astro files to TypeScript.
|
|
105
|
-
const resolved =
|
|
106
|
-
origResolveModuleNames?.(
|
|
107
|
-
moduleNames,
|
|
108
|
-
containingFile,
|
|
109
|
-
reusedNames,
|
|
110
|
-
redirectedReference,
|
|
111
|
-
compilerOptions
|
|
112
|
-
) || Array.from<undefined>(Array(moduleNames.length));
|
|
113
|
-
|
|
114
|
-
return resolved.map((moduleName, idx) => {
|
|
115
|
-
const fileName = moduleNames[idx];
|
|
116
|
-
if (moduleName || !ensureRealAstroFilePath(fileName).endsWith('.astro')) {
|
|
117
|
-
return moduleName;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const cachedModule = moduleCache.get(fileName, containingFile);
|
|
121
|
-
if (cachedModule) {
|
|
122
|
-
return cachedModule;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const resolvedModule = resolveModuleName(fileName, containingFile, compilerOptions);
|
|
126
|
-
moduleCache.set(fileName, containingFile, resolvedModule);
|
|
127
|
-
return resolvedModule;
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function resolveModuleName(
|
|
132
|
-
name: string,
|
|
133
|
-
containingFile: string,
|
|
134
|
-
compilerOptions: ts.CompilerOptions
|
|
135
|
-
): ts.ResolvedModuleFull | undefined {
|
|
136
|
-
const astroResolvedModule = typescript.resolveModuleName(
|
|
137
|
-
name,
|
|
138
|
-
containingFile,
|
|
139
|
-
compilerOptions,
|
|
140
|
-
astroSys
|
|
141
|
-
).resolvedModule;
|
|
142
|
-
if (!astroResolvedModule || !isVirtualAstroFilePath(astroResolvedModule.resolvedFileName)) {
|
|
143
|
-
return astroResolvedModule;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const resolvedFileName = ensureRealAstroFilePath(astroResolvedModule.resolvedFileName);
|
|
147
|
-
logger.log('Resolved', name, 'to astro file', resolvedFileName);
|
|
148
|
-
const snapshot = snapshotManager.create(resolvedFileName);
|
|
149
|
-
if (!snapshot) {
|
|
150
|
-
return undefined;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const resolvedAstroModule: ts.ResolvedModuleFull = {
|
|
154
|
-
extension: typescript.Extension.Tsx,
|
|
155
|
-
resolvedFileName,
|
|
156
|
-
};
|
|
157
|
-
return resolvedAstroModule;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function resolveModuleNameLiterals(
|
|
161
|
-
moduleLiterals: readonly ts.StringLiteralLike[],
|
|
162
|
-
containingFile: string,
|
|
163
|
-
redirectedReference: ts.ResolvedProjectReference | undefined,
|
|
164
|
-
options: ts.CompilerOptions,
|
|
165
|
-
containingSourceFile: ts.SourceFile,
|
|
166
|
-
reusedNames: readonly ts.StringLiteralLike[] | undefined
|
|
167
|
-
): readonly ts.ResolvedModuleWithFailedLookupLocations[] {
|
|
168
|
-
logger.log('Resolving modules names for ' + containingFile);
|
|
169
|
-
// Try resolving all module names with the original method first.
|
|
170
|
-
// The ones that are undefined will be re-checked if they are a
|
|
171
|
-
// Astro file and if so, are resolved, too. This way we can defer
|
|
172
|
-
// all module resolving logic except for Astro files to TypeScript.
|
|
173
|
-
const resolved =
|
|
174
|
-
origResolveModuleNamLiterals?.(
|
|
175
|
-
moduleLiterals,
|
|
176
|
-
containingFile,
|
|
177
|
-
redirectedReference,
|
|
178
|
-
options,
|
|
179
|
-
containingSourceFile,
|
|
180
|
-
reusedNames
|
|
181
|
-
) ??
|
|
182
|
-
moduleLiterals.map(
|
|
183
|
-
(): ts.ResolvedModuleWithFailedLookupLocations => ({
|
|
184
|
-
resolvedModule: undefined,
|
|
185
|
-
})
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
return resolved.map((tsResolvedModule, idx) => {
|
|
189
|
-
const moduleName = moduleLiterals[idx].text;
|
|
190
|
-
if (
|
|
191
|
-
tsResolvedModule.resolvedModule ||
|
|
192
|
-
!ensureRealAstroFilePath(moduleName).endsWith('.astro')
|
|
193
|
-
) {
|
|
194
|
-
return tsResolvedModule;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return resolveAstroModuleNameFromCache(moduleName, containingFile, options);
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function resolveAstroModuleNameFromCache(
|
|
202
|
-
moduleName: string,
|
|
203
|
-
containingFile: string,
|
|
204
|
-
options: ts.CompilerOptions
|
|
205
|
-
) {
|
|
206
|
-
const cachedModule = moduleCache.get(moduleName, containingFile);
|
|
207
|
-
if (cachedModule) {
|
|
208
|
-
return {
|
|
209
|
-
resolvedModule: cachedModule,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const resolvedModule = resolveModuleName(moduleName, containingFile, options);
|
|
214
|
-
moduleCache.set(moduleName, containingFile, resolvedModule);
|
|
215
|
-
return {
|
|
216
|
-
resolvedModule: resolvedModule,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import type { AstroSnapshotManager } from './astro-snapshots';
|
|
3
|
-
import { getConfigPathForProject, isAstroFilePath, readProjectAstroFilesFromFs } from './utils';
|
|
4
|
-
|
|
5
|
-
export class ProjectAstroFilesManager {
|
|
6
|
-
private files = new Set<string>();
|
|
7
|
-
private directoryWatchers = new Set<ts.FileWatcher>();
|
|
8
|
-
|
|
9
|
-
private static instances = new Map<string, ProjectAstroFilesManager>();
|
|
10
|
-
|
|
11
|
-
static getInstance(projectName: string) {
|
|
12
|
-
return this.instances.get(projectName);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
constructor(
|
|
16
|
-
private readonly typescript: typeof ts,
|
|
17
|
-
private readonly project: ts.server.Project,
|
|
18
|
-
private readonly serverHost: ts.server.ServerHost,
|
|
19
|
-
private readonly snapshotManager: AstroSnapshotManager,
|
|
20
|
-
private parsedCommandLine: ts.ParsedCommandLine
|
|
21
|
-
) {
|
|
22
|
-
this.setupWatchers();
|
|
23
|
-
this.updateProjectAstroFiles();
|
|
24
|
-
|
|
25
|
-
ProjectAstroFilesManager.instances.set(project.getProjectName(), this);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
updateProjectConfig(serviceHost: ts.LanguageServiceHost) {
|
|
29
|
-
const parsedCommandLine = serviceHost.getParsedCommandLine?.(
|
|
30
|
-
getConfigPathForProject(this.project)
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
if (!parsedCommandLine) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
this.disposeWatchersAndFiles();
|
|
38
|
-
this.parsedCommandLine = parsedCommandLine;
|
|
39
|
-
this.setupWatchers();
|
|
40
|
-
this.updateProjectAstroFiles();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
getFiles() {
|
|
44
|
-
return Array.from(this.files);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Create directory watcher for include and exclude
|
|
49
|
-
* The watcher in tsserver doesn't support astro file
|
|
50
|
-
* It won't add new created astro file to root
|
|
51
|
-
*/
|
|
52
|
-
private setupWatchers() {
|
|
53
|
-
for (const directory in this.parsedCommandLine.wildcardDirectories) {
|
|
54
|
-
if (
|
|
55
|
-
!Object.prototype.hasOwnProperty.call(this.parsedCommandLine.wildcardDirectories, directory)
|
|
56
|
-
) {
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const watchDirectoryFlags = this.parsedCommandLine.wildcardDirectories[directory];
|
|
61
|
-
const watcher = this.serverHost.watchDirectory(
|
|
62
|
-
directory,
|
|
63
|
-
this.watcherCallback.bind(this),
|
|
64
|
-
watchDirectoryFlags === this.typescript.WatchDirectoryFlags.Recursive,
|
|
65
|
-
this.parsedCommandLine.watchOptions
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
this.directoryWatchers.add(watcher);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private watcherCallback(fileName: string) {
|
|
73
|
-
if (!isAstroFilePath(fileName)) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// We can't just add the file to the project directly, because
|
|
78
|
-
// - the casing of fileName is different
|
|
79
|
-
// - we don't know whether the file was added or deleted
|
|
80
|
-
this.updateProjectAstroFiles();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private updateProjectAstroFiles() {
|
|
84
|
-
const fileNamesAfter = readProjectAstroFilesFromFs(
|
|
85
|
-
this.typescript,
|
|
86
|
-
this.project,
|
|
87
|
-
this.parsedCommandLine
|
|
88
|
-
);
|
|
89
|
-
const removedFiles = new Set(...this.files);
|
|
90
|
-
const newFiles = fileNamesAfter.filter((fileName) => {
|
|
91
|
-
const has = this.files.has(fileName);
|
|
92
|
-
if (has) {
|
|
93
|
-
removedFiles.delete(fileName);
|
|
94
|
-
}
|
|
95
|
-
return !has;
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
for (const newFile of newFiles) {
|
|
99
|
-
this.addFileToProject(newFile);
|
|
100
|
-
this.files.add(newFile);
|
|
101
|
-
}
|
|
102
|
-
for (const removedFile of removedFiles) {
|
|
103
|
-
this.removeFileFromProject(removedFile, false);
|
|
104
|
-
this.files.delete(removedFile);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
private addFileToProject(newFile: string) {
|
|
109
|
-
this.snapshotManager.create(newFile);
|
|
110
|
-
const snapshot = this.project.projectService.getScriptInfo(newFile);
|
|
111
|
-
|
|
112
|
-
if (snapshot) {
|
|
113
|
-
this.project.addRoot(snapshot);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
private removeFileFromProject(file: string, exists = true) {
|
|
118
|
-
const info = this.project.getScriptInfo(file);
|
|
119
|
-
|
|
120
|
-
if (info) {
|
|
121
|
-
this.project.removeFile(info, exists, true);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private disposeWatchersAndFiles() {
|
|
126
|
-
this.directoryWatchers.forEach((watcher) => watcher.close());
|
|
127
|
-
this.directoryWatchers.clear();
|
|
128
|
-
|
|
129
|
-
this.files.forEach((file) => this.removeFileFromProject(file));
|
|
130
|
-
this.files.clear();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
dispose() {
|
|
134
|
-
this.disposeWatchersAndFiles();
|
|
135
|
-
|
|
136
|
-
ProjectAstroFilesManager.instances.delete(this.project.getProjectName());
|
|
137
|
-
}
|
|
138
|
-
}
|