feat: add mp-webpack-plugin2
This commit is contained in:
parent
41b4d799da
commit
2487db8d79
|
@ -0,0 +1,13 @@
|
|||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
'extends': [
|
||||
path.resolve(__dirname, '../../.eslintrc.js'),
|
||||
],
|
||||
'rules': {
|
||||
'import/no-unresolved': 'off',
|
||||
},
|
||||
'globals': {
|
||||
'init': true,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# 更新日志
|
||||
|
||||
## next version
|
|
@ -0,0 +1,15 @@
|
|||
# mp-webpack-plugin
|
||||
|
||||
## 介绍
|
||||
|
||||
一个用于将 vue 组件转化成小程序代码的 webpack 插件。
|
||||
|
||||
## 安装
|
||||
|
||||
```
|
||||
npm install --save-dev mp-webpack-plugin
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
[查看文档](https://github.com/wechat-miniprogram/kbone/blob/master/docs/quickstart.md)。
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"_from": "mp-webpack-plugin2",
|
||||
"_id": "mp-webpack-plugin2@0.0.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-6hBKApXShnJropp93oV5/z6mnO+1+pG/n7o0nzOY3W80U+8YtcAO19nGXQmVt6yYlcDIR5qTlbfxduv+hGz6Kw==",
|
||||
"_location": "/mp-webpack-plugin2",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "tag",
|
||||
"registry": true,
|
||||
"raw": "mp-webpack-plugin2",
|
||||
"name": "mp-webpack-plugin2",
|
||||
"escapedName": "mp-webpack-plugin2",
|
||||
"rawSpec": "",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "latest"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/mp-webpack-plugin2/-/mp-webpack-plugin2-0.0.1.tgz",
|
||||
"_shasum": "5719cd7fa9fd48af3f48df4503d0aca39e50bfee",
|
||||
"_spec": "mp-webpack-plugin2",
|
||||
"_where": "/Users/dntzhang/Documents/GitHub/omi",
|
||||
"author": {
|
||||
"name": "wechat-miniprogram"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/wechat-miniprogram/kbone/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"path-to-regexp": "^3.0.0",
|
||||
"postcss": "^7.0.17",
|
||||
"webpack": "^4.35.3",
|
||||
"webpack-sources": "^1.3.0"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "A tool for develop miniprogram.",
|
||||
"homepage": "https://github.com/wechat-miniprogram/kbone#readme",
|
||||
"license": "MIT",
|
||||
"main": "src/index.js",
|
||||
"miniprogram": "src",
|
||||
"name": "mp-webpack-plugin2",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wechat-miniprogram/kbone.git"
|
||||
},
|
||||
"scripts": {},
|
||||
"version": "0.0.1"
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const ConcatSource = require('webpack-sources').ConcatSource
|
||||
const ModuleFilenameHelpers = require('webpack/lib/ModuleFilenameHelpers')
|
||||
const {RawSource} = require('webpack-sources')
|
||||
const pathToRegexp = require('path-to-regexp')
|
||||
const adjustCss = require('./tool/adjust-css')
|
||||
const _ = require('./tool/utils')
|
||||
|
||||
const PluginName = 'MpPlugin'
|
||||
const pageJsTmpl = fs.readFileSync(path.resolve(__dirname, './tmpl/page.tmpl.js'), 'utf8')
|
||||
const appWxssTmpl = fs.readFileSync(path.resolve(__dirname, './tmpl/app.tmpl.wxss'), 'utf8')
|
||||
const projectConfigJsonTmpl = require('./tmpl/project.config.tmpl.json')
|
||||
const packageConfigJsonTmpl = require('./tmpl/package.tmpl.json')
|
||||
|
||||
process.env.isMiniprogram = true // 设置环境变量
|
||||
const globalVars = ['navigator', 'HTMLElement', 'localStorage', 'sessionStorage', 'location']
|
||||
|
||||
/**
|
||||
* 添加文件
|
||||
*/
|
||||
function addFile(compilation, filename, content) {
|
||||
compilation.assets[filename] = {
|
||||
source: () => content,
|
||||
size: () => Buffer.from(content).length,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 chunk 头尾追加内容
|
||||
*/
|
||||
function wrapChunks(compilation, chunks) {
|
||||
chunks.forEach(chunk => {
|
||||
chunk.files.forEach(fileName => {
|
||||
if (ModuleFilenameHelpers.matchObject({test: /\.js$/}, fileName)) {
|
||||
const headerContent = 'module.exports = function(window, document) {' + globalVars.map(item => `var ${item} = window.${item}`).join(';') + ';'
|
||||
const footerContent = '}'
|
||||
|
||||
compilation.assets[fileName] = new ConcatSource(headerContent, compilation.assets[fileName], footerContent)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
class MpPlugin {
|
||||
constructor(options) {
|
||||
this.options = options
|
||||
}
|
||||
|
||||
apply(compiler) {
|
||||
const options = this.options
|
||||
|
||||
// 补充其他文件输出
|
||||
compiler.hooks.emit.tapAsync(PluginName, (compilation, callback) => {
|
||||
const entryNames = Array.from(compilation.entrypoints.keys())
|
||||
|
||||
// 合并页面配置
|
||||
const globalConfig = options.global || {}
|
||||
const pageConfigMap = options.pages || {}
|
||||
entryNames.forEach(entryName => {
|
||||
pageConfigMap[entryName] = Object.assign({}, globalConfig, pageConfigMap[entryName] || {})
|
||||
})
|
||||
|
||||
for (const entryName of entryNames) {
|
||||
const assets = {
|
||||
js: [],
|
||||
css: [],
|
||||
}
|
||||
|
||||
const filePathMap = {}
|
||||
const extRegex = /\.(css|js|wxss)(\?|$)/
|
||||
const entryFiles = compilation.entrypoints.get(entryName).getFiles()
|
||||
|
||||
entryFiles.forEach(filePath => {
|
||||
// 跳过非 css 和 js
|
||||
const extMatch = extRegex.exec(filePath)
|
||||
if (!extMatch) return
|
||||
|
||||
// 跳过已记录的
|
||||
if (filePathMap[filePath]) return
|
||||
filePathMap[filePath] = true
|
||||
|
||||
// 记录
|
||||
let ext = extMatch[1]
|
||||
ext = ext === 'wxss' ? 'css' : ext
|
||||
assets[ext].push(filePath)
|
||||
|
||||
// 调整 css 内容
|
||||
if (ext === 'css') {
|
||||
compilation.assets[filePath] = new RawSource(adjustCss(compilation.assets[filePath].source()))
|
||||
}
|
||||
})
|
||||
|
||||
const addPageScroll = pageConfigMap[entryName] && pageConfigMap[entryName].windowScroll
|
||||
const pageBackgroundColor = pageConfigMap[entryName] && pageConfigMap[entryName].backgroundColor
|
||||
const reachBottom = pageConfigMap[entryName] && pageConfigMap[entryName].reachBottom
|
||||
const reachBottomDistance = pageConfigMap[entryName] && pageConfigMap[entryName].reachBottomDistance
|
||||
const pullDownRefresh = pageConfigMap[entryName] && pageConfigMap[entryName].pullDownRefresh
|
||||
|
||||
// 页面 js
|
||||
let pageJsContent = pageJsTmpl.replace('/* INIT_FUNCTION */', `function init(window, document) {${assets.js.map(js => 'require(\'../../common/' + js + '\')(window, document)').join(';')}}`)
|
||||
let pageScrollFunction = ''
|
||||
let reachBottomFunction = ''
|
||||
let pullDownRefreshFunction = ''
|
||||
if (addPageScroll) {
|
||||
pageScrollFunction = () => 'onPageScroll({ scrollTop }) {if (this.window) {this.window.document.documentElement.scrollTop = scrollTop || 0;this.window.$$trigger(\'scroll\');}},'
|
||||
}
|
||||
if (reachBottom) {
|
||||
reachBottomFunction = () => 'onReachBottom() {if (this.window) {this.window.$$trigger(\'reachbottom\');}},'
|
||||
}
|
||||
if (pullDownRefresh) {
|
||||
pullDownRefreshFunction = () => 'onPullDownRefresh() {if (this.window) {this.window.$$trigger(\'pulldownrefresh\');}},'
|
||||
}
|
||||
pageJsContent = pageJsContent.replace('/* PAGE_SCROLL_FUNCTION */', pageScrollFunction)
|
||||
pageJsContent = pageJsContent.replace('/* REACH_BOTTOM_FUNCTION */', reachBottomFunction)
|
||||
pageJsContent = pageJsContent.replace('/* PULL_DOWN_REFRESH_FUNCTION */', pullDownRefreshFunction)
|
||||
addFile(compilation, `../pages/${entryName}/index.js`, pageJsContent)
|
||||
|
||||
// 页面 wxml
|
||||
const pageWxmlContent = '<element wx:if="{{pageId}}" class="{{bodyClass}}" style="{{bodyStyle}}" data-private-node-id="e-body" data-private-page-id="{{pageId}}"></element>'
|
||||
addFile(compilation, `../pages/${entryName}/index.wxml`, pageWxmlContent)
|
||||
|
||||
// 页面 wxss
|
||||
let pageWxssContent = assets.css.map(css => `@import "../../common/${css}";`).join('\n')
|
||||
if (pageBackgroundColor) pageWxssContent = `page { background-color: ${pageBackgroundColor}; }\n` + pageWxssContent
|
||||
addFile(compilation, `../pages/${entryName}/index.wxss`, adjustCss(pageWxssContent))
|
||||
|
||||
// 页面 json
|
||||
const pageJson = {
|
||||
usingComponents: {
|
||||
element: 'miniprogram-element'
|
||||
}
|
||||
}
|
||||
if (reachBottom && typeof reachBottomDistance === 'number') {
|
||||
pageJson.onReachBottomDistance = reachBottomDistance
|
||||
}
|
||||
if (pullDownRefresh) {
|
||||
pageJson.enablePullDownRefresh = pullDownRefresh
|
||||
}
|
||||
const pageJsonContent = JSON.stringify(pageJson, null, '\t')
|
||||
addFile(compilation, `../pages/${entryName}/index.json`, pageJsonContent)
|
||||
}
|
||||
|
||||
const pages = entryNames.map(entryName => `pages/${entryName}/index`)
|
||||
|
||||
// 追加 webview 页面
|
||||
if (options.redirect && (options.redirect.notFound === 'webview' || options.redirect.accessDenied === 'webview')) {
|
||||
addFile(compilation, '../pages/webview/index.js', 'Page({data:{url:\'\'},onLoad: function(query){this.setData({url:decodeURIComponent(query.url)})}})')
|
||||
addFile(compilation, '../pages/webview/index.wxml', '<web-view src="{{url}}"></web-view>')
|
||||
addFile(compilation, '../pages/webview/index.wxss', '')
|
||||
addFile(compilation, '../pages/webview/index.json', '{"usingComponents":{}}')
|
||||
pages.push('pages/webview/index')
|
||||
}
|
||||
|
||||
// app js
|
||||
const appJsContent = 'App({})'
|
||||
addFile(compilation, '../app.js', appJsContent)
|
||||
|
||||
// app wxss
|
||||
const appWxssContent = appWxssTmpl
|
||||
addFile(compilation, '../app.wxss', adjustCss(appWxssContent))
|
||||
|
||||
// app json
|
||||
const userAppJson = options.appExtraConfig || {}
|
||||
const appJsonContent = JSON.stringify({
|
||||
pages,
|
||||
window: options.app || {},
|
||||
...userAppJson,
|
||||
}, null, '\t')
|
||||
addFile(compilation, '../app.json', appJsonContent)
|
||||
|
||||
// config js
|
||||
const router = {}
|
||||
if (options.router) {
|
||||
// 处理 router
|
||||
Object.keys(options.router).forEach(key => {
|
||||
const pathObjList = []
|
||||
let pathList = options.router[key]
|
||||
pathList = Array.isArray(pathList) ? pathList : [pathList]
|
||||
|
||||
for (const pathItem of pathList) {
|
||||
// 将每个 route 转成正则并进行序列化
|
||||
if (!pathItem || typeof pathItem !== 'string') continue
|
||||
|
||||
const keys = []
|
||||
const regexp = pathToRegexp(pathItem, keys)
|
||||
const pattern = regexp.valueOf()
|
||||
|
||||
pathObjList.push({
|
||||
regexp: pattern.source,
|
||||
options: `${pattern.global ? 'g' : ''}${pattern.ignoreCase ? 'i' : ''}${pattern.multiline ? 'm' : ''}`,
|
||||
})
|
||||
}
|
||||
router[key] = pathObjList
|
||||
})
|
||||
}
|
||||
const configJsContent = 'module.exports = ' + JSON.stringify({
|
||||
origin: options.origin || 'https://miniprogram.default',
|
||||
entry: options.entry || '/',
|
||||
router,
|
||||
pages: pageConfigMap,
|
||||
redirect: options.redirect || {},
|
||||
optimization: options.optimization || {},
|
||||
}, null, '\t')
|
||||
addFile(compilation, '../config.js', configJsContent)
|
||||
|
||||
// project.config.json
|
||||
const userPorjectConfigJson = options.projectConfig || {}
|
||||
const projectConfigJson = Object.assign({}, projectConfigJsonTmpl)
|
||||
const projectConfigJsonContent = JSON.stringify(_.merge(projectConfigJson, userPorjectConfigJson), null, '\t')
|
||||
addFile(compilation, '../project.config.json', projectConfigJsonContent)
|
||||
|
||||
// package.json
|
||||
const userPackageConfigJson = options.packageConfig || {}
|
||||
const packageConfigJson = Object.assign({}, packageConfigJsonTmpl)
|
||||
const packageConfigJsonContent = JSON.stringify(_.merge(packageConfigJson, userPackageConfigJson), null, '\t')
|
||||
addFile(compilation, '../package.json', packageConfigJsonContent)
|
||||
|
||||
// sitemap.json
|
||||
const userSitemapConfigJson = options.sitemapConfig
|
||||
if (userSitemapConfigJson) {
|
||||
const sitemapConfigJsonContent = JSON.stringify(userSitemapConfigJson, null, '\t')
|
||||
addFile(compilation, '../sitemap.json', sitemapConfigJsonContent)
|
||||
}
|
||||
|
||||
// node_modules
|
||||
addFile(compilation, '../node_modules/.miniprogram', '')
|
||||
|
||||
callback()
|
||||
})
|
||||
|
||||
// 处理头尾追加内容
|
||||
compiler.hooks.compilation.tap(PluginName, compilation => {
|
||||
if (this.afterOptimizations) {
|
||||
compilation.hooks.afterOptimizeChunkAssets.tap(PluginName, chunks => {
|
||||
wrapChunks(compilation, chunks)
|
||||
})
|
||||
} else {
|
||||
compilation.hooks.optimizeChunkAssets.tapAsync(PluginName, (chunks, callback) => {
|
||||
wrapChunks(compilation, chunks)
|
||||
callback()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MpPlugin
|
|
@ -0,0 +1,415 @@
|
|||
body {
|
||||
display: block;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
-webkit-margin-before: 1em;
|
||||
-webkit-margin-after: 1em;
|
||||
-webkit-margin-start: 0;
|
||||
-webkit-margin-end: 0;
|
||||
}
|
||||
|
||||
address, article, aside, div, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
display: block;
|
||||
-webkit-margin-before: 1em;
|
||||
-webkit-margin-after: 1em;
|
||||
-webkit-margin-start: 40px;
|
||||
-webkit-margin-end: 40px;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
display: block;
|
||||
}
|
||||
|
||||
figure {
|
||||
display: block;
|
||||
-webkit-margin-before: 1em;
|
||||
-webkit-margin-after: 1em;
|
||||
-webkit-margin-start: 40px;
|
||||
-webkit-margin-end: 40px;
|
||||
}
|
||||
|
||||
q {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
center {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
-webkit-margin-before: 0.5em;
|
||||
-webkit-margin-after: 0.5em;
|
||||
-webkit-margin-start: auto;
|
||||
-webkit-margin-end: auto;
|
||||
border-style: inset;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
video {
|
||||
object-fit: contain;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
/* heading elements */
|
||||
|
||||
h1 {
|
||||
display: block;
|
||||
font-size: 2em;
|
||||
-webkit-margin-before: 0.67em;
|
||||
-webkit-margin-after: 0.67em;
|
||||
-webkit-margin-start: 0;
|
||||
-webkit-margin-end: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
display: block;
|
||||
font-size: 1.5em;
|
||||
-webkit-margin-before: 0.83em;
|
||||
-webkit-margin-after: 0.83em;
|
||||
-webkit-margin-start: 0;
|
||||
-webkit-margin-end: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h3 {
|
||||
display: block;
|
||||
font-size: 1.17em;
|
||||
-webkit-margin-before: 1em;
|
||||
-webkit-margin-after: 1em;
|
||||
-webkit-margin-start: 0;
|
||||
-webkit-margin-end: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h4 {
|
||||
display: block;
|
||||
-webkit-margin-before: 1.33em;
|
||||
-webkit-margin-after: 1.33em;
|
||||
-webkit-margin-start: 0;
|
||||
-webkit-margin-end: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h5 {
|
||||
display: block;
|
||||
font-size: .83em;
|
||||
-webkit-margin-before: 1.67em;
|
||||
-webkit-margin-after: 1.67em;
|
||||
-webkit-margin-start: 0;
|
||||
-webkit-margin-end: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h6 {
|
||||
display: block;
|
||||
font-size: .67em;
|
||||
-webkit-margin-before: 2.33em;
|
||||
-webkit-margin-after: 2.33em;
|
||||
-webkit-margin-start: 0;
|
||||
-webkit-margin-end: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* tables */
|
||||
|
||||
table {
|
||||
display: table;
|
||||
border-collapse: separate;
|
||||
border-spacing: 2px;
|
||||
border-color: gray;
|
||||
}
|
||||
|
||||
thead {
|
||||
display: table-header-group;
|
||||
vertical-align: middle;
|
||||
border-color: inherit;
|
||||
}
|
||||
|
||||
tbody {
|
||||
display: table-row-group;
|
||||
vertical-align: middle;
|
||||
border-color: inherit;
|
||||
}
|
||||
|
||||
tfoot {
|
||||
display: table-footer-group;
|
||||
vertical-align: middle;
|
||||
border-color: inherit;
|
||||
}
|
||||
|
||||
/* for tables without table section elements (can happen with XHTML or dynamically created tables) */
|
||||
table > tr {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
col {
|
||||
display: table-column;
|
||||
}
|
||||
|
||||
colgroup {
|
||||
display: table-column-group;
|
||||
}
|
||||
|
||||
tr {
|
||||
display: table-row;
|
||||
vertical-align: inherit;
|
||||
border-color: inherit;
|
||||
}
|
||||
|
||||
td, th {
|
||||
display: table-cell;
|
||||
vertical-align: inherit;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
caption {
|
||||
display: table-caption;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* lists */
|
||||
|
||||
ul, menu, dir {
|
||||
display: block;
|
||||
list-style-type: disc;
|
||||
-webkit-margin-before: 1em;
|
||||
-webkit-margin-after: 1em;
|
||||
-webkit-margin-start: 0;
|
||||
-webkit-margin-end: 0;
|
||||
-webkit-padding-start: 40px;
|
||||
}
|
||||
|
||||
ol {
|
||||
display: block;
|
||||
list-style-type: decimal;
|
||||
-webkit-margin-before: 1em;
|
||||
-webkit-margin-after: 1em;
|
||||
-webkit-margin-start: 0;
|
||||
-webkit-margin-end: 0;
|
||||
-webkit-padding-start: 40px;
|
||||
}
|
||||
|
||||
li {
|
||||
display: list-item;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
dd {
|
||||
display: block;
|
||||
-webkit-margin-start: 40px;
|
||||
}
|
||||
|
||||
dl {
|
||||
display: block;
|
||||
-webkit-margin-before: 1em;
|
||||
-webkit-margin-after: 1em;
|
||||
-webkit-margin-start: 0;
|
||||
-webkit-margin-end: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* form elements */
|
||||
|
||||
form {
|
||||
display: block;
|
||||
margin-top: 0em;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
-webkit-padding-start: 2px;
|
||||
-webkit-padding-end: 2px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
display: block;
|
||||
-webkit-margin-start: 2px;
|
||||
-webkit-margin-end: 2px;
|
||||
-webkit-padding-before: 0.35em;
|
||||
-webkit-padding-start: 0.75em;
|
||||
-webkit-padding-end: 0.75em;
|
||||
-webkit-padding-after: 0.625em;
|
||||
border: 2px groove ThreeDFace;
|
||||
min-width: min-content;
|
||||
}
|
||||
|
||||
/* Form controls don't go vertical. */
|
||||
input, textarea, keygen, select, button, progress {
|
||||
-webkit-writing-mode: horizontal-tb !important;
|
||||
}
|
||||
|
||||
input, textarea, keygen, select, button {
|
||||
margin: 0em;
|
||||
font: -webkit-small-control;
|
||||
color: initial;
|
||||
letter-spacing: normal;
|
||||
word-spacing: normal;
|
||||
line-height: normal;
|
||||
text-transform: none;
|
||||
text-indent: 0;
|
||||
text-shadow: none;
|
||||
display: inline-block;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
datalist {
|
||||
display: none;
|
||||
}
|
||||
|
||||
keygen, select {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
area, param {
|
||||
display: none;
|
||||
}
|
||||
|
||||
select {
|
||||
box-sizing: border-box;
|
||||
letter-spacing: normal;
|
||||
word-spacing: normal;
|
||||
line-height: normal;
|
||||
border: 1px solid #4c4c4c;
|
||||
/* We want to be as close to background:transparent as possible without actually being transparent */
|
||||
background-color: rgba(255, 255, 255, 0.01);
|
||||
font: 11px Helvetica;
|
||||
padding: 0 0.4em 0 0.4em;
|
||||
border: 1px solid;
|
||||
color: text;
|
||||
background-color: -apple-system-control-background;
|
||||
color: black;
|
||||
background-color: white;
|
||||
align-items: center;
|
||||
white-space: pre;
|
||||
-webkit-rtl-ordering: logical;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
optgroup {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
option {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* inline elements */
|
||||
|
||||
u, ins {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
strong, b {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
i, cite, em, var, address, dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
tt, code, kbd, samp {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: yellow;
|
||||
color: black;
|
||||
}
|
||||
|
||||
big {
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
s, strike, del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
sub {
|
||||
vertical-align: sub;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
sup {
|
||||
vertical-align: super;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
/* other elements */
|
||||
|
||||
iframe {
|
||||
border: 2px inset;
|
||||
}
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
bdi, output {
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
|
||||
bdo {
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
|
||||
img {
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容标签样式
|
||||
*/
|
||||
img {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
br {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
a, abbr, b, button, code, i, label, small, span, strong, time {
|
||||
display: inline;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "miniprogram-project",
|
||||
"version": "0.0.1",
|
||||
"description": "miniprogram project",
|
||||
"dependencies": {
|
||||
"miniprogram-render": "latest",
|
||||
"miniprogram-element": "latest"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
const mp = require('miniprogram-render')
|
||||
const config = require('../../config')
|
||||
|
||||
/* INIT_FUNCTION */
|
||||
|
||||
/**
|
||||
* 处理一些特殊的页面
|
||||
*/
|
||||
function dealWithPage(evt, window, value) {
|
||||
const type = evt.type
|
||||
let url = evt.url
|
||||
|
||||
if (value === 'webview') {
|
||||
// 补全 url
|
||||
url = mp.$$adapter.tool.completeURL(url, window.location.origin)
|
||||
|
||||
const options = {url: `/pages/webview/index?url=${encodeURIComponent(url)}`}
|
||||
if (type === 'jump') wx.redirectTo(options)
|
||||
else if (type === 'open') wx.navigateTo(options)
|
||||
} else if (value === 'error') {
|
||||
console.error(`page not found: ${evt.url}`)
|
||||
} else if (value !== 'none') {
|
||||
const targeturl = `${window.location.origin}/redirect?url=${encodeURIComponent(url)}`
|
||||
const options = {url: `/pages/${value}/index?type=${type}&targeturl=${encodeURIComponent(targeturl)}`}
|
||||
if (type === 'jump') wx.redirectTo(options)
|
||||
else if (type === 'open') wx.navigateTo(options)
|
||||
}
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
pageId: '',
|
||||
bodyClass: 'h5-body miniprogram-root',
|
||||
bodyStyle: '',
|
||||
},
|
||||
onLoad(query) {
|
||||
const pageName = mp.$$adapter.tool.getPageName(this.route)
|
||||
const pageConfig = this.pageConfig = config.pages[pageName] || {}
|
||||
|
||||
if (pageConfig.loadingText) {
|
||||
wx.showLoading({
|
||||
title: pageConfig.loadingText,
|
||||
mask: true,
|
||||
})
|
||||
}
|
||||
|
||||
const mpRes = mp.createPage(this.route, config)
|
||||
this.pageId = mpRes.pageId
|
||||
this.window = mpRes.window
|
||||
this.document = mpRes.document
|
||||
this.query = query
|
||||
|
||||
init(this.window, this.document)
|
||||
|
||||
// 处理跳转页面不存在的情况
|
||||
if (config.redirect && config.redirect.notFound) {
|
||||
this.window.addEventListener('pagenotfound', evt => {
|
||||
dealWithPage(evt, mpRes.window, config.redirect.notFound)
|
||||
})
|
||||
}
|
||||
|
||||
// 处理跳转受限制页面的情况
|
||||
if (config.redirect && config.redirect.accessDenied) {
|
||||
this.window.addEventListener('pageaccessdenied', evt => {
|
||||
dealWithPage(evt, mpRes.window, config.redirect.accessDenied)
|
||||
})
|
||||
}
|
||||
|
||||
if (query.type === 'open' || query.type === 'jump' || query.type === 'share') {
|
||||
// 处理页面参数,只有当页面是其他页面打开或跳转时才处理
|
||||
this.window.$$miniprogram.init(query.targeturl ? decodeURIComponent(query.targeturl) : null)
|
||||
|
||||
if (query.search) this.window.location.search = decodeURIComponent(query.search)
|
||||
if (query.hash) this.window.location.hash = decodeURIComponent(query.hash)
|
||||
} else {
|
||||
this.window.$$miniprogram.init()
|
||||
}
|
||||
|
||||
// 处理分享显示
|
||||
if (!pageConfig.share) {
|
||||
wx.hideShareMenu()
|
||||
}
|
||||
|
||||
// 处理 body 更新
|
||||
this.document.documentElement.addEventListener('$$childNodesUpdate', () => {
|
||||
const domNode = this.document.body
|
||||
const data = {
|
||||
bodyClass: `${domNode.className || ''} h5-body miniprogram-root`, // 增加默认 class
|
||||
bodyStyle: domNode.style.cssText || ''
|
||||
}
|
||||
|
||||
if (data.bodyClass !== this.data.bodyClass || data.bodyStyle !== this.data.bodyStyle) {
|
||||
this.setData(data)
|
||||
}
|
||||
})
|
||||
|
||||
// 处理 selectorQuery 获取
|
||||
this.window.$$createSelectorQuery = () => wx.createSelectorQuery().in(this)
|
||||
|
||||
// 处理 intersectionObserver 获取
|
||||
this.window.$$createIntersectionObserver = options => wx.createIntersectionObserver(this, options)
|
||||
|
||||
this.setData({
|
||||
pageId: this.pageId
|
||||
})
|
||||
this.app = this.window.createApp()
|
||||
},
|
||||
onShow() {
|
||||
// 方便调试
|
||||
global.$$runtime = {
|
||||
window: this.window,
|
||||
document: this.document,
|
||||
}
|
||||
},
|
||||
onReady() {
|
||||
if (this.pageConfig.loadingText) wx.hideLoading()
|
||||
},
|
||||
onHide() {
|
||||
global.$$runtime = null
|
||||
},
|
||||
onUnload() {
|
||||
this.window.$$trigger('beforeunload')
|
||||
this.app && this.app.$destroy && this.app.$destroy()
|
||||
this.document.body.$$recycle() // 回收 dom 节点
|
||||
|
||||
mp.destroyPage(this.pageId)
|
||||
global.$$runtime = null
|
||||
|
||||
this.pageConfig = null
|
||||
this.pageId = null
|
||||
this.window = null
|
||||
this.document = null
|
||||
this.app = null
|
||||
this.query = null
|
||||
},
|
||||
onShareAppMessage(data) {
|
||||
if (this.window.onShareAppMessage) {
|
||||
const shareOptions = this.window.onShareAppMessage(data)
|
||||
const query = Object.assign({}, this.query || {})
|
||||
|
||||
if (shareOptions.path) {
|
||||
query.targeturl = encodeURIComponent(shareOptions.path)
|
||||
} else {
|
||||
// 组装当前页面路径
|
||||
const location = this.window.location
|
||||
|
||||
query.targeturl = encodeURIComponent(location.href)
|
||||
query.search = encodeURIComponent(location.search)
|
||||
query.hash = encodeURIComponent(location.hash)
|
||||
}
|
||||
|
||||
query.type = 'share'
|
||||
const queryString = Object.keys(query).map(key => `${key}=${query[key] || ''}`).join('&')
|
||||
const currentPagePath = `${this.route}?${queryString}`
|
||||
shareOptions.path = currentPagePath
|
||||
|
||||
return shareOptions
|
||||
}
|
||||
},
|
||||
/* PAGE_SCROLL_FUNCTION */
|
||||
/* REACH_BOTTOM_FUNCTION */
|
||||
/* PULL_DOWN_REFRESH_FUNCTION */
|
||||
})
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"description": "项目配置文件",
|
||||
"packOptions": {
|
||||
"ignore": []
|
||||
},
|
||||
"setting": {
|
||||
"urlCheck": false,
|
||||
"es6": true,
|
||||
"enhance": true,
|
||||
"postcss": true,
|
||||
"minified": true,
|
||||
"newFeature": true,
|
||||
"nodeModules": true,
|
||||
"autoAudits": false,
|
||||
"uglifyFileName": true,
|
||||
"checkInvalidKey": true,
|
||||
"checkSiteMap": true,
|
||||
"uploadWithSourceMap": true,
|
||||
"babelSetting": {
|
||||
"ignore": [
|
||||
"common/*"
|
||||
],
|
||||
"disablePlugins": [],
|
||||
"outputPath": ""
|
||||
}
|
||||
},
|
||||
"compileType": "miniprogram",
|
||||
"libVersion": "2.7.4",
|
||||
"appid": "",
|
||||
"projectname": "kbone-project",
|
||||
"debugOptions": {
|
||||
"hidedInDevtools": [
|
||||
{
|
||||
"type": "file",
|
||||
"value": "WAService.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"isGameTourist": false,
|
||||
"simulatorType": "wechat",
|
||||
"simulatorPluginLibVersion": {},
|
||||
"condition": {
|
||||
"search": {
|
||||
"current": -1,
|
||||
"list": []
|
||||
},
|
||||
"conversation": {
|
||||
"current": -1,
|
||||
"list": []
|
||||
},
|
||||
"game": {
|
||||
"currentL": -1,
|
||||
"list": []
|
||||
},
|
||||
"miniprogram": {
|
||||
"current": -1,
|
||||
"list": []
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
const postcss = require('postcss')
|
||||
const tagList = require('./tag-list')
|
||||
|
||||
const replaceRegexp = new RegExp(`(\\W|\\b)(${['html', ...tagList].join('|')})(\\W|\\b)`, 'ig')
|
||||
const prefixRegexp = /[a-zA-Z0-9:.#_-]/
|
||||
const suffixRegexp = /[a-zA-Z0-9_-]/
|
||||
|
||||
/**
|
||||
* 替换标签名
|
||||
*/
|
||||
const replaceTagNamePlugin = postcss.plugin('replaceTagName', () => root => {
|
||||
root.walk(child => {
|
||||
if (child.type === 'rule') {
|
||||
const selectors = []
|
||||
|
||||
child.selectors.forEach(selector => {
|
||||
// 处理标签名选择器
|
||||
selector = selector.replace(replaceRegexp, (all, $1, tagName, $2, offset, string) => {
|
||||
// 非标签选择器,调整 \b 匹配的情况
|
||||
let start = $1
|
||||
let end = $2
|
||||
if (!start) start = string[offset - 1] || ''
|
||||
if (!end) end = string[offset + all.length] || ''
|
||||
|
||||
if (prefixRegexp.test(start) || suffixRegexp.test(end)) {
|
||||
// 非标签选择器
|
||||
return all
|
||||
}
|
||||
|
||||
tagName = tagName.toLowerCase()
|
||||
|
||||
if (tagName === 'html') {
|
||||
// 页面单独处理
|
||||
return `${$1}page${$2}`
|
||||
} else if (tagName) {
|
||||
// 其他用原本的标签名
|
||||
return `${$1}.h5-${tagName}${$2}`
|
||||
} else {
|
||||
return all
|
||||
}
|
||||
})
|
||||
|
||||
// 处理 * 号选择器
|
||||
selector = selector.replace(/(.*)\*(.*)/g, (all, $1, $2) => {
|
||||
if ($2[0] === '=') return all
|
||||
|
||||
tagList.forEach(tagName => selectors.push(`${$1}.h5-${tagName}${$2}`))
|
||||
|
||||
selectors.push(`${$1}page${$2}`)
|
||||
|
||||
return ''
|
||||
})
|
||||
|
||||
if (selector.trim()) selectors.push(selector)
|
||||
})
|
||||
|
||||
child.selectors = selectors
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = function(code) {
|
||||
code = postcss([replaceTagNamePlugin]).process(code, {
|
||||
from: undefined, // 主要是不想看到那个 warning
|
||||
map: null,
|
||||
})
|
||||
|
||||
return code.css
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
// css * 号选择器支持的标签列表
|
||||
module.exports = ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote', 'big', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'nav', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 's', 'samp', 'section', 'select', 'small', 'source', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tt', 'tr', 'track', 'u', 'ul', 'video', 'wbr', 'strike', 'details', 'summary']
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* 深合并对象
|
||||
*/
|
||||
function merge(to, from) {
|
||||
if (typeof to !== 'object' || typeof from !== 'object') return to
|
||||
|
||||
const fromKeys = Object.keys(from)
|
||||
for (const key of fromKeys) {
|
||||
const fromValue = from[key]
|
||||
const fromType = typeof fromValue
|
||||
const isFromArray = +Array.isArray(fromValue)
|
||||
const toValue = to[key]
|
||||
const toType = typeof toValue
|
||||
const isToArray = +Array.isArray(toValue)
|
||||
|
||||
// eslint-disable-next-line no-bitwise
|
||||
if (fromType !== toType || (isFromArray ^ isToArray)) {
|
||||
// 不同类型
|
||||
to[key] = fromValue
|
||||
} else {
|
||||
// 相同类型
|
||||
// eslint-disable-next-line no-lonely-if
|
||||
if (isFromArray) {
|
||||
fromValue.forEach(item => toValue.push(item))
|
||||
} else if (fromType === 'object') {
|
||||
to[key] = merge(toValue, fromValue)
|
||||
} else {
|
||||
to[key] = fromValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return to
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
merge,
|
||||
}
|
Loading…
Reference in New Issue