You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
7.3 KiB
238 lines
7.3 KiB
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' |
|
}
|
|
|