123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- const defaults = {
- clean: true,
- target: 'app',
- formats: 'commonjs,umd,umd-min',
- 'unsafe-inline': true
- }
- const buildModes = {
- lib: 'library',
- wc: 'web component',
- 'wc-async': 'web component (async)'
- }
- const modifyConfig = (config, fn) => {
- if (Array.isArray(config)) {
- config.forEach(c => fn(c))
- } else {
- fn(config)
- }
- }
- module.exports = (api, options) => {
- api.registerCommand('build', {
- description: 'build for production',
- usage: 'vue-cli-service build [options] [entry|pattern]',
- options: {
- '--mode': `specify env mode (default: production)`,
- '--dest': `specify output directory (default: ${options.outputDir})`,
- '--modern': `build app targeting modern browsers with auto fallback`,
- '--no-unsafe-inline': `build app without introducing inline scripts`,
- '--target': `app | lib | wc | wc-async (default: ${defaults.target})`,
- '--formats': `list of output formats for library builds (default: ${defaults.formats})`,
- '--name': `name for lib or web-component mode (default: "name" in package.json or entry filename)`,
- '--filename': `file name for output, only usable for 'lib' target (default: value of --name)`,
- '--no-clean': `do not remove the dist directory before building the project`,
- '--report': `generate report.html to help analyze bundle content`,
- '--report-json': 'generate report.json to help analyze bundle content',
- '--watch': `watch for changes`
- }
- }, async (args, rawArgs) => {
- for (const key in defaults) {
- if (args[key] == null) {
- args[key] = defaults[key]
- }
- }
- args.entry = args.entry || args._[0]
- if (args.target !== 'app') {
- args.entry = args.entry || 'src/App.vue'
- }
- process.env.VUE_CLI_BUILD_TARGET = args.target
- if (args.modern && args.target === 'app') {
- process.env.VUE_CLI_MODERN_MODE = true
- if (!process.env.VUE_CLI_MODERN_BUILD) {
- // main-process for legacy build
- await build(Object.assign({}, args, {
- modernBuild: false,
- keepAlive: true
- }), api, options)
- // spawn sub-process of self for modern build
- const { execa } = require('@vue/cli-shared-utils')
- const cliBin = require('path').resolve(__dirname, '../../../bin/vue-cli-service.js')
- await execa(cliBin, ['build', ...rawArgs], {
- stdio: 'inherit',
- env: {
- VUE_CLI_MODERN_BUILD: true
- }
- })
- } else {
- // sub-process for modern build
- await build(Object.assign({}, args, {
- modernBuild: true,
- clean: false
- }), api, options)
- }
- delete process.env.VUE_CLI_MODERN_MODE
- } else {
- if (args.modern) {
- const { warn } = require('@vue/cli-shared-utils')
- warn(
- `Modern mode only works with default target (app). ` +
- `For libraries or web components, use the browserslist ` +
- `config to specify target browsers.`
- )
- }
- await build(args, api, options)
- }
- delete process.env.VUE_CLI_BUILD_TARGET
- })
- }
- async function build (args, api, options) {
- const fs = require('fs-extra')
- const path = require('path')
- const chalk = require('chalk')
- const webpack = require('webpack')
- const formatStats = require('./formatStats')
- const validateWebpackConfig = require('../../util/validateWebpackConfig')
- const {
- log,
- done,
- info,
- logWithSpinner,
- stopSpinner
- } = require('@vue/cli-shared-utils')
- log()
- const mode = api.service.mode
- if (args.target === 'app') {
- const bundleTag = args.modern
- ? args.modernBuild
- ? `modern bundle `
- : `legacy bundle `
- : ``
- logWithSpinner(`Building ${bundleTag}for ${mode}...`)
- } else {
- const buildMode = buildModes[args.target]
- if (buildMode) {
- const additionalParams = buildMode === 'library' ? ` (${args.formats})` : ``
- logWithSpinner(`Building for ${mode} as ${buildMode}${additionalParams}...`)
- } else {
- throw new Error(`Unknown build target: ${args.target}`)
- }
- }
- const targetDir = api.resolve(args.dest || options.outputDir)
- const isLegacyBuild = args.target === 'app' && args.modern && !args.modernBuild
- // resolve raw webpack config
- let webpackConfig
- if (args.target === 'lib') {
- webpackConfig = require('./resolveLibConfig')(api, args, options)
- } else if (
- args.target === 'wc' ||
- args.target === 'wc-async'
- ) {
- webpackConfig = require('./resolveWcConfig')(api, args, options)
- } else {
- webpackConfig = require('./resolveAppConfig')(api, args, options)
- }
- // check for common config errors
- validateWebpackConfig(webpackConfig, api, options, args.target)
- // apply inline dest path after user configureWebpack hooks
- // so it takes higher priority
- if (args.dest) {
- modifyConfig(webpackConfig, config => {
- config.output.path = targetDir
- })
- }
- if (args.watch) {
- modifyConfig(webpackConfig, config => {
- config.watch = true
- })
- }
- // Expose advanced stats
- if (args.dashboard) {
- const DashboardPlugin = require('../../webpack/DashboardPlugin')
- modifyConfig(webpackConfig, config => {
- config.plugins.push(new DashboardPlugin({
- type: 'build',
- modernBuild: args.modernBuild,
- keepAlive: args.keepAlive
- }))
- })
- }
- if (args.report || args['report-json']) {
- const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
- modifyConfig(webpackConfig, config => {
- const bundleName = args.target !== 'app'
- ? config.output.filename.replace(/\.js$/, '-')
- : isLegacyBuild ? 'legacy-' : ''
- config.plugins.push(new BundleAnalyzerPlugin({
- logLevel: 'warn',
- openAnalyzer: false,
- analyzerMode: args.report ? 'static' : 'disabled',
- reportFilename: `${bundleName}report.html`,
- statsFilename: `${bundleName}report.json`,
- generateStatsFile: !!args['report-json']
- }))
- })
- }
- if (args.clean) {
- await fs.remove(targetDir)
- }
- return new Promise((resolve, reject) => {
- webpack(webpackConfig, (err, stats) => {
- stopSpinner(false)
- if (err) {
- return reject(err)
- }
- if (stats.hasErrors()) {
- return reject(`Build failed with errors.`)
- }
- if (!args.silent) {
- const targetDirShort = path.relative(
- api.service.context,
- targetDir
- )
- log(formatStats(stats, targetDirShort, api))
- if (args.target === 'app' && !isLegacyBuild) {
- if (!args.watch) {
- done(`Build complete. The ${chalk.cyan(targetDirShort)} directory is ready to be deployed.`)
- info(`Check out deployment instructions at ${chalk.cyan(`https://cli.vuejs.org/guide/deployment.html`)}\n`)
- } else {
- done(`Build complete. Watching for changes...`)
- }
- }
- }
- // test-only signal
- if (process.env.VUE_CLI_TEST) {
- console.log('Build complete.')
- }
- resolve()
- })
- })
- }
- module.exports.defaultModes = {
- build: 'production'
- }
|