Add svelte as a view engine to swaf #33
@ -94,8 +94,11 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
// Register migrations
|
// Register migrations
|
||||||
MysqlConnectionManager.registerMigrations(this.getMigrations());
|
MysqlConnectionManager.registerMigrations(this.getMigrations());
|
||||||
|
|
||||||
// Register all components and alike
|
// Register and initialize all components and alike
|
||||||
await this.init();
|
await this.init();
|
||||||
|
for (const component of this.components) {
|
||||||
|
await component.init?.();
|
||||||
|
}
|
||||||
|
|
||||||
// Process command line
|
// Process command line
|
||||||
if (!this.ignoreCommandLine) {
|
if (!this.ignoreCommandLine) {
|
||||||
@ -218,14 +221,14 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
|
|
||||||
// Components routes
|
// Components routes
|
||||||
for (const component of this.components) {
|
for (const component of this.components) {
|
||||||
if (component.init) {
|
if (component.initRoutes) {
|
||||||
component.setCurrentRouter(initRouter);
|
component.setCurrentRouter(initRouter);
|
||||||
await component.init(initRouter);
|
await component.initRoutes(initRouter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.handle) {
|
if (component.handleRoutes) {
|
||||||
component.setCurrentRouter(handleRouter);
|
component.setCurrentRouter(handleRouter);
|
||||||
await component.handle(handleRouter);
|
await component.handleRoutes(handleRouter);
|
||||||
}
|
}
|
||||||
|
|
||||||
component.setCurrentRouter(null);
|
component.setCurrentRouter(null);
|
||||||
@ -292,7 +295,6 @@ export default abstract class Application implements Extendable<ApplicationCompo
|
|||||||
}
|
}
|
||||||
|
|
||||||
const frontendToolsComponent = this.as(FrontendToolsComponent);
|
const frontendToolsComponent = this.as(FrontendToolsComponent);
|
||||||
frontendToolsComponent.setupGlobals();
|
|
||||||
await frontendToolsComponent.preCompileViews(flags.watch);
|
await frontendToolsComponent.preCompileViews(flags.watch);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,13 @@ export default abstract class ApplicationComponent {
|
|||||||
|
|
||||||
public async checkSecuritySettings?(): Promise<void>;
|
public async checkSecuritySettings?(): Promise<void>;
|
||||||
|
|
||||||
|
public async init?(): Promise<void>;
|
||||||
|
|
||||||
public async start?(expressApp: Express): Promise<void>;
|
public async start?(expressApp: Express): Promise<void>;
|
||||||
|
|
||||||
public async init?(router: Router): Promise<void>;
|
public async initRoutes?(router: Router): Promise<void>;
|
||||||
|
|
||||||
public async handle?(router: Router): Promise<void>;
|
public async handleRoutes?(router: Router): Promise<void>;
|
||||||
|
|
||||||
public async stop?(): Promise<void>;
|
public async stop?(): Promise<void>;
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ export default class TestApp extends Application {
|
|||||||
this.use(new AuthComponent(this, new MagicLinkAuthMethod(this, MAGIC_LINK_MAIL), new PasswordAuthMethod(this)));
|
this.use(new AuthComponent(this, new MagicLinkAuthMethod(this, MAGIC_LINK_MAIL), new PasswordAuthMethod(this)));
|
||||||
|
|
||||||
// WebSocket server
|
// WebSocket server
|
||||||
this.use(new WebSocketServerComponent(this, this.as(ExpressAppComponent), this.as(RedisComponent)));
|
this.use(new WebSocketServerComponent());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected registerWebSocketListeners(): void {
|
protected registerWebSocketListeners(): void {
|
||||||
|
@ -18,7 +18,7 @@ export default class AuthComponent extends ApplicationComponent {
|
|||||||
this.authGuard = new AuthGuard(app, ...authMethods);
|
this.authGuard = new AuthGuard(app, ...authMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(): Promise<void> {
|
public async initRoutes(): Promise<void> {
|
||||||
this.use(AuthMiddleware);
|
this.use(AuthMiddleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ export default class AutoUpdateComponent extends ApplicationComponent {
|
|||||||
this.checkSecurityConfigField('gitlab_webhook_token');
|
this.checkSecurityConfigField('gitlab_webhook_token');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(router: Router): Promise<void> {
|
public async initRoutes(router: Router): Promise<void> {
|
||||||
router.post('/update/push.json', (req, res) => {
|
router.post('/update/push.json', (req, res) => {
|
||||||
const token = req.header('X-Gitlab-Token');
|
const token = req.header('X-Gitlab-Token');
|
||||||
if (!token || token !== config.get<string>('gitlab_webhook_token'))
|
if (!token || token !== config.get<string>('gitlab_webhook_token'))
|
||||||
|
@ -20,7 +20,7 @@ export default class CsrfProtectionComponent extends ApplicationComponent {
|
|||||||
this.excluders.push(excluder);
|
this.excluders.push(excluder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(router: Router): Promise<void> {
|
public async handleRoutes(router: Router): Promise<void> {
|
||||||
router.use(async (req, res, next) => {
|
router.use(async (req, res, next) => {
|
||||||
for (const excluder of CsrfProtectionComponent.excluders) {
|
for (const excluder of CsrfProtectionComponent.excluders) {
|
||||||
if (excluder(req)) return next();
|
if (excluder(req)) return next();
|
||||||
|
@ -30,7 +30,7 @@ export default class ExpressAppComponent extends ApplicationComponent {
|
|||||||
this.expressApp = app;
|
this.expressApp = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(router: Router): Promise<void> {
|
public async initRoutes(router: Router): Promise<void> {
|
||||||
router.use(preventContextCorruptionMiddleware(express.json({
|
router.use(preventContextCorruptionMiddleware(express.json({
|
||||||
type: req => req.headers['content-type']?.match(/^application\/(.+\+)?json$/),
|
type: req => req.headers['content-type']?.match(/^application\/(.+\+)?json$/),
|
||||||
})));
|
})));
|
||||||
|
@ -3,7 +3,7 @@ import {Router} from "express";
|
|||||||
import ApplicationComponent from "../ApplicationComponent.js";
|
import ApplicationComponent from "../ApplicationComponent.js";
|
||||||
|
|
||||||
export default class FormHelperComponent extends ApplicationComponent {
|
export default class FormHelperComponent extends ApplicationComponent {
|
||||||
public async init(router: Router): Promise<void> {
|
public async initRoutes(router: Router): Promise<void> {
|
||||||
router.use((req, res, next) => {
|
router.use((req, res, next) => {
|
||||||
let _validation: unknown | null;
|
let _validation: unknown | null;
|
||||||
res.locals.validation = () => {
|
res.locals.validation = () => {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import config from "config";
|
import config from "config";
|
||||||
import {Express, Router} from "express";
|
import {Express, Router} from "express";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import * as querystring from "querystring";
|
|
||||||
import {ParsedUrlQueryInput} from "querystring";
|
import {ParsedUrlQueryInput} from "querystring";
|
||||||
import util from "util";
|
import util from "util";
|
||||||
|
|
||||||
@ -9,6 +8,7 @@ import ApplicationComponent from "../ApplicationComponent.js";
|
|||||||
import Controller, {RouteParams} from "../Controller.js";
|
import Controller, {RouteParams} from "../Controller.js";
|
||||||
import AssetCompiler from "../frontend/AssetCompiler.js";
|
import AssetCompiler from "../frontend/AssetCompiler.js";
|
||||||
import AssetPreCompiler from "../frontend/AssetPreCompiler.js";
|
import AssetPreCompiler from "../frontend/AssetPreCompiler.js";
|
||||||
|
import Globals from "../frontend/Globals.js";
|
||||||
import ViewEngine from "../frontend/ViewEngine.js";
|
import ViewEngine from "../frontend/ViewEngine.js";
|
||||||
import {logger} from "../Logger.js";
|
import {logger} from "../Logger.js";
|
||||||
import {listFilesRecursively} from "../Utils.js";
|
import {listFilesRecursively} from "../Utils.js";
|
||||||
@ -18,6 +18,7 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
|||||||
private readonly publicDir: string;
|
private readonly publicDir: string;
|
||||||
private readonly publicAssetsCache: FileCache = new FileCache();
|
private readonly publicAssetsCache: FileCache = new FileCache();
|
||||||
private readonly assetPreCompilers: AssetPreCompiler[];
|
private readonly assetPreCompilers: AssetPreCompiler[];
|
||||||
|
private readonly globals: Globals = new Globals();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly assetCompiler: AssetCompiler,
|
private readonly assetCompiler: AssetCompiler,
|
||||||
@ -31,9 +32,30 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
|||||||
if (assetPreCompiler.isPublic()) {
|
if (assetPreCompiler.isPublic()) {
|
||||||
this.assetCompiler.addExtension(assetPreCompiler.getExtension());
|
this.assetCompiler.addExtension(assetPreCompiler.getExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assetPreCompiler.setGlobals(this.globals);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async init(): Promise<void> {
|
||||||
|
this.globals.set('route', (
|
||||||
|
route: string,
|
||||||
|
params: RouteParams = [],
|
||||||
|
query: ParsedUrlQueryInput = {},
|
||||||
|
absolute: boolean = false,
|
||||||
|
) => Controller.route(route, params, query, absolute));
|
||||||
|
this.globals.set('app_version', this.getApp().getVersion());
|
||||||
|
this.globals.set('core_version', this.getApp().getCoreVersion());
|
||||||
|
this.globals.set('app', config.get('app'));
|
||||||
|
this.globals.set('dump', (val: unknown) => {
|
||||||
|
return util.inspect(val);
|
||||||
|
});
|
||||||
|
this.globals.set('hex', (v: number) => {
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async start(app: Express): Promise<void> {
|
public async start(app: Express): Promise<void> {
|
||||||
// Cache public assets
|
// Cache public assets
|
||||||
if (config.get<boolean>('asset_cache')) {
|
if (config.get<boolean>('asset_cache')) {
|
||||||
@ -55,9 +77,6 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
|||||||
main = false;
|
main = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add util globals
|
|
||||||
this.setupGlobals();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async stop(): Promise<void> {
|
public async stop(): Promise<void> {
|
||||||
@ -66,7 +85,7 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(router: Router): Promise<void> {
|
public async handleRoutes(router: Router): Promise<void> {
|
||||||
router.use((req, res, next) => {
|
router.use((req, res, next) => {
|
||||||
res.locals.inlineAsset = (urlPath: string) => {
|
res.locals.inlineAsset = (urlPath: string) => {
|
||||||
return this.publicAssetsCache.getOrFail(path.join(this.publicDir, urlPath));
|
return this.publicAssetsCache.getOrFail(path.join(this.publicDir, urlPath));
|
||||||
@ -86,25 +105,6 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public setupGlobals(): void {
|
|
||||||
ViewEngine.setGlobal('route', (
|
|
||||||
route: string,
|
|
||||||
params: RouteParams = [],
|
|
||||||
query: ParsedUrlQueryInput = {},
|
|
||||||
absolute: boolean = false,
|
|
||||||
) => Controller.route(route, params, query, absolute));
|
|
||||||
ViewEngine.setGlobal('app_version', this.getApp().getVersion());
|
|
||||||
ViewEngine.setGlobal('core_version', this.getApp().getCoreVersion());
|
|
||||||
ViewEngine.setGlobal('querystring', querystring);
|
|
||||||
ViewEngine.setGlobal('app', config.get('app'));
|
|
||||||
ViewEngine.setGlobal('dump', (val: unknown) => {
|
|
||||||
return util.inspect(val);
|
|
||||||
});
|
|
||||||
ViewEngine.setGlobal('hex', (v: number) => {
|
|
||||||
return v.toString(16);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public hookPreCompilers(): void {
|
public hookPreCompilers(): void {
|
||||||
for (const assetPreCompiler of this.assetPreCompilers) {
|
for (const assetPreCompiler of this.assetPreCompilers) {
|
||||||
assetPreCompiler.onPreCompile(async watch => {
|
assetPreCompiler.onPreCompile(async watch => {
|
||||||
@ -127,4 +127,8 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
|||||||
this.hookPreCompilers();
|
this.hookPreCompilers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getGlobals(): Globals {
|
||||||
|
return this.globals;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ export default class LogRequestsComponent extends ApplicationComponent {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(router: Router): Promise<void> {
|
public async initRoutes(router: Router): Promise<void> {
|
||||||
router.use((req, res, next) => {
|
router.use((req, res, next) => {
|
||||||
onFinished(res, (err) => {
|
onFinished(res, (err) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
|
@ -11,6 +11,7 @@ import {logger} from "../Logger.js";
|
|||||||
import Mail from "../mail/Mail.js";
|
import Mail from "../mail/Mail.js";
|
||||||
import MailError from "../mail/MailError.js";
|
import MailError from "../mail/MailError.js";
|
||||||
import SecurityError from "../SecurityError.js";
|
import SecurityError from "../SecurityError.js";
|
||||||
|
import FrontendToolsComponent from "./FrontendToolsComponent.js";
|
||||||
|
|
||||||
export default class MailComponent extends ApplicationComponent {
|
export default class MailComponent extends ApplicationComponent {
|
||||||
private transporter?: Transporter;
|
private transporter?: Transporter;
|
||||||
@ -91,7 +92,7 @@ export default class MailComponent extends ApplicationComponent {
|
|||||||
locals.mail_to = options.to;
|
locals.mail_to = options.to;
|
||||||
locals.mail_link = config.get<string>('public_url') +
|
locals.mail_link = config.get<string>('public_url') +
|
||||||
Controller.route('mail', [template.template], locals);
|
Controller.route('mail', [template.template], locals);
|
||||||
Object.assign(locals, ViewEngine.getGlobals());
|
Object.assign(locals, this.getApp().as(FrontendToolsComponent).getGlobals().get());
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
logger.debug(`Send mail from ${options.from.address} to ${options.to}`);
|
logger.debug(`Send mail from ${options.from.address} to ${options.to}`);
|
||||||
|
@ -15,7 +15,7 @@ export default class MaintenanceComponent extends ApplicationComponent {
|
|||||||
this.canServe = canServe;
|
this.canServe = canServe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(router: Router): Promise<void> {
|
public async handleRoutes(router: Router): Promise<void> {
|
||||||
router.use((req: Request, res: Response, next: NextFunction) => {
|
router.use((req: Request, res: Response, next: NextFunction) => {
|
||||||
if (res.headersSent) {
|
if (res.headersSent) {
|
||||||
return next();
|
return next();
|
||||||
|
@ -3,11 +3,18 @@ import onFinished from "on-finished";
|
|||||||
|
|
||||||
import ApplicationComponent from "../ApplicationComponent.js";
|
import ApplicationComponent from "../ApplicationComponent.js";
|
||||||
import {logger} from "../Logger.js";
|
import {logger} from "../Logger.js";
|
||||||
|
import FrontendToolsComponent from "./FrontendToolsComponent.js";
|
||||||
import SessionComponent from "./SessionComponent.js";
|
import SessionComponent from "./SessionComponent.js";
|
||||||
|
|
||||||
export default class PreviousUrlComponent extends ApplicationComponent {
|
export default class PreviousUrlComponent extends ApplicationComponent {
|
||||||
|
public async init(): Promise<void> {
|
||||||
|
const globals = this.getApp().asOptional(FrontendToolsComponent)?.getGlobals();
|
||||||
|
if (globals) {
|
||||||
|
globals.set('getPreviousUrl', () => null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async handle(router: Router): Promise<void> {
|
public async handleRoutes(router: Router): Promise<void> {
|
||||||
router.use((req, res, next) => {
|
router.use((req, res, next) => {
|
||||||
req.getPreviousUrl = () => {
|
req.getPreviousUrl = () => {
|
||||||
let url = req.header('referer');
|
let url = req.header('referer');
|
||||||
|
@ -15,7 +15,7 @@ export default class ServeStaticDirectoryComponent extends ApplicationComponent
|
|||||||
this.path = routePath;
|
this.path = routePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(router: Router): Promise<void> {
|
public async initRoutes(router: Router): Promise<void> {
|
||||||
const resolvedRoot = path.resolve(this.root);
|
const resolvedRoot = path.resolve(this.root);
|
||||||
|
|
||||||
if (this.path) {
|
if (this.path) {
|
||||||
|
@ -4,7 +4,9 @@ import {Router} from "express";
|
|||||||
import session from "express-session";
|
import session from "express-session";
|
||||||
|
|
||||||
import ApplicationComponent from "../ApplicationComponent.js";
|
import ApplicationComponent from "../ApplicationComponent.js";
|
||||||
|
import ViewEngine from "../frontend/ViewEngine.js";
|
||||||
import SecurityError from "../SecurityError.js";
|
import SecurityError from "../SecurityError.js";
|
||||||
|
import FrontendToolsComponent from "./FrontendToolsComponent.js";
|
||||||
import RedisComponent from "./RedisComponent.js";
|
import RedisComponent from "./RedisComponent.js";
|
||||||
|
|
||||||
export default class SessionComponent extends ApplicationComponent {
|
export default class SessionComponent extends ApplicationComponent {
|
||||||
@ -15,6 +17,13 @@ export default class SessionComponent extends ApplicationComponent {
|
|||||||
this.storeComponent = storeComponent;
|
this.storeComponent = storeComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async init(): Promise<void> {
|
||||||
|
const globals = this.getApp().asOptional(FrontendToolsComponent)?.getGlobals();
|
||||||
|
if (globals) {
|
||||||
|
globals.set('flash', () => '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async checkSecuritySettings(): Promise<void> {
|
public async checkSecuritySettings(): Promise<void> {
|
||||||
this.checkSecurityConfigField('session.secret');
|
this.checkSecurityConfigField('session.secret');
|
||||||
if (!config.get<boolean>('session.cookie.secure')) {
|
if (!config.get<boolean>('session.cookie.secure')) {
|
||||||
@ -22,7 +31,7 @@ export default class SessionComponent extends ApplicationComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(router: Router): Promise<void> {
|
public async initRoutes(router: Router): Promise<void> {
|
||||||
router.use(session({
|
router.use(session({
|
||||||
saveUninitialized: true,
|
saveUninitialized: true,
|
||||||
secret: config.get('session.secret'),
|
secret: config.get('session.secret'),
|
||||||
|
@ -7,29 +7,33 @@ import WebSocket from "ws";
|
|||||||
|
|
||||||
import Application from "../Application.js";
|
import Application from "../Application.js";
|
||||||
import ApplicationComponent from "../ApplicationComponent.js";
|
import ApplicationComponent from "../ApplicationComponent.js";
|
||||||
import ViewEngine from "../frontend/ViewEngine.js";
|
|
||||||
import {logger} from "../Logger.js";
|
import {logger} from "../Logger.js";
|
||||||
import WebSocketListener from "../WebSocketListener.js";
|
import WebSocketListener from "../WebSocketListener.js";
|
||||||
import ExpressAppComponent from "./ExpressAppComponent.js";
|
import ExpressAppComponent from "./ExpressAppComponent.js";
|
||||||
|
import FrontendToolsComponent from "./FrontendToolsComponent.js";
|
||||||
import RedisComponent from "./RedisComponent.js";
|
import RedisComponent from "./RedisComponent.js";
|
||||||
|
|
||||||
export default class WebSocketServerComponent extends ApplicationComponent {
|
export default class WebSocketServerComponent extends ApplicationComponent {
|
||||||
private wss?: WebSocket.Server;
|
private wss?: WebSocket.Server;
|
||||||
|
|
||||||
public constructor(
|
public async init(): Promise<void> {
|
||||||
private readonly application: Application,
|
const app = this.getApp();
|
||||||
private readonly expressAppComponent: ExpressAppComponent,
|
|
||||||
private readonly storeComponent: RedisComponent,
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
ViewEngine.setGlobal('websocketUrl', config.get('public_websocket_url'));
|
app.require(ExpressAppComponent);
|
||||||
|
app.require(RedisComponent);
|
||||||
|
|
||||||
|
const globals = app.asOptional(FrontendToolsComponent)?.getGlobals();
|
||||||
|
if (globals) {
|
||||||
|
globals.set('websocketUrl', config.get('public_websocket_url'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async start(_app: Express): Promise<void> {
|
public async start(_app: Express): Promise<void> {
|
||||||
const listeners: { [p: string]: WebSocketListener<Application> } = this.application.getWebSocketListeners();
|
const app = this.getApp();
|
||||||
|
|
||||||
|
const listeners: { [p: string]: WebSocketListener<Application> } = app.getWebSocketListeners();
|
||||||
this.wss = new WebSocket.Server({
|
this.wss = new WebSocket.Server({
|
||||||
server: this.expressAppComponent.getServer(),
|
server: app.as(ExpressAppComponent).getServer(),
|
||||||
}, () => {
|
}, () => {
|
||||||
logger.info(`Websocket server started over webserver.`);
|
logger.info(`Websocket server started over webserver.`);
|
||||||
}).on('error', (err) => {
|
}).on('error', (err) => {
|
||||||
@ -57,7 +61,7 @@ export default class WebSocketServerComponent extends ApplicationComponent {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = this.storeComponent.getStore();
|
const store = app.as(RedisComponent).getStore();
|
||||||
store.get(sid, (err, session) => {
|
store.get(sid, (err, session) => {
|
||||||
if (err || !session) {
|
if (err || !session) {
|
||||||
logger.error(err, 'Error while initializing session in websocket.');
|
logger.error(err, 'Error while initializing session in websocket.');
|
||||||
|
@ -4,9 +4,11 @@ import path from "path";
|
|||||||
|
|
||||||
import {logger} from "../Logger.js";
|
import {logger} from "../Logger.js";
|
||||||
import {doesFileExist, listFilesRecursively} from "../Utils.js";
|
import {doesFileExist, listFilesRecursively} from "../Utils.js";
|
||||||
|
import Globals from "./Globals.js";
|
||||||
|
|
||||||
export default abstract class AssetPreCompiler {
|
export default abstract class AssetPreCompiler {
|
||||||
protected readonly assetPaths: string[];
|
protected readonly assetPaths: string[];
|
||||||
|
private globals?: Globals;
|
||||||
private watcher?: FSWatcher;
|
private watcher?: FSWatcher;
|
||||||
private afterPreCompileHandlers: ((watch: boolean) => Promise<void>)[] = [];
|
private afterPreCompileHandlers: ((watch: boolean) => Promise<void>)[] = [];
|
||||||
private inputChangeHandler?: (restart: boolean) => Promise<void>;
|
private inputChangeHandler?: (restart: boolean) => Promise<void>;
|
||||||
@ -51,6 +53,19 @@ export default abstract class AssetPreCompiler {
|
|||||||
return this.outputToPublicDir;
|
return this.outputToPublicDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getViewPaths(): string[] {
|
||||||
|
return this.assetPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getGlobals(): Globals {
|
||||||
|
if (!this.globals) throw new Error('globals field not intialized.');
|
||||||
|
return this.globals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setGlobals(globals: Globals): void {
|
||||||
|
this.globals = globals;
|
||||||
|
}
|
||||||
|
|
||||||
public async stop(): Promise<void> {
|
public async stop(): Promise<void> {
|
||||||
if (this.watcher) {
|
if (this.watcher) {
|
||||||
await this.watcher.close();
|
await this.watcher.close();
|
||||||
@ -58,10 +73,6 @@ export default abstract class AssetPreCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getViewPaths(): string[] {
|
|
||||||
return this.assetPaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract preCompile(canonicalName: string, alsoCompileDependents: boolean): Promise<void>;
|
public abstract preCompile(canonicalName: string, alsoCompileDependents: boolean): Promise<void>;
|
||||||
|
|
||||||
public onPreCompile(afterPreCompileHandler: (watch: boolean) => Promise<void>): void {
|
public onPreCompile(afterPreCompileHandler: (watch: boolean) => Promise<void>): void {
|
||||||
|
11
src/frontend/Globals.ts
Normal file
11
src/frontend/Globals.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export default class Globals {
|
||||||
|
private readonly globals: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
public get(): Record<string, unknown> {
|
||||||
|
return {...this.globals};
|
||||||
|
}
|
||||||
|
|
||||||
|
public set(key: string, value: unknown): void {
|
||||||
|
this.globals[key] = value;
|
||||||
|
}
|
||||||
|
}
|
@ -278,7 +278,7 @@ export default class SvelteViewEngine extends ViewEngine {
|
|||||||
// Load locals into locals store
|
// Load locals into locals store
|
||||||
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 = this.getGlobals().get();
|
||||||
const localMap = this.compileBackendCalls(backendCalls, locals, true);
|
const localMap = this.compileBackendCalls(backendCalls, locals, true);
|
||||||
localsModule.locals.set((key: string, args: string) => {
|
localsModule.locals.set((key: string, args: string) => {
|
||||||
return localMap[args ?
|
return localMap[args ?
|
||||||
|
@ -3,17 +3,6 @@ import {Express} from "express";
|
|||||||
import AssetPreCompiler from "./AssetPreCompiler.js";
|
import AssetPreCompiler from "./AssetPreCompiler.js";
|
||||||
|
|
||||||
export default abstract class ViewEngine extends AssetPreCompiler {
|
export default abstract class ViewEngine extends AssetPreCompiler {
|
||||||
private static readonly globals: Record<string, unknown> = {};
|
|
||||||
|
|
||||||
public static getGlobals(): Record<string, unknown> {
|
|
||||||
return {...this.globals};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static setGlobal(key: string, value: unknown): void {
|
|
||||||
this.globals[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
targetDir: string,
|
targetDir: string,
|
||||||
assetType: string,
|
assetType: string,
|
||||||
@ -32,7 +21,7 @@ export default abstract class ViewEngine extends AssetPreCompiler {
|
|||||||
public setup(app: Express, main: boolean): void {
|
public setup(app: Express, main: boolean): void {
|
||||||
app.engine(this.extension, (path, options, callback) => {
|
app.engine(this.extension, (path, options, callback) => {
|
||||||
// Props (locals)
|
// Props (locals)
|
||||||
const locals = Object.assign(options, ViewEngine.getGlobals());
|
const locals = Object.assign(options, this.getGlobals().get());
|
||||||
|
|
||||||
this.render(path, locals)
|
this.render(path, locals)
|
||||||
.then(value => callback(null, value))
|
.then(value => callback(null, value))
|
||||||
|
Loading…
Reference in New Issue
Block a user