11import { SourceMapConsumer } from 'source-map' ;
2- import { PreprocessorGroup , Processed } from 'svelte/types/compiler/preprocess/types' ;
32import type { compile } from 'svelte/compiler' ;
43import { CompileOptions } from 'svelte/types/compiler/interfaces' ;
5-
4+ import { PreprocessorGroup , Processed } from 'svelte/types/compiler/preprocess/types' ;
65import { Position } from 'vscode-languageserver' ;
6+ import { getPackageInfo , importSvelte } from '../../importPackage' ;
77import {
88 Document ,
99 DocumentMapper ,
@@ -17,7 +17,7 @@ import {
1717 SourceMapDocumentMapper ,
1818 TagInformation
1919} from '../../lib/documents' ;
20- import { importSvelte } from '../../importPackage ' ;
20+ import { SvelteConfig } from '../../lib/documents/configLoader ' ;
2121import { isNotNullOrUndefined } from '../../utils' ;
2222
2323export type SvelteCompileResult = ReturnType < typeof compile > ;
@@ -33,7 +33,7 @@ type PositionMapper = Pick<DocumentMapper, 'getGeneratedPosition' | 'getOriginal
3333 * Represents a text document that contains a svelte component.
3434 */
3535export class SvelteDocument {
36- private transpiledDoc : TranspiledSvelteDocument | undefined ;
36+ private transpiledDoc : ITranspiledSvelteDocument | undefined ;
3737 private compileResult : SvelteCompileResult | undefined ;
3838
3939 public script : TagInformation | null ;
@@ -65,14 +65,25 @@ export class SvelteDocument {
6565 return this . parent . offsetAt ( position ) ;
6666 }
6767
68- async getTranspiled ( ) : Promise < TranspiledSvelteDocument > {
68+ async getTranspiled ( ) : Promise < ITranspiledSvelteDocument > {
6969 if ( ! this . transpiledDoc ) {
70- this . transpiledDoc = await TranspiledSvelteDocument . create (
71- this . parent ,
72- (
70+ const {
71+ version : { major, minor }
72+ } = getPackageInfo ( 'svelte' , this . getFilePath ( ) ) ;
73+
74+ if ( major > 3 || ( major === 3 && minor >= 32 ) ) {
75+ this . transpiledDoc = await TranspiledSvelteDocument . create (
76+ this . parent ,
7377 await this . config
74- ) ?. preprocess
75- ) ;
78+ ) ;
79+ } else {
80+ this . transpiledDoc = await FallbackTranspiledSvelteDocument . create (
81+ this . parent ,
82+ (
83+ await this . config
84+ ) ?. preprocess
85+ ) ;
86+ }
7687 }
7788 return this . transpiledDoc ;
7889 }
@@ -101,7 +112,67 @@ export class SvelteDocument {
101112 }
102113}
103114
104- export class TranspiledSvelteDocument implements PositionMapper {
115+ export interface ITranspiledSvelteDocument extends PositionMapper {
116+ getText ( ) : string ;
117+ destroy ( ) : void ;
118+ }
119+
120+ export class TranspiledSvelteDocument implements ITranspiledSvelteDocument {
121+ static async create ( document : Document , config : SvelteConfig | undefined ) {
122+ if ( ! config ?. preprocess ) {
123+ return new TranspiledSvelteDocument ( document . getText ( ) ) ;
124+ }
125+
126+ const filename = document . getFilePath ( ) || '' ;
127+ const svelte = importSvelte ( filename ) ;
128+ const preprocessed = await svelte . preprocess ( document . getText ( ) , config ?. preprocess || [ ] , {
129+ filename
130+ } ) ;
131+
132+ if ( preprocessed . code === document . getText ( ) ) {
133+ return new TranspiledSvelteDocument ( document . getText ( ) ) ;
134+ }
135+
136+ return new TranspiledSvelteDocument (
137+ preprocessed . code ,
138+ preprocessed . map
139+ ? new SourceMapDocumentMapper (
140+ await createSourceMapConsumer ( preprocessed . map ) ,
141+ // The "sources" array only contains the Svelte filename, not its path.
142+ // For getting generated positions, the sourcemap consumer wants an exact match
143+ // of the source filepath. Therefore only pass in the filename here.
144+ filename . replace ( / \\ / g, '/' ) . split ( '/' ) . pop ( ) || ''
145+ )
146+ : undefined
147+ ) ;
148+ }
149+
150+ constructor ( private code : string , private mapper ?: SourceMapDocumentMapper ) { }
151+
152+ getOriginalPosition ( generatedPosition : Position ) : Position {
153+ return this . mapper ?. getOriginalPosition ( generatedPosition ) || generatedPosition ;
154+ }
155+
156+ getText ( ) {
157+ return this . code ;
158+ }
159+
160+ getGeneratedPosition ( originalPosition : Position ) : Position {
161+ return this . mapper ?. getGeneratedPosition ( originalPosition ) || originalPosition ;
162+ }
163+
164+ destroy ( ) {
165+ this . mapper ?. destroy ( ) ;
166+ }
167+ }
168+
169+ /**
170+ * Only used when the user has an old Svelte version installed where source map support
171+ * for preprocessors is not built in yet.
172+ * This fallback version does not map correctly when there's both a module and instance script.
173+ * It isn't worth fixing these cases though now that Svelte ships a preprocessor with source maps.
174+ */
175+ export class FallbackTranspiledSvelteDocument implements ITranspiledSvelteDocument {
105176 static async create (
106177 document : Document ,
107178 preprocessors : PreprocessorGroup | PreprocessorGroup [ ] = [ ]
@@ -121,7 +192,12 @@ export class TranspiledSvelteDocument implements PositionMapper {
121192 processedStyles
122193 ) ;
123194
124- return new TranspiledSvelteDocument ( document , transpiled , scriptMapper , styleMapper ) ;
195+ return new FallbackTranspiledSvelteDocument (
196+ document ,
197+ transpiled ,
198+ scriptMapper ,
199+ styleMapper
200+ ) ;
125201 }
126202
127203 private fragmentInfos = [ this . scriptMapper ?. fragmentInfo , this . styleMapper ?. fragmentInfo ]
@@ -252,23 +328,13 @@ export class SvelteFragmentMapper implements PositionMapper {
252328 async ( parent , processedSingle ) =>
253329 processedSingle ?. map
254330 ? new SourceMapDocumentMapper (
255- await new SourceMapConsumer ( normalizeMap ( processedSingle . map ) ) ,
331+ await createSourceMapConsumer ( processedSingle . map ) ,
256332 originalDoc . uri ,
257333 await parent
258334 )
259335 : new IdentityMapper ( originalDoc . uri , await parent ) ,
260336 Promise . resolve < DocumentMapper > ( < any > undefined )
261337 ) ;
262-
263- function normalizeMap ( map : any ) {
264- // We don't know what we get, could be a stringified sourcemap,
265- // or a class which has the required properties on it, or a class
266- // which we need to call toString() on to get the correct format.
267- if ( typeof map === 'string' || map . version ) {
268- return map ;
269- }
270- return map . toString ( ) ;
271- }
272338 }
273339
274340 private constructor (
@@ -380,3 +446,17 @@ async function transpile(
380446
381447 return { transpiled, processedScripts, processedStyles } ;
382448}
449+
450+ async function createSourceMapConsumer ( map : any ) : Promise < SourceMapConsumer > {
451+ return new SourceMapConsumer ( normalizeMap ( map ) ) ;
452+
453+ function normalizeMap ( map : any ) {
454+ // We don't know what we get, could be a stringified sourcemap,
455+ // or a class which has the required properties on it, or a class
456+ // which we need to call toString() on to get the correct format.
457+ if ( typeof map === 'string' || map . version ) {
458+ return map ;
459+ }
460+ return map . toString ( ) ;
461+ }
462+ }
0 commit comments