build.js 7.7 KB
Newer Older
fxy060608's avatar
init v3  
fxy060608 已提交
1
const path = require('path')
2
const fs = require('fs')
fxy060608's avatar
init v3  
fxy060608 已提交
3

fxy060608's avatar
fxy060608 已提交
4
const {
fxy060608's avatar
fxy060608 已提交
5
  runByHBuilderX,
d-u-a's avatar
d-u-a 已提交
6
  isInHBuilderX
fxy060608's avatar
fxy060608 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
} = require('@dcloudio/uni-cli-shared')

const defaults = {
  clean: true,
  target: 'app',
  'unsafe-inline': true
}

const modifyConfig = (config, fn) => {
  if (Array.isArray(config)) {
    config.forEach(c => fn(c))
  } else {
    fn(config)
  }
}

module.exports = (api, options) => {
  api.registerCommand('uni-build', {
    description: 'build for production',
    usage: 'vue-cli-service uni-build [options]',
    options: {
fxy060608's avatar
fxy060608 已提交
28
      '--watch': 'watch for changes',
fxy060608's avatar
fxy060608 已提交
29 30
      '--minimize': 'Tell webpack to minimize the bundle using the TerserPlugin.',
      '--auto-host': 'specify automator host',
fxy060608's avatar
fxy060608 已提交
31
      '--auto-port': 'specify automator port',
32 33
      '--subpackage': 'specify subpackage',
      '--plugin': 'specify plugin'
fxy060608's avatar
fxy060608 已提交
34 35 36 37 38 39 40 41
    }
  }, async (args) => {
    for (const key in defaults) {
      if (args[key] == null) {
        args[key] = defaults[key]
      }
    }

fxy060608's avatar
fxy060608 已提交
42 43
    const platforms = ['mp-weixin', 'mp-qq', 'mp-baidu', 'mp-alipay', 'mp-toutiao']
    if (args.subpackage && platforms.includes(process.env.UNI_PLATFORM)) {
fxy060608's avatar
fxy060608 已提交
44 45 46
      process.env.UNI_SUBPACKGE = args.subpackage
    }

47 48 49 50 51 52 53 54
    if (args.plugin) {
      if (process.env.UNI_PLATFORM === 'mp-weixin') {
        process.env.UNI_MP_PLUGIN = args.plugin
        analysisPluginDir()
      } else {
        console.error('编译到小程序插件只支持微信小程序')
        process.exit(0)
      }
55 56
    }

fxy060608's avatar
fxy060608 已提交
57
    require('./util').initAutomator(args)
fxy060608's avatar
fxy060608 已提交
58

fxy060608's avatar
fxy060608 已提交
59 60 61 62 63 64 65 66 67 68
    args.entry = args.entry || args._[0]

    process.env.VUE_CLI_BUILD_TARGET = args.target

    await build(args, api, options)

    delete process.env.VUE_CLI_BUILD_TARGET
  })
}

fxy060608's avatar
init v3  
fxy060608 已提交
69
function getWebpackConfig (api, args, options) {
fxy060608's avatar
fxy060608 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
  const validateWebpackConfig = require('@vue/cli-service/lib/util/validateWebpackConfig')
  // resolve raw webpack config
  const webpackConfig = require('@vue/cli-service/lib/commands/build/resolveAppConfig')(api, args, options)

  // check for common config errors
  validateWebpackConfig(webpackConfig, api, options, args.target)

  if (args.watch) {
    modifyConfig(webpackConfig, config => {
      config.watch = true
    })
  }

  if (args.minimize && process.env.NODE_ENV !== 'production') {
    modifyConfig(webpackConfig, config => {
      config.optimization.minimize = true
      config.optimization.namedModules = false
    })
  } else {
    modifyConfig(webpackConfig, config => {
fxy060608's avatar
fxy060608 已提交
90 91 92
      if (!config.optimization) {
        config.optimization = {}
      }
fxy060608's avatar
fxy060608 已提交
93 94 95
      config.optimization.namedModules = false
    })
  }
fxy060608's avatar
init v3  
fxy060608 已提交
96 97 98 99 100 101 102 103 104 105
  return webpackConfig
}

function getWebpackConfigs (api, args, options) {
  if (!process.env.UNI_USING_V3) {
    return [getWebpackConfig(api, args, options)]
  }
  const pluginOptions = (options.pluginOptions || (options.pluginOptions = {}))
  pluginOptions['uni-app-plus'] = {
    service: true
fxy060608's avatar
fxy060608 已提交
106
  }
fxy060608's avatar
fxy060608 已提交
107
  options.publicPath = '/'
fxy060608's avatar
init v3  
fxy060608 已提交
108
  const serviceWebpackConfig = getWebpackConfig(api, args, options)
fxy060608's avatar
fxy060608 已提交
109 110
  delete pluginOptions['uni-app-plus'].service
  pluginOptions['uni-app-plus'].view = true
fxy060608's avatar
fxy060608 已提交
111
  options.publicPath = './'
fxy060608's avatar
init v3  
fxy060608 已提交
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
  const viewWebpackConfig = getWebpackConfig(api, args, options)
  return [serviceWebpackConfig, viewWebpackConfig]
}

async function build (args, api, options) {
  const fs = require('fs-extra')
  const chalk = require('chalk')
  const webpack = require('webpack')

  const {
    log,
    done,
    logWithSpinner,
    stopSpinner
  } = require('@vue/cli-shared-utils')

  const runByAliIde = process.env.BUILD_ENV === 'ali-ide'

  log()

  if (!runByHBuilderX && !runByAliIde) {
fxy060608's avatar
fxy060608 已提交
133
    logWithSpinner(`开始编译当前项目至 ${process.env.UNI_SUB_PLATFORM || process.env.UNI_PLATFORM} 平台...`)
fxy060608's avatar
init v3  
fxy060608 已提交
134 135 136 137 138
  }

  const targetDir = api.resolve(options.outputDir)

  const webpackConfigs = getWebpackConfigs(api, args, options)
fxy060608's avatar
fxy060608 已提交
139 140 141 142 143 144 145 146 147 148 149

  if (process.env.NODE_ENV === 'production') {
    try {
      fs.emptyDir(path.resolve(process.env.UNI_CLI_CONTEXT, 'node_modules/.cache'))
    } catch (e) {}
  }

  if (args.clean || process.env.UNI_PLATFORM === 'app-plus') {
    await fs.emptyDir(targetDir)
  }

fxy060608's avatar
fxy060608 已提交
150
  if (process.env.UNI_USING_NATIVE || process.env.UNI_USING_V3_NATIVE) {
fxy060608's avatar
fxy060608 已提交
151 152 153
    webpackConfigs.length = 0
  }

fxy060608's avatar
fxy060608 已提交
154 155 156 157 158
  if (
    process.env.UNI_USING_NATIVE ||
    process.env.UNI_USING_V3_NATIVE ||
    (process.UNI_NVUE_ENTRY && Object.keys(process.UNI_NVUE_ENTRY).length)
  ) {
fxy060608's avatar
fxy060608 已提交
159 160 161 162 163
    webpackConfigs.push(require('@dcloudio/vue-cli-plugin-hbuilderx/build/webpack.nvue.conf.js')(process.UNI_NVUE_ENTRY))
  }

  return new Promise((resolve, reject) => {
    webpack(webpackConfigs, (err, stats) => {
fxy060608's avatar
fxy060608 已提交
164
      if (!runByHBuilderX && !runByAliIde) {
fxy060608's avatar
fxy060608 已提交
165 166 167 168 169 170 171 172
        stopSpinner(false)
      }
      if (err) {
        return reject(err)
      }

      if (stats.hasErrors()) {
        /* eslint-disable prefer-promise-reject-errors */
fxy060608's avatar
fxy060608 已提交
173
        return reject('Build failed with errors.')
fxy060608's avatar
fxy060608 已提交
174 175
      }

fxy060608's avatar
fxy060608 已提交
176
      if (!args.silent && (process.env.UNI_PLATFORM !== 'app-plus' || process.env.UNI_AUTOMATOR_WS_ENDPOINT)) {
fxy060608's avatar
fxy060608 已提交
177 178 179 180 181 182 183 184
        const targetDirShort = path.relative(
          api.service.context,
          process.env.UNI_OUTPUT_DIR
        )

        if (!args.watch) {
          const dirMsg = runByHBuilderX ? ''
            : `The ${chalk.cyan(targetDirShort)} directory is ready to be deployed.`
fxy060608's avatar
fxy060608 已提交
185 186 187 188 189 190
          done(`Build complete. ${dirMsg}`)

          if (process.env.UNI_PLATFORM === 'h5' && !isInHBuilderX) {
            console.log()
            console.log('欢迎将H5站部署到uniCloud前端网页托管平台,高速、免费、安全、省心,详见:')
            console.log('https://uniapp.dcloud.io/uniCloud/hosting')
d-u-a's avatar
d-u-a 已提交
191
          }
fxy060608's avatar
fxy060608 已提交
192 193 194 195 196 197 198 199 200 201 202 203
        } else {
          const dirMsg = runByHBuilderX ? '' : `The ${chalk.cyan(targetDirShort)} directory is ready. `
          done(`Build complete. ${dirMsg}Watching for changes...`)
        }
      }

      resolve()
    })
  })
}

module.exports.defaultModes = {
fxy060608's avatar
fxy060608 已提交
204
  'uni-build': process.env.NODE_ENV
205
}
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250

/**
 * 编译到微信小程序插件 文件校验
 */
function analysisPluginDir () {
  // plugin.json 是否存在
  const pluginJsonName = 'plugin.json'
  const pluginJsonPath = path.resolve(process.env.UNI_INPUT_DIR, pluginJsonName)
  if (!fs.existsSync(pluginJsonPath)) {
    console.error(`${pluginJsonName}文件不存在,请检查后重试`)
    process.exit(0)
  }

  const pluginJson = require(pluginJsonPath)

  // index.js 入口文件是否存在
  process.env.UNI_MP_PLUGIN_MAIN = pluginJson.main
  const UNI_MP_PLUGIN_MAIN = process.env.UNI_MP_PLUGIN_MAIN
  const mainFilePath = path.resolve(process.env.UNI_INPUT_DIR, UNI_MP_PLUGIN_MAIN)
  if (UNI_MP_PLUGIN_MAIN && !fs.existsSync(mainFilePath)) {
    console.error(`${UNI_MP_PLUGIN_MAIN}入口文件不存在,请检查后重试`)
    process.exit(0)
  }

  // 目前编译到小程序插件,需要在 pages.json 中配置页面,在main.js中引入使用一下组件,因此先不做一下校验
  // 配置的路径是否存在
  /* const pages = pluginJson.pages || {}
  const publicComponents = pluginJson.publicComponents || {}
  const allFilesPath = Object.values(pages).map(item => item + '.vue').concat(Object.values(publicComponents).map(item => item + '.vue'))
  const inexistenceFiles = []
  if (allFilesPath.length) {
    allFilesPath.forEach(pagePath => {
      const curentPageAbsolutePath = path.resolve(process.env.UNI_INPUT_DIR, pagePath)
      if (!fs.existsSync(curentPageAbsolutePath)) {
        inexistenceFiles.push(curentPageAbsolutePath)
      }
    })
  }
  if (inexistenceFiles.length) {
    inexistenceFiles.forEach(path => {
      console.error(`${path}文件不存在,请检查后重试`)
    })
    process.exit(0)
  } */
}