UNPKG

@mpxjs/webpack-plugin

Version:

mpx compile core

468 lines (445 loc) 12.6 kB
const runRules = require('../../run-rules') const normalizeTest = require('../normalize-test') const changeKey = require('../change-key') const normalize = require('../../../utils/normalize') const { capitalToHyphen } = require('../../../utils/string') const { isOriginTag, isBuildInWebTag, isBuildInReactTag } = require('../../../utils/dom-tag-config') const mpxViewPath = normalize.lib('runtime/components/ali/mpx-view.mpx') const mpxTextPath = normalize.lib('runtime/components/ali/mpx-text.mpx') module.exports = function getSpec ({ warn, error }) { function print (mode, path, isError) { const msg = `Json path <${path}> is not supported in ${mode} environment!` isError ? error(msg) : warn(msg) } function deletePath (opts) { let isError = opts let shouldLog = true if (typeof opts === 'object') { shouldLog = !opts.noLog isError = opts.isError } return function (input, { mode, pathArr = [] }, meta) { const currPath = meta.paths.join('|') if (shouldLog) { print(mode, pathArr.concat(currPath).join('.'), isError) } meta.paths.forEach((path) => { delete input[path] }) return input } } /** * @desc 在app.mpx里配置usingComponents作为全局组件 */ function addGlobalComponents (input, { globalComponents }) { if (globalComponents) { input.usingComponents = Object.assign({}, globalComponents, input.usingComponents) } return input } // 处理支付宝 componentPlaceholder 不支持 view、text 原生标签 function aliComponentPlaceholderFallback (input) { // 处理 驼峰转连字符 input = componentNameCapitalToHyphen('componentPlaceholder')(input) const componentPlaceholder = input.componentPlaceholder const usingComponents = input.usingComponents || (input.usingComponents = {}) for (const cph in componentPlaceholder) { const cur = componentPlaceholder[cph] const placeholderCompMatched = cur.match(/^(?:view|text)$/g) if (!Array.isArray(placeholderCompMatched)) continue let compName, compPath switch (placeholderCompMatched[0]) { case 'view': compName = 'mpx-view' compPath = mpxViewPath break case 'text': compName = 'mpx-text' compPath = mpxTextPath } usingComponents[compName] = compPath componentPlaceholder[cph] = compName } return input } // 校验输出支付宝 componentGenerics 配置的正确性 function aliComponentGenericsValidate (input) { const componentGenerics = input.componentGenerics if (componentGenerics && typeof componentGenerics === 'object') { Object.keys(componentGenerics).forEach(key => { if (!componentGenerics[key].default) { error(`Ali environment componentGenerics need to specify a default custom component! please check the configuration of component ${key}`) } }) } return input } function fillGlobalComponents (input, { globalComponents }, meta) { // 通过meta进行globalComponents的透传 meta.usingComponents = input.usingComponents return input } // 处理 ali swan 的组件名大写字母转连字符:WordExample/wordExample -> word-example function componentNameCapitalToHyphen (type) { return function (input) { // 百度和支付宝不支持大写组件标签名,统一转成带“-”和小写的形式。百度自带标签不会有带大写的情况 // 后续可能需要考虑这些平台支持 componentGenerics 后的转换 https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/generics.html const obj = input[type] if (obj) { Object.entries(obj).forEach(([k, v]) => { const keyNeed = /[A-Z]/g.test(k) const valueNeed = /[A-Z]/g.test(v) let newK let newV if (keyNeed) { newK = capitalToHyphen(k) if (obj[newK]) { warn && warn(`Component name "${newK}" already exists, so component "${k}" can't be converted automatically and it isn't supported in ali/swan environment!`) } else { obj[newK] = v delete obj[k] } } // componentPlaceholder 的 value 也需要转换 if (type === 'componentPlaceholder' && valueNeed) { newV = capitalToHyphen(v) obj[newK || k] = newV } }) } return input } } /** * 将小程序代码中使用的与原生 HTML tag 或 内建组件 同名的组件进行转化,以解决与原生tag命名冲突问题。 */ function fixComponentName (input, { mode }) { const isNeedFixTag = (tag) => { switch (mode) { case 'web': return isOriginTag(tag) || isBuildInWebTag(tag) case 'ios': case 'android': case 'harmony': return isOriginTag(tag) || isBuildInReactTag(tag) } } const usingComponents = input.usingComponents const componentPlaceholder = input.componentPlaceholder if (usingComponents) { const transfromKeys = [] Object.keys(usingComponents).forEach(tag => { if (isNeedFixTag(tag)) { usingComponents[`mpx-com-${tag}`] = usingComponents[tag] delete usingComponents[tag] transfromKeys.push(tag) } }) if (transfromKeys.length && componentPlaceholder) { Object.keys(componentPlaceholder).forEach(key => { if (transfromKeys.includes(componentPlaceholder[key])) { componentPlaceholder[key] = `mpx-com-${componentPlaceholder[key]}` } if (transfromKeys.includes(key)) { componentPlaceholder[`mpx-com-${key}`] = componentPlaceholder[key] delete componentPlaceholder[key] } }) } } return input } const componentRules = [ { test: 'componentGenerics', ali: aliComponentGenericsValidate }, { test: 'componentPlaceholder', ali: aliComponentPlaceholderFallback, swan: deletePath(), jd: deletePath() }, { test: 'usingComponents', ali: componentNameCapitalToHyphen('usingComponents'), swan: componentNameCapitalToHyphen('usingComponents') }, { swan: addGlobalComponents, qq: addGlobalComponents, tt: addGlobalComponents, jd: addGlobalComponents, web: fixComponentName, ios: fixComponentName, android: fixComponentName, harmony: fixComponentName } ] const windowRules = [ { test: 'navigationBarTitleText', ali (input) { return changeKey(input, this.test, 'defaultTitle') } }, { test: 'enablePullDownRefresh', ali (input) { input = changeKey(input, this.test, 'pullRefresh') if (input.pullRefresh) { input.allowsBounceVertical = 'YES' } return input }, jd: deletePath() }, { test: 'navigationBarBackgroundColor', ali (input) { return changeKey(input, this.test, 'titleBarColor') } }, { test: 'disableSwipeBack', ali: deletePath(), qq: deletePath(), jd: deletePath(), swan: deletePath() }, { test: 'onReachBottomDistance', qq: deletePath(), jd: deletePath() }, { test: 'disableScroll', ali: deletePath(), qq: deletePath(), jd: deletePath() }, { test: 'backgroundColorTop|backgroundColorBottom', ali: deletePath(), swan: deletePath() }, { test: 'navigationBarTextStyle|navigationStyle|backgroundTextStyle', ali: deletePath() }, { test: 'pageOrientation', ali: deletePath(), swan: deletePath(), tt: deletePath(), jd: deletePath() } ] const getTabBarRule = () => (input, { mode }) => { input.tabBar = runRules(spec.tabBar, input.tabBar, { mode, normalizeTest, waterfall: true, data: { pathArr: ['tabBar'] } }) return input } const getWindowRule = () => (input, { mode }) => { input.window = runRules(spec.window, input.window, { mode, normalizeTest, waterfall: true, data: { pathArr: ['window'] } }) return input } const spec = { supportedModes: [ 'ali', 'swan', 'qq', 'tt', 'jd', 'qa', 'dd', 'web', 'ios', 'android', 'harmony' ], normalizeTest, page: [...windowRules, ...componentRules], component: componentRules, window: windowRules, tabBar: { list: [ { test: 'text', ali (input) { return changeKey(input, this.test, 'name') } }, { test: 'iconPath', ali (input) { return changeKey(input, this.test, 'icon') } }, { test: 'selectedIconPath', ali (input) { return changeKey(input, this.test, 'activeIcon') } } ], rules: [ { test: 'color', ali (input) { return changeKey(input, this.test, 'textColor') } }, { test: 'list', ali (input) { const value = input.list delete input.list input.items = value.map((item) => { return runRules(spec.tabBar.list, item, { mode: 'ali', normalizeTest, waterfall: true, data: { pathArr: ['tabBar', 'list'] } }) }) return input } }, { test: 'position', ali: deletePath(), swan: deletePath() }, { test: 'borderStyle', ali: deletePath() }, { test: 'custom', ali: function (input) { return changeKey(input, this.test, 'customize') }, swan: deletePath(), tt: deletePath(), jd: deletePath() } ] }, rules: [ { test: 'resizable', ali: deletePath(), qq: deletePath(), swan: deletePath(), tt: deletePath(), jd: deletePath() }, { test: 'preloadRule', jd: deletePath() }, { test: 'functionalPages', ali: deletePath(true), qq: deletePath(true), swan: deletePath(true), tt: deletePath(), jd: deletePath(true) }, { test: 'plugins', qq: deletePath(true), swan: deletePath(true), tt: deletePath(), jd: deletePath(true) }, { test: 'usingComponents', ali: componentNameCapitalToHyphen('usingComponents'), swan: componentNameCapitalToHyphen('usingComponents') }, { test: 'usingComponents', qq: fillGlobalComponents, swan: fillGlobalComponents, tt: fillGlobalComponents, jd: fillGlobalComponents }, { test: 'usingComponents', qq: deletePath({ noLog: true }), swan: deletePath({ noLog: true }), tt: deletePath({ noLog: true }), jd: deletePath({ noLog: true }) }, { test: 'debug', ali: deletePath(), swan: deletePath() }, { test: 'requiredBackgroundModes', ali: deletePath(), tt: deletePath() }, { test: 'workers', jd: deletePath(), ali: deletePath(), swan: deletePath(), tt: deletePath() }, { test: 'subpackages|subPackages', jd: deletePath(true) }, { test: 'packages', jd: deletePath() }, { test: 'navigateToMiniProgramAppIdList|networkTimeout', ali: deletePath(), jd: deletePath() }, { test: 'tabBar', ali: getTabBarRule(), qq: getTabBarRule(), swan: getTabBarRule(), tt: getTabBarRule(), jd: getTabBarRule() }, { test: 'window', ali: getWindowRule(), qq: getWindowRule(), swan: getWindowRule(), tt: getWindowRule(), jd: getWindowRule() }, { web: fixComponentName, ios: fixComponentName, android: fixComponentName, harmony: fixComponentName } ] } return spec }