flu-cli-core 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  从 V6.0 开始,核心生成器由传统的命令式逻辑转变为 **原子化任务流水线** 架构。
8
8
 
9
9
  ### 1. 乐高式搭建 (The Lego Model)
10
+
10
11
  `ProjectGenerator` 不再直接操作文件,而是作为一个 **Orchestrator (指挥官)**。它通过 `ProjectPipeline` 编排一系列 `IProjectTask`。
11
12
 
12
13
  ```mermaid
@@ -22,6 +23,7 @@ graph LR
22
23
  ```
23
24
 
24
25
  ### 2. 骨架 + 增强 (Skeleton + Enrichment)
26
+
25
27
  - **Skeleton (骨架)**: 从 Git 或本地加载的纯净 Flutter 代码模板。
26
28
  - **Enrichment (增强)**: CLI 通过任务流水线动态注入的功能层(如:`NetworkEnrichTask` 注入 Dio 封装,`StateManagerEnrichTask` 注入状态管理适配器)。
27
29
 
@@ -35,10 +37,13 @@ graph LR
35
37
  ## 🚀 核心组件
36
38
 
37
39
  ### 生成器 (Generators)
40
+
38
41
  - **ProjectGenerator**: 采用 Pipeline 模式的项目实例化引擎。
39
42
  - **Page/ViewModel/Model Generator**: 针对不同架构模板的高级代码生成器。
43
+ - **Service Generator**: 智能服务层生成器,支持自动网络层检测与 Mock 降级。
40
44
 
41
45
  ### 任务体系 (Tasks)
46
+
42
47
  - `FlutterInitTask`: 确保原生 Flutter 环境就绪。
43
48
  - `TemplateCopyTask`: 智能处理模板复制与冗余清理。
44
49
  - `NetworkEnrichTask`: 声明式注入网络层基础设施。
@@ -48,6 +53,7 @@ graph LR
48
53
  ## 🛠 开发与扩展
49
54
 
50
55
  ### 如何添加一个新任务?
56
+
51
57
  1. 在 `src/generators/tasks/` 创建实现 `IProjectTask` 的类。
52
58
  2. 在 `ProjectGenerator.generate` 中通过 `.addTask()` 链式调用。
53
59
 
package/dist/index.cjs CHANGED
@@ -1545,6 +1545,8 @@ function getTemplatesRootDir() {
1545
1545
  searchDirs.push((0, import_path6.join)(temp, "packages", "core", "templates"));
1546
1546
  searchDirs.push((0, import_path6.join)(temp, "templates"));
1547
1547
  searchDirs.push((0, import_path6.join)(temp, "..", "templates"));
1548
+ searchDirs.push((0, import_path6.join)(temp, "dist", "templates"));
1549
+ searchDirs.push((0, import_path6.join)(temp, "..", "dist", "templates"));
1548
1550
  temp = (0, import_path6.dirname)(temp);
1549
1551
  if (temp === (0, import_path6.dirname)(temp)) break;
1550
1552
  }
@@ -2015,19 +2017,16 @@ class ${namePascal} extends StatelessWidget {
2015
2017
  }
2016
2018
  function getSimpleModel(namePascal) {
2017
2019
  return `class ${namePascal} {
2018
- // TODO: Add properties
2019
2020
 
2020
2021
  ${namePascal}();
2021
2022
 
2022
2023
  factory ${namePascal}.fromJson(Map<String, dynamic> json) {
2023
2024
  return ${namePascal}(
2024
- // TODO: Map json to properties
2025
2025
  );
2026
2026
  }
2027
2027
 
2028
2028
  Map<String, dynamic> toJson() {
2029
2029
  return {
2030
- // TODO: Map properties to json
2031
2030
  };
2032
2031
  }
2033
2032
  }
@@ -2830,7 +2829,8 @@ async function generateService(name, options = {}, customLogger) {
2830
2829
  }
2831
2830
  const templateType = selectTemplate(outputDir, serviceConfig);
2832
2831
  let content = null;
2833
- const snippetKey = serviceConfig?.snippetKey || "flu.service";
2832
+ const defaultSnippetKey = templateType === "network" ? "flu.service.network" : "flu.service";
2833
+ const snippetKey = serviceConfig?.snippetKey || defaultSnippetKey;
2834
2834
  content = getSnippetContent(outputDir, snippetKey, {
2835
2835
  Name: namePascal,
2836
2836
  snake_name: nameSnake,
@@ -2892,6 +2892,7 @@ class ${namePascal}Service {
2892
2892
  int page = 1,
2893
2893
  int pageSize = 10,
2894
2894
  }) async {
2895
+ // \u964D\u7EA7\u4E0E Mock \u5904\u7406
2895
2896
  if (AppConfig.I.useMockData) {
2896
2897
  return _loadMockData(page, pageSize);
2897
2898
  }
@@ -2901,28 +2902,26 @@ class ${namePascal}Service {
2901
2902
  queryParameters: {'page': page, 'pageSize': pageSize},
2902
2903
  );
2903
2904
 
2904
- // \u964D\u7EA7\u5904\u7406\uFF1A\u8BF7\u6C42\u5931\u8D25\u65F6\u8FD4\u56DE Mock \u6570\u636E
2905
+ // \u6210\u529F\u5904\u7406
2905
2906
  if (response.isSuccess && response.data is List) {
2906
2907
  return response.data as List;
2907
2908
  }
2908
2909
 
2910
+ // \u5931\u8D25\u56DE\u9000
2909
2911
  return _loadMockData(page, pageSize);
2910
2912
  }
2911
2913
 
2912
2914
  /// \u6839\u636E ID \u83B7\u53D6\u8BE6\u60C5
2913
2915
  Future<dynamic> fetchById(String id) async {
2914
2916
  if (AppConfig.I.useMockData) {
2915
- return null; // \u8FD4\u56DE Mock \u6570\u636E
2917
+ return null;
2916
2918
  }
2917
2919
 
2918
2920
  final response = await _http.get('/${nameSnake}/$id');
2919
2921
  return response.isSuccess ? response.data : null;
2920
2922
  }
2921
2923
 
2922
- /// Mock \u6570\u636E\u52A0\u8F7D
2923
2924
  List<dynamic> _loadMockData(int page, int pageSize) {
2924
- // \u6A21\u62DF\u6570\u636E\u52A0\u8F7D\u903B\u8F91
2925
- // \u793A\u4F8B: \u8FD4\u56DE\u6D4B\u8BD5\u6570\u636E\u6216\u4ECE\u672C\u5730\u5B58\u50A8\u8BFB\u53D6
2926
2925
  return [];
2927
2926
  }
2928
2927
  }
package/dist/index.js CHANGED
@@ -931,6 +931,8 @@ function getTemplatesRootDir() {
931
931
  searchDirs.push(join6(temp, "packages", "core", "templates"));
932
932
  searchDirs.push(join6(temp, "templates"));
933
933
  searchDirs.push(join6(temp, "..", "templates"));
934
+ searchDirs.push(join6(temp, "dist", "templates"));
935
+ searchDirs.push(join6(temp, "..", "dist", "templates"));
934
936
  temp = dirname(temp);
935
937
  if (temp === dirname(temp)) break;
936
938
  }
@@ -1399,19 +1401,16 @@ class ${namePascal} extends StatelessWidget {
1399
1401
  }
1400
1402
  function getSimpleModel(namePascal) {
1401
1403
  return `class ${namePascal} {
1402
- // TODO: Add properties
1403
1404
 
1404
1405
  ${namePascal}();
1405
1406
 
1406
1407
  factory ${namePascal}.fromJson(Map<String, dynamic> json) {
1407
1408
  return ${namePascal}(
1408
- // TODO: Map json to properties
1409
1409
  );
1410
1410
  }
1411
1411
 
1412
1412
  Map<String, dynamic> toJson() {
1413
1413
  return {
1414
- // TODO: Map properties to json
1415
1414
  };
1416
1415
  }
1417
1416
  }
@@ -2210,7 +2209,8 @@ async function generateService(name, options = {}, customLogger) {
2210
2209
  }
2211
2210
  const templateType = selectTemplate(outputDir, serviceConfig);
2212
2211
  let content = null;
2213
- const snippetKey = serviceConfig?.snippetKey || "flu.service";
2212
+ const defaultSnippetKey = templateType === "network" ? "flu.service.network" : "flu.service";
2213
+ const snippetKey = serviceConfig?.snippetKey || defaultSnippetKey;
2214
2214
  content = getSnippetContent(outputDir, snippetKey, {
2215
2215
  Name: namePascal,
2216
2216
  snake_name: nameSnake,
@@ -2272,6 +2272,7 @@ class ${namePascal}Service {
2272
2272
  int page = 1,
2273
2273
  int pageSize = 10,
2274
2274
  }) async {
2275
+ // \u964D\u7EA7\u4E0E Mock \u5904\u7406
2275
2276
  if (AppConfig.I.useMockData) {
2276
2277
  return _loadMockData(page, pageSize);
2277
2278
  }
@@ -2281,28 +2282,26 @@ class ${namePascal}Service {
2281
2282
  queryParameters: {'page': page, 'pageSize': pageSize},
2282
2283
  );
2283
2284
 
2284
- // \u964D\u7EA7\u5904\u7406\uFF1A\u8BF7\u6C42\u5931\u8D25\u65F6\u8FD4\u56DE Mock \u6570\u636E
2285
+ // \u6210\u529F\u5904\u7406
2285
2286
  if (response.isSuccess && response.data is List) {
2286
2287
  return response.data as List;
2287
2288
  }
2288
2289
 
2290
+ // \u5931\u8D25\u56DE\u9000
2289
2291
  return _loadMockData(page, pageSize);
2290
2292
  }
2291
2293
 
2292
2294
  /// \u6839\u636E ID \u83B7\u53D6\u8BE6\u60C5
2293
2295
  Future<dynamic> fetchById(String id) async {
2294
2296
  if (AppConfig.I.useMockData) {
2295
- return null; // \u8FD4\u56DE Mock \u6570\u636E
2297
+ return null;
2296
2298
  }
2297
2299
 
2298
2300
  final response = await _http.get('/${nameSnake}/$id');
2299
2301
  return response.isSuccess ? response.data : null;
2300
2302
  }
2301
2303
 
2302
- /// Mock \u6570\u636E\u52A0\u8F7D
2303
2304
  List<dynamic> _loadMockData(int page, int pageSize) {
2304
- // \u6A21\u62DF\u6570\u636E\u52A0\u8F7D\u903B\u8F91
2305
- // \u793A\u4F8B: \u8FD4\u56DE\u6D4B\u8BD5\u6570\u636E\u6216\u4ECE\u672C\u5730\u5B58\u50A8\u8BFB\u53D6
2306
2305
  return [];
2307
2306
  }
2308
2307
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flu-cli-core",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Core logic for flu-cli",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -49,4 +49,4 @@
49
49
  "tsup": "^8.0.1",
50
50
  "typescript": "^5.3.3"
51
51
  }
52
- }
52
+ }
@@ -245,6 +245,58 @@
245
245
  ""
246
246
  ]
247
247
  },
248
+ "flu.service.network": {
249
+ "prefix": "serviceNetwork",
250
+ "description": "Service (Network Enabled)",
251
+ "body": [
252
+ "import '${relative_core_path}';",
253
+ "",
254
+ "class ${1:Name}Service {",
255
+ " final AppHttp _http;",
256
+ "",
257
+ " ${1:Name}Service({AppHttp? http}) : _http = http ?? AppHttp();",
258
+ "",
259
+ " /// 获取列表数据",
260
+ " Future<List<dynamic>> fetchList({",
261
+ " int page = 1,",
262
+ " int pageSize = 10,",
263
+ " }) async {",
264
+ " // 降级与 Mock 处理",
265
+ " if (AppConfig.I.useMockData) {",
266
+ " return _loadMockData(page, pageSize);",
267
+ " }",
268
+ "",
269
+ " final response = await _http.get(",
270
+ " '/${snake_name}/list',",
271
+ " queryParameters: {'page': page, 'pageSize': pageSize},",
272
+ " );",
273
+ "",
274
+ " // 成功处理",
275
+ " if (response.isSuccess && response.data is List) {",
276
+ " return response.data as List;",
277
+ " }",
278
+ "",
279
+ " // 失败回退",
280
+ " return _loadMockData(page, pageSize);",
281
+ " }",
282
+ "",
283
+ " /// 根据 ID 获取详情",
284
+ " Future<dynamic> fetchById(String id) async {",
285
+ " if (AppConfig.I.useMockData) {",
286
+ " return null;",
287
+ " }",
288
+ "",
289
+ " final response = await _http.get('/${snake_name}/$id');",
290
+ " return response.isSuccess ? response.data : null;",
291
+ " }",
292
+ "",
293
+ " List<dynamic> _loadMockData(int page, int pageSize) {",
294
+ " return [];",
295
+ " }",
296
+ "}",
297
+ ""
298
+ ]
299
+ },
248
300
  "flu.component": {
249
301
  "prefix": "component",
250
302
  "description": "通用组件",