Skip to content

Commit 37d73e9

Browse files
authored
(fix) only patch readFile once (#1761)
Fixes issue with multiple ts projects in one workspace, for which the projectService host is reused. Also fix fallback logic, ensuring snapshot is always added
1 parent 518072f commit 37d73e9

File tree

1 file changed

+92
-62
lines changed

1 file changed

+92
-62
lines changed

packages/typescript-plugin/src/svelte-snapshots.ts

Lines changed: 92 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -279,73 +279,103 @@ export class SvelteSnapshotManager {
279279
}
280280

281281
private patchProjectServiceReadFile() {
282-
const readFile = this.projectService.host.readFile;
283-
this.projectService.host.readFile = (path: string, encoding?: string | undefined) => {
284-
if (!this.configManager.getConfig().enable) {
285-
return readFile(path, encoding);
286-
}
282+
// @ts-ignore The projectService is shared across some instances, make sure we patch readFile only once
283+
if (!this.projectService.host[onReadSvelteFile]) {
284+
this.logger.log('patching projectService host readFile');
285+
286+
// @ts-ignore
287+
this.projectService.host[onReadSvelteFile] = [];
287288

288-
// The following (very hacky) first two checks make sure that the ambient module definitions
289-
// that tell TS "every import ending with .svelte is a valid module" are removed.
290-
// They exist in svelte2tsx and svelte to make sure that people don't
291-
// get errors in their TS files when importing Svelte files and not using our TS plugin.
292-
// If someone wants to get back the behavior they can add an ambient module definition
293-
// on their own.
294-
const normalizedPath = path.replace(/\\/g, '/');
295-
if (normalizedPath.endsWith('node_modules/svelte/types/runtime/ambient.d.ts')) {
296-
return '';
297-
} else if (normalizedPath.endsWith('svelte2tsx/svelte-shims.d.ts')) {
298-
let originalText = readFile(path) || '';
299-
if (!originalText.includes('// -- start svelte-ls-remove --')) {
300-
return originalText; // uses an older version of svelte2tsx
289+
const readFile = this.projectService.host.readFile;
290+
this.projectService.host.readFile = (path: string, encoding?: string | undefined) => {
291+
if (!this.configManager.getConfig().enable) {
292+
return readFile(path, encoding);
301293
}
302-
originalText =
303-
originalText.substring(
304-
0,
305-
originalText.indexOf('// -- start svelte-ls-remove --')
306-
) +
307-
originalText.substring(originalText.indexOf('// -- end svelte-ls-remove --'));
308-
return originalText;
309-
} else if (isSvelteFilePath(path)) {
310-
this.logger.debug('Read Svelte file:', path);
311-
const svelteCode = readFile(path) || '';
312-
try {
313-
const isTsFile = true; // TODO check file contents? TS might be okay with importing ts into js.
314-
const result = svelte2tsx(svelteCode, {
315-
filename: path.split('/').pop(),
316-
isTsFile,
317-
mode: 'ts', // useNewTransformation
318-
typingsNamespace: this.svelteOptions.namespace
319-
});
320-
const canonicalFilePath = this.projectService.toCanonicalFileName(path);
321-
const existingSnapshot = this.snapshots.get(canonicalFilePath);
322-
if (existingSnapshot) {
323-
existingSnapshot.update(svelteCode, new SourceMapper(result.map.mappings));
324-
} else {
325-
this.snapshots.set(
326-
canonicalFilePath,
327-
new SvelteSnapshot(
328-
this.typescript,
329-
path,
330-
svelteCode,
331-
new SourceMapper(result.map.mappings),
332-
this.logger,
333-
isTsFile
334-
)
294+
295+
// The following (very hacky) first two checks make sure that the ambient module definitions
296+
// that tell TS "every import ending with .svelte is a valid module" are removed.
297+
// They exist in svelte2tsx and svelte to make sure that people don't
298+
// get errors in their TS files when importing Svelte files and not using our TS plugin.
299+
// If someone wants to get back the behavior they can add an ambient module definition
300+
// on their own.
301+
const normalizedPath = path.replace(/\\/g, '/');
302+
if (normalizedPath.endsWith('node_modules/svelte/types/runtime/ambient.d.ts')) {
303+
return '';
304+
} else if (normalizedPath.endsWith('svelte2tsx/svelte-shims.d.ts')) {
305+
let originalText = readFile(path) || '';
306+
if (!originalText.includes('// -- start svelte-ls-remove --')) {
307+
return originalText; // uses an older version of svelte2tsx or is already patched
308+
}
309+
originalText =
310+
originalText.substring(
311+
0,
312+
originalText.indexOf('// -- start svelte-ls-remove --')
313+
) +
314+
originalText.substring(
315+
originalText.indexOf('// -- end svelte-ls-remove --')
335316
);
317+
return originalText;
318+
} else if (isSvelteFilePath(path)) {
319+
this.logger.debug('Read Svelte file:', path);
320+
const svelteCode = readFile(path) || '';
321+
const isTsFile = true; // TODO check file contents? TS might be okay with importing ts into js.
322+
let code: string;
323+
let mapper: SourceMapper;
324+
325+
try {
326+
const result = svelte2tsx(svelteCode, {
327+
filename: path.split('/').pop(),
328+
isTsFile,
329+
mode: 'ts', // useNewTransformation
330+
typingsNamespace: this.svelteOptions.namespace
331+
});
332+
code = result.code;
333+
mapper = new SourceMapper(result.map.mappings);
334+
this.logger.log('Successfully read Svelte file contents of', path);
335+
} catch (e) {
336+
this.logger.log('Error loading Svelte file:', path, ' Using fallback.');
337+
this.logger.debug('Error:', e);
338+
// Return something either way, else "X is not a module" errors will appear
339+
// in the TS files that use this file.
340+
code = 'export default class extends Svelte2TsxComponent<any,any,any> {}';
341+
mapper = new SourceMapper('');
336342
}
337-
this.logger.log('Successfully read Svelte file contents of', path);
338-
return result.code;
339-
} catch (e) {
340-
this.logger.log('Error loading Svelte file:', path, ' Using fallback.');
341-
this.logger.debug('Error:', e);
342-
// Return something either way, else "X is not a module" errors will appear
343-
// in the TS files that use this file.
344-
return 'export default class extends Svelte2TsxComponent<any,any,any> {}';
343+
344+
// @ts-ignore
345+
this.projectService.host[onReadSvelteFile].forEach((listener) =>
346+
listener(path, code, isTsFile, mapper)
347+
);
348+
349+
return code;
350+
} else {
351+
return readFile(path, encoding);
352+
}
353+
};
354+
}
355+
356+
// @ts-ignore
357+
this.projectService.host[onReadSvelteFile].push(
358+
(path: string, svelteCode: string, isTsFile: boolean, mapper: SourceMapper) => {
359+
const canonicalFilePath = this.projectService.toCanonicalFileName(path);
360+
const existingSnapshot = this.snapshots.get(canonicalFilePath);
361+
if (existingSnapshot) {
362+
existingSnapshot.update(svelteCode, mapper);
363+
} else {
364+
this.snapshots.set(
365+
canonicalFilePath,
366+
new SvelteSnapshot(
367+
this.typescript,
368+
path,
369+
svelteCode,
370+
mapper,
371+
this.logger,
372+
isTsFile
373+
)
374+
);
345375
}
346-
} else {
347-
return readFile(path, encoding);
348376
}
349-
};
377+
);
350378
}
351379
}
380+
381+
const onReadSvelteFile = Symbol('sveltePluginPatchSymbol');

0 commit comments

Comments
 (0)