layui/docs/src/plugin/demo.ts
2021-09-27 06:09:33 +08:00

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>
`
},
}