cgreact-loader
Version:
CGReact与Webpack配套的loader
265 lines (264 loc) • 14.8 kB
JavaScript
"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;