|
import * as svelte from 'svelte/compiler'; |
|
|
|
import { createMakeHot } from 'svelte-hmr'; |
|
import { safeBase64Hash } from './hash.js'; |
|
import { log } from './log.js'; |
|
|
|
import { |
|
checkPreprocessDependencies, |
|
createInjectScopeEverythingRulePreprocessorGroup |
|
} from './preprocess.js'; |
|
import { mapToRelative } from './sourcemaps.js'; |
|
import { enhanceCompileError } from './error.js'; |
|
import { isSvelte5 } from './svelte-version.js'; |
|
|
|
|
|
|
|
|
|
const scriptLangRE = |
|
/<!--[^]*?-->|<script (?:[^>]*|(?:[^=>'"/]+=(?:"[^"]*"|'[^']*'|[^>\s]+)\s+)*)lang=["']?([^"' >]+)["']?[^>]*>/g; |
|
|
|
|
|
|
|
|
|
|
|
export const _createCompileSvelte = (makeHot) => { |
|
|
|
let stats; |
|
const devStylePreprocessor = createInjectScopeEverythingRulePreprocessorGroup(); |
|
|
|
return async function compileSvelte(svelteRequest, code, options) { |
|
const { filename, normalizedFilename, cssId, ssr, raw } = svelteRequest; |
|
const { emitCss = true } = options; |
|
|
|
const dependencies = []; |
|
|
|
const warnings = []; |
|
|
|
if (options.stats) { |
|
if (options.isBuild) { |
|
if (!stats) { |
|
|
|
|
|
stats = options.stats.startCollection(`${ssr ? 'ssr' : 'dom'} compile`, { |
|
logInProgress: () => false |
|
}); |
|
} |
|
} else { |
|
|
|
if (ssr && !stats) { |
|
stats = options.stats.startCollection('ssr compile'); |
|
} |
|
|
|
if (!ssr && stats) { |
|
stats.finish(); |
|
stats = undefined; |
|
} |
|
|
|
|
|
|
|
} |
|
} |
|
|
|
const compileOptions = { |
|
...options.compilerOptions, |
|
filename, |
|
|
|
generate: isSvelte5 ? (ssr ? 'server' : 'client') : ssr ? 'ssr' : 'dom' |
|
}; |
|
|
|
if (options.hot && options.emitCss) { |
|
const hash = `s-${safeBase64Hash(normalizedFilename)}`; |
|
compileOptions.cssHash = () => hash; |
|
} |
|
|
|
let preprocessed; |
|
let preprocessors = options.preprocess; |
|
if (!options.isBuild && options.emitCss && options.hot) { |
|
|
|
if (!Array.isArray(preprocessors)) { |
|
preprocessors = preprocessors |
|
? [preprocessors, devStylePreprocessor] |
|
: [devStylePreprocessor]; |
|
} else { |
|
preprocessors = preprocessors.concat(devStylePreprocessor); |
|
} |
|
} |
|
if (preprocessors) { |
|
try { |
|
preprocessed = await svelte.preprocess(code, preprocessors, { filename }); |
|
} catch (e) { |
|
e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`; |
|
throw e; |
|
} |
|
|
|
if (preprocessed.dependencies?.length) { |
|
const checked = checkPreprocessDependencies(filename, preprocessed.dependencies); |
|
if (checked.warnings.length) { |
|
warnings.push(...checked.warnings); |
|
} |
|
if (checked.dependencies.length) { |
|
dependencies.push(...checked.dependencies); |
|
} |
|
} |
|
|
|
if (preprocessed.map) compileOptions.sourcemap = preprocessed.map; |
|
} |
|
if (typeof preprocessed?.map === 'object') { |
|
mapToRelative(preprocessed?.map, filename); |
|
} |
|
if (raw && svelteRequest.query.type === 'preprocessed') { |
|
|
|
return { |
|
preprocessed: preprocessed ?? { code } |
|
}; |
|
} |
|
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 = stats?.start(filename); |
|
|
|
let compiled; |
|
try { |
|
compiled = svelte.compile(finalCode, finalCompileOptions); |
|
} catch (e) { |
|
enhanceCompileError(e, code, preprocessors); |
|
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) { |
|
|
|
const hasCss = compiled.css?.code?.trim().length > 0; |
|
|
|
if (emitCss && hasCss) { |
|
|
|
compiled.js.code += `\nimport ${JSON.stringify(cssId)};\n`; |
|
} |
|
|
|
|
|
if (!ssr && makeHot) { |
|
compiled.js.code = makeHot({ |
|
id: filename, |
|
compiledCode: compiled.js.code, |
|
|
|
hotOptions: { ...options.hot, injectCss: options.hot?.injectCss === true && hasCss }, |
|
compiled, |
|
originalCode: code, |
|
compileOptions: finalCompileOptions |
|
}); |
|
} |
|
} |
|
|
|
let lang = 'js'; |
|
for (const match of code.matchAll(scriptLangRE)) { |
|
if (match[1]) { |
|
lang = match[1]; |
|
break; |
|
} |
|
} |
|
|
|
return { |
|
filename, |
|
normalizedFilename, |
|
lang, |
|
|
|
compiled, |
|
ssr, |
|
dependencies, |
|
preprocessed: preprocessed ?? { code } |
|
}; |
|
}; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
function buildMakeHot(options) { |
|
const needsMakeHot = |
|
!isSvelte5 && options.hot !== false && options.isServe && !options.isProduction; |
|
if (needsMakeHot) { |
|
|
|
const hotApi = options?.hot?.hotApi; |
|
|
|
const adapter = options?.hot?.adapter; |
|
return createMakeHot({ |
|
walk: svelte.walk, |
|
hotApi, |
|
adapter, |
|
hotOptions: { noOverlay: true, ... (options.hot) } |
|
}); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
export function createCompileSvelte(options) { |
|
const makeHot = buildMakeHot(options); |
|
return _createCompileSvelte(makeHot); |
|
} |
|
|