From 2663931f2a5f31dc6f0bd46599ec65d5da42ab94 Mon Sep 17 00:00:00 2001 From: Nikita Melnikov Date: Sat, 8 Oct 2022 20:07:32 +0800 Subject: [PATCH] implement basic static renderer --- package.json | 1 + src/backend/build-static.ts | 94 +++++++++++++++++++++++++ src/backend/routes/middlewares/pages.ts | 49 +------------ src/backend/utils/menu.ts | 49 +++++++++++++ src/backend/views/pages/index.twig | 4 +- src/backend/views/pages/page.twig | 4 +- 6 files changed, 149 insertions(+), 52 deletions(-) create mode 100644 src/backend/build-static.ts create mode 100644 src/backend/utils/menu.ts diff --git a/package.json b/package.json index 26c2daf..407b466 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "scripts": { "start": "concurrently \"yarn start-backend\" \"yarn build-frontend\"", "dev": "concurrently \"yarn start-backend\" \"yarn build-frontend:dev\"", + "build-static": "ts-node src/backend/build-static.ts", "start-backend": "cross-env NODE_ENV=development npx nodemon --config nodemon.json src/backend/server.ts -c app-config.yaml -c app-config.local.yaml", "compile": "tsc && copyfiles -u 3 ./src/**/*.twig ./dist/backend/views && copyfiles -u 1 ./src/**/*.svg ./dist/", "build-frontend": "webpack --mode=production", diff --git a/src/backend/build-static.ts b/src/backend/build-static.ts new file mode 100644 index 0000000..1273cf2 --- /dev/null +++ b/src/backend/build-static.ts @@ -0,0 +1,94 @@ +import twig from 'twig'; +import Page from './models/page.js'; +import PagesFlatArray from './models/pagesFlatArray.js'; +import appConfig from './utils/appConfig.js'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import('./utils/twig.js'); +import fs from 'fs/promises'; +import mkdirp from 'mkdirp'; +import { createMenuTree } from './utils/menu.js'; +import { EntityId } from './database/types.js'; +import PagesOrder from './controllers/pagesOrder.js'; + +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const cwd = process.cwd(); +const distPath = path.resolve(cwd, 'dist'); +const pagesOrder = await PagesOrder.getAll(); +const allPages = await Page.getAll(); + +await mkdirp(distPath); + +/** + * Render template with twig by path + * + * @param filePath - path to template + * @param data - data to render template + */ +function renderTemplate(filePath: string, data: Record): Promise { + return new Promise((resolve, reject) => { + twig.renderFile(path.resolve(dirname, filePath), data, (err, html) => { + if (err) { + reject(err); + } + resolve(html); + }); + }); +} + +/** + * Renders single page + * + * @param page - page to render + */ +async function renderPage(page: Page): Promise { + console.log(`Rendering page ${page.uri}`); + const pageParent = await page.getParent(); + const pageId = page._id; + + if (!pageId) { + throw new Error('Page id is not defined'); + } + const parentIdOfRootPages = '0' as EntityId; + const previousPage = await PagesFlatArray.getPageBefore(pageId); + const nextPage = await PagesFlatArray.getPageAfter(pageId); + const menu = createMenuTree(parentIdOfRootPages, allPages, pagesOrder, 2); + const result = await renderTemplate('./views/pages/page.twig', { + page, + pageParent, + previousPage, + nextPage, + menu, + config: appConfig.frontend, + }); + + // console.log(result); + const filename = page.uri === '' ? 'index.html' : `${page.uri}.html`; + + await fs.writeFile(path.resolve(distPath, filename), result); + console.log(`Page ${page.uri} rendered`); +} + +/** + * Render all pages + */ +for (const page of allPages) { + await renderPage(page); +} + +/** + * Render index page + */ +async function renderIndexPage(): Promise { + console.log('Rendering index page'); + const result = await renderTemplate('./views/pages/index.twig', { + config: appConfig.frontend, + }); + + const filename = 'index.html'; + + await fs.writeFile(path.resolve(distPath, filename), result); + console.log('Index page rendered'); +} + +await renderIndexPage(); diff --git a/src/backend/routes/middlewares/pages.ts b/src/backend/routes/middlewares/pages.ts index 58917f8..8fafe4b 100644 --- a/src/backend/routes/middlewares/pages.ts +++ b/src/backend/routes/middlewares/pages.ts @@ -1,57 +1,10 @@ import { NextFunction, Request, Response } from 'express'; import Pages from '../../controllers/pages.js'; import PagesOrder from '../../controllers/pagesOrder.js'; -import Page from '../../models/page.js'; import asyncMiddleware from '../../utils/asyncMiddleware.js'; -import PageOrder from '../../models/pageOrder.js'; import { EntityId } from '../../database/types.js'; -import { isEqualIds } from '../../database/index.js'; +import { createMenuTree } from '../../utils/menu.js'; -/** - * Process one-level pages list to parent-children list - * - * @param {string} parentPageId - parent page id - * @param {Page[]} pages - list of all available pages - * @param {PagesOrder[]} pagesOrder - list of pages order - * @param {number} level - max level recursion - * @param {number} currentLevel - current level of element - * @returns {Page[]} - */ -function createMenuTree(parentPageId: EntityId, pages: Page[], pagesOrder: PageOrder[], level = 1, currentLevel = 1): Page[] { - const childrenOrder = pagesOrder.find(order => isEqualIds(order.data.page, parentPageId)); - - /** - * branch is a page children in tree - * if we got some children order on parents tree, then we push found pages in order sequence - * otherwise just find all pages includes parent tree - */ - let ordered: any[] = []; - - if (childrenOrder) { - ordered = childrenOrder.order.map((pageId: EntityId) => { - return pages.find(page => isEqualIds(page._id, pageId)); - }); - } - - const unordered = pages.filter(page => isEqualIds(page._parent, parentPageId)); - const branch = Array.from(new Set([...ordered, ...unordered])); - - /** - * stop recursion when we got the passed max level - */ - if (currentLevel === level + 1) { - return []; - } - - /** - * Each parents children can have subbranches - */ - return branch.filter(page => page && page._id).map(page => { - return Object.assign({ - children: createMenuTree(page._id, pages, pagesOrder, level, currentLevel + 1), - }, page.data); - }); -} /** * Middleware for all /page/... routes diff --git a/src/backend/utils/menu.ts b/src/backend/utils/menu.ts new file mode 100644 index 0000000..1b43417 --- /dev/null +++ b/src/backend/utils/menu.ts @@ -0,0 +1,49 @@ +import { EntityId } from '../database/types.js'; +import Page from '../models/page.js'; +import PageOrder from '../models/pageOrder.js'; +import { isEqualIds } from '../database/index.js'; + +/** + * Process one-level pages list to parent-children list + * + * @param parentPageId - parent page id + * @param pages - list of all available pages + * @param pagesOrder - list of pages order + * @param level - max level recursion + * @param currentLevel - current level of element + */ +export function createMenuTree(parentPageId: EntityId, pages: Page[], pagesOrder: PageOrder[], level = 1, currentLevel = 1): Page[] { + const childrenOrder = pagesOrder.find(order => isEqualIds(order.data.page, parentPageId)); + + /** + * branch is a page children in tree + * if we got some children order on parents tree, then we push found pages in order sequence + * otherwise just find all pages includes parent tree + */ + let ordered: any[] = []; + + if (childrenOrder) { + ordered = childrenOrder.order.map((pageId: EntityId) => { + return pages.find(page => isEqualIds(page._id, pageId)); + }); + } + + const unordered = pages.filter(page => isEqualIds(page._parent, parentPageId)); + const branch = Array.from(new Set([...ordered, ...unordered])); + + /** + * stop recursion when we got the passed max level + */ + if (currentLevel === level + 1) { + return []; + } + + /** + * Each parents children can have subbranches + */ + return branch.filter(page => page && page._id).map(page => { + return Object.assign({ + children: createMenuTree(page._id, pages, pagesOrder, level, currentLevel + 1), + }, page.data); + }); +} diff --git a/src/backend/views/pages/index.twig b/src/backend/views/pages/index.twig index ca975db..cf84415 100644 --- a/src/backend/views/pages/index.twig +++ b/src/backend/views/pages/index.twig @@ -12,13 +12,13 @@ - {% include "components/header.twig" %} + {% include "../components/header.twig" %}
{{ svg('frog') }}

It’s time to create the first page!

- {% include 'components/button.twig' with {label: 'Add page', icon: 'plus', size: 'small', url: '/page/new'} %} + {% include '../components/button.twig' with {label: 'Add page', icon: 'plus', size: 'small', url: '/page/new'} %}
{% if config.yandexMetrikaId is not empty %}