async function preprocess(source, preprocessor, options) {
	/**
	 * @type {string | undefined}
	 */
	const filename = (options && options.filename) || /** @type {any} */ (preprocessor).filename; // legacy
	const preprocessors = preprocessor
		? Array.isArray(preprocessor)
			? preprocessor
			: [preprocessor]
		: [];
	const result = new PreprocessResult(source, filename);

	// TODO keep track: what preprocessor generated what sourcemap?
	// to make debugging easier = detect low-resolution sourcemaps in fn combine_mappings
	for (const preprocessor of preprocessors) {
		if (preprocessor.markup) {
			// @ts-expect-error TODO there might be a bug in hiding here
			result.update_source(await process_markup(preprocessor.markup, result));
		}
		if (preprocessor.script) {
			// @ts-expect-error TODO there might be a bug in hiding here
			result.update_source(await process_tag('script', preprocessor.script, result));
		}
		if (preprocessor.style) {
			// @ts-expect-error TODO there might be a bug in hiding here
			result.update_source(await process_tag('style', preprocessor.style, result));
		}
	}

	return result.to_processed();
}
The preprocess function provides convenient hooks for arbitrarily transforming component source code.
For example, it can be used to convert a `<style lang="sass">` block into vanilla CSS.

@param {string} source
@param {PreprocessorGroup | PreprocessorGroup[]} preprocessor
@param {{ filename?: string }} [options]
@returns {Promise<Processed>}