feat: add styling and typography method for macors
This commit is contained in:
@@ -6,8 +6,7 @@
|
||||
* @FilePath: /server/apps/server/src/auth/roles.guard.ts
|
||||
* Mark: Coding with Love
|
||||
*/
|
||||
import { CanActivate, ExecutionContext } from '@nestjs/common'
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'
|
||||
import { AuthGuard } from '@nestjs/passport'
|
||||
|
||||
import { isTest } from '~/global/env.global'
|
||||
|
||||
@@ -178,7 +178,7 @@ export class MarkdownController {
|
||||
const { id } = params
|
||||
const now = performance.now()
|
||||
const [
|
||||
{ html: markdown, document, type },
|
||||
{ html: markdownMacros, document, type },
|
||||
{
|
||||
url: { webUrl },
|
||||
},
|
||||
@@ -212,9 +212,7 @@ export class MarkdownController {
|
||||
})()
|
||||
|
||||
const url = new URL(relativePath!, webUrl)
|
||||
const markdownMacros = this.service.renderMarkdownContent(
|
||||
await this.macroService.replaceTextMacro(markdown, document),
|
||||
)
|
||||
|
||||
const structure = await this.service.getRenderedMarkdownHtmlStructure(
|
||||
markdownMacros,
|
||||
document.title,
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ReturnModelType } from '@typegoose/typegoose'
|
||||
|
||||
import { DatabaseService } from '~/processors/database/database.service'
|
||||
import { AssetService } from '~/processors/helper/helper.asset.service'
|
||||
import { TextMacroService } from '~/processors/helper/helper.macro.service'
|
||||
import { InjectModel } from '~/transformers/model.transformer'
|
||||
|
||||
import { CategoryModel } from '../category/category.model'
|
||||
@@ -39,6 +40,8 @@ export class MarkdownService {
|
||||
private readonly pageModel: ReturnModelType<typeof PageModel>,
|
||||
|
||||
private readonly databaseService: DatabaseService,
|
||||
|
||||
private readonly macroService: TextMacroService,
|
||||
) {}
|
||||
|
||||
async insertPostsToDb(data: DatatypeDto[]) {
|
||||
@@ -223,7 +226,12 @@ ${text.trim()}
|
||||
}
|
||||
|
||||
return {
|
||||
html: doc.document.text,
|
||||
html: this.renderMarkdownContent(
|
||||
await this.macroService.replaceTextMacro(
|
||||
doc.document.text,
|
||||
doc.document,
|
||||
),
|
||||
),
|
||||
...doc,
|
||||
document: doc.document,
|
||||
}
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
import dayjs from 'dayjs'
|
||||
import { FastifyRequest } from 'fastify'
|
||||
import { marked } from 'marked'
|
||||
|
||||
import { BadRequestException, Injectable, Logger } from '@nestjs/common'
|
||||
import {
|
||||
BadRequestException,
|
||||
Inject,
|
||||
Injectable,
|
||||
Logger,
|
||||
Scope,
|
||||
} from '@nestjs/common'
|
||||
import { REQUEST } from '@nestjs/core'
|
||||
|
||||
import { UserModel } from '~/modules/user/user.model'
|
||||
import { deepCloneWithFunction } from '~/utils'
|
||||
import { safeEval } from '~/utils/safe-eval.util'
|
||||
|
||||
@Injectable()
|
||||
export class TextMacroService {
|
||||
private readonly logger: Logger
|
||||
constructor() {
|
||||
this.logger = new Logger(TextMacroService.name)
|
||||
}
|
||||
static readonly Reg = {
|
||||
'#': /^#(.*?)$/g,
|
||||
$: /^\$(.*?)$/g,
|
||||
'?': /^\?\??(.*?)\??\?$/g,
|
||||
}
|
||||
const logger = new Logger('TextMacroService')
|
||||
const RegMap = {
|
||||
'#': /^#(.*?)$/g,
|
||||
$: /^\$(.*?)$/g,
|
||||
'?': /^\?\??(.*?)\??\?$/g,
|
||||
} as const
|
||||
|
||||
private ifConditionGrammar<T extends object>(text: string, model: T) {
|
||||
class HelperStatic {
|
||||
public ifConditionGrammar<T extends object>(text: string, model: T) {
|
||||
const conditionSplitter = text.split('|')
|
||||
conditionSplitter.forEach((item: string, index: string | number) => {
|
||||
conditionSplitter[index] = item.replace(/"/g, '')
|
||||
@@ -66,9 +71,42 @@ export class TextMacroService {
|
||||
return output
|
||||
}
|
||||
|
||||
private generateFunctionContext = (variables: object) => {
|
||||
return {
|
||||
// time utils
|
||||
dayjs: deepCloneWithFunction(dayjs),
|
||||
fromNow: (time: Date | string) => dayjs(time).fromNow(),
|
||||
|
||||
// typography
|
||||
center: (text: string) => {
|
||||
return `<p align="center">${text}</p>`
|
||||
},
|
||||
right: (text: string) => {
|
||||
return `<p align="right">${text}</p>`
|
||||
},
|
||||
|
||||
// styling
|
||||
opacity: (text: string, opacity = 0.8) => {
|
||||
return `<span style="opacity: ${opacity}">${text}</span>`
|
||||
},
|
||||
blur: (text: string, blur = 1) => {
|
||||
return `<span style="filter: blur(${blur}px)">${text}</span>`
|
||||
},
|
||||
color: (text: string, color = '') => {
|
||||
return `<span style="color: ${color}">${text}</span>`
|
||||
},
|
||||
size: (text: string, size = '1em') => {
|
||||
return `<span style="font-size: ${size}">${text}</span>`
|
||||
},
|
||||
|
||||
...variables,
|
||||
}
|
||||
}
|
||||
public async replaceTextMacro<T extends object>(
|
||||
text: string,
|
||||
model: T,
|
||||
|
||||
extraContext: Record<string, any> = {},
|
||||
): Promise<string> {
|
||||
try {
|
||||
const matchedReg = /\[\[\s(.*?)\s\]\]/g
|
||||
@@ -88,19 +126,19 @@ export class TextMacroService {
|
||||
}
|
||||
|
||||
condition = condition?.trim()
|
||||
if (condition.search(TextMacroService.Reg['?']) != -1) {
|
||||
return this.ifConditionGrammar(condition, model)
|
||||
if (condition.search(RegMap['?']) != -1) {
|
||||
return helper.ifConditionGrammar(condition, model)
|
||||
}
|
||||
if (condition.search(TextMacroService.Reg['$']) != -1) {
|
||||
if (condition.search(RegMap['$']) != -1) {
|
||||
const variable = condition
|
||||
.replace(TextMacroService.Reg['$'], '$1')
|
||||
.replace(RegMap['$'], '$1')
|
||||
.replace(/\s/g, '')
|
||||
return model[variable]
|
||||
return model[variable] ?? extraContext[variable]
|
||||
}
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
if (condition.search(TextMacroService.Reg['#']) != -1) {
|
||||
if (condition.search(RegMap['#']) != -1) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const functions = condition.replace(TextMacroService.Reg['#'], '$1')
|
||||
const functions = condition.replace(RegMap['#'], '$1')
|
||||
|
||||
const variables = Object.keys(model).reduce(
|
||||
(acc, key) => ({ [`$${key}`]: model[key], ...acc }),
|
||||
@@ -110,12 +148,7 @@ export class TextMacroService {
|
||||
try {
|
||||
return safeEval(
|
||||
`return ${functions}`,
|
||||
{
|
||||
dayjs: deepCloneWithFunction(dayjs),
|
||||
fromNow: (time: Date | string) => dayjs(time).fromNow(),
|
||||
|
||||
...variables,
|
||||
},
|
||||
this.generateFunctionContext({ ...variables, ...extraContext }),
|
||||
{ timeout: 1000 },
|
||||
)
|
||||
} catch {
|
||||
@@ -126,8 +159,29 @@ export class TextMacroService {
|
||||
}
|
||||
return text
|
||||
} catch (err) {
|
||||
this.logger.log(err.message)
|
||||
logger.log(err.message)
|
||||
return text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const helper = new HelperStatic()
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class TextMacroService {
|
||||
constructor(
|
||||
@Inject(REQUEST)
|
||||
private readonly request: FastifyRequest & {
|
||||
isMaster: boolean
|
||||
user: UserModel
|
||||
},
|
||||
) {}
|
||||
|
||||
public replaceTextMacro(text: string, model: object) {
|
||||
const isMaster = this.request.isMaster
|
||||
return helper.replaceTextMacro(text, model, {
|
||||
hideForGuest(text: string) {
|
||||
return isMaster ? text : ''
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user