UNPKG

@sveltejs/vite-plugin-svelte

Version:

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

136 lines (125 loc) 4.19 kB
/** @import { VitePreprocessOptions } from './public.js' */ /** @import { Preprocessor as SveltePreprocessor, PreprocessorGroup } from 'svelte/compiler' */ /** @import { InlineConfig, ResolvedConfig } from 'vite' */ import process from 'node:process'; import * as vite from 'vite'; import { mapToRelative, removeLangSuffix } from './utils/sourcemaps.js'; const { isCSSRequest, preprocessCSS, resolveConfig, //@ts-ignore rolldown types don't exist transformWithOxc } = vite; /** * @typedef {(code: string, filename: string) => Promise<{ code: string; map?: any; deps?: Set<string> }>} CssTransform */ const supportedScriptLangs = ['ts']; export const lang_sep = '.vite-preprocess'; /** * @param {VitePreprocessOptions} [opts] * @returns {PreprocessorGroup} */ export function vitePreprocess(opts) { /** @type {PreprocessorGroup} */ const preprocessor = { name: 'vite-preprocess' }; if (opts?.script === true) { preprocessor.script = viteScript().script; } if (opts?.style !== false) { const styleOpts = typeof opts?.style == 'object' ? opts?.style : undefined; preprocessor.style = viteStyle(styleOpts).style; } return preprocessor; } /** * @returns {{ script: SveltePreprocessor }} */ function viteScript() { return { async script({ attributes, content, filename = '' }) { if (typeof attributes.lang !== 'string' || !supportedScriptLangs.includes(attributes.lang)) { return; } const lang = /** @type {'ts'} */ (attributes.lang); const { code, map } = await transformWithOxc(content, filename, { lang, target: 'esnext', // oxc strips value imports it considers unused, but in Svelte files // these imports may be referenced in the template which oxc cannot see. // onlyRemoveTypeImports tells oxc to only strip type-only imports // (import type {...}, import { type X }) and preserve all value imports. typescript: { onlyRemoveTypeImports: true } }); mapToRelative(map, filename); return { code, map }; } }; } /** * @param {ResolvedConfig | InlineConfig} config * @returns {{ style: SveltePreprocessor }} */ function viteStyle(config = {}) { /** @type {Promise<CssTransform> | CssTransform} */ let cssTransform; const style = /** @type {SveltePreprocessor} */ ( async ({ attributes, content, filename = '' }) => { const ext = attributes.lang ? `.${attributes.lang}` : '.css'; if (attributes.lang && !isCSSRequest(ext)) return; if (!cssTransform) { cssTransform = createCssTransform(style, config).then((t) => (cssTransform = t)); } const transform = await cssTransform; const suffix = `${lang_sep}${ext}`; const moduleId = `${filename}${suffix}`; const { code, map, deps } = await transform(content, moduleId); removeLangSuffix(map, suffix); mapToRelative(map, filename); const dependencies = deps ? Array.from(deps).filter((d) => !d.endsWith(suffix)) : undefined; return { code, map: map ?? undefined, dependencies }; } ); // @ts-expect-error tag so can be found by v-p-s style.__resolvedConfig = null; return { style }; } /** * @param {SveltePreprocessor} style * @param {ResolvedConfig | InlineConfig} config * @returns {Promise<CssTransform>} */ async function createCssTransform(style, config) { /** @type {ResolvedConfig} */ let resolvedConfig; // @ts-expect-error special prop added if running in v-p-s if (style.__resolvedConfig) { // @ts-expect-error not typed resolvedConfig = style.__resolvedConfig; } else if (isResolvedConfig(config)) { resolvedConfig = config; } else { // default to "build" if no NODE_ENV is set to avoid running in dev mode for svelte-check etc. const useBuild = !process.env.NODE_ENV || process.env.NODE_ENV === 'production'; const command = useBuild ? 'build' : 'serve'; const defaultMode = useBuild ? 'production' : 'development'; resolvedConfig = await resolveConfig(config, command, defaultMode, defaultMode, false); } return async (code, filename) => { return preprocessCSS(code, filename, resolvedConfig); }; } /** * @param {any} config * @returns {config is ResolvedConfig} */ function isResolvedConfig(config) { return !!config.inlineConfig; }