You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
3.3 KiB
TypeScript

import { preprocessCSS, resolveConfig, transformWithEsbuild } from 'vite';
import type { ESBuildOptions, InlineConfig, ResolvedConfig } from 'vite';
// eslint-disable-next-line node/no-missing-import
import type { Preprocessor, PreprocessorGroup } from 'svelte/types/compiler/preprocess';
import { mapToRelative, removeLangSuffix } from './utils/sourcemaps';
const supportedStyleLangs = ['css', 'less', 'sass', 'scss', 'styl', 'stylus', 'postcss', 'sss'];
const supportedScriptLangs = ['ts'];
export const lang_sep = '.vite-preprocess.';
export function vitePreprocess(opts?: {
script?: boolean;
style?: boolean | InlineConfig | ResolvedConfig;
}) {
const preprocessor: PreprocessorGroup = {};
if (opts?.script !== false) {
preprocessor.script = viteScript().script;
}
if (opts?.style !== false) {
const styleOpts = typeof opts?.style == 'object' ? opts?.style : undefined;
preprocessor.style = viteStyle(styleOpts).style;
}
return preprocessor;
}
function viteScript(): { script: Preprocessor } {
return {
async script({ attributes, content, filename = '' }) {
const lang = attributes.lang as string;
if (!supportedScriptLangs.includes(lang)) return;
const { code, map } = await transformWithEsbuild(content, filename, {
loader: lang as ESBuildOptions['loader'],
target: 'esnext',
tsconfigRaw: {
compilerOptions: {
// svelte typescript needs this flag to work with type imports
importsNotUsedAsValues: 'preserve',
preserveValueImports: true
}
}
});
mapToRelative(map, filename);
return {
code,
map
};
}
};
}
function viteStyle(config: InlineConfig | ResolvedConfig = {}): {
style: Preprocessor;
} {
let transform: CssTransform;
const style: Preprocessor = async ({ attributes, content, filename = '' }) => {
const lang = attributes.lang as string;
if (!supportedStyleLangs.includes(lang)) return;
if (!transform) {
let resolvedConfig: ResolvedConfig;
// @ts-expect-error special prop added if running in v-p-s
if (style.__resolvedConfig) {
// @ts-expect-error
resolvedConfig = style.__resolvedConfig;
} else if (isResolvedConfig(config)) {
resolvedConfig = config;
} else {
resolvedConfig = await resolveConfig(
config,
process.env.NODE_ENV === 'production' ? 'build' : 'serve'
);
}
transform = getCssTransformFn(resolvedConfig);
}
const suffix = `${lang_sep}${lang}`;
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 };
}
type CssTransform = (
// eslint-disable-next-line no-unused-vars
code: string,
// eslint-disable-next-line no-unused-vars
filename: string
) => Promise<{ code: string; map?: any; deps?: Set<string> }>;
function getCssTransformFn(config: ResolvedConfig): CssTransform {
return async (code, filename) => {
return preprocessCSS(code, filename, config);
};
}
function isResolvedConfig(config: any): config is ResolvedConfig {
return !!config.inlineConfig;
}