feat: 增加格式化代码
This commit is contained in:
parent
77cf3a4a11
commit
dc7c17b212
|
|
@ -0,0 +1,76 @@
|
|||
# 1. 继承 Lint 规则集 (必选其一)
|
||||
# --------------------------------------------------------------------------
|
||||
# 强烈推荐!根据你的项目类型选择一个 Lint 规则集作为起点。
|
||||
# 这能大大减少手动配置的工作量,并与社区最佳实践保持一致。
|
||||
# Linter 规则
|
||||
|
||||
# https://dart.ac.cn/tools/linter-rules
|
||||
|
||||
# 如果是 Flutter 项目,推荐使用:
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# 如果是纯 Dart 项目,推荐使用:
|
||||
# include: package:lints/recommended.yaml
|
||||
|
||||
# 2. 配置分析器 (必选)
|
||||
# --------------------------------------------------------------------------
|
||||
# 分析器用于检查代码的语法和潜在问题。
|
||||
# 强烈建议启用所有规则,以确保代码质量和一致性。
|
||||
|
||||
analyzer:
|
||||
errors:
|
||||
require_trailing_commas: ignore
|
||||
# 排除不想被分析的文件或目录。
|
||||
# 对于由代码生成工具(如 json_serializable, freezed)生成的 *.g.dart 文件,
|
||||
# 强烈建议排除,因为你通常不需要对它们进行 Lint 检查。
|
||||
exclude:
|
||||
- '**/*.g.dart' # 排除所有以 .g.dart 结尾的文件
|
||||
- 'lib/generated/**' # 排除 lib/generated/ 目录下的所有文件 (如果你的生成文件都在这里)
|
||||
- 'build/**' # 排除 Flutter/Dart 构建输出目录
|
||||
|
||||
|
||||
# 3. 配置 Lint 规则
|
||||
# --------------------------------------------------------------------------
|
||||
linter:
|
||||
# 在此处启用或禁用特定的 Lint 规则。
|
||||
# `include` 中的规则集已经包含了大部分常用规则,这里可以进行微调。
|
||||
rules:
|
||||
# 常用且推荐启用的规则 (即使默认集没有包含,也建议手动添加)
|
||||
- avoid_empty_else # 避免空的 else 块
|
||||
- avoid_print # 在生产代码中避免使用 print (可根据项目需求启用/禁用)
|
||||
- avoid_relative_lib_imports # 避免从 'lib/' 相对导入
|
||||
# - avoid_return_and_type_annotation # 避免冗余的返回类型注解
|
||||
- curly_braces_in_flow_control_structures # 控制流语句强制使用大括号
|
||||
- empty_catches # 避免空的 catch 块
|
||||
- empty_constructor_bodies # 避免空的构造函数体
|
||||
- empty_statements # 避免空的语句
|
||||
- file_names # 文件名使用小写下划线命名 (my_file.dart)
|
||||
- prefer_const_constructors # 尽可能使用 const 构造函数
|
||||
- prefer_const_declarations # 尽可能使用 const 声明
|
||||
- prefer_const_literals_to_create_immutables # 尽可能使用 const 创建不可变集合
|
||||
# - prefer_single_quotes # 优先使用单引号 (或 prefer_double_quotes)
|
||||
- prefer_final_fields # 类中的私有字段尽可能使用 final
|
||||
- prefer_final_locals # 局部变量尽可能使用 final
|
||||
# - prefer_for_elements_to_map_fromIterable # 优先使用 for 元素创建 Map
|
||||
# - prefer_is_empty # 优先使用 .isEmpty
|
||||
# - prefer_is_not_empty # 优先使用 .isNotEmpty
|
||||
- unnecessary_new # Dart 2.0 后 new 关键字是可选的,推荐省略
|
||||
- unnecessary_this # 避免不必要的 this 关键字
|
||||
# - use_key_in_widget_constructors # Flutter Widget 构造函数中推荐使用 Key
|
||||
|
||||
# 根据项目特性考虑启用的规则 (可能需要团队讨论)
|
||||
# - annotate_overrides # 推荐:覆写方法添加 @override 注解 (如果 flutter_lints 已包含则无需重复)
|
||||
# - lines_longer_than_80_chars # 强制行长 80 字符 (默认是警告,但通常较严格)
|
||||
# - public_member_api_docs # 推荐:为公共 API 编写文档注释 (对库项目非常重要,应用项目可酌情)
|
||||
- require_trailing_commas # 强制多行参数列表使用尾随逗号 (有助于格式化)
|
||||
# - sort_constructors_first # 构造函数在类中声明在前
|
||||
# - sort_declarations_as_members # 类成员按字母顺序排序
|
||||
# - sort_pub_dependencies # pubspec.yaml 依赖按字母排序
|
||||
|
||||
# 4. 格式化器配置
|
||||
# --------------------------------------------------------------------------
|
||||
formatter:
|
||||
# 设置 `dart format` 工具的行宽。
|
||||
# Dart 官方推荐 80,但许多团队会使用 100 或 120 以适应现代宽屏显示器。
|
||||
# 最重要的是整个团队**保持一致**。
|
||||
page_width: 80
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import 'dart:io';
|
||||
|
||||
import '../lib/swagger_cli_new.dart';
|
||||
import 'package:swagger_generator_flutter/swagger_cli_new.dart';
|
||||
|
||||
/// Swagger CLI 工具主入口
|
||||
///
|
||||
|
|
|
|||
|
|
@ -42,9 +42,8 @@ abstract class BaseCommand {
|
|||
print('选项:');
|
||||
for (final option in options) {
|
||||
final short = option.shortName != null ? '-${option.shortName}, ' : '';
|
||||
final defaultValue = option.defaultValue != null
|
||||
? ' (默认: ${option.defaultValue})'
|
||||
: '';
|
||||
final defaultValue =
|
||||
option.defaultValue != null ? ' (默认: ${option.defaultValue})' : '';
|
||||
print(' $short--${option.name} ${option.description}$defaultValue');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -320,7 +320,6 @@ class GenerateCommand extends BaseCommand {
|
|||
// 生成文件头
|
||||
buffer.writeln('// API 模型导出文件');
|
||||
buffer.writeln('// 基于 Swagger API 文档: ');
|
||||
buffer.writeln('// 自动生成于: ${DateTime.now().toString().substring(0, 10)} ');
|
||||
buffer.writeln('// 由 xy_swagger_generator by max 生成');
|
||||
buffer.writeln('// Copyright (C) 2025 YuanXuan. All rights reserved.');
|
||||
buffer.writeln('');
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ class GeneratorOptions {
|
|||
bool generateModels = false;
|
||||
bool generateDocs = false;
|
||||
bool useSimpleModels = false;
|
||||
bool separateModelFiles = true;
|
||||
const bool separateModelFiles = true;
|
||||
String modelsDirectory = 'models';
|
||||
String outputDirectory = 'generator';
|
||||
String endpointsFileName = 'api_paths.dart';
|
||||
|
|
|
|||
|
|
@ -863,7 +863,7 @@ class RetrofitApiGenerator extends BaseGenerator {
|
|||
param.name.isNotEmpty ? param.name : 'request'),
|
||||
type: bodyType,
|
||||
annotation: useRetrofit ? '@Body()' : '',
|
||||
required: true,
|
||||
required: false,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -877,7 +877,7 @@ class RetrofitApiGenerator extends BaseGenerator {
|
|||
name: 'request',
|
||||
type: bodyType,
|
||||
annotation: useRetrofit ? '@Body()' : '',
|
||||
required: true,
|
||||
required: false,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -1010,7 +1010,7 @@ class RetrofitApiGenerator extends BaseGenerator {
|
|||
buffer.writeln(' ) async {');
|
||||
|
||||
// 构建请求路径
|
||||
var requestPath = cleanPath;
|
||||
final requestPath = cleanPath;
|
||||
final pathParams =
|
||||
parameters.where((p) => p.annotation.contains('@Path')).toList();
|
||||
|
||||
|
|
@ -1525,7 +1525,7 @@ class RetrofitApiGenerator extends BaseGenerator {
|
|||
buffer.writeln('');
|
||||
|
||||
// 生成参数实体类
|
||||
buffer.writeln('@JsonSerializable(checked: true, includeIfNull:false)');
|
||||
buffer.writeln('@JsonSerializable(checked: true, includeIfNull: false)');
|
||||
buffer.writeln('class $className {');
|
||||
|
||||
// 生成属性
|
||||
|
|
|
|||
|
|
@ -265,8 +265,9 @@ class StringUtils {
|
|||
static String formatBytes(int bytes) {
|
||||
if (bytes < 1024) return '${bytes}B';
|
||||
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)}KB';
|
||||
if (bytes < 1024 * 1024 * 1024)
|
||||
if (bytes < 1024 * 1024 * 1024) {
|
||||
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}MB';
|
||||
}
|
||||
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)}GB';
|
||||
}
|
||||
|
||||
|
|
@ -344,10 +345,9 @@ class StringUtils {
|
|||
|
||||
/// 生成文件头注释
|
||||
static String generateFileHeader(String description, String source) {
|
||||
final timestamp = DateTime.now().toLocal().toString().split(" ").first;
|
||||
// final timestamp = DateTime.now().toLocal().toString().split(" ").first;
|
||||
return '''// $description
|
||||
// 基于 Swagger API 文档:
|
||||
// 自动生成于: $timestamp
|
||||
// 由 xy_swagger_generator by max 生成
|
||||
// Copyright (C) 2025 YuanXuan. All rights reserved.
|
||||
''';
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class TypeValidator {
|
|||
if (property.type == PropertyType.reference) {
|
||||
if (property.reference == null || property.reference!.isEmpty) {
|
||||
errors.add(
|
||||
ValidationError(
|
||||
const ValidationError(
|
||||
field: 'reference',
|
||||
message: '引用类型缺少引用目标',
|
||||
severity: ErrorSeverity.error,
|
||||
|
|
@ -217,7 +217,7 @@ class TypeValidator {
|
|||
// 基础语法检查
|
||||
if (code.trim().isEmpty) {
|
||||
errors.add(
|
||||
ValidationError(
|
||||
const ValidationError(
|
||||
field: 'code',
|
||||
message: '生成的代码为空',
|
||||
severity: ErrorSeverity.error,
|
||||
|
|
@ -228,7 +228,7 @@ class TypeValidator {
|
|||
// 检查括号匹配
|
||||
if (!_isBalancedBrackets(code)) {
|
||||
errors.add(
|
||||
ValidationError(
|
||||
const ValidationError(
|
||||
field: 'syntax',
|
||||
message: '代码中存在不匹配的括号',
|
||||
severity: ErrorSeverity.error,
|
||||
|
|
@ -241,7 +241,7 @@ class TypeValidator {
|
|||
case CodeType.model:
|
||||
if (!code.contains('class ') && !code.contains('enum ')) {
|
||||
errors.add(
|
||||
ValidationError(
|
||||
const ValidationError(
|
||||
field: 'content',
|
||||
message: '模型代码必须包含class或enum声明',
|
||||
severity: ErrorSeverity.error,
|
||||
|
|
@ -252,7 +252,7 @@ class TypeValidator {
|
|||
case CodeType.endpoints:
|
||||
if (!code.contains('class ')) {
|
||||
errors.add(
|
||||
ValidationError(
|
||||
const ValidationError(
|
||||
field: 'content',
|
||||
message: '端点代码必须包含class声明',
|
||||
severity: ErrorSeverity.error,
|
||||
|
|
@ -288,7 +288,7 @@ class TypeValidator {
|
|||
|
||||
if (model.enumValues.isEmpty) {
|
||||
errors.add(
|
||||
ValidationError(
|
||||
const ValidationError(
|
||||
field: 'enumValues',
|
||||
message: '枚举类型必须包含至少一个值',
|
||||
severity: ErrorSeverity.error,
|
||||
|
|
@ -311,7 +311,7 @@ class TypeValidator {
|
|||
final uniqueValues = model.enumValues.toSet();
|
||||
if (uniqueValues.length != model.enumValues.length) {
|
||||
errors.add(
|
||||
ValidationError(
|
||||
const ValidationError(
|
||||
field: 'enumValues',
|
||||
message: '枚举值存在重复',
|
||||
severity: ErrorSeverity.error,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ dependencies:
|
|||
json_annotation: ^4.8.1
|
||||
http: ^1.1.0
|
||||
|
||||
path: any
|
||||
logging: any
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
|
@ -23,5 +25,8 @@ dev_dependencies:
|
|||
json_serializable: ^6.7.1
|
||||
test: ^1.24.0
|
||||
|
||||
dio: any
|
||||
retrofit: any
|
||||
learning_officer_oa: any
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
|
@ -40,15 +40,23 @@ main() {
|
|||
case "$1" in
|
||||
all)
|
||||
dart run "$CLI_DART_FILE" generate --models --api --split-by-tags
|
||||
dart format .
|
||||
dart fix --apply
|
||||
;;
|
||||
models)
|
||||
dart run "$CLI_DART_FILE" generate --models
|
||||
dart format .
|
||||
dart fix --apply
|
||||
;;
|
||||
docs)
|
||||
dart run "$CLI_DART_FILE" generate --docs
|
||||
dart format .
|
||||
dart fix --apply
|
||||
;;
|
||||
api)
|
||||
dart run "$CLI_DART_FILE" generate --api
|
||||
dart format .
|
||||
dart fix --apply
|
||||
;;
|
||||
*)
|
||||
echo -e "${YELLOW}未知命令: $1${NC}"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import '../lib/core/models.dart';
|
||||
import 'package:swagger_generator_flutter/core/models.dart';
|
||||
|
||||
void main() {
|
||||
group('ApiPath', () {
|
||||
test('creates ApiPath with required fields', () {
|
||||
final path = ApiPath(
|
||||
const path = ApiPath(
|
||||
path: '/api/users',
|
||||
method: HttpMethod.get,
|
||||
summary: 'Get users',
|
||||
|
|
@ -29,7 +29,7 @@ void main() {
|
|||
|
||||
test('creates ApiPath with all fields', () {
|
||||
final parameters = [
|
||||
ApiParameter(
|
||||
const ApiParameter(
|
||||
name: 'id',
|
||||
location: ParameterLocation.path,
|
||||
required: true,
|
||||
|
|
@ -39,7 +39,7 @@ void main() {
|
|||
];
|
||||
|
||||
final responses = {
|
||||
'200': ApiResponse(
|
||||
'200': const ApiResponse(
|
||||
code: '200',
|
||||
description: 'Success',
|
||||
schema: {'type': 'object'},
|
||||
|
|
@ -47,7 +47,7 @@ void main() {
|
|||
),
|
||||
};
|
||||
|
||||
final requestBody = ApiRequestBody(
|
||||
const requestBody = ApiRequestBody(
|
||||
description: 'User data',
|
||||
required: true,
|
||||
content: {
|
||||
|
|
@ -77,7 +77,7 @@ void main() {
|
|||
|
||||
group('ApiParameter', () {
|
||||
test('creates ApiParameter with required fields', () {
|
||||
final param = ApiParameter(
|
||||
const param = ApiParameter(
|
||||
name: 'id',
|
||||
location: ParameterLocation.path,
|
||||
required: true,
|
||||
|
|
@ -93,7 +93,7 @@ void main() {
|
|||
});
|
||||
|
||||
test('creates ApiParameter with optional fields', () {
|
||||
final param = ApiParameter(
|
||||
const param = ApiParameter(
|
||||
name: 'page',
|
||||
location: ParameterLocation.query,
|
||||
required: false,
|
||||
|
|
@ -136,7 +136,7 @@ void main() {
|
|||
|
||||
group('ApiResponse', () {
|
||||
test('creates ApiResponse with required fields', () {
|
||||
final response = ApiResponse(
|
||||
const response = ApiResponse(
|
||||
code: '200',
|
||||
description: 'Success',
|
||||
schema: null,
|
||||
|
|
@ -190,7 +190,7 @@ void main() {
|
|||
|
||||
group('ApiRequestBody', () {
|
||||
test('creates ApiRequestBody with required fields', () {
|
||||
final requestBody = ApiRequestBody(
|
||||
const requestBody = ApiRequestBody(
|
||||
description: 'User data',
|
||||
required: true,
|
||||
content: null,
|
||||
|
|
@ -244,7 +244,7 @@ void main() {
|
|||
|
||||
group('ApiModel', () {
|
||||
test('creates ApiModel with required fields', () {
|
||||
final model = ApiModel(
|
||||
const model = ApiModel(
|
||||
name: 'User',
|
||||
description: 'User model',
|
||||
properties: {},
|
||||
|
|
@ -262,13 +262,13 @@ void main() {
|
|||
|
||||
test('creates ApiModel with properties', () {
|
||||
final properties = {
|
||||
'id': ApiProperty(
|
||||
'id': const ApiProperty(
|
||||
name: 'id',
|
||||
type: PropertyType.integer,
|
||||
description: 'User ID',
|
||||
required: true,
|
||||
),
|
||||
'name': ApiProperty(
|
||||
'name': const ApiProperty(
|
||||
name: 'name',
|
||||
type: PropertyType.string,
|
||||
description: 'User name',
|
||||
|
|
@ -288,7 +288,7 @@ void main() {
|
|||
});
|
||||
|
||||
test('creates enum ApiModel', () {
|
||||
final model = ApiModel(
|
||||
const model = ApiModel(
|
||||
name: 'UserStatus',
|
||||
description: 'User status enum',
|
||||
properties: {},
|
||||
|
|
@ -344,7 +344,7 @@ void main() {
|
|||
|
||||
group('ApiProperty', () {
|
||||
test('creates ApiProperty with required fields', () {
|
||||
final property = ApiProperty(
|
||||
const property = ApiProperty(
|
||||
name: 'id',
|
||||
type: PropertyType.integer,
|
||||
description: 'User ID',
|
||||
|
|
@ -359,7 +359,7 @@ void main() {
|
|||
});
|
||||
|
||||
test('creates ApiProperty with optional fields', () {
|
||||
final property = ApiProperty(
|
||||
const property = ApiProperty(
|
||||
name: 'name',
|
||||
type: PropertyType.string,
|
||||
description: 'User name',
|
||||
|
|
@ -497,7 +497,7 @@ void main() {
|
|||
|
||||
group('SwaggerDocument', () {
|
||||
test('creates SwaggerDocument with required fields', () {
|
||||
final document = SwaggerDocument(
|
||||
const document = SwaggerDocument(
|
||||
title: 'Test API',
|
||||
version: '1.0.0',
|
||||
description: 'Test API description',
|
||||
|
|
@ -578,7 +578,7 @@ void main() {
|
|||
|
||||
group('ApiController', () {
|
||||
test('creates ApiController with required fields', () {
|
||||
final controller = ApiController(
|
||||
const controller = ApiController(
|
||||
name: 'UserController',
|
||||
description: 'User management controller',
|
||||
paths: [],
|
||||
|
|
@ -591,7 +591,7 @@ void main() {
|
|||
|
||||
test('creates ApiController with paths', () {
|
||||
final paths = [
|
||||
ApiPath(
|
||||
const ApiPath(
|
||||
path: '/api/users',
|
||||
method: HttpMethod.get,
|
||||
summary: 'Get users',
|
||||
|
|
@ -602,7 +602,7 @@ void main() {
|
|||
responses: {},
|
||||
requestBody: null,
|
||||
),
|
||||
ApiPath(
|
||||
const ApiPath(
|
||||
path: '/api/users/{id}',
|
||||
method: HttpMethod.post,
|
||||
summary: 'Create user',
|
||||
|
|
@ -628,7 +628,7 @@ void main() {
|
|||
|
||||
test('creates ApiController from paths', () {
|
||||
final paths = [
|
||||
ApiPath(
|
||||
const ApiPath(
|
||||
path: '/api/users',
|
||||
method: HttpMethod.get,
|
||||
summary: 'Get users',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import '../lib/utils/string_utils.dart';
|
||||
import 'package:swagger_generator_flutter/utils/string_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('StringUtils', () {
|
||||
|
|
@ -211,10 +211,10 @@ void main() {
|
|||
|
||||
group('formatDuration', () {
|
||||
test('formats duration correctly', () {
|
||||
expect(
|
||||
StringUtils.formatDuration(Duration(milliseconds: 500)), '500毫秒');
|
||||
expect(StringUtils.formatDuration(Duration(seconds: 1)), '1.00秒');
|
||||
expect(StringUtils.formatDuration(Duration(seconds: 2)), '2.00秒');
|
||||
expect(StringUtils.formatDuration(const Duration(milliseconds: 500)),
|
||||
'500毫秒');
|
||||
expect(StringUtils.formatDuration(const Duration(seconds: 1)), '1.00秒');
|
||||
expect(StringUtils.formatDuration(const Duration(seconds: 2)), '2.00秒');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue