Skip to content

Commit d1089f6

Browse files
author
wanglinquan
committed
feat: init project
1 parent 1fc76f8 commit d1089f6

File tree

11 files changed

+6179
-0
lines changed

11 files changed

+6179
-0
lines changed

build.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const { resolveEntryConfig } = require('./utils/index')
2+
3+
module.exports = function resolveBuild (api, options, key, immediate) {
4+
return resolveEntryConfig('build', key, { immediate, api, options })
5+
}

generator.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = api => {
2+
3+
}

index.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const resolveWatch = require('./watch')
2+
const resolveBuild = require('./build')
3+
const print = require('./utils/print')
4+
const initVueConfig = require('./utils/initVueConfig')
5+
6+
module.exports = (api, options) => {
7+
api.registerCommand('init-config', {
8+
description: 'generate `vue.config.js`、`Views.json` file to your project',
9+
usage: 'vue-cli-service init-config'
10+
}, () => {
11+
initVueConfig()
12+
})
13+
14+
api.registerCommand('build-multi', {
15+
description: 'build for multipage',
16+
usage: 'vue-cli-service build-multi',
17+
options: {
18+
'--key': 'specify a key to build entry',
19+
'--inspect': 'output <filename> the final webpack config'
20+
}
21+
},
22+
(args) => {
23+
const { key, inspect } = args
24+
const immediate = typeof inspect === 'undefined' && !inspect
25+
const config = resolveBuild(api, options, key, immediate)
26+
27+
if (!immediate) {
28+
const outputFileName = typeof inspect === 'boolean' ? undefined : inspect
29+
print(config, outputFileName)
30+
}
31+
})
32+
33+
api.registerCommand('watch-multi', {
34+
description: 'watch for multipage',
35+
usage: 'vue-cli-service watch-multi',
36+
options: {
37+
'--key': 'specify a key to build entry',
38+
'--inspect': 'output <filename> the final webpack config'
39+
}
40+
},
41+
(args) => {
42+
const { key, inspect } = args
43+
const immediate = typeof inspect === 'undefined' && !inspect
44+
const config = resolveWatch(api, options, key, immediate)
45+
46+
if (!immediate) {
47+
const outputFileName = typeof inspect === 'boolean' ? undefined : inspect
48+
print(config, outputFileName)
49+
}
50+
})
51+
}

package.json

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"name": "vue-cli-plugin-multiple-page",
3+
"version": "0.1.0",
4+
"main": "index.js",
5+
"author": {
6+
"name": "lenq",
7+
"email": "qqcome110@gmail.com",
8+
"url": "https://github.com/qqcome110/"
9+
},
10+
"license": "MIT",
11+
"bugs": {
12+
"url": "https://github.com/qqcome110/vue-cli-plugin-multiple-page/issues"
13+
},
14+
"peerDependencies": {
15+
"@vue/cli-service": "^3.0.0 || ^4.0.0-0"
16+
},
17+
"devDependencies": {
18+
"chalk": "^4.1.0",
19+
"clean-webpack-plugin": "^3.0.0",
20+
"cli-highlight": "^2.1.4",
21+
"cosmiconfig": "^7.0.0",
22+
"optimize-css-assets-webpack-plugin": "^5.0.4",
23+
"prompts": "^2.3.2",
24+
"webpack": "4.4.0",
25+
"webpack-chain": "^6.5.1",
26+
"husky": "^2.3.0",
27+
"lint-staged": "^8.1.7",
28+
"commitlint": "^8.0.0",
29+
"commitlint-config-cz": "^0.11.1",
30+
"cz-customizable": "^6.1.0",
31+
"eslint": "^5.16.0",
32+
"eslint-plugin-import": "^2.17.3",
33+
"eslint-plugin-node": "^9.1.0",
34+
"eslint-plugin-promise": "^4.1.1",
35+
"eslint-plugin-standard": "^4.0.0",
36+
"babel-eslint": "^10.0.1",
37+
"eslint-config-standard": "^12.0.0"
38+
},
39+
"husky": {
40+
"hooks": {
41+
"pre-commit": "lint-staged",
42+
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
43+
}
44+
},
45+
"lint-staged": {
46+
"src/**/*.{js}": [
47+
"eslint --fix",
48+
"git add"
49+
],
50+
"*.js": [
51+
"eslint --fix",
52+
"git add"
53+
]
54+
},
55+
"config": {
56+
"commitizen": {
57+
"path": "node_modules/cz-customizable"
58+
}
59+
}
60+
}

template/Views.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"basePath": "resources/assets/js/views",
3+
"destBasePath": "public/js/v",
4+
"entry": {
5+
"页面key": {
6+
"srcPath": "your/entry/file/path",
7+
"name": "index",
8+
"output": "index",
9+
"description": "页面描述"
10+
}
11+
}
12+
}

template/vue.config.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const path = require('path')
2+
3+
module.exports = {
4+
outputDir: 'dist',
5+
6+
assetsDir: '/',
7+
8+
filenameHashing: false,
9+
10+
// 是否使用包含运行时编译器的Vue核心的构建
11+
runtimeCompiler: true,
12+
13+
// 默认情况下 babel-loader 忽略其中的所有文件 node_modules
14+
transpileDependencies: [],
15+
16+
// 生产环境 sourceMap
17+
productionSourceMap: false,
18+
19+
// webpack 配置,键值对象时会合并配置,为方法时会改写配置
20+
// 针对该项目,建议在 chainWebpack 字段中使用链式调用方式配置,不支持在字段进行 webpack 相关配置
21+
// 在自定义命令时,使用 vue-cli-service 提供的 resolveChainableWebpackConfig()进行配置重写后,该方法是通过new Config() 获得一个新的配置对象,忽略这个配置
22+
23+
// webpack 链接 API,用于生成和修改 webapck 配置
24+
// https://github.com/mozilla-neutrino/webpack-chain
25+
chainWebpack: (config) => {
26+
config.externals({})
27+
28+
// config.optimization.splitChunks({
29+
// chunks: 'initial', // 必须三选一: "initial" | "all"(默认就是all) | "async",
30+
// name: 'vendor'
31+
// })
32+
// config.optimization.runtimeChunk({
33+
// name: 'manifest',
34+
// })
35+
// 设置别名
36+
config.resolve.alias
37+
.set('@', path.resolve(__dirname, 'src'))
38+
},
39+
40+
// 配置高于chainWebpack中关于 css loader 的配置
41+
css: {
42+
// 是否开启支持 foo.module.css 样式
43+
requireModuleExtension: false,
44+
45+
// 是否使用 css 分离插件 ExtractTextPlugin,采用独立样式文件载入,不采用 <style> 方式内联至 html 文件中
46+
extract: false,
47+
48+
// 是否构建样式地图,false 将提高构建速度
49+
sourceMap: false
50+
}
51+
}

utils/index.js

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
const path = require('path')
2+
const chalk = require('chalk')
3+
const webpack = require('webpack')
4+
const { cosmiconfig } = require('cosmiconfig')
5+
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
6+
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
7+
const splitRe = /,/
8+
const CONFIG_NAME = 'Views.json'
9+
const CUSTOM_CONFIG_NAME = 'multiple-page'
10+
11+
function getEntryConfig (config, key) {
12+
const entryKeys = Object.keys(config)
13+
const allEntryConfig = entryKeys.map(entryKey => {
14+
const { entry, output, description, chunkName, extract } = config[entryKey] || {}
15+
16+
return {
17+
key: entryKey,
18+
extract,
19+
entry,
20+
output,
21+
description,
22+
chunkName
23+
}
24+
})
25+
26+
// 判断用户传入的 key 是否有效
27+
if (splitRe.test(key)) {
28+
return allEntryConfig.filter(ety => key.split(splitRe).indexOf(ety.key) > -1)
29+
} else {
30+
if (entryKeys.indexOf(key) > -1) {
31+
return allEntryConfig.filter(ety => ety.key === key)
32+
} else if (key === 'all') {
33+
return allEntryConfig
34+
} else {
35+
return []
36+
}
37+
}
38+
}
39+
40+
function normalizedConfig (entryFileJsonPath) {
41+
const entryConfig = require(entryFileJsonPath)
42+
const { entry: entries, basePath, destBasePath } = entryConfig
43+
const entryKeys = Object.keys(entries)
44+
45+
return entryKeys.reduce((obj, key) => {
46+
const currentEntry = entries[key]
47+
// 设置当前页面对应 webpack 配置的 entry
48+
const currentEntryFilePath = path.join(basePath, currentEntry.srcPath, currentEntry.name + '.js')
49+
const entry = path.resolve(process.cwd(), currentEntryFilePath)
50+
const outputFilePath = path.join(destBasePath, currentEntry.srcPath)
51+
const output = path.resolve(process.cwd(), outputFilePath)
52+
53+
obj[key] = {
54+
entry,
55+
output,
56+
extract: currentEntry.extract,
57+
description: currentEntry.description,
58+
chunkName: currentEntry.name
59+
}
60+
61+
return obj
62+
}, {})
63+
}
64+
65+
async function getEntryFiles (key) {
66+
const explorer = cosmiconfig(CUSTOM_CONFIG_NAME)
67+
const entryFileJsonPath = process.env.VIEW_JSON || path.resolve(process.cwd(), CONFIG_NAME)
68+
const result = await explorer.search()
69+
let entryConfigs = []
70+
71+
// 命令行没有传入 key 时直接退出操作
72+
if (!key || !key.toString().trim()) {
73+
console.log(chalk.red('输入的key为空'))
74+
process.exit(1)
75+
}
76+
77+
if (result && !result.isEmpty && result.config) {
78+
entryConfigs = getEntryConfig(result.config, key)
79+
} else if (entryFileJsonPath) {
80+
const config = normalizedConfig(entryFileJsonPath, key)
81+
entryConfigs = getEntryConfig(config, key)
82+
} else {
83+
console.log(chalk.red(`找不到配置文件, 请指定 \`${CUSTOM_CONFIG_NAME}.config.js\` 或 \`View.json\` 文件进行配置`))
84+
process.exit(1)
85+
}
86+
87+
return entryConfigs
88+
}
89+
90+
// [{ entry: '', output: '', description: '', chunkName: '' }, { }]
91+
async function resolveEntryConfig (mode, key, { immediate = true, api, options }) {
92+
const entryConfigs = await getEntryFiles(key)
93+
const entryWebpackConfigs = []
94+
const entryDescriptions = new Set()
95+
96+
if (Array.isArray(entryConfigs) && !entryConfigs.length) {
97+
console.log(chalk.yellow('请指定正确的 key !'))
98+
process.exit(1)
99+
}
100+
101+
entryConfigs.forEach(entryConfig => {
102+
// override vue's original configuration
103+
options.css.extract = Boolean(entryConfig.extract)
104+
const webpackConfigChain = api.resolveChainableWebpackConfig()
105+
106+
pruneDefaultWebpackConfig(webpackConfigChain)
107+
108+
if (mode === 'build') {
109+
// 设置 mode 为 production
110+
webpackConfigChain.mode('production')
111+
// 关闭 watch 模式
112+
webpackConfigChain.watch(false)
113+
114+
webpackConfigChain
115+
.plugin('optimize-css')
116+
.use(OptimizeCSSAssetsPlugin)
117+
}
118+
119+
if (mode === 'watch') {
120+
// 是否开启watch模式
121+
webpackConfigChain.watch(true)
122+
}
123+
// set entry config
124+
webpackConfigChain
125+
.entry(entryConfig.chunkName)
126+
.add(entryConfig.entry)
127+
128+
// set output config
129+
webpackConfigChain
130+
.output
131+
.path(entryConfig.output)
132+
.filename('[name].js')
133+
.chunkFilename('[name].js')
134+
135+
// clean dist path
136+
webpackConfigChain
137+
.plugin('clean')
138+
.use(CleanWebpackPlugin)
139+
140+
// save per entry file webpack config
141+
entryWebpackConfigs.push(webpackConfigChain.toConfig())
142+
entryDescriptions.add(entryConfig.description)
143+
144+
// clean entry config & dist config
145+
webpackConfigChain.entryPoints.store.delete(entryConfig.output)
146+
webpackConfigChain.output.clear()
147+
})
148+
149+
if (entryDescriptions.size) {
150+
[...entryDescriptions].forEach(description => {
151+
console.log(chalk.green(description))
152+
})
153+
}
154+
155+
if (immediate) {
156+
webpack(entryWebpackConfigs, (err, stats) => {
157+
if (err || stats.hasErrors()) {
158+
console.log(chalk.red('webpack build error!'))
159+
// process.exit(1)
160+
}
161+
})
162+
}
163+
164+
return entryWebpackConfigs
165+
}
166+
167+
function pruneDefaultWebpackConfig (webpackConfigChain) {
168+
// 删除默认入口
169+
webpackConfigChain.entryPoints.store.delete('app')
170+
// 删除 copy-webpack-plugin 插件,避免打包时将 public 文件夹打包到输出路径
171+
webpackConfigChain.plugins.delete('copy')
172+
// 删除 html-webpack-plugin 插件
173+
webpackConfigChain.plugins.delete('html')
174+
// 删除热加载插件
175+
webpackConfigChain.plugins.delete('hmr')
176+
// 删除 preload 和 prefetch 插件
177+
webpackConfigChain.plugins.delete('preload')
178+
webpackConfigChain.plugins.delete('prefetch')
179+
// 禁止任何异步加载的 chunk
180+
webpackConfigChain
181+
.plugin('LimitChunkCountPlugin')
182+
.use(webpack.optimize.LimitChunkCountPlugin, [{
183+
maxChunks: 1
184+
}])
185+
}
186+
187+
module.exports = {
188+
getEntryFiles,
189+
resolveEntryConfig,
190+
pruneDefaultWebpackConfig
191+
}

0 commit comments

Comments
 (0)