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

View File

@ -73,25 +73,7 @@ export default class SvelteViewEngine extends ViewEngine {
css, css,
] = view.split(SvelteViewEngine.getPreCompileSeparator(canonicalViewName)); ] = view.split(SvelteViewEngine.getPreCompileSeparator(canonicalViewName));
const localMap: Record<string, unknown> = {}; const localMap: Record<string, unknown> = this.compileBackendCalls(backendCalls.split('\n'), locals, false);
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 actualLocals = JSON.stringify(localMap, (key, value) => { const actualLocals = JSON.stringify(localMap, (key, value) => {
return typeof value === 'function' ? return typeof value === 'function' ?
value.toString() : value.toString() :
@ -128,7 +110,7 @@ export default class SvelteViewEngine extends ViewEngine {
allBackendCalls.push(...backendCalls); allBackendCalls.push(...backendCalls);
// Server Side Render (initial HTML and CSS, no-js) // 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 separator = SvelteViewEngine.getPreCompileSeparator(canonicalName);
const finalCode = [ 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, head: string,
css: CssResult, css: CssResult,
html: string, html: string,
@ -297,8 +279,9 @@ export default class SvelteViewEngine extends ViewEngine {
const localsModulePath = "../../build/ts/stores.js"; const localsModulePath = "../../build/ts/stores.js";
const localsModule = await import(localsModulePath); const localsModule = await import(localsModulePath);
const locals = ViewEngine.getGlobals(); const locals = ViewEngine.getGlobals();
const localMap = this.compileBackendCalls(backendCalls, locals, true);
localsModule.locals.set((key: string, args: string) => { localsModule.locals.set((key: string, args: string) => {
return locals[args ? return localMap[args ?
`'${key}', \`${args}\`` `'${key}', \`${args}\``
: `'${key}'`]; : `'${key}'`];
}); });
@ -308,6 +291,35 @@ export default class SvelteViewEngine extends ViewEngine {
clearModule.single(moduleId); clearModule.single(moduleId);
return requireFromString(svelteSsr.js.code, moduleId).default.render(); 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 = { type PreprocessingCacheEntry = {

View File

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