index.ts 5.0 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1 2
import path from 'path'
import fs from 'fs-extra'
fxy060608's avatar
fxy060608 已提交
3

fxy060608's avatar
fxy060608 已提交
4
import type { Plugin } from 'vite'
fxy060608's avatar
fxy060608 已提交
5
import type { SFCBlock, SFCDescriptor, SFCParseResult } from '@vue/compiler-sfc'
fxy060608's avatar
fxy060608 已提交
6 7 8
import type { TransformPluginContext } from 'rollup'

import { normalizePath, parseVueRequest } from '@dcloudio/uni-cli-shared'
fxy060608's avatar
fxy060608 已提交
9

fxy060608's avatar
fxy060608 已提交
10 11 12 13 14 15
import {
  ResolvedOptions,
  createDescriptor,
  getDescriptor,
  getSrcDescriptor,
} from './descriptorCache'
fxy060608's avatar
fxy060608 已提交
16
import { createRollupError } from './error'
fxy060608's avatar
fxy060608 已提交
17
import { genClassName, isVue, parseImports } from '../utils'
fxy060608's avatar
fxy060608 已提交
18 19
import { genScript } from './code/script'
import { genTemplate } from './code/template'
fxy060608's avatar
fxy060608 已提交
20
import { genJsStylesCode, genStyle, transformStyle } from './code/style'
fxy060608's avatar
fxy060608 已提交
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

function resolveAppVue(inputDir: string) {
  const appUVue = path.resolve(inputDir, 'app.uvue')
  if (fs.existsSync(appUVue)) {
    return normalizePath(appUVue)
  }
  return normalizePath(path.resolve(inputDir, 'App.vue'))
}

export function uniAppUVuePlugin(): Plugin {
  const options: ResolvedOptions = {
    root: process.env.UNI_INPUT_DIR,
    sourceMap: false,
    // eslint-disable-next-line no-restricted-globals
    compiler: require('@vue/compiler-sfc'),
fxy060608's avatar
fxy060608 已提交
36
    targetLanguage: process.env.UNI_UVUE_TARGET_LANGUAGE,
fxy060608's avatar
fxy060608 已提交
37 38 39 40 41 42 43 44 45 46
  }

  const appVue = resolveAppVue(process.env.UNI_INPUT_DIR)
  function isAppVue(id: string) {
    return normalizePath(id) === appVue
  }

  return {
    name: 'uni:app-uvue',
    apply: 'build',
fxy060608's avatar
fxy060608 已提交
47 48 49 50
    async resolveId(id) {
      // serve sub-part requests (*?vue) as virtual modules
      if (parseVueRequest(id).query.vue) {
        return id
fxy060608's avatar
fxy060608 已提交
51
      }
fxy060608's avatar
fxy060608 已提交
52
    },
fxy060608's avatar
fxy060608 已提交
53

fxy060608's avatar
fxy060608 已提交
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
    load(id) {
      const { filename, query } = parseVueRequest(id)
      // select corresponding block for sub-part virtual modules
      if (query.vue) {
        if (query.src) {
          return fs.readFileSync(filename, 'utf-8')
        }
        const descriptor = getDescriptor(filename, options)!
        let block: SFCBlock | null | undefined
        if (query.type === 'style') {
          block = descriptor.styles[query.index!]
        } else if (query.index != null) {
          block = descriptor.customBlocks[query.index]
        }
        if (block) {
          return {
            code: block.content,
            map: block.map as any,
          }
        }
fxy060608's avatar
fxy060608 已提交
74
      }
fxy060608's avatar
fxy060608 已提交
75 76 77 78 79
    },
    async transform(code, id) {
      const { filename, query } = parseVueRequest(id)
      if (!isVue(filename)) {
        return
fxy060608's avatar
fxy060608 已提交
80
      }
fxy060608's avatar
fxy060608 已提交
81 82
      if (!query.vue) {
        // main request
fxy060608's avatar
fxy060608 已提交
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
        const { errors, uts, js } = await transformVue(
          code,
          filename,
          options,
          this,
          isAppVue
        )
        if (errors.length) {
          errors.forEach((error) =>
            this.error(createRollupError(filename, error))
          )
          return null
        }
        const fileName = path.relative(process.env.UNI_INPUT_DIR, filename)

        this.emitFile({
          type: 'asset',
          fileName,
          source: uts,
        })
        return {
          code: js,
        }
fxy060608's avatar
fxy060608 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
      } else {
        // sub block request
        const descriptor = query.src
          ? getSrcDescriptor(filename)!
          : getDescriptor(filename, options)!

        if (query.type === 'style') {
          return transformStyle(
            code,
            descriptor,
            Number(query.index),
            options,
            this,
            filename
          )
        }
fxy060608's avatar
fxy060608 已提交
122 123 124 125
      }
    },
  }
}
fxy060608's avatar
fxy060608 已提交
126 127 128 129 130

interface TransformVueResult {
  errors: SFCParseResult['errors']
  uts?: string
  js?: string
fxy060608's avatar
fxy060608 已提交
131
  descriptor: SFCDescriptor
fxy060608's avatar
fxy060608 已提交
132 133 134 135 136 137 138 139 140
}

export async function transformVue(
  code: string,
  filename: string,
  options: ResolvedOptions,
  pluginContext: TransformPluginContext | undefined,
  isAppVue: (id: string) => boolean = () => false
): Promise<TransformVueResult> {
fxy060608's avatar
fxy060608 已提交
141 142 143
  if (!options.compiler) {
    options.compiler = require('@vue/compiler-sfc')
  }
fxy060608's avatar
fxy060608 已提交
144 145 146 147
  // prev descriptor is only set and used for hmr
  const { descriptor, errors } = createDescriptor(filename, code, options)

  if (errors.length) {
fxy060608's avatar
fxy060608 已提交
148
    return { errors, descriptor }
fxy060608's avatar
fxy060608 已提交
149 150
  }
  const isApp = isAppVue(filename)
fxy060608's avatar
fxy060608 已提交
151 152
  const fileName = path.relative(options.root, filename)
  const className = genClassName(fileName, options.classNamePrefix)
fxy060608's avatar
fxy060608 已提交
153 154 155 156 157 158 159 160 161 162 163
  let templateCode = ''
  if (!isApp) {
    const templateResult = genTemplate(descriptor, {
      targetLanguage: options.targetLanguage as any,
      mode: 'function',
      filename: className,
      prefixIdentifiers: true,
      sourceMap: true,
    })
    templateCode = templateResult.code
  }
fxy060608's avatar
fxy060608 已提交
164
  // 生成 script 文件
fxy060608's avatar
fxy060608 已提交
165
  let utsCode =
fxy060608's avatar
fxy060608 已提交
166 167 168
    genScript(descriptor, { filename: className }) +
    '\n' +
    genStyle(descriptor, { filename: fileName, className }) +
fxy060608's avatar
fxy060608 已提交
169 170
    '\n'
  utsCode += templateCode
fxy060608's avatar
fxy060608 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183
  let jsCode = ''
  const content = descriptor.script?.content
  if (content) {
    jsCode += await parseImports(content)
  }
  if (descriptor.styles.length) {
    jsCode += '\n' + (await genJsStylesCode(descriptor, pluginContext!))
  }
  jsCode += `\nexport default "${className}"`
  return {
    errors: [],
    uts: utsCode,
    js: jsCode,
fxy060608's avatar
fxy060608 已提交
184
    descriptor,
fxy060608's avatar
fxy060608 已提交
185 186
  }
}