123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- const defaults = {
- clean: true,
- target: 'app',
- module: true,
- formats: 'commonjs,umd,umd-min'
- }
- 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})`,
- '--no-module': `build app without generating <script type="module"> chunks for modern browsers`,
- '--target': `app | lib | wc | wc-async (default: ${defaults.target})`,
- '--inline-vue': 'include the Vue module in the final bundle of library or web component 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 contents before building the project`,
- '--report': `generate report.html to help analyze bundle content`,
- '--report-json': 'generate report.json to help analyze bundle content',
- '--skip-plugins': `comma-separated list of plugin names to skip for this run`,
- '--watch': `watch for changes`,
- '--stdin': `close when stdin ends`
- }
- }, 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
- const { log, execa } = require('@vue/cli-shared-utils')
- const { allProjectTargetsSupportModule } = require('../../util/targets')
- let needsDifferentialLoading = args.target === 'app' && args.module
- if (allProjectTargetsSupportModule) {
- log(
- `All browser targets in the browserslist configuration have supported ES module.\n` +
- `Therefore we don't build two separate bundles for differential loading.\n`
- )
- needsDifferentialLoading = false
- }
- args.needsDifferentialLoading = needsDifferentialLoading
- if (!needsDifferentialLoading) {
- await build(args, api, options)
- return
- }
- process.env.VUE_CLI_MODERN_MODE = true
- if (!process.env.VUE_CLI_MODERN_BUILD) {
- // main-process for legacy build
- const legacyBuildArgs = { ...args, moduleBuild: false, keepAlive: true }
- await build(legacyBuildArgs, api, options)
- // spawn sub-process of self for modern build
- const cliBin = require('path').resolve(__dirname, '../../../bin/vue-cli-service.js')
- await execa('node', [cliBin, 'build', ...rawArgs], {
- stdio: 'inherit',
- env: {
- VUE_CLI_MODERN_BUILD: true
- }
- })
- } else {
- // sub-process for modern build
- const moduleBuildArgs = { ...args, moduleBuild: true, clean: false }
- await build(moduleBuildArgs, api, options)
- }
- })
- }
- async function build (args, api, options) {
- const fs = require('fs-extra')
- const path = require('path')
- const webpack = require('webpack')
- const { chalk } = require('@vue/cli-shared-utils')
- 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.needsDifferentialLoading
- ? args.moduleBuild
- ? `module 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}`)
- }
- }
- if (args.dest) {
- // Override outputDir before resolving webpack config as config relies on it (#2327)
- options.outputDir = args.dest
- }
- const targetDir = api.resolve(options.outputDir)
- const isLegacyBuild = args.needsDifferentialLoading && !args.moduleBuild
- // 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)
- if (args.watch) {
- modifyConfig(webpackConfig, config => {
- config.watch = true
- })
- }
- if (args.stdin) {
- process.stdin.on('end', () => {
- process.exit(0)
- })
- process.stdin.resume()
- }
- // Expose advanced stats
- if (args.dashboard) {
- const DashboardPlugin = require('../../webpack/DashboardPlugin')
- modifyConfig(webpackConfig, config => {
- config.plugins.push(new DashboardPlugin({
- type: 'build',
- moduleBuild: args.moduleBuild,
- 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.emptyDir(targetDir)
- }
- return new Promise((resolve, reject) => {
- webpack(webpackConfig, (err, stats) => {
- stopSpinner(false)
- if (err) {
- return reject(err)
- }
- if (stats.hasErrors()) {
- return reject(new Error('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'
- }
|