File size: 4,217 Bytes
bc20498 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
import { readFileSync } from 'node:fs';
import * as svelte from 'svelte/compiler';
import { log } from './log.js';
import { toESBuildError } from './error.js';
import { isSvelte5 } from './svelte-version.js';
/**
* @typedef {NonNullable<import('vite').DepOptimizationOptions['esbuildOptions']>} EsbuildOptions
* @typedef {NonNullable<EsbuildOptions['plugins']>[number]} EsbuildPlugin
*/
export const facadeEsbuildSveltePluginName = 'vite-plugin-svelte:facade';
const svelteModuleExtension = '.svelte.js';
/**
* @param {import('../types/options.d.ts').ResolvedOptions} options
* @returns {EsbuildPlugin}
*/
export function esbuildSveltePlugin(options) {
return {
name: 'vite-plugin-svelte:optimize-svelte',
setup(build) {
// Skip in scanning phase as Vite already handles scanning Svelte files.
// Otherwise this would heavily slow down the scanning phase.
if (build.initialOptions.plugins?.some((v) => v.name === 'vite:dep-scan')) return;
const svelteExtensions = (options.extensions ?? ['.svelte']).map((ext) => ext.slice(1));
if (isSvelte5) {
svelteExtensions.push(svelteModuleExtension.slice(1));
}
const svelteFilter = new RegExp('\\.(' + svelteExtensions.join('|') + ')(\\?.*)?$');
/** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */
let statsCollection;
build.onStart(() => {
statsCollection = options.stats?.startCollection('prebundle libraries', {
logResult: (c) => c.stats.length > 1
});
});
build.onLoad({ filter: svelteFilter }, async ({ path: filename }) => {
const code = readFileSync(filename, 'utf8');
try {
const contents = await compileSvelte(options, { filename, code }, statsCollection);
return { contents };
} catch (e) {
return { errors: [toESBuildError(e, options)] };
}
});
build.onEnd(() => {
statsCollection?.finish();
});
}
};
}
/**
* @param {import('../types/options.d.ts').ResolvedOptions} options
* @param {{ filename: string; code: string }} input
* @param {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection} [statsCollection]
* @returns {Promise<string>}
*/
async function compileSvelte(options, { filename, code }, statsCollection) {
if (isSvelte5 && filename.endsWith(svelteModuleExtension)) {
const endStat = statsCollection?.start(filename);
// @ts-expect-error compileModule does not exist in svelte4
const compiled = svelte.compileModule(code, {
filename,
generate: 'client'
});
if (endStat) {
endStat();
}
return compiled.js.map
? compiled.js.code + '//# sourceMappingURL=' + compiled.js.map.toUrl()
: compiled.js.code;
}
let css = options.compilerOptions.css;
if (css !== 'none') {
// TODO ideally we'd be able to externalize prebundled styles too, but for now always put them in the js
css = 'injected';
}
/** @type {import('svelte/compiler').CompileOptions} */
const compileOptions = {
...options.compilerOptions,
css,
filename,
// @ts-expect-error svelte4 uses 'dom', svelte5 uses 'client'
generate: isSvelte5 ? 'client' : 'dom'
};
let preprocessed;
if (options.preprocess) {
try {
preprocessed = await svelte.preprocess(code, options.preprocess, { filename });
} catch (e) {
e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
throw e;
}
if (preprocessed.map) compileOptions.sourcemap = preprocessed.map;
}
const finalCode = preprocessed ? preprocessed.code : code;
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;
const endStat = statsCollection?.start(filename);
const compiled = svelte.compile(finalCode, finalCompileOptions);
if (endStat) {
endStat();
}
return compiled.js.map
? compiled.js.code + '//# sourceMappingURL=' + compiled.js.map.toUrl()
: compiled.js.code;
}
|