diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java index 6e9563c..d675558 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java @@ -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 initBindingMap(DbType dbType, CodegenTableDO table, List columns, diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenFormatUtils.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenFormatUtils.java deleted file mode 100644 index 7b5fbb4..0000000 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenFormatUtils.java +++ /dev/null @@ -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(); - } -} diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java index e4a553f..5d278b3 100644 --- a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java @@ -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.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"); } } diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenFormatUtilsTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenFormatUtilsTest.java deleted file mode 100644 index 3d8f1a5..0000000 --- a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenFormatUtilsTest.java +++ /dev/null @@ -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")); - } -} -