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>

349 lines (334 loc) 10.4 kB
/* eslint-disable */ // @ts-ignore /** * @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 /** * 通过 @babel/parser 将 JS 代码转换为 ast 格式,并修改后,你需要将 ast 格式转回 JS 代码,交给下一个 webpack loader * 最后输出物为 core.transformFromAstSync(ast).code */ var core = require("@babel/core") var IconSvgPaths = require("./lib/icon/IconSvgPaths").default var fs = require("fs") var path = require("path") /** * addedIconsArray: 记录已经被添加的 icon */ var addedIconsArray = [] 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", "cancel-circle", ], dialog: ["cancel"], menu: ["triangle-right"], numericinput: ["arrow-up", "arrow-down"], tag: ["cancel-circle"], timepicker: ["time-outlined"], treeselect: [ "list", "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 searchIconByName(name) { if (!name) { return } var paths = IconSvgPaths[name] /** * 如果 name 合法,并且 addedIconsArray 中不存在,则写入 */ if (paths && addedIconsArray.indexOf(name) === -1) { console.log("[adui-icon-loader]", name) addedIconsArray.push(name) var content = `\n_default["${name}"] = ["${paths.join('","')}"]` if (!fs.existsSync(tempFilePath) || !fs.readFileSync(tempFilePath).length) { fs.writeFileSync( tempFilePath, `/* eslint-disable */ // @ts-ignore" "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _default = {} window.aduiIconReduced = _default exports["default"] = _default;` ) } fs.appendFileSync(tempFilePath, content) } } 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 } /** * adui-icon-loader * @param {source} source babel-loader 吐给我的 js 文件代码 * @returns core.transformFromAstSync(ast).code */ module.exports = function (source) { if (!fs.existsSync(tempFilePath)) { tempFilePath = path.resolve(__dirname, "adui-icons-reduced.js") } 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]) } } } }, }) return core.transformFromAstSync(ast).code }