import { transformWithEsbuild, ESBuildOptions, ResolvedConfig, TransformResult, Plugin } from 'vite'; import MagicString from 'magic-string'; import { preprocess } from 'svelte/compiler'; import { Preprocessor, PreprocessorGroup, Processed, ResolvedOptions } from './options'; import { TransformPluginContext } from 'rollup'; import { log } from './log'; import { buildSourceMap } from './sourcemap'; import path from 'path'; const supportedStyleLangs = ['css', 'less', 'sass', 'scss', 'styl', 'stylus', 'postcss']; const supportedScriptLangs = ['ts']; function createViteScriptPreprocessor(): Preprocessor { return async ({ attributes, content, filename = '' }) => { const lang = attributes.lang as string; if (!supportedScriptLangs.includes(lang)) return; const transformResult = await transformWithEsbuild(content, filename, { loader: lang as ESBuildOptions['loader'], tsconfigRaw: { compilerOptions: { // svelte typescript needs this flag to work with type imports importsNotUsedAsValues: 'preserve', preserveValueImports: true } } }); return { code: transformResult.code, map: transformResult.map }; }; } function createViteStylePreprocessor(config: ResolvedConfig): Preprocessor { const pluginName = 'vite:css'; const plugin = config.plugins.find((p) => p.name === pluginName); if (!plugin) { throw new Error(`failed to find plugin ${pluginName}`); } if (!plugin.transform) { throw new Error(`plugin ${pluginName} has no transform`); } const pluginTransform = plugin.transform!.bind(null as unknown as TransformPluginContext); return async ({ attributes, content, filename = '' }) => { const lang = attributes.lang as string; if (!supportedStyleLangs.includes(lang)) return; const moduleId = `${filename}.${lang}`; const transformResult: TransformResult = (await pluginTransform( content, moduleId )) as TransformResult; // patch sourcemap source to point back to original filename if (transformResult.map?.sources?.[0] === moduleId) { transformResult.map.sources[0] = path.basename(filename); } return { code: transformResult.code, map: transformResult.map ?? undefined }; }; } function createVitePreprocessorGroup(config: ResolvedConfig): PreprocessorGroup { return { markup({ content, filename }) { return preprocess( content, { script: createViteScriptPreprocessor(), style: createViteStylePreprocessor(config) }, { filename } ); } } as PreprocessorGroup; } /** * this appends a *{} rule to component styles to force the svelte compiler to add style classes to all nodes * That means adding/removing class rules from