删除了不需要的目录,然后针对需要编译的文件调用了compileFile方法:
// varlet-cli/src/compiler/compileModule.ts
export async function compileFile(file: string) {
  isSFC(file) && (await compileSFC(file))// 编译vue文件
  isScript(file) && (await compileScriptFile(file))// 编译js文件
  isLess(file) && (await compileLess(file))// 编译less文件
  isDir(file) && (await compileDir(file))// 如果是目录则进行递归
}分别处理三种文件,让我们一一来看。
编译Vue单文件
// varlet-cli/src/compiler/compileSFC.ts
import { parse } from '@vue/compiler-sfc'
export async function compileSFC(sfc: string) {
    // 读取Vue单文件内容
    const sources: string = await readFile(sfc, 'utf-8')
    // 使用@vue/compiler-sfc包解析单文件
    const { descriptor } = parse(sources, { sourceMap: false })
    // 取出单文件的每部分内容
    const { script, scriptSetup, template, styles } = descriptor
    // Varlet暂时不支持setup语法
    if (scriptSetup) {
        logger.warning(
            `
 Varlet Cli does not support compiling script setup syntax
  The error in ${sfc}`
        )
        return
    }
    // ...
}使用@vue/compiler-sfc包来解析Vue单文件,parse方法可以解析出Vue单文件中的各个块,针对各个块,@vue/compiler-sfc包都提供了相应的编译方法,后续都会涉及到。
// varlet-cli/src/compiler/compileSFC.ts
import hash from 'hash-sum'
export async function compileSFC(sfc: string) {
    // ...
    // scoped
    // 检查是否存在scoped作用域的样式块
    const hasScope = styles.some((style) => style.scoped)
    // 将单文件的内容进行hash生成id
    const id = hash(sources)
    // 生成样式的scopeId
    const scopeId = hasScope ? `data-v-${id}` : ''
    // ...
}这一步主要是检查style块是否存在作用域块,存在的话会生成一个作用域id,作为css的作用域,防止和其他样式冲突,这两个id相关的编译方法需要用到。
// varlet-cli/src/compiler/compileSFC.ts
import { compileTemplate } from '@vue/compiler-sfc'
export async function compileSFC(sfc: string) {
    // ...
    if (script) {
        // template
        // 编译模板为渲染函数
        const render =
              template &&
              compileTemplate({
                  id,
                  source: template.content,
                  filename: sfc,
                  compilerOptions: {
                      scopeId,
                  },
              })
  // 注入render函数
        let { content } = script
        if (render) {
            const { code } = render
            content = injectRender(content, code)
        }
        // ...
    }
}使用@vue/compiler-sfc包的compileTemplate方法将解析出的模板部分编译为渲染函数,然后调用injectRender方法将渲染函数注入到script中:
// varlet-cli/src/compiler/compileSFC.ts
const NORMAL_EXPORT_START_RE = /exports+defaults+{/
const DEFINE_EXPORT_START_RE = /exports+defaults+defineComponents*(s*{/
export function injectRender(script: string, render: string): string {
  if (DEFINE_EXPORT_START_RE.test(script.trim())) {
    return script.trim().replace(
      DEFINE_EXPORT_START_RE,
      `${render}
export default defineComponent({
  render,
    `
    )
  }
  if (NORMAL_EXPORT_START_RE.test(script.trim())) {
    return script.trim().replace(
      NORMAL_EXPORT_START_RE,
      `${render}
export default {
  render,
    `
    )
  }
  return script
}兼容两种导出方式,以一个小例子来看一下,比如生成的渲染函数为:
export function render(_ctx, _cache) {
    // ...
}script的内容为:
export default defineComponent({
    name: 'VarButton',
    // ...
})注入render后script的内容变成了:
export function render(_ctx, _cache) {
    // ...
}
export default defineComponent({
    render,
    name: 'VarButton',
    /// ...
})其实就是把渲染函数的内容和script的内容合并了,script其实就是组件的选项对象,所以同时也把组件的渲染函数添加到组件对象上。
