Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

添加对vue filters过滤器的支持 #45

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 95 additions & 9 deletions lib/mp-compiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ const path = require('path')
const fs = require('fs')
const deepEqual = require('deep-equal')

const { parseConfig, parseComponentsDeps, parseGlobalComponents, clearGlobalComponents } = require('./parse')
const { parseConfig, parseComponentsDeps, parseGlobalComponents, clearGlobalComponents, parseMixinsDeps } = require('./parse')
const { parseComponentsDeps: parseComponentsDepsTs } = require('./parse-ts')
const { genPageWxml } = require('./templates')
const { extractScriptFilters, combineScriptAndMixinsFilters } = require('../script-compiler')

const {
cacheFileInfo,
getFileInfo,
getCompNameAndSrc,
getFiltersOutputSrc,
resolveTarget,
covertCCVar,
cacheSlots,
Expand Down Expand Up @@ -57,7 +59,7 @@ const cacheCreateWxmlFns = {}

function createWxml ({ emitWarning, emitError, emitFile, resourcePath, context, compiled }) {
cacheCreateWxmlFns[resourcePath] = arguments
const { pageType, moduleId, components } = getFileInfo(resourcePath) || {}
const { pageType, moduleId, components, filters } = getFileInfo(resourcePath) || {}

// TODO, 这儿传 options 进去
// {
Expand All @@ -69,7 +71,7 @@ function createWxml ({ emitWarning, emitError, emitFile, resourcePath, context,
// moduleId: 'moduleId'
// }
const { name, filePath: wxmlSrc } = getCompNameAndSrc(context, resourcePath)
const options = { components, pageType, name, moduleId }
const options = { components, pageType, name, moduleId, filters }
const wxmlContent = genComponentWxml(compiled, options, emitFile, emitError, emitWarning)
emitFile(wxmlSrc, wxmlContent)
}
Expand All @@ -91,8 +93,8 @@ function compileWxml (compiled, html) {
}
return new Promise(resolve => {
const pollComponentsStatus = () => {
const { pageType, components } = getFileInfo(this.resourcePath) || {}
if (!pageType || (components && !components.isCompleted)) {
const { pageType, components, filters } = getFileInfo(this.resourcePath) || {}
if (!pageType || (components && !components.isCompleted) || (filters && !filters.isCompleted)) {
setTimeout(pollComponentsStatus, 20)
} else {
resolve()
Expand All @@ -116,11 +118,11 @@ function compileWxml (compiled, html) {
// 针对 .vue 单文件的脚本逻辑的处理
// 处理出当前单文件组件的子组件依赖
function compileMPScript (script, mpOptioins, moduleId) {
const { resourcePath, options, resolve, context } = this
const { resourcePath, options, resolve, context, emitFile } = this
const babelrc = getBabelrc(mpOptioins.globalBabelrc)
let result, metadata
let scriptContent = script.content
const babelOptions = { extends: babelrc, plugins: [parseComponentsDeps] }
const babelOptions = { extends: babelrc, plugins: [parseComponentsDeps, parseMixinsDeps] }
if (script.src) { // 处理src
const scriptpath = path.join(path.dirname(resourcePath), script.src)
scriptContent = fs.readFileSync(scriptpath).toString()
Expand All @@ -133,11 +135,12 @@ function compileMPScript (script, mpOptioins, moduleId) {
}

// metadata: importsMap, components
const { importsMap, components: originComponents } = metadata
const { importsMap, components: originComponents, mixins } = metadata

const fileInfo = resolveTarget(resourcePath, options.entry)

// 处理子组件的信息
const components = {}
const fileInfo = resolveTarget(resourcePath, options.entry)
if (originComponents) {
resolveSrc(originComponents, components, resolve, context, options.context).then(() => {
resolveComponent(resourcePath, fileInfo, importsMap, components, moduleId)
Expand All @@ -149,6 +152,21 @@ function compileMPScript (script, mpOptioins, moduleId) {
resolveComponent(resourcePath, fileInfo, importsMap, components, moduleId)
}

// 处理filters信息
const mixinsFilters = []
if (mixins) {
// 包含外部mixins引用
resolveMixinsFilters(mixins, mixinsFilters, resolve, context, options.context, babelOptions, emitFile).then(() => {
resolveFilters(resourcePath, fileInfo, scriptContent, babelOptions, mixinsFilters, context, options.context, emitFile)
}).catch(err => {
console.error(err)
resolveFilters(resourcePath, fileInfo, scriptContent, babelOptions, mixinsFilters, context, options.context, emitFile)
})
} else {
// 不包外部mixins引用,则直接获取脚本中的filters信息
resolveFilters(resourcePath, fileInfo, scriptContent, babelOptions, mixinsFilters, context, options.context, emitFile)
}

return script
}

Expand Down Expand Up @@ -247,4 +265,72 @@ function resolveComponent (resourcePath, fileInfo, importsMap, localComponents,
}
}

// 创建wxs文件
function createWxs (resourcePath, wxsContent, context, emitFile) {
const { filePath: wxsSrc } = getFiltersOutputSrc(context, resourcePath)
emitFile(wxsSrc, wxsContent)
}

// 分析mixins解析外部引用Filters
const cacheResolveMixinsFilters = {}

function resolveMixinsFilters (mixins, mixinsFilters, resolveFn, context, projectRoot, babelOptions, emitFile) {
return Promise.all(Object.keys(mixins).map(k => {
return new Promise((resolve, reject) => {
resolveFn(context, mixins[k], (err, realSrc) => {
if (err) return reject(err)
if (cacheResolveMixinsFilters[realSrc]) {
// 之前解析过该组件
mixinsFilters.push(realSrc)
} else {
// 之前未解析过该组件
// 读取文件
const mixinsFileSource = fs.readFileSync(realSrc).toString()
// 提取filters
const mixinsFileFilter = extractScriptFilters(mixinsFileSource, babelOptions)
if (mixinsFileFilter) {
mixinsFilters.push(realSrc)
cacheResolveMixinsFilters[realSrc] = mixinsFileFilter
// 创建引用的wxs文件
createWxs(realSrc, mixinsFileFilter.code, projectRoot, emitFile)
}
}
resolve()
})
})
}))
}

// 解析Filters
function resolveFilters (resourcePath, fileInfo, scriptContent, babelOptions, mixinsFilters, context, projectRoot, emitFile) {
const resourceSrcPath = getFiltersOutputSrc(projectRoot, resourcePath)
const mixinsFiltersArray = []
mixinsFilters.forEach((item) => {
// 计算相对路径,由于wxs里require只支持相对路径,微信真渣!
const itemSrcPath = getFiltersOutputSrc(projectRoot, item)
const realtivePath = path.join(path.relative(path.dirname(resourceSrcPath.filePath), path.dirname(itemSrcPath.filePath)), path.basename(itemSrcPath.filePath))
mixinsFiltersArray.push({
filePath: item,
name: itemSrcPath.name,
extractFilter: cacheResolveMixinsFilters[item],
realtivePath: realtivePath
})
})
// 提取脚本内的filters
const scriptFilter = extractScriptFilters(scriptContent, babelOptions)
// 合并外部引用和脚本内的filters
const combineFilter = combineScriptAndMixinsFilters(scriptFilter, mixinsFiltersArray, babelOptions)
// 保存文件信息
if (combineFilter) {
const filters = {
isCompleted: true,
src: path.join('./', path.basename(resourceSrcPath.filePath)),
module: resourceSrcPath.name
}
cacheFileInfo(resourcePath, fileInfo, { filters })
// 创建引用的wxs文件
createWxs(resourcePath, combineFilter.code, projectRoot, emitFile)
}
}

module.exports = { compileWxml, compileMPScript, compileMP }
30 changes: 29 additions & 1 deletion lib/mp-compiler/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,32 @@ function parseGlobalComponents (babel) {
function clearGlobalComponents () {
globalComponents = {}
}
module.exports = { parseConfig, parseComponentsDeps, parseGlobalComponents, clearGlobalComponents }

function parseMixinsDeps (babel) {
return {
visitor: {
ExportDefaultDeclaration (path) {
path.traverse({
Property: function (path) {
if (path.node.key.name !== 'mixins') {
return
}
path.stop()
const { metadata } = path.hub.file
const { importsMap } = getImportsMap(metadata)
// 找到所有的 imports
const { elements } = path.node.value
const mixins = {}
elements.forEach(p => {
const k = p.name
mixins[k] = importsMap[k]
})
metadata.mixins = mixins
}
})
}
}
}
}

module.exports = { parseConfig, parseComponentsDeps, parseGlobalComponents, clearGlobalComponents, parseMixinsDeps }
11 changes: 11 additions & 0 deletions lib/mp-compiler/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ function getCompNameAndSrc (context, file) {
}
}

function getFiltersOutputSrc (context, file) {
const filePath = `/${resolveSrc(context, file)}.wxs`
const name = (path.basename(file) + hash(file)).replace(/[^\d|\w]/g, '')
return {
filePath,
name: name
}
}

// 根据路径获得组件名
function getNameByFile (dir) {
// const arr = dir.match(/[pages?/components?]\/(.*?)(\/)/)
Expand Down Expand Up @@ -134,6 +143,7 @@ const defaultPart = type => {
names: [],
mappings: '',
sourcesContent: []
}
}
}

Expand All @@ -142,6 +152,7 @@ module.exports = {
cacheFileInfo,
getFileInfo,
getCompNameAndSrc,
getFiltersOutputSrc,
resolveTarget,
covertCCVar,
cacheSlots,
Expand Down
54 changes: 54 additions & 0 deletions lib/script-compiler/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const babel = require('babel-core')
const extractFiltersBabelPlugins = require('./plugins/babel-plugin-extract-filters')
const combineFiltersBabelPlugins = require('./plugins/babel-plugin-combine-filters')

function extractScriptFilters (scriptContent, babelOptions) {
babelOptions.plugins = []
babelOptions.plugins.push(extractFiltersBabelPlugins)
babelOptions.comments = false
const result = babel.transform(scriptContent, babelOptions)
result.code = result.code.trim()
if (result.code) {
// 遍历解析处的filter,复制filter包含哪些函数
const filtersFuncArray = []
babel.traverse(result.ast, {
Program: function (path) {
path.stop()
const filters = path.get('body.0.expression.right.properties')
if (filters.length && filters.length > 0) {
filters.forEach((filter) => {
filtersFuncArray.push(filter.get('key').node.name)
})
}
}
})
result.filtersFuncArray = filtersFuncArray
return result
} else {
return null
}
}

function combineScriptAndMixinsFilters (scriptFilter, mixinsFiltersArray, babelOptions) {
if (scriptFilter === null && (mixinsFiltersArray === null || mixinsFiltersArray.length === 0)) {
return null
}
if (scriptFilter === null && mixinsFiltersArray.length > 0) {
scriptFilter = {
code: 'module.exports={\n_empty:null\n};'
}
}
// 包含脚本内的filters
babelOptions.plugins = []
babelOptions.plugins.push([combineFiltersBabelPlugins, { mixinsFiltersArray: mixinsFiltersArray }])
babelOptions.comments = false
const result = babel.transform(scriptFilter.code || '', babelOptions)
result.code = result.code.trim()
if (result.code) {
return result
} else {
return null
}
}

module.exports = { extractScriptFilters, combineScriptAndMixinsFilters }
58 changes: 58 additions & 0 deletions lib/script-compiler/plugins/babel-plugin-combine-filters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const t = require('babel-types')

module.exports = () => ({
visitor: {
Program: {
enter (path, options) {
const { mixinsFiltersArray } = options.opts
if (mixinsFiltersArray && mixinsFiltersArray.length > 0) {
mixinsFiltersArray.forEach((filters) => {
if (path.get('body.0')) {
const insert = t.variableDeclaration('var', [t.variableDeclarator(t.identifier(filters.name), t.callExpression(t.identifier('require'), [t.stringLiteral(filters.realtivePath)]))])
path.get('body.0').insertBefore(insert)
}
})
}
},
exit (path) {
// 清理use strict
var list = path.node.directives
for (var i = list.length - 1, it; i >= 0; i--) {
it = list[i]
if (it.value.value === 'use strict') {
list.splice(i, 1)
}
}
}
},
AssignmentExpression (path, options) {
if (path.get('left').isMemberExpression() &&
path.get('left.object').node.name === 'module' &&
path.get('left.property').node.name === 'exports') {
// 暂停搜索
path.stop()
const { mixinsFiltersArray } = options.opts
if (mixinsFiltersArray && mixinsFiltersArray.length > 0) {
const currentFunArray = path.get('right.properties')
const currentFunNameArray = []
let lastChild
currentFunArray.forEach((func, index) => {
currentFunNameArray.push(func.get('key.name').node)
if (index === currentFunArray.length - 1) {
lastChild = func
}
})
mixinsFiltersArray.forEach((filters) => {
const { extractFilter } = filters
extractFilter && extractFilter.filtersFuncArray.forEach((func) => {
if (currentFunNameArray.indexOf(func) <= 0 && lastChild) {
const insert = t.objectProperty(t.identifier(func), t.identifier(filters.name + '.' + func))
lastChild.insertAfter(insert)
}
})
})
}
}
}
}
})
Loading