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: replace svelte-preprocess with barebones TS preprocessor #2452

Merged
merged 3 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion packages/language-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"prettier": "~3.2.5",
"prettier-plugin-svelte": "^3.2.2",
"svelte": "^3.57.0",
"svelte-preprocess": "^5.1.3",
"svelte2tsx": "workspace:~",
"typescript": "^5.5.2",
"typescript-auto-import-cache": "^0.3.3",
Expand Down
20 changes: 14 additions & 6 deletions packages/language-server/src/importPackage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { dirname, resolve } from 'path';
import * as prettier from 'prettier';
import * as svelte from 'svelte/compiler';
import sveltePreprocess from 'svelte-preprocess';
import { Logger } from './logger';

/**
Expand All @@ -25,11 +24,15 @@ function dynamicRequire(dynamicFileToRequire: string): any {
return require(dynamicFileToRequire);
}

export function getPackageInfo(packageName: string, fromPath: string) {
const paths = [__dirname];
export function getPackageInfo(packageName: string, fromPath: string, use_fallback = true) {
const paths: string[] = [];
if (isTrusted) {
paths.unshift(fromPath);
paths.push(fromPath);
}
if (use_fallback) {
paths.push(__dirname);
}

const packageJSONPath = require.resolve(`${packageName}/package.json`, {
paths
});
Expand Down Expand Up @@ -73,8 +76,13 @@ export function importSvelte(fromPath: string): typeof svelte {
}
}

export function importSveltePreprocess(fromPath: string): typeof sveltePreprocess {
const pkg = getPackageInfo('svelte-preprocess', fromPath);
/** Can throw because no fallback guaranteed */
export function importSveltePreprocess(fromPath: string): any {
const pkg = getPackageInfo(
'svelte-preprocess',
fromPath,
false // svelte-language-server doesn't have a dependency on svelte-preprocess so we can't provide a fallback
);
const main = resolve(pkg.path);
Logger.debug('Using svelte-preprocess v' + pkg.version.full, 'from', main);
return dynamicRequire(main);
Expand Down
62 changes: 44 additions & 18 deletions packages/language-server/src/lib/documents/configLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import _path from 'path';
import _fs from 'fs';
import { pathToFileURL, URL } from 'url';
import { FileMap } from './fileCollection';
import ts from 'typescript';

export type InternalPreprocessorGroup = PreprocessorGroup & {
/**
Expand Down Expand Up @@ -249,24 +250,49 @@ export class ConfigLoader {
}

private useFallbackPreprocessor(path: string, foundConfig: boolean): SvelteConfig {
Logger.log(
(foundConfig
? 'Found svelte.config.js but there was an error loading it. '
: 'No svelte.config.js found. ') +
'Using https://github.com/sveltejs/svelte-preprocess as fallback'
);
const sveltePreprocess = importSveltePreprocess(path);
return {
preprocess: sveltePreprocess({
// 4.x does not have transpileOnly anymore, but if the user has version 3.x
// in his repo, that one is loaded instead, for which we still need this.
typescript: <any>{
transpileOnly: true,
compilerOptions: { sourceMap: true, inlineSourceMap: false }
}
}),
isFallbackConfig: true
};
try {
const sveltePreprocess = importSveltePreprocess(path);
Logger.log(
(foundConfig
? 'Found svelte.config.js but there was an error loading it. '
: 'No svelte.config.js found. ') +
'Using https://github.com/sveltejs/svelte-preprocess as fallback'
);
return {
preprocess: sveltePreprocess({
// 4.x does not have transpileOnly anymore, but if the user has version 3.x
// in his repo, that one is loaded instead, for which we still need this.
typescript: {
transpileOnly: true,
compilerOptions: { sourceMap: true, inlineSourceMap: false }
}
}),
isFallbackConfig: true
};
} catch (e) {
// User doesn't have svelte-preprocess installed, provide a barebones TS preprocessor
return {
preprocess: {
// @ts-ignore name property exists in Svelte 4 onwards
name: 'svelte-language-tools-ts-fallback-preprocessor',
script: ({ content, attributes, filename }) => {
if (attributes.lang !== 'ts') return;

const { outputText, sourceMapText } = ts.transpileModule(content, {
fileName: filename,
compilerOptions: {
module: ts.ModuleKind.ESNext,
target: ts.ScriptTarget.ESNext,
sourceMap: true,
verbatimModuleSyntax: true
}
});
return { code: outputText, map: sourceMapText };
}
},
isFallbackConfig: true
};
}
}
}

Expand Down
9 changes: 6 additions & 3 deletions packages/language-server/src/plugins/svelte/SvelteDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type PositionMapper = Pick<DocumentMapper, 'getGeneratedPosition' | 'getOriginal
export class SvelteDocument {
private transpiledDoc: ITranspiledSvelteDocument | undefined;
private compileResult: SvelteCompileResult | undefined;
private svelteVersion: [number, number] | undefined;

public script: TagInformation | null;
public moduleScript: TagInformation | null;
Expand Down Expand Up @@ -69,9 +70,11 @@ export class SvelteDocument {

async getTranspiled(): Promise<ITranspiledSvelteDocument> {
if (!this.transpiledDoc) {
const {
version: { major, minor }
} = getPackageInfo('svelte', this.getFilePath());
if (!this.svelteVersion) {
const { major, minor } = getPackageInfo('svelte', this.getFilePath()).version;
this.svelteVersion = [major, minor];
}
const [major, minor] = this.svelteVersion;

if (major > 3 || (major === 3 && minor >= 32)) {
this.transpiledDoc = await TranspiledSvelteDocument.create(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-ignore
import { Warning } from 'svelte/types/compiler/interfaces';
import {
CancellationToken,
Diagnostic,
Expand Down Expand Up @@ -63,6 +61,17 @@ async function tryGetDiagnostics(
if (cancellationToken?.isCancellationRequested) {
return [];
}

let ignoreScriptWarnings = false;
let ignoreStyleWarnings = false;
let ignoreTemplateWarnings = false;
if (!document.config?.preprocess || !!document.config.isFallbackConfig) {
ignoreTemplateWarnings = !!document.getLanguageAttribute('template');
ignoreStyleWarnings = !!document.getLanguageAttribute('style');
const scriptAttr = document.getLanguageAttribute('script');
ignoreScriptWarnings = !!scriptAttr && scriptAttr !== 'ts';
}

return (res.warnings || [])
.filter((warning) => settings[warning.code] !== 'ignore')
.map((warning) => {
Expand All @@ -81,7 +90,15 @@ async function tryGetDiagnostics(
})
.map((diag) => mapObjWithRangeToOriginal(transpiled, diag))
.map((diag) => adjustMappings(diag, document))
.filter((diag) => isNoFalsePositive(diag, document));
.filter((diag) =>
isNoFalsePositive(
diag,
document,
ignoreScriptWarnings,
ignoreStyleWarnings,
ignoreTemplateWarnings
)
);
} catch (err) {
return createParserErrorDiagnostic(err, document)
.map((diag) => mapObjWithRangeToOriginal(transpiled, diag))
Expand Down Expand Up @@ -290,8 +307,28 @@ function getErrorMessage(error: any, source: string, hint = '') {
);
}

function isNoFalsePositive(diag: Diagnostic, doc: Document): boolean {
if (diag.code !== 'unused-export-let') {
function isNoFalsePositive(
diag: Diagnostic,
doc: Document,
ignoreScriptWarnings: boolean,
ignoreStyleWarnings: boolean,
ignoreTemplateWarnings: boolean
): boolean {
if (
(ignoreTemplateWarnings || ignoreScriptWarnings) &&
(typeof diag.code !== 'string' || !diag.code.startsWith('a11y'))
) {
return false;
}

if (
ignoreStyleWarnings &&
(diag.code === 'css-unused-selector' || diag.code === 'css_unused_selector')
) {
return false;
}

if (diag.code !== 'unused-export-let' && diag.code !== 'export_let_unused') {
return true;
}

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

if (
diag.code === 'css-unused-selector' &&
(diag.code === 'css-unused-selector' || diag.code === 'css_unused_selector') &&
doc.styleInfo &&
!isInTag(diag.range.start, doc.styleInfo)
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ describe('SveltePlugin#getDiagnostics', () => {
docText?: string;
}) {
const document = new Document('', docText);
const svelteDoc: SvelteDocument = <any>{ getTranspiled, getCompiled, config };
const svelteDoc: SvelteDocument = <any>{
getTranspiled,
getCompiled,
config,
getSvelteVersion: () => [4, 0]
};
const result = await getDiagnostics(document, svelteDoc, settings);
return {
toEqual: (expected: Diagnostic[]) => assert.deepStrictEqual(result, expected)
Expand Down Expand Up @@ -298,7 +303,9 @@ describe('SveltePlugin#getDiagnostics', () => {
}
]
}),
config: {}
config: {
preprocess: []
}
})
).toEqual([
{
Expand Down Expand Up @@ -343,7 +350,9 @@ describe('SveltePlugin#getDiagnostics', () => {
]
}
}),
config: {}
config: {
preprocess: []
}
})
).toEqual([]);
});
Expand Down Expand Up @@ -372,7 +381,9 @@ describe('SveltePlugin#getDiagnostics', () => {
]
}
}),
config: {}
config: {
preprocess: []
}
})
).toEqual([]);
});
Expand All @@ -399,7 +410,9 @@ describe('SveltePlugin#getDiagnostics', () => {
]
}
}),
config: {},
config: {
preprocess: []
},
settings: { 123: 'ignore' }
})
).toEqual([]);
Expand All @@ -425,7 +438,9 @@ describe('SveltePlugin#getDiagnostics', () => {
}
]
}),
config: {},
config: {
preprocess: []
},
settings: { 123: 'error' }
})
).toEqual([
Expand Down
3 changes: 0 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.