UNPKG

adui

Version:

<div> <img src="https://wxa.wxs.qq.com/mpweb/delivery/legacy/wxadtouch/upload/t1/od834zef_52939fc6.png" style="margin:40px 0 0 -8px; background-color: #fcfcfc; box-shadow: none;" /> </div>

439 lines (419 loc) 13.9 kB
/* eslint-disable */ // @ts-nocheck /** * @babel/parser: Babel 中使用的 JavaScript 解析器,主要基于 acorn 和 acorn jsx * Babel解析器根据 Babel-AS T格式生成 AST。它基于 ESTree 规范 */ var parser = require("@babel/parser") /** * @babel/traverse: parse 的好伙伴,用于遍历和设置 ast 节点信息 */ var traverse = require("@babel/traverse").default var finalIcons = {} var IconSvgPaths = require("./lib/icon/IconSvgPaths").default /** * addedIconsArray: 记录已经被添加的 icon */ var addedIconsArray = [] var componentNames = [] var validTypeArr = ["Literal", "StringLiteral"] var ICON_INTENTS = { danger: "alert-circle", info: "info-circle", normal: "info-circle", primary: "tick-circle", success: "tick-circle", warning: "warning", } var ICON_DEFAULTS = { breadcrumb: ["arrow-right"], cascader: ["triangle-down", "triangle-right", "cancel-circle"], colorpicker: ["arrow-down"], datepicker: ["calendar-outlined", "triangle-down", "arrow-down"], dialog: ["cancel"], menu: ["triangle-right"], numericinput: ["arrow-up", "arrow-down"], tag: ["cancel-circle"], timepicker: ["time-outlined"], treeselect: [ "menu", "triangle-right", "triangle-down", "cancel", "file-outlined", ], upload: ["delete-outlined", "add", "cancel"], } /** * 此方法来自 create-react-app/config/webpack.config.js */ var hasJsxRuntime = (function () { if (process.env.DISABLE_NEW_JSX_TRANSFORM === "true") { return false } try { require.resolve("react/jsx-runtime") return true } catch (e) { return false } })() function isArray(arrLike) { return Object.prototype.toString.call(arrLike) === "[object Array]" } function isEleType(astParam, evarype) { if ( Object.hasOwnProperty.call(astParam, "name") && astParam.name.toLowerCase() === evarype ) { return evarype } if ( Object.hasOwnProperty.call(astParam, "object") && astParam.object.name && astParam.object.name.toLowerCase() === evarype ) { return evarype } return false } function isValidIdentifier(astParam) { return ( isEleType(astParam, "alert") || isEleType(astParam, "button") || isEleType(astParam, "checkbox") || isEleType(astParam, "icon") || isEleType(astParam, "radio") || isEleType(astParam, "form") || isEleType(astParam, "nav") || isEleType(astParam, "select") ) } function getProps(astParam, type) { if (["alert", "icon", "nav"].includes(type)) { return getEleProps(astParam, ["icon"]) } if (["checkbox", "radio"].includes(type)) { return getEleProps(astParam, ["helperIcon"]) } if (type === "form") { return getEleProps(astParam, ["labelHelperIcon"]) } if (type === "button") { return getEleProps(astParam, ["leftIcon", "rightIcon"]) } if (type === "select") { return getEleProps(astParam, ["rightIcon"]) } } function isValidIconType(node) { return validTypeArr.indexOf(node.type) >= 0 } /** * Prop 支持两种语法: * 1. 字面量 * 2. 三元判断 * * 其他无法支持 */ function getEleProps(astParam, propKeys = []) { var result = {} if (isArray(astParam)) { for (var i = 0; i < astParam.length; i++) { var keyName = astParam[i].key && astParam[i].key.name if (propKeys.includes(keyName)) { if (astParam[i].value.type === "ConditionalExpression") { result[keyName] = [] if (isValidIconType(astParam[i].value.consequent)) { result[keyName].push(astParam[i].value.consequent.value) } if (isValidIconType(astParam[i].value.alternate)) { result[keyName].push(astParam[i].value.alternate.value) } } else if (isValidIconType(astParam[i].value)) { result[keyName] = [astParam[i].value.value] } } } } return result } function initOptionIcons(initIcons) { if (initIcons.length > 0) { searchIcons(initIcons) } } function searchIcons(icons = []) { icons.forEach(function (iconItem) { searchIconByName(iconItem) }) } function searchIconByName(name) { if (!name) { return } var paths = IconSvgPaths[name] /** * 如果 name 合法,并且 addedIconsArray 中不存在,则写入 */ if (paths && addedIconsArray.indexOf(name) === -1) { console.log("\n[📦 adui Shake Icon]: ", name) addedIconsArray.push(name) finalIcons[name] = paths } } function aduiIconPlugin(options = {}) { const { initIcons } = options let savedId = "" let viteConfig return { name: "adui-icon", configResolved(resolvedConfig) { viteConfig = resolvedConfig if (initIcons && initIcons.length) { initOptionIcons(initIcons) } }, transform(source, id) { if (viteConfig.command !== "build") { return } try { var ast = parser.parse(source, { sourceType: "module", plugins: ["dynamicImport"], }) traverse(ast, { CallExpression: function (path) { if (path.node.callee && isArray(path.node.arguments)) { /** * React 16: * React.createElement(xxx, { xxx: "" }) * ↓ * object.property(Identifier, ObjectExpression) * * React 17 hasJsxRuntime: * _jsx(xxx, {xxx: ""}) * ↓ * callee(Identifier, ObjectExpression) */ var { object, property, name } = path.node.callee var [Identifier, ObjectExpression] = path.node.arguments if ((!object || !property) && name !== "_jsx") { return } /** * 确认格式分支: * 1. React.createElement(xxx) || _jsx(xxx) * 2. Dialog.xxx() * 3. Message.xxx() */ if ( name === "_jsx" || (object.name === "React" && property.name === "createElement") ) { if ( (!object || !property || !Identifier || !ObjectExpression) && name !== "_jsx" ) { return } /** * 分支: * 1. 动态获取 prop 值 * 2. 静态设置 icon 值 */ var validIdentifierType = isValidIdentifier(Identifier) /** * 确认格式 React.createElement(xxx, { xxx: "" }) || _jsx(xxx, {xxx: ""}) */ if (validIdentifierType) { if (isArray(ObjectExpression.properties)) { var props = getProps( ObjectExpression.properties, validIdentifierType ) Object.keys(props).forEach(function (key) { props[key].forEach(function (item) { searchIconByName(item) }) }) } /** * 组件:alert checkbox radio form nav * 既支持自定义 icon,又有一些组件内 icon 使用,在这里设置 */ if (validIdentifierType === "alert") { /** * 判断 intent,如果没有设置 icon,则拿到对应 intent 的 icon */ var props = getEleProps(ObjectExpression.properties, [ "icon", "intent", ]) if (!props.icon) { ;(props.intent || ["normal"]).forEach(function (o) { searchIconByName(ICON_INTENTS[o]) }) } } if (["checkbox", "radio"].includes(validIdentifierType)) { if (isArray(ObjectExpression.properties)) { var props = getEleProps(ObjectExpression.properties, [ "helperIcon", "helper", ]) /** * 如果存在 helper,则判断是否有 helperIcon, * 有则设置 helperIcon(上面已经做过这一步) * 无则设置默认的 help-circle */ if (props.helper && !props.helperIcon) { searchIconByName("help-circle") } } } if (validIdentifierType === "form") { if (isArray(ObjectExpression.properties)) { var props = getEleProps(ObjectExpression.properties, [ "labelHelperIcon", "labelHelper", ]) /** * 如果存在 labelHelper,则判断是否有 labelHelperIcon, * 有则设置 labelHelperIcon(上面已经做过这一步) * 无则设置默认的 help-circle */ if (props.labelHelper && !props.labelHelperIcon) { searchIconByName("help-circle") } } } if (validIdentifierType === "nav") { /** * 判断 nav,加入 arrow-up */ searchIconByName("arrow-up") } if (validIdentifierType === "select") { if (isArray(ObjectExpression.properties)) { var props = getEleProps(ObjectExpression.properties, [ "rightIcon", ]) if (!props.rightIcon) { searchIconByName("triangle-down") } searchIconByName("search") } } } else { Object.keys(ICON_DEFAULTS).forEach(function (o) { if (isEleType(Identifier, o)) { ICON_DEFAULTS[o].forEach(function (p) { searchIconByName(p) }) } }) } } else if (["Dialog", "Message"].includes(object.name)) { if (property && property.name) { searchIconByName(ICON_INTENTS[property.name]) } } } }, }) if (id.includes("IconSvgPaths")) { savedId = id return { code: ` var finalIcons = ${JSON.stringify(finalIcons)}; window.aduiIconReduced = finalIcons; export default finalIcons; `, } } } catch (error) {} }, } } function aduiImportPlugin() { let viteConfig return { name: "adui-import", configResolved(resolvedConfig) { viteConfig = resolvedConfig }, transform(source, id) { try { if ( !/(node_modules)/.test(id) && /('adui')|("adui")/.test(source) && viteConfig.command === "build" ) { var newSource = source var ast = parser.parse(source, { sourceType: "module", plugins: ["dynamicImport"], }) /** * rollup 按需 CSS 引入 * import { Button, Icon } from 'adui' * ↓ * import Button from 'adui/es/button' * import Icon from 'adui/es/icon' * import 'adui/es/button/style' * import 'adui/es/icon/style' */ var removeIndexes = [] let newImportStatement = "" traverse(ast, { ImportDeclaration: function (path) { if (path.node.source.value === "adui") { removeIndexes.push([path.node.start, path.node.end]) path.node.specifiers.forEach(function (specifier, index) { var name = specifier && specifier.imported.name var localName = specifier && specifier.local.name if (!name) { return } var finalName = name .replace(/([A-Z])/g, (str) => { return "-" + str.toLowerCase() }) .substring(1) newImportStatement += "import " + localName + " from 'adui/es/" + finalName + "'\n" if (!componentNames.includes(finalName)) { console.log("\n[📦 adui Shake Component]: ", name) componentNames.push(finalName) } }) } }, }) removeIndexes.forEach(function (removeIndex) { var [start, end] = removeIndex newSource = newSource.slice(0, start) + newSource.slice(end) }) componentNames.forEach(function (name) { if (!["resize-observer", "motion", "transition", "portal"].includes(name)) { newImportStatement = newImportStatement + `import 'adui/es/${name}/style' \n` } }) return { code: newImportStatement + newSource, } } } catch (error) {} }, } } module.exports = { aduiImportPlugin, aduiIconPlugin, }