diff --git a/src/frontend/SvelteViewEngine.ts b/src/frontend/SvelteViewEngine.ts index 97fa456..5a87df4 100644 --- a/src/frontend/SvelteViewEngine.ts +++ b/src/frontend/SvelteViewEngine.ts @@ -7,7 +7,6 @@ import {promises as fs} from 'fs'; import path from "path"; import requireFromString from "require-from-string"; import {compile, preprocess} from "svelte/compiler"; -import {CssResult} from "svelte/types/compiler/interfaces"; import {sveltePreprocess} from "svelte-preprocess/dist/autoProcess.js"; import {logger} from "../Logger.js"; @@ -31,6 +30,7 @@ export default class SvelteViewEngine extends ViewEngine { private readonly fileCache: FileCache = new FileCache(); private readonly dependencyCache: Record> = {}; private readonly preprocessingCache: Record = {}; + private readonly cssCache: Record = {}; public constructor( targetDir: string, @@ -96,24 +96,20 @@ export default class SvelteViewEngine extends ViewEngine { } public async preCompile(canonicalName: string, alsoCompileDependents: boolean): Promise { - const intermediateFile = path.join(this.targetDir, canonicalName); + const targetFile = path.join(this.targetDir, canonicalName); + logger.info(canonicalName + ' > ', 'Pre-compiling', canonicalName, '->', targetFile); - logger.info(canonicalName + ' > ', 'Pre-compiling', canonicalName, '->', intermediateFile); - - const allBackendCalls: string[] = []; - - const {backendCalls, code} = await this.preprocess(canonicalName); - allBackendCalls.push(...backendCalls); + const {backendCalls} = await this.preprocess(canonicalName); // Server Side Render (initial HTML and CSS, no-js) - const ssr = await this.compileSsr(canonicalName, intermediateFile, code, allBackendCalls); + const ssr = await this.compileSsr(canonicalName); const separator = SvelteViewEngine.getPreCompileSeparator(canonicalName); const finalCode = [ - [...new Set(allBackendCalls).values()].join('\n'), + [...new Set(backendCalls).values()].join('\n'), ssr.head, ssr.html, - ssr.css.code, + ssr.css, ].join(separator); const swafViewFile = path.join(this.targetDir, canonicalName + COMPILED_SVELTE_EXTENSION); @@ -261,18 +257,36 @@ export default class SvelteViewEngine extends ViewEngine { }; } - private async compileSsr(canonicalName: string, file: string, code: string, backendCalls: string[]): Promise<{ + private async compileSsr(canonicalName: string): Promise<{ head: string, - css: CssResult, + css: string, html: string, }> { + const targetFile = path.join(this.targetDir, canonicalName); + const {backendCalls, code} = await this.preprocess(canonicalName); + + // Get dependencies css + const dependenciesCss: string[] = []; + for (const dependency of this.resolveDependencies(code, canonicalName)) { + if (this.cssCache[dependency] === undefined) { + await this.compileSsr(dependency); + } + const css = this.cssCache[dependency]; + if (css === undefined) { + logger.error(typeof this.cssCache[dependency], !!this.cssCache[dependency]); + throw new Error(`Compiling ssr of ${dependency} didn't cache its css.`); + } + dependenciesCss.push(...css); + } + + logger.info(canonicalName + ' > ', 'Compiling svelte ssr', targetFile); + // Svelte compile - logger.info(canonicalName + ' > ', 'Compiling svelte ssr', file); const svelteSsr = compile(code, { dev: config.get('view.dev'), generate: 'ssr', format: 'cjs', - cssOutputFilename: file + '.css', + cssOutputFilename: targetFile + '.css', }); // Load locals into locals store @@ -287,9 +301,22 @@ export default class SvelteViewEngine extends ViewEngine { }); // Load module and render - const moduleId = path.resolve(file); + const moduleId = path.resolve(targetFile); clearModule.single(moduleId); - return requireFromString(svelteSsr.js.code, moduleId).default.render(); + const { + head, + css, + html, + } = requireFromString(svelteSsr.js.code, moduleId).default.render(); + const cssFragments = css.code === '' ? + dependenciesCss : + [...dependenciesCss, css.code]; + this.cssCache[canonicalName] = cssFragments; + return { + head, + css: [...new Set(cssFragments)].join(''), + html, + }; } private compileBackendCalls(