UNPKG

cgreact-loader

Version:

CGReact与Webpack配套的loader

265 lines (264 loc) 14.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var chalk = require("chalk"); var TS = require("typescript"); function transformTypeScript(context) { // 在编译的过程中寻找到应用程序的入口类 var ApplicationMainClass; // 代码编译处理器 return visitor; function visitor(node) { // 处理import后面带webpackchunk注释的懒加载模块 if (TS.isImportDeclaration(node)) { return handleWebpackImportChunk(node); } // 处理affectStyle标签 if (TS.isDecorator(node) && TS.isCallExpression(node.expression) && node.expression.expression.getFullText() == "affectStyle") { return handleAffectStyleAnnotation(node); } // 处理img的src引入路径 if ((TS.isJsxOpeningElement(node) || TS.isJsxSelfClosingElement(node)) && node.tagName.getFullText() == "img") { return handleImageJSXSrcAttribute(node); } // 处理表单元素value的自动value-set和value-key属性自动添加 if (TS.isJsxOpeningElement(node) || TS.isJsxSelfClosingElement(node)) { return handleFormItemValueSet(node); } // 处理importAssetsScript等三个方法的自动require路径 if (TS.isCallExpression(node)) { return handleImportFunctionUrlParameter(node); } // 自动处理应用主类的初始化 if (ApplicationMainClass == undefined && TS.isClassDeclaration(node)) { return handleApplicationNewInstance(node); } return TS.visitEachChild(node, visitor, context); } ; /*处理懒加载:import MyPage form "./pages/my-page" /*webpackChucnkName:xxx*/ function handleWebpackImportChunk(node) { // 获取导入声明变量,构造pageName var importDeclaration = node; //import MyPage form "./pages/my-page" /*webpackChucnkName:xxx*/; var importClause = importDeclaration.importClause; //MyPage if (importClause == undefined) { // 这是直接import "xxx"的情况,不用处理 return node; } var identifier = importClause.name; // MyPage if (identifier == undefined) { // 这是import * as的情况,不用处理 return node; } var pageName = identifier.getFullText().trim(); // 获取引入路径 "./pages/my-page" var moduleSpec = importDeclaration.moduleSpecifier; var pagePath = moduleSpec.getFullText().trim(); pagePath = pagePath.replace("\"", ""); pagePath = pagePath.replace("\"", ""); pagePath = pagePath.replace("'", ""); pagePath = pagePath.replace("'", ""); // 获取 /*webpackChucnkName:xxx*/; var semicolonToken = importDeclaration.getLastToken(); if (semicolonToken.kind != TS.SyntaxKind.SemicolonToken) { // 不以;结尾的import语句不符合语法,不处理 return node; } var comment = semicolonToken.getFullText(); if (comment.indexOf("webpackChunkName:") < 0) { // 注释里不包含webpackChuncName:肯定不是我们要的 return node; } comment = comment.replace(";", ""); // 去掉无用的结束;标记 var config = comment.replace("/*", "").replace("*/", "").trim(); // 去掉注释标记 var keyArray = config.split(":"); if (keyArray.length < 2) { // :后面可能没有配置东西,不合语法不处理 return node; } var pageComment = comment; //获得最终配置的注释 // 执行到这里可以进行处理,首先输出打印信息 var hintText = node.getFullText() .replace(/(?:^|\n|\r)\s*\/\/.*(?:\r|\n|$)/g, "") .replace(/(?:^|\n|\r)\s*\/\*[\s\S]*?\*\/\s*(?:\r|\n|$)/g, "") .replace("\n", "") .replace("\r", ""); console.log(chalk.blue("CGReactLoader分离了" + chalk.red("TSX") + " : ") + hintText); // 构造表达式:()=>import("xxx") // let moduleStringLiteral: StringLiteral =TS.createStringLiteral(pagePath); //标准写法webpack的注释出不来 var moduleStringLiteral = node.getChildAt(3); var importCallExpression = TS.createCall(TS.createIdentifier("import"), null, [moduleStringLiteral]); var arrowFunction = TS.createArrowFunction(null, null, [], null, null, importCallExpression); // 构造表达式:require("cgreact-core").CGReactApplication var requireCallExpression = TS.createCall(TS.createIdentifier("require"), null, [TS.createStringLiteral("cgreact-core")]); var requirePropertyAccessExpression = TS.createPropertyAccess(requireCallExpression, TS.createIdentifier("CGReactApplication")); // 构造表达式:CGReactApplication.lazyLoadPage() var propertyAccessExpression = TS.createPropertyAccess(requirePropertyAccessExpression, TS.createIdentifier("lazyLoadPage")); var lazyLoadPageCallExpression = TS.createCall(propertyAccessExpression, null, [arrowFunction]); // 形成最后的表达式 var variableDeclaration = TS.createVariableDeclaration(TS.createIdentifier(pageName), null, lazyLoadPageCallExpression); var variableDeclarationList = TS.createVariableDeclarationList([variableDeclaration], TS.NodeFlags.Const); return TS.createVariableStatement(undefined, variableDeclarationList); // } /** 在第一个扫描到的CGReactApplication子类后面自动不上一句new */ function handleApplicationNewInstance(node) { // console.log("------------分析--------------------"); // 捕捉到了类声明 var classDeclaration = node; // 判断是否有继承类 if (!classDeclaration.heritageClauses || classDeclaration.heritageClauses[0].token != TS.SyntaxKind.ExtendsKeyword) { // 没有继承类肯定不需要处理 return TS.visitEachChild(node, visitor, context); } // 获得继承表达式,可能是CGReactApplication或者CGReact.CGReactApplication或者其他的类 var extendsExpression = classDeclaration.heritageClauses[0].types[0].expression; var extendsClass = TS.createIdentifier("OtherClass"); extendsClass = TS.isPropertyAccessExpression(extendsExpression) ? extendsExpression.name : extendsClass; extendsClass = TS.isIdentifier(extendsExpression) ? extendsExpression : extendsClass; if (extendsClass.text.trim() != "CGReactApplication") { // 不是CGReactApplication的子类不用处理 return TS.visitEachChild(node, visitor, context); } else { // 找到了应用的入口主类 ApplicationMainClass = classDeclaration; // console.log("应用程序的入口是:" + ApplicationMainClass.name.text); } // 获取类名 var className = classDeclaration.name; // 自动增加new 应用类的代码 return TS.createNodeArray([ TS.visitEachChild(classDeclaration, visitor, context), TS.createExpressionStatement(TS.createNew(className, undefined, [])) ], undefined); } /* 处理注解:@affectStyle("XXX")*/ function handleAffectStyleAnnotation(node) { var decorator = node; var callExpression = decorator.expression; var argumentArray = callExpression.arguments; if (argumentArray.length <= 0) { // affectStyle必须至少有1个参数,否则这里会报错 console.error("affectStyle标签必须至少有一个参数"); return node; } if (!TS.isStringLiteral(argumentArray[0])) { // 不是string表达式的情况不处理,可能是用户已经requrie()或者传了css // console.log(argumentArray[0].getFullText() + "不用处理"); return node; } // console.log("-------------------分析---------------"); // console.log(node.getFullText().trim()); var urlString = argumentArray[0]; //获得到url了 // 开始构造返回节点 return TS.createDecorator(TS.createCall(callExpression.expression, undefined, TS.createNodeArray([ TS.createCall(TS.createIdentifier("require"), undefined, [urlString]), urlString, ], undefined))); } /* 处理<img>标签的src属性:<img src="xxx">*/ function handleImageJSXSrcAttribute(node) { // console.log("------------分析--------------"); var jsxElement = node; var jsxProp = jsxElement.attributes.properties; for (var i = 0; i < jsxProp.length; i++) { if (TS.isJsxAttribute(jsxProp[i]) && jsxProp[i].name.getFullText().trim() == "src") { // 只需要处理src标签就可以了 var currentProp = jsxProp[i]; var url = void 0; //寻找用户直接写的引入路径 if (TS.isStringLiteral(currentProp.initializer)) { // 这个是用户直接写了src="xxx"的字符串情况 url = currentProp.initializer; } else if (TS.isJsxExpression(currentProp.initializer)) { // 这个是用户写了src={????}的情况 var jsxExpression = currentProp.initializer; if (TS.isStringLiteral(jsxExpression.expression)) { // 这个是用户直接写了scr={"xxx"}的情况 url = jsxExpression.expression; } else { // 用户写了更加复杂的表达式,可能是src={require}等等,不处理 continue; } } else { // 没有写直接src的情况不处理 continue; } var urlText = url.getText().replace("\"", "").replace("\'", "").replace("`", ""); if (urlText.startsWith("http") || urlText.startsWith("data:image")) { // 制定了全路径或者base64图片不处理 continue; } // 替换这个参数为我们构造的带require的参数 jsxProp[i] = TS.createJsxAttribute(currentProp.name, TS.createJsxExpression(undefined, TS.createCall(TS.createIdentifier("require"), undefined, [url]))); } } return node; } /** 处理CGReact表单元素prop中value配置后,自动添加value-set和value-key属性*/ function handleFormItemValueSet(node) { // 获得所有的prop参数数组 var props = node.attributes.properties; // 判断是否有value属性 var index = props.findIndex(function (attribute) { return attribute.name && attribute.name.getFullText().trim() == "value"; }); if (index >= 0 && !props.find(function (attribute) { return attribute.name && attribute.name.getFullText().trim() == "state"; })) { // 用户配置了value属性 var value = props[index]; if (TS.SyntaxKind[value.initializer.kind] == "JsxExpression") { // 只处理表达式,其余情况不处理 var jsxExpression = value.initializer; // 找到valueset和key var valueSet = undefined; var valueKey = undefined; if (TS.SyntaxKind[jsxExpression.expression.kind] == "PropertyAccessExpression") { // 处理 value=this.state.xxx的情况 var expression = jsxExpression.expression; valueSet = expression.expression; valueKey = TS.createStringLiteral(expression.name.getFullText()); } if (TS.SyntaxKind[jsxExpression.expression.kind] == "ElementAccessExpression") { // 处理value=this.state['xxx']的情况 var expression = jsxExpression.expression; valueSet = expression.expression; valueKey = TS.createStringLiteral(expression.argumentExpression.getFullText()); } // 执行添加value-set和value-key属性 if (valueSet && valueKey) { var newProps = Array.from(props); newProps.push(TS.createJsxAttribute(TS.createIdentifier("value-set"), TS.createJsxExpression(undefined, valueSet))); newProps.push(TS.createJsxAttribute(TS.createIdentifier("value-key"), valueKey)); if (TS.SyntaxKind[node.kind] == "JsxOpeningElement") { return TS.createJsxOpeningElement(node.tagName, null, TS.createJsxAttributes(newProps)); } else if (TS.SyntaxKind[node.kind] == "JsxSelfClosingElement") { return TS.createJsxSelfClosingElement(node.tagName, null, TS.createJsxAttributes(newProps)); } } } } return node; } /* 处理importAssetsScript方法的url参数:importAssetsScript(tag,"xxxx.js")*/ function handleImportFunctionUrlParameter(node) { // console.log("------------分析--------------------"); var callExpression = node; var functionName = TS.createIdentifier("NotCGReactFunction"); functionName = TS.isPropertyAccessExpression(callExpression.expression) ? callExpression.expression.name : functionName; functionName = TS.isIdentifier(callExpression.expression) ? callExpression.expression : functionName; var name = functionName.text.trim(); if (name != "importAssetsScript" && name != "importAssetsSCSS" && name != "importAssetsCSS") { // 不是我们想处理的方法 return node; } if (callExpression.arguments.length < 2 || !TS.isStringLiteral(callExpression.arguments[1])) { // 没有二参,或者第二个参数不是string,要么错误的调用了这个方法,要么已经加了require return node; } var url = callExpression.arguments[1]; // @ts-ignore偷梁换柱方法的参数 callExpression.arguments[1] = TS.createCall(TS.createIdentifier("require"), undefined, [url]); return node; } } exports.default = transformTypeScript;