@astrojs/ts-plugin 0.3.0 → 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.
- package/.turbo/turbo-build.log +5 -2
- package/CHANGELOG.md +10 -0
- package/LICENSE +36 -0
- package/dist/astro-snapshots.js +48 -47
- package/dist/astro-sys.js +22 -35
- package/dist/astro2tsx.js +22 -26
- package/dist/index.js +38 -39
- package/dist/language-service/completions.js +42 -42
- package/dist/language-service/definition.js +22 -40
- package/dist/language-service/diagnostics.js +15 -18
- package/dist/language-service/file-references.js +52 -0
- package/dist/language-service/find-references.js +20 -38
- package/dist/language-service/implementation.js +18 -37
- package/dist/language-service/index.js +26 -42
- package/dist/language-service/line-column-offset.js +44 -0
- package/dist/language-service/rename.js +24 -37
- package/dist/logger.js +18 -5
- package/dist/module-loader.js +28 -22
- package/dist/project-astro-files.js +125 -0
- package/dist/utils.js +34 -19
- package/dist/workers/TSXService.js +39 -0
- package/dist/workers/TSXWorker.js +9 -0
- package/package.json +21 -11
- package/src/astro-snapshots.ts +17 -26
- package/src/astro-sys.ts +1 -1
- package/src/astro2tsx.ts +5 -46
- package/src/index.ts +34 -34
- package/src/language-service/completions.ts +15 -4
- package/src/language-service/definition.ts +2 -7
- package/src/language-service/diagnostics.ts +1 -2
- package/src/language-service/file-references.ts +31 -0
- package/src/language-service/find-references.ts +2 -2
- package/src/language-service/implementation.ts +2 -7
- package/src/language-service/index.ts +10 -25
- package/src/language-service/line-column-offset.ts +21 -0
- package/src/language-service/rename.ts +2 -2
- package/src/module-loader.ts +12 -4
- package/src/project-astro-files.ts +130 -0
- package/src/utils.ts +29 -20
- package/src/workers/TSXService.ts +11 -0
- package/src/workers/TSXWorker.ts +8 -0
- package/test/fixtures/MyAstroComponent.astro +5 -0
- package/test/fixtures/script.ts +9 -0
- package/test/fixtures/tsconfig.json +3 -0
- package/test/runTest.js +41 -0
- package/test/suite/extension.test.js +68 -0
- package/test/suite/index.js +38 -0
- package/test/tsconfig.json +14 -0
- package/test/utils.js +65 -0
- package/tsconfig.json +2 -1
- package/dist/source-mapper.js +0 -110
- package/src/source-mapper.ts +0 -107
package/src/astro-snapshots.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { EncodedSourceMap, originalPositionFor, TraceMap } from '@jridgewell/trace-mapping';
|
|
1
2
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
3
|
import { astro2tsx } from './astro2tsx.js';
|
|
3
|
-
import { Logger } from './logger.js';
|
|
4
|
-
import {
|
|
5
|
-
import { isAstroFilePath, isNoTextSpanInGeneratedCode } from './utils.js';
|
|
4
|
+
import type { Logger } from './logger.js';
|
|
5
|
+
import { isAstroFilePath } from './utils.js';
|
|
6
6
|
|
|
7
7
|
export class AstroSnapshot {
|
|
8
8
|
private scriptInfo?: ts.server.ScriptInfo;
|
|
@@ -13,23 +13,18 @@ export class AstroSnapshot {
|
|
|
13
13
|
private typescript: typeof ts,
|
|
14
14
|
private fileName: string,
|
|
15
15
|
private astroCode: string,
|
|
16
|
-
private
|
|
17
|
-
private logger: Logger
|
|
18
|
-
public readonly isTsFile: boolean
|
|
16
|
+
private traceMap: TraceMap,
|
|
17
|
+
private logger: Logger
|
|
19
18
|
) {}
|
|
20
19
|
|
|
21
|
-
update(astroCode: string,
|
|
20
|
+
update(astroCode: string, traceMap: TraceMap) {
|
|
22
21
|
this.astroCode = astroCode;
|
|
23
|
-
this.
|
|
22
|
+
this.traceMap = traceMap;
|
|
24
23
|
this.lineOffsets = undefined;
|
|
25
24
|
this.log('Updated Snapshot');
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
getOriginalTextSpan(textSpan: ts.TextSpan): ts.TextSpan | null {
|
|
29
|
-
if (!isNoTextSpanInGeneratedCode(this.getText(), textSpan)) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
28
|
const start = this.getOriginalOffset(textSpan.start);
|
|
34
29
|
if (start === -1) {
|
|
35
30
|
return null;
|
|
@@ -50,16 +45,17 @@ export class AstroSnapshot {
|
|
|
50
45
|
this.toggleMappingMode(true);
|
|
51
46
|
const lineOffset = this.scriptInfo.positionToLineOffset(generatedOffset);
|
|
52
47
|
this.debug('try convert offset', generatedOffset, '/', lineOffset);
|
|
53
|
-
const original = this.
|
|
54
|
-
line: lineOffset.line
|
|
55
|
-
|
|
48
|
+
const original = originalPositionFor(this.traceMap, {
|
|
49
|
+
line: lineOffset.line,
|
|
50
|
+
column: lineOffset.offset,
|
|
56
51
|
});
|
|
57
52
|
this.toggleMappingMode(false);
|
|
58
|
-
|
|
53
|
+
|
|
54
|
+
if (!original.line) {
|
|
59
55
|
return -1;
|
|
60
56
|
}
|
|
61
57
|
|
|
62
|
-
const originalOffset = this.scriptInfo.lineOffsetToPosition(original.line
|
|
58
|
+
const originalOffset = this.scriptInfo.lineOffsetToPosition(original.line, original.column);
|
|
63
59
|
this.debug('converted offset to', original, '/', originalOffset);
|
|
64
60
|
return originalOffset;
|
|
65
61
|
}
|
|
@@ -250,14 +246,10 @@ export class AstroSnapshotManager {
|
|
|
250
246
|
this.logger.debug('Read Astro file:', path);
|
|
251
247
|
const astroCode = readFile(path) || '';
|
|
252
248
|
try {
|
|
253
|
-
const
|
|
254
|
-
const result = astro2tsx(astroCode, {
|
|
255
|
-
filename: path.split('/').pop(),
|
|
256
|
-
isTsFile,
|
|
257
|
-
});
|
|
249
|
+
const result = astro2tsx(astroCode, path);
|
|
258
250
|
const existingSnapshot = this.snapshots.get(path);
|
|
259
251
|
if (existingSnapshot) {
|
|
260
|
-
existingSnapshot.update(astroCode, new
|
|
252
|
+
existingSnapshot.update(astroCode, new TraceMap(result.map as EncodedSourceMap));
|
|
261
253
|
} else {
|
|
262
254
|
this.snapshots.set(
|
|
263
255
|
path,
|
|
@@ -265,9 +257,8 @@ export class AstroSnapshotManager {
|
|
|
265
257
|
this.typescript,
|
|
266
258
|
path,
|
|
267
259
|
astroCode,
|
|
268
|
-
new
|
|
269
|
-
this.logger
|
|
270
|
-
isTsFile
|
|
260
|
+
new TraceMap(result.map as EncodedSourceMap),
|
|
261
|
+
this.logger
|
|
271
262
|
)
|
|
272
263
|
);
|
|
273
264
|
}
|
package/src/astro-sys.ts
CHANGED
package/src/astro2tsx.ts
CHANGED
|
@@ -1,49 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TSXResult } from '@astrojs/compiler/shared/types';
|
|
2
|
+
import { convertToTSX } from './workers/TSXService';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
export function astro2tsx(content: string, fileName: string): TSXResult {
|
|
5
|
+
const tsx = convertToTSX(content, { sourcefile: fileName });
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
filename: string | undefined;
|
|
8
|
-
isTsFile: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface Astro2TSXResult {
|
|
12
|
-
code: string;
|
|
13
|
-
map: {
|
|
14
|
-
mappings: FileMapping;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function astro2tsx(code: string, options: Astro2TSXOptions): Astro2TSXResult {
|
|
19
|
-
const compiled = transformContent(code);
|
|
20
|
-
|
|
21
|
-
const result: Astro2TSXResult = {
|
|
22
|
-
code: compiled,
|
|
23
|
-
map: {
|
|
24
|
-
mappings: [],
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
return result;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// This is hacky but it works for now
|
|
32
|
-
function addProps(content: string): string {
|
|
33
|
-
let defaultExportType = 'Record<string, any>';
|
|
34
|
-
|
|
35
|
-
if (/(interface|type) Props/.test(content)) {
|
|
36
|
-
defaultExportType = 'Props';
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return '\n' + `export default function (props: ${defaultExportType}): string;`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function transformContent(content: string) {
|
|
43
|
-
const ts = content.replace(/---/g, '///');
|
|
44
|
-
return (
|
|
45
|
-
ts +
|
|
46
|
-
// Add TypeScript definitions
|
|
47
|
-
addProps(ts)
|
|
48
|
-
);
|
|
7
|
+
return tsx;
|
|
49
8
|
}
|
package/src/index.ts
CHANGED
|
@@ -3,12 +3,17 @@ import { AstroSnapshotManager } from './astro-snapshots.js';
|
|
|
3
3
|
import { decorateLanguageService } from './language-service/index.js';
|
|
4
4
|
import { Logger } from './logger.js';
|
|
5
5
|
import { patchModuleLoader } from './module-loader.js';
|
|
6
|
+
import { ProjectAstroFilesManager } from './project-astro-files.js';
|
|
7
|
+
import { getConfigPathForProject, readProjectAstroFilesFromFs } from './utils.js';
|
|
6
8
|
|
|
7
|
-
function init(modules: { typescript: typeof
|
|
8
|
-
|
|
9
|
+
function init(modules: { typescript: typeof import('typescript/lib/tsserverlibrary') }) {
|
|
10
|
+
const ts = modules.typescript;
|
|
11
|
+
|
|
12
|
+
function create(info: ts.server.PluginCreateInfo): ts.LanguageService {
|
|
9
13
|
const logger = new Logger(info.project.projectService.logger);
|
|
14
|
+
const parsedCommandLine = info.languageServiceHost.getParsedCommandLine?.(getConfigPathForProject(info.project));
|
|
10
15
|
|
|
11
|
-
if (!isAstroProject(info)) {
|
|
16
|
+
if (!isAstroProject(info.project, parsedCommandLine)) {
|
|
12
17
|
logger.log('Detected that this is not an Astro project, abort patching TypeScript');
|
|
13
18
|
return info.languageService;
|
|
14
19
|
}
|
|
@@ -16,47 +21,42 @@ function init(modules: { typescript: typeof ts }) {
|
|
|
16
21
|
logger.log('Starting Astro plugin');
|
|
17
22
|
|
|
18
23
|
const snapshotManager = new AstroSnapshotManager(modules.typescript, info.project.projectService, logger);
|
|
24
|
+
if (parsedCommandLine) {
|
|
25
|
+
new ProjectAstroFilesManager(
|
|
26
|
+
modules.typescript,
|
|
27
|
+
info.project,
|
|
28
|
+
info.serverHost,
|
|
29
|
+
snapshotManager,
|
|
30
|
+
parsedCommandLine
|
|
31
|
+
);
|
|
32
|
+
}
|
|
19
33
|
|
|
20
|
-
patchCompilerOptions(info.project);
|
|
21
34
|
patchModuleLoader(logger, snapshotManager, modules.typescript, info.languageServiceHost, info.project);
|
|
22
35
|
return decorateLanguageService(info.languageService, snapshotManager, logger);
|
|
23
36
|
}
|
|
24
37
|
|
|
25
|
-
function getExternalFiles(
|
|
26
|
-
|
|
27
|
-
/*const astroTsPath = dirname(require.resolve('astro2tsx'));
|
|
28
|
-
const astroTsxFiles = [
|
|
29
|
-
'./astro-shims.d.ts',
|
|
30
|
-
'./astro-jsx.d.ts',
|
|
31
|
-
'./astro-native-jsx.d.ts'
|
|
32
|
-
].map((f) => modules.typescript.sys.resolvePath(resolve(astroTsPath, f)));
|
|
33
|
-
return astroTsxFiles;*/
|
|
34
|
-
return [];
|
|
38
|
+
function getExternalFiles(project: ts.server.ConfiguredProject) {
|
|
39
|
+
return ProjectAstroFilesManager.getInstance(project.getProjectName())?.getFiles() ?? [];
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
function
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
compilerOptions.jsx = modules.typescript.JsxEmit.Preserve;
|
|
42
|
+
function isAstroProject(project: ts.server.Project, parsedCommandLine: ts.ParsedCommandLine | undefined) {
|
|
43
|
+
if (parsedCommandLine) {
|
|
44
|
+
const astroFiles = readProjectAstroFilesFromFs(ts, project, parsedCommandLine);
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
if (!compilerOptions.jsxFactory?.startsWith('astro')) {
|
|
44
|
-
// Default to regular astro, this causes the usage of the "astro.JSX" namespace
|
|
45
|
-
// We don't need to add a switch for astro-native because the jsx is only relevant
|
|
46
|
-
// within Astro files, which this plugin does not deal with.
|
|
47
|
-
compilerOptions.jsxFactory = 'astro.createElement';
|
|
46
|
+
if (astroFiles.length > 0) return true;
|
|
48
47
|
}
|
|
49
|
-
}
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
try {
|
|
50
|
+
const compilerOptions = project.getCompilerOptions();
|
|
51
|
+
const hasAstroInstalled =
|
|
52
|
+
typeof compilerOptions.configFilePath !== 'string' ||
|
|
53
|
+
require.resolve('astro', { paths: [compilerOptions.configFilePath] });
|
|
54
|
+
|
|
55
|
+
return hasAstroInstalled;
|
|
56
|
+
} catch (e) {
|
|
57
|
+
project.projectService.logger.info(e as string);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
return { create, getExternalFiles };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { Logger } from '../logger.js';
|
|
2
|
+
import type { Logger } from '../logger.js';
|
|
3
3
|
import { isAstroFilePath, replaceDeep } from '../utils.js';
|
|
4
4
|
|
|
5
5
|
const componentPostfix = '__AstroComponent_';
|
|
@@ -27,9 +27,20 @@ export function decorateCompletions(ls: ts.LanguageService, logger: Logger): voi
|
|
|
27
27
|
|
|
28
28
|
const getCompletionEntryDetails = ls.getCompletionEntryDetails;
|
|
29
29
|
ls.getCompletionEntryDetails = (fileName, position, entryName, formatOptions, source, preferences, data) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
if (!isAstroFilePath(source || '')) {
|
|
31
|
+
const details = getCompletionEntryDetails(
|
|
32
|
+
fileName,
|
|
33
|
+
position,
|
|
34
|
+
entryName,
|
|
35
|
+
formatOptions,
|
|
36
|
+
source,
|
|
37
|
+
preferences,
|
|
38
|
+
data
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (details) {
|
|
42
|
+
return details;
|
|
43
|
+
}
|
|
33
44
|
}
|
|
34
45
|
|
|
35
46
|
// In the completion list we removed the component postfix. Internally,
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { AstroSnapshotManager } from '../astro-snapshots';
|
|
3
|
-
import { Logger } from '../logger';
|
|
2
|
+
import type { AstroSnapshotManager } from '../astro-snapshots';
|
|
4
3
|
import { isAstroFilePath, isNotNullOrUndefined } from '../utils';
|
|
5
4
|
|
|
6
|
-
export function decorateGetDefinition(
|
|
7
|
-
ls: ts.LanguageService,
|
|
8
|
-
snapshotManager: AstroSnapshotManager,
|
|
9
|
-
logger: Logger
|
|
10
|
-
): void {
|
|
5
|
+
export function decorateGetDefinition(ls: ts.LanguageService, snapshotManager: AstroSnapshotManager): void {
|
|
11
6
|
const getDefinitionAndBoundSpan = ls.getDefinitionAndBoundSpan;
|
|
12
7
|
ls.getDefinitionAndBoundSpan = (fileName, position) => {
|
|
13
8
|
const definition = getDefinitionAndBoundSpan(fileName, position);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { Logger } from '../logger.js';
|
|
3
2
|
import { isAstroFilePath } from '../utils.js';
|
|
4
3
|
|
|
5
|
-
export function decorateDiagnostics(ls: ts.LanguageService
|
|
4
|
+
export function decorateDiagnostics(ls: ts.LanguageService): void {
|
|
6
5
|
decorateSyntacticDiagnostics(ls);
|
|
7
6
|
decorateSemanticDiagnostics(ls);
|
|
8
7
|
decorateSuggestionDiagnostics(ls);
|
|
@@ -0,0 +1,31 @@
|
|
|
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 decorateGetFileReferences(ls: ts.LanguageService, snapshotManager: AstroSnapshotManager): void {
|
|
6
|
+
const getFileReferences = ls.getFileReferences;
|
|
7
|
+
ls.getFileReferences = (fileName) => {
|
|
8
|
+
const references = getFileReferences(fileName);
|
|
9
|
+
return references
|
|
10
|
+
?.map((ref) => {
|
|
11
|
+
if (!isAstroFilePath(ref.fileName)) {
|
|
12
|
+
return ref;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const textSpan = snapshotManager.get(ref.fileName)?.getOriginalTextSpan(ref.textSpan);
|
|
16
|
+
if (!textSpan) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
...ref,
|
|
22
|
+
textSpan,
|
|
23
|
+
// Spare the work for now
|
|
24
|
+
contextSpan: undefined,
|
|
25
|
+
originalTextSpan: undefined,
|
|
26
|
+
originalContextSpan: undefined,
|
|
27
|
+
};
|
|
28
|
+
})
|
|
29
|
+
.filter(isNotNullOrUndefined);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
-
import { Logger } from '../logger';
|
|
2
|
+
import type { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
+
import type { Logger } from '../logger';
|
|
4
4
|
import { isAstroFilePath, isNotNullOrUndefined } from '../utils.js';
|
|
5
5
|
|
|
6
6
|
export function decorateFindReferences(
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
-
import { Logger } from '../logger.js';
|
|
2
|
+
import type { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
4
3
|
import { isAstroFilePath, isNotNullOrUndefined } from '../utils.js';
|
|
5
4
|
|
|
6
|
-
export function decorateGetImplementation(
|
|
7
|
-
ls: ts.LanguageService,
|
|
8
|
-
snapshotManager: AstroSnapshotManager,
|
|
9
|
-
logger: Logger
|
|
10
|
-
): void {
|
|
5
|
+
export function decorateGetImplementation(ls: ts.LanguageService, snapshotManager: AstroSnapshotManager): void {
|
|
11
6
|
const getImplementationAtPosition = ls.getImplementationAtPosition;
|
|
12
7
|
ls.getImplementationAtPosition = (fileName, position) => {
|
|
13
8
|
const implementation = getImplementationAtPosition(fileName, position);
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
-
import { Logger } from '../logger';
|
|
4
|
-
import { isAstroFilePath } from '../utils.js';
|
|
2
|
+
import type { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
+
import type { Logger } from '../logger';
|
|
5
4
|
import { decorateCompletions } from './completions.js';
|
|
6
5
|
import { decorateGetDefinition } from './definition.js';
|
|
7
6
|
import { decorateDiagnostics } from './diagnostics.js';
|
|
7
|
+
import { decorateGetFileReferences } from './file-references.js';
|
|
8
8
|
import { decorateFindReferences } from './find-references.js';
|
|
9
9
|
import { decorateGetImplementation } from './implementation.js';
|
|
10
|
+
import { decorateLineColumnOffset } from './line-column-offset.js';
|
|
10
11
|
import { decorateRename } from './rename.js';
|
|
11
12
|
|
|
12
13
|
export function decorateLanguageService(
|
|
@@ -14,30 +15,14 @@ export function decorateLanguageService(
|
|
|
14
15
|
snapshotManager: AstroSnapshotManager,
|
|
15
16
|
logger: Logger
|
|
16
17
|
): ts.LanguageService {
|
|
17
|
-
|
|
18
|
+
decorateLineColumnOffset(ls, snapshotManager);
|
|
18
19
|
decorateRename(ls, snapshotManager, logger);
|
|
19
|
-
decorateDiagnostics(ls
|
|
20
|
+
decorateDiagnostics(ls);
|
|
20
21
|
decorateFindReferences(ls, snapshotManager, logger);
|
|
21
22
|
decorateCompletions(ls, logger);
|
|
22
|
-
decorateGetDefinition(ls, snapshotManager
|
|
23
|
-
decorateGetImplementation(ls, snapshotManager
|
|
24
|
-
|
|
25
|
-
}
|
|
23
|
+
decorateGetDefinition(ls, snapshotManager);
|
|
24
|
+
decorateGetImplementation(ls, snapshotManager);
|
|
25
|
+
decorateGetFileReferences(ls, snapshotManager);
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
if (!ls.toLineColumnOffset) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
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
|
-
};
|
|
27
|
+
return ls;
|
|
43
28
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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(ls: ts.LanguageService, snapshotManager: AstroSnapshotManager) {
|
|
6
|
+
if (!ls.toLineColumnOffset) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// We need to patch this because (according to source, only) getDefinition uses this
|
|
11
|
+
const toLineColumnOffset = ls.toLineColumnOffset;
|
|
12
|
+
ls.toLineColumnOffset = (fileName, position) => {
|
|
13
|
+
if (isAstroFilePath(fileName)) {
|
|
14
|
+
const snapshot = snapshotManager.get(fileName);
|
|
15
|
+
if (snapshot) {
|
|
16
|
+
return snapshot.positionAt(position);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return toLineColumnOffset(fileName, position);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
-
import { Logger } from '../logger.js';
|
|
2
|
+
import type { AstroSnapshotManager } from '../astro-snapshots.js';
|
|
3
|
+
import type { Logger } from '../logger.js';
|
|
4
4
|
import { isAstroFilePath, isNotNullOrUndefined } from '../utils.js';
|
|
5
5
|
|
|
6
6
|
export function decorateRename(ls: ts.LanguageService, snapshotManager: AstroSnapshotManager, logger: Logger): void {
|
package/src/module-loader.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { AstroSnapshotManager } from './astro-snapshots.js';
|
|
2
|
+
import type { AstroSnapshotManager } from './astro-snapshots.js';
|
|
3
3
|
import { createAstroSys } from './astro-sys.js';
|
|
4
|
-
import { Logger } from './logger.js';
|
|
4
|
+
import type { Logger } from './logger.js';
|
|
5
5
|
import { ensureRealAstroFilePath, isVirtualAstroFilePath } from './utils.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -73,6 +73,13 @@ export function patchModuleLoader(
|
|
|
73
73
|
return origRemoveFile(info, fileExists, detachFromProject);
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
+
// Patch readDirectory so we get completions for .astro files
|
|
77
|
+
const origReadDirectory = project.readDirectory.bind(project);
|
|
78
|
+
project.readDirectory = (path, extensions, exclude, include, depth) => {
|
|
79
|
+
const extensionsWithAstro = (extensions ?? []).concat('.astro', '.md', '.mdx');
|
|
80
|
+
return origReadDirectory(path, extensionsWithAstro, exclude, include, depth);
|
|
81
|
+
};
|
|
82
|
+
|
|
76
83
|
function resolveModuleNames(
|
|
77
84
|
moduleNames: string[],
|
|
78
85
|
containingFile: string,
|
|
@@ -80,7 +87,8 @@ export function patchModuleLoader(
|
|
|
80
87
|
redirectedReference: ts.ResolvedProjectReference | undefined,
|
|
81
88
|
compilerOptions: ts.CompilerOptions
|
|
82
89
|
): Array<ts.ResolvedModule | undefined> {
|
|
83
|
-
logger.log('Resolving modules names for ' + containingFile);
|
|
90
|
+
// logger.log('Resolving modules names for ' + containingFile);
|
|
91
|
+
|
|
84
92
|
// Try resolving all module names with the original method first.
|
|
85
93
|
// The ones that are undefined will be re-checked if they are a
|
|
86
94
|
// astro file and if so, are resolved, too. This way we can defer
|
|
@@ -129,7 +137,7 @@ export function patchModuleLoader(
|
|
|
129
137
|
}
|
|
130
138
|
|
|
131
139
|
const resolvedAstroModule: ts.ResolvedModuleFull = {
|
|
132
|
-
extension:
|
|
140
|
+
extension: typescript.Extension.Tsx,
|
|
133
141
|
resolvedFileName,
|
|
134
142
|
};
|
|
135
143
|
return resolvedAstroModule;
|
|
@@ -0,0 +1,130 @@
|
|
|
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?.(getConfigPathForProject(this.project));
|
|
30
|
+
|
|
31
|
+
if (!parsedCommandLine) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.disposeWatchersAndFiles();
|
|
36
|
+
this.parsedCommandLine = parsedCommandLine;
|
|
37
|
+
this.setupWatchers();
|
|
38
|
+
this.updateProjectAstroFiles();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getFiles() {
|
|
42
|
+
return Array.from(this.files);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create directory watcher for include and exclude
|
|
47
|
+
* The watcher in tsserver doesn't support astro file
|
|
48
|
+
* It won't add new created astro file to root
|
|
49
|
+
*/
|
|
50
|
+
private setupWatchers() {
|
|
51
|
+
for (const directory in this.parsedCommandLine.wildcardDirectories) {
|
|
52
|
+
if (!Object.prototype.hasOwnProperty.call(this.parsedCommandLine.wildcardDirectories, directory)) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const watchDirectoryFlags = this.parsedCommandLine.wildcardDirectories[directory];
|
|
57
|
+
const watcher = this.serverHost.watchDirectory(
|
|
58
|
+
directory,
|
|
59
|
+
this.watcherCallback.bind(this),
|
|
60
|
+
watchDirectoryFlags === this.typescript.WatchDirectoryFlags.Recursive,
|
|
61
|
+
this.parsedCommandLine.watchOptions
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
this.directoryWatchers.add(watcher);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private watcherCallback(fileName: string) {
|
|
69
|
+
if (!isAstroFilePath(fileName)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// We can't just add the file to the project directly, because
|
|
74
|
+
// - the casing of fileName is different
|
|
75
|
+
// - we don't know whether the file was added or deleted
|
|
76
|
+
this.updateProjectAstroFiles();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private updateProjectAstroFiles() {
|
|
80
|
+
const fileNamesAfter = readProjectAstroFilesFromFs(this.typescript, this.project, this.parsedCommandLine);
|
|
81
|
+
const removedFiles = new Set(...this.files);
|
|
82
|
+
const newFiles = fileNamesAfter.filter((fileName) => {
|
|
83
|
+
const has = this.files.has(fileName);
|
|
84
|
+
if (has) {
|
|
85
|
+
removedFiles.delete(fileName);
|
|
86
|
+
}
|
|
87
|
+
return !has;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
for (const newFile of newFiles) {
|
|
91
|
+
this.addFileToProject(newFile);
|
|
92
|
+
this.files.add(newFile);
|
|
93
|
+
}
|
|
94
|
+
for (const removedFile of removedFiles) {
|
|
95
|
+
this.removeFileFromProject(removedFile, false);
|
|
96
|
+
this.files.delete(removedFile);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private addFileToProject(newFile: string) {
|
|
101
|
+
this.snapshotManager.create(newFile);
|
|
102
|
+
const snapshot = this.project.projectService.getScriptInfo(newFile);
|
|
103
|
+
|
|
104
|
+
if (snapshot) {
|
|
105
|
+
this.project.addRoot(snapshot);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private removeFileFromProject(file: string, exists = true) {
|
|
110
|
+
const info = this.project.getScriptInfo(file);
|
|
111
|
+
|
|
112
|
+
if (info) {
|
|
113
|
+
this.project.removeFile(info, exists, true);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private disposeWatchersAndFiles() {
|
|
118
|
+
this.directoryWatchers.forEach((watcher) => watcher.close());
|
|
119
|
+
this.directoryWatchers.clear();
|
|
120
|
+
|
|
121
|
+
this.files.forEach((file) => this.removeFileFromProject(file));
|
|
122
|
+
this.files.clear();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
dispose() {
|
|
126
|
+
this.disposeWatchersAndFiles();
|
|
127
|
+
|
|
128
|
+
ProjectAstroFilesManager.instances.delete(this.project.getProjectName());
|
|
129
|
+
}
|
|
130
|
+
}
|