Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Project reference support #2463

Merged
merged 41 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
541738f
resolve module with the compilerOptions from project reference
jasonlyu123 Jan 22, 2024
124ea40
fix test, might already be loaded in global snapshot manager
jasonlyu123 Jan 24, 2024
56d1959
wip
jasonlyu123 Mar 6, 2024
d01b678
breaking: make TypeScript a peer dependency
dummdidumm Jul 31, 2024
6fda024
breaking: require node 18 or later
dummdidumm Jul 31, 2024
03482d0
breaking: require Svelte 4 or later
dummdidumm Jul 31, 2024
d80e742
Merge branch 'master' of https://github.com/sveltejs/language-tools i…
jasonlyu123 Aug 5, 2024
856ac7f
fix service test
jasonlyu123 Aug 6, 2024
116285f
fix diagnostics test and flaky test
jasonlyu123 Aug 7, 2024
e98aec2
now I remember why the test file doesn't exist
jasonlyu123 Aug 7, 2024
01d111e
test for source project reference redirect
jasonlyu123 Aug 7, 2024
3131eb1
cache ParsedCommandLine, open ls for referenced tsconfig
jasonlyu123 Aug 12, 2024
2b12603
reuse config and snapshot manager
jasonlyu123 Aug 13, 2024
d2f4110
reload TsConfigInfo
jasonlyu123 Aug 13, 2024
3d25d43
make the scope consistent with service container in test, don't updat…
jasonlyu123 Aug 13, 2024
cc6b1c9
format
jasonlyu123 Aug 13, 2024
08043ba
project-reference references another solution
jasonlyu123 Aug 14, 2024
d9a5fca
clear projectReferenceInfo as well otherwise the snapshotManager will…
jasonlyu123 Aug 14, 2024
36d222c
breaking: force less tsconfig
jasonlyu123 Aug 14, 2024
7d0f346
avoid creating new service when file is in multiple services
jasonlyu123 Aug 16, 2024
3fd24f8
update import with existing services
jasonlyu123 Aug 16, 2024
fc0da19
fix snapshotMap in services without tsconfig
jasonlyu123 Aug 16, 2024
006e807
virtual doc won't pass the project files check so we have to directly…
jasonlyu123 Aug 16, 2024
6f87833
breaking: put non-project files to default service
jasonlyu123 Aug 16, 2024
cd76dbe
skip type-check for shim files
jasonlyu123 Aug 16, 2024
1395cfb
change test config target
jasonlyu123 Aug 16, 2024
5aca29f
Merge branch 'master' into svelte-check-major
dummdidumm Aug 19, 2024
ea8c0bd
chore: switch from fast-glob to fdir (#2433)
benmccann Aug 19, 2024
6471fae
Merge branch 'master' into svelte-check-major
dummdidumm Aug 19, 2024
7a1f80a
chore: remove svelte-preprocess dependency from svelte-check
dummdidumm Aug 19, 2024
6dac14e
simplify default compiler options logic
jasonlyu123 Aug 20, 2024
f580633
breaking: throw when `--ignore` is used without `--no-tsconfig`
dummdidumm Aug 20, 2024
e9a22d0
set target
dummdidumm Aug 20, 2024
75802e3
Merge branch 'svelte-check-major' into project-reference
dummdidumm Aug 20, 2024
5181ab0
fix service test.
jasonlyu123 Aug 20, 2024
f105638
no svelte input diagnostics
jasonlyu123 Aug 20, 2024
0b0f2a4
format
jasonlyu123 Aug 20, 2024
eec3b2b
show config errors in svelte-check
jasonlyu123 Aug 21, 2024
fd22d6a
tweak word, test
jasonlyu123 Aug 21, 2024
dc5662d
single quote
jasonlyu123 Aug 21, 2024
bf1c77f
Merge branch 'master' into project-reference
dummdidumm Aug 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/language-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"@vscode/emmet-helper": "2.8.4",
"chokidar": "^3.4.1",
"estree-walker": "^2.0.1",
"fast-glob": "^3.2.7",
"fdir": "^6.2.0",
"lodash": "^4.17.21",
"prettier": "~3.2.5",
"prettier-plugin-svelte": "^3.2.2",
Expand Down
27 changes: 18 additions & 9 deletions packages/language-server/src/lib/documents/configLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CompileOptions } from 'svelte/types/compiler/interfaces';
// @ts-ignore
import { PreprocessorGroup } from 'svelte/types/compiler/preprocess';
import { importSveltePreprocess } from '../../importPackage';
import _glob from 'fast-glob';
import { fdir } from 'fdir';
import _path from 'path';
import _fs from 'fs';
import { pathToFileURL, URL } from 'url';
Expand Down Expand Up @@ -47,6 +47,8 @@ const _dynamicImport = new Function('modulePath', 'return import(modulePath)') a
modulePath: URL
) => Promise<any>;

const configRegex = /\/svelte\.config\.(js|cjs|mjs)$/;

/**
* Loads svelte.config.{js,cjs,mjs} files. Provides both a synchronous and asynchronous
* interface to get a config file because snapshots need access to it synchronously.
Expand All @@ -61,7 +63,7 @@ export class ConfigLoader {
private disabled = false;

constructor(
private globSync: typeof _glob.sync,
private globSync: typeof fdir,
private fs: Pick<typeof _fs, 'existsSync'>,
private path: Pick<typeof _path, 'dirname' | 'relative' | 'join'>,
private dynamicImport: typeof _dynamicImport
Expand All @@ -84,12 +86,19 @@ export class ConfigLoader {
Logger.log('Trying to load configs for', directory);

try {
const pathResults = this.globSync('**/svelte.config.{js,cjs,mjs}', {
cwd: directory,
// the second pattern is necessary because else fast-glob treats .tmp/../node_modules/.. as a valid match for some reason
ignore: ['**/node_modules/**', '**/.*/**'],
onlyFiles: true
});
const pathResults = new this.globSync({})
.withPathSeparator('/')
.exclude((_, path) => {
// no / at the start, path could start with node_modules
return path.includes('node_modules/') || path.includes('/.');
})
.filter((path, isDir) => {
return !isDir && configRegex.test(path);
})
.withRelativePaths()
.crawl(directory)
.sync();

const someConfigIsImmediateFileInDirectory =
pathResults.length > 0 && pathResults.some((res) => !this.path.dirname(res));
if (!someConfigIsImmediateFileInDirectory) {
Expand Down Expand Up @@ -296,4 +305,4 @@ export class ConfigLoader {
}
}

export const configLoader = new ConfigLoader(_glob.sync, _fs, _path, _dynamicImport);
export const configLoader = new ConfigLoader(fdir, _fs, _path, _dynamicImport);
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { dirname, join } from 'path';
import ts from 'typescript';
import { RelativePattern, TextDocumentContentChangeEvent } from 'vscode-languageserver';
import {
PublishDiagnosticsParams,
RelativePattern,
TextDocumentContentChangeEvent
} from 'vscode-languageserver';
import { Document, DocumentManager } from '../../lib/documents';
import { LSConfigManager } from '../../ls-config';
import {
Expand Down Expand Up @@ -37,6 +41,7 @@ interface LSAndTSDocResolverOptions {
tsconfigPath?: string;

onProjectReloaded?: () => void;
reportConfigError?: (diagnostic: PublishDiagnosticsParams) => void;
watch?: boolean;
tsSystem?: ts.System;
watchDirectory?: (patterns: RelativePattern[]) => void;
Expand All @@ -50,14 +55,10 @@ export class LSAndTSDocResolver {
private readonly configManager: LSConfigManager,
private readonly options?: LSAndTSDocResolverOptions
) {
const handleDocumentChange = (document: Document) => {
// This refreshes the document in the ts language service
this.getSnapshot(document);
};
docManager.on(
'documentChange',
debounceSameArg(
handleDocumentChange,
this.updateSnapshot.bind(this),
(newDoc, prevDoc) => newDoc.uri === prevDoc?.uri,
1000
)
Expand All @@ -68,7 +69,11 @@ export class LSAndTSDocResolver {
// where multiple files and their dependencies
// being loaded in a short period of times
docManager.on('documentOpen', (document) => {
handleDocumentChange(document);
if (document.openedByClient) {
this.getOrCreateSnapshot(document);
} else {
this.updateSnapshot(document);
}
docManager.lockDocument(document.uri);
});

Expand Down Expand Up @@ -121,7 +126,8 @@ export class LSAndTSDocResolver {
watchDirectory: this.options?.watchDirectory
? this.watchDirectory.bind(this)
: undefined,
nonRecursiveWatchPattern: this.options?.nonRecursiveWatchPattern
nonRecursiveWatchPattern: this.options?.nonRecursiveWatchPattern,
reportConfigError: this.options?.reportConfigError
};
}

Expand Down Expand Up @@ -151,18 +157,20 @@ export class LSAndTSDocResolver {
private lsDocumentContext: LanguageServiceDocumentContext;
private readonly watchedDirectories: FileSet;

async getLSForPath(path: string) {
return (await this.getTSService(path)).getService();
}

async getLSAndTSDoc(document: Document): Promise<{
tsDoc: SvelteDocumentSnapshot;
lang: ts.LanguageService;
userPreferences: ts.UserPreferences;
lsContainer: LanguageServiceContainer;
}> {
const { tsDoc, lsContainer, userPreferences } = await this.getLSAndTSDocWorker(document);

return { tsDoc, lang: lsContainer.getService(), userPreferences };
return {
tsDoc,
lang: lsContainer.getService(),
userPreferences,
lsContainer
};
}

/**
Expand All @@ -181,7 +189,7 @@ export class LSAndTSDocResolver {

private async getLSAndTSDocWorker(document: Document) {
const lsContainer = await this.getTSService(document.getFilePath() || '');
const tsDoc = await this.getSnapshot(document);
const tsDoc = await this.getOrCreateSnapshot(document);
const userPreferences = this.getUserPreferences(tsDoc);

return { tsDoc, lsContainer, userPreferences };
Expand All @@ -192,13 +200,21 @@ export class LSAndTSDocResolver {
* the ts service it primarily belongs into.
* The update is mirrored in all other services, too.
*/
async getSnapshot(document: Document): Promise<SvelteDocumentSnapshot>;
async getSnapshot(pathOrDoc: string | Document): Promise<DocumentSnapshot>;
async getSnapshot(pathOrDoc: string | Document) {
async getOrCreateSnapshot(document: Document): Promise<SvelteDocumentSnapshot>;
async getOrCreateSnapshot(pathOrDoc: string | Document): Promise<DocumentSnapshot>;
async getOrCreateSnapshot(pathOrDoc: string | Document) {
const filePath = typeof pathOrDoc === 'string' ? pathOrDoc : pathOrDoc.getFilePath() || '';
const tsService = await this.getTSService(filePath);
return tsService.updateSnapshot(pathOrDoc);
}
private async updateSnapshot(document: Document) {
const filePath = document.getFilePath();
if (!filePath) {
return;
}
// ensure no new service is created
await this.updateExistingFile(filePath, (service) => service.updateSnapshot(document));
}

/**
* Updates snapshot path in all existing ts services and retrieves snapshot
Expand All @@ -217,7 +233,7 @@ export class LSAndTSDocResolver {
});
} else {
// This may not be a file but a directory, still try
await this.getSnapshot(newPath);
await this.getOrCreateSnapshot(newPath);
}
}

Expand Down Expand Up @@ -280,19 +296,11 @@ export class LSAndTSDocResolver {
});
}

/**
* @internal Public for tests only
*/
async getSnapshotManager(filePath: string): Promise<SnapshotManager> {
return (await this.getTSService(filePath)).snapshotManager;
}

async getTSService(filePath?: string): Promise<LanguageServiceContainer> {
if (this.options?.tsconfigPath) {
return getServiceForTsconfig(
this.options?.tsconfigPath,
dirname(this.options.tsconfigPath),
this.lsDocumentContext
return this.getTSServiceByConfigPath(
this.options.tsconfigPath,
dirname(this.options.tsconfigPath)
);
}
if (!filePath) {
Expand All @@ -301,6 +309,13 @@ export class LSAndTSDocResolver {
return getService(filePath, this.workspaceUris, this.lsDocumentContext);
}

async getTSServiceByConfigPath(
tsconfigPath: string,
workspacePath: string
): Promise<LanguageServiceContainer> {
return getServiceForTsconfig(tsconfigPath, workspacePath, this.lsDocumentContext);
}

private getUserPreferences(tsDoc: DocumentSnapshot): ts.UserPreferences {
const configLang =
tsDoc.scriptKind === ts.ScriptKind.TS || tsDoc.scriptKind === ts.ScriptKind.TSX
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,11 @@ export class SnapshotManager {
return Array.from(this.projectFileToOriginalCasing.values());
}

isProjectFile(fileName: string): boolean {
fileName = normalizePath(fileName);
return this.projectFileToOriginalCasing.has(this.getCanonicalFileName(fileName));
}

private logStatistics() {
const date = new Date();
// Don't use setInterval because that will keep tests running forever
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,10 @@ export class TypeScriptPlugin
this.completionProvider,
configManager
);
this.updateImportsProvider = new UpdateImportsProviderImpl(this.lsAndTsDocResolver);
this.updateImportsProvider = new UpdateImportsProviderImpl(
this.lsAndTsDocResolver,
ts.sys.useCaseSensitiveFileNames
);
this.diagnosticsProvider = new DiagnosticsProviderImpl(
this.lsAndTsDocResolver,
configManager
Expand Down Expand Up @@ -383,7 +386,7 @@ export class TypeScriptPlugin
}

async getDefinitions(document: Document, position: Position): Promise<DefinitionLink[]> {
const { lang, tsDoc } = await this.lsAndTsDocResolver.getLSAndTSDoc(document);
const { lang, tsDoc, lsContainer } = await this.lsAndTsDocResolver.getLSAndTSDoc(document);

const defs = lang.getDefinitionAndBoundSpan(
tsDoc.filePath,
Expand All @@ -394,7 +397,7 @@ export class TypeScriptPlugin
return [];
}

const snapshots = new SnapshotMap(this.lsAndTsDocResolver);
const snapshots = new SnapshotMap(this.lsAndTsDocResolver, lsContainer);
snapshots.set(tsDoc.filePath, tsDoc);

const result = await Promise.all(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class CallHierarchyProviderImpl implements CallHierarchyProvider {
position: Position,
cancellationToken?: CancellationToken
): Promise<CallHierarchyItem[] | null> {
const { lang, tsDoc } = await this.lsAndTsDocResolver.getLSAndTSDoc(document);
const { lang, tsDoc, lsContainer } = await this.lsAndTsDocResolver.getLSAndTSDoc(document);

if (cancellationToken?.isCancellationRequested) {
return null;
Expand All @@ -52,7 +52,7 @@ export class CallHierarchyProviderImpl implements CallHierarchyProvider {

const itemsArray = Array.isArray(items) ? items : items ? [items] : [];

const snapshots = new SnapshotMap(this.lsAndTsDocResolver);
const snapshots = new SnapshotMap(this.lsAndTsDocResolver, lsContainer);
snapshots.set(tsDoc.filePath, tsDoc);

const program = lang.getProgram();
Expand Down Expand Up @@ -251,16 +251,17 @@ export class CallHierarchyProviderImpl implements CallHierarchyProvider {
return null;
}

const lang = await this.lsAndTsDocResolver.getLSForPath(filePath);
const tsDoc = await this.lsAndTsDocResolver.getSnapshot(filePath);
const lsContainer = await this.lsAndTsDocResolver.getTSService(filePath);
const lang = lsContainer.getService();
const tsDoc = await this.lsAndTsDocResolver.getOrCreateSnapshot(filePath);

if (cancellationToken?.isCancellationRequested) {
return null;
}

const program = lang.getProgram();

const snapshots = new SnapshotMap(this.lsAndTsDocResolver);
const snapshots = new SnapshotMap(this.lsAndTsDocResolver, lsContainer);
snapshots.set(tsDoc.filePath, tsDoc);

const isComponentModulePosition =
Expand Down
Loading
Loading