Skip to content

Commit 11e6722

Browse files
committed
init project
1 parent 597d26b commit 11e6722

File tree

13 files changed

+460
-9451
lines changed

13 files changed

+460
-9451
lines changed

LICENSE

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2015-present Yuxi (Evan) You
3+
Copyright (c) 2018-present, 美团点评
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99
copies of the Software, and to permit persons to whom the Software is
1010
furnished to do so, subject to the following conditions:
1111

12-
The above copyright notice and this permission notice shall be included in all
13-
copies or substantial portions of the Software.
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
1414

1515
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21-
SOFTWARE.
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,9 @@
1-
# vue-loader [![Build Status](https://circleci.com/gh/vuejs/vue-loader/tree/master.svg?style=shield)](https://circleci.com/gh/vuejs/vue-loader/tree/master) [![Windows Build status](https://ci.appveyor.com/api/projects/status/8cdonrkbg6m4k1tm/branch/master?svg=true)](https://ci.appveyor.com/project/yyx990803/vue-loader/branch/master) [![npm package](https://img.shields.io/npm/v/vue-loader.svg?maxAge=2592000)](https://www.npmjs.com/package/vue-loader)
1+
# mpvue-loader
22

3-
> Vue.js component loader for [Webpack](https://webpack.js.org/).
3+
>Webpack loader for mpvue components
44
5-
**NOTE: the master branch (9.0+) only works with Vue 2.x. For usage with Vue 1.x, see the [8.x branch](https://github.com/vuejs/vue-loader/tree/8.x).**
5+
本仓库是 fork 自 [vue-loader](https://github.com/vuejs/vue-loader) 修改而来,主要为 webpack 打包 mpvue components 提供能力。
66

7-
It allows you to write your components in this format:
7+
更多详细文档请查阅 [mpvue-loader](http://mpvue.com/build/mpvue-loader)
88

9-
![screenshot](http://blog.evanyou.me/images/vue-component.png)
10-
11-
The best way to get started is with [vue-cli](https://github.com/vuejs/vue-cli):
12-
13-
``` js
14-
npm install -g vue-cli
15-
vue init webpack-simple hello
16-
cd hello
17-
npm install
18-
npm run dev
19-
```
20-
21-
This will setup a basic Webpack + `vue-loader` project for you, with `*.vue` files and hot-reloading working out of the box!
22-
23-
For advanced `vue-loader` configuration, checkout the [documentation](https://vue-loader.vuejs.org).
9+
bug 或者交流建议等请反馈到 [mpvue/issues](https://github.com/Meituan-Dianping/mpvue/issues)

lib/loader.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ var hotReloadAPIPath = normalize.dep('vue-hot-reload-api')
2222
var hasBabel = !!tryRequire('babel-loader')
2323
var hasBuble = !!tryRequire('buble-loader')
2424

25+
// for mp js
26+
var { compileMP, compileMPScript } = require('./mp-compiler')
27+
2528
var rewriterInjectRE = /\b(css(?:-loader)?(?:\?[^!]+)?)(?:!|$)/
2629

2730
var defaultLang = {
@@ -42,7 +45,29 @@ function getRawRequest (context, excludedPreLoaders) {
4245
})
4346
}
4447

48+
const mpPages = []
49+
const mpInfo = {}
50+
4551
module.exports = function (content) {
52+
// for mp
53+
// 针对 entry 的 main.js 处理 page 和 app 的入口文件和配置等
54+
const mpOptions = loaderUtils.getOptions(this) || {}
55+
if (!mpPages.length) {
56+
const { entry = {}} = this.options
57+
Object.keys(entry).forEach(k => {
58+
mpInfo[entry[k]] = k
59+
if (k !== 'app') {
60+
mpPages.push(`pages/${k}/${k}`)
61+
}
62+
})
63+
}
64+
mpOptions.pages = mpPages
65+
mpOptions.mpInfo = mpInfo
66+
67+
if (mpOptions.checkMPEntry) {
68+
return compileMP.call(this, content, mpOptions)
69+
}
70+
4671
this.cacheable()
4772
var isServer = this.target === 'node'
4873
var isProduction = this.minimize || process.env.NODE_ENV === 'production'
@@ -104,12 +129,20 @@ module.exports = function (content) {
104129
: undefined
105130
})
106131

132+
// mp compiler 全局模式下注入 babelrc
133+
const babelLoaderOptions = mpOptions.globalBabelrc ? {
134+
loader: 'babel-loader',
135+
options: {
136+
extends: mpOptions.globalBabelrc
137+
}
138+
} : 'babel-loader'
139+
107140
var defaultLoaders = {
108141
html: templateCompilerPath + templateCompilerOptions,
109142
css: options.extractCSS
110143
? getCSSExtractLoader()
111144
: styleLoaderPath + '!' + 'css-loader' + cssLoaderOptions,
112-
js: hasBuble ? ('buble-loader' + bubleOptions) : hasBabel ? 'babel-loader' : ''
145+
js: hasBuble ? ('buble-loader' + bubleOptions) : hasBabel ? babelLoaderOptions : ''
113146
}
114147

115148
// check if there are custom loaders specified via
@@ -227,6 +260,10 @@ module.exports = function (content) {
227260
output += '/* script */\n'
228261
var script = parts.script
229262
if (script) {
263+
// for mp js
264+
// 需要解析组件的 components 给 wxml 生成用
265+
script = compileMPScript.call(this, script, mpOptions, moduleId)
266+
230267
if (options.esModule) {
231268
output += script.src
232269
? getImportForImport('script', script)

lib/mp-compiler/index.js

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// for mp
2+
const compiler = require('mpvue-template-compiler')
3+
const htmlBeautify = require('js-beautify').html
4+
5+
const babel = require('babel-core')
6+
const { parseConfig, parseComponentsDeps } = require('./parse')
7+
const { genScript, genStyle, genPageWxml } = require('./templates')
8+
9+
const { cacheFileInfo, getFileInfo, getCompNameBySrc, resolveTarget, covertCCVar, cacheSlots, getSlots } = require('./util')
10+
11+
const htmlBeautifyOptions = {
12+
wrap_line_length: '80',
13+
indent_size: 2,
14+
preserve_newlines: true,
15+
max_preserve_newlines: 0,
16+
e4x: true,
17+
unformatted: ['a', 'span', 'img', 'code', 'pre', 'sub', 'sup', 'em', 'strong', 'b', 'i', 'u', 'strike', 'big', 'small', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
18+
}
19+
20+
function createSlotsWxml (emitFile, slots) {
21+
cacheSlots(slots)
22+
const allSlots = getSlots()
23+
const content = Object.keys(allSlots).map(v => allSlots[v].code).join('\n')
24+
if (content.trim()) {
25+
emitFile('components/slots.wxml', htmlBeautify(content, htmlBeautifyOptions))
26+
}
27+
}
28+
29+
// 调用 compiler 生成 wxml
30+
function genComponentWxml (compiled, options, emitFile, emitError, emitWarning) {
31+
options.components['slots'] = { src: 'slots', name: 'slots' }
32+
const { code: wxmlCodeStr, compiled: cp, slots } = compiler.compileToWxml(compiled, options)
33+
const { mpErrors, mpTips } = cp
34+
35+
// 缓存 slots,延迟编译
36+
createSlotsWxml(emitFile, slots)
37+
38+
if (mpErrors && mpErrors.length) {
39+
emitError(
40+
`\n Error compiling template:\n` +
41+
mpErrors.map(e => ` - ${e}`).join('\n') + '\n'
42+
)
43+
}
44+
if (mpTips && mpTips.length) {
45+
emitWarning(
46+
mpTips.map(e => ` - ${e}`).join('\n') + '\n'
47+
)
48+
}
49+
return htmlBeautify(wxmlCodeStr, htmlBeautifyOptions)
50+
}
51+
52+
function createWxml (emitWarning, emitError, emitFile, resourcePath, rootComponent, compiled, html) {
53+
const { pageType, moduleId, components, src } = getFileInfo(resourcePath) || {}
54+
55+
// 这儿一个黑魔法,和 webpack 约定的规范写法有点偏差!
56+
if (!pageType || (components && !components.isCompleted)) {
57+
return setTimeout(createWxml, 20, ...arguments)
58+
}
59+
60+
let wxmlContent = ''
61+
let wxmlSrc = ''
62+
63+
if (rootComponent) {
64+
const componentName = getCompNameBySrc(rootComponent)
65+
wxmlContent = genPageWxml(componentName)
66+
wxmlSrc = src
67+
} else {
68+
// TODO, 这儿传 options 进去
69+
// {
70+
// components: {
71+
// 'com-a': { src: '../../components/comA$hash', name: 'comA$hash' }
72+
// },
73+
// pageType: 'component',
74+
// name: 'comA$hash',
75+
// moduleId: 'moduleId'
76+
// }
77+
const name = getCompNameBySrc(resourcePath)
78+
const options = { components, pageType, name, moduleId }
79+
wxmlContent = genComponentWxml(compiled, options, emitFile, emitError, emitWarning)
80+
wxmlSrc = `components/${name}`
81+
}
82+
83+
emitFile(`${wxmlSrc}.wxml`, wxmlContent)
84+
}
85+
86+
// 编译出 wxml
87+
function compileWxml (compiled, html) {
88+
return createWxml(this.emitWarning, this.emitError, this.emitFile, this.resourcePath, null, compiled, html)
89+
}
90+
91+
// 针对 .vue 单文件的脚本逻辑的处理
92+
// 处理出当前单文件组件的子组件依赖
93+
function compileMPScript (script, optioins, moduleId) {
94+
const { metadata } = babel.transform(script.content, { plugins: [parseComponentsDeps] })
95+
96+
// metadata: importsMap, components
97+
const { importsMap, components: originComponents } = metadata
98+
99+
// 处理子组件的信息
100+
const components = {}
101+
if (originComponents) {
102+
const allP = Object.keys(originComponents).map(k => {
103+
return new Promise((resolve, reject) => {
104+
this.resolve(this.context, originComponents[k], (err, realSrc) => {
105+
if (err) return reject(err)
106+
const com = covertCCVar(k)
107+
const comName = getCompNameBySrc(realSrc)
108+
components[com] = { src: comName, name: comName }
109+
resolve()
110+
})
111+
})
112+
})
113+
Promise.all(allP)
114+
.then(res => {
115+
components.isCompleted = true
116+
})
117+
.catch(err => {
118+
console.error(err)
119+
components.isCompleted = true
120+
})
121+
} else {
122+
components.isCompleted = true
123+
}
124+
125+
const fileInfo = resolveTarget(this.resourcePath, optioins.mpInfo)
126+
cacheFileInfo(this.resourcePath, fileInfo, { importsMap, components, moduleId })
127+
128+
return script
129+
}
130+
131+
// checkMPEntry 针对 entry main.js 的入口处理
132+
// 编译出 app, page 的入口js/wxml/json
133+
134+
const startPageReg = /^\^/
135+
136+
function compileMP (content, optioins) {
137+
const { resourcePath, emitError, emitFile, emitWarning, resolve, context } = this
138+
const { metadata } = babel.transform(content, { plugins: [parseConfig] })
139+
140+
// metadata: config
141+
const { config, rootComponent } = metadata
142+
143+
const fileInfo = resolveTarget(resourcePath, optioins.mpInfo)
144+
cacheFileInfo(resourcePath, fileInfo)
145+
const { src, name, isApp, isPage } = fileInfo
146+
147+
if (isApp || isPage) {
148+
// 生成入口 json
149+
if (config) {
150+
const configObj = config.value
151+
152+
// 只有 app 才处理 pages
153+
if (isApp) {
154+
const pages = (configObj.pages || []).concat(optioins.pages)
155+
156+
// ^ 开头的放在第一个
157+
const startPageIndex = pages.findIndex(v => startPageReg.test(v))
158+
if (startPageIndex !== -1) {
159+
const startPage = pages[startPageIndex].slice(1)
160+
pages.splice(startPageIndex, 1)
161+
pages.unshift(startPage)
162+
}
163+
configObj.pages = [...new Set(pages)]
164+
}
165+
emitFile(`${src}.json`, JSON.stringify(configObj, null, ' '))
166+
}
167+
168+
// 生成入口 js
169+
emitFile(`${src}.js`, genScript(name, isPage))
170+
171+
// 生成入口 wxss
172+
emitFile(`${src}.wxss`, genStyle(name, isPage))
173+
174+
// 这儿应该异步在所有的模块都清晰后再生成
175+
// 生成入口 wxml
176+
if (isPage && rootComponent) {
177+
resolve(context, rootComponent, (err, rootComponentSrc) => {
178+
if (err) return
179+
// 这儿需要搞定 根组件的 路径
180+
createWxml(emitWarning, emitError, emitFile, resourcePath, rootComponentSrc)
181+
})
182+
}
183+
}
184+
185+
return content
186+
}
187+
188+
module.exports = { compileWxml, compileMPScript, compileMP }

0 commit comments

Comments
 (0)