mirror of
https://gitee.com/zhijiantianya/ruoyi-vue-pro.git
synced 2026-06-05 18:25:41 +08:00
Compare commits
3 Commits
83ce168a68
...
6a3b384fc3
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a3b384fc3 | |||
| eddd21f4e8 | |||
| 78d798bbe9 |
@ -457,8 +457,10 @@ public class CodegenEngine {
|
||||
* @return 格式化后的代码
|
||||
*/
|
||||
private String prettyCode(String content, String vmPath) {
|
||||
// Vue 界面:去除字段后面多余的 , 逗号,解决前端的 Pretty 代码格式检查的报错(内含换行归一化)
|
||||
content = CodegenFormatUtils.applyVueTrailingCommaFix(content, vmPath);
|
||||
// Vue 界面:去除字段后面多余的 , 逗号,解决前端的 Pretty 代码格式检查的报错(需要排除 vben5、vue3_admin_uniapp)
|
||||
if (!StrUtil.containsAny(vmPath, "vben5", "vue3_admin_uniapp")) {
|
||||
content = content.replaceAll(",\\r?\\n}", "\n}").replaceAll(",\\r?\\n }", "\n }");
|
||||
}
|
||||
// Vue 界面:去除多的 dateFormatter,只有一个的情况下,说明没使用到
|
||||
if (StrUtil.count(content, "dateFormatter") == 1) {
|
||||
content = StrUtils.removeLineContains(content, "dateFormatter");
|
||||
@ -480,7 +482,7 @@ public class CodegenEngine {
|
||||
if (StrUtil.count(content, "DICT_TYPE.") == 0) {
|
||||
content = StrUtils.removeLineContains(content, "DICT_TYPE");
|
||||
}
|
||||
return CodegenFormatUtils.formatGeneratedContent(content);
|
||||
return content;
|
||||
}
|
||||
|
||||
private Map<String, Object> initBindingMap(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns,
|
||||
|
||||
@ -1,116 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen.inner;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 代码生成结果格式化工具
|
||||
*/
|
||||
final class CodegenFormatUtils {
|
||||
|
||||
/**
|
||||
* 经典 Vue2 模板目录,与 {@link CodegenEngine#vueTemplatePath(String)} 一致
|
||||
*/
|
||||
private static final String CLASSIC_VUE2_TEMPLATE_PREFIX = "codegen/vue/";
|
||||
|
||||
/**
|
||||
* 经典 Vue3 模板目录,与 {@link CodegenEngine#vue3TemplatePath(String)} 一致。
|
||||
* 使用前缀 {@code codegen/vue3/} 而非子串匹配,避免误伤 {@code vue3_vben5}、{@code vue3_admin_uniapp} 等目录。
|
||||
*/
|
||||
private static final String CLASSIC_VUE3_TEMPLATE_PREFIX = "codegen/vue3/";
|
||||
|
||||
private CodegenFormatUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 经典 Vue2 / Vue3 模板(Element UI / Element Plus 标准模版)
|
||||
*/
|
||||
static boolean isClassicVueTemplate(String vmPath) {
|
||||
if (StrUtil.isEmpty(vmPath)) {
|
||||
return false;
|
||||
}
|
||||
return StrUtil.startWith(vmPath, CLASSIC_VUE2_TEMPLATE_PREFIX)
|
||||
|| StrUtil.startWith(vmPath, CLASSIC_VUE3_TEMPLATE_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否处理 {@code ,\n}} / {@code ,\n }} 形式的末尾逗号(经典 Vue + vue3_vben2;排除 vben5、uniapp)
|
||||
*/
|
||||
static boolean needsVueTrailingCommaBeforeBraceFix(String vmPath) {
|
||||
if (StrUtil.containsAny(vmPath, "vben5", "vue3_admin_uniapp")) {
|
||||
return false;
|
||||
}
|
||||
return isClassicVueTemplate(vmPath) || StrUtil.startWith(vmPath, "codegen/vue3_vben/");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否处理 {@code ,\n)} 形式的末尾逗号(仅经典 Vue 的 .vue 视图模板)
|
||||
*/
|
||||
static boolean needsReactiveTrailingCommaFix(String vmPath) {
|
||||
return isClassicVueTemplate(vmPath) && StrUtil.endWith(vmPath, ".vue.vm");
|
||||
}
|
||||
|
||||
/**
|
||||
* Vue 模板:统一去除对象/参数列表末尾多余逗号(与 {@link CodegenEngine#prettyCode} 历史行为一致)
|
||||
*/
|
||||
static String applyVueTrailingCommaFix(String content, String vmPath) {
|
||||
if (content == null) {
|
||||
return null;
|
||||
}
|
||||
content = normalizeLineSeparators(content);
|
||||
if (needsVueTrailingCommaBeforeBraceFix(vmPath)) {
|
||||
content = content.replaceAll(",\\r?\\n}", "\n}")
|
||||
.replaceAll(",\\r?\\n }", "\n }");
|
||||
}
|
||||
if (needsReactiveTrailingCommaFix(vmPath)) {
|
||||
content = removeTrailingCommaBeforeCloseParen(content);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除多行实参中 {@code ,\n)} 形式的末尾逗号(如 {@code foo(bar,\n)};{@code reactive} 的 {@code ,\n})} 由 {@code ,\n}} 规则处理)
|
||||
*/
|
||||
static String removeTrailingCommaBeforeCloseParen(String content) {
|
||||
if (content == null) {
|
||||
return null;
|
||||
}
|
||||
return content.replaceAll(",\\r?\\n\\)", "\n)");
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一换行符为 LF
|
||||
*/
|
||||
static String normalizeLineSeparators(String content) {
|
||||
if (content == null) {
|
||||
return null;
|
||||
}
|
||||
return content.replace("\r\n", "\n").replace('\r', '\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* 生产生成结果:LF + 保证文件以单个换行结尾(符合 POSIX / IDE 习惯)
|
||||
*/
|
||||
static String formatGeneratedContent(String content) {
|
||||
content = normalizeLineSeparators(content);
|
||||
if (StrUtil.isEmpty(content)) {
|
||||
return content;
|
||||
}
|
||||
while (content.endsWith("\n\n")) {
|
||||
content = content.substring(0, content.length() - 1);
|
||||
}
|
||||
if (!content.endsWith("\n")) {
|
||||
content = content + "\n";
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单测断言:LF + 去掉末尾空白(与历史 fixture 无尾换行对齐)
|
||||
*/
|
||||
static String normalizeForAssert(String content) {
|
||||
if (content == null) {
|
||||
return null;
|
||||
}
|
||||
return normalizeLineSeparators(content).stripTrailing();
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,12 @@ import org.mockito.Spy;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ -99,17 +104,20 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {
|
||||
.map(m -> (String) m.get("filePath"))
|
||||
.collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new));
|
||||
assertEquals(expectedFiles, result.keySet(), "生成文件集合不匹配");
|
||||
// 校验每个文件;归一化换行符,让断言不依赖文件落盘的换行风格
|
||||
// 校验每个文件;归一化 \r\n 为 \n,让断言不依赖文件落盘的换行风格
|
||||
asserts.forEach(assertMap -> {
|
||||
String contentPath = (String) assertMap.get("contentPath");
|
||||
String filePath = (String) assertMap.get("filePath");
|
||||
String expected = CodegenFormatUtils.normalizeForAssert(
|
||||
ResourceUtil.readUtf8Str("codegen/" + path + "/" + contentPath));
|
||||
String actual = CodegenFormatUtils.normalizeForAssert(result.get(filePath));
|
||||
assertEquals(expected, actual, filePath + ":不匹配");
|
||||
String expected = normalizeLineEndings(ResourceUtil.readUtf8Str("codegen/" + path + "/" + contentPath));
|
||||
String actual = result.get(filePath);
|
||||
assertEquals(expected, normalizeLineEndings(actual), filePath + ":不匹配");
|
||||
});
|
||||
}
|
||||
|
||||
private static String normalizeLineEndings(String content) {
|
||||
return content == null ? null : content.replace("\r\n", "\n").replace("\r", "\n").stripTrailing();
|
||||
}
|
||||
|
||||
// ==================== 调试专用 ====================
|
||||
|
||||
/**
|
||||
@ -143,10 +151,10 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {
|
||||
+ '/' + StrUtil.subBefore(lastFilePath, '.', true);
|
||||
asserts.add(MapUtil.<String, String>builder().put("filePath", filePath)
|
||||
.put("contentPath", contentPath).build());
|
||||
FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath);
|
||||
FileUtil.writeUtf8String(normalizeLineEndings(fileContent), basePath + "/" + contentPath);
|
||||
});
|
||||
// 写入 assert.json 文件
|
||||
FileUtil.writeUtf8String(JsonUtils.toJsonPrettyString(asserts), basePath + "/assert.json");
|
||||
FileUtil.writeUtf8String(normalizeLineEndings(JsonUtils.toJsonPrettyString(asserts)), basePath + "/assert.json");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen.inner;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class CodegenFormatUtilsTest {
|
||||
|
||||
@Test
|
||||
void testNormalizeLineSeparators() {
|
||||
assertEquals("a\nb", CodegenFormatUtils.normalizeLineSeparators("a\r\nb"));
|
||||
assertEquals("a\nb", CodegenFormatUtils.normalizeLineSeparators("a\rb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFormatGeneratedContent() {
|
||||
assertEquals("class A {\n}\n", CodegenFormatUtils.formatGeneratedContent("class A {\r\n}\r\n"));
|
||||
assertEquals("x\n", CodegenFormatUtils.formatGeneratedContent("x"));
|
||||
assertEquals("x\n", CodegenFormatUtils.formatGeneratedContent("x\n\n\n"));
|
||||
assertEquals("", CodegenFormatUtils.formatGeneratedContent(""));
|
||||
assertEquals(" \n", CodegenFormatUtils.formatGeneratedContent(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNormalizeForAssert() {
|
||||
assertEquals("}", CodegenFormatUtils.normalizeForAssert("}\n"));
|
||||
assertEquals("a\nb", CodegenFormatUtils.normalizeForAssert("a\r\nb\r\n"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNormalizeForAssert_matchesFormatGeneratedContentForAssert() {
|
||||
String raw = "package demo;\r\n\r\npublic class A {\r\n private Long id;\r\n}\r\n";
|
||||
String generated = CodegenFormatUtils.formatGeneratedContent(raw);
|
||||
assertEquals(CodegenFormatUtils.normalizeForAssert(raw),
|
||||
CodegenFormatUtils.normalizeForAssert(generated));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveTrailingCommaBeforeCloseParen() {
|
||||
assertEquals("foo(bar\n)", CodegenFormatUtils.removeTrailingCommaBeforeCloseParen("foo(bar,\n)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testApplyVueTrailingCommaFix_classicVueReactive() {
|
||||
String input = "const queryParams = reactive({\n createTime: [],\n})\n";
|
||||
String expected = "const queryParams = reactive({\n createTime: []\n})\n";
|
||||
assertEquals(expected, CodegenFormatUtils.applyVueTrailingCommaFix(
|
||||
input, "codegen/vue3/views/index.vue.vm"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testApplyVueTrailingCommaFix_skipVben5() {
|
||||
String input = "const x = {\n a: 1,\n}\n";
|
||||
assertEquals(input, CodegenFormatUtils.applyVueTrailingCommaFix(
|
||||
input, "codegen/vue3_vben5_antd/general/views/index.vue.vm"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsClassicVueTemplate() {
|
||||
assertTrue(CodegenFormatUtils.isClassicVueTemplate("codegen/vue/views/index.vue.vm"));
|
||||
assertTrue(CodegenFormatUtils.isClassicVueTemplate("codegen/vue3/views/index.vue.vm"));
|
||||
assertTrue(CodegenFormatUtils.isClassicVueTemplate("codegen/vue3/api/api.ts.vm"));
|
||||
assertFalse(CodegenFormatUtils.isClassicVueTemplate("codegen/vue3_vben5_antd/schema/views/index.vue.vm"));
|
||||
assertFalse(CodegenFormatUtils.isClassicVueTemplate("codegen/vue3_vben/views/index.vue.vm"));
|
||||
assertFalse(CodegenFormatUtils.isClassicVueTemplate("codegen/vue3_admin_uniapp/views/index.vue.vm"));
|
||||
assertFalse(CodegenFormatUtils.isClassicVueTemplate("codegen/vue30/views/index.vue.vm")); // 非 codegen/vue3/ 前缀
|
||||
assertFalse(CodegenFormatUtils.isClassicVueTemplate("codegen/java/controller/controller.vm"));
|
||||
assertFalse(CodegenFormatUtils.isClassicVueTemplate(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNeedsReactiveTrailingCommaFix() {
|
||||
assertTrue(CodegenFormatUtils.needsReactiveTrailingCommaFix("codegen/vue3/views/index.vue.vm"));
|
||||
assertFalse(CodegenFormatUtils.needsReactiveTrailingCommaFix("codegen/vue3/api/api.ts.vm"));
|
||||
assertFalse(CodegenFormatUtils.needsReactiveTrailingCommaFix("codegen/vue3_vben5_antd/general/views/index.vue.vm"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNeedsVueTrailingCommaBeforeBraceFix() {
|
||||
assertTrue(CodegenFormatUtils.needsVueTrailingCommaBeforeBraceFix("codegen/vue3/views/index.vue.vm"));
|
||||
assertTrue(CodegenFormatUtils.needsVueTrailingCommaBeforeBraceFix("codegen/vue3_vben/views/index.vue.vm"));
|
||||
assertFalse(CodegenFormatUtils.needsVueTrailingCommaBeforeBraceFix("codegen/vue3_vben5_antd/general/views/index.vue.vm"));
|
||||
assertFalse(CodegenFormatUtils.needsVueTrailingCommaBeforeBraceFix("codegen/vue3_admin_uniapp/views/index.vue.vm"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user