diff --git a/packages/svelte/src/preprocessors.ts b/packages/svelte/src/preprocessors.ts index f97948eef868..49f8346aa47d 100644 --- a/packages/svelte/src/preprocessors.ts +++ b/packages/svelte/src/preprocessors.ts @@ -22,8 +22,30 @@ export function componentTrackingPreprocessor(options?: ComponentTrackingInitOpt const mergedOptions = { ...defaultComponentTrackingOptions, ...options }; const visitedFiles = new Set(); + const visitedFilesMarkup = new Set(); const preprocessor: PreprocessorGroup = { + // This markup hook is called once per .svelte component file, before the `script` hook is called + // We use it to check if the passed component has a is important! Without any content, + // the `script` hook wouldn't be executed for the added script tag. + const s = new MagicString(content); + s.prepend('\n'); + return { code: s.toString(), map: s.generateMap().toString() }; + } + + return { code: content }; + }, + // This script hook is called whenever a Svelte component's \n

I'm just a plain component

\n", + ); }); - expectComponentCodeToBeModified([cmp11, cmp2], { trackInit: true, trackUpdates: true }); - expect(cmp12.newCode).toEqual(cmp12.originalCode); + it("doesn't add a \n

I'm a component with a script

\n", + filename: 'lib/Cmp2.svelte', + name: 'Cmp2', + }; + + const res: any = + preProc.markup && + preProc.markup({ + content: component.originalCode, + filename: component.filename, + }); + + expect(res.code).toEqual( + "\n

I'm a component with a script

\n", + ); + }); }); - it('doesnt inject the function call to a module context script block', () => { - const preProc = componentTrackingPreprocessor(); - const component = { - originalCode: 'console.log(cmp2)', - filename: 'lib/Cmp2.svelte', - name: 'Cmp2', - }; + // These are more "higher level" tests in which we use the actual preprocessing command of the Svelte compiler + // This lets us test all preprocessor hooks we use in the correct order + describe('all hooks combined, using the svelte compiler', () => { + it('handles components without script tags correctly', async () => { + const component = { + originalCode: "

I'm just a plain component

\n", + filename: 'lib/Cmp1.svelte', + }; - const res: any = - preProc.script && - preProc.script({ - content: component.originalCode, + const processedCode = await svelteCompiler.preprocess(component.originalCode, [componentTrackingPreprocessor()], { filename: component.filename, - attributes: { context: 'module' }, - markup: '', }); - const processedComponent = { ...component, newCode: res.code, map: res.map }; + expect(processedCode.code).toEqual( + '\n' + + "

I'm just a plain component

\n" + + '', + ); + }); + + it('handles components with script tags correctly', async () => { + const component = { + originalCode: + "\n

I'm a component with a script

\n", + filename: 'lib/Cmp2.svelte', + }; + + const processedCode = await svelteCompiler.preprocess(component.originalCode, [componentTrackingPreprocessor()], { + filename: component.filename, + }); - expect(processedComponent.newCode).toEqual(processedComponent.originalCode); + expect(processedCode.code).toEqual( + '\n" + + "

I'm a component with a script

\n" + + '', + ); + }); + + it("falls back to 'unknown' if filename isn't passed to preprocessor", async () => { + const component = { + originalCode: "", + filename: undefined, + }; + + const processedCode = await svelteCompiler.preprocess(component.originalCode, [componentTrackingPreprocessor()], { + filename: component.filename, + }); + + expect(processedCode.code).toEqual( + '", + ); + }); }); });