147 lines
4.4 KiB
TypeScript
147 lines
4.4 KiB
TypeScript
|
import markdown from 'markdown-it'
|
||
|
import highlight from './highlight'
|
||
|
import type Token from 'markdown-it/lib/token'
|
||
|
|
||
|
/**
|
||
|
* Combine the script content
|
||
|
* @param {string} script script string
|
||
|
*/
|
||
|
function assignScript(script: string) {
|
||
|
const dependencies = {} as Record<string, string[]>
|
||
|
const attrs = {} as Record<string, string>
|
||
|
const content = script
|
||
|
// import { ref } from 'vue' -> ''
|
||
|
.replace(/import\s?\{.*\}.*/g, (item) => {
|
||
|
const key = getInnerString(item.replace(/'/g, '"'), '"', '"')
|
||
|
const value = getInnerString(item.replace(/\s+/g, ''), '{', '}')
|
||
|
const list = value ? value.split(',') : []
|
||
|
if (key && dependencies[key]) {
|
||
|
dependencies[key] = dependencies[key].concat(list)
|
||
|
} else if (key) {
|
||
|
dependencies[key] = list
|
||
|
}
|
||
|
return ''
|
||
|
})
|
||
|
/**
|
||
|
* const -> let
|
||
|
*
|
||
|
* const a = -> let a =
|
||
|
* const a = -> a =
|
||
|
*/
|
||
|
.replace(/(const|let|var)\s\w*\s?=/g, (item) => {
|
||
|
const attr = getInnerString(item, '\\s', '\\s?=')
|
||
|
if (attr && !(attr in attrs)) {
|
||
|
attrs[attr] = attr
|
||
|
return `let ${attr} =`
|
||
|
} else {
|
||
|
return attr + ' ='
|
||
|
}
|
||
|
})
|
||
|
// Remove extra line breaks
|
||
|
.replace(/\n+/gm, '\n')
|
||
|
// Combine the import
|
||
|
const reImport = Object.keys(dependencies).reduce((all, item) => {
|
||
|
const filterAttrs = [...new Set(dependencies[item])]
|
||
|
return all + `import {${filterAttrs + ','}} from '${item}';\n`
|
||
|
}, '')
|
||
|
|
||
|
return reImport + content
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Extract part of the new string from the middle of the string
|
||
|
* @param {string} string string
|
||
|
* @param {string} prefix RegExp string
|
||
|
* @param {string} postfix RegExp string
|
||
|
* @param {string} type g | m | i
|
||
|
*/
|
||
|
function getInnerString(
|
||
|
string: string,
|
||
|
prefix: string,
|
||
|
postfix = '',
|
||
|
type: 'i' | 'g' | 'm' = 'i'
|
||
|
): string | undefined {
|
||
|
const result = new RegExp(`${prefix}(.*)${postfix}`, type)
|
||
|
const match = string.match(result)
|
||
|
return match ? match[1].trim() : undefined
|
||
|
}
|
||
|
|
||
|
let script = '' // Record the <script> label of the current page
|
||
|
|
||
|
export default {
|
||
|
render: (tokens: Token[], idx: number): string => {
|
||
|
// the `demo` block of the current page
|
||
|
const htmlBlock = tokens.filter((item) => item.type === 'html_block')
|
||
|
const { nesting, info = '', map } = tokens[idx]
|
||
|
|
||
|
if (nesting === -1) {
|
||
|
return '</lay-code>'
|
||
|
}
|
||
|
|
||
|
const matchedInfo = info.trim().match(/^demo\s+(.*)$/)
|
||
|
const description = matchedInfo && matchedInfo[1]
|
||
|
const descTemplate = markdown().render(description || '')
|
||
|
let str = '' // copy the current `demo` block code
|
||
|
let lastLine = NaN
|
||
|
|
||
|
for (let i = 0; i < htmlBlock.length; i++) {
|
||
|
const item = htmlBlock[i]
|
||
|
|
||
|
if (item.map && map && item.map[0] >= map[0] && item.map[1] <= map[1]) {
|
||
|
const { map, content } = item
|
||
|
const delta = map[0] - (lastLine || map[1])
|
||
|
|
||
|
if (delta > 0) {
|
||
|
str += '\n'.repeat(delta)
|
||
|
}
|
||
|
str += content
|
||
|
lastLine = map[1]
|
||
|
if (i === 0) {
|
||
|
script = ''
|
||
|
}
|
||
|
// Remove top <template>
|
||
|
if (/^<template>/.test(content)) {
|
||
|
const reContent = content.match(/^<template>((\s|\S)*)<\/template>/m)
|
||
|
|
||
|
htmlBlock[i].content = (reContent && reContent[1]) || ''
|
||
|
}
|
||
|
// Extract the <script> label content
|
||
|
if (content.includes('<script')) {
|
||
|
if (/export\sdefault\s?\{/m.test(content)) {
|
||
|
const setup = content.match(
|
||
|
/setup\s?\(\)\s?\{((\s|\S)*)return\s?\{/m
|
||
|
)
|
||
|
const reContent = content.replace(
|
||
|
/export\sdefault\s?\{((\s|\S)*)\}/m,
|
||
|
(setup && setup[1]) || ''
|
||
|
)
|
||
|
const reScript = reContent.match(
|
||
|
/^<script\s?.*?>((\s|\S)*)<\/script>/m
|
||
|
)
|
||
|
script += (reScript && reScript[1]) || ''
|
||
|
} else {
|
||
|
const reScript = content.match(
|
||
|
/^<script\s?.*?>((\s|\S)*)<\/script>/m
|
||
|
)
|
||
|
script += (reScript && reScript[1]) || ''
|
||
|
}
|
||
|
htmlBlock[i].content = ''
|
||
|
}
|
||
|
// Change the last content to <script> of the current page
|
||
|
if (i + 1 === htmlBlock.length) {
|
||
|
htmlBlock[i].content = `
|
||
|
<script setup>
|
||
|
${assignScript(script)}
|
||
|
</script>`
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return `
|
||
|
<lay-code>
|
||
|
${description ? `<template #description>${descTemplate}</template>` : ''}
|
||
|
<template #code>${highlight(str, 'vue')}</template>
|
||
|
`
|
||
|
},
|
||
|
}
|