Skip to content

Commit 5aca29f

Browse files
authored
Merge branch 'master' into svelte-check-major
2 parents 03482d0 + f6ad24d commit 5aca29f

File tree

9 files changed

+132
-47
lines changed

9 files changed

+132
-47
lines changed

packages/language-server/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
"prettier": "~3.2.5",
5656
"prettier-plugin-svelte": "^3.2.2",
5757
"svelte": "^3.57.0",
58-
"svelte-preprocess": "^5.1.3",
5958
"svelte2tsx": "workspace:~",
6059
"typescript": "^5.5.2",
6160
"typescript-auto-import-cache": "^0.3.3",

packages/language-server/src/importPackage.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { dirname, resolve } from 'path';
22
import * as prettier from 'prettier';
33
import * as svelte from 'svelte/compiler';
4-
import sveltePreprocess from 'svelte-preprocess';
54
import { Logger } from './logger';
65

76
/**
@@ -25,11 +24,15 @@ function dynamicRequire(dynamicFileToRequire: string): any {
2524
return require(dynamicFileToRequire);
2625
}
2726

28-
export function getPackageInfo(packageName: string, fromPath: string) {
29-
const paths = [__dirname];
27+
export function getPackageInfo(packageName: string, fromPath: string, use_fallback = true) {
28+
const paths: string[] = [];
3029
if (isTrusted) {
31-
paths.unshift(fromPath);
30+
paths.push(fromPath);
3231
}
32+
if (use_fallback) {
33+
paths.push(__dirname);
34+
}
35+
3336
const packageJSONPath = require.resolve(`${packageName}/package.json`, {
3437
paths
3538
});
@@ -73,8 +76,13 @@ export function importSvelte(fromPath: string): typeof svelte {
7376
}
7477
}
7578

76-
export function importSveltePreprocess(fromPath: string): typeof sveltePreprocess {
77-
const pkg = getPackageInfo('svelte-preprocess', fromPath);
79+
/** Can throw because no fallback guaranteed */
80+
export function importSveltePreprocess(fromPath: string): any {
81+
const pkg = getPackageInfo(
82+
'svelte-preprocess',
83+
fromPath,
84+
false // svelte-language-server doesn't have a dependency on svelte-preprocess so we can't provide a fallback
85+
);
7886
const main = resolve(pkg.path);
7987
Logger.debug('Using svelte-preprocess v' + pkg.version.full, 'from', main);
8088
return dynamicRequire(main);

packages/language-server/src/lib/documents/configLoader.ts

+44-18
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import _path from 'path';
99
import _fs from 'fs';
1010
import { pathToFileURL, URL } from 'url';
1111
import { FileMap } from './fileCollection';
12+
import ts from 'typescript';
1213

1314
export type InternalPreprocessorGroup = PreprocessorGroup & {
1415
/**
@@ -249,24 +250,49 @@ export class ConfigLoader {
249250
}
250251

251252
private useFallbackPreprocessor(path: string, foundConfig: boolean): SvelteConfig {
252-
Logger.log(
253-
(foundConfig
254-
? 'Found svelte.config.js but there was an error loading it. '
255-
: 'No svelte.config.js found. ') +
256-
'Using https://github.com/sveltejs/svelte-preprocess as fallback'
257-
);
258-
const sveltePreprocess = importSveltePreprocess(path);
259-
return {
260-
preprocess: sveltePreprocess({
261-
// 4.x does not have transpileOnly anymore, but if the user has version 3.x
262-
// in his repo, that one is loaded instead, for which we still need this.
263-
typescript: <any>{
264-
transpileOnly: true,
265-
compilerOptions: { sourceMap: true, inlineSourceMap: false }
266-
}
267-
}),
268-
isFallbackConfig: true
269-
};
253+
try {
254+
const sveltePreprocess = importSveltePreprocess(path);
255+
Logger.log(
256+
(foundConfig
257+
? 'Found svelte.config.js but there was an error loading it. '
258+
: 'No svelte.config.js found. ') +
259+
'Using https://github.com/sveltejs/svelte-preprocess as fallback'
260+
);
261+
return {
262+
preprocess: sveltePreprocess({
263+
// 4.x does not have transpileOnly anymore, but if the user has version 3.x
264+
// in his repo, that one is loaded instead, for which we still need this.
265+
typescript: {
266+
transpileOnly: true,
267+
compilerOptions: { sourceMap: true, inlineSourceMap: false }
268+
}
269+
}),
270+
isFallbackConfig: true
271+
};
272+
} catch (e) {
273+
// User doesn't have svelte-preprocess installed, provide a barebones TS preprocessor
274+
return {
275+
preprocess: {
276+
// @ts-ignore name property exists in Svelte 4 onwards
277+
name: 'svelte-language-tools-ts-fallback-preprocessor',
278+
script: ({ content, attributes, filename }) => {
279+
if (attributes.lang !== 'ts') return;
280+
281+
const { outputText, sourceMapText } = ts.transpileModule(content, {
282+
fileName: filename,
283+
compilerOptions: {
284+
module: ts.ModuleKind.ESNext,
285+
target: ts.ScriptTarget.ESNext,
286+
sourceMap: true,
287+
verbatimModuleSyntax: true
288+
}
289+
});
290+
return { code: outputText, map: sourceMapText };
291+
}
292+
},
293+
isFallbackConfig: true
294+
};
295+
}
270296
}
271297
}
272298

packages/language-server/src/plugins/svelte/SvelteDocument.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type PositionMapper = Pick<DocumentMapper, 'getGeneratedPosition' | 'getOriginal
3737
export class SvelteDocument {
3838
private transpiledDoc: ITranspiledSvelteDocument | undefined;
3939
private compileResult: SvelteCompileResult | undefined;
40+
private svelteVersion: [number, number] | undefined;
4041

4142
public script: TagInformation | null;
4243
public moduleScript: TagInformation | null;
@@ -69,9 +70,11 @@ export class SvelteDocument {
6970

7071
async getTranspiled(): Promise<ITranspiledSvelteDocument> {
7172
if (!this.transpiledDoc) {
72-
const {
73-
version: { major, minor }
74-
} = getPackageInfo('svelte', this.getFilePath());
73+
if (!this.svelteVersion) {
74+
const { major, minor } = getPackageInfo('svelte', this.getFilePath()).version;
75+
this.svelteVersion = [major, minor];
76+
}
77+
const [major, minor] = this.svelteVersion;
7578

7679
if (major > 3 || (major === 3 && minor >= 32)) {
7780
this.transpiledDoc = await TranspiledSvelteDocument.create(

packages/language-server/src/plugins/svelte/features/getDiagnostics.ts

+43-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// @ts-ignore
2-
import { Warning } from 'svelte/types/compiler/interfaces';
31
import {
42
CancellationToken,
53
Diagnostic,
@@ -63,6 +61,17 @@ async function tryGetDiagnostics(
6361
if (cancellationToken?.isCancellationRequested) {
6462
return [];
6563
}
64+
65+
let ignoreScriptWarnings = false;
66+
let ignoreStyleWarnings = false;
67+
let ignoreTemplateWarnings = false;
68+
if (!document.config?.preprocess || !!document.config.isFallbackConfig) {
69+
ignoreTemplateWarnings = !!document.getLanguageAttribute('template');
70+
ignoreStyleWarnings = !!document.getLanguageAttribute('style');
71+
const scriptAttr = document.getLanguageAttribute('script');
72+
ignoreScriptWarnings = !!scriptAttr && scriptAttr !== 'ts';
73+
}
74+
6675
return (res.warnings || [])
6776
.filter((warning) => settings[warning.code] !== 'ignore')
6877
.map((warning) => {
@@ -81,7 +90,15 @@ async function tryGetDiagnostics(
8190
})
8291
.map((diag) => mapObjWithRangeToOriginal(transpiled, diag))
8392
.map((diag) => adjustMappings(diag, document))
84-
.filter((diag) => isNoFalsePositive(diag, document));
93+
.filter((diag) =>
94+
isNoFalsePositive(
95+
diag,
96+
document,
97+
ignoreScriptWarnings,
98+
ignoreStyleWarnings,
99+
ignoreTemplateWarnings
100+
)
101+
);
85102
} catch (err) {
86103
return createParserErrorDiagnostic(err, document)
87104
.map((diag) => mapObjWithRangeToOriginal(transpiled, diag))
@@ -290,8 +307,28 @@ function getErrorMessage(error: any, source: string, hint = '') {
290307
);
291308
}
292309

293-
function isNoFalsePositive(diag: Diagnostic, doc: Document): boolean {
294-
if (diag.code !== 'unused-export-let') {
310+
function isNoFalsePositive(
311+
diag: Diagnostic,
312+
doc: Document,
313+
ignoreScriptWarnings: boolean,
314+
ignoreStyleWarnings: boolean,
315+
ignoreTemplateWarnings: boolean
316+
): boolean {
317+
if (
318+
(ignoreTemplateWarnings || ignoreScriptWarnings) &&
319+
(typeof diag.code !== 'string' || !diag.code.startsWith('a11y'))
320+
) {
321+
return false;
322+
}
323+
324+
if (
325+
ignoreStyleWarnings &&
326+
(diag.code === 'css-unused-selector' || diag.code === 'css_unused_selector')
327+
) {
328+
return false;
329+
}
330+
331+
if (diag.code !== 'unused-export-let' && diag.code !== 'export_let_unused') {
295332
return true;
296333
}
297334

@@ -328,7 +365,7 @@ function adjustMappings(diag: Diagnostic, doc: Document): Diagnostic {
328365
diag.range = moveRangeStartToEndIfNecessary(diag.range);
329366

330367
if (
331-
diag.code === 'css-unused-selector' &&
368+
(diag.code === 'css-unused-selector' || diag.code === 'css_unused_selector') &&
332369
doc.styleInfo &&
333370
!isInTag(diag.range.start, doc.styleInfo)
334371
) {

packages/language-server/src/plugins/typescript/features/DiagnosticsProvider.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,11 @@ function moveBindingErrorMessage(
211211
);
212212
diagnostic.message =
213213
"Cannot use 'bind:' with this property. It is declared as non-bindable inside the component.\n" +
214-
`To mark a property as bindable: 'let { ${propName} = $bindable() = $props()'`;
214+
`To mark a property as bindable: 'let { ${propName} = $bindable() } = $props()'`;
215215
} else {
216216
diagnostic.message =
217217
"Cannot use 'bind:' with this property. It is declared as non-bindable inside the component.\n" +
218-
`To mark a property as bindable: 'let { prop = $bindable() = $props()'\n\n` +
218+
`To mark a property as bindable: 'let { prop = $bindable() } = $props()'\n\n` +
219219
diagnostic.message;
220220
}
221221
diagnostic.range = {

packages/language-server/test/plugins/svelte/features/getDiagnostics.test.ts

+21-6
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ describe('SveltePlugin#getDiagnostics', () => {
3131
docText?: string;
3232
}) {
3333
const document = new Document('', docText);
34-
const svelteDoc: SvelteDocument = <any>{ getTranspiled, getCompiled, config };
34+
const svelteDoc: SvelteDocument = <any>{
35+
getTranspiled,
36+
getCompiled,
37+
config,
38+
getSvelteVersion: () => [4, 0]
39+
};
3540
const result = await getDiagnostics(document, svelteDoc, settings);
3641
return {
3742
toEqual: (expected: Diagnostic[]) => assert.deepStrictEqual(result, expected)
@@ -298,7 +303,9 @@ describe('SveltePlugin#getDiagnostics', () => {
298303
}
299304
]
300305
}),
301-
config: {}
306+
config: {
307+
preprocess: []
308+
}
302309
})
303310
).toEqual([
304311
{
@@ -343,7 +350,9 @@ describe('SveltePlugin#getDiagnostics', () => {
343350
]
344351
}
345352
}),
346-
config: {}
353+
config: {
354+
preprocess: []
355+
}
347356
})
348357
).toEqual([]);
349358
});
@@ -372,7 +381,9 @@ describe('SveltePlugin#getDiagnostics', () => {
372381
]
373382
}
374383
}),
375-
config: {}
384+
config: {
385+
preprocess: []
386+
}
376387
})
377388
).toEqual([]);
378389
});
@@ -399,7 +410,9 @@ describe('SveltePlugin#getDiagnostics', () => {
399410
]
400411
}
401412
}),
402-
config: {},
413+
config: {
414+
preprocess: []
415+
},
403416
settings: { 123: 'ignore' }
404417
})
405418
).toEqual([]);
@@ -425,7 +438,9 @@ describe('SveltePlugin#getDiagnostics', () => {
425438
}
426439
]
427440
}),
428-
config: {},
441+
config: {
442+
preprocess: []
443+
},
429444
settings: { 123: 'error' }
430445
})
431446
).toEqual([

packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings/expected_svelte_5.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
33
"code": 2322,
4-
"message": "Cannot use 'bind:' with this property. It is declared as non-bindable inside the component.\nTo mark a property as bindable: 'let { readonly = $bindable() = $props()'",
4+
"message": "Cannot use 'bind:' with this property. It is declared as non-bindable inside the component.\nTo mark a property as bindable: 'let { readonly = $bindable() } = $props()'",
55
"range": {
66
"end": {
77
"character": 20,
@@ -35,7 +35,7 @@
3535
},
3636
{
3737
"code": 2322,
38-
"message": "Cannot use 'bind:' with this property. It is declared as non-bindable inside the component.\nTo mark a property as bindable: 'let { only_bind = $bindable() = $props()'",
38+
"message": "Cannot use 'bind:' with this property. It is declared as non-bindable inside the component.\nTo mark a property as bindable: 'let { only_bind = $bindable() } = $props()'",
3939
"range": {
4040
"end": {
4141
"character": 21,

pnpm-lock.yaml

-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)