Svelte view engine: also compile backend calls on SSR

Add local to determine whether render is SSR or not
This commit is contained in:
Alice Gaudon 2021-05-12 13:54:54 +02:00
parent b247d73fcb
commit 85b4b39dd2
3 changed files with 36 additions and 25 deletions

View File

@ -1,5 +1,5 @@
import chokidar, {FSWatcher} from "chokidar";
import {existsSync, mkdirSync,promises as fs} from "fs";
import {existsSync, mkdirSync} from "fs";
import path from "path";
import {logger} from "../Logger.js";
@ -109,7 +109,7 @@ export default abstract class AssetPreCompiler {
await this.afterPreCompile(watch);
if (this.hadError && !watch) throw new Error('Errors while precompiling assets.');
}
}
public async watch(): Promise<void> {
const watchedPaths = this.assetPaths.map(p => `${p}/**/*.${this.getExtension()}`);

View File

@ -73,25 +73,7 @@ export default class SvelteViewEngine extends ViewEngine {
css,
] = view.split(SvelteViewEngine.getPreCompileSeparator(canonicalViewName));
const localMap: Record<string, unknown> = {};
backendCalls.split('\n').forEach(code => {
const key = code.substring(1, code.indexOf(',') >= 0 ? code.indexOf(',') - 1 : code.length - 1);
if (code.indexOf('`[') >= 0) {
const args = code.substring(code.indexOf('`[') + 2, code.length - 2)
.split(/, *?/)
.map(arg => {
if (arg.startsWith("'")) return '"' + arg.substring(1, arg.length - 1) + '"';
return arg;
})
.map(arg => Function(`"use strict";const locals = arguments[0];return (${arg});`)(locals)); // Uses named parameter locals
const f = locals[key];
if (typeof f !== 'function') throw new Error(key + ' is not a function.');
localMap[`'${key}', \`[${code.substring(code.indexOf('`[') + 2, code.length - 2)}]\``] = f.call(locals, ...args);
} else {
localMap[`'${key}'`] = locals[key];
}
});
const localMap: Record<string, unknown> = this.compileBackendCalls(backendCalls.split('\n'), locals, false);
const actualLocals = JSON.stringify(localMap, (key, value) => {
return typeof value === 'function' ?
value.toString() :
@ -128,7 +110,7 @@ export default class SvelteViewEngine extends ViewEngine {
allBackendCalls.push(...backendCalls);
// Server Side Render (initial HTML and CSS, no-js)
const ssr = await this.compileSsr(canonicalName, intermediateFile, code);
const ssr = await this.compileSsr(canonicalName, intermediateFile, code, allBackendCalls);
const separator = SvelteViewEngine.getPreCompileSeparator(canonicalName);
const finalCode = [
@ -279,7 +261,7 @@ export default class SvelteViewEngine extends ViewEngine {
};
}
private async compileSsr(canonicalName: string, file: string, code: string): Promise<{
private async compileSsr(canonicalName: string, file: string, code: string, backendCalls: string[]): Promise<{
head: string,
css: CssResult,
html: string,
@ -297,8 +279,9 @@ export default class SvelteViewEngine extends ViewEngine {
const localsModulePath = "../../build/ts/stores.js";
const localsModule = await import(localsModulePath);
const locals = ViewEngine.getGlobals();
const localMap = this.compileBackendCalls(backendCalls, locals, true);
localsModule.locals.set((key: string, args: string) => {
return locals[args ?
return localMap[args ?
`'${key}', \`${args}\``
: `'${key}'`];
});
@ -308,6 +291,35 @@ export default class SvelteViewEngine extends ViewEngine {
clearModule.single(moduleId);
return requireFromString(svelteSsr.js.code, moduleId).default.render();
}
private compileBackendCalls(
backendCalls: string[],
locals: Record<string, unknown>,
isPreRender: boolean,
): Record<string, unknown> {
locals = {...locals, isPreRender};
const localMap: Record<string, unknown> = {};
backendCalls.forEach(code => {
const key = code.substring(1, code.indexOf(',') >= 0 ? code.indexOf(',') - 1 : code.length - 1);
if (code.indexOf('`[') >= 0) {
const args = code.substring(code.indexOf('`[') + 2, code.length - 2)
.split(/, *?/)
.map(arg => {
if (arg.startsWith("'")) return '"' + arg.substring(1, arg.length - 1) + '"';
return arg;
})
.map(arg => Function(`"use strict";const $locals = arguments[0];return (${arg});`)(locals)); // Uses named parameter locals
const f = locals[key];
if (typeof f !== 'function') throw new Error(key + ' is not a function.');
localMap[`'${key}', \`[${code.substring(code.indexOf('`[') + 2, code.length - 2)}]\``] = f.call(locals, ...args);
} else {
localMap[`'${key}'`] = locals[key];
}
});
return localMap;
}
}
type PreprocessingCacheEntry = {

View File

@ -1,6 +1,5 @@
import {Express} from "express";
import {logger} from "../Logger.js";
import AssetPreCompiler from "./AssetPreCompiler.js";
export default abstract class ViewEngine extends AssetPreCompiler {