tiny-vue/internals/vite-plugin-template2jsx/index.js

144 lines
4.4 KiB
JavaScript

import { createRequire } from 'node:module'
import { readFileSync, existsSync } from 'node:fs'
import { transformVueTemplateToSolid } from './src/plugin.js'
import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const require = createRequire(import.meta.url)
const indexTemplatePath = path.resolve(__dirname, './template/src/index.tss')
const pcTemplatePath = path.resolve(__dirname, './template/src/pc.jsxs')
// 多模板入口模板
const indexTemplate = readFileSync(indexTemplatePath, { encoding: 'utf-8' })
// PC模板入口
const pcTemplate = readFileSync(pcTemplatePath, { encoding: 'utf-8' })
const matchComponentRegx = /vue\/src\/([a-z]+)\/src/
const fundProps = (code, findStr) => {
let findIndex = code.indexOf(findStr)
let props = ''
if (~findIndex) {
const endStr = 'export '
let constantsPart = code.substring(findIndex + findStr.length, code.length)
props = constantsPart.substring(0, constantsPart.indexOf(endStr))
}
return props
}
const getSvgContent = (svg) => {
return svg.substring(svg.indexOf('>') + 1, svg.length)
}
const getDefaultValueFromEntry = (id) => {
const componentMatchs = id.match(matchComponentRegx)
const entryPath = id.replace('pc.vue', 'index.ts')
if (existsSync(entryPath)) {
if (componentMatchs?.[1]) {
const code = readFileSync(entryPath, { encoding: 'utf-8' })
let constants = fundProps(code, '$constants =') || '{}'
let propsContent = fundProps(code, 'Props =') || fundProps(code, 'props :')
let props = {}
if (propsContent) {
const propsContentParts = propsContent.split(/\n/)
propsContentParts.forEach((item, index) => {
if (item.indexOf('type:') && ['Boolean', 'String', 'Number'].some((type) => item.includes(type))) {
const name = propsContentParts[index - 1].replace(/\W/g, '')
let value = propsContentParts[index + 1]
if (name && value.includes('default:')) {
const type = /['"]/.test(value) ? 'Char' : 'Other'
value = value.replace('default:', '').replace(/["|'|\s,]/g, '')
props[name] = { type, value }
}
}
})
}
return {
$constants: constants,
$props: props
}
}
}
return {
$constants: '{}',
$props: {}
}
}
export default function vueTemplate2Jsx() {
return {
name: '@opentiny/vue-vite-template2jsx',
enforce: 'pre',
transform(code, id) {
if (id.includes('vue-icon/src')) {
const svgName = code.match(/([a-z1-9-]+)\.svg/)
if (svgName && svgName[0]) {
let svgPath
try {
svgPath = require.resolve(`@opentiny/vue-theme/svgs/${svgName[0]}`)
} catch (e) {
svgPath = path.resolve(__dirname, `../../packages/theme/src/svgs/${svgName[0]}`)
}
if (existsSync(svgPath)) {
let svgContent = readFileSync(svgPath, { encoding: 'utf-8' })
if (svgContent.includes('<!--')) {
svgContent = svgContent.replace(/<!--[\s\S]+-->/, '')
}
if (svgContent.includes('<?xml')) {
svgContent = getSvgContent(svgContent)
}
if (svgContent.includes('<!DOCTYPE')) {
svgContent = getSvgContent(svgContent)
}
if (svgContent.includes('<style')) {
svgContent = svgContent.replace(/<style[\s\S]+<\/style>/, '')
}
const viewBox = svgContent.match(/viewBox="[\d|\s]+"/)?.[0] || `viewBox="0 0 24 24"`
return `export default function(props) {
return (<svg style={props.style} xmlns="http://www.w3.org/2000/svg" onClick={props.onClick} ${viewBox} xml:space="preserve" class={'tiny-svg svgs-icon ' + props.class}>
${getSvgContent(svgContent)}
)
}`
}
}
return code
}
if (id.includes(`src/index.ts`) && !id.includes(`common/src/index.ts`)) {
return indexTemplate
}
if (id.includes('pc.vue')) {
const componentMatchs = id.match(matchComponentRegx)
if (componentMatchs?.[1]) {
const mapData = getDefaultValueFromEntry(id)
const pcComponent = transformVueTemplateToSolid(pcTemplate, code, componentMatchs[1], mapData)
return pcComponent
}
}
}
}
}