feat: udpate
This commit is contained in:
parent
cae78c5fe7
commit
6a4a31b39b
|
|
@ -2,6 +2,13 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [3.2.0] - 2026-01-12
|
||||
|
||||
### 🎉 新特性
|
||||
|
||||
#### 模型类名前缀支持
|
||||
- ✅ **支持配置文件配置**:在 `generator_config.yaml` 中配置 `models.class_prefix`,自动为生成的模型类添加前缀。
|
||||
- ✅ **性能优化**:为 `ConfigRepository` 添加缓存机制,减少磁盘 I/O。
|
||||
## [3.1.0] - 2025-11-24
|
||||
|
||||
### 🎉 新特性
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ class SwaggerConfig {
|
|||
static JsonSerializableConfig? get jsonSerializableConfig =>
|
||||
ConfigRepository.loadSync().jsonSerializableConfig;
|
||||
|
||||
/// 获取模型类名前缀(从配置文件读取)
|
||||
static String? get modelClassPrefix =>
|
||||
ConfigRepository.loadSync().modelClassPrefix;
|
||||
|
||||
/// 默认文档文件名
|
||||
static const String defaultDocumentationFile =
|
||||
'generated_api_documentation.md';
|
||||
|
|
|
|||
|
|
@ -64,8 +64,16 @@ class ConfigRepository {
|
|||
}
|
||||
}
|
||||
|
||||
static ConfigRepository? _cachedConfig;
|
||||
|
||||
/// 同步加载配置(用于向后兼容或必须同步的场景)
|
||||
/// 为默认配置路径启用缓存
|
||||
static ConfigRepository loadSync([String? configPath]) {
|
||||
// 仅在使用默认路径时使用缓存
|
||||
if (configPath == null && _cachedConfig != null) {
|
||||
return _cachedConfig!;
|
||||
}
|
||||
|
||||
final file = File(configPath ?? PathResolver.findConfigFile() ?? '');
|
||||
if (!file.existsSync()) {
|
||||
return ConfigRepository({});
|
||||
|
|
@ -75,7 +83,12 @@ class ConfigRepository {
|
|||
final content = file.readAsStringSync();
|
||||
final yaml = loadYaml(content);
|
||||
final map = _yamlToMap(yaml);
|
||||
return ConfigRepository(map);
|
||||
final config = ConfigRepository(map);
|
||||
|
||||
if (configPath == null) {
|
||||
_cachedConfig = config;
|
||||
}
|
||||
return config;
|
||||
} on Exception catch (e) {
|
||||
appLogger.warning('⚠️ 配置文件解析失败: $e');
|
||||
return ConfigRepository({});
|
||||
|
|
@ -323,6 +336,13 @@ class ConfigRepository {
|
|||
return JsonSerializableConfig.fromMap(jsonSerializable);
|
||||
}
|
||||
|
||||
/// 获取模型类名前缀
|
||||
String? get modelClassPrefix {
|
||||
final generation = _config['generation'] as Map<String, dynamic>?;
|
||||
final models = generation?['models'] as Map<String, dynamic>?;
|
||||
return models?['class_prefix'] as String?;
|
||||
}
|
||||
|
||||
/// 获取枚举键名映射配置
|
||||
/// 返回格式: { "EnumName": { value: { "name": "KEY_NAME", "description": "描述" } } }
|
||||
Map<String, Map<dynamic, EnumKeyMapping>>? get enumKeyMappings {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
///
|
||||
library;
|
||||
|
||||
import 'package:swagger_generator_flutter/core/config.dart';
|
||||
import 'package:swagger_generator_flutter/core/models.dart';
|
||||
import 'package:swagger_generator_flutter/utils/string_utils/formatting_utils.dart';
|
||||
import 'package:swagger_generator_flutter/utils/string_utils/naming_converter.dart';
|
||||
|
|
@ -47,8 +48,16 @@ class StringHelper {
|
|||
NamingConverter.toDartPropertyName(propName);
|
||||
|
||||
/// 生成 Dart 类名
|
||||
static String generateClassName(String name) =>
|
||||
NamingConverter.generateClassName(name);
|
||||
static String generateClassName(String name) {
|
||||
var className = NamingConverter.generateClassName(name);
|
||||
final prefix = SwaggerConfig.modelClassPrefix;
|
||||
if (prefix != null && prefix.isNotEmpty) {
|
||||
if (!className.startsWith(prefix)) {
|
||||
className = prefix + className;
|
||||
}
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
/// 生成常量名称 (UPPER_SNAKE_CASE)
|
||||
static String generateConstantName(String name) =>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
name: swagger_generator_flutter
|
||||
description: A powerful Swagger/OpenAPI code generator for Flutter projects with Dio + Retrofit support
|
||||
|
||||
version: 3.1.4
|
||||
version: 3.2.0
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:swagger_generator_flutter/core/config.dart';
|
||||
import 'package:swagger_generator_flutter/core/config_repository.dart';
|
||||
import 'package:swagger_generator_flutter/utils/string_helper.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('Model Class Prefix Support', () {
|
||||
late Directory tempDir;
|
||||
late File configFile;
|
||||
|
||||
setUp(() {
|
||||
tempDir = Directory.systemTemp.createTempSync('swagger_gen_test_');
|
||||
configFile = File('${tempDir.path}/generator_config.yaml');
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tempDir.deleteSync(recursive: true);
|
||||
});
|
||||
|
||||
test('should parse class_prefix from config', () {
|
||||
configFile.writeAsStringSync('''
|
||||
generator:
|
||||
name: test
|
||||
|
||||
output:
|
||||
models:
|
||||
class_prefix: "MyPrefix"
|
||||
|
||||
generation:
|
||||
models:
|
||||
class_prefix: "MyPrefix"
|
||||
''');
|
||||
|
||||
final config = ConfigRepository.loadSync(configFile.path);
|
||||
expect(config.modelClassPrefix, equals('MyPrefix'));
|
||||
});
|
||||
|
||||
test('should return null when class_prefix is missing', () {
|
||||
configFile.writeAsStringSync('''
|
||||
generator:
|
||||
name: test
|
||||
''');
|
||||
|
||||
final config = ConfigRepository.loadSync(configFile.path);
|
||||
expect(config.modelClassPrefix, isNull);
|
||||
});
|
||||
|
||||
// NOTE: Testing StringHelper.generateClassName directly implies checking if it reads from the GLOBAL config.
|
||||
// However, ConfigRepository.loadSync() creates an instance, but SwaggerConfig accessors call ConfigRepository.loadSync() individually.
|
||||
// Since ConfigRepository.loadSync() without args looks for default file, we need a way to inject the config or point it to our file.
|
||||
// The current implementation of SwaggerConfig calls ConfigRepository.loadSync() which defaults to finding a config file.
|
||||
|
||||
// Changing the implementation of StringHelper to depend on a reloadable config or global state would be better,
|
||||
// but without changing that, we rely on how ConfigRepository finds the file.
|
||||
// Ideally we should test ConfigRepository logic separately from StringHelper if StringHelper uses a static/global config lookup.
|
||||
|
||||
// BUT, wait. ConfigRepository.loadSync() does THIS:
|
||||
// final file = File(configPath ?? PathResolver.findConfigFile() ?? '');
|
||||
|
||||
// If we want SwaggerConfig (static) to pick up our test config, we might need to trick PathResolver
|
||||
// OR we can explicitly pass the config path if the code supported it, but StringHelper uses static SwaggerConfig.modelClassPrefix.
|
||||
|
||||
// The current implementation of `SwaggerConfig.modelClassPrefix` is:
|
||||
// static String? get modelClassPrefix => ConfigRepository.loadSync().modelClassPrefix;
|
||||
|
||||
// So every time we call `StringHelper.generateClassName`, it calls `ConfigRepository.loadSync()`.
|
||||
// `ConfigRepository.loadSync()` calls `PathResolver.findConfigFile()`.
|
||||
|
||||
// Since we cannot easily mock `PathResolver`'s static method or filesystem search path in a unit test without dependency injection,
|
||||
// we might face issues testing `StringHelper` integration end-to-end here unless we run this test in a context where `findConfigFile` returns our temp file.
|
||||
|
||||
// However, for verify purposes, verifying `ConfigRepository` parses it is the most critical part we added.
|
||||
// The `StringHelper` logic is simple string concatenation.
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:swagger_generator_flutter/core/config.dart';
|
||||
import 'package:swagger_generator_flutter/core/config_repository.dart';
|
||||
import 'package:swagger_generator_flutter/utils/string_helper.dart';
|
||||
|
||||
void main() {
|
||||
final configFile = File('generator_config.yaml');
|
||||
final resultFile = File('test_result.txt');
|
||||
|
||||
try {
|
||||
// 1. Setup config file
|
||||
configFile.writeAsStringSync('''
|
||||
generator:
|
||||
name: test
|
||||
output:
|
||||
models:
|
||||
class_prefix: "MyPrefix"
|
||||
generation:
|
||||
models:
|
||||
class_prefix: "MyPrefix"
|
||||
''');
|
||||
|
||||
// 2. Test direct ConfigRepository load
|
||||
final config = ConfigRepository.loadSync('generator_config.yaml');
|
||||
if (config.modelClassPrefix != 'MyPrefix') {
|
||||
throw 'ConfigRepository failed to PARSE prefix. Got: ${config.modelClassPrefix}';
|
||||
}
|
||||
|
||||
// 3. Test SwaggerConfig (static access)
|
||||
// Note: ConfigRepository.loadSync() tries to find config file.
|
||||
// Since we created generator_config.yaml in CWD, and PathResolver likely checks CWD, this matches.
|
||||
if (SwaggerConfig.modelClassPrefix != 'MyPrefix') {
|
||||
throw 'SwaggerConfig failed to READ prefix. Got: ${SwaggerConfig.modelClassPrefix}';
|
||||
}
|
||||
|
||||
// 4. Test StringHelper
|
||||
final className = StringHelper.generateClassName('User');
|
||||
if (className != 'MyPrefixUser') {
|
||||
throw 'StringHelper failed to APPLY prefix. Got: $className';
|
||||
}
|
||||
|
||||
// 5. Test Prefix Avoidance (Idempotency)
|
||||
final className2 = StringHelper.generateClassName('MyPrefixUser');
|
||||
if (className2 != 'MyPrefixUser') {
|
||||
throw 'StringHelper double-prefixed. Got: $className2';
|
||||
}
|
||||
|
||||
resultFile.writeAsStringSync('PASS');
|
||||
print('Verification Passed');
|
||||
} catch (e, stack) {
|
||||
resultFile.writeAsStringSync('FAIL: $e\n$stack');
|
||||
print('Verification Failed: $e');
|
||||
} finally {
|
||||
// Cleanup
|
||||
if (configFile.existsSync()) configFile.deleteSync();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue