Skip to content

Commit 6734686

Browse files
authored
(feat) resolve svelte.d.ts files first (#885)
Files ending with .svelte.d.ts are now resolved prior to a same file ending with .svelte . This enables library authors to ship type definitions of Svelte files next to their implementation. #878
1 parent 7ccc6b4 commit 6734686

File tree

2 files changed

+44
-31
lines changed

2 files changed

+44
-31
lines changed

packages/language-server/src/plugins/typescript/module-loader.ts

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ import ts from 'typescript';
22
import {
33
isVirtualSvelteFilePath,
44
ensureRealSvelteFilePath,
5-
isSvelteFilePath,
65
getExtensionFromScriptKind
76
} from './utils';
8-
import { isAbsolute } from 'path';
97
import { DocumentSnapshot } from './DocumentSnapshot';
108
import { createSvelteSys } from './svelte-sys';
119

@@ -67,7 +65,6 @@ export function createSvelteModuleLoader(
6765
) {
6866
const svelteSys = createSvelteSys(getSnapshot);
6967
const moduleCache = new ModuleResolutionCache();
70-
const pathAliases = getPathAliases(compilerOptions.paths);
7168

7269
return {
7370
fileExists: svelteSys.fileExists,
@@ -97,24 +94,29 @@ export function createSvelteModuleLoader(
9794
name: string,
9895
containingFile: string
9996
): ts.ResolvedModule | undefined {
100-
// In the normal case, delegate to ts.resolveModuleName.
101-
// In the relative-imported.svelte case, delegate to our own svelte module loader.
102-
if (isAbsolutePath(name) || !isSvelteFilePath(name)) {
103-
return ts.resolveModuleName(name, containingFile, compilerOptions, ts.sys)
104-
.resolvedModule;
97+
// Delegate to the TS resolver first.
98+
// If that does not bring up anything, try the Svelte Module loader
99+
// which is able to deal with .svelte files.
100+
const tsResolvedModule = ts.resolveModuleName(name, containingFile, compilerOptions, ts.sys)
101+
.resolvedModule;
102+
if (tsResolvedModule && !isVirtualSvelteFilePath(tsResolvedModule.resolvedFileName)) {
103+
return tsResolvedModule;
105104
}
106105

107-
const tsResolvedModule = ts.resolveModuleName(
106+
const svelteResolvedModule = ts.resolveModuleName(
108107
name,
109108
containingFile,
110109
compilerOptions,
111110
svelteSys
112111
).resolvedModule;
113-
if (!tsResolvedModule || !isVirtualSvelteFilePath(tsResolvedModule.resolvedFileName)) {
114-
return tsResolvedModule;
112+
if (
113+
!svelteResolvedModule ||
114+
!isVirtualSvelteFilePath(svelteResolvedModule.resolvedFileName)
115+
) {
116+
return svelteResolvedModule;
115117
}
116118

117-
const resolvedFileName = ensureRealSvelteFilePath(tsResolvedModule.resolvedFileName);
119+
const resolvedFileName = ensureRealSvelteFilePath(svelteResolvedModule.resolvedFileName);
118120
const snapshot = getSnapshot(resolvedFileName);
119121

120122
const resolvedSvelteModule: ts.ResolvedModuleFull = {
@@ -123,17 +125,4 @@ export function createSvelteModuleLoader(
123125
};
124126
return resolvedSvelteModule;
125127
}
126-
127-
function isAbsolutePath(path: string) {
128-
return isAbsolute(path) && !pathAliases.some((p) => path.startsWith(p));
129-
}
130-
131-
function getPathAliases(paths: ts.MapLike<string[]> | undefined) {
132-
return Object.keys(paths || {}).map((key) => {
133-
if (key.endsWith('*')) {
134-
key = key.substr(0, key.length - 1);
135-
}
136-
return key;
137-
});
138-
}
139128
}

packages/language-server/test/plugins/typescript/module-loader.test.ts

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ describe('createSvelteModuleLoader', () => {
3535
};
3636
}
3737

38+
function lastCall(stub: sinon.SinonStub<any[], any>) {
39+
return stub.getCall(stub.getCalls().length - 1);
40+
}
41+
3842
it('uses tsSys for normal files', async () => {
3943
const resolvedModule: ts.ResolvedModuleFull = {
4044
extension: ts.Extension.Ts,
@@ -47,7 +51,7 @@ describe('createSvelteModuleLoader', () => {
4751
);
4852

4953
assert.deepStrictEqual(result, [resolvedModule]);
50-
assert.deepStrictEqual(resolveStub.getCall(0).args, [
54+
assert.deepStrictEqual(lastCall(resolveStub).args, [
5155
'./normal.ts',
5256
'C:/somerepo/somefile.svelte',
5357
compilerOptions,
@@ -67,14 +71,34 @@ describe('createSvelteModuleLoader', () => {
6771
);
6872

6973
assert.deepStrictEqual(result, [resolvedModule]);
70-
assert.deepStrictEqual(resolveStub.getCall(0).args, [
74+
assert.deepStrictEqual(lastCall(resolveStub).args, [
7175
'/@/normal',
7276
'C:/somerepo/somefile.svelte',
7377
compilerOptions,
7478
ts.sys
7579
]);
7680
});
7781

82+
it('uses tsSys for svelte.d.ts files', async () => {
83+
const resolvedModule: ts.ResolvedModuleFull = {
84+
extension: ts.Extension.Dts,
85+
resolvedFileName: 'filename.d.ts'
86+
};
87+
const { resolveStub, moduleResolver, compilerOptions } = setup(resolvedModule);
88+
const result = moduleResolver.resolveModuleNames(
89+
['./normal.ts'],
90+
'C:/somerepo/somefile.svelte'
91+
);
92+
93+
assert.deepStrictEqual(result, [resolvedModule]);
94+
assert.deepStrictEqual(lastCall(resolveStub).args, [
95+
'./normal.ts',
96+
'C:/somerepo/somefile.svelte',
97+
compilerOptions,
98+
ts.sys
99+
]);
100+
});
101+
78102
it('uses svelte module loader for virtual svelte files', async () => {
79103
const resolvedModule: ts.ResolvedModuleFull = {
80104
extension: ts.Extension.Ts,
@@ -98,13 +122,13 @@ describe('createSvelteModuleLoader', () => {
98122
resolvedFileName: 'filename.svelte'
99123
}
100124
]);
101-
assert.deepStrictEqual(resolveStub.getCall(0).args, [
125+
assert.deepStrictEqual(lastCall(resolveStub).args, [
102126
'./svelte.svelte',
103127
'C:/somerepo/somefile.svelte',
104128
compilerOptions,
105129
svelteSys
106130
]);
107-
assert.deepStrictEqual(getSvelteSnapshotStub.getCall(0).args, ['filename.svelte']);
131+
assert.deepStrictEqual(lastCall(getSvelteSnapshotStub).args, ['filename.svelte']);
108132
});
109133

110134
it('uses svelte module loader for virtual svelte files with TS path aliases', async () => {
@@ -130,13 +154,13 @@ describe('createSvelteModuleLoader', () => {
130154
resolvedFileName: 'filename.svelte'
131155
}
132156
]);
133-
assert.deepStrictEqual(resolveStub.getCall(0).args, [
157+
assert.deepStrictEqual(lastCall(resolveStub).args, [
134158
'/@/svelte.svelte',
135159
'C:/somerepo/somefile.svelte',
136160
compilerOptions,
137161
svelteSys
138162
]);
139-
assert.deepStrictEqual(getSvelteSnapshotStub.getCall(0).args, ['filename.svelte']);
163+
assert.deepStrictEqual(lastCall(getSvelteSnapshotStub).args, ['filename.svelte']);
140164
});
141165

142166
it('uses cache if module was already resolved before', async () => {

0 commit comments

Comments
 (0)