mirror of
https://github.com/purocean/yn.git
synced 2026-09-24 15:46:02 +08:00
feat(macro): add $include to include other document fragments
This commit is contained in:
@@ -1,40 +1,44 @@
|
||||
import frontMatter from 'front-matter'
|
||||
import type { Plugin } from '@fe/context'
|
||||
import { render } from '@fe/services/view'
|
||||
import { readFile } from '@fe/support/api'
|
||||
import type { Doc } from '@fe/types'
|
||||
import { getLogger, md5 } from '@fe/utils'
|
||||
import { dirname, resolve } from '@fe/utils/path'
|
||||
|
||||
const logger = getLogger('macro')
|
||||
type Result = { __macroResult: true, vars?: Record<string, any>, toString: () => string }
|
||||
type Expression = { id: string, match: string, exp: string, vars: Record<string, any> }
|
||||
|
||||
const logger = getLogger('plugin-macro')
|
||||
|
||||
const AsyncFunction = Object.getPrototypeOf(async () => 0).constructor
|
||||
let macroCache: Record<string, Record<string, string>> = {}
|
||||
let macroCache: Record<string, Record<string, Result>> = {}
|
||||
|
||||
function checkMacroResult (result: any) {
|
||||
if (typeof result === 'object' ||
|
||||
if (
|
||||
result === null ||
|
||||
typeof result === 'function' ||
|
||||
typeof result === 'symbol' ||
|
||||
typeof result === 'undefined'
|
||||
typeof result === 'undefined' ||
|
||||
(typeof result === 'object' && (!result.__macroResult))
|
||||
) {
|
||||
throw new Error('Macro result type error.')
|
||||
}
|
||||
}
|
||||
|
||||
function macro (match: string, vars: Record<string, any>, cache: Record<string, string>) {
|
||||
const expression = match
|
||||
.substring(2, match.length - 2)
|
||||
.trim()
|
||||
.replace(/(?:=\\>|<\\=)/g, x => x.replace('\\', ''))
|
||||
function macro (expression: Expression, cache: Record<string, Result>): Result {
|
||||
logger.debug('macro', expression)
|
||||
|
||||
const id = md5(expression)
|
||||
|
||||
logger.debug(id, expression, vars)
|
||||
const { id, match, exp, vars } = expression
|
||||
|
||||
// async expression result cache
|
||||
if (id in cache) {
|
||||
return cache[id]
|
||||
}
|
||||
|
||||
const FunctionConstructor = expression.startsWith('await') ? AsyncFunction : Function
|
||||
const FunctionConstructor = exp.startsWith('await') ? AsyncFunction : Function
|
||||
|
||||
const fun = new FunctionConstructor('vars', `with (vars) { return ${expression}; }`)
|
||||
const fun = new FunctionConstructor('vars', `with (vars) { return ${exp}; }`)
|
||||
|
||||
const result = fun(vars)
|
||||
|
||||
@@ -43,20 +47,20 @@ function macro (match: string, vars: Record<string, any>, cache: Record<string,
|
||||
checkMacroResult(res)
|
||||
return res
|
||||
}).catch(e => {
|
||||
logger.error(id, e)
|
||||
logger.error('macro', id, e)
|
||||
return match
|
||||
}).then(res => {
|
||||
cache[id] = res
|
||||
logger.debug('async', id, 'rerender')
|
||||
logger.debug('macro', 'async', id, 'rerender')
|
||||
render()
|
||||
})
|
||||
|
||||
return 'macro is running……'
|
||||
return { __macroResult: true, toString: () => 'macro is running……' }
|
||||
}
|
||||
|
||||
checkMacroResult(result)
|
||||
|
||||
return '' + result
|
||||
return result
|
||||
}
|
||||
|
||||
function lineCount (str: string) {
|
||||
@@ -72,9 +76,36 @@ function lineCount (str: string) {
|
||||
return s
|
||||
}
|
||||
|
||||
async function include (belongDoc: Doc | undefined | null, path: string): Promise<Result> {
|
||||
if (!belongDoc) {
|
||||
throw new Error('Current document is null')
|
||||
}
|
||||
|
||||
try {
|
||||
const absolutePath = resolve(dirname(belongDoc.path), path)
|
||||
const { content } = await readFile({ repo: belongDoc.repo, path: absolutePath })
|
||||
const fm = frontMatter(content)
|
||||
|
||||
// merge front-matter attributes to current document vars.
|
||||
const vars = {}
|
||||
if (fm.attributes && typeof fm.attributes === 'object') {
|
||||
Object.assign(vars, fm.attributes)
|
||||
}
|
||||
|
||||
return { __macroResult: true, vars, toString: () => fm.body }
|
||||
} catch (error: any) {
|
||||
return error.message
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'markdown-macro',
|
||||
register: ctx => {
|
||||
// clear cache after view refresh
|
||||
ctx.registerHook('VIEW_BEFORE_REFRESH', () => {
|
||||
macroCache = {}
|
||||
})
|
||||
|
||||
ctx.markdown.registerPlugin(md => {
|
||||
md.core.ruler.after('normalize', 'macro', (state) => {
|
||||
const env = state.env || {}
|
||||
@@ -84,13 +115,14 @@ export default {
|
||||
}
|
||||
|
||||
const file = env.file || {}
|
||||
const vars = {
|
||||
const vars: Record<string, any> = {
|
||||
$include: include.bind(null, file),
|
||||
$ctx: ctx,
|
||||
$doc: {
|
||||
basename: file.name ? file.name.substring(0, file.name.lastIndexOf('.')) : '',
|
||||
...ctx.lib.lodash.pick(env.file, 'name', 'repo', 'path', 'content', 'status')
|
||||
},
|
||||
...env.attributes
|
||||
...env.attributes,
|
||||
}
|
||||
|
||||
if (!env.macroLines) {
|
||||
@@ -106,14 +138,31 @@ export default {
|
||||
let posOffset = 0
|
||||
state.src = state.src.replace(reg, (match, matchPos) => {
|
||||
try {
|
||||
const result = macro(match, vars, macroCache[cacheKey])
|
||||
console.log('xxx', match, { ...vars })
|
||||
|
||||
const exp = match
|
||||
.substring(2, match.length - 2)
|
||||
.trim()
|
||||
.replace(/(?:=\\>|<\\=)/g, x => x.replace('\\', ''))
|
||||
|
||||
const id = md5(exp)
|
||||
|
||||
const expression = { id, match, exp, vars }
|
||||
const result = macro(expression, macroCache[cacheKey])
|
||||
|
||||
if (result && result.__macroResult && result.vars) {
|
||||
Object.assign(vars, result.vars)
|
||||
}
|
||||
|
||||
// result maybe string or other type.
|
||||
const resultStr = '' + result
|
||||
|
||||
const matchLine = lineCount(match)
|
||||
const resultLine = lineCount(result)
|
||||
const resultLine = lineCount(resultStr)
|
||||
|
||||
if (resultLine !== matchLine) {
|
||||
const currentLineOffset = (matchLine - resultLine)
|
||||
const currentPosOffset = (match.length - result.length)
|
||||
const currentPosOffset = (match.length - resultStr.length)
|
||||
lineOffset += currentLineOffset
|
||||
posOffset += currentPosOffset
|
||||
env.macroLines.push({
|
||||
@@ -125,11 +174,11 @@ export default {
|
||||
currentPosOffset,
|
||||
currentLineOffset,
|
||||
matchLength: match.length,
|
||||
resultLength: result.length,
|
||||
resultLength: resultStr.length,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
return resultStr
|
||||
} catch (error) {}
|
||||
|
||||
return match
|
||||
|
||||
Reference in New Issue
Block a user