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

Feat condition compiler #14733

Open
wants to merge 4 commits into
base: 3.x
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
3 changes: 2 additions & 1 deletion packages/taro-plugin-vue2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"@tarojs/runtime": "workspace:*",
"@tarojs/service": "workspace:*",
"@tarojs/shared": "workspace:*",
"lodash": "^4.17.21"
"lodash": "^4.17.21",
"xregexp": "^3.2.0"
},
"devDependencies": {
"vue": "^2.7.0",
Expand Down
15 changes: 14 additions & 1 deletion packages/taro-plugin-vue2/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,17 @@ const runtimeConfig = {
...base
}

module.exports = [compileConfig, runtimeConfig]

// loader 入口
const loaderConfig = {
input: path.join(cwd, 'src/condition-compiler-loader.ts'),
output: {
exports: 'auto',
file: path.join(cwd, 'dist/condition-compiler-loader.js'),
format: 'cjs',
sourcemap: true
},
...base
}

module.exports = [compileConfig, runtimeConfig, loaderConfig]
149 changes: 149 additions & 0 deletions packages/taro-plugin-vue2/src/condition-compiler-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
const XRegExp = require('xregexp')

const template = /<template>([\s\S]+)<\/template>/gi
const platforms = {
weapp: false,
h5: false,
rn: false,
swan: false,
alipay: false,
tt: false,
qq: false,
jd: false,
quickapp: false
}
if (process.env.TARO_ENV && platforms.hasOwnProperty(process.env.TARO_ENV)) {
platforms[process.env.TARO_ENV] = true
}

module.exports = function (source) {
let _source = ''
if (template.test(source)) {
_source = source.match(template)[0]
const preprocessResult = preprocess(_source, platforms)
return source.replace(template, preprocessResult)
} else {
return source
}
}

function preprocess (src, context) {
const ifTag = {
start: '[ \t]*<!--[ \t]*#(ifndef|ifdef|if)[ \t]+(.*?)[ \t]*(?:-->|!>)(?:[ \t]*\n+)?',
end: '[ \t]*<!(?:--)?[ \t]*#endif[ \t]*(?:-->|!>)(?:[ \t]*\n)?'
}
return replaceRecursive(src, ifTag, function (startMatches, endMatches, include, recurse) {
const variant = startMatches[1]
const test = (startMatches[2] || '').trim()
switch (variant) {
case 'if':
case 'ifdef':
return testPasses(test, context) ? (padContent(startMatches.input) + recurse(include) + padContent(endMatches.input)) : padContent(startMatches.input + include + endMatches.input)
case 'ifndef':
return !testPasses(test, context) ? (padContent(startMatches.input) + recurse(include) + padContent(endMatches.input)) : padContent(startMatches.input + include + endMatches.input)
default:
throw new Error('Unknown if variant ' + variant + '.')
}
})
}

const splitRE = /\r?\n/g
function padContent (content) {
return Array(content.split(splitRE).length).join('\n')
}



function replaceRecursive (rv, rule, processor) {
if (!rule.start || !rule.end) {
throw new Error('Recursive rule must have start and end.')
}

const startRegex = new RegExp(rule.start, 'mi')
const endRegex = new RegExp(rule.end, 'mi')

function matchReplacePass (content) {
let matches
try {
matches = XRegExp.matchRecursive(content, rule.start, rule.end, 'gmi', {
valueNames: ['between', 'left', 'match', 'right']
})
} catch (error) {
// eslint-disable-next-line no-console
console.log(
`
template节点 条件编译失败,参考示例(注意 ifdef 与 endif 必须配对使用):
< !-- #ifdef % PLATFORM % -->
模板代码
< !-- #endif -->
`
)
return content
}

const matchGroup = {
left: null as RegExpExecArray | null,
match: null as string | null,
right: null as RegExpExecArray | null,
}



return matches.reduce(function (builder, match) {
switch (match.name) {
case 'between':
builder += match.value
break
case 'left':
matchGroup.left = startRegex.exec(match.value)
break
case 'match':
matchGroup.match = match.value
break
case 'right':
matchGroup.right = endRegex.exec(match.value)
builder += processor(matchGroup.left, matchGroup.right, matchGroup.match, matchReplacePass)
break
}
return builder
}, '')
}

return matchReplacePass(rv)
}



function getTestTemplate (test) {
test = test || 'true'
test = test.trim()
test = test.replace(/-/g, '_')
// eslint-disable-next-line no-new, no-new-func
return new Function('context', 'with (context||{}){ return ( ' + test + ' ); }')
}

function testPasses (test, context) {
const testFn = getTestTemplate(test)
try {
return testFn(context, getDeepPropFromObj)
} catch (e) {
return false
}
}


function getDeepPropFromObj (obj, propPath) {
propPath.replace(/\[([^\]+?])\]/g, '.$1')
propPath = propPath.split('.')

if (propPath.length === 1) {
return obj[propPath[0]]
}

propPath.some(function (pathSegment) {
obj = obj[pathSegment]
return (obj == null)
})

return obj
}
3 changes: 3 additions & 0 deletions packages/taro-plugin-vue2/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ function customVueChain (chain, data) {
.use('vueLoader')
.loader(vueLoaderPath)
.options(vueLoaderOption)
.end()
.use('conditionCompilerLoader')
.loader(require.resolve('./condition-compiler-loader'))
}

function setLoader (chain) {
Expand Down
7 changes: 4 additions & 3 deletions packages/taro-plugin-vue3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,18 @@
"@tarojs/runtime": "workspace:*",
"@tarojs/service": "workspace:*",
"@tarojs/shared": "workspace:*",
"lodash": "^4.17.21"
"lodash": "^4.17.21",
"xregexp": "^3.2.0"
},
"devDependencies": {
"@tarojs/taro": "workspace:*",
"@vue/compiler-core": "^3.0.0",
"@vue/runtime-core": "^3.0.0",
"vue": "^3.0.0",
"rollup": "^3.8.1",
"rollup-plugin-node-externals": "^5.0.0",
"rollup-plugin-ts": "^3.0.2",
"typescript": "^4.7.4"
"typescript": "^4.7.4",
"vue": "^3.0.0"
},
"peerDependencies": {
"vue": "^3.0.0"
Expand Down
15 changes: 14 additions & 1 deletion packages/taro-plugin-vue3/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,17 @@ const loaderConfig = {
...base
}

module.exports = [compileConfig, runtimeConfig, loaderConfig]

// loader 入口
const conditionCompilerLoaderConfig = {
input: path.join(cwd, 'src/condition-compiler-loader.ts'),
output: {
exports: 'auto',
file: path.join(cwd, 'dist/condition-compiler-loader.js'),
format: 'cjs',
sourcemap: true
},
...base
}

module.exports = [compileConfig, runtimeConfig, loaderConfig, conditionCompilerLoaderConfig]
149 changes: 149 additions & 0 deletions packages/taro-plugin-vue3/src/condition-compiler-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
const XRegExp = require('xregexp')

const template = /<template>([\s\S]+)<\/template>/gi
const platforms = {
weapp: false,
h5: false,
rn: false,
swan: false,
alipay: false,
tt: false,
qq: false,
jd: false,
quickapp: false
}
if (process.env.TARO_ENV && platforms.hasOwnProperty(process.env.TARO_ENV)) {
platforms[process.env.TARO_ENV] = true
}

module.exports = function (source) {
let _source = ''
if (template.test(source)) {
_source = source.match(template)[0]
const preprocessResult = preprocess(_source, platforms)
return source.replace(template, preprocessResult)
} else {
return source
}
}

function preprocess (src, context) {
const ifTag = {
start: '[ \t]*<!--[ \t]*#(ifndef|ifdef|if)[ \t]+(.*?)[ \t]*(?:-->|!>)(?:[ \t]*\n+)?',
end: '[ \t]*<!(?:--)?[ \t]*#endif[ \t]*(?:-->|!>)(?:[ \t]*\n)?'
}
return replaceRecursive(src, ifTag, function (startMatches, endMatches, include, recurse) {
const variant = startMatches[1]
const test = (startMatches[2] || '').trim()
switch (variant) {
case 'if':
case 'ifdef':
return testPasses(test, context) ? (padContent(startMatches.input) + recurse(include) + padContent(endMatches.input)) : padContent(startMatches.input + include + endMatches.input)
case 'ifndef':
return !testPasses(test, context) ? (padContent(startMatches.input) + recurse(include) + padContent(endMatches.input)) : padContent(startMatches.input + include + endMatches.input)
default:
throw new Error('Unknown if variant ' + variant + '.')
}
})
}

const splitRE = /\r?\n/g
function padContent (content) {
return Array(content.split(splitRE).length).join('\n')
}



function replaceRecursive (rv, rule, processor) {
if (!rule.start || !rule.end) {
throw new Error('Recursive rule must have start and end.')
}

const startRegex = new RegExp(rule.start, 'mi')
const endRegex = new RegExp(rule.end, 'mi')

function matchReplacePass (content) {
let matches
try {
matches = XRegExp.matchRecursive(content, rule.start, rule.end, 'gmi', {
valueNames: ['between', 'left', 'match', 'right']
})
} catch (error) {
// eslint-disable-next-line no-console
console.log(
`
template节点 条件编译失败,参考示例(注意 ifdef 与 endif 必须配对使用):
< !-- #ifdef % PLATFORM % -->
模板代码
< !-- #endif -->
`
)
return content
}

const matchGroup = {
left: null as RegExpExecArray | null,
match: null as string | null,
right: null as RegExpExecArray | null,
}



return matches.reduce(function (builder, match) {
switch (match.name) {
case 'between':
builder += match.value
break
case 'left':
matchGroup.left = startRegex.exec(match.value)
break
case 'match':
matchGroup.match = match.value
break
case 'right':
matchGroup.right = endRegex.exec(match.value)
builder += processor(matchGroup.left, matchGroup.right, matchGroup.match, matchReplacePass)
break
}
return builder
}, '')
}

return matchReplacePass(rv)
}



function getTestTemplate (test) {
test = test || 'true'
test = test.trim()
test = test.replace(/-/g, '_')
// eslint-disable-next-line no-new, no-new-func
return new Function('context', 'with (context||{}){ return ( ' + test + ' ); }')
}

function testPasses (test, context) {
const testFn = getTestTemplate(test)
try {
return testFn(context, getDeepPropFromObj)
} catch (e) {
return false
}
}


function getDeepPropFromObj (obj, propPath) {
propPath.replace(/\[([^\]+?])\]/g, '.$1')
propPath = propPath.split('.')

if (propPath.length === 1) {
return obj[propPath[0]]
}

propPath.some(function (pathSegment) {
obj = obj[pathSegment]
return (obj == null)
})

return obj
}
3 changes: 3 additions & 0 deletions packages/taro-plugin-vue3/src/webpack.h5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ function setVueLoader (chain, config: IConfig) {
.use('vueLoader')
.loader(vueLoaderPath)
.options(vueLoaderOption)
.end()
.use('conditionCompilerLoader')
.loader(require.resolve('./condition-compiler-loader'))
}

function setLoader (chain) {
Expand Down
Loading