Commit ca801c4f authored by 尚斌杰's avatar 尚斌杰

feat(多页面):支持vue/cli@5.x

parent 4b362fdd
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
yarn.lock
\ No newline at end of file
...@@ -15,6 +15,7 @@ const buildModes = { ...@@ -15,6 +15,7 @@ const buildModes = {
wc: 'web component', wc: 'web component',
'wc-async': 'web component (async)' 'wc-async': 'web component (async)'
} }
if(!fs.existsSync(path.resolve('./src/pages'))){ if(!fs.existsSync(path.resolve('./src/pages'))){
fs.mkdirSync(path.resolve('./src/pages')) fs.mkdirSync(path.resolve('./src/pages'))
} }
...@@ -30,6 +31,7 @@ for (let index = 0; index < dirs.length; index++) { ...@@ -30,6 +31,7 @@ for (let index = 0; index < dirs.length; index++) {
allPages.push(dirName) allPages.push(dirName)
} }
} }
const modifyConfig = (config, fn) => { const modifyConfig = (config, fn) => {
if (Array.isArray(config)) { if (Array.isArray(config)) {
config.forEach(c => fn(c)) config.forEach(c => fn(c))
...@@ -48,13 +50,16 @@ module.exports = (api, options) => { ...@@ -48,13 +50,16 @@ module.exports = (api, options) => {
'--modern': `build app targeting modern browsers with auto fallback`, '--modern': `build app targeting modern browsers with auto fallback`,
'--no-unsafe-inline': `build app without introducing inline scripts`, '--no-unsafe-inline': `build app without introducing inline scripts`,
'--target': `app | lib | wc | wc-async (default: ${defaults.target})`, '--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})`, '--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)`, '--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)`, '--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`, '--no-clean': `do not remove the dist directory contents before building the project`,
'--report': `generate report.html to help analyze bundle content`, '--report': `generate report.html to help analyze bundle content`,
'--report-json': 'generate report.json to help analyze bundle content', '--report-json': 'generate report.json to help analyze bundle content',
'--watch': `watch for changes` '--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) => { }, async (args, rawArgs) => {
for (const key in defaults) { for (const key in defaults) {
...@@ -62,7 +67,6 @@ module.exports = (api, options) => { ...@@ -62,7 +67,6 @@ module.exports = (api, options) => {
args[key] = defaults[key] args[key] = defaults[key]
} }
} }
if(!args._[0]){ if(!args._[0]){
if(allPages){ if(allPages){
args._[0] = allPages.join('+') args._[0] = allPages.join('+')
...@@ -77,8 +81,9 @@ module.exports = (api, options) => { ...@@ -77,8 +81,9 @@ module.exports = (api, options) => {
console.log(chalk.red(`你所编译的页面不存在`)) console.log(chalk.red(`你所编译的页面不存在`))
return return
} }
pageData.forEach(async item=>{
args.entry = item pageData.forEach(async ()=>{
args.entry = args.entry || args._[0]
if (args.target !== 'app') { if (args.target !== 'app') {
args.entry = args.entry || 'src/App.vue' args.entry = args.entry || 'src/App.vue'
} }
...@@ -86,32 +91,29 @@ module.exports = (api, options) => { ...@@ -86,32 +91,29 @@ module.exports = (api, options) => {
process.env.VUE_CLI_BUILD_TARGET = args.target process.env.VUE_CLI_BUILD_TARGET = args.target
if (args.modern && args.target === 'app') { if (args.modern && args.target === 'app') {
process.env.VUE_CLI_MODERN_MODE = true process.env.VUE_CLI_MODERN_MODE = true
delete process.env.VUE_CLI_MODERN_BUILD if (!process.env.VUE_CLI_MODERN_BUILD) {
// if (!process.env.VUE_CLI_MODERN_BUILD) {
// main-process for legacy build // main-process for legacy build
await build(Object.assign({}, args, { await build(Object.assign({}, args, {
modernBuild: false, modernBuild: false,
keepAlive: true keepAlive: true
}), api, options) }), api, options)
// spawn sub-process of self for modern build // spawn sub-process of self for modern build
// const { execa } = require('@vue/cli-shared-utils') const { execa } = require('@vue/cli-shared-utils')
// const cliBin = require('@vue/cli-service/bin/vue-cli-service.js') const cliBin = require('path').resolve(__dirname, '../../@vue/cli-service/bin/vue-cli-service.js')
// await execa(cliBin, ['build', ...rawArgs], { await execa('node', [cliBin, 'build', ...rawArgs], {
// stdio: 'inherit', stdio: 'inherit',
// env: { env: {
// VUE_CLI_MODERN_BUILD: true VUE_CLI_MODERN_BUILD: true
// } }
// }) })
// } else { } else {
process.env.VUE_CLI_MODERN_BUILD = true
// sub-process for modern build // sub-process for modern build
await build(Object.assign({}, args, { await build(Object.assign({}, args, {
modernBuild: true, modernBuild: true,
clean: false clean: false
}), api, options) }), api, options)
// } }
delete process.env.VUE_CLI_MODERN_MODE delete process.env.VUE_CLI_MODERN_MODE
delete process.env.VUE_CLI_MODERN_BUILD
} else { } else {
if (args.modern) { if (args.modern) {
const { warn } = require('@vue/cli-shared-utils') const { warn } = require('@vue/cli-shared-utils')
...@@ -132,7 +134,7 @@ module.exports = (api, options) => { ...@@ -132,7 +134,7 @@ module.exports = (api, options) => {
* 获取页面 * 获取页面
* @param {Array} args * @param {Array} args
*/ */
function getPages(args) { function getPages(args) {
let pages = [] let pages = []
if(args._[0]){ if(args._[0]){
args._[0].split('+').forEach(item=>{ args._[0].split('+').forEach(item=>{
...@@ -145,10 +147,12 @@ function getPages(args) { ...@@ -145,10 +147,12 @@ function getPages(args) {
} }
async function build (args, api, options) { async function build (args, api, options) {
const chalk = require('chalk') const fs = require('fs-extra')
const path = require('path')
const webpack = require('webpack') const webpack = require('webpack')
const formatStats = require('../util/formatStats') const { chalk } = require('@vue/cli-shared-utils')
const validateWebpackConfig = require('../util/validateWebpackConfig') const formatStats = require('../../@vue/cli-service/lib/commands/build/formatStats')
const validateWebpackConfig = require('../../@vue/cli-service/lib/util/validateWebpackConfig')
const { const {
log, log,
done, done,
...@@ -176,20 +180,25 @@ async function build (args, api, options) { ...@@ -176,20 +180,25 @@ async function build (args, api, options) {
} }
} }
const targetDir = api.resolve(args.dest || options.outputDir) + '/' + args.entry 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) + '/' + args.entry
const isLegacyBuild = args.target === 'app' && args.modern && !args.modernBuild const isLegacyBuild = args.target === 'app' && args.modern && !args.modernBuild
// resolve raw webpack config // resolve raw webpack config
let webpackConfig let webpackConfig
if (args.target === 'lib') { if (args.target === 'lib') {
webpackConfig = require('../util/resolveLibConfig')(api, args, options) webpackConfig = require('../../@vue/cli-service/lib/commands/build/resolveLibConfig')(api, args, options)
} else if ( } else if (
args.target === 'wc' || args.target === 'wc' ||
args.target === 'wc-async' args.target === 'wc-async'
) { ) {
webpackConfig = require('../util/resolveWcConfig')(api, args, options) webpackConfig = require('../../@vue/cli-service/lib/commands/build/resolveWcConfig')(api, args, options)
} else { } else {
webpackConfig = require('../util/resolveAppConfig')(api, args, options) webpackConfig = require('../../@vue/cli-service/lib/commands/build/resolveAppConfig')(api, args, options)
} }
// check for common config errors // check for common config errors
...@@ -231,23 +240,22 @@ async function build (args, api, options) { ...@@ -231,23 +240,22 @@ async function build (args, api, options) {
} }
} }
// apply inline dest path after user configureWebpack hooks if (args.watch) {
// so it takes higher priority
if (args.dest) {
modifyConfig(webpackConfig, config => { modifyConfig(webpackConfig, config => {
config.output.path = targetDir config.watch = true
}) })
} }
if (args.watch) { if (args.stdin) {
modifyConfig(webpackConfig, config => { process.stdin.on('end', () => {
config.watch = true process.exit(0)
}) })
process.stdin.resume()
} }
// Expose advanced stats // Expose advanced stats
if (args.dashboard) { if (args.dashboard) {
const DashboardPlugin = require('@vue/cli-service/lib/webpack/DashboardPlugin') const DashboardPlugin = require('../../@vue/cli-service/lib/webpack/DashboardPlugin')
modifyConfig(webpackConfig, config => { modifyConfig(webpackConfig, config => {
config.plugins.push(new DashboardPlugin({ config.plugins.push(new DashboardPlugin({
type: 'build', type: 'build',
...@@ -275,7 +283,8 @@ async function build (args, api, options) { ...@@ -275,7 +283,8 @@ async function build (args, api, options) {
} }
if (args.clean) { if (args.clean) {
await fs.remove(api.resolve(args.dest || options.outputDir)) await fs.emptyDir(args.dest || options.outputDir)
// await fs.remove(api.resolve(args.dest || options.outputDir))
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
...@@ -286,7 +295,7 @@ async function build (args, api, options) { ...@@ -286,7 +295,7 @@ async function build (args, api, options) {
} }
if (stats.hasErrors()) { if (stats.hasErrors()) {
return reject(`Build failed with errors.`) return reject(new Error('Build failed with errors.'))
} }
if (!args.silent) { if (!args.silent) {
......
This diff is collapsed.
{ {
"name": "vue-cli-plugin-ts-multi-pages", "name": "vue-cli-plugin-ts-multi-pages",
"version": "1.2.1", "version": "2.0.0-beta.0",
"description": "vue-cli3多页开发插件TS版", "description": "vue-cli3多页开发插件TS版",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
...@@ -8,5 +8,8 @@ ...@@ -8,5 +8,8 @@
}, },
"keywords": [], "keywords": [],
"author": "shangbj", "author": "shangbj",
"license": "ISC" "license": "ISC",
"dependencies": {
"ejs": "^3.1.6"
}
} }
module.exports = function formatStats (stats, dir, api) {
const fs = require('fs')
const path = require('path')
const zlib = require('zlib')
const chalk = require('chalk')
const ui = require('cliui')({ width: 80 })
const json = stats.toJson({
hash: false,
modules: false,
chunks: false
})
let assets = json.assets
? json.assets
: json.children.reduce((acc, child) => acc.concat(child.assets), [])
const seenNames = new Map()
const isJS = val => /\.js$/.test(val)
const isCSS = val => /\.css$/.test(val)
const isMinJS = val => /\.min\.js$/.test(val)
assets = assets
.filter(a => {
if (seenNames.has(a.name)) {
return false
}
seenNames.set(a.name, true)
return isJS(a.name) || isCSS(a.name)
})
.sort((a, b) => {
if (isJS(a.name) && isCSS(b.name)) return -1
if (isCSS(a.name) && isJS(b.name)) return 1
if (isMinJS(a.name) && !isMinJS(b.name)) return -1
if (!isMinJS(a.name) && isMinJS(b.name)) return 1
return b.size - a.size
})
function formatSize (size) {
return (size / 1024).toFixed(2) + ' KiB'
}
function getGzippedSize (asset) {
const filepath = api.resolve(path.join(dir, asset.name))
const buffer = fs.readFileSync(filepath)
return formatSize(zlib.gzipSync(buffer).length)
}
function makeRow (a, b, c) {
return ` ${a}\t ${b}\t ${c}`
}
ui.div(
makeRow(
chalk.cyan.bold(`File`),
chalk.cyan.bold(`Size`),
chalk.cyan.bold(`Gzipped`)
) + `\n\n` +
assets.map(asset => makeRow(
/js$/.test(asset.name)
? chalk.green(path.join(dir, asset.name))
: chalk.blue(path.join(dir, asset.name)),
formatSize(asset.size),
getGzippedSize(asset)
)).join(`\n`)
)
return `${ui.toString()}\n\n ${chalk.gray(`Images and other types of assets omitted.`)}\n`
}
\ No newline at end of file
module.exports = function isAbsoluteUrl (url) {
// A URL is considered absolute if it begins with "<scheme>://" or "//"
return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url)
}
\ No newline at end of file
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file at
* https://github.com/facebookincubator/create-react-app/blob/master/LICENSE
*/
const fs = require('fs')
const url = require('url')
const path = require('path')
const chalk = require('chalk')
const address = require('address')
const defaultConfig = {
logLevel: 'silent',
secure: false,
changeOrigin: true,
ws: true,
xfwd: true
}
module.exports = function prepareProxy (proxy, appPublicFolder) {
// `proxy` lets you specify alternate servers for specific requests.
// It can either be a string or an object conforming to the Webpack dev server proxy configuration
// https://webpack.github.io/docs/webpack-dev-server.html
if (!proxy) {
return undefined
}
if (Array.isArray(proxy) || (typeof proxy !== 'object' && typeof proxy !== 'string')) {
console.log(
chalk.red(
'When specified, "proxy" in package.json must be a string or an object.'
)
)
console.log(
chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".')
)
console.log(
chalk.red(
'Either remove "proxy" from package.json, or make it an object.'
)
)
process.exit(1)
}
// Otherwise, if proxy is specified, we will let it handle any request except for files in the public folder.
function mayProxy (pathname) {
const maybePublicPath = path.resolve(appPublicFolder, pathname.slice(1))
return !fs.existsSync(maybePublicPath)
}
function createProxyEntry (target, usersOnProxyReq, context) {
// #2478
// There're a little-known use case that the `target` field is an object rather than a string
// https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/https.md
if (typeof target === 'string' && process.platform === 'win32') {
target = resolveLoopback(target)
}
return {
target,
context (pathname, req) {
// is a static asset
if (!mayProxy(pathname)) {
return false
}
if (context) {
// Explicit context, e.g. /api
return pathname.match(context)
} else {
// not a static request
if (req.method !== 'GET') {
return true
}
// Heuristics: if request `accept`s text/html, we pick /index.html.
// Modern browsers include text/html into `accept` header when navigating.
// However API calls like `fetch()` won’t generally accept text/html.
// If this heuristic doesn’t work well for you, use a custom `proxy` object.
return (
req.headers.accept &&
req.headers.accept.indexOf('text/html') === -1
)
}
},
onProxyReq (proxyReq, req, res) {
if (usersOnProxyReq) {
usersOnProxyReq(proxyReq, req, res)
}
// Browsers may send Origin headers even with same-origin
// requests. To prevent CORS issues, we have to change
// the Origin to match the target URL.
if (!proxyReq.agent && proxyReq.getHeader('origin')) {
proxyReq.setHeader('origin', target)
}
},
onError: onProxyError(target)
}
}
// Support proxy as a string for those who are using the simple proxy option
if (typeof proxy === 'string') {
if (!/^http(s)?:\/\//.test(proxy)) {
console.log(
chalk.red(
'When "proxy" is specified in package.json it must start with either http:// or https://'
)
)
process.exit(1)
}
return [
Object.assign({}, defaultConfig, createProxyEntry(proxy))
]
}
// Otherwise, proxy is an object so create an array of proxies to pass to webpackDevServer
return Object.keys(proxy).map(context => {
const config = proxy[context]
if (!config.hasOwnProperty('target')) {
console.log(
chalk.red(
'When `proxy` in package.json is an object, each `context` object must have a ' +
'`target` property specified as a url string'
)
)
process.exit(1)
}
const entry = createProxyEntry(config.target, config.onProxyReq, context)
return Object.assign({}, defaultConfig, config, entry)
})
}
function resolveLoopback (proxy) {
const o = url.parse(proxy)
o.host = undefined
if (o.hostname !== 'localhost') {
return proxy
}
// Unfortunately, many languages (unlike node) do not yet support IPv6.
// This means even though localhost resolves to ::1, the application
// must fall back to IPv4 (on 127.0.0.1).
// We can re-enable this in a few years.
/* try {
o.hostname = address.ipv6() ? '::1' : '127.0.0.1';
} catch (_ignored) {
o.hostname = '127.0.0.1';
}*/
try {
// Check if we're on a network; if we are, chances are we can resolve
// localhost. Otherwise, we can just be safe and assume localhost is
// IPv4 for maximum compatibility.
if (!address.ip()) {
o.hostname = '127.0.0.1'
}
} catch (_ignored) {
o.hostname = '127.0.0.1'
}
return url.format(o)
}
// We need to provide a custom onError function for httpProxyMiddleware.
// It allows us to log custom error messages on the console.
function onProxyError (proxy) {
return (err, req, res) => {
const host = req.headers && req.headers.host
console.log(
chalk.red('Proxy error:') +
' Could not proxy request ' +
chalk.cyan(req.url) +
' from ' +
chalk.cyan(host) +
' to ' +
chalk.cyan(proxy) +
'.'
)
console.log(
'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' +
chalk.cyan(err.code) +
').'
)
console.log()
// And immediately send the proper error response to the client.
// Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side.
if (res.writeHead && !res.headersSent) {
res.writeHead(500)
}
res.end(
'Proxy error: Could not proxy request ' +
req.url +
' from ' +
host +
' to ' +
proxy +
' (' +
err.code +
').'
)
}
}
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file at
* https://github.com/facebookincubator/create-react-app/blob/master/LICENSE
*/
const url = require('url')
const chalk = require('chalk')
const address = require('address')
const defaultGateway = require('default-gateway')
module.exports = function prepareUrls (protocol, host, port, pathname = '/') {
const formatUrl = hostname =>
url.format({
protocol,
hostname,
port,
pathname
})
const prettyPrintUrl = hostname =>
url.format({
protocol,
hostname,
port: chalk.bold(port),
pathname
})
const isUnspecifiedHost = host === '0.0.0.0' || host === '::'
let prettyHost, lanUrlForConfig
let lanUrlForTerminal = chalk.gray('unavailable')
if (isUnspecifiedHost) {
prettyHost = 'localhost'
try {
// This can only return an IPv4 address
const result = defaultGateway.v4.sync()
lanUrlForConfig = address.ip(result && result.interface)
if (lanUrlForConfig) {
// Check if the address is a private ip
// https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
if (
/^10[.]|^172[.](1[6-9]|2[0-9]|3[0-1])[.]|^192[.]168[.]/.test(
lanUrlForConfig
)
) {
// Address is private, format it for later use
lanUrlForTerminal = prettyPrintUrl(lanUrlForConfig)
} else {
// Address is not private, so we will discard it
lanUrlForConfig = undefined
}
}
} catch (_e) {
// ignored
}
} else {
prettyHost = host
lanUrlForConfig = host
lanUrlForTerminal = prettyPrintUrl(lanUrlForConfig)
}
const localUrlForTerminal = prettyPrintUrl(prettyHost)
const localUrlForBrowser = formatUrl(prettyHost)
return {
lanUrlForConfig,
lanUrlForTerminal,
localUrlForTerminal,
localUrlForBrowser
}
}
module.exports = (api, args, options) => {
const config = api.resolveChainableWebpackConfig()
const targetDir = api.resolve(args.dest || options.outputDir)
// respect inline build destination in copy plugin
if (args.dest && config.plugins.has('copy')) {
config.plugin('copy').tap(args => {
args[0][0].to = targetDir
return args
})
}
if (args.modern) {
const ModernModePlugin = require('../../webpack/ModernModePlugin')
if (!args.modernBuild) {
// Inject plugin to extract build stats and write to disk
config
.plugin('modern-mode-legacy')
.use(ModernModePlugin, [{
targetDir,
isModernBuild: false,
unsafeInline: args['unsafe-inline']
}])
} else {
// Inject plugin to read non-modern build stats and inject HTML
config
.plugin('modern-mode-modern')
.use(ModernModePlugin, [{
targetDir,
isModernBuild: true,
unsafeInline: args['unsafe-inline'],
// as we may generate an addition file asset (if `no-unsafe-inline` specified)
// we need to provide the correct directory for that file to place in
jsDirectory: require('../../util/getAssetPath')(options, 'js')
}])
}
}
const rawConfig = api.resolveWebpackConfig(config)
// respect inline entry
if (args.entry && !options.pages) {
rawConfig.entry = { app: api.resolve(args.entry) }
}
return rawConfig
}
\ No newline at end of file
const fs = require('fs')
const path = require('path')
module.exports = (api, { entry, name, formats, filename }, options) => {
const { log, error } = require('@vue/cli-shared-utils')
const abort = msg => {
log()
error(msg)
process.exit(1)
}
const fullEntryPath = api.resolve(entry)
if (!fs.existsSync(fullEntryPath)) {
abort(
`Failed to resolve lib entry: ${entry}${entry === `src/App.vue` ? ' (default)' : ''}. ` +
`Make sure to specify the correct entry file.`
)
}
const isVueEntry = /\.vue$/.test(entry)
const libName = (
name ||
api.service.pkg.name ||
path.basename(entry).replace(/\.(jsx?|vue)$/, '')
)
filename = filename || libName
function genConfig (format, postfix = format, genHTML) {
const config = api.resolveChainableWebpackConfig()
const browserslist = require('browserslist')
const targets = browserslist(undefined, { path: fullEntryPath })
const supportsIE = targets.some(agent => agent.includes('ie'))
const webpack = require('webpack')
config.plugin('need-current-script-polyfill')
.use(webpack.DefinePlugin, [{
'process.env.NEED_CURRENTSCRIPT_POLYFILL': JSON.stringify(supportsIE)
}])
// adjust css output name so they write to the same file
if (config.plugins.has('extract-css')) {
config
.plugin('extract-css')
.tap(args => {
args[0].filename = `${filename}.css`
return args
})
}
// only minify min entry
if (!/\.min/.test(postfix)) {
config.optimization.minimize(false)
}
// externalize Vue in case user imports it
config
.externals({
...config.get('externals'),
vue: {
commonjs: 'vue',
commonjs2: 'vue',
root: 'Vue'
}
})
// inject demo page for umd
if (genHTML) {
const template = isVueEntry ? 'demo-lib.html' : 'demo-lib-js.html'
config
.plugin('demo-html')
.use(require('html-webpack-plugin'), [{
template: path.resolve(__dirname, template),
inject: false,
filename: 'demo.html',
libName,
assetsFileName: filename,
cssExtract: config.plugins.has('extract-css')
}])
}
// resolve entry/output
const entryName = `${filename}.${postfix}`
config.resolve
.alias
.set('~entry', fullEntryPath)
// set output target before user configureWebpack hooks are applied
config.output.libraryTarget(format)
// set entry/output after user configureWebpack hooks are applied
const rawConfig = api.resolveWebpackConfig(config)
let realEntry = require.resolve('./entry-lib.js')
// avoid importing default if user entry file does not have default export
if (!isVueEntry) {
const entryContent = fs.readFileSync(fullEntryPath, 'utf-8')
if (!/\b(export\s+default|export\s{[^}]+as\s+default)\b/.test(entryContent)) {
realEntry = require.resolve('./entry-lib-no-default.js')
}
}
rawConfig.entry = {
[entryName]: realEntry
}
rawConfig.output = Object.assign({
library: libName,
libraryExport: isVueEntry ? 'default' : undefined,
libraryTarget: format,
// preserve UDM header from webpack 3 until webpack provides either
// libraryTarget: 'esm' or target: 'universal'
// https://github.com/webpack/webpack/issues/6522
// https://github.com/webpack/webpack/issues/6525
globalObject: `(typeof self !== 'undefined' ? self : this)`
}, rawConfig.output, {
filename: `${entryName}.js`,
chunkFilename: `${entryName}.[name].js`,
// use dynamic publicPath so this can be deployed anywhere
// the actual path will be determined at runtime by checking
// document.currentScript.src.
publicPath: ''
})
return rawConfig
}
const configMap = {
commonjs: genConfig('commonjs2', 'common'),
umd: genConfig('umd', undefined, true),
'umd-min': genConfig('umd', 'umd.min')
}
const formatArray = (formats + '').split(',')
const configs = formatArray.map(format => configMap[format])
if (configs.indexOf(undefined) !== -1) {
const unknownFormats = formatArray.filter(f => configMap[f] === undefined).join(', ')
abort(
`Unknown library build formats: ${unknownFormats}`
)
}
return configs
}
const path = require('path')
const { resolveEntry, fileToComponentName } = require('./resolveWcEntry')
module.exports = (api, { target, entry, name }) => {
// Disable CSS extraction and turn on CSS shadow mode for vue-style-loader
process.env.VUE_CLI_CSS_SHADOW_MODE = true
const { log, error } = require('@vue/cli-shared-utils')
const abort = msg => {
log()
error(msg)
process.exit(1)
}
const isAsync = /async/.test(target)
// generate dynamic entry based on glob files
const resolvedFiles = require('globby').sync([entry], { cwd: api.resolve('.') })
if (!resolvedFiles.length) {
abort(`entry pattern "${entry}" did not match any files.`)
}
let libName
let prefix
if (resolvedFiles.length === 1) {
// in single mode, determine the lib name from filename
libName = name || fileToComponentName('', resolvedFiles[0]).kebabName
prefix = ''
if (libName.indexOf('-') < 0) {
abort(`--name must contain a hyphen when building a single web component.`)
}
} else {
// multi mode
libName = prefix = (name || api.service.pkg.name)
if (!libName) {
abort(`--name is required when building multiple web components.`)
}
}
const dynamicEntry = resolveEntry(prefix, libName, resolvedFiles, isAsync)
function genConfig (minify, genHTML) {
const config = api.resolveChainableWebpackConfig()
// make sure not to transpile wc-wrapper
config.module
.rule('js')
.exclude
.add(/vue-wc-wrapper/)
// only minify min entry
if (!minify) {
config.optimization.minimize(false)
}
// externalize Vue in case user imports it
config
.externals({
...config.get('externals'),
vue: 'Vue'
})
config
.plugin('web-component-options')
.use(require('webpack/lib/DefinePlugin'), [{
'process.env': {
CUSTOM_ELEMENT_NAME: JSON.stringify(libName)
}
}])
// enable shadow mode in vue-loader
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
options.shadowMode = true
return options
})
if (genHTML) {
config
.plugin('demo-html')
.use(require('html-webpack-plugin'), [{
template: path.resolve(__dirname, `./demo-wc.html`),
inject: false,
filename: 'demo.html',
libName,
components:
prefix === ''
? [libName]
: resolvedFiles.map(file => {
return fileToComponentName(prefix, file).kebabName
})
}])
}
// set entry/output last so it takes higher priority than user
// configureWebpack hooks
// set proxy entry for *.vue files
config.resolve
.alias
.set('~root', api.resolve('.'))
const rawConfig = api.resolveWebpackConfig(config)
const entryName = `${libName}${minify ? `.min` : ``}`
rawConfig.entry = {
[entryName]: dynamicEntry
}
Object.assign(rawConfig.output, {
// to ensure that multiple copies of async wc bundles can co-exist
// on the same page.
jsonpFunction: libName.replace(/-\w/g, c => c.charAt(1).toUpperCase()) + '_jsonp',
filename: `${entryName}.js`,
chunkFilename: `${libName}.[name]${minify ? `.min` : ``}.js`,
// use dynamic publicPath so this can be deployed anywhere
// the actual path will be determined at runtime by checking
// document.currentScript.src.
publicPath: ''
})
return rawConfig
}
return [
genConfig(false, true),
genConfig(true, false)
]
}
module.exports = function validateWebpackConfig (
webpackConfig,
api,
options,
target = 'app'
) {
const singleConfig = Array.isArray(webpackConfig)
? webpackConfig[0]
: webpackConfig
const actualTargetDir = singleConfig.output.path
if (actualTargetDir !== api.resolve(options.outputDir)) {
// user directly modifies output.path in configureWebpack or chainWebpack.
// this is not supported because there's no way for us to give copy
// plugin the correct value this way.
throw new Error(
`\n\nConfiguration Error: ` +
`Avoid modifying webpack output.path directly. ` +
`Use the "outputDir" option instead.\n`
)
}
if (actualTargetDir === api.service.context) {
throw new Error(
`\n\nConfiguration Error: ` +
`Do not set output directory to project root.\n`
)
}
if (target === 'app' && singleConfig.output.publicPath !== options.publicPath) {
throw new Error(
`\n\nConfiguration Error: ` +
`Avoid modifying webpack output.publicPath directly. ` +
`Use the "publicPath" option instead.\n`
)
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment