UNPKG

@sveltejs/vite-plugin-svelte

Version:

The official [Svelte](https://svelte.dev) plugin for [Vite](https://vitejs.dev).

150 lines (138 loc) 4.71 kB
import * as svelte from 'svelte/compiler'; import { log } from './log.js'; import { mapToRelative } from './sourcemaps.js'; import { enhanceCompileError } from './error.js'; // TODO this is a patched version of https://github.com/sveltejs/vite-plugin-svelte/pull/796/files#diff-3bce0b33034aad4b35ca094893671f7e7ddf4d27254ae7b9b0f912027a001b15R10 // which is closer to the other regexes in at least not falling into commented script // but ideally would be shared exactly with svelte and other tools that use it const scriptLangRE = /<!--[^]*?-->|<script\s+(?:[^>]*|(?:[^=>'"/]+=(?:"[^"]*"|'[^']*'|[^>\s]+)\s+)*)lang=(["'])?([^"' >]+)\1[^>]*>/g; /** * @returns {import('../types/compile.d.ts').CompileSvelte} */ export function createCompileSvelte() { /** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */ let stats; /** @type {import('../types/compile.d.ts').CompileSvelte} */ return async function compileSvelte(svelteRequest, code, options, sourcemap) { const { filename, normalizedFilename, cssId, ssr, raw } = svelteRequest; const { emitCss = true } = options; /** @type {import('svelte/compiler').Warning[]} */ const warnings = []; if (options.stats) { if (options.isBuild) { if (!stats) { // build is either completely ssr or csr, create stats collector on first compile // it is then finished in the buildEnd hook. stats = options.stats.startCollection(`${ssr ? 'ssr' : 'dom'} compile`, { logInProgress: () => false }); } } else { // dev time ssr, it's a ssr request and there are no stats, assume new page load and start collecting if (ssr && !stats) { stats = options.stats.startCollection('ssr compile'); } // stats are being collected but this isn't an ssr request, assume page loaded and stop collecting if (!ssr && stats) { stats.finish(); stats = undefined; } // TODO find a way to trace dom compile during dev // problem: we need to call finish at some point but have no way to tell if page load finished // also they for hmr updates too } } /** @type {import('svelte/compiler').CompileOptions} */ const compileOptions = { ...options.compilerOptions, filename, generate: ssr ? 'server' : 'client' }; let finalCode = code; if (compileOptions.hmr && options.emitCss) { const closeStylePos = code.lastIndexOf('</style>'); if (closeStylePos > -1) { // inject rule that forces compile to attach scope class to every node in the template // this reduces the amount of js hot updates when editing css in .svelte files finalCode = finalCode.slice(0, closeStylePos) + ' *{}' + finalCode.slice(closeStylePos); } } const dynamicCompileOptions = await options?.dynamicCompileOptions?.({ filename, code: finalCode, compileOptions }); if (dynamicCompileOptions && log.debug.enabled) { log.debug( `dynamic compile options for ${filename}: ${JSON.stringify(dynamicCompileOptions)}`, undefined, 'compile' ); } const finalCompileOptions = dynamicCompileOptions ? { ...compileOptions, ...dynamicCompileOptions } : compileOptions; if (sourcemap) { finalCompileOptions.sourcemap = sourcemap; } const endStat = stats?.start(filename); /** @type {import('svelte/compiler').CompileResult} */ let compiled; try { compiled = svelte.compile(finalCode, { ...finalCompileOptions, filename }); // patch output with partial accept until svelte does it // TODO remove later if ( options.server?.config.experimental.hmrPartialAccept && compiled.js.code.includes('import.meta.hot.accept(') ) { compiled.js.code = compiled.js.code.replaceAll( 'import.meta.hot.accept(', 'import.meta.hot.acceptExports(["default"],' ); } } catch (e) { enhanceCompileError(e, code, options.preprocess); throw e; } if (endStat) { endStat(); } mapToRelative(compiled.js?.map, filename); mapToRelative(compiled.css?.map, filename); if (warnings.length) { if (!compiled.warnings) { compiled.warnings = []; } compiled.warnings.push(...warnings); } if (!raw) { // wire css import and code for hmr const hasCss = compiled.css?.code?.trim()?.length ?? 0 > 0; // compiler might not emit css with mode none or it may be empty if (emitCss && hasCss) { // TODO properly update sourcemap? compiled.js.code += `\nimport ${JSON.stringify(cssId)};\n`; } } let lang = 'js'; for (const match of code.matchAll(scriptLangRE)) { if (match[2]) { lang = match[2]; break; } } return { filename, normalizedFilename, cssId, lang, compiled, ssr }; }; }