flu-cli-core 1.0.0

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.
Files changed (56) hide show
  1. package/README.md +60 -0
  2. package/dist/chunk-FOMWV2YP.js +378 -0
  3. package/dist/chunk-SW6YDKXI.js +112 -0
  4. package/dist/factory-6DDXZYQP.js +6 -0
  5. package/dist/index.cjs +4668 -0
  6. package/dist/index.d.cts +644 -0
  7. package/dist/index.d.ts +644 -0
  8. package/dist/index.js +4037 -0
  9. package/dist/upgrade_snippets-XFR7Q444.js +8 -0
  10. package/locales/en-US.json +59 -0
  11. package/locales/zh-CN.json +59 -0
  12. package/package.json +52 -0
  13. package/templates/README.md +129 -0
  14. package/templates/core_files/base/base_list_page.dart.template +225 -0
  15. package/templates/core_files/base/base_list_viewmodel.dart.template +164 -0
  16. package/templates/core_files/base/base_page.dart.template +252 -0
  17. package/templates/core_files/base/base_viewmodel.dart.template +68 -0
  18. package/templates/core_files/base/index.dart.template +5 -0
  19. package/templates/core_files/config/app_config.dart.template +142 -0
  20. package/templates/core_files/config/app_initializer.dart.template +74 -0
  21. package/templates/core_files/config/index.dart.template +3 -0
  22. package/templates/core_files/index.dart.template +8 -0
  23. package/templates/core_files/network/README.md +378 -0
  24. package/templates/core_files/network/app_error_code.dart.template +49 -0
  25. package/templates/core_files/network/app_http.dart.template +306 -0
  26. package/templates/core_files/network/app_response.dart.template +81 -0
  27. package/templates/core_files/network/index.dart.template +12 -0
  28. package/templates/core_files/network/interceptors/app_response_interceptor.dart.template +44 -0
  29. package/templates/core_files/network/interceptors/auth_interceptor.dart.template +30 -0
  30. package/templates/core_files/network/interceptors/error_interceptor.dart.template +48 -0
  31. package/templates/core_files/network/interceptors/index.dart.template +6 -0
  32. package/templates/core_files/network/interceptors/log_interceptor.dart.template +97 -0
  33. package/templates/core_files/network/interceptors/network_error_interceptor.dart.template +58 -0
  34. package/templates/core_files/network/interceptors/retry_interceptor.dart.template +69 -0
  35. package/templates/core_files/network/response_adapter.dart.template +69 -0
  36. package/templates/core_files/router/app_routes.dart.template +32 -0
  37. package/templates/core_files/router/index.dart.template +3 -0
  38. package/templates/core_files/router/navigator_util_getx.dart.template +131 -0
  39. package/templates/core_files/router/navigator_util_material.dart.template +191 -0
  40. package/templates/core_files/storage/index.dart.template +3 -0
  41. package/templates/core_files/storage/storage_keys.dart.template +34 -0
  42. package/templates/core_files/storage/storage_util.dart.template +102 -0
  43. package/templates/core_files/theme/app_theme.dart.template +37 -0
  44. package/templates/core_files/theme/index.dart.template +3 -0
  45. package/templates/core_files/theme/status_views_theme.dart.template +40 -0
  46. package/templates/core_files/utils/index.dart.template +2 -0
  47. package/templates/core_files/utils/loading_util.dart.template +55 -0
  48. package/templates/core_files/utils/toast_util.dart.template +128 -0
  49. package/templates/examples/eg_list_page.dart.template +340 -0
  50. package/templates/examples/eg_list_viewmodel.dart.template +31 -0
  51. package/templates/examples/eg_service.dart.template +78 -0
  52. package/templates/examples/mock_data.dart.template +50388 -0
  53. package/templates/examples/tu_chong_model.dart.template +633 -0
  54. package/templates/request_helper.dart.template +59 -0
  55. package/templates/snippets/flu-cli.code-snippets +268 -0
  56. package/templates/snippets/flu-cli.code-snippets.backup +268 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,4668 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/utils/logger.ts
34
+ var import_chalk, ConsoleLogger, logger;
35
+ var init_logger = __esm({
36
+ "src/utils/logger.ts"() {
37
+ "use strict";
38
+ import_chalk = __toESM(require("chalk"), 1);
39
+ ConsoleLogger = class {
40
+ info(message) {
41
+ console.log(import_chalk.default.blue("\u2139\uFE0F " + message));
42
+ }
43
+ success(message) {
44
+ console.log(import_chalk.default.green("\u2705 " + message));
45
+ }
46
+ warn(message) {
47
+ console.log(import_chalk.default.yellow("\u26A0\uFE0F " + message));
48
+ }
49
+ error(message) {
50
+ console.log(import_chalk.default.red("\u274C " + message));
51
+ }
52
+ title(message) {
53
+ console.log(import_chalk.default.bold.cyan("\n" + message + "\n"));
54
+ }
55
+ newLine() {
56
+ console.log("");
57
+ }
58
+ };
59
+ logger = new ConsoleLogger();
60
+ }
61
+ });
62
+
63
+ // src/generators/tasks/adapters/provider_adapter.ts
64
+ var ProviderAdapter;
65
+ var init_provider_adapter = __esm({
66
+ "src/generators/tasks/adapters/provider_adapter.ts"() {
67
+ "use strict";
68
+ ProviderAdapter = class {
69
+ name;
70
+ constructor(name = "provider") {
71
+ this.name = name;
72
+ }
73
+ getDependencies() {
74
+ return this.name === "provider" ? ["provider: ^6.1.1"] : [];
75
+ }
76
+ getBaseViewModelTemplate(context) {
77
+ return null;
78
+ }
79
+ getBasePageParent(isListPage) {
80
+ return isListPage ? "BaseListPage" : "BasePage";
81
+ }
82
+ getBaseViewModelParent(isListPage) {
83
+ return isListPage ? "BaseListViewModel" : "BaseViewModel";
84
+ }
85
+ getImports(relativeCorePath) {
86
+ if (this.name === "provider") {
87
+ return [
88
+ `import 'package:provider/provider.dart';`,
89
+ `import '${relativeCorePath}';`
90
+ ];
91
+ }
92
+ return [`import '${relativeCorePath}';`];
93
+ }
94
+ patchAppEntry(content, context) {
95
+ return content;
96
+ }
97
+ getCommonInfo() {
98
+ return ` ViewState _state = ViewState.idle;
99
+ String? _errorMessage;
100
+ bool _isRefreshing = false;
101
+
102
+ // ==================== Getters ====================
103
+ // \u5F53\u524D\u89C6\u56FE\u72B6\u6001
104
+ ViewState get state => _state;
105
+ // \u9519\u8BEF\u4FE1\u606F
106
+ String? get errorMessage => _errorMessage;
107
+ // \u662F\u5426\u6B63\u5728\u52A0\u8F7D
108
+ bool get isLoading => _state == ViewState.loading;
109
+ // \u662F\u5426\u52A0\u8F7D\u5931\u8D25
110
+ bool get isError => _state == ViewState.error;
111
+ // \u662F\u5426\u52A0\u8F7D\u6210\u529F
112
+ bool get isSuccess => _state == ViewState.success;
113
+ // \u662F\u5426\u7A7A\u95F2\u72B6\u6001
114
+ bool get isIdle => _state == ViewState.idle;
115
+ // \u662F\u5426\u6B63\u5728\u5237\u65B0
116
+ bool get isRefreshing => _isRefreshing;
117
+
118
+ // ==================== \u72B6\u6001\u8BBE\u7F6E ====================
119
+
120
+ /// \u8BBE\u7F6E\u89C6\u56FE\u72B6\u6001
121
+ void setState(ViewState state, {String? error}) {
122
+ _state = state;
123
+ _errorMessage = error;
124
+ notifyListeners();
125
+ }
126
+
127
+ /// \u8BBE\u7F6E\u5237\u65B0\u72B6\u6001
128
+ void setRefreshing(bool refreshing) {
129
+ _isRefreshing = refreshing;
130
+ notifyListeners();
131
+ }
132
+ `;
133
+ }
134
+ };
135
+ }
136
+ });
137
+
138
+ // src/generators/tasks/adapters/getx_adapter.ts
139
+ var GetXAdapter;
140
+ var init_getx_adapter = __esm({
141
+ "src/generators/tasks/adapters/getx_adapter.ts"() {
142
+ "use strict";
143
+ GetXAdapter = class {
144
+ name = "getx";
145
+ getDependencies() {
146
+ return ["get: ^4.6.6"];
147
+ }
148
+ getBaseViewModelTemplate(context) {
149
+ return `import 'package:get/get.dart';
150
+
151
+ enum ViewState { idle, loading, success, error }
152
+
153
+ class BaseViewModel extends GetxController {
154
+ ViewState _state = ViewState.idle;
155
+ String? _errorMessage;
156
+ bool _isRefreshing = false;
157
+ bool _disposed = false;
158
+
159
+ // ==================== Getters ====================
160
+ ViewState get state => _state;
161
+ String? get errorMessage => _errorMessage;
162
+ bool get isLoading => _state == ViewState.loading;
163
+ bool get isError => _state == ViewState.error;
164
+ bool get isSuccess => _state == ViewState.success;
165
+ bool get isIdle => _state == ViewState.idle;
166
+ bool get isRefreshing => _isRefreshing;
167
+ // GetX \u4F7F\u7528 isClosed \u5224\u65AD\u662F\u5426\u5DF2\u91CA\u653E
168
+ bool get isDisposed => _disposed || isClosed;
169
+
170
+ // ==================== \u72B6\u6001\u8BBE\u7F6E ====================
171
+ void setState(ViewState state, {String? error}) {
172
+ if (_disposed || isClosed) return;
173
+ _state = state;
174
+ _errorMessage = error;
175
+ update(); // GetX \u4F7F\u7528 update() \u800C\u975E notifyListeners()
176
+ }
177
+
178
+ void setRefreshing(bool refreshing) {
179
+ if (_disposed || isClosed) return;
180
+ _isRefreshing = refreshing;
181
+ update();
182
+ }
183
+
184
+ /// \u901A\u77E5\u6570\u636E\u53D8\u5316(\u4EC5\u7528\u4E8E\u5C40\u90E8\u5237\u65B0)
185
+ void notifyDataChange() {
186
+ if (_disposed || isClosed) return;
187
+ update();
188
+ }
189
+
190
+ // ==================== \u751F\u547D\u5468\u671F ====================
191
+ /// \u521D\u59CB\u5316\u94A9\u5B50
192
+ @override
193
+ Future<void> onInit() async {
194
+ super.onInit();
195
+ // \u5B50\u7C7B\u53EF\u4EE5\u91CD\u5199\u6B64\u65B9\u6CD5\u8FDB\u884C\u521D\u59CB\u5316
196
+ }
197
+
198
+ /// \u5237\u65B0\u6570\u636E(\u94A9\u5B50)
199
+ Future<void> refreshData() async {
200
+ // \u5B50\u7C7B\u53EF\u4EE5\u91CD\u5199\u6B64\u65B9\u6CD5\u5B9E\u73B0\u5237\u65B0\u903B\u8F91
201
+ }
202
+
203
+ @override
204
+ void onClose() {
205
+ _disposed = true;
206
+ super.onClose();
207
+ }
208
+ }
209
+ `;
210
+ }
211
+ getBasePageParent(isListPage) {
212
+ return isListPage ? "BaseListPage" : "BasePage";
213
+ }
214
+ getBaseViewModelParent(isListPage) {
215
+ return isListPage ? "BaseListViewModel" : "BaseViewModel";
216
+ }
217
+ getImports(relativeCorePath) {
218
+ return [
219
+ `import '${relativeCorePath}';`
220
+ ];
221
+ }
222
+ /**
223
+ * 修改 app.dart
224
+ */
225
+ patchAppEntry(content, context) {
226
+ let raw = content;
227
+ if (!raw.includes("GetMaterialApp(")) {
228
+ raw = raw.replace(/MaterialApp\s*\(/, "GetMaterialApp(");
229
+ }
230
+ if (raw.includes("GetMaterialApp(") && !raw.includes("import 'package:get/get.dart'")) {
231
+ if (raw.includes("import 'package:flutter/material.dart';")) {
232
+ raw = raw.replace(
233
+ "import 'package:flutter/material.dart';",
234
+ "import 'package:flutter/material.dart';\nimport 'package:get/get.dart';"
235
+ );
236
+ } else {
237
+ raw = `import 'package:get/get.dart';
238
+ ` + raw;
239
+ }
240
+ }
241
+ if (raw.includes("routes: AppRoutes.routes")) {
242
+ raw = raw.replace("routes: AppRoutes.routes", "getPages: AppRoutes.pages");
243
+ }
244
+ if (raw.includes("navigatorKey: NavigatorUtil.navigatorKey")) {
245
+ raw = raw.replace(/navigatorKey:\s*NavigatorUtil\.navigatorKey\s*,?/g, "");
246
+ }
247
+ if (raw.includes("scaffoldMessengerKey: NavigatorUtil.scaffoldMessengerKey")) {
248
+ raw = raw.replace(/scaffoldMessengerKey:\s*NavigatorUtil\.scaffoldMessengerKey\s*,?\s*\n?/g, "");
249
+ }
250
+ return raw;
251
+ }
252
+ /**
253
+ * 修改 app_routes.dart
254
+ */
255
+ patchRoutes(content, context) {
256
+ let raw = content;
257
+ if (raw.includes("static List<GetPage> get pages")) {
258
+ return raw;
259
+ }
260
+ if (!raw.includes("import 'package:get/get.dart'")) {
261
+ if (raw.includes("import 'package:flutter/material.dart';")) {
262
+ raw = raw.replace(
263
+ "import 'package:flutter/material.dart';",
264
+ "import 'package:get/get.dart';"
265
+ );
266
+ } else {
267
+ raw = `import 'package:get/get.dart';
268
+ ` + raw;
269
+ }
270
+ }
271
+ const startMarker = "// __ROUTE_CONFIG_START__";
272
+ const endMarker = "// __ROUTE_CONFIG_END__";
273
+ const pagesGetter = `
274
+ /// GetX \u8DEF\u7531\u6620\u5C04
275
+ /// \u5728 app.dart \u4E2D\u4F7F\u7528: GetMaterialApp(getPages: AppRoutes.pages, ...)
276
+ static List<GetPage> get pages {
277
+ return [
278
+ GetPage(name: home, page: () => const HomePage()),
279
+ GetPage(name: userList, page: () => const UserListPage()),
280
+ // --- \u5728\u6B64\u4E0B\u65B9\u6DFB\u52A0\u60A8\u7684\u81EA\u5B9A\u4E49\u8DEF\u7531 ---
281
+ ];
282
+ }
283
+ `;
284
+ const startIndex = raw.indexOf(startMarker);
285
+ const endIndex = raw.indexOf(endMarker);
286
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
287
+ const before = raw.substring(0, startIndex);
288
+ const after = raw.substring(endIndex + endMarker.length);
289
+ raw = before + pagesGetter.trim() + after;
290
+ } else {
291
+ const standardRoutesPattern = /\s*\/\/\/\s*Material\s+路由映射[\s\S]*?static\s+Map<String,\s*WidgetBuilder>\s*get\s*routes\s*\{\s*return\s*\{[\s\S]*?\}\s*;\s*\}/;
292
+ if (standardRoutesPattern.test(raw)) {
293
+ raw = raw.replace(standardRoutesPattern, pagesGetter);
294
+ } else {
295
+ const lastBraceIndex = raw.lastIndexOf("}");
296
+ if (lastBraceIndex !== -1) {
297
+ raw = raw.slice(0, lastBraceIndex) + "\n" + pagesGetter + raw.slice(lastBraceIndex);
298
+ }
299
+ }
300
+ }
301
+ return raw;
302
+ }
303
+ };
304
+ }
305
+ });
306
+
307
+ // src/generators/tasks/adapters/riverpod_adapter.ts
308
+ var import_path8, import_fs_extra4, RiverpodAdapter;
309
+ var init_riverpod_adapter = __esm({
310
+ "src/generators/tasks/adapters/riverpod_adapter.ts"() {
311
+ "use strict";
312
+ import_path8 = require("path");
313
+ import_fs_extra4 = __toESM(require("fs-extra"), 1);
314
+ RiverpodAdapter = class {
315
+ name = "riverpod";
316
+ getDependencies() {
317
+ return ["flutter_riverpod: ^2.4.9"];
318
+ }
319
+ getBasePageParent(isListPage) {
320
+ return isListPage ? "BaseListPageRiverpod" : "BasePageRiverpod";
321
+ }
322
+ getBaseViewModelParent(isListPage) {
323
+ return isListPage ? "BaseListNotifier" : "BaseNotifier";
324
+ }
325
+ getImports(relativeCorePath) {
326
+ return [
327
+ `import 'package:flutter_riverpod/flutter_riverpod.dart';`,
328
+ `import '${relativeCorePath}';`
329
+ ];
330
+ }
331
+ getBaseViewModelTemplate(context) {
332
+ return null;
333
+ }
334
+ patchAppEntry(content, context) {
335
+ let raw = content;
336
+ if (raw.includes("runApp(") && !raw.includes("ProviderScope")) {
337
+ raw = raw.replace(/runApp\((.*?)\)/, "runApp(const ProviderScope(child: $1))");
338
+ if (!raw.includes("import 'package:flutter_riverpod/flutter_riverpod.dart'")) {
339
+ raw = `import 'package:flutter_riverpod/flutter_riverpod.dart';
340
+ ` + raw;
341
+ }
342
+ }
343
+ return raw;
344
+ }
345
+ async onEnrich(context) {
346
+ const { projectPath } = context;
347
+ const baseDir = (0, import_path8.join)(projectPath, "lib", "core", "base");
348
+ await import_fs_extra4.default.ensureDir(baseDir);
349
+ await import_fs_extra4.default.writeFile((0, import_path8.join)(baseDir, "view_state.dart"), "enum ViewState { idle, loading, success, error }\n");
350
+ await import_fs_extra4.default.writeFile((0, import_path8.join)(baseDir, "base_state.dart"), `
351
+ import 'view_state.dart';
352
+
353
+ class BaseState {
354
+ final ViewState state;
355
+ final bool isRefreshing;
356
+ final String? error;
357
+
358
+ const BaseState({
359
+ this.state = ViewState.idle,
360
+ this.isRefreshing = false,
361
+ this.error,
362
+ });
363
+
364
+ BaseState copyWith({ ViewState? state, bool? isRefreshing, String? error }) {
365
+ return BaseState(
366
+ state: state ?? this.state,
367
+ isRefreshing: isRefreshing ?? this.isRefreshing,
368
+ error: error ?? this.error,
369
+ );
370
+ }
371
+
372
+ bool get isLoading => state == ViewState.loading;
373
+ bool get isSuccess => state == ViewState.success;
374
+ bool get isError => state == ViewState.error;
375
+ bool get isIdle => state == ViewState.idle;
376
+ }
377
+ `);
378
+ await import_fs_extra4.default.writeFile((0, import_path8.join)(baseDir, "base_notifier.dart"), `
379
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
380
+ import 'view_state.dart';
381
+ import 'base_state.dart';
382
+
383
+ abstract class BaseNotifier<T extends BaseState> extends Notifier<T> {
384
+ T createInitialState();
385
+ @override
386
+ T build() => createInitialState();
387
+
388
+ void setLoading() { state = (state.copyWith(state: ViewState.loading, error: null)) as T; }
389
+ void setSuccess() { state = (state.copyWith(state: ViewState.success)) as T; }
390
+ void setError(String message) { state = (state.copyWith(state: ViewState.error, error: message)) as T; }
391
+ void setRefreshing(bool refreshing) { state = (state.copyWith(isRefreshing: refreshing)) as T; }
392
+
393
+ Future<void> run(Future<void> Function() task) async {
394
+ setLoading();
395
+ try {
396
+ await task();
397
+ setSuccess();
398
+ } catch (e) {
399
+ setError(e.toString());
400
+ }
401
+ }
402
+ }
403
+ `);
404
+ await import_fs_extra4.default.writeFile((0, import_path8.join)(baseDir, "base_page_riverpod.dart"), `
405
+ import 'package:flutter/material.dart';
406
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
407
+ import 'base_state.dart';
408
+
409
+ typedef ContentBuilder<TState extends BaseState, TNotifier extends Notifier<TState>> = Widget Function(BuildContext context, TState state, TNotifier vm);
410
+
411
+ class BasePageRiverpod<TState extends BaseState, TNotifier extends Notifier<TState>> extends ConsumerWidget {
412
+ final ProviderListenable<TState> provider;
413
+ final String title;
414
+ final ContentBuilder<TState, TNotifier> builder;
415
+
416
+ const BasePageRiverpod({ super.key, required this.provider, required this.title, required this.builder });
417
+
418
+ @override
419
+ Widget build(BuildContext context, WidgetRef ref) {
420
+ final state = ref.watch(provider);
421
+ final vm = ref.read(provider as dynamic).notifier as TNotifier;
422
+ return Scaffold(
423
+ appBar: AppBar(title: Text(title)),
424
+ body: Center(
425
+ child: state.isLoading
426
+ ? const CircularProgressIndicator()
427
+ : state.isError
428
+ ? Text('Error: ' + (state.error ?? ''))
429
+ : builder(context, state, vm),
430
+ ),
431
+ );
432
+ }
433
+ }
434
+ `);
435
+ }
436
+ };
437
+ }
438
+ });
439
+
440
+ // src/generators/tasks/adapters/factory.ts
441
+ var factory_exports = {};
442
+ __export(factory_exports, {
443
+ StateManagerAdapterFactory: () => StateManagerAdapterFactory
444
+ });
445
+ var StateManagerAdapterFactory;
446
+ var init_factory = __esm({
447
+ "src/generators/tasks/adapters/factory.ts"() {
448
+ "use strict";
449
+ init_provider_adapter();
450
+ init_getx_adapter();
451
+ init_riverpod_adapter();
452
+ StateManagerAdapterFactory = class {
453
+ static getAdapter(name) {
454
+ switch (name.toLowerCase()) {
455
+ case "getx":
456
+ return new GetXAdapter();
457
+ case "provider":
458
+ return new ProviderAdapter("provider");
459
+ case "riverpod":
460
+ return new RiverpodAdapter();
461
+ default:
462
+ return new ProviderAdapter("default");
463
+ }
464
+ }
465
+ };
466
+ }
467
+ });
468
+
469
+ // src/commands/upgrade_snippets.ts
470
+ var upgrade_snippets_exports = {};
471
+ __export(upgrade_snippets_exports, {
472
+ checkSnippetsVersion: () => checkSnippetsVersion,
473
+ upgradeSnippets: () => upgradeSnippets
474
+ });
475
+ async function upgradeSnippets(options = {}, logger2 = new ConsoleLogger()) {
476
+ try {
477
+ const projectDir = options.projectDir || process.cwd();
478
+ const force = options.force || false;
479
+ logger2.info("\u{1F504} \u6B63\u5728\u5347\u7EA7\u9879\u76EE snippets...");
480
+ const pubspecPath = (0, import_path10.join)(projectDir, "pubspec.yaml");
481
+ if (!(0, import_fs7.existsSync)(pubspecPath)) {
482
+ logger2.error("\u274C \u672A\u68C0\u6D4B\u5230 Flutter \u9879\u76EE (\u7F3A\u5C11 pubspec.yaml)");
483
+ return false;
484
+ }
485
+ const sourceSnippetsPath = (0, import_path10.join)(
486
+ __dirname2,
487
+ "..",
488
+ "..",
489
+ "templates",
490
+ "snippets",
491
+ "flu-cli.code-snippets"
492
+ );
493
+ if (!(0, import_fs7.existsSync)(sourceSnippetsPath)) {
494
+ logger2.error(`\u274C \u627E\u4E0D\u5230\u6E90 snippets \u6587\u4EF6: ${sourceSnippetsPath}`);
495
+ return false;
496
+ }
497
+ const targetDir = (0, import_path10.join)(projectDir, ".vscode");
498
+ const targetPath = (0, import_path10.join)(targetDir, "flu-cli.code-snippets");
499
+ if ((0, import_fs7.existsSync)(targetPath) && !force) {
500
+ logger2.warn("\u26A0\uFE0F \u9879\u76EE\u4E2D\u5DF2\u5B58\u5728 snippets \u6587\u4EF6");
501
+ logger2.info("\u63D0\u793A: \u4F7F\u7528 --force \u53C2\u6570\u5F3A\u5236\u8986\u76D6");
502
+ return false;
503
+ }
504
+ if (!(0, import_fs7.existsSync)(targetDir)) {
505
+ (0, import_fs7.mkdirSync)(targetDir, { recursive: true });
506
+ logger2.info("\u2713 \u521B\u5EFA .vscode \u76EE\u5F55");
507
+ }
508
+ (0, import_fs7.copyFileSync)(sourceSnippetsPath, targetPath);
509
+ logger2.success(`\u2705 Snippets \u66F4\u65B0\u6210\u529F!`);
510
+ logger2.info(` \u4F4D\u7F6E: ${targetPath}`);
511
+ logger2.newLine();
512
+ logger2.info("\u{1F4DD} \u66F4\u65B0\u5185\u5BB9:");
513
+ logger2.info(" \u2022 Core \u5BFC\u5165\u8DEF\u5F84\u6539\u4E3A\u4F7F\u7528\u53D8\u91CF ${relative_core_path}");
514
+ logger2.info(" \u2022 \u652F\u6301 package imports (\u81EA\u52A8\u751F\u6210)");
515
+ logger2.info(" \u2022 \u9002\u914D\u6240\u6709\u67B6\u6784 (Lite/Modular/Clean)");
516
+ return true;
517
+ } catch (error) {
518
+ const msg = error instanceof Error ? error.message : String(error);
519
+ logger2.error(`\u274C \u5347\u7EA7\u5931\u8D25: ${msg}`);
520
+ return false;
521
+ }
522
+ }
523
+ function checkSnippetsVersion(projectDir = process.cwd()) {
524
+ const snippetsPath = (0, import_path10.join)(projectDir, ".vscode", "flu-cli.code-snippets");
525
+ if (!(0, import_fs7.existsSync)(snippetsPath)) {
526
+ return "missing";
527
+ }
528
+ try {
529
+ const content = (0, import_fs7.readFileSync)(snippetsPath, "utf8");
530
+ if (content.includes("${relative_core_path}")) {
531
+ return "up-to-date";
532
+ } else {
533
+ return "outdated";
534
+ }
535
+ } catch {
536
+ return "missing";
537
+ }
538
+ }
539
+ var import_fs7, import_path10, import_url2, import_meta2, __dirname2;
540
+ var init_upgrade_snippets = __esm({
541
+ "src/commands/upgrade_snippets.ts"() {
542
+ "use strict";
543
+ import_fs7 = require("fs");
544
+ import_path10 = require("path");
545
+ import_url2 = require("url");
546
+ init_logger();
547
+ import_meta2 = {};
548
+ try {
549
+ if (typeof import_meta2 !== "undefined" && import_meta2.url) {
550
+ const __filename = (0, import_url2.fileURLToPath)(import_meta2.url);
551
+ __dirname2 = (0, import_path10.dirname)(__filename);
552
+ } else {
553
+ throw new Error("Using CJS fallback");
554
+ }
555
+ } catch {
556
+ __dirname2 = typeof global !== "undefined" && global.__dirname ? global.__dirname : process.cwd();
557
+ }
558
+ }
559
+ });
560
+
561
+ // src/index.ts
562
+ var index_exports = {};
563
+ __export(index_exports, {
564
+ AppAssetsManager: () => AppAssetsManager,
565
+ BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES,
566
+ ConfigManager: () => ConfigManager,
567
+ ConsoleLogger: () => ConsoleLogger,
568
+ I18nManager: () => I18nManager,
569
+ ProjectConfigManager: () => ProjectConfigManager,
570
+ ProjectGenerator: () => ProjectGenerator,
571
+ TemplateGenerator: () => TemplateGenerator,
572
+ TemplateManager: () => TemplateManager,
573
+ checkFlutterInstalled: () => checkFlutterInstalled,
574
+ checkSnippetsVersion: () => checkSnippetsVersion,
575
+ cleanupTemplateFiles: () => cleanupTemplateFiles,
576
+ copyCoreFiles: () => copyCoreFiles,
577
+ copyCustomTemplate: () => copyCustomTemplate,
578
+ copyInfrastructure: () => copyInfrastructure,
579
+ copyNetworkFiles: () => copyNetworkFiles,
580
+ copyTemplate: () => copyTemplate,
581
+ detectProjectTemplate: () => detectProjectTemplate,
582
+ ensurePubspecName: () => ensurePubspecName,
583
+ generateComponent: () => generateComponent,
584
+ generateIndexFile: () => generateIndexFile,
585
+ generateModel: () => generateModel,
586
+ generateModule: () => generateModule,
587
+ generatePage: () => generatePage,
588
+ generateService: () => generateService,
589
+ generateViewModel: () => generateViewModel,
590
+ generateWidget: () => generateWidget,
591
+ getCoreFilesDir: () => getCoreFilesDir,
592
+ getFlutterVersion: () => getFlutterVersion,
593
+ getModelPath: () => getModelPath,
594
+ getNetworkDir: () => getNetworkDir,
595
+ getPagePath: () => getPagePath,
596
+ getRelativeImportPath: () => getRelativeImportPath,
597
+ getServicePath: () => getServicePath,
598
+ getSnippetContent: () => getSnippetContent,
599
+ getStateManager: () => getStateManager,
600
+ getTemplatesRootDir: () => getTemplatesRootDir,
601
+ getViewModelPath: () => getViewModelPath,
602
+ getWidgetPath: () => getWidgetPath,
603
+ injectNetworkExamples: () => injectNetworkExamples,
604
+ isCustomTemplateProject: () => isCustomTemplateProject,
605
+ loadProjectSnippets: () => loadProjectSnippets,
606
+ logger: () => logger,
607
+ removeTemplateSuffix: () => removeTemplateSuffix,
608
+ renderSnippet: () => renderSnippet,
609
+ replaceVariables: () => replaceVariables,
610
+ runFlutterCreate: () => runFlutterCreate,
611
+ runFlutterPubGet: () => runFlutterPubGet,
612
+ t: () => t,
613
+ toCamelCase: () => toCamelCase,
614
+ toKebabCase: () => toKebabCase,
615
+ toPascalCase: () => toPascalCase,
616
+ toSnakeCase: () => toSnakeCase,
617
+ toTitleCase: () => toTitleCase,
618
+ updateIndexFile: () => updateIndexFile,
619
+ upgradeSnippets: () => upgradeSnippets
620
+ });
621
+ module.exports = __toCommonJS(index_exports);
622
+ init_logger();
623
+
624
+ // src/utils/string_helper.ts
625
+ function toPascalCase(str) {
626
+ return str.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
627
+ }
628
+ function toSnakeCase(str) {
629
+ return str.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
630
+ }
631
+ function toCamelCase(str) {
632
+ const pascal = toPascalCase(str);
633
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
634
+ }
635
+ function toTitleCase(str) {
636
+ return str.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
637
+ }
638
+ function toKebabCase(str) {
639
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
640
+ }
641
+
642
+ // src/utils/project_detector.ts
643
+ var import_fs2 = require("fs");
644
+ var import_path2 = require("path");
645
+ var import_fs_extra = __toESM(require("fs-extra"), 1);
646
+
647
+ // src/utils/project_config.ts
648
+ var import_fs = require("fs");
649
+ var import_path = require("path");
650
+ var ProjectConfigManager = class {
651
+ static CONFIG_FILE_NAME = ".flu-cli.json";
652
+ /**
653
+ * 加载项目配置
654
+ * @param projectDir 项目目录
655
+ * @returns 配置对象,如果不存在则返回 null
656
+ */
657
+ static loadConfig(projectDir) {
658
+ try {
659
+ const configPath = (0, import_path.join)(projectDir, this.CONFIG_FILE_NAME);
660
+ if (!(0, import_fs.existsSync)(configPath)) {
661
+ return null;
662
+ }
663
+ const content = (0, import_fs.readFileSync)(configPath, "utf-8");
664
+ const config = JSON.parse(content);
665
+ return config;
666
+ } catch (error) {
667
+ return null;
668
+ }
669
+ }
670
+ /**
671
+ * 检查项目是否有配置文件
672
+ * @param projectDir 项目目录
673
+ * @returns 是否存在配置文件
674
+ */
675
+ static hasConfig(projectDir) {
676
+ const configPath = (0, import_path.join)(projectDir, this.CONFIG_FILE_NAME);
677
+ return (0, import_fs.existsSync)(configPath);
678
+ }
679
+ /**
680
+ * 获取指定生成器的配置
681
+ * @param projectDir 项目目录
682
+ * @param generatorType 生成器类型
683
+ * @returns 生成器配置,如果不存在则返回 null
684
+ */
685
+ static getGeneratorConfig(projectDir, generatorType) {
686
+ const config = this.loadConfig(projectDir);
687
+ return config?.generators?.[generatorType] || null;
688
+ }
689
+ /**
690
+ * 获取默认配置模板
691
+ * @param templateType 模板类型
692
+ * @returns 配置模板对象
693
+ */
694
+ static getDefaultConfigTemplate(templateType) {
695
+ const t2 = String(templateType).toLowerCase();
696
+ if (t2.includes("lite")) {
697
+ return {
698
+ template: "lite",
699
+ generators: {
700
+ page: {
701
+ path: "lib/pages",
702
+ fileSuffix: "_page",
703
+ defaultType: "stateful",
704
+ withBasePage: true,
705
+ basePageClass: "BasePage",
706
+ basePageImport: "lib/core/index.dart",
707
+ withViewModel: true,
708
+ viewModelPath: "lib/viewmodels"
709
+ },
710
+ viewModel: {
711
+ path: "lib/viewmodels",
712
+ fileSuffix: "_viewmodel",
713
+ withBaseViewModel: true,
714
+ baseViewModelClass: "BaseViewModel",
715
+ baseViewModelImport: "lib/core/index.dart"
716
+ },
717
+ widget: {
718
+ path: "lib/widgets",
719
+ fileSuffix: "_widget",
720
+ defaultType: "stateless"
721
+ },
722
+ model: {
723
+ path: "lib/models",
724
+ fileSuffix: "_model"
725
+ },
726
+ component: {
727
+ path: "lib/components"
728
+ },
729
+ service: {
730
+ path: "lib/services"
731
+ },
732
+ module: {
733
+ path: "lib/features/{feature}"
734
+ }
735
+ }
736
+ };
737
+ } else if (t2.includes("modular")) {
738
+ return {
739
+ template: "modular",
740
+ generators: {
741
+ page: {
742
+ path: "lib/features/{feature}/pages",
743
+ fileSuffix: "_page",
744
+ defaultType: "stateful",
745
+ withBasePage: true,
746
+ basePageClass: "BasePage",
747
+ basePageImport: "lib/core/index.dart",
748
+ withViewModel: true,
749
+ viewModelPath: "lib/features/{feature}/viewmodels"
750
+ },
751
+ viewModel: {
752
+ path: "lib/features/{feature}/viewmodels",
753
+ fileSuffix: "_viewmodel",
754
+ withBaseViewModel: true,
755
+ baseViewModelClass: "BaseViewModel",
756
+ baseViewModelImport: "lib/core/index.dart"
757
+ },
758
+ widget: {
759
+ path: "lib/shared/widgets",
760
+ fileSuffix: "_widget",
761
+ defaultType: "stateless"
762
+ },
763
+ model: {
764
+ path: "lib/shared/models",
765
+ fileSuffix: "_model"
766
+ },
767
+ component: {
768
+ path: "lib/features/{feature}/components"
769
+ },
770
+ service: {
771
+ path: "lib/features/{feature}/services"
772
+ },
773
+ module: {
774
+ path: "lib/features/{feature}"
775
+ }
776
+ }
777
+ };
778
+ } else if (t2.includes("clean")) {
779
+ return {
780
+ template: "clean",
781
+ generators: {
782
+ page: {
783
+ path: "lib/features/{feature}/presentation/pages",
784
+ fileSuffix: "_page",
785
+ defaultType: "stateful",
786
+ withBasePage: true,
787
+ basePageClass: "BasePage",
788
+ basePageImport: "lib/core/index.dart",
789
+ withViewModel: true,
790
+ viewModelPath: "lib/features/{feature}/presentation/viewmodels"
791
+ },
792
+ viewModel: {
793
+ path: "lib/features/{feature}/presentation/viewmodels",
794
+ fileSuffix: "_viewmodel",
795
+ withBaseViewModel: true,
796
+ baseViewModelClass: "BaseViewModel",
797
+ baseViewModelImport: "lib/core/index.dart"
798
+ },
799
+ widget: {
800
+ path: "lib/shared/widgets",
801
+ fileSuffix: "_widget",
802
+ defaultType: "stateless"
803
+ },
804
+ model: {
805
+ path: "lib/features/{feature}/data/models",
806
+ fileSuffix: "_model"
807
+ },
808
+ component: {
809
+ path: "lib/features/{feature}/presentation/components"
810
+ },
811
+ service: {
812
+ path: "lib/features/{feature}/data/datasources"
813
+ },
814
+ module: {
815
+ path: "lib/features/{feature}"
816
+ }
817
+ }
818
+ };
819
+ } else {
820
+ return {
821
+ generators: {
822
+ page: {
823
+ path: "lib/pages",
824
+ defaultType: "stateful",
825
+ withBasePage: false,
826
+ withViewModel: false
827
+ },
828
+ viewModel: {
829
+ path: "lib/viewmodels",
830
+ withBaseViewModel: false
831
+ },
832
+ widget: {
833
+ path: "lib/widgets",
834
+ defaultType: "stateless"
835
+ },
836
+ model: {
837
+ path: "lib/models"
838
+ },
839
+ component: {
840
+ path: "lib/components"
841
+ },
842
+ service: {
843
+ path: "lib/services"
844
+ },
845
+ module: {
846
+ path: "lib/features/{feature}"
847
+ }
848
+ }
849
+ };
850
+ }
851
+ }
852
+ };
853
+
854
+ // src/utils/project_detector.ts
855
+ function detectProjectTemplate(projectDir = process.cwd()) {
856
+ try {
857
+ const config = ProjectConfigManager.loadConfig(projectDir);
858
+ if (config?.template) {
859
+ const t2 = String(config.template).toLowerCase();
860
+ if (t2.includes("modular")) return "modular";
861
+ if (t2.includes("clean")) return "clean";
862
+ if (t2.includes("lite")) return "lite";
863
+ return t2;
864
+ }
865
+ const libDir = (0, import_path2.join)(projectDir, "lib");
866
+ if (!(0, import_fs2.existsSync)(libDir)) {
867
+ return null;
868
+ }
869
+ const featuresDir = (0, import_path2.join)(libDir, "features");
870
+ if ((0, import_fs2.existsSync)(featuresDir)) {
871
+ const dirs = import_fs_extra.default.readdirSync(featuresDir);
872
+ const anyFeatureDir = dirs.find((d) => !d.startsWith("."));
873
+ if (anyFeatureDir) {
874
+ const presentationDir = (0, import_path2.join)(featuresDir, anyFeatureDir, "presentation");
875
+ if ((0, import_fs2.existsSync)(presentationDir)) {
876
+ return "clean";
877
+ }
878
+ }
879
+ const coreDir = (0, import_path2.join)(libDir, "core");
880
+ if ((0, import_fs2.existsSync)(coreDir)) {
881
+ if ((0, import_fs2.existsSync)((0, import_path2.join)(coreDir, "usecases")) || (0, import_fs2.existsSync)((0, import_path2.join)(coreDir, "domain"))) {
882
+ return "clean";
883
+ }
884
+ }
885
+ return "modular";
886
+ }
887
+ const pagesDir = (0, import_path2.join)(libDir, "pages");
888
+ const viewmodelsDir = (0, import_path2.join)(libDir, "viewmodels");
889
+ const widgetsDir = (0, import_path2.join)(libDir, "widgets");
890
+ if ((0, import_fs2.existsSync)(pagesDir) && (0, import_fs2.existsSync)(viewmodelsDir) && (0, import_fs2.existsSync)(widgetsDir)) {
891
+ return "lite";
892
+ }
893
+ return null;
894
+ } catch (error) {
895
+ return null;
896
+ }
897
+ }
898
+ function isCustomTemplateProject(projectDir = process.cwd()) {
899
+ const template = detectProjectTemplate(projectDir);
900
+ return template === null;
901
+ }
902
+ function getStateManager(projectDir = process.cwd()) {
903
+ try {
904
+ const pubspec = (0, import_path2.join)(projectDir, "pubspec.yaml");
905
+ if ((0, import_fs2.existsSync)(pubspec)) {
906
+ const content = import_fs_extra.default.readFileSync(pubspec, "utf8");
907
+ if (/flutter_riverpod\s*:/m.test(content)) return "riverpod";
908
+ if (/\n\s*get\s*:/m.test(content)) return "getx";
909
+ if (/\n\s*provider\s*:/m.test(content)) return "provider";
910
+ }
911
+ } catch {
912
+ }
913
+ return "default";
914
+ }
915
+ function getPagePath(projectDir, moduleName) {
916
+ const config = ProjectConfigManager.getGeneratorConfig(projectDir, "page");
917
+ if (config?.path) {
918
+ return (0, import_path2.join)(projectDir, config.path.replace("{feature}", moduleName));
919
+ }
920
+ const template = detectProjectTemplate(projectDir);
921
+ switch (template) {
922
+ case "lite":
923
+ return (0, import_path2.join)(projectDir, "lib", "pages");
924
+ case "modular":
925
+ return (0, import_path2.join)(projectDir, "lib", "features", moduleName, "pages");
926
+ case "clean":
927
+ return (0, import_path2.join)(projectDir, "lib", "features", moduleName, "presentation", "pages");
928
+ default:
929
+ return (0, import_path2.join)(projectDir, "lib", "pages");
930
+ }
931
+ }
932
+ function getViewModelPath(projectDir, moduleName) {
933
+ const config = ProjectConfigManager.getGeneratorConfig(projectDir, "viewModel");
934
+ if (config?.path) {
935
+ return (0, import_path2.join)(projectDir, config.path.replace("{feature}", moduleName));
936
+ }
937
+ const template = detectProjectTemplate(projectDir);
938
+ switch (template) {
939
+ case "lite":
940
+ return (0, import_path2.join)(projectDir, "lib", "viewmodels");
941
+ case "modular":
942
+ return (0, import_path2.join)(projectDir, "lib", "features", moduleName, "viewmodels");
943
+ case "clean":
944
+ return (0, import_path2.join)(projectDir, "lib", "features", moduleName, "presentation", "viewmodels");
945
+ default:
946
+ return (0, import_path2.join)(projectDir, "lib", "viewmodels");
947
+ }
948
+ }
949
+ function getWidgetPath(projectDir, moduleName = null) {
950
+ const config = ProjectConfigManager.getGeneratorConfig(projectDir, "widget");
951
+ if (config?.path) {
952
+ return (0, import_path2.join)(projectDir, config.path.replace("{feature}", moduleName || ""));
953
+ }
954
+ const template = detectProjectTemplate(projectDir);
955
+ switch (template) {
956
+ case "lite":
957
+ return (0, import_path2.join)(projectDir, "lib", "widgets");
958
+ case "modular":
959
+ return moduleName ? (0, import_path2.join)(projectDir, "lib", "features", moduleName, "widgets") : (0, import_path2.join)(projectDir, "lib", "shared", "widgets");
960
+ case "clean":
961
+ return moduleName ? (0, import_path2.join)(projectDir, "lib", "features", moduleName, "presentation", "widgets") : (0, import_path2.join)(projectDir, "lib", "shared", "widgets");
962
+ default:
963
+ return (0, import_path2.join)(projectDir, "lib", "widgets");
964
+ }
965
+ }
966
+ function getServicePath(projectDir, moduleName = null) {
967
+ const config = ProjectConfigManager.getGeneratorConfig(projectDir, "component");
968
+ if (config?.path) {
969
+ return (0, import_path2.join)(projectDir, config.path.replace("{feature}", moduleName || ""));
970
+ }
971
+ const template = detectProjectTemplate(projectDir);
972
+ switch (template) {
973
+ case "lite":
974
+ return (0, import_path2.join)(projectDir, "lib", "services");
975
+ case "modular":
976
+ return moduleName ? (0, import_path2.join)(projectDir, "lib", "features", moduleName, "services") : (0, import_path2.join)(projectDir, "lib", "shared", "services");
977
+ case "clean":
978
+ return moduleName ? (0, import_path2.join)(projectDir, "lib", "features", moduleName, "data", "datasources") : (0, import_path2.join)(projectDir, "lib", "core", "network");
979
+ default:
980
+ return (0, import_path2.join)(projectDir, "lib", "services");
981
+ }
982
+ }
983
+ function getModelPath(projectDir, moduleName = null) {
984
+ const config = ProjectConfigManager.getGeneratorConfig(projectDir, "model");
985
+ if (config?.path) {
986
+ return (0, import_path2.join)(projectDir, config.path.replace("{feature}", moduleName || ""));
987
+ }
988
+ const template = detectProjectTemplate(projectDir);
989
+ switch (template) {
990
+ case "lite":
991
+ return (0, import_path2.join)(projectDir, "lib", "models");
992
+ case "modular":
993
+ return moduleName ? (0, import_path2.join)(projectDir, "lib", "features", moduleName, "models") : (0, import_path2.join)(projectDir, "lib", "shared", "models");
994
+ case "clean":
995
+ return moduleName ? (0, import_path2.join)(projectDir, "lib", "features", moduleName, "data", "models") : (0, import_path2.join)(projectDir, "lib", "shared", "models");
996
+ default:
997
+ return (0, import_path2.join)(projectDir, "lib", "models");
998
+ }
999
+ }
1000
+ function getRelativeImportPath(fromPath, toPath) {
1001
+ const fromParts = fromPath.split("/");
1002
+ const toParts = toPath.split("/");
1003
+ let commonIndex = 0;
1004
+ while (commonIndex < fromParts.length && commonIndex < toParts.length) {
1005
+ if (fromParts[commonIndex] !== toParts[commonIndex]) {
1006
+ break;
1007
+ }
1008
+ commonIndex++;
1009
+ }
1010
+ const upLevels = fromParts.length - commonIndex - 1;
1011
+ const upPath = "../".repeat(upLevels);
1012
+ const targetPath = toParts.slice(commonIndex).join("/");
1013
+ return upPath + targetPath;
1014
+ }
1015
+ function getPackageName(projectDir = process.cwd()) {
1016
+ try {
1017
+ const pubspec = (0, import_path2.join)(projectDir, "pubspec.yaml");
1018
+ if (!(0, import_fs2.existsSync)(pubspec)) {
1019
+ return null;
1020
+ }
1021
+ const content = import_fs_extra.default.readFileSync(pubspec, "utf8");
1022
+ const match = content.match(/^name:\s+(.+)$/m);
1023
+ if (match && match[1]) {
1024
+ return match[1].trim();
1025
+ }
1026
+ return null;
1027
+ } catch (error) {
1028
+ return null;
1029
+ }
1030
+ }
1031
+ function getPackageImportPath(projectDir, targetPath) {
1032
+ const packageName = getPackageName(projectDir);
1033
+ if (!packageName) {
1034
+ return null;
1035
+ }
1036
+ const cleanPath = targetPath.replace(/^lib\//, "");
1037
+ return `package:${packageName}/${cleanPath}`;
1038
+ }
1039
+
1040
+ // src/utils/snippet_loader.ts
1041
+ var import_fs3 = require("fs");
1042
+ var import_path3 = require("path");
1043
+ var import_json5 = __toESM(require("json5"), 1);
1044
+ var SNIPPET_FILE_NAME = "flu-cli.code-snippets";
1045
+ function loadProjectSnippets(projectDir) {
1046
+ try {
1047
+ const snippetsPath = (0, import_path3.join)(projectDir, ".vscode", SNIPPET_FILE_NAME);
1048
+ if (!(0, import_fs3.existsSync)(snippetsPath)) return {};
1049
+ const raw = (0, import_fs3.readFileSync)(snippetsPath, "utf8");
1050
+ const json = import_json5.default.parse(raw);
1051
+ return json || {};
1052
+ } catch (_) {
1053
+ return {};
1054
+ }
1055
+ }
1056
+ function renderSnippet(bodyLines, variables) {
1057
+ const body = Array.isArray(bodyLines) ? bodyLines.join("\n") : String(bodyLines || "");
1058
+ let result = body.replace(/\{\{(\w+)\}\}/g, (_, key) => {
1059
+ return Object.prototype.hasOwnProperty.call(variables, key) ? String(variables[key]) : `{{${key}}}`;
1060
+ });
1061
+ return replaceVSCodeVariables(result, variables);
1062
+ }
1063
+ function replaceVSCodeVariables(text, variables) {
1064
+ let result = "";
1065
+ let i = 0;
1066
+ while (i < text.length) {
1067
+ if (text.substring(i, i + 2) === "${") {
1068
+ let depth = 1;
1069
+ let j = i + 2;
1070
+ const contentStart = j;
1071
+ while (j < text.length && depth > 0) {
1072
+ if (text.substring(j, j + 2) === "${") {
1073
+ depth++;
1074
+ j += 2;
1075
+ } else if (text[j] === "}") {
1076
+ depth--;
1077
+ j++;
1078
+ } else if (text[j] === "\\") {
1079
+ j += 2;
1080
+ } else {
1081
+ j++;
1082
+ }
1083
+ }
1084
+ if (depth === 0) {
1085
+ const innerContent = text.substring(contentStart, j - 1);
1086
+ let replacement = text.substring(i, j);
1087
+ let separatorIndex = -1;
1088
+ let separatorChar = "";
1089
+ let k = 0;
1090
+ let innerDepth = 0;
1091
+ while (k < innerContent.length) {
1092
+ if (innerContent.substring(k, k + 2) === "${") {
1093
+ innerDepth++;
1094
+ k += 2;
1095
+ } else if (innerContent[k] === "}") {
1096
+ innerDepth--;
1097
+ k++;
1098
+ } else if (innerDepth === 0 && (innerContent[k] === ":" || innerContent[k] === "/")) {
1099
+ separatorIndex = k;
1100
+ separatorChar = innerContent[k];
1101
+ break;
1102
+ } else {
1103
+ k++;
1104
+ }
1105
+ }
1106
+ if (separatorChar === ":") {
1107
+ const defaultValue = innerContent.substring(separatorIndex + 1);
1108
+ const processedDefault = replaceVSCodeVariables(defaultValue, variables);
1109
+ if (Object.prototype.hasOwnProperty.call(variables, processedDefault)) {
1110
+ replacement = String(variables[processedDefault]);
1111
+ } else {
1112
+ replacement = processedDefault;
1113
+ }
1114
+ } else if (separatorChar === "/") {
1115
+ if (variables.snake_name) {
1116
+ replacement = variables.snake_name;
1117
+ }
1118
+ } else {
1119
+ const key = innerContent;
1120
+ if (Object.prototype.hasOwnProperty.call(variables, key)) {
1121
+ replacement = String(variables[key]);
1122
+ }
1123
+ }
1124
+ result += replacement;
1125
+ i = j;
1126
+ } else {
1127
+ result += text[i];
1128
+ i++;
1129
+ }
1130
+ } else {
1131
+ result += text[i];
1132
+ i++;
1133
+ }
1134
+ }
1135
+ return result;
1136
+ }
1137
+ function getSnippetContent(projectDir, key, variables) {
1138
+ try {
1139
+ const map = loadProjectSnippets(projectDir);
1140
+ const item = map[key];
1141
+ if (!item || !item.body) return null;
1142
+ return renderSnippet(item.body, variables);
1143
+ } catch (error) {
1144
+ console.error(`Failed to parse snippet for key "${key}":`, error);
1145
+ return null;
1146
+ }
1147
+ }
1148
+
1149
+ // src/templates/template_generator.ts
1150
+ var TemplateGenerator = class {
1151
+ /**
1152
+ * 生成 Stateful Page (BasePage)
1153
+ */
1154
+ /**
1155
+ * 生成 Stateful Page (BasePage)
1156
+ */
1157
+ static generateStatefulPageWithBase(namePascal, nameTitle, options = {}) {
1158
+ const {
1159
+ vmImportPath = `../viewmodels/${namePascal.toLowerCase()}_viewmodel.dart`,
1160
+ coreImportPath,
1161
+ baseClass = "BasePage",
1162
+ extraImports = []
1163
+ } = options;
1164
+ let imports = `import 'package:flutter/material.dart';
1165
+ `;
1166
+ if (extraImports.length > 0) {
1167
+ imports += extraImports.join("\n") + "\n";
1168
+ }
1169
+ if (coreImportPath && !extraImports.some((i) => i.includes(coreImportPath))) {
1170
+ imports += `import '${coreImportPath}';
1171
+ `;
1172
+ }
1173
+ if (vmImportPath) imports += `import '${vmImportPath}';
1174
+ `;
1175
+ return `${imports}
1176
+ class ${namePascal}Page extends ${baseClass}<${namePascal}ViewModel> {
1177
+ const ${namePascal}Page({super.key});
1178
+
1179
+ @override
1180
+ State<${namePascal}Page> createState() => _${namePascal}PageState();
1181
+ }
1182
+
1183
+ class _${namePascal}PageState extends ${baseClass}State<${namePascal}ViewModel, ${namePascal}Page> {
1184
+ // ==================== UI \u914D\u7F6E ====================
1185
+ @override
1186
+ String get title => '${nameTitle}';
1187
+
1188
+ // ==================== ViewModel ====================
1189
+ @override
1190
+ ${namePascal}ViewModel createViewModel() => ${namePascal}ViewModel();
1191
+
1192
+ // ==================== UI \u6784\u5EFA ====================
1193
+ @override
1194
+ Widget buildContent(BuildContext context) {
1195
+ return const Center(
1196
+ child: Text('${namePascal}Page'),
1197
+ );
1198
+ }
1199
+ }
1200
+ `;
1201
+ }
1202
+ /**
1203
+ * 生成 Simple Stateful Page
1204
+ */
1205
+ static generateSimpleStatefulPage(namePascal, nameTitle) {
1206
+ return `import 'package:flutter/material.dart';
1207
+
1208
+ class ${namePascal}Page extends StatefulWidget {
1209
+ const ${namePascal}Page({super.key});
1210
+
1211
+ @override
1212
+ State<${namePascal}Page> createState() => _${namePascal}PageState();
1213
+ }
1214
+
1215
+ class _${namePascal}PageState extends State<${namePascal}Page> {
1216
+ @override
1217
+ Widget build(BuildContext context) {
1218
+ return Scaffold(
1219
+ appBar: AppBar(
1220
+ title: const Text('${nameTitle}'),
1221
+ ),
1222
+ body: const Center(
1223
+ child: Text('${namePascal}Page'),
1224
+ ),
1225
+ );
1226
+ }
1227
+ }
1228
+ `;
1229
+ }
1230
+ /**
1231
+ * 生成 Simple Stateless Page
1232
+ */
1233
+ static generateSimpleStatelessPage(namePascal, nameTitle) {
1234
+ return `import 'package:flutter/material.dart';
1235
+
1236
+ class ${namePascal}Page extends StatelessWidget {
1237
+ const ${namePascal}Page({super.key});
1238
+
1239
+ @override
1240
+ Widget build(BuildContext context) {
1241
+ return Scaffold(
1242
+ appBar: AppBar(
1243
+ title: const Text('${nameTitle}'),
1244
+ ),
1245
+ body: const Center(
1246
+ child: Text('${namePascal}Page'),
1247
+ ),
1248
+ );
1249
+ }
1250
+ }
1251
+ `;
1252
+ }
1253
+ /**
1254
+ * 生成 ViewModel
1255
+ */
1256
+ static generateViewModel(namePascal, options = {}) {
1257
+ const {
1258
+ coreImportPath,
1259
+ baseClass = "BaseViewModel",
1260
+ extraImports = []
1261
+ } = options;
1262
+ let imports = "";
1263
+ if (extraImports.length > 0) {
1264
+ imports += extraImports.join("\n") + "\n";
1265
+ }
1266
+ if (coreImportPath) {
1267
+ if (!extraImports.some((i) => i.includes(coreImportPath))) {
1268
+ imports += `import '${coreImportPath}';
1269
+ `;
1270
+ }
1271
+ }
1272
+ return `${imports}
1273
+
1274
+ class ${namePascal}ViewModel extends ${baseClass} {
1275
+ @override
1276
+ Future<void> onInit() async {
1277
+ await super.onInit();
1278
+ }
1279
+ }
1280
+ `;
1281
+ }
1282
+ /**
1283
+ * 生成 Service
1284
+ */
1285
+ static generateService(namePascal) {
1286
+ return `class ${namePascal}Service {
1287
+
1288
+ Future<void> fetchData() async {
1289
+ }
1290
+ }
1291
+ `;
1292
+ }
1293
+ /**
1294
+ * 生成 Widget (Stateful)
1295
+ */
1296
+ static generateStatefulWidget(namePascal) {
1297
+ return `import 'package:flutter/material.dart';
1298
+
1299
+ class ${namePascal}Widget extends StatefulWidget {
1300
+ const ${namePascal}Widget({super.key});
1301
+
1302
+ @override
1303
+ State<${namePascal}Widget> createState() => _${namePascal}WidgetState();
1304
+ }
1305
+
1306
+ class _${namePascal}WidgetState extends State<${namePascal}Widget> {
1307
+ @override
1308
+ Widget build(BuildContext context) {
1309
+ return const SizedBox.shrink();
1310
+ }
1311
+ }
1312
+ `;
1313
+ }
1314
+ /**
1315
+ * 生成 Widget (Stateless)
1316
+ */
1317
+ static generateStatelessWidget(namePascal) {
1318
+ return `import 'package:flutter/material.dart';
1319
+
1320
+ class ${namePascal}Widget extends StatelessWidget {
1321
+ const ${namePascal}Widget({super.key});
1322
+
1323
+ @override
1324
+ Widget build(BuildContext context) {
1325
+ return const SizedBox.shrink();
1326
+ }
1327
+ }
1328
+ `;
1329
+ }
1330
+ /**
1331
+ * 生成 Component (Stateless)
1332
+ */
1333
+ static generateComponent(namePascal) {
1334
+ return `import 'package:flutter/material.dart';
1335
+
1336
+ class ${namePascal}Component extends StatelessWidget {
1337
+ const ${namePascal}Component({super.key});
1338
+
1339
+ @override
1340
+ Widget build(BuildContext context) {
1341
+ return const SizedBox.shrink();
1342
+ }
1343
+ }
1344
+ `;
1345
+ }
1346
+ /**
1347
+ * 生成 Component (Stateful)
1348
+ */
1349
+ static generateStatefulComponent(namePascal) {
1350
+ return `import 'package:flutter/material.dart';
1351
+
1352
+ class ${namePascal}Component extends StatefulWidget {
1353
+ const ${namePascal}Component({super.key});
1354
+
1355
+ @override
1356
+ State<${namePascal}Component> createState() => _${namePascal}ComponentState();
1357
+ }
1358
+
1359
+ class _${namePascal}ComponentState extends State<${namePascal}Component> {
1360
+ @override
1361
+ Widget build(BuildContext context) {
1362
+ return const SizedBox.shrink();
1363
+ }
1364
+ }
1365
+ `;
1366
+ }
1367
+ /**
1368
+ * 生成 Model
1369
+ */
1370
+ static generateModel(namePascal) {
1371
+ return `class ${namePascal}Model {
1372
+ final String id;
1373
+ final String name;
1374
+
1375
+ ${namePascal}Model({
1376
+ required this.id,
1377
+ required this.name,
1378
+ });
1379
+
1380
+ factory ${namePascal}Model.fromJson(Map<String, dynamic> json) {
1381
+ return ${namePascal}Model(
1382
+ id: json['id'] as String,
1383
+ name: json['name'] as String,
1384
+ );
1385
+ }
1386
+
1387
+ Map<String, dynamic> toJson() {
1388
+ return {
1389
+ 'id': id,
1390
+ 'name': name,
1391
+ };
1392
+ }
1393
+ }
1394
+ `;
1395
+ }
1396
+ };
1397
+
1398
+ // src/utils/index_updater.ts
1399
+ var import_fs4 = require("fs");
1400
+ var import_path4 = require("path");
1401
+ function updateIndexFile(dirPath, fileName) {
1402
+ try {
1403
+ const indexPath = (0, import_path4.join)(dirPath, "index.dart");
1404
+ if (!(0, import_fs4.existsSync)(indexPath)) {
1405
+ return false;
1406
+ }
1407
+ let content = (0, import_fs4.readFileSync)(indexPath, "utf8");
1408
+ const exportStatement = `export '${fileName}';`;
1409
+ if (content.includes(exportStatement)) {
1410
+ return false;
1411
+ }
1412
+ const lines = content.split("\n");
1413
+ const comments = [];
1414
+ const exports2 = [];
1415
+ const otherLines = [];
1416
+ for (const line of lines) {
1417
+ const trimmed = line.trim();
1418
+ if (trimmed.startsWith("//")) {
1419
+ comments.push(line);
1420
+ } else if (trimmed.startsWith("export ")) {
1421
+ exports2.push(trimmed);
1422
+ } else if (trimmed !== "") {
1423
+ otherLines.push(line);
1424
+ }
1425
+ }
1426
+ exports2.push(exportStatement);
1427
+ exports2.sort();
1428
+ const newLines = [];
1429
+ if (comments.length > 0) {
1430
+ newLines.push(...comments);
1431
+ newLines.push("");
1432
+ }
1433
+ newLines.push(...exports2);
1434
+ if (otherLines.length > 0) {
1435
+ newLines.push("");
1436
+ newLines.push(...otherLines);
1437
+ }
1438
+ newLines.push("");
1439
+ content = newLines.join("\n");
1440
+ (0, import_fs4.writeFileSync)(indexPath, content, "utf8");
1441
+ return true;
1442
+ } catch (error) {
1443
+ return false;
1444
+ }
1445
+ }
1446
+
1447
+ // src/utils/template_copier.ts
1448
+ var import_fs_extra3 = __toESM(require("fs-extra"), 1);
1449
+ var import_path6 = require("path");
1450
+ var import_url = require("url");
1451
+
1452
+ // src/utils/template_renderer.ts
1453
+ var import_fs_extra2 = __toESM(require("fs-extra"), 1);
1454
+ var import_path5 = require("path");
1455
+ var TemplateRenderer = class {
1456
+ /**
1457
+ * 渲染文件内容
1458
+ * @param content 原始字符串内容
1459
+ * @param data 渲染上下文数据
1460
+ */
1461
+ static render(content, data) {
1462
+ let result = content;
1463
+ const blockRegex = /[\r\n]*{{#if_(\w+)}}([\s\S]*?){{\/if_\1}}[\r\n]*/g;
1464
+ result = result.replace(blockRegex, (match, key, blockContent) => {
1465
+ const condition = !!data[`if_${key}`] || !!data[key];
1466
+ const elseParts = blockContent.split(/{{else}}/);
1467
+ if (elseParts.length > 1) {
1468
+ return condition ? elseParts[0] : elseParts[1];
1469
+ }
1470
+ return condition ? blockContent : "";
1471
+ });
1472
+ const varRegex = /{{(\w+)}}/g;
1473
+ result = result.replace(varRegex, (match, key) => {
1474
+ if (data.hasOwnProperty(key)) {
1475
+ return data[key];
1476
+ }
1477
+ return match;
1478
+ });
1479
+ return result;
1480
+ }
1481
+ /**
1482
+ * 渲染指定文件并覆盖
1483
+ * @param filePath 文件路径
1484
+ * @param data 渲染上下文数据
1485
+ */
1486
+ static async renderFile(filePath, data) {
1487
+ if (!await import_fs_extra2.default.pathExists(filePath)) return;
1488
+ try {
1489
+ const content = await import_fs_extra2.default.readFile(filePath, "utf8");
1490
+ const rendered = this.render(content, data);
1491
+ await import_fs_extra2.default.writeFile(filePath, rendered, "utf8");
1492
+ } catch (e) {
1493
+ }
1494
+ }
1495
+ /**
1496
+ * 递归渲染目录下的所有文本文件
1497
+ * @param dir 目录路径
1498
+ * @param data 渲染上下文数据
1499
+ */
1500
+ static async renderDirectory(dir, data) {
1501
+ if (!await import_fs_extra2.default.pathExists(dir)) return;
1502
+ const items = await import_fs_extra2.default.readdir(dir);
1503
+ for (const item of items) {
1504
+ const fullPath = (0, import_path5.join)(dir, item);
1505
+ const stat = await import_fs_extra2.default.stat(fullPath);
1506
+ if (stat.isDirectory()) {
1507
+ await this.renderDirectory(fullPath, data);
1508
+ } else if (stat.isFile() && this.isTextFile(item)) {
1509
+ await this.renderFile(fullPath, data);
1510
+ }
1511
+ }
1512
+ }
1513
+ static isTextFile(fileName) {
1514
+ const textExtensions = [
1515
+ ".dart",
1516
+ ".yaml",
1517
+ ".json",
1518
+ ".md",
1519
+ ".gradle",
1520
+ ".xml",
1521
+ ".plist",
1522
+ ".template"
1523
+ ];
1524
+ return textExtensions.some((ext) => fileName.endsWith(ext));
1525
+ }
1526
+ };
1527
+
1528
+ // src/utils/template_copier.ts
1529
+ var import_meta = {};
1530
+ function getTemplatesRootDir() {
1531
+ const searchDirs = [];
1532
+ if (process.env.FLU_CLI_TEMPLATES_DIR) {
1533
+ searchDirs.push(process.env.FLU_CLI_TEMPLATES_DIR);
1534
+ }
1535
+ try {
1536
+ let current = "";
1537
+ if (typeof import_meta !== "undefined" && import_meta.url) {
1538
+ current = (0, import_path6.dirname)((0, import_url.fileURLToPath)(import_meta.url));
1539
+ } else if (typeof __dirname !== "undefined" && __dirname) {
1540
+ current = __dirname;
1541
+ }
1542
+ if (current) {
1543
+ let temp = current;
1544
+ for (let i = 0; i < 6; i++) {
1545
+ searchDirs.push((0, import_path6.join)(temp, "packages", "core", "templates"));
1546
+ searchDirs.push((0, import_path6.join)(temp, "templates"));
1547
+ searchDirs.push((0, import_path6.join)(temp, "..", "templates"));
1548
+ temp = (0, import_path6.dirname)(temp);
1549
+ if (temp === (0, import_path6.dirname)(temp)) break;
1550
+ }
1551
+ }
1552
+ } catch (e) {
1553
+ }
1554
+ searchDirs.push((0, import_path6.join)(process.cwd(), "templates"));
1555
+ searchDirs.push((0, import_path6.join)(process.cwd(), "packages", "core", "templates"));
1556
+ for (const dir of searchDirs) {
1557
+ try {
1558
+ if (import_fs_extra3.default.pathExistsSync(dir) && import_fs_extra3.default.pathExistsSync((0, import_path6.join)(dir, "core_files"))) {
1559
+ return dir;
1560
+ }
1561
+ } catch {
1562
+ }
1563
+ }
1564
+ return (0, import_path6.join)(process.cwd(), "templates");
1565
+ }
1566
+ function getCoreFilesDir() {
1567
+ return (0, import_path6.join)(getTemplatesRootDir(), "core_files");
1568
+ }
1569
+ async function copyCoreFiles(projectPath, options = {}) {
1570
+ const { includeNetworkLayer = false, templateType = "lite" } = options;
1571
+ const sourceDir = getCoreFilesDir();
1572
+ const targetDir = (0, import_path6.join)(projectPath, "lib/core");
1573
+ const templateTypeLower = String(templateType).toLowerCase();
1574
+ const stateManagerLower = String(options.stateManager || "provider").toLowerCase();
1575
+ if (!await import_fs_extra3.default.pathExists(sourceDir)) {
1576
+ throw new Error(`Core files template directory not found: ${sourceDir}`);
1577
+ }
1578
+ await import_fs_extra3.default.copy(sourceDir, targetDir, {
1579
+ overwrite: true,
1580
+ filter: (src) => {
1581
+ const name = src.split("/").pop() || "";
1582
+ if (name === ".DS_Store") return false;
1583
+ if (!includeNetworkLayer && (name === "network" || src.endsWith("/network"))) {
1584
+ return false;
1585
+ }
1586
+ if (!includeNetworkLayer && name === "request_helper.dart.template") {
1587
+ return false;
1588
+ }
1589
+ return true;
1590
+ }
1591
+ });
1592
+ await removeTemplateSuffix(targetDir);
1593
+ const navigatorUtilPath = (0, import_path6.join)(targetDir, "router", "navigator_util.dart");
1594
+ const navigatorUtilMaterial = (0, import_path6.join)(targetDir, "router", "navigator_util_material.dart");
1595
+ const navigatorUtilGetx = (0, import_path6.join)(targetDir, "router", "navigator_util_getx.dart");
1596
+ if (stateManagerLower === "getx") {
1597
+ if (await import_fs_extra3.default.pathExists(navigatorUtilGetx)) {
1598
+ await import_fs_extra3.default.move(navigatorUtilGetx, navigatorUtilPath, { overwrite: true });
1599
+ }
1600
+ if (await import_fs_extra3.default.pathExists(navigatorUtilMaterial)) {
1601
+ await import_fs_extra3.default.remove(navigatorUtilMaterial);
1602
+ }
1603
+ } else {
1604
+ if (await import_fs_extra3.default.pathExists(navigatorUtilMaterial)) {
1605
+ await import_fs_extra3.default.move(navigatorUtilMaterial, navigatorUtilPath, { overwrite: true });
1606
+ }
1607
+ if (await import_fs_extra3.default.pathExists(navigatorUtilGetx)) {
1608
+ await import_fs_extra3.default.remove(navigatorUtilGetx);
1609
+ }
1610
+ }
1611
+ await TemplateRenderer.renderDirectory(targetDir, {
1612
+ ...options,
1613
+ if_getx: stateManagerLower === "getx",
1614
+ if_provider: stateManagerLower === "provider",
1615
+ if_bloc: stateManagerLower === "bloc",
1616
+ if_riverpod: stateManagerLower === "riverpod",
1617
+ if_network: includeNetworkLayer,
1618
+ if_lite: templateTypeLower.includes("lite"),
1619
+ if_modular: templateTypeLower.includes("modular"),
1620
+ if_clean: templateTypeLower.includes("clean"),
1621
+ BASE_IMPORT: `package:${options.projectName}/core/base`,
1622
+ MODELS_IMPORT: `package:${options.projectName}/core/models`,
1623
+ SERVICES_IMPORT: `package:${options.projectName}/core/services`,
1624
+ VIEWMODELS_IMPORT: `package:${options.projectName}/core/viewmodels`,
1625
+ NETWORK_IMPORT: `package:${options.projectName}/core/network`,
1626
+ UTILS_IMPORT: `package:${options.projectName}/core/utils`
1627
+ });
1628
+ const filesToRemove = [
1629
+ (0, import_path6.join)(targetDir, "router", "router_util.dart")
1630
+ // 这个文件可能不再存在,但保留以防万一
1631
+ ];
1632
+ for (const file of filesToRemove) {
1633
+ if (await import_fs_extra3.default.pathExists(file)) {
1634
+ await import_fs_extra3.default.remove(file);
1635
+ }
1636
+ }
1637
+ const appRoutesPath = (0, import_path6.join)(targetDir, "router", "app_routes.dart");
1638
+ if (await import_fs_extra3.default.pathExists(appRoutesPath)) {
1639
+ let content = await import_fs_extra3.default.readFile(appRoutesPath, "utf8");
1640
+ let pagesImport = "";
1641
+ if (templateTypeLower.includes("lite")) {
1642
+ pagesImport = "import '../../pages/index.dart';";
1643
+ } else if (templateTypeLower.includes("modular") || templateTypeLower.includes("clean")) {
1644
+ pagesImport = "import '../../features/index.dart';";
1645
+ } else {
1646
+ pagesImport = "import '../../pages/index.dart';";
1647
+ }
1648
+ content = content.replace("// PAGES_IMPORT_PLACEHOLDER", pagesImport);
1649
+ await import_fs_extra3.default.writeFile(appRoutesPath, content);
1650
+ }
1651
+ if (!includeNetworkLayer) {
1652
+ const coreIndexFile = (0, import_path6.join)(targetDir, "index.dart");
1653
+ if (await import_fs_extra3.default.pathExists(coreIndexFile)) {
1654
+ let content = await import_fs_extra3.default.readFile(coreIndexFile, "utf8");
1655
+ content = content.replace(/export 'network\/.*';\n/g, "");
1656
+ await import_fs_extra3.default.writeFile(coreIndexFile, content);
1657
+ }
1658
+ const utilsIndexFile = (0, import_path6.join)(targetDir, "utils", "index.dart");
1659
+ if (await import_fs_extra3.default.pathExists(utilsIndexFile)) {
1660
+ let content = await import_fs_extra3.default.readFile(utilsIndexFile, "utf8");
1661
+ content = content.replace(/export 'request_helper\.dart';\n?/g, "");
1662
+ await import_fs_extra3.default.writeFile(utilsIndexFile, content);
1663
+ }
1664
+ }
1665
+ }
1666
+ function getNetworkDir() {
1667
+ return (0, import_path6.join)(getCoreFilesDir(), "network");
1668
+ }
1669
+ async function copyNetworkFiles(projectPath, options = {}) {
1670
+ const { targetDir = "lib/core/network", includeExamples = false } = options;
1671
+ const sourceDir = getNetworkDir();
1672
+ const destDir = (0, import_path6.join)(projectPath, targetDir);
1673
+ if (!await import_fs_extra3.default.pathExists(sourceDir)) {
1674
+ throw new Error(`Network template directory not found: ${sourceDir}`);
1675
+ }
1676
+ await import_fs_extra3.default.copy(sourceDir, destDir, {
1677
+ overwrite: true,
1678
+ filter: (src) => {
1679
+ const name = src.split("/").pop() || "";
1680
+ if (name === ".DS_Store") {
1681
+ return false;
1682
+ }
1683
+ if (!includeExamples) {
1684
+ if (name.includes("example") || name === "README.md") {
1685
+ return false;
1686
+ }
1687
+ }
1688
+ return true;
1689
+ }
1690
+ });
1691
+ await removeTemplateSuffix(destDir);
1692
+ }
1693
+ async function copyInfrastructure(projectPath, includeExamples = true, includeNetworkLayer = true, templateType = "lite") {
1694
+ await copyCoreFiles(projectPath, { includeNetworkLayer, templateType });
1695
+ }
1696
+ async function copyTemplate(templatePath, targetPath, options = {}) {
1697
+ try {
1698
+ if (!await import_fs_extra3.default.pathExists(templatePath)) {
1699
+ return false;
1700
+ }
1701
+ const defaultExcludes = [
1702
+ ".git",
1703
+ ".github",
1704
+ "node_modules",
1705
+ ".flu-cli.yaml",
1706
+ "README.template.md",
1707
+ ".DS_Store",
1708
+ "pubspec.yaml"
1709
+ ];
1710
+ const allExcludes = [...defaultExcludes, ...options.excludes || []];
1711
+ await import_fs_extra3.default.copy(templatePath, targetPath, {
1712
+ overwrite: true,
1713
+ filter: (src) => {
1714
+ const relativePath = src.replace(templatePath, "");
1715
+ const basename = src.split(/[\\/]/).pop() || "";
1716
+ if (allExcludes.includes(basename)) return false;
1717
+ if (options.excludes && options.excludes.some((ex) => relativePath.includes(ex))) {
1718
+ return false;
1719
+ }
1720
+ return true;
1721
+ }
1722
+ });
1723
+ const templatePubspec = (0, import_path6.join)(templatePath, "pubspec.yaml.template");
1724
+ const targetPubspec = (0, import_path6.join)(targetPath, "pubspec.yaml");
1725
+ if (await import_fs_extra3.default.pathExists(templatePubspec)) {
1726
+ await import_fs_extra3.default.copy(templatePubspec, targetPubspec, { overwrite: true });
1727
+ }
1728
+ await removeTemplateSuffix(targetPath);
1729
+ return true;
1730
+ } catch (error) {
1731
+ return false;
1732
+ }
1733
+ }
1734
+ async function injectNetworkExamples(projectPath, contextData = {}) {
1735
+ const templatesRoot = getTemplatesRootDir();
1736
+ const examplesDir = (0, import_path6.join)(templatesRoot, "examples");
1737
+ if (!await import_fs_extra3.default.pathExists(examplesDir)) return;
1738
+ const templateType = (contextData.templateType || "lite").toLowerCase();
1739
+ let pagesPath = "lib/pages";
1740
+ let vmsPath = "lib/viewmodels";
1741
+ let servicesPath = "lib/services";
1742
+ let modelsPath = "lib/models";
1743
+ if (templateType.includes("modular")) {
1744
+ pagesPath = "lib/features/home/pages";
1745
+ vmsPath = "lib/features/home/viewmodels";
1746
+ servicesPath = "lib/features/home/services";
1747
+ modelsPath = "lib/features/home/models";
1748
+ } else if (templateType.includes("clean")) {
1749
+ pagesPath = "lib/features/home/presentation/pages";
1750
+ vmsPath = "lib/features/home/presentation/viewmodels";
1751
+ servicesPath = "lib/features/home/data/datasources";
1752
+ modelsPath = "lib/features/home/data/models";
1753
+ }
1754
+ const dataLayerImport = templateType.includes("clean") ? `package:${contextData.projectName}/features/home/data/index.dart` : `package:${contextData.projectName}/${modelsPath.replace(/^lib\//, "")}/index.dart`;
1755
+ const renderData = {
1756
+ ...contextData,
1757
+ if_getx: String(contextData.stateManager).toLowerCase() === "getx",
1758
+ if_provider: String(contextData.stateManager).toLowerCase() === "provider",
1759
+ if_bloc: String(contextData.stateManager).toLowerCase() === "bloc",
1760
+ if_riverpod: String(contextData.stateManager).toLowerCase() === "riverpod",
1761
+ if_clean: templateType.includes("clean"),
1762
+ CORE_IMPORT: `package:${contextData.projectName}/core/index.dart`,
1763
+ BASE_IMPORT: `package:${contextData.projectName}/core/base/index.dart`,
1764
+ CONFIG_IMPORT: `package:${contextData.projectName}/core/config/index.dart`,
1765
+ // ✅ 复用 dataLayerImport 变量
1766
+ DATA_IMPORT: dataLayerImport,
1767
+ MODELS_IMPORT: dataLayerImport,
1768
+ SERVICES_IMPORT: templateType.includes("clean") ? dataLayerImport : `package:${contextData.projectName}/${servicesPath.replace(/^lib\//, "")}/index.dart`,
1769
+ VIEWMODELS_IMPORT: `package:${contextData.projectName}/${vmsPath.replace(/^lib\//, "")}/index.dart`,
1770
+ NETWORK_IMPORT: `package:${contextData.projectName}/core/network/index.dart`,
1771
+ UTILS_IMPORT: `package:${contextData.projectName}/core/utils/index.dart`
1772
+ };
1773
+ const copyAndExport = async (srcFile, targetSubDir, targetFileName) => {
1774
+ const srcPath = (0, import_path6.join)(examplesDir, srcFile);
1775
+ const targetDir = (0, import_path6.join)(projectPath, targetSubDir);
1776
+ const targetPath = (0, import_path6.join)(targetDir, targetFileName);
1777
+ if (await import_fs_extra3.default.pathExists(srcPath)) {
1778
+ await import_fs_extra3.default.ensureDir(targetDir);
1779
+ await import_fs_extra3.default.copy(srcPath, targetPath);
1780
+ let finalPath = targetPath;
1781
+ if (targetPath.endsWith(".template")) {
1782
+ finalPath = targetPath.replace(/\.template$/, "");
1783
+ await import_fs_extra3.default.rename(targetPath, finalPath);
1784
+ }
1785
+ await TemplateRenderer.renderFile(finalPath, renderData);
1786
+ }
1787
+ };
1788
+ await copyAndExport("eg_list_page.dart.template", pagesPath, "eg_list_page.dart.template");
1789
+ await copyAndExport("eg_list_viewmodel.dart.template", vmsPath, "eg_list_viewmodel.dart.template");
1790
+ await copyAndExport("eg_service.dart.template", servicesPath, "eg_service.dart.template");
1791
+ await copyAndExport("tu_chong_model.dart.template", modelsPath, "tu_chong_model.dart.template");
1792
+ await copyAndExport("mock_data.dart.template", modelsPath, "mock_data.dart.template");
1793
+ await generateIndexFile((0, import_path6.join)(projectPath, pagesPath));
1794
+ await generateIndexFile((0, import_path6.join)(projectPath, vmsPath));
1795
+ await generateIndexFile((0, import_path6.join)(projectPath, servicesPath));
1796
+ await generateIndexFile((0, import_path6.join)(projectPath, modelsPath));
1797
+ }
1798
+ async function generateIndexFile(dir) {
1799
+ if (!await import_fs_extra3.default.pathExists(dir)) return;
1800
+ const files = await import_fs_extra3.default.readdir(dir);
1801
+ const exports2 = [];
1802
+ for (const file of files) {
1803
+ if (file.endsWith(".dart") && file !== "index.dart" && !file.includes(".template")) {
1804
+ const content = await import_fs_extra3.default.readFile((0, import_path6.join)(dir, file), "utf8");
1805
+ if (!content.trim().startsWith("part of")) {
1806
+ exports2.push(`export '${file}';`);
1807
+ }
1808
+ }
1809
+ }
1810
+ if (exports2.length > 0) {
1811
+ const indexContent = exports2.sort().join("\n") + "\n";
1812
+ await import_fs_extra3.default.writeFile((0, import_path6.join)(dir, "index.dart"), indexContent);
1813
+ }
1814
+ }
1815
+ async function copyCustomTemplate(templatePath, targetPath) {
1816
+ try {
1817
+ if (!await import_fs_extra3.default.pathExists(templatePath)) {
1818
+ return false;
1819
+ }
1820
+ const includeDirs = ["lib", "assets", ".vscode"];
1821
+ const includeFiles = [
1822
+ "analysis_options.yaml",
1823
+ "README.md",
1824
+ "pubspec.yaml"
1825
+ ];
1826
+ for (const dir of includeDirs) {
1827
+ const srcDir = (0, import_path6.join)(templatePath, dir);
1828
+ const dstDir = (0, import_path6.join)(targetPath, dir);
1829
+ if (await import_fs_extra3.default.pathExists(srcDir)) {
1830
+ await import_fs_extra3.default.copy(srcDir, dstDir, { overwrite: true });
1831
+ }
1832
+ }
1833
+ for (const file of includeFiles) {
1834
+ const srcFile = (0, import_path6.join)(templatePath, file);
1835
+ const dstFile = (0, import_path6.join)(targetPath, file);
1836
+ if (await import_fs_extra3.default.pathExists(srcFile)) {
1837
+ await import_fs_extra3.default.copy(srcFile, dstFile, { overwrite: true });
1838
+ }
1839
+ }
1840
+ await removeTemplateSuffix(targetPath);
1841
+ return true;
1842
+ } catch (error) {
1843
+ return false;
1844
+ }
1845
+ }
1846
+ async function replaceVariables(projectDir, variables) {
1847
+ try {
1848
+ const replacements = {
1849
+ "{{projectName}}": variables.projectName,
1850
+ "{{project_name}}": variables.projectName,
1851
+ "{{package_name}}": variables.packageName,
1852
+ "{{author}}": variables.author || "Your Name",
1853
+ "{{year}}": (/* @__PURE__ */ new Date()).getFullYear().toString()
1854
+ };
1855
+ await replaceInDirectory(projectDir, replacements);
1856
+ return true;
1857
+ } catch (error) {
1858
+ return false;
1859
+ }
1860
+ }
1861
+ async function replaceInDirectory(dir, replacements) {
1862
+ const items = await import_fs_extra3.default.readdir(dir);
1863
+ for (const item of items) {
1864
+ const itemPath = (0, import_path6.join)(dir, item);
1865
+ const stat = await import_fs_extra3.default.stat(itemPath);
1866
+ if (stat.isDirectory()) {
1867
+ if ([".git", "node_modules", ".dart_tool", "build"].includes(item)) {
1868
+ continue;
1869
+ }
1870
+ await replaceInDirectory(itemPath, replacements);
1871
+ } else if (stat.isFile()) {
1872
+ if (isTextFile(itemPath)) {
1873
+ await replaceInFile(itemPath, replacements);
1874
+ }
1875
+ }
1876
+ }
1877
+ }
1878
+ async function replaceInFile(filePath, replacements) {
1879
+ try {
1880
+ let content = await import_fs_extra3.default.readFile(filePath, "utf8");
1881
+ let modified = false;
1882
+ for (const [pattern, value] of Object.entries(replacements)) {
1883
+ if (content.includes(pattern)) {
1884
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1885
+ content = content.replace(new RegExp(escaped, "g"), value);
1886
+ modified = true;
1887
+ }
1888
+ }
1889
+ if (modified) {
1890
+ await import_fs_extra3.default.writeFile(filePath, content, "utf8");
1891
+ }
1892
+ } catch (e) {
1893
+ }
1894
+ }
1895
+ function isTextFile(filePath) {
1896
+ const textExtensions = [
1897
+ ".dart",
1898
+ ".yaml",
1899
+ ".yml",
1900
+ ".json",
1901
+ ".md",
1902
+ ".txt",
1903
+ ".gradle",
1904
+ ".xml",
1905
+ ".plist",
1906
+ ".swift",
1907
+ ".kt",
1908
+ ".java",
1909
+ ".js",
1910
+ ".ts",
1911
+ ".html",
1912
+ ".css",
1913
+ ".sh"
1914
+ ];
1915
+ return textExtensions.some((ext) => filePath.endsWith(ext));
1916
+ }
1917
+ async function removeTemplateSuffix(dir) {
1918
+ if (!await import_fs_extra3.default.pathExists(dir)) {
1919
+ return;
1920
+ }
1921
+ const items = await import_fs_extra3.default.readdir(dir);
1922
+ for (const item of items) {
1923
+ const itemPath = (0, import_path6.join)(dir, item);
1924
+ const stat = await import_fs_extra3.default.stat(itemPath);
1925
+ if (stat.isDirectory()) {
1926
+ await removeTemplateSuffix(itemPath);
1927
+ } else if (item.endsWith(".template")) {
1928
+ const newPath = itemPath.replace(/\.template$/, "");
1929
+ await import_fs_extra3.default.rename(itemPath, newPath);
1930
+ }
1931
+ }
1932
+ }
1933
+ async function ensurePubspecName(projectDir, projectName) {
1934
+ try {
1935
+ const pubspec = (0, import_path6.join)(projectDir, "pubspec.yaml");
1936
+ if (!await import_fs_extra3.default.pathExists(pubspec)) return;
1937
+ let content = await import_fs_extra3.default.readFile(pubspec, "utf8");
1938
+ if (/^name:\s+/m.test(content)) {
1939
+ content = content.replace(/^name:\s+.*/m, `name: ${projectName}`);
1940
+ } else {
1941
+ content = `name: ${projectName}
1942
+ ` + content;
1943
+ }
1944
+ await import_fs_extra3.default.writeFile(pubspec, content, "utf8");
1945
+ } catch (error) {
1946
+ }
1947
+ }
1948
+ async function cleanupTemplateFiles(projectDir) {
1949
+ const filesToRemove = [".flu-cli.yaml", "README.template.md", ".git"];
1950
+ for (const file of filesToRemove) {
1951
+ const filePath = (0, import_path6.join)(projectDir, file);
1952
+ if (await import_fs_extra3.default.pathExists(filePath)) {
1953
+ await import_fs_extra3.default.remove(filePath);
1954
+ }
1955
+ }
1956
+ }
1957
+
1958
+ // src/generators/page_generator.ts
1959
+ var import_fs8 = require("fs");
1960
+ var import_path11 = require("path");
1961
+ init_logger();
1962
+
1963
+ // src/generators/model_generator.ts
1964
+ var import_fs5 = require("fs");
1965
+ var import_path7 = require("path");
1966
+ init_logger();
1967
+
1968
+ // src/templates/simple_templates.ts
1969
+ function getSimpleViewModel(namePascal) {
1970
+ return `import 'package:flutter/foundation.dart';
1971
+
1972
+ class ${namePascal}ViewModel extends ChangeNotifier {
1973
+ bool _isLoading = false;
1974
+ bool get isLoading => _isLoading;
1975
+
1976
+ void setLoading(bool loading) {
1977
+ _isLoading = loading;
1978
+ notifyListeners();
1979
+ }
1980
+
1981
+ // TODO: Add your logic here
1982
+ }
1983
+ `;
1984
+ }
1985
+ function getSimpleWidget(namePascal, isStateful = false) {
1986
+ if (isStateful) {
1987
+ return `import 'package:flutter/material.dart';
1988
+
1989
+ class ${namePascal} extends StatefulWidget {
1990
+ const ${namePascal}({super.key});
1991
+
1992
+ @override
1993
+ State<${namePascal}> createState() => _${namePascal}State();
1994
+ }
1995
+
1996
+ class _${namePascal}State extends State<${namePascal}> {
1997
+ @override
1998
+ Widget build(BuildContext context) {
1999
+ return const SizedBox.shrink();
2000
+ }
2001
+ }
2002
+ `;
2003
+ }
2004
+ return `import 'package:flutter/material.dart';
2005
+
2006
+ class ${namePascal} extends StatelessWidget {
2007
+ const ${namePascal}({super.key});
2008
+
2009
+ @override
2010
+ Widget build(BuildContext context) {
2011
+ return const SizedBox.shrink();
2012
+ }
2013
+ }
2014
+ `;
2015
+ }
2016
+ function getSimpleModel(namePascal) {
2017
+ return `class ${namePascal} {
2018
+ // TODO: Add properties
2019
+
2020
+ ${namePascal}();
2021
+
2022
+ factory ${namePascal}.fromJson(Map<String, dynamic> json) {
2023
+ return ${namePascal}(
2024
+ // TODO: Map json to properties
2025
+ );
2026
+ }
2027
+
2028
+ Map<String, dynamic> toJson() {
2029
+ return {
2030
+ // TODO: Map properties to json
2031
+ };
2032
+ }
2033
+ }
2034
+ `;
2035
+ }
2036
+
2037
+ // src/generators/model_generator.ts
2038
+ async function generateModel(name, options = {}, logger2 = new ConsoleLogger()) {
2039
+ try {
2040
+ const {
2041
+ feature = null,
2042
+ jsonFile = null,
2043
+ jsonData = null,
2044
+ // 新增:支持直接传入 JSON 对象
2045
+ outputDir = process.cwd()
2046
+ } = options;
2047
+ const projectConfig = ProjectConfigManager.loadConfig(outputDir);
2048
+ const modelConfig = projectConfig?.generators?.model;
2049
+ const namePascal = toPascalCase(name);
2050
+ const nameSnake = toSnakeCase(name);
2051
+ let modelsDir;
2052
+ if (modelConfig?.path) {
2053
+ modelsDir = (0, import_path7.join)(outputDir, modelConfig.path.replace("{feature}", feature || name));
2054
+ } else {
2055
+ modelsDir = getModelPath(outputDir, feature);
2056
+ }
2057
+ if (!(0, import_fs5.existsSync)(modelsDir)) {
2058
+ (0, import_fs5.mkdirSync)(modelsDir, { recursive: true });
2059
+ }
2060
+ let content = null;
2061
+ const isCustom = isCustomTemplateProject(outputDir);
2062
+ if (jsonData) {
2063
+ logger2.info("\u4F7F\u7528\u4F20\u5165\u7684 JSON \u6570\u636E\u751F\u6210\u6A21\u578B");
2064
+ content = generateModelFromJson(namePascal, jsonData);
2065
+ } else if (jsonFile && (0, import_fs5.existsSync)(jsonFile)) {
2066
+ logger2.info(`\u4ECE JSON \u6587\u4EF6\u751F\u6210: ${jsonFile}`);
2067
+ const jsonContent = (0, import_fs5.readFileSync)(jsonFile, "utf8");
2068
+ const parsedData = JSON.parse(jsonContent);
2069
+ content = generateModelFromJson(namePascal, parsedData);
2070
+ } else {
2071
+ if (modelConfig) {
2072
+ if (!modelConfig.withBaseModel) {
2073
+ content = getSimpleModel(namePascal);
2074
+ }
2075
+ }
2076
+ if (!content && !modelConfig && isCustom) {
2077
+ content = getSimpleModel(namePascal);
2078
+ }
2079
+ if (!content) {
2080
+ content = getSnippetContent(outputDir, "flu.model", { Name: namePascal }) || generateBasicModel(namePascal);
2081
+ }
2082
+ }
2083
+ const fileName = modelConfig?.fileName ? modelConfig.fileName.replace("{name}", nameSnake) : `${nameSnake}${modelConfig?.fileSuffix ?? "_model"}.dart`;
2084
+ const filePath = (0, import_path7.join)(modelsDir, fileName);
2085
+ if ((0, import_fs5.existsSync)(filePath)) {
2086
+ logger2.error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}`);
2087
+ return false;
2088
+ }
2089
+ (0, import_fs5.writeFileSync)(filePath, content, "utf8");
2090
+ logger2.success(`Model \u521B\u5EFA\u6210\u529F: ${filePath}`);
2091
+ updateIndexFile(modelsDir, fileName);
2092
+ return true;
2093
+ } catch (error) {
2094
+ logger2.error(`\u751F\u6210 Model \u5931\u8D25: ${error.message}`);
2095
+ return false;
2096
+ }
2097
+ }
2098
+ function generateBasicModel(namePascal) {
2099
+ return `class ${namePascal}Model {
2100
+ final String id;
2101
+ final String name;
2102
+ final DateTime createdAt;
2103
+
2104
+ ${namePascal}Model({
2105
+ required this.id,
2106
+ required this.name,
2107
+ required this.createdAt,
2108
+ });
2109
+
2110
+ // \u4ECE JSON \u521B\u5EFA
2111
+ factory ${namePascal}Model.fromJson(Map<String, dynamic> json) {
2112
+ return ${namePascal}Model(
2113
+ id: json['id'] as String,
2114
+ name: json['name'] as String,
2115
+ createdAt: DateTime.parse(json['created_at'] as String),
2116
+ );
2117
+ }
2118
+
2119
+ // \u8F6C\u6362\u4E3A JSON
2120
+ Map<String, dynamic> toJson() {
2121
+ return {
2122
+ 'id': id,
2123
+ 'name': name,
2124
+ 'created_at': createdAt.toIso8601String(),
2125
+ };
2126
+ }
2127
+
2128
+ // \u590D\u5236\u5E76\u4FEE\u6539
2129
+ ${namePascal}Model copyWith({
2130
+ String? id,
2131
+ String? name,
2132
+ DateTime? createdAt,
2133
+ }) {
2134
+ return ${namePascal}Model(
2135
+ id: id ?? this.id,
2136
+ name: name ?? this.name,
2137
+ createdAt: createdAt ?? this.createdAt,
2138
+ );
2139
+ }
2140
+
2141
+ @override
2142
+ String toString() {
2143
+ return '${namePascal}Model(id: $id, name: $name, createdAt: $createdAt)';
2144
+ }
2145
+
2146
+ @override
2147
+ bool operator ==(Object other) {
2148
+ if (identical(this, other)) return true;
2149
+ return other is ${namePascal}Model &&
2150
+ other.id == id &&
2151
+ other.name == name &&
2152
+ other.createdAt == createdAt;
2153
+ }
2154
+
2155
+ @override
2156
+ int get hashCode {
2157
+ return id.hashCode ^ name.hashCode ^ createdAt.hashCode;
2158
+ }
2159
+ }
2160
+ `;
2161
+ }
2162
+ function generateModelFromJson(namePascal, jsonData) {
2163
+ const fields = analyzeJsonStructure(jsonData);
2164
+ const fieldDeclarations = fields.map((f) => ` final ${f.type} ${f.name};`).join("\n");
2165
+ const constructorParams = fields.map((f) => ` required this.${f.name},`).join("\n");
2166
+ const fromJsonFields = fields.map((f) => {
2167
+ if (f.type === "String") {
2168
+ return ` ${f.name}: (json['${f.jsonKey}'] as String?) ?? '',`;
2169
+ } else if (f.type === "int") {
2170
+ return ` ${f.name}: (json['${f.jsonKey}'] as num?)?.toInt() ?? 0,`;
2171
+ } else if (f.type === "double") {
2172
+ return ` ${f.name}: (json['${f.jsonKey}'] as num?)?.toDouble() ?? 0.0,`;
2173
+ } else if (f.type === "bool") {
2174
+ return ` ${f.name}: (json['${f.jsonKey}'] as bool?) ?? false,`;
2175
+ } else if (f.type === "DateTime") {
2176
+ return ` ${f.name}: DateTime.tryParse((json['${f.jsonKey}'] as String?) ?? '') ?? DateTime.now(),`;
2177
+ } else if (f.type.startsWith("List<")) {
2178
+ const itemType = f.type.slice(5, -1);
2179
+ if (["String", "int", "double", "bool"].includes(itemType)) {
2180
+ return ` ${f.name}: (json['${f.jsonKey}'] as List<dynamic>?)?.map((e) => e as ${itemType}).toList() ?? [],`;
2181
+ }
2182
+ return ` ${f.name}: (json['${f.jsonKey}'] as List<dynamic>?)?.cast<${itemType}>() ?? [],`;
2183
+ } else {
2184
+ return ` ${f.name}: json['${f.jsonKey}'],`;
2185
+ }
2186
+ }).join("\n");
2187
+ const toJsonFields = fields.map((f) => {
2188
+ if (f.type === "DateTime") {
2189
+ return ` '${f.jsonKey}': ${f.name}.toIso8601String(),`;
2190
+ } else {
2191
+ return ` '${f.jsonKey}': ${f.name},`;
2192
+ }
2193
+ }).join("\n");
2194
+ const copyWithParams = fields.map((f) => ` ${f.type}? ${f.name},`).join("\n");
2195
+ const copyWithFields = fields.map((f) => ` ${f.name}: ${f.name} ?? this.${f.name},`).join("\n");
2196
+ const toStringFields = fields.map((f) => `${f.name}: $${f.name}`).join(", ");
2197
+ const equalityChecks = fields.map((f) => ` other.${f.name} == ${f.name}`).join(" &&\n");
2198
+ const hashCodeFields = fields.map((f) => `${f.name}.hashCode`).join(" ^ ");
2199
+ return `class ${namePascal}Model {
2200
+ ${fieldDeclarations}
2201
+
2202
+ ${namePascal}Model({
2203
+ ${constructorParams}
2204
+ });
2205
+
2206
+ // \u4ECE JSON \u521B\u5EFA
2207
+ factory ${namePascal}Model.fromJson(Map<String, dynamic> json) {
2208
+ return ${namePascal}Model(
2209
+ ${fromJsonFields}
2210
+ );
2211
+ }
2212
+
2213
+ // \u8F6C\u6362\u4E3A JSON
2214
+ Map<String, dynamic> toJson() {
2215
+ return {
2216
+ ${toJsonFields}
2217
+ };
2218
+ }
2219
+
2220
+ // \u590D\u5236\u5E76\u4FEE\u6539
2221
+ ${namePascal}Model copyWith({
2222
+ ${copyWithParams}
2223
+ }) {
2224
+ return ${namePascal}Model(
2225
+ ${copyWithFields}
2226
+ );
2227
+ }
2228
+
2229
+ @override
2230
+ String toString() {
2231
+ return '${namePascal}Model(${toStringFields})';
2232
+ }
2233
+
2234
+ @override
2235
+ bool operator ==(Object other) {
2236
+ if (identical(this, other)) return true;
2237
+ return other is ${namePascal}Model &&
2238
+ ${equalityChecks};
2239
+ }
2240
+
2241
+ @override
2242
+ int get hashCode {
2243
+ return ${hashCodeFields};
2244
+ }
2245
+ }
2246
+ `;
2247
+ }
2248
+ function analyzeJsonStructure(jsonData) {
2249
+ const fields = [];
2250
+ for (const [key, value] of Object.entries(jsonData)) {
2251
+ const camelKey = toCamelCase(key);
2252
+ const type = inferDartType(value);
2253
+ fields.push({
2254
+ name: camelKey,
2255
+ jsonKey: key,
2256
+ type
2257
+ });
2258
+ }
2259
+ return fields;
2260
+ }
2261
+ function inferDartType(value) {
2262
+ if (value === null) return "dynamic";
2263
+ const type = typeof value;
2264
+ if (type === "string") {
2265
+ if (/^\d{4}-\d{2}-\d{2}/.test(value)) {
2266
+ return "DateTime";
2267
+ }
2268
+ return "String";
2269
+ }
2270
+ if (type === "number") {
2271
+ return Number.isInteger(value) ? "int" : "double";
2272
+ }
2273
+ if (type === "boolean") {
2274
+ return "bool";
2275
+ }
2276
+ if (Array.isArray(value)) {
2277
+ if (value.length === 0) {
2278
+ return "List<dynamic>";
2279
+ }
2280
+ const itemType = inferDartType(value[0]);
2281
+ return `List<${itemType}>`;
2282
+ }
2283
+ if (type === "object") {
2284
+ return "Map<String, dynamic>";
2285
+ }
2286
+ return "dynamic";
2287
+ }
2288
+
2289
+ // src/generators/viewmodel_generator.ts
2290
+ var import_fs6 = require("fs");
2291
+ var import_path9 = require("path");
2292
+ init_logger();
2293
+ async function generateViewModel(name, options = {}, logger2 = new ConsoleLogger()) {
2294
+ try {
2295
+ const { feature = null, outputDir = process.cwd() } = options;
2296
+ const namePascal = toPascalCase(name);
2297
+ const nameSnake = toSnakeCase(name);
2298
+ const projectConfig = ProjectConfigManager.loadConfig(outputDir);
2299
+ const vmConfig = projectConfig?.generators?.viewModel;
2300
+ let vmDir;
2301
+ if (vmConfig?.path) {
2302
+ vmDir = (0, import_path9.join)(
2303
+ outputDir,
2304
+ vmConfig.path.replace("{feature}", feature || name)
2305
+ );
2306
+ } else {
2307
+ vmDir = getViewModelPath(outputDir, feature || name);
2308
+ }
2309
+ if (!(0, import_fs6.existsSync)(vmDir)) {
2310
+ (0, import_fs6.mkdirSync)(vmDir, { recursive: true });
2311
+ }
2312
+ const template = options.template || detectProjectTemplate(outputDir) || "lite";
2313
+ const stateManager = options.stateManager || getStateManager(outputDir);
2314
+ const isCustom = isCustomTemplateProject(outputDir);
2315
+ let content = null;
2316
+ if (vmConfig) {
2317
+ if (!vmConfig.withBaseViewModel) {
2318
+ content = getSimpleViewModel(namePascal);
2319
+ }
2320
+ }
2321
+ if (!content && !vmConfig) {
2322
+ if (isCustom && !["lite", "modular", "clean"].includes(template)) {
2323
+ content = getSimpleViewModel(namePascal);
2324
+ logger2.info("\u751F\u6210\u7C7B\u578B: Simple ViewModel (\u672A\u77E5/\u81EA\u5B9A\u4E49\u6A21\u677F)");
2325
+ }
2326
+ }
2327
+ if (!content) {
2328
+ const fileName2 = vmConfig?.fileName ? vmConfig.fileName.replace("{name}", nameSnake) : `${nameSnake}${vmConfig?.fileSuffix ?? "_viewmodel"}.dart`;
2329
+ const filePath2 = (0, import_path9.join)(vmDir, fileName2);
2330
+ const { StateManagerAdapterFactory: StateManagerAdapterFactory2 } = await Promise.resolve().then(() => (init_factory(), factory_exports));
2331
+ const adapter = StateManagerAdapterFactory2.getAdapter(stateManager);
2332
+ const coreImportPath = getPackageImportPath(outputDir, "core/index.dart") || calculateRelativeImport(filePath2, (0, import_path9.join)(outputDir, "lib", "core", "index.dart"));
2333
+ const variables = {
2334
+ Name: namePascal,
2335
+ name: nameSnake.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
2336
+ snake_name: nameSnake,
2337
+ relative_core_path: coreImportPath,
2338
+ base_viewmodel: adapter.getBaseViewModelParent?.(options.isListPage || false) || "BaseViewModel"
2339
+ };
2340
+ let key = stateManager === "riverpod" ? "flu.riverpodVm" : "flu.vm";
2341
+ content = getSnippetContent(outputDir, key, variables);
2342
+ if (!content) {
2343
+ const imports = adapter.getImports?.(coreImportPath) || [`import '${coreImportPath}';`];
2344
+ const baseClass = adapter.getBaseViewModelParent?.(options.isListPage || false) || "BaseViewModel";
2345
+ if (stateManager === "riverpod") {
2346
+ const stateClass = `${namePascal}State`;
2347
+ const camel = nameSnake.replace(/_([a-z])/g, (_, $1) => $1.toUpperCase());
2348
+ const providerName = `${camel}Provider`;
2349
+ const baseStatePath = coreImportPath.startsWith("package:") ? coreImportPath.replace("index.dart", "core/base/base_state.dart") : coreImportPath.replace("index.dart", "core/base/base_state.dart");
2350
+ const baseNotifierPath = coreImportPath.startsWith("package:") ? coreImportPath.replace("index.dart", "core/base/base_notifier.dart") : coreImportPath.replace("index.dart", "core/base/base_notifier.dart");
2351
+ content = `import 'package:flutter_riverpod/flutter_riverpod.dart';
2352
+ import '${baseStatePath}';
2353
+ import '${baseNotifierPath}';
2354
+
2355
+ class ${stateClass} extends BaseState {
2356
+ final int counter;
2357
+ const ${stateClass}({ ViewState state = ViewState.idle, bool isRefreshing = false, String? error, this.counter = 0 })
2358
+ : super(state: state, isRefreshing: isRefreshing, error: error);
2359
+
2360
+ ${stateClass} copy({ ViewState? state, bool? isRefreshing, String? error, int? counter }) {
2361
+ return ${stateClass}(
2362
+ state: state ?? this.state,
2363
+ isRefreshing: isRefreshing ?? this.isRefreshing,
2364
+ error: error ?? this.error,
2365
+ counter: counter ?? this.counter,
2366
+ );
2367
+ }
2368
+ }
2369
+
2370
+ class ${namePascal}ViewModel extends BaseNotifier<${stateClass}> {
2371
+ @override
2372
+ ${stateClass} createInitialState() => const ${stateClass}();
2373
+
2374
+ Future<void> loadData() async {
2375
+ await run(() async {
2376
+ await Future.delayed(const Duration(milliseconds: 500));
2377
+ state = state.copy(counter: state.counter + 1);
2378
+ });
2379
+ }
2380
+
2381
+ Future<void> refresh() async {
2382
+ setRefreshing(true);
2383
+ await loadData();
2384
+ setRefreshing(false);
2385
+ }
2386
+ }
2387
+
2388
+ final ${providerName} = NotifierProvider<${namePascal}ViewModel, ${stateClass}>(${namePascal}ViewModel.new);
2389
+ `;
2390
+ } else {
2391
+ content = TemplateGenerator.generateViewModel(namePascal, {
2392
+ coreImportPath,
2393
+ baseClass,
2394
+ extraImports: imports
2395
+ });
2396
+ }
2397
+ }
2398
+ }
2399
+ const fileName = vmConfig?.fileName ? vmConfig.fileName.replace("{name}", nameSnake) : `${nameSnake}${vmConfig?.fileSuffix ?? "_viewmodel"}.dart`;
2400
+ const filePath = (0, import_path9.join)(vmDir, fileName);
2401
+ if ((0, import_fs6.existsSync)(filePath)) {
2402
+ logger2.error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}`);
2403
+ return false;
2404
+ }
2405
+ if (content === null) {
2406
+ logger2.error("\u65E0\u6CD5\u751F\u6210 ViewModel \u5185\u5BB9");
2407
+ return false;
2408
+ }
2409
+ (0, import_fs6.writeFileSync)(filePath, content, "utf8");
2410
+ logger2.success(`ViewModel \u521B\u5EFA\u6210\u529F: ${filePath}`);
2411
+ updateIndexFile(vmDir, fileName);
2412
+ return true;
2413
+ } catch (error) {
2414
+ logger2.error(`\u751F\u6210 ViewModel \u5931\u8D25: ${error.message}`);
2415
+ return false;
2416
+ }
2417
+ }
2418
+ function calculateRelativeImport(fromFile, toFile) {
2419
+ const fromDir = (0, import_path9.dirname)(fromFile);
2420
+ const relPath = (0, import_path9.relative)(fromDir, toFile);
2421
+ return relPath.replace(/\\/g, "/");
2422
+ }
2423
+
2424
+ // src/generators/page_generator.ts
2425
+ async function generatePage(name, options = {}, logger2 = new ConsoleLogger()) {
2426
+ try {
2427
+ const {
2428
+ feature = null,
2429
+ stateful = false,
2430
+ stateless = false,
2431
+ withViewModel = true,
2432
+ isListPage = false,
2433
+ outputDir = process.cwd()
2434
+ } = options;
2435
+ const projectConfig = ProjectConfigManager.loadConfig(outputDir);
2436
+ const pageConfig = projectConfig?.generators?.page;
2437
+ const template = detectProjectTemplate(outputDir);
2438
+ const stateManager = getStateManager(outputDir);
2439
+ const isCustom = isCustomTemplateProject(outputDir);
2440
+ logger2.info(
2441
+ `\u68C0\u6D4B\u5230\u9879\u76EE\u7C7B\u578B: ${template || (isCustom ? "\u81EA\u5B9A\u4E49 (Custom)" : "\u672A\u77E5")}`
2442
+ );
2443
+ if (pageConfig) {
2444
+ logger2.info("\u4F7F\u7528\u9879\u76EE\u914D\u7F6E\u6587\u4EF6 (.flu-cli.json)");
2445
+ }
2446
+ const { checkSnippetsVersion: checkSnippetsVersion2 } = await Promise.resolve().then(() => (init_upgrade_snippets(), upgrade_snippets_exports));
2447
+ const snippetsStatus = checkSnippetsVersion2(outputDir);
2448
+ if (snippetsStatus === "outdated") {
2449
+ logger2.warn("\u26A0\uFE0F \u68C0\u6D4B\u5230\u9879\u76EE\u4E2D\u7684 snippets \u6587\u4EF6\u5DF2\u8FC7\u65F6");
2450
+ logger2.info("\u{1F4A1} \u5EFA\u8BAE\u8FD0\u884C: flu-cli upgrade snippets");
2451
+ }
2452
+ const namePascal = toPascalCase(name);
2453
+ const nameSnake = toSnakeCase(name);
2454
+ const nameTitle = toTitleCase(name);
2455
+ let moduleName = feature || name;
2456
+ let pagesDir;
2457
+ if (pageConfig?.path) {
2458
+ pagesDir = (0, import_path11.join)(
2459
+ outputDir,
2460
+ pageConfig.path.replace("{feature}", moduleName)
2461
+ );
2462
+ } else {
2463
+ pagesDir = getPagePath(outputDir, moduleName);
2464
+ }
2465
+ const vmDir = getViewModelPath(outputDir, moduleName);
2466
+ const pageFileName = pageConfig?.fileName ? pageConfig.fileName.replace("{name}", nameSnake) : `${nameSnake}${pageConfig?.fileSuffix ?? "_page"}.dart`;
2467
+ const vmFileName = projectConfig?.generators?.viewModel?.fileName ? projectConfig.generators.viewModel.fileName.replace(
2468
+ "{name}",
2469
+ nameSnake
2470
+ ) : `${nameSnake}${projectConfig?.generators?.viewModel?.fileSuffix ?? "_viewmodel"}.dart`;
2471
+ const pageFilePath = (0, import_path11.join)(pagesDir, pageFileName);
2472
+ const vmFilePath = (0, import_path11.join)(vmDir, vmFileName);
2473
+ if (!(0, import_fs8.existsSync)(pagesDir)) {
2474
+ (0, import_fs8.mkdirSync)(pagesDir, { recursive: true });
2475
+ }
2476
+ if (withViewModel && !(0, import_fs8.existsSync)(vmDir)) {
2477
+ (0, import_fs8.mkdirSync)(vmDir, { recursive: true });
2478
+ }
2479
+ const vmImportPath = calculateRelativeImport2(pageFilePath, vmFilePath);
2480
+ const variables = {
2481
+ Name: namePascal,
2482
+ name: nameSnake.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
2483
+ snake_name: nameSnake,
2484
+ title: nameTitle,
2485
+ vm_import: withViewModel ? vmImportPath : "",
2486
+ ModelName: namePascal + "Model"
2487
+ // 默认 Model 名称
2488
+ };
2489
+ let content = null;
2490
+ let key = "";
2491
+ if (pageConfig) {
2492
+ if (!pageConfig.withBasePage && !pageConfig.withViewModel) {
2493
+ if (pageConfig.defaultType === "stateless") {
2494
+ content = TemplateGenerator.generateSimpleStatelessPage(
2495
+ namePascal,
2496
+ nameTitle
2497
+ );
2498
+ } else {
2499
+ content = TemplateGenerator.generateSimpleStatefulPage(
2500
+ namePascal,
2501
+ nameTitle
2502
+ );
2503
+ }
2504
+ }
2505
+ }
2506
+ if (!content && !pageConfig && !withViewModel) {
2507
+ if (stateless) {
2508
+ content = TemplateGenerator.generateSimpleStatelessPage(
2509
+ namePascal,
2510
+ nameTitle
2511
+ );
2512
+ logger2.info("\u751F\u6210\u7C7B\u578B: Simple Stateless Page (\u65E0\u914D\u7F6E\u6587\u4EF6)");
2513
+ } else {
2514
+ content = TemplateGenerator.generateSimpleStatefulPage(
2515
+ namePascal,
2516
+ nameTitle
2517
+ );
2518
+ logger2.info("\u751F\u6210\u7C7B\u578B: Simple Stateful Page (\u65E0\u914D\u7F6E\u6587\u4EF6)");
2519
+ }
2520
+ }
2521
+ if (!content) {
2522
+ const { StateManagerAdapterFactory: StateManagerAdapterFactory2 } = await Promise.resolve().then(() => (init_factory(), factory_exports));
2523
+ const adapter = StateManagerAdapterFactory2.getAdapter(stateManager);
2524
+ const coreImportPath = getPackageImportPath(outputDir, "core/index.dart") || calculateRelativeImport2(pageFilePath, (0, import_path11.join)(outputDir, "lib", "core", "index.dart"));
2525
+ logger2.info(`[DEBUG] Core import path: ${coreImportPath}`);
2526
+ const variables2 = {
2527
+ Name: namePascal,
2528
+ name: nameSnake.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
2529
+ snake_name: nameSnake,
2530
+ title: nameTitle,
2531
+ vm_import: withViewModel ? vmImportPath : "",
2532
+ relative_core_path: coreImportPath,
2533
+ base_page: adapter.getBasePageParent?.(isListPage) || "BasePage",
2534
+ base_viewmodel: adapter.getBaseViewModelParent?.(isListPage) || "BaseViewModel"
2535
+ };
2536
+ if (stateManager === "riverpod") {
2537
+ key = "flu.riverpodPage";
2538
+ content = getSnippetContent(outputDir, key, variables2);
2539
+ } else if (isListPage) {
2540
+ key = "flu.listPage";
2541
+ } else if (stateless) {
2542
+ key = "flu.lessPage";
2543
+ } else {
2544
+ key = "flu.stPage";
2545
+ }
2546
+ if (!content) {
2547
+ content = getSnippetContent(outputDir, key, variables2);
2548
+ }
2549
+ if (!content) {
2550
+ const imports = adapter.getImports?.(coreImportPath) || [`import '${coreImportPath}';`];
2551
+ const baseClass = adapter.getBasePageParent?.(isListPage) || "BasePage";
2552
+ if (stateManager === "riverpod") {
2553
+ const camel = nameSnake.replace(/_([a-z])/g, (_, $1) => $1.toUpperCase());
2554
+ content = `${imports.join("\n")}
2555
+ import '${vmImportPath}';
2556
+
2557
+ class ${namePascal}Page extends ${baseClass}<${namePascal}State, ${namePascal}ViewModel> {
2558
+ const ${namePascal}Page({super.key});
2559
+ @override
2560
+ String get title => '${nameTitle}';
2561
+ @override
2562
+ ProviderListenable<${namePascal}State> get provider => ${camel}Provider;
2563
+
2564
+ @override
2565
+ Widget buildContent(BuildContext context, ${namePascal}State state, ${namePascal}ViewModel vm) {
2566
+ return Center(child: Text('${namePascal}Page'));
2567
+ }
2568
+ }
2569
+ `;
2570
+ } else if (withViewModel && !stateless) {
2571
+ content = TemplateGenerator.generateStatefulPageWithBase(namePascal, nameTitle, {
2572
+ vmImportPath,
2573
+ coreImportPath,
2574
+ baseClass,
2575
+ extraImports: imports
2576
+ });
2577
+ } else if (stateless) {
2578
+ content = TemplateGenerator.generateSimpleStatelessPage(namePascal, nameTitle);
2579
+ } else {
2580
+ content = TemplateGenerator.generateSimpleStatefulPage(namePascal, nameTitle);
2581
+ }
2582
+ }
2583
+ }
2584
+ if ((0, import_fs8.existsSync)(pageFilePath)) {
2585
+ logger2.error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${pageFilePath}`);
2586
+ return false;
2587
+ }
2588
+ (0, import_fs8.writeFileSync)(pageFilePath, content, "utf8");
2589
+ logger2.success(`\u9875\u9762\u521B\u5EFA\u6210\u529F: ${pageFilePath}`);
2590
+ updateIndexFile(pagesDir, pageFileName);
2591
+ if (isListPage) {
2592
+ logger2.info("\u5217\u8868\u9875\u9700\u8981 Model\uFF0C\u6B63\u5728\u81EA\u52A8\u751F\u6210...");
2593
+ const modelFeature = template && (template.includes("modular") || template.includes("clean")) ? moduleName : null;
2594
+ generateModel(name, { feature: modelFeature, outputDir }, logger2);
2595
+ }
2596
+ if (withViewModel) {
2597
+ const vmFeature = template && (template.includes("modular") || template.includes("clean")) ? moduleName : null;
2598
+ generateViewModel(
2599
+ name,
2600
+ { feature: vmFeature, outputDir, template, isListPage },
2601
+ logger2
2602
+ );
2603
+ }
2604
+ return true;
2605
+ } catch (error) {
2606
+ logger2.error(`\u751F\u6210\u9875\u9762\u5931\u8D25: ${error.message}`);
2607
+ return false;
2608
+ }
2609
+ }
2610
+ function calculateRelativeImport2(fromFile, toFile) {
2611
+ const fromDir = (0, import_path11.dirname)(fromFile);
2612
+ const relPath = (0, import_path11.relative)(fromDir, toFile);
2613
+ return relPath.replace(/\\/g, "/");
2614
+ }
2615
+
2616
+ // src/generators/widget_generator.ts
2617
+ var import_fs9 = require("fs");
2618
+ var import_path12 = require("path");
2619
+ init_logger();
2620
+ async function generateWidget(name, options = {}, logger2 = new ConsoleLogger()) {
2621
+ try {
2622
+ const {
2623
+ feature = null,
2624
+ stateful = false,
2625
+ outputDir = process.cwd()
2626
+ } = options;
2627
+ const projectConfig = ProjectConfigManager.loadConfig(outputDir);
2628
+ const widgetConfig = projectConfig?.generators?.widget;
2629
+ const template = detectProjectTemplate(outputDir);
2630
+ const isCustom = isCustomTemplateProject(outputDir);
2631
+ const namePascal = toPascalCase(name);
2632
+ const nameSnake = toSnakeCase(name);
2633
+ let widgetsDir;
2634
+ if (widgetConfig?.path) {
2635
+ widgetsDir = (0, import_path12.join)(outputDir, widgetConfig.path.replace("{feature}", feature || name));
2636
+ } else {
2637
+ widgetsDir = getWidgetPath(outputDir, feature);
2638
+ }
2639
+ if (!(0, import_fs9.existsSync)(widgetsDir)) {
2640
+ (0, import_fs9.mkdirSync)(widgetsDir, { recursive: true });
2641
+ }
2642
+ let content = null;
2643
+ const key = stateful ? "flu.stWidget" : "flu.lessWidget";
2644
+ if (widgetConfig) {
2645
+ content = getSimpleWidget(namePascal, stateful);
2646
+ }
2647
+ if (!content && !widgetConfig && isCustom) {
2648
+ content = getSimpleWidget(namePascal, stateful);
2649
+ }
2650
+ if (!content) {
2651
+ content = getSnippetContent(outputDir, key, { Name: namePascal });
2652
+ if (!content) {
2653
+ content = generateWidgetContent(namePascal, stateful);
2654
+ }
2655
+ }
2656
+ const fileName = widgetConfig?.fileName ? widgetConfig.fileName.replace("{name}", nameSnake) : `${nameSnake}${widgetConfig?.fileSuffix ?? "_widget"}.dart`;
2657
+ const filePath = (0, import_path12.join)(widgetsDir, fileName);
2658
+ if ((0, import_fs9.existsSync)(filePath)) {
2659
+ logger2.error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}`);
2660
+ return false;
2661
+ }
2662
+ (0, import_fs9.writeFileSync)(filePath, content, "utf8");
2663
+ logger2.success(`Widget \u521B\u5EFA\u6210\u529F: ${filePath}`);
2664
+ updateIndexFile(widgetsDir, fileName);
2665
+ return true;
2666
+ } catch (error) {
2667
+ logger2.error(`\u751F\u6210 Widget \u5931\u8D25: ${error.message}`);
2668
+ return false;
2669
+ }
2670
+ }
2671
+ function generateWidgetContent(namePascal, stateful) {
2672
+ if (stateful) {
2673
+ return `import 'package:flutter/material.dart';
2674
+
2675
+ class ${namePascal}Widget extends StatefulWidget {
2676
+ const ${namePascal}Widget({super.key});
2677
+
2678
+ @override
2679
+ State<${namePascal}Widget> createState() => _${namePascal}WidgetState();
2680
+ }
2681
+
2682
+ class _${namePascal}WidgetState extends State<${namePascal}Widget> {
2683
+ @override
2684
+ Widget build(BuildContext context) {
2685
+ return Container(
2686
+ child: const Text('${namePascal}Widget'),
2687
+ );
2688
+ }
2689
+ }
2690
+ `;
2691
+ } else {
2692
+ return `import 'package:flutter/material.dart';
2693
+
2694
+ class ${namePascal}Widget extends StatelessWidget {
2695
+ const ${namePascal}Widget({super.key});
2696
+
2697
+ @override
2698
+ Widget build(BuildContext context) {
2699
+ return Container(
2700
+ child: const Text('${namePascal}Widget'),
2701
+ );
2702
+ }
2703
+ }
2704
+ `;
2705
+ }
2706
+ }
2707
+
2708
+ // src/generators/component_generator.ts
2709
+ var import_fs10 = require("fs");
2710
+ var import_path13 = require("path");
2711
+ init_logger();
2712
+ async function generateComponent(name, options = {}, logger2 = new ConsoleLogger()) {
2713
+ try {
2714
+ const {
2715
+ feature = null,
2716
+ stateful = false,
2717
+ outputDir = process.cwd()
2718
+ } = options;
2719
+ const namePascal = toPascalCase(name);
2720
+ const nameSnake = toSnakeCase(name);
2721
+ const projectConfig = ProjectConfigManager.loadConfig(outputDir);
2722
+ const componentConfig = projectConfig?.generators?.component;
2723
+ let componentsDir;
2724
+ if (componentConfig?.path) {
2725
+ componentsDir = (0, import_path13.join)(outputDir, componentConfig.path.replace("{feature}", feature || name));
2726
+ } else if (feature) {
2727
+ componentsDir = (0, import_path13.join)(outputDir, "lib", "features", feature, "components");
2728
+ } else {
2729
+ componentsDir = (0, import_path13.join)(outputDir, "lib", "components");
2730
+ }
2731
+ if (!(0, import_fs10.existsSync)(componentsDir)) {
2732
+ (0, import_fs10.mkdirSync)(componentsDir, { recursive: true });
2733
+ }
2734
+ const snippetKey = stateful ? "flu.stComponent" : "flu.component";
2735
+ const content = getSnippetContent(outputDir, snippetKey, { Name: namePascal }) || generateComponentContent(namePascal, stateful);
2736
+ logger2.info(`\u751F\u6210\u7C7B\u578B: ${stateful ? "Stateful" : "Stateless"} Component`);
2737
+ const fileName = componentConfig?.fileName ? componentConfig.fileName.replace("{name}", nameSnake) : `${nameSnake}${componentConfig?.fileSuffix ?? "_component"}.dart`;
2738
+ const filePath = (0, import_path13.join)(componentsDir, fileName);
2739
+ if ((0, import_fs10.existsSync)(filePath)) {
2740
+ logger2.error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}`);
2741
+ return false;
2742
+ }
2743
+ (0, import_fs10.writeFileSync)(filePath, content, "utf8");
2744
+ logger2.success(`Component \u521B\u5EFA\u6210\u529F: ${filePath}`);
2745
+ updateIndexFile(componentsDir, fileName);
2746
+ return true;
2747
+ } catch (error) {
2748
+ const errorMessage = error instanceof Error ? error.message : String(error);
2749
+ logger2.error(`\u751F\u6210 Component \u5931\u8D25: ${errorMessage}`);
2750
+ return false;
2751
+ }
2752
+ }
2753
+ function generateComponentContent(namePascal, stateful = false) {
2754
+ if (stateful) {
2755
+ return `import 'package:flutter/material.dart';
2756
+
2757
+ class ${namePascal}Component extends StatefulWidget {
2758
+ const ${namePascal}Component({super.key});
2759
+
2760
+ @override
2761
+ State<${namePascal}Component> createState() => _${namePascal}ComponentState();
2762
+ }
2763
+
2764
+ class _${namePascal}ComponentState extends State<${namePascal}Component> {
2765
+ @override
2766
+ Widget build(BuildContext context) {
2767
+ return Container(
2768
+ padding: const EdgeInsets.all(16),
2769
+ child: Column(
2770
+ crossAxisAlignment: CrossAxisAlignment.start,
2771
+ children: [
2772
+ Text(
2773
+ '${namePascal}Component',
2774
+ style: Theme.of(context).textTheme.titleLarge,
2775
+ ),
2776
+ const SizedBox(height: 8),
2777
+ const Text('Component content goes here'),
2778
+ ],
2779
+ ),
2780
+ );
2781
+ }
2782
+ }
2783
+ `;
2784
+ }
2785
+ return `import 'package:flutter/material.dart';
2786
+
2787
+ class ${namePascal}Component extends StatelessWidget {
2788
+ const ${namePascal}Component({super.key});
2789
+
2790
+ @override
2791
+ Widget build(BuildContext context) {
2792
+ return Container(
2793
+ padding: const EdgeInsets.all(16),
2794
+ child: Column(
2795
+ crossAxisAlignment: CrossAxisAlignment.start,
2796
+ children: [
2797
+ Text(
2798
+ '${namePascal}Component',
2799
+ style: Theme.of(context).textTheme.titleLarge,
2800
+ ),
2801
+ const SizedBox(height: 8),
2802
+ const Text('Component content goes here'),
2803
+ ],
2804
+ ),
2805
+ );
2806
+ }
2807
+ }
2808
+ `;
2809
+ }
2810
+
2811
+ // src/generators/service_generator.ts
2812
+ var import_fs11 = require("fs");
2813
+ var import_path14 = require("path");
2814
+ init_logger();
2815
+ async function generateService(name, options = {}, customLogger) {
2816
+ try {
2817
+ const { feature = null, outputDir = process.cwd() } = options;
2818
+ const namePascal = toPascalCase(name);
2819
+ const nameSnake = toSnakeCase(name);
2820
+ const projectConfig = ProjectConfigManager.loadConfig(outputDir);
2821
+ const serviceConfig = projectConfig?.generators?.service;
2822
+ let servicesDir;
2823
+ if (serviceConfig?.path) {
2824
+ servicesDir = (0, import_path14.join)(outputDir, serviceConfig.path.replace("{feature}", feature || name));
2825
+ } else {
2826
+ servicesDir = getServicePath(outputDir, feature);
2827
+ }
2828
+ if (!(0, import_fs11.existsSync)(servicesDir)) {
2829
+ (0, import_fs11.mkdirSync)(servicesDir, { recursive: true });
2830
+ }
2831
+ const templateType = selectTemplate(outputDir, serviceConfig);
2832
+ let content = null;
2833
+ const snippetKey = serviceConfig?.snippetKey || "flu.service";
2834
+ content = getSnippetContent(outputDir, snippetKey, {
2835
+ Name: namePascal,
2836
+ snake_name: nameSnake,
2837
+ relative_core_path: calculateCoreImport(servicesDir, outputDir)
2838
+ });
2839
+ if (!content) {
2840
+ if (templateType === "network") {
2841
+ content = generateNetworkService(namePascal, nameSnake, servicesDir, outputDir);
2842
+ } else {
2843
+ content = generateSimpleService(namePascal);
2844
+ }
2845
+ }
2846
+ const fileName = serviceConfig?.fileName ? serviceConfig.fileName.replace("{name}", nameSnake) : `${nameSnake}_service.dart`;
2847
+ const filePath = (0, import_path14.join)(servicesDir, fileName);
2848
+ if ((0, import_fs11.existsSync)(filePath)) {
2849
+ logger.error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}`);
2850
+ return false;
2851
+ }
2852
+ (0, import_fs11.writeFileSync)(filePath, content, "utf8");
2853
+ logger.success(`Service \u521B\u5EFA\u6210\u529F: ${filePath}`);
2854
+ updateIndexFile(servicesDir, fileName);
2855
+ return true;
2856
+ } catch (error) {
2857
+ logger.error(
2858
+ `\u751F\u6210 Service \u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
2859
+ );
2860
+ return false;
2861
+ }
2862
+ }
2863
+ function selectTemplate(outputDir, config) {
2864
+ if (config?.template) {
2865
+ return config.template === "network" ? "network" : "simple";
2866
+ }
2867
+ if (hasNetworkLayer(outputDir)) {
2868
+ return "network";
2869
+ }
2870
+ return "simple";
2871
+ }
2872
+ function hasNetworkLayer(outputDir) {
2873
+ const appHttpPath = (0, import_path14.join)(outputDir, "lib/core/network/app_http.dart");
2874
+ return (0, import_fs11.existsSync)(appHttpPath);
2875
+ }
2876
+ function calculateCoreImport(fromDir, projectRoot) {
2877
+ const coreIndexPath = (0, import_path14.join)(projectRoot, "lib/core/index.dart");
2878
+ const relPath = (0, import_path14.relative)(fromDir, coreIndexPath);
2879
+ return relPath.replace(/\\/g, "/");
2880
+ }
2881
+ function generateNetworkService(namePascal, nameSnake, servicesDir, projectRoot) {
2882
+ const coreImport = calculateCoreImport(servicesDir, projectRoot);
2883
+ return `import '${coreImport}';
2884
+
2885
+ class ${namePascal}Service {
2886
+ final AppHttp _http;
2887
+
2888
+ ${namePascal}Service({AppHttp? http}) : _http = http ?? AppHttp();
2889
+
2890
+ /// \u83B7\u53D6\u5217\u8868\u6570\u636E
2891
+ Future<List<dynamic>> fetchList({
2892
+ int page = 1,
2893
+ int pageSize = 10,
2894
+ }) async {
2895
+ if (AppConfig.I.useMockData) {
2896
+ return _loadMockData(page, pageSize);
2897
+ }
2898
+
2899
+ final response = await _http.get(
2900
+ '/${nameSnake}/list',
2901
+ queryParameters: {'page': page, 'pageSize': pageSize},
2902
+ );
2903
+
2904
+ // \u964D\u7EA7\u5904\u7406\uFF1A\u8BF7\u6C42\u5931\u8D25\u65F6\u8FD4\u56DE Mock \u6570\u636E
2905
+ if (response.isSuccess && response.data is List) {
2906
+ return response.data as List;
2907
+ }
2908
+
2909
+ return _loadMockData(page, pageSize);
2910
+ }
2911
+
2912
+ /// \u6839\u636E ID \u83B7\u53D6\u8BE6\u60C5
2913
+ Future<dynamic> fetchById(String id) async {
2914
+ if (AppConfig.I.useMockData) {
2915
+ return null; // \u8FD4\u56DE Mock \u6570\u636E
2916
+ }
2917
+
2918
+ final response = await _http.get('/${nameSnake}/$id');
2919
+ return response.isSuccess ? response.data : null;
2920
+ }
2921
+
2922
+ /// Mock \u6570\u636E\u52A0\u8F7D
2923
+ 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
+ return [];
2927
+ }
2928
+ }
2929
+ `;
2930
+ }
2931
+ function generateSimpleService(namePascal) {
2932
+ return `class ${namePascal}Service {
2933
+ /// \u83B7\u53D6\u6570\u636E\u5217\u8868
2934
+ Future<List<Map<String, dynamic>>> fetchList() async {
2935
+ // \u5B9E\u73B0\u4E1A\u52A1\u903B\u8F91
2936
+ // \u4F8B\u5982: \u4ECE\u672C\u5730\u5B58\u50A8\u8BFB\u53D6\u3001\u8BA1\u7B97\u7B49
2937
+ return [];
2938
+ }
2939
+
2940
+ /// \u6839\u636E ID \u83B7\u53D6\u6570\u636E
2941
+ Future<Map<String, dynamic>?> fetchById(String id) async {
2942
+ // \u5B9E\u73B0\u4E1A\u52A1\u903B\u8F91
2943
+ return null;
2944
+ }
2945
+
2946
+ /// \u4FDD\u5B58\u6216\u66F4\u65B0\u6570\u636E
2947
+ Future<bool> save(Map<String, dynamic> data) async {
2948
+ // \u5B9E\u73B0\u4E1A\u52A1\u903B\u8F91
2949
+ return true;
2950
+ }
2951
+ }
2952
+ `;
2953
+ }
2954
+
2955
+ // src/generators/module_generator.ts
2956
+ var import_fs12 = require("fs");
2957
+ var import_path15 = require("path");
2958
+ init_logger();
2959
+ async function generateModule(name, options = {}, logger2 = new ConsoleLogger()) {
2960
+ try {
2961
+ const {
2962
+ outputDir = process.cwd()
2963
+ } = options;
2964
+ const nameSnake = toSnakeCase(name);
2965
+ const projectConfig = ProjectConfigManager.loadConfig(outputDir);
2966
+ const moduleConfig = projectConfig?.generators?.module;
2967
+ const moduleDir = moduleConfig?.path ? (0, import_path15.join)(outputDir, moduleConfig.path.replace("{feature}", nameSnake)) : (0, import_path15.join)(outputDir, "lib", "features", nameSnake);
2968
+ if ((0, import_fs12.existsSync)(moduleDir)) {
2969
+ logger2.error(`\u6A21\u5757\u5DF2\u5B58\u5728: ${moduleDir}`);
2970
+ return false;
2971
+ }
2972
+ const directories = [
2973
+ "pages",
2974
+ "viewmodels",
2975
+ "widgets",
2976
+ "services",
2977
+ "models"
2978
+ ];
2979
+ logger2.info(`\u521B\u5EFA\u6A21\u5757: ${nameSnake}`);
2980
+ for (const dir of directories) {
2981
+ const dirPath = (0, import_path15.join)(moduleDir, dir);
2982
+ (0, import_fs12.mkdirSync)(dirPath, { recursive: true });
2983
+ const indexPath = (0, import_path15.join)(dirPath, "index.dart");
2984
+ const indexContent = generateIndexContent(dir, nameSnake);
2985
+ (0, import_fs12.writeFileSync)(indexPath, indexContent, "utf8");
2986
+ logger2.success(`\u521B\u5EFA\u76EE\u5F55: ${dir}/`);
2987
+ }
2988
+ const moduleIndexPath = (0, import_path15.join)(moduleDir, "index.dart");
2989
+ const moduleIndexContent = generateModuleIndexContent(nameSnake);
2990
+ (0, import_fs12.writeFileSync)(moduleIndexPath, moduleIndexContent, "utf8");
2991
+ logger2.newLine();
2992
+ logger2.success(`\u2705 \u6A21\u5757\u521B\u5EFA\u6210\u529F: features/${nameSnake}/`);
2993
+ logger2.newLine();
2994
+ logger2.info("\u6A21\u5757\u7ED3\u6784:");
2995
+ logger2.info(` features/${nameSnake}/`);
2996
+ logger2.info(` \u251C\u2500\u2500 pages/`);
2997
+ logger2.info(` \u2502 \u2514\u2500\u2500 index.dart`);
2998
+ logger2.info(` \u251C\u2500\u2500 viewmodels/`);
2999
+ logger2.info(` \u2502 \u2514\u2500\u2500 index.dart`);
3000
+ logger2.info(` \u251C\u2500\u2500 widgets/`);
3001
+ logger2.info(` \u2502 \u2514\u2500\u2500 index.dart`);
3002
+ logger2.info(` \u251C\u2500\u2500 services/`);
3003
+ logger2.info(` \u2502 \u2514\u2500\u2500 index.dart`);
3004
+ logger2.info(` \u251C\u2500\u2500 models/`);
3005
+ logger2.info(` \u2502 \u2514\u2500\u2500 index.dart`);
3006
+ logger2.info(` \u2514\u2500\u2500 index.dart`);
3007
+ logger2.newLine();
3008
+ logger2.info("\u4E0B\u4E00\u6B65:");
3009
+ logger2.info(` 1. \u4F7F\u7528 "flu-cli add page <name> -f ${nameSnake}" \u6DFB\u52A0\u9875\u9762`);
3010
+ logger2.info(` 2. \u4F7F\u7528 "flu-cli add service <name> -f ${nameSnake}" \u6DFB\u52A0\u670D\u52A1`);
3011
+ logger2.info(` 3. \u4F7F\u7528 "flu-cli add model <name> -f ${nameSnake}" \u6DFB\u52A0\u6A21\u578B`);
3012
+ return true;
3013
+ } catch (error) {
3014
+ logger2.error(`\u6A21\u5757\u521B\u5EFA\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
3015
+ return false;
3016
+ }
3017
+ }
3018
+ function generateIndexContent(dirName, moduleName) {
3019
+ const comments = {
3020
+ pages: "// \u5BFC\u51FA\u6240\u6709\u9875\u9762",
3021
+ viewmodels: "// \u5BFC\u51FA\u6240\u6709 ViewModel",
3022
+ widgets: "// \u5BFC\u51FA\u6240\u6709 Widget",
3023
+ services: "// \u5BFC\u51FA\u6240\u6709 Service",
3024
+ models: "// \u5BFC\u51FA\u6240\u6709 Model"
3025
+ };
3026
+ const examples = {
3027
+ pages: `// export 'home_page.dart';`,
3028
+ viewmodels: `// export 'home_viewmodel.dart';`,
3029
+ widgets: `// export 'custom_widget.dart';`,
3030
+ services: `// export 'api_service.dart';`,
3031
+ models: `// export 'user_model.dart';`
3032
+ };
3033
+ return `${comments[dirName] || "// \u5BFC\u51FA\u6587\u4EF6"}
3034
+
3035
+ ${examples[dirName] || "// export 'example.dart';"}
3036
+ `;
3037
+ }
3038
+ function generateModuleIndexContent(moduleName) {
3039
+ return `// ${moduleName} \u6A21\u5757\u5BFC\u51FA
3040
+
3041
+ // \u5BFC\u51FA\u9875\u9762
3042
+ export 'pages/index.dart';
3043
+
3044
+ // \u5BFC\u51FA ViewModel
3045
+ export 'viewmodels/index.dart';
3046
+
3047
+ // \u5BFC\u51FA Widget
3048
+ export 'widgets/index.dart';
3049
+
3050
+ // \u5BFC\u51FA Service
3051
+ export 'services/index.dart';
3052
+
3053
+ // \u5BFC\u51FA Model
3054
+ export 'models/index.dart';
3055
+ `;
3056
+ }
3057
+
3058
+ // src/index.ts
3059
+ init_upgrade_snippets();
3060
+
3061
+ // src/generators/project_generator.ts
3062
+ var import_fs14 = require("fs");
3063
+ var import_path26 = require("path");
3064
+ init_logger();
3065
+
3066
+ // src/utils/template_manager.ts
3067
+ var import_simple_git = require("simple-git");
3068
+ var import_path17 = require("path");
3069
+ var import_fs_extra5 = __toESM(require("fs-extra"), 1);
3070
+ var import_os2 = require("os");
3071
+ init_logger();
3072
+
3073
+ // src/utils/config_manager.ts
3074
+ var import_fs13 = require("fs");
3075
+ var import_path16 = require("path");
3076
+ var import_os = require("os");
3077
+ var ConfigManager = class _ConfigManager {
3078
+ static instance;
3079
+ configPath;
3080
+ config;
3081
+ constructor() {
3082
+ const configDir = (0, import_path16.join)((0, import_os.homedir)(), ".flu-cli");
3083
+ if (!(0, import_fs13.existsSync)(configDir)) {
3084
+ (0, import_fs13.mkdirSync)(configDir, { recursive: true });
3085
+ }
3086
+ this.configPath = (0, import_path16.join)(configDir, "config.json");
3087
+ this.config = this.loadConfig();
3088
+ }
3089
+ static getInstance() {
3090
+ if (!_ConfigManager.instance) {
3091
+ _ConfigManager.instance = new _ConfigManager();
3092
+ }
3093
+ return _ConfigManager.instance;
3094
+ }
3095
+ loadConfig() {
3096
+ if ((0, import_fs13.existsSync)(this.configPath)) {
3097
+ try {
3098
+ const content = (0, import_fs13.readFileSync)(this.configPath, "utf8");
3099
+ const parsed = JSON.parse(content);
3100
+ return {
3101
+ templates: parsed.templates || [],
3102
+ authorName: parsed.authorName,
3103
+ defaultTemplate: parsed.defaultTemplate,
3104
+ locale: parsed.locale
3105
+ };
3106
+ } catch (e) {
3107
+ return { templates: [] };
3108
+ }
3109
+ }
3110
+ return { templates: [] };
3111
+ }
3112
+ saveConfig() {
3113
+ (0, import_fs13.writeFileSync)(this.configPath, JSON.stringify(this.config, null, 2), "utf8");
3114
+ }
3115
+ // --- 模板管理 ---
3116
+ getTemplates() {
3117
+ return this.config.templates || [];
3118
+ }
3119
+ addTemplate(template) {
3120
+ if (!this.config.templates) {
3121
+ this.config.templates = [];
3122
+ }
3123
+ const index = this.config.templates.findIndex((t2) => t2.id === template.id);
3124
+ if (index !== -1) {
3125
+ this.config.templates[index] = { ...this.config.templates[index], ...template, lastUsedAt: Date.now() };
3126
+ } else {
3127
+ this.config.templates.push({ ...template, lastUsedAt: Date.now() });
3128
+ }
3129
+ this.saveConfig();
3130
+ }
3131
+ removeTemplate(id) {
3132
+ if (!this.config.templates) return false;
3133
+ const initialLength = this.config.templates.length;
3134
+ this.config.templates = this.config.templates.filter((t2) => t2.id !== id);
3135
+ if (this.config.templates.length !== initialLength) {
3136
+ this.saveConfig();
3137
+ return true;
3138
+ }
3139
+ return false;
3140
+ }
3141
+ getTemplate(id) {
3142
+ return this.config.templates?.find((t2) => t2.id === id);
3143
+ }
3144
+ // --- 全局设置 ---
3145
+ getAuthorName() {
3146
+ return this.config.authorName || "Your Name";
3147
+ }
3148
+ setAuthorName(name) {
3149
+ this.config.authorName = name;
3150
+ this.saveConfig();
3151
+ }
3152
+ getDefaultTemplate() {
3153
+ return this.config.defaultTemplate || { type: "builtin", idOrName: "lite" };
3154
+ }
3155
+ setDefaultTemplate(type, idOrName) {
3156
+ this.config.defaultTemplate = { type, idOrName };
3157
+ this.saveConfig();
3158
+ }
3159
+ getLocale() {
3160
+ return this.config.locale || "";
3161
+ }
3162
+ setLocale(locale) {
3163
+ this.config.locale = locale;
3164
+ this.saveConfig();
3165
+ }
3166
+ };
3167
+
3168
+ // src/utils/template_manager.ts
3169
+ function loadEnvFile() {
3170
+ try {
3171
+ const possiblePaths = [
3172
+ (0, import_path17.resolve)(process.cwd(), ".env"),
3173
+ (0, import_path17.resolve)(process.cwd(), "..", ".env"),
3174
+ (0, import_path17.resolve)(process.cwd(), "../..", ".env"),
3175
+ (0, import_path17.resolve)(process.cwd(), "../../..", ".env")
3176
+ ];
3177
+ for (const envPath of possiblePaths) {
3178
+ if (import_fs_extra5.default.existsSync(envPath)) {
3179
+ const envContent = import_fs_extra5.default.readFileSync(envPath, "utf8");
3180
+ const lines = envContent.split("\n");
3181
+ for (const line of lines) {
3182
+ const trimmed = line.trim();
3183
+ if (!trimmed || trimmed.startsWith("#")) continue;
3184
+ const [key, ...valueParts] = trimmed.split("=");
3185
+ const value = valueParts.join("=").trim();
3186
+ const cleanValue = value.replace(/^["']|["']$/g, "");
3187
+ process.env[key] = cleanValue;
3188
+ }
3189
+ break;
3190
+ }
3191
+ }
3192
+ } catch (error) {
3193
+ }
3194
+ }
3195
+ var BUILTIN_TEMPLATES = {
3196
+ lite: {
3197
+ name: "Lite",
3198
+ displayName: "Lite - \u7CBE\u7B80\u7248",
3199
+ description: "\u6700\u5C0F\u4F9D\u8D56\uFF0C\u5355\u6587\u4EF6\u7ED3\u6784\uFF0C\u9002\u5408\u5C0F\u578B\u9879\u76EE\u548C\u5FEB\u901F\u539F\u578B",
3200
+ repo: "https://gitee.com/flu-cli/template_lite.git",
3201
+ branch: "main",
3202
+ complexity: 1,
3203
+ teamSize: "1 \u4EBA",
3204
+ codeSize: "< 5k \u884C",
3205
+ features: ["\u2705 \u6700\u5C0F\u4F9D\u8D56", "\u2705 \u5355\u6587\u4EF6\u7ED3\u6784", "\u2705 Material 3 \u8BBE\u8BA1", "\u2705 \u5FEB\u901F\u542F\u52A8"],
3206
+ structure: `
3207
+ lib/
3208
+ \u251C\u2500\u2500 \u{1F4C4} main.dart # \u5E94\u7528\u5165\u53E3
3209
+ \u251C\u2500\u2500 \u{1F4C4} app.dart # \u5E94\u7528\u914D\u7F6E
3210
+ \u251C\u2500\u2500 \u{1F4C1} pages/ # \u9875\u9762
3211
+ \u251C\u2500\u2500 \u{1F4C1} viewmodels/ # \u89C6\u56FE\u6A21\u578B
3212
+ \u251C\u2500\u2500 \u{1F4C1} widgets/ # \u7B80\u5355\u7EC4\u4EF6
3213
+ \u251C\u2500\u2500 \u{1F4C1} components/ # \u590D\u5408\u7EC4\u4EF6
3214
+ \u251C\u2500\u2500 \u{1F4C1} services/ # \u4E1A\u52A1\u670D\u52A1
3215
+ \u251C\u2500\u2500 \u{1F4C1} models/ # \u6570\u636E\u6A21\u578B
3216
+ \u251C\u2500\u2500 \u{1F4C1} config/ # \u914D\u7F6E
3217
+ \u2514\u2500\u2500 \u{1F4C1} utils/ # \u5DE5\u5177\u7C7B
3218
+ `
3219
+ },
3220
+ modular: {
3221
+ name: "Modular",
3222
+ displayName: "Modular - \u6A21\u5757\u5316\u7248",
3223
+ description: "\u6A21\u5757\u5316\u7ED3\u6784\uFF0C\u529F\u80FD\u5206\u7EC4\uFF0C\u9002\u5408\u4E2D\u578B\u9879\u76EE",
3224
+ repo: "https://gitee.com/flu-cli/template_modular.git",
3225
+ branch: "main",
3226
+ complexity: 3,
3227
+ teamSize: "2-5 \u4EBA",
3228
+ codeSize: "5k-20k \u884C",
3229
+ features: [
3230
+ "\u2705 \u6A21\u5757\u5316\u7ED3\u6784",
3231
+ "\u2705 \u8DEF\u7531\u914D\u7F6E\u72EC\u7ACB",
3232
+ "\u2705 \u4E3B\u9898\u914D\u7F6E\u72EC\u7ACB",
3233
+ "\u2705 \u529F\u80FD\u5206\u7EC4"
3234
+ ],
3235
+ structure: `
3236
+ lib/
3237
+ \u251C\u2500\u2500 \u{1F4C4} main.dart
3238
+ \u251C\u2500\u2500 \u{1F4C1} core/ # \u6838\u5FC3\u57FA\u7840\u8BBE\u65BD
3239
+ \u2502 \u251C\u2500\u2500 theme/ # \u4E3B\u9898\u7CFB\u7EDF
3240
+ \u2502 \u251C\u2500\u2500 router/ # \u8DEF\u7531\u7CFB\u7EDF
3241
+ \u2502 \u251C\u2500\u2500 constants/ # \u5E38\u91CF\u5B9A\u4E49
3242
+ \u2502 \u2514\u2500\u2500 base/ # \u57FA\u7840\u7C7B\uFF08BaseViewModel, BasePage\uFF09
3243
+ \u251C\u2500\u2500 \u{1F4C1} shared/ # \u8DE8\u6A21\u5757\u5171\u4EAB
3244
+ \u2502 \u251C\u2500\u2500 widgets/ # \u901A\u7528\u7B80\u5355\u7EC4\u4EF6
3245
+ \u2502 \u251C\u2500\u2500 components/ # \u901A\u7528\u590D\u5408\u7EC4\u4EF6
3246
+ \u2502 \u251C\u2500\u2500 models/ # \u901A\u7528\u6570\u636E\u6A21\u578B
3247
+ \u2502 \u2514\u2500\u2500 utils/ # \u901A\u7528\u5DE5\u5177\u7C7B
3248
+ \u2514\u2500\u2500 \u{1F4C1} features/ # \u4E1A\u52A1\u529F\u80FD\u6A21\u5757
3249
+ \u251C\u2500\u2500 hoem/ # \u7528\u6237\u6A21\u5757
3250
+ \u2502 \u251C\u2500\u2500 pages/
3251
+ \u2502 \u251C\u2500\u2500 viewmodels/
3252
+ \u2502 \u251C\u2500\u2500 widgets/
3253
+ \u2502 \u251C\u2500\u2500 services/
3254
+ \u2514\u2500\u2500 \u2514\u2500\u2500 models/
3255
+ `
3256
+ },
3257
+ clean: {
3258
+ name: "Clean",
3259
+ displayName: "Clean - \u5206\u5C42\u7248",
3260
+ description: "\u4E25\u683C\u5206\u5C42\u67B6\u6784\uFF0C\u6E05\u6670\u7684\u76EE\u5F55\u7ED3\u6784\uFF0C\u9002\u5408\u5927\u578B\u9879\u76EE",
3261
+ repo: "https://gitee.com/flu-cli/template_clean.git",
3262
+ branch: "main",
3263
+ complexity: 5,
3264
+ teamSize: "5+ \u4EBA",
3265
+ codeSize: "> 20k \u884C",
3266
+ features: [
3267
+ "\u2705 \u4E25\u683C\u5206\u5C42\u67B6\u6784",
3268
+ "\u2705 \u6E05\u6670\u7684\u76EE\u5F55\u7ED3\u6784",
3269
+ "\u2705 \u6613\u4E8E\u6269\u5C55\u548C\u7EF4\u62A4",
3270
+ "\u2705 \u9002\u5408\u56E2\u961F\u534F\u4F5C"
3271
+ ],
3272
+ structure: `
3273
+ lib/
3274
+ \u251C\u2500\u2500 \u{1F4C4} main.dart # \u5E94\u7528\u5165\u53E3
3275
+ \u251C\u2500\u2500 \u{1F4C4} app.dart # \u5E94\u7528\u914D\u7F6E
3276
+ \u251C\u2500\u2500 \u{1F4C1} core/ # \u6838\u5FC3\u5C42(\u6700\u5185\u5C42)
3277
+ \u2502 \u251C\u2500\u2500 constants/ # \u5E38\u91CF\u5B9A\u4E49
3278
+ \u2502 \u251C\u2500\u2500 errors/ # \u9519\u8BEF\u548C\u5F02\u5E38
3279
+ \u2502 \u251C\u2500\u2500 usecases/ # UseCase \u57FA\u7C7B
3280
+ \u2502 \u251C\u2500\u2500 utils/ # \u5DE5\u5177\u7C7B
3281
+ \u2502 \u2514\u2500\u2500 network/ # \u7F51\u7EDC\u914D\u7F6E
3282
+ \u251C\u2500\u2500 \u{1F4C1} features/ # \u529F\u80FD\u6A21\u5757(\u6309\u4E1A\u52A1\u5212\u5206)
3283
+ \u2502 \u2514\u2500\u2500 home/ # \u793A\u4F8B:\u9996\u9875\u6A21\u5757
3284
+ \u2502 \u251C\u2500\u2500 data/ # \u6570\u636E\u5C42
3285
+ \u2502 \u2502 \u251C\u2500\u2500 datasources/ # \u6570\u636E\u6E90(API/\u672C\u5730)
3286
+ \u2502 \u2502 \u251C\u2500\u2500 models/ # \u6570\u636E\u6A21\u578B(DTO)
3287
+ \u2502 \u2502 \u2514\u2500\u2500 repositories/ # Repository \u5B9E\u73B0
3288
+ \u2502 \u251C\u2500\u2500 domain/ # \u9886\u57DF\u5C42
3289
+ \u2502 \u2502 \u251C\u2500\u2500 entities/ # \u4E1A\u52A1\u5B9E\u4F53
3290
+ \u2502 \u2502 \u251C\u2500\u2500 repositories/ # Repository \u63A5\u53E3
3291
+ \u2502 \u2502 \u2514\u2500\u2500 usecases/ # \u7528\u4F8B(\u4E1A\u52A1\u903B\u8F91)
3292
+ \u2502 \u2514\u2500\u2500 presentation/ # \u8868\u73B0\u5C42
3293
+ \u2502 \u251C\u2500\u2500 pages/ # \u9875\u9762
3294
+ \u2502 \u251C\u2500\u2500 widgets/ # \u6A21\u5757\u4E13\u7528\u7EC4\u4EF6
3295
+ \u2502 \u2514\u2500\u2500 viewmodels/ # \u89C6\u56FE\u6A21\u578B
3296
+ \u251C\u2500\u2500 \u{1F4C1} shared/ # \u5171\u4EAB\u8D44\u6E90
3297
+ \u2502 \u251C\u2500\u2500 widgets/ # \u901A\u7528\u7EC4\u4EF6
3298
+ \u2502 \u251C\u2500\u2500 components/ # \u901A\u7528\u590D\u5408\u7EC4\u4EF6
3299
+ \u2502 \u2514\u2500\u2500 extensions/ # \u6269\u5C55\u65B9\u6CD5
3300
+ \u2514\u2500\u2500 \u{1F4C1} config/ # \u914D\u7F6E\u5C42
3301
+ \u251C\u2500\u2500 routes/ # \u8DEF\u7531\u914D\u7F6E
3302
+ \u2514\u2500\u2500 theme/ # \u4E3B\u9898\u914D\u7F6E
3303
+ `
3304
+ },
3305
+ native: {
3306
+ name: "Native",
3307
+ displayName: "Native - Flutter \u539F\u751F\u9ED8\u8BA4\u6A21\u677F",
3308
+ description: "\u4EC5\u6267\u884C flutter create\uFF0C\u4E0D\u6CE8\u5165 flu-cli \u7684\u4EFB\u4F55\u57FA\u7840\u8BBE\u65BD\u5C42",
3309
+ repo: "",
3310
+ branch: "main",
3311
+ complexity: 0,
3312
+ teamSize: "N/A",
3313
+ codeSize: "N/A",
3314
+ features: ["\u2705 \u5B98\u65B9\u539F\u751F\u7ED3\u6784", "\u2705 \u65E0\u989D\u5916\u4F9D\u8D56", "\u2705 \u7EAF\u51C0\u73AF\u5883"],
3315
+ structure: `
3316
+ lib/
3317
+ \u2514\u2500\u2500 \u{1F4C4} main.dart # \u5B98\u65B9\u9ED8\u8BA4 Counter Demo
3318
+ `
3319
+ }
3320
+ };
3321
+ var TemplateManager = class _TemplateManager {
3322
+ static instance;
3323
+ configManager;
3324
+ cacheDir;
3325
+ constructor() {
3326
+ this.configManager = ConfigManager.getInstance();
3327
+ this.cacheDir = (0, import_path17.join)((0, import_os2.homedir)(), ".flu-cli", "templates");
3328
+ }
3329
+ static getInstance() {
3330
+ if (!_TemplateManager.instance) {
3331
+ _TemplateManager.instance = new _TemplateManager();
3332
+ }
3333
+ return _TemplateManager.instance;
3334
+ }
3335
+ /**
3336
+ * 获取模板缓存目录
3337
+ */
3338
+ getCacheDir() {
3339
+ return this.cacheDir;
3340
+ }
3341
+ /**
3342
+ * 确保缓存目录存在
3343
+ */
3344
+ async ensureCacheDir() {
3345
+ if (!await import_fs_extra5.default.pathExists(this.cacheDir)) {
3346
+ await import_fs_extra5.default.mkdirp(this.cacheDir);
3347
+ }
3348
+ }
3349
+ /**
3350
+ * 获取模板本地路径
3351
+ * @param templateName 模板名称
3352
+ * @param isCustom 是否为自定义模板
3353
+ */
3354
+ getTemplatePath(templateName, isCustom = false) {
3355
+ const prefix = isCustom ? "custom-" : "template_";
3356
+ return (0, import_path17.join)(this.cacheDir, `${prefix}${templateName}`);
3357
+ }
3358
+ /**
3359
+ * 准备模板(确保已缓存且为最新)
3360
+ *
3361
+ * 支持以下模板源(优先级从高到低):
3362
+ * 1. 环境变量指定的本地模板目录 (FLU_CLI_USE_LOCAL_TEMPLATES=true + FLU_CLI_LOCAL_TEMPLATES_DIR)
3363
+ * 2. 自定义模板配置 (ConfigManager)
3364
+ * 3. 内置模板 (lite/modular/clean)
3365
+ */
3366
+ async prepareTemplate(templateId, logger2 = new ConsoleLogger(), forceUpdate = false) {
3367
+ loadEnvFile();
3368
+ await this.ensureCacheDir();
3369
+ const nodeEnv = process.env.NODE_ENV || "production";
3370
+ const isDevelopment = nodeEnv === "development";
3371
+ if (isDevelopment) {
3372
+ const localTemplatesDir = process.env.FLU_CLI_LOCAL_TEMPLATES_DIR;
3373
+ if (localTemplatesDir) {
3374
+ if ((0, import_path17.isAbsolute)(templateId) && await import_fs_extra5.default.pathExists(templateId)) {
3375
+ return templateId;
3376
+ }
3377
+ let resolvedPath = localTemplatesDir;
3378
+ if (!localTemplatesDir.startsWith("/") && !localTemplatesDir.match(/^[A-Z]:/)) {
3379
+ resolvedPath = (0, import_path17.resolve)(process.cwd(), localTemplatesDir);
3380
+ }
3381
+ const localTemplatePath = (0, import_path17.join)(
3382
+ resolvedPath,
3383
+ `template_${templateId.toLowerCase()}`
3384
+ );
3385
+ if (await import_fs_extra5.default.pathExists(localTemplatePath)) {
3386
+ return localTemplatePath;
3387
+ }
3388
+ logger2.warn(`[DEV] \u672C\u5730\u6A21\u677F\u4E0D\u5B58\u5728: ${localTemplatePath}`);
3389
+ }
3390
+ }
3391
+ if (BUILTIN_TEMPLATES[templateId.toLowerCase()]) {
3392
+ const config = BUILTIN_TEMPLATES[templateId.toLowerCase()];
3393
+ const targetDir = this.getTemplatePath(
3394
+ templateId.toLowerCase(),
3395
+ false
3396
+ );
3397
+ return this.ensureGitRepoCached(
3398
+ targetDir,
3399
+ config.repo,
3400
+ config.branch,
3401
+ logger2,
3402
+ forceUpdate
3403
+ );
3404
+ }
3405
+ const customTemplate = this.configManager.getTemplate(templateId);
3406
+ if (customTemplate) {
3407
+ if (customTemplate.type === "local" && customTemplate.path) {
3408
+ if (await import_fs_extra5.default.pathExists(customTemplate.path)) {
3409
+ return customTemplate.path;
3410
+ }
3411
+ logger2.error(`\u672C\u5730\u6A21\u677F\u8DEF\u5F84\u4E0D\u5B58\u5728: ${customTemplate.path}`);
3412
+ return void 0;
3413
+ }
3414
+ if (customTemplate.type === "git" && customTemplate.url) {
3415
+ const targetDir = this.getTemplatePath(customTemplate.id, true);
3416
+ return this.ensureGitRepoCached(
3417
+ targetDir,
3418
+ customTemplate.url,
3419
+ customTemplate.branch || "main",
3420
+ logger2,
3421
+ forceUpdate
3422
+ );
3423
+ }
3424
+ }
3425
+ logger2.error(`\u65E0\u6CD5\u8BC6\u522B\u7684\u6A21\u677F: ${templateId}`);
3426
+ return void 0;
3427
+ }
3428
+ /**
3429
+ * 通用 Git 仓库缓存逻辑
3430
+ */
3431
+ async ensureGitRepoCached(targetDir, repoUrl, branch, logger2, forceUpdate = false) {
3432
+ try {
3433
+ if (await import_fs_extra5.default.pathExists(targetDir)) {
3434
+ const gitDir = (0, import_path17.join)(targetDir, ".git");
3435
+ if (await import_fs_extra5.default.pathExists(gitDir)) {
3436
+ if (forceUpdate) {
3437
+ logger2.info(`\u6B63\u5728\u66F4\u65B0\u6A21\u677F: ${repoUrl}`);
3438
+ const git = (0, import_simple_git.simpleGit)(targetDir);
3439
+ await git.fetch();
3440
+ await git.reset(["--hard", `origin/${branch}`]);
3441
+ await git.clean("f", ["-d", "-x"]);
3442
+ logger2.success(`\u6A21\u677F\u5DF2\u66F4\u65B0\u5230\u6700\u65B0\u7248\u672C`);
3443
+ } else {
3444
+ logger2.info(`\u4F7F\u7528\u7F13\u5B58\u6A21\u677F: ${targetDir}`);
3445
+ }
3446
+ } else {
3447
+ await import_fs_extra5.default.remove(targetDir);
3448
+ await this.cloneRepo(repoUrl, targetDir, branch, logger2);
3449
+ }
3450
+ return targetDir;
3451
+ }
3452
+ await this.cloneRepo(repoUrl, targetDir, branch, logger2);
3453
+ return targetDir;
3454
+ } catch (error) {
3455
+ logger2.error(`\u83B7\u53D6\u6A21\u677F\u5931\u8D25: ${error.message}`);
3456
+ return void 0;
3457
+ }
3458
+ }
3459
+ /**
3460
+ * 克隆仓库
3461
+ */
3462
+ async cloneRepo(repoUrl, targetDir, branch, logger2) {
3463
+ logger2.info(`\u6B63\u5728\u4E0B\u8F7D\u6A21\u677F: ${repoUrl} (${branch})...`);
3464
+ const git = (0, import_simple_git.simpleGit)();
3465
+ await git.clone(repoUrl, targetDir, [
3466
+ "--depth",
3467
+ "1",
3468
+ "--branch",
3469
+ branch
3470
+ ]);
3471
+ logger2.success(`\u6A21\u677F\u4E0B\u8F7D\u6210\u529F`);
3472
+ }
3473
+ /**
3474
+ * 检查模板更新状态
3475
+ */
3476
+ async checkUpdate(templateId) {
3477
+ const templatePath = await this.prepareTemplate(
3478
+ templateId,
3479
+ new ConsoleLogger(),
3480
+ false
3481
+ );
3482
+ if (!templatePath) return { hasUpdate: false, message: "\u6A21\u677F\u4E0D\u5B58\u5728" };
3483
+ if (!await import_fs_extra5.default.pathExists((0, import_path17.join)(templatePath, ".git"))) {
3484
+ return { hasUpdate: false, message: "\u975E Git \u6A21\u677F\uFF0C\u65E0\u6CD5\u68C0\u67E5\u66F4\u65B0" };
3485
+ }
3486
+ try {
3487
+ const git = (0, import_simple_git.simpleGit)(templatePath);
3488
+ await git.fetch();
3489
+ const status = await git.status();
3490
+ if (status.behind > 0) {
3491
+ return {
3492
+ hasUpdate: true,
3493
+ message: `\u53D1\u73B0 ${status.behind} \u4E2A\u65B0\u63D0\u4EA4`
3494
+ };
3495
+ }
3496
+ return { hasUpdate: false, message: "\u5DF2\u662F\u6700\u65B0\u7248\u672C" };
3497
+ } catch (error) {
3498
+ return { hasUpdate: false, message: `\u68C0\u67E5\u5931\u8D25: ${error.message}` };
3499
+ }
3500
+ }
3501
+ };
3502
+
3503
+ // src/generators/tasks/project_task.ts
3504
+ init_logger();
3505
+ function createDefaultContext(projectPath, projectName, options = {}, logger2 = new ConsoleLogger()) {
3506
+ let rawTemplateType = options.templateType || options.template || "lite";
3507
+ if (typeof rawTemplateType === "string" && rawTemplateType.includes("/")) {
3508
+ const match = rawTemplateType.match(/template[_-](lite|modular|clean)/i);
3509
+ rawTemplateType = match ? match[1] : "lite";
3510
+ }
3511
+ const templateType = rawTemplateType.toLowerCase();
3512
+ const rawIncludeNetwork = options.includeNetworkLayer !== void 0 ? options.includeNetworkLayer : options.includeNetwork ?? true;
3513
+ const includeNetworkLayer = typeof rawIncludeNetwork === "string" ? rawIncludeNetwork === "true" : !!rawIncludeNetwork;
3514
+ const stateManager = (options.stateManager || "provider").toLowerCase();
3515
+ return {
3516
+ projectPath,
3517
+ projectName,
3518
+ templateType,
3519
+ includeNetworkLayer,
3520
+ stateManager,
3521
+ options: {
3522
+ ...options,
3523
+ templateType,
3524
+ includeNetworkLayer
3525
+ },
3526
+ variables: {
3527
+ projectName,
3528
+ ...options.variables || {}
3529
+ },
3530
+ logger: logger2
3531
+ };
3532
+ }
3533
+
3534
+ // src/generators/tasks/pipeline.ts
3535
+ var import_chalk2 = __toESM(require("chalk"), 1);
3536
+ var ProjectPipeline = class {
3537
+ tasks = [];
3538
+ /**
3539
+ * 添加任务
3540
+ */
3541
+ addTask(task) {
3542
+ this.tasks.push(task);
3543
+ return this;
3544
+ }
3545
+ /**
3546
+ * 执行所有任务
3547
+ */
3548
+ async execute(context) {
3549
+ context.logger.info(import_chalk2.default.cyan(`
3550
+ \u{1F680} \u5F00\u59CB\u6267\u884C\u9879\u76EE\u751F\u6210\u6D41\u6C34\u7EBF: ${context.projectName}`));
3551
+ context.logger.info(import_chalk2.default.gray(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
3552
+ try {
3553
+ for (const task of this.tasks) {
3554
+ context.logger.info(import_chalk2.default.blue(`\u{1F539} \u6267\u884C\u4EFB\u52A1: ${task.name}...`));
3555
+ await task.run(context);
3556
+ }
3557
+ context.logger.info(import_chalk2.default.gray(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
3558
+ context.logger.success(import_chalk2.default.green(`\u{1F3C1} \u6D41\u6C34\u7EBF\u6267\u884C\u6210\u529F!
3559
+ `));
3560
+ return true;
3561
+ } catch (error) {
3562
+ context.logger.error(`
3563
+ \u274C \u4EFB\u52A1\u6267\u884C\u5931\u8D25 [${error.message}]`);
3564
+ return false;
3565
+ }
3566
+ }
3567
+ };
3568
+
3569
+ // src/generators/tasks/template_copy_task.ts
3570
+ var import_path18 = require("path");
3571
+ var import_fs_extra6 = __toESM(require("fs-extra"), 1);
3572
+ var TemplateCopyTask = class {
3573
+ name = "\u590D\u5236\u9AA8\u67B6\u6A21\u677F";
3574
+ async run(context) {
3575
+ const { projectPath, options, logger: logger2 } = context;
3576
+ const sourcePath = options.templateSource;
3577
+ if (!sourcePath) {
3578
+ throw new Error("\u672A\u6307\u5B9A\u6A21\u677F\u6E90\u8DEF\u5F84");
3579
+ }
3580
+ logger2.info(`\u{1F4DD} \u6B63\u5728\u4ECE ${sourcePath} \u5B9E\u4F8B\u5316\u9879\u76EE...`);
3581
+ const success = await copyTemplate(sourcePath, projectPath, {
3582
+ excludes: ["lib/core"]
3583
+ });
3584
+ if (!success) {
3585
+ throw new Error("\u6A21\u677F\u6587\u4EF6\u590D\u5236\u5931\u8D25");
3586
+ }
3587
+ const scriptPath = (0, import_path18.join)(projectPath, "scripts");
3588
+ if (await import_fs_extra6.default.pathExists(scriptPath)) {
3589
+ await import_fs_extra6.default.remove(scriptPath);
3590
+ logger2.info(" \u2713 \u5DF2\u79FB\u9664\u6A21\u677F\u521D\u59CB\u5316\u811A\u672C");
3591
+ }
3592
+ await cleanupTemplateFiles(projectPath);
3593
+ logger2.info(" \u2713 \u9AA8\u67B6\u590D\u5236\u5B8C\u6210");
3594
+ }
3595
+ };
3596
+
3597
+ // src/generators/tasks/variables_replace_task.ts
3598
+ var VariablesReplaceTask = class {
3599
+ name = "\u53D8\u91CF\u81EA\u52A8\u843D\u5730";
3600
+ async run(context) {
3601
+ const { projectPath, projectName, variables, logger: logger2 } = context;
3602
+ logger2.info("\u{1F4DD} \u6B63\u5728\u6267\u884C\u9879\u76EE\u5168\u5C40\u53D8\u91CF\u66FF\u6362...");
3603
+ const smName = (context.options.stateManager || "provider").toLowerCase();
3604
+ const templateType = (context.options.templateType || "lite").toLowerCase();
3605
+ const renderData = {
3606
+ projectName,
3607
+ packageName: context.options.packageName || `com.example.${projectName}`,
3608
+ author: variables.author || "Your Name",
3609
+ year: (/* @__PURE__ */ new Date()).getFullYear().toString(),
3610
+ // 状态管理标识
3611
+ stateManager: smName,
3612
+ if_getx: smName === "getx",
3613
+ if_provider: smName === "provider",
3614
+ if_bloc: smName === "bloc",
3615
+ if_riverpod: smName === "riverpod",
3616
+ // 模板类型标识
3617
+ templateType,
3618
+ if_lite: templateType.includes("lite"),
3619
+ if_modular: templateType.includes("modular"),
3620
+ if_clean: templateType.includes("clean"),
3621
+ // 用于 pubspec.yaml 的依赖注入 (简单处理)
3622
+ state_manager_dependency: this.getStateManagerDependency(smName)
3623
+ };
3624
+ await TemplateRenderer.renderDirectory(projectPath, renderData);
3625
+ await ensurePubspecName(projectPath, projectName);
3626
+ logger2.info(" \u2713 \u53D8\u91CF\u66FF\u6362\u4E0E\u6A21\u677F\u6E32\u67D3\u5B8C\u6210");
3627
+ }
3628
+ getStateManagerDependency(sm) {
3629
+ switch (sm) {
3630
+ case "getx":
3631
+ return "get: ^4.6.6";
3632
+ case "provider":
3633
+ return "provider: ^6.1.1";
3634
+ case "riverpod":
3635
+ return "flutter_riverpod: ^2.4.9";
3636
+ case "bloc":
3637
+ return "flutter_bloc: ^8.1.3";
3638
+ default:
3639
+ return "";
3640
+ }
3641
+ }
3642
+ };
3643
+
3644
+ // src/utils/pubspec_editor.ts
3645
+ var import_path19 = require("path");
3646
+ var import_fs_extra7 = __toESM(require("fs-extra"), 1);
3647
+ var PubspecEditor = class {
3648
+ projectPath;
3649
+ logger;
3650
+ constructor(projectPath, logger2) {
3651
+ this.projectPath = projectPath;
3652
+ this.logger = logger2;
3653
+ }
3654
+ /**
3655
+ * 添加依赖
3656
+ * @param deps 依赖列表,如 ['provider: ^6.0.0']
3657
+ * @param isDev 是否为 dev_dependencies (默认 false)
3658
+ */
3659
+ async addDependencies(deps, isDev = false) {
3660
+ if (deps.length === 0) return;
3661
+ const pubspecPath = (0, import_path19.join)(this.projectPath, "pubspec.yaml");
3662
+ if (!import_fs_extra7.default.existsSync(pubspecPath)) {
3663
+ this.logger?.error(`Pubspec.yaml not found at ${pubspecPath}`);
3664
+ return;
3665
+ }
3666
+ let content = await import_fs_extra7.default.readFile(pubspecPath, "utf8");
3667
+ const lines = content.split("\n");
3668
+ const sectionKey = isDev ? "dev_dependencies:" : "dependencies:";
3669
+ const sectionIndex = lines.findIndex((l) => l.trim() === sectionKey);
3670
+ if (sectionIndex === -1) {
3671
+ this.logger?.warn(`Could not find ${sectionKey} in pubspec.yaml`);
3672
+ return;
3673
+ }
3674
+ let indent = " ";
3675
+ for (let i = sectionIndex + 1; i < lines.length; i++) {
3676
+ const line = lines[i];
3677
+ if (line.trim() && !line.trim().startsWith("#")) {
3678
+ const match = line.match(/^(\s+)/);
3679
+ if (match) {
3680
+ indent = match[1];
3681
+ }
3682
+ break;
3683
+ }
3684
+ if (line.trim() && !line.startsWith(" ")) break;
3685
+ }
3686
+ let lastDepLineIndex = sectionIndex;
3687
+ for (let i = sectionIndex + 1; i < lines.length; i++) {
3688
+ const line = lines[i];
3689
+ if (line.trim() === "" || line.trim().startsWith("#") || line.startsWith(" ") || line.startsWith(" ")) {
3690
+ if (line.trim() !== "") {
3691
+ lastDepLineIndex = i;
3692
+ }
3693
+ continue;
3694
+ }
3695
+ break;
3696
+ }
3697
+ const newLines = [...lines];
3698
+ let insertOffset = 0;
3699
+ for (const dep of deps) {
3700
+ const [name] = dep.split(":");
3701
+ if (content.includes(`${name}:`)) {
3702
+ this.logger?.info(` - \u4F9D\u8D56\u5DF2\u5B58\u5728: ${name}`);
3703
+ continue;
3704
+ }
3705
+ const newLine = `${indent}${dep}`;
3706
+ newLines.splice(lastDepLineIndex + 1 + insertOffset, 0, newLine);
3707
+ insertOffset++;
3708
+ this.logger?.info(` + \u6DFB\u52A0\u4F9D\u8D56: ${name} (to ${sectionKey})`);
3709
+ }
3710
+ await import_fs_extra7.default.writeFile(pubspecPath, newLines.join("\n"));
3711
+ }
3712
+ };
3713
+
3714
+ // src/generators/tasks/core_enrich_task.ts
3715
+ var import_path20 = require("path");
3716
+ var import_fs_extra8 = __toESM(require("fs-extra"), 1);
3717
+ var CoreEnrichTask = class {
3718
+ name = "\u6838\u5FC3\u6A21\u5757\u751F\u6210";
3719
+ async run(context) {
3720
+ const { projectPath, logger: logger2, templateType, includeNetworkLayer } = context;
3721
+ logger2.info("\u{1F3D7}\uFE0F \u6B63\u5728\u6784\u5EFA\u6838\u5FC3\u6A21\u5757 (Core Module)...");
3722
+ logger2.info(`[DEBUG] Network: ${includeNetworkLayer}`);
3723
+ logger2.info(`[DEBUG] Template Type: ${templateType}`);
3724
+ logger2.info(`[DEBUG] State Manager: ${context.stateManager}`);
3725
+ await copyCoreFiles(projectPath, {
3726
+ includeNetworkLayer,
3727
+ templateType: (templateType || "lite").toLowerCase(),
3728
+ projectName: context.projectName,
3729
+ stateManager: context.stateManager
3730
+ });
3731
+ const baseDeps = [
3732
+ "shared_preferences: ^2.2.2"
3733
+ ];
3734
+ if (includeNetworkLayer) {
3735
+ baseDeps.push("dio: ^5.4.0");
3736
+ }
3737
+ const editor = new PubspecEditor(projectPath, logger2);
3738
+ await editor.addDependencies(baseDeps);
3739
+ const templateTypeLower = (templateType || "lite").toLowerCase();
3740
+ let pagesPath = "lib/pages";
3741
+ let vmsPath = "lib/viewmodels";
3742
+ let servicesPath = "lib/services";
3743
+ let modelsPath = "lib/models";
3744
+ if (templateTypeLower.includes("modular")) {
3745
+ pagesPath = "lib/features/home/pages";
3746
+ vmsPath = "lib/features/home/viewmodels";
3747
+ servicesPath = "lib/features/home/services";
3748
+ modelsPath = "lib/features/home/models";
3749
+ } else if (templateTypeLower.includes("clean")) {
3750
+ pagesPath = "lib/features/home/presentation/pages";
3751
+ vmsPath = "lib/features/home/presentation/viewmodels";
3752
+ servicesPath = "lib/features/home/data/datasources";
3753
+ modelsPath = "lib/features/home/data/models";
3754
+ }
3755
+ await generateIndexFile((0, import_path20.join)(projectPath, pagesPath));
3756
+ await generateIndexFile((0, import_path20.join)(projectPath, vmsPath));
3757
+ await generateIndexFile((0, import_path20.join)(projectPath, servicesPath));
3758
+ await generateIndexFile((0, import_path20.join)(projectPath, modelsPath));
3759
+ if (templateTypeLower.includes("clean")) {
3760
+ await generateIndexFile((0, import_path20.join)(projectPath, "lib/features/home/data"));
3761
+ await generateIndexFile((0, import_path20.join)(projectPath, "lib/features/home/presentation"));
3762
+ }
3763
+ if (includeNetworkLayer) {
3764
+ logger2.info("\u{1F4E1} \u6B63\u5728\u6CE8\u5165\u7F51\u7EDC\u5C42\u4E0E\u6F14\u793A\u4EE3\u7801...");
3765
+ await injectNetworkExamples(projectPath, {
3766
+ projectName: context.projectName,
3767
+ stateManager: context.stateManager,
3768
+ templateType: templateTypeLower
3769
+ // 传递小写的模板类型
3770
+ });
3771
+ logger2.info(" \u2713 \u7F51\u7EDC\u5C42\u4E0E\u6F14\u793A\u4EE3\u7801\u6CE8\u5165\u5B8C\u6210");
3772
+ } else {
3773
+ logger2.info(" \u2713 \u6838\u5FC3\u6A21\u5757\u751F\u6210\u5B8C\u6210 (\u4E0D\u542B\u7F51\u7EDC\u5C42)");
3774
+ }
3775
+ await this.copySnippets(projectPath, logger2);
3776
+ }
3777
+ /**
3778
+ * 复制 VSCode 代码片段文件到项目
3779
+ */
3780
+ async copySnippets(projectPath, logger2) {
3781
+ try {
3782
+ const sourceFile = (0, import_path20.join)(getCoreFilesDir(), "../snippets/flu-cli.code-snippets");
3783
+ const targetDir = (0, import_path20.join)(projectPath, ".vscode");
3784
+ const targetFile = (0, import_path20.join)(targetDir, "flu-cli.code-snippets");
3785
+ const oldFile = (0, import_path20.join)(targetDir, "dart.code-snippets");
3786
+ await import_fs_extra8.default.ensureDir(targetDir);
3787
+ if (await import_fs_extra8.default.pathExists(sourceFile)) {
3788
+ await import_fs_extra8.default.copyFile(sourceFile, targetFile);
3789
+ logger2.info(" \u2713 VSCode \u4EE3\u7801\u7247\u6BB5\u5DF2\u590D\u5236");
3790
+ if (await import_fs_extra8.default.pathExists(oldFile)) {
3791
+ await import_fs_extra8.default.remove(oldFile);
3792
+ logger2.info(" \u2713 \u5DF2\u6E05\u7406\u65E7\u7684\u4EE3\u7801\u7247\u6BB5\u6587\u4EF6");
3793
+ }
3794
+ } else {
3795
+ logger2.warn(" \u26A0 \u4EE3\u7801\u7247\u6BB5\u6587\u4EF6\u672A\u627E\u5230,\u8DF3\u8FC7\u590D\u5236");
3796
+ }
3797
+ } catch (error) {
3798
+ logger2.warn(` \u26A0 \u590D\u5236\u4EE3\u7801\u7247\u6BB5\u5931\u8D25: ${error.message}`);
3799
+ }
3800
+ }
3801
+ };
3802
+
3803
+ // src/utils/flutterHelper.ts
3804
+ var import_child_process = require("child_process");
3805
+ var import_util = require("util");
3806
+ init_logger();
3807
+ var execAsync = (0, import_util.promisify)(import_child_process.exec);
3808
+ async function runFlutterCreate(projectDir, projectName, packageName, flutterTemplate = "app") {
3809
+ try {
3810
+ const command = `flutter create --project-name ${projectName} --org ${packageName} --template ${flutterTemplate} ${projectDir}`;
3811
+ const { stdout, stderr } = await execAsync(command);
3812
+ if (stderr && !stderr.includes("Warning")) {
3813
+ logger.warn(stderr);
3814
+ }
3815
+ return stdout.includes("All done!") || stdout.includes("Created project");
3816
+ } catch (error) {
3817
+ logger.error(`Flutter create \u5931\u8D25: ${error.message}`);
3818
+ return false;
3819
+ }
3820
+ }
3821
+ async function runFlutterPubGet(projectDir) {
3822
+ try {
3823
+ const { stdout, stderr } = await execAsync("flutter pub get", {
3824
+ cwd: projectDir
3825
+ });
3826
+ if (stderr) {
3827
+ logger.warn(stderr);
3828
+ }
3829
+ return true;
3830
+ } catch (error) {
3831
+ logger.error(`Flutter pub get \u5931\u8D25: ${error.message}`);
3832
+ return false;
3833
+ }
3834
+ }
3835
+ async function checkFlutterInstalled() {
3836
+ try {
3837
+ await execAsync("flutter --version");
3838
+ return true;
3839
+ } catch (error) {
3840
+ return false;
3841
+ }
3842
+ }
3843
+ async function getFlutterVersion() {
3844
+ try {
3845
+ const { stdout } = await execAsync("flutter --version");
3846
+ const match = stdout.match(/Flutter (\d+\.\d+\.\d+)/);
3847
+ return match ? match[1] : null;
3848
+ } catch (error) {
3849
+ return null;
3850
+ }
3851
+ }
3852
+
3853
+ // src/generators/tasks/flutter_init_task.ts
3854
+ var FlutterInitTask = class {
3855
+ name = "\u539F\u751F Flutter \u521D\u59CB\u5316";
3856
+ async run(context) {
3857
+ const { projectPath, projectName, options, logger: logger2 } = context;
3858
+ if (!options.createFlutterProject && context.templateType !== "native") {
3859
+ return;
3860
+ }
3861
+ logger2.info(`\u{1F528} \u6B63\u5728\u521D\u59CB\u5316\u539F\u751F Flutter \u73AF\u5883 (${options.flutterTemplate || "app"})...`);
3862
+ const org = options.packageName ? options.packageName.split(".").slice(0, -1).join(".") : "com.example";
3863
+ const success = await runFlutterCreate(
3864
+ projectPath,
3865
+ projectName,
3866
+ org,
3867
+ options.flutterTemplate || "app"
3868
+ );
3869
+ if (!success) {
3870
+ throw new Error("flutter create \u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 Flutter SDK \u73AF\u5883");
3871
+ }
3872
+ logger2.info(" \u2713 Native Flutter \u9879\u76EE\u521D\u59CB\u5316\u6210\u529F");
3873
+ }
3874
+ };
3875
+
3876
+ // src/generators/tasks/route_mapping_task.ts
3877
+ var import_path21 = require("path");
3878
+ var import_fs_extra9 = __toESM(require("fs-extra"), 1);
3879
+ var RouteMappingTask = class {
3880
+ name = "\u4E1A\u52A1\u8DEF\u7531\u81EA\u52A8\u6CE8\u518C";
3881
+ async run(context) {
3882
+ const { projectPath, templateType, logger: logger2, stateManager } = context;
3883
+ try {
3884
+ const routesPath = (0, import_path21.join)(projectPath, "lib", "core", "router", "app_routes.dart");
3885
+ if (!await import_fs_extra9.default.pathExists(routesPath)) return;
3886
+ let content = await import_fs_extra9.default.readFile(routesPath, "utf8");
3887
+ const todoMarker = stateManager === "getx" ? "// --- \u5728\u6B64\u4E0B\u65B9\u6DFB\u52A0\u60A8\u7684\u81EA\u5B9A\u4E49\u8DEF\u7531 ---" : "// --- \u5728\u6B64\u4E0B\u65B9\u6DFB\u52A0\u60A8\u7684\u81EA\u5B9A\u4E49\u8DEF\u7531 ---";
3888
+ if (!content.includes(todoMarker)) {
3889
+ return;
3890
+ }
3891
+ const pages = await this.scanGeneratedPages(projectPath, templateType);
3892
+ if (pages.length === 0) return;
3893
+ let routeMappings = [];
3894
+ if (stateManager === "getx") {
3895
+ routeMappings = pages.filter((page) => !content.includes(`GetPage(name: ${page.routeName},`)).map(
3896
+ (page) => `GetPage(name: ${page.routeName}, page: () => const ${page.className}()),`
3897
+ );
3898
+ } else {
3899
+ routeMappings = pages.filter((page) => !content.includes(`${page.routeName}:`)).map(
3900
+ (page) => `${page.routeName}: (context) => const ${page.className}(),`
3901
+ );
3902
+ }
3903
+ const injection = routeMappings.join("\n ") + "\n " + todoMarker;
3904
+ content = content.replace(todoMarker, injection);
3905
+ await import_fs_extra9.default.writeFile(routesPath, content);
3906
+ logger2.info(` \u2713 \u5DF2\u81EA\u52A8\u6CE8\u518C ${pages.length} \u4E2A\u4E1A\u52A1\u8DEF\u7531`);
3907
+ } catch (e) {
3908
+ logger2.warn(` \u26A0\uFE0F \u8DEF\u7531\u81EA\u52A8\u6CE8\u518C\u8DF3\u8FC7: ${e.message}`);
3909
+ }
3910
+ }
3911
+ async scanGeneratedPages(projectPath, templateType) {
3912
+ const pages = [];
3913
+ const searchPaths = [
3914
+ (0, import_path21.join)(projectPath, "lib", "pages"),
3915
+ // Lite
3916
+ (0, import_path21.join)(projectPath, "lib", "features", "home", "pages"),
3917
+ // Modular
3918
+ (0, import_path21.join)(projectPath, "lib", "features", "home", "presentation", "pages"),
3919
+ // Clean
3920
+ (0, import_path21.join)(projectPath, "lib", "features", "user", "presentation", "pages")
3921
+ // Clean
3922
+ ];
3923
+ for (const dir of searchPaths) {
3924
+ if (!await import_fs_extra9.default.pathExists(dir)) continue;
3925
+ const files = await import_fs_extra9.default.readdir(dir);
3926
+ for (const file of files) {
3927
+ if (file.endsWith("_page.dart") && file !== "index.dart" && file !== "home_page.dart" && file !== "splash_page.dart") {
3928
+ const baseName = file.replace("_page.dart", "");
3929
+ const className = toPascalCase(baseName) + "Page";
3930
+ const routeName = baseName.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
3931
+ pages.push({ className, routeName });
3932
+ }
3933
+ }
3934
+ }
3935
+ return pages;
3936
+ }
3937
+ };
3938
+
3939
+ // src/generators/tasks/cleanup_task.ts
3940
+ var import_path22 = require("path");
3941
+ var import_fs_extra10 = __toESM(require("fs-extra"), 1);
3942
+ var CleanupTask = class {
3943
+ name = "\u9879\u76EE\u73AF\u5883\u4FEE\u590D\u4E0E\u6E05\u7406";
3944
+ async run(context) {
3945
+ const { projectPath, logger: logger2, projectName } = context;
3946
+ logger2.info("\u{1F9F9} \u6B63\u5728\u6267\u884C\u6700\u540E\u7684\u517C\u5BB9\u6027\u68C0\u67E5\u4E0E\u6E05\u7406...");
3947
+ await this.patchMacOSEntitlements(projectPath, logger2);
3948
+ await this.fixTestFile(projectPath, projectName, logger2);
3949
+ await this.fixLegacyImports(projectPath, logger2);
3950
+ logger2.info(" \u2713 \u73AF\u5883\u4FEE\u590D\u5B8C\u6210");
3951
+ }
3952
+ /**
3953
+ * 自动修复 macOS 网络权限 (避免 SocketException)
3954
+ */
3955
+ async patchMacOSEntitlements(projectPath, logger2) {
3956
+ try {
3957
+ const entitlementsDir = (0, import_path22.join)(projectPath, "macos", "Runner");
3958
+ if (!await import_fs_extra10.default.pathExists(entitlementsDir)) return;
3959
+ const debugFile = (0, import_path22.join)(entitlementsDir, "DebugProfile.entitlements");
3960
+ const releaseFile = (0, import_path22.join)(entitlementsDir, "Release.entitlements");
3961
+ const entitlementKey = "com.apple.security.network.client";
3962
+ const patch = async (filePath) => {
3963
+ if (await import_fs_extra10.default.pathExists(filePath)) {
3964
+ let content = await import_fs_extra10.default.readFile(filePath, "utf8");
3965
+ if (!content.includes(entitlementKey)) {
3966
+ const entitlementStr = `
3967
+ <key>${entitlementKey}</key>
3968
+ <true/>`;
3969
+ content = content.replace("</dict>", `${entitlementStr}
3970
+ </dict>`);
3971
+ await import_fs_extra10.default.writeFile(filePath, content, "utf8");
3972
+ }
3973
+ }
3974
+ };
3975
+ await patch(debugFile);
3976
+ await patch(releaseFile);
3977
+ logger2.info(" \u2713 \u5DF2\u6CE8\u5165 macOS \u7F51\u7EDC\u8BBF\u95EE\u6743\u9650");
3978
+ } catch (e) {
3979
+ }
3980
+ }
3981
+ /**
3982
+ * 修复默认测试文件中的导入路径
3983
+ */
3984
+ async fixTestFile(projectDir, projectName, logger2) {
3985
+ const testFile = (0, import_path22.join)(projectDir, "test", "widget_test.dart");
3986
+ if (!await import_fs_extra10.default.pathExists(testFile)) return;
3987
+ try {
3988
+ let content = await import_fs_extra10.default.readFile(testFile, "utf8");
3989
+ if (content.includes("Counter increments smoke test")) {
3990
+ content = `import 'package:flutter_test/flutter_test.dart';
3991
+ import 'package:${projectName}/app.dart';
3992
+
3993
+ void main() {
3994
+ testWidgets('App smoke test', (WidgetTester tester) async {
3995
+ await tester.pumpWidget(const App());
3996
+ expect(find.byType(App), findsOneWidget);
3997
+ });
3998
+ }
3999
+ `;
4000
+ await import_fs_extra10.default.writeFile(testFile, content, "utf8");
4001
+ }
4002
+ } catch {
4003
+ }
4004
+ }
4005
+ /**
4006
+ * 修复残留的旧路径引用
4007
+ */
4008
+ async fixLegacyImports(projectPath, logger2) {
4009
+ const libPath = (0, import_path22.join)(projectPath, "lib");
4010
+ if (!await import_fs_extra10.default.pathExists(libPath)) return;
4011
+ }
4012
+ };
4013
+
4014
+ // src/generators/tasks/state_manager_enrich_task.ts
4015
+ init_factory();
4016
+ var import_path23 = require("path");
4017
+ var import_fs_extra11 = __toESM(require("fs-extra"), 1);
4018
+ var StateManagerEnrichTask = class {
4019
+ name = "\u72B6\u6001\u7BA1\u7406\u67B6\u6784\u589E\u5F3A";
4020
+ async run(context) {
4021
+ const { projectPath, options, logger: logger2 } = context;
4022
+ const smName = options.stateManager || "default";
4023
+ logger2.info(`\u{1F9EA} \u6B63\u5728\u6CE8\u5165 ${smName} \u72B6\u6001\u7BA1\u7406\u9002\u914D\u5668...`);
4024
+ const adapter = StateManagerAdapterFactory.getAdapter(smName);
4025
+ try {
4026
+ await this.enrichDependencies(projectPath, adapter.getDependencies(), logger2);
4027
+ const baseVM = adapter.getBaseViewModelTemplate(context);
4028
+ if (baseVM !== null && baseVM !== "") {
4029
+ await this.enrichBaseViewModel(projectPath, adapter, baseVM, logger2);
4030
+ }
4031
+ if (adapter.onEnrich) {
4032
+ await adapter.onEnrich(context);
4033
+ }
4034
+ await this.enrichAppEntry(projectPath, adapter, context, logger2);
4035
+ await this.enrichRoutes(projectPath, adapter, context, logger2);
4036
+ logger2.info(` \u2713 \u72B6\u6001\u7BA1\u7406\u589E\u5F3A\u5B8C\u6210 (${adapter.name})`);
4037
+ } catch (e) {
4038
+ logger2.error(` \u274C \u72B6\u6001\u7BA1\u7406\u589E\u5F3A\u5931\u8D25: ${e.message}`);
4039
+ throw e;
4040
+ }
4041
+ }
4042
+ async enrichDependencies(projectPath, deps, logger2) {
4043
+ const editor = new PubspecEditor(projectPath, logger2);
4044
+ await editor.addDependencies(deps);
4045
+ }
4046
+ async enrichBaseViewModel(projectPath, adapter, content, logger2) {
4047
+ const baseDir = (0, import_path23.join)(projectPath, "lib", "core", "base");
4048
+ await import_fs_extra11.default.ensureDir(baseDir);
4049
+ const target = (0, import_path23.join)(baseDir, "base_viewmodel.dart");
4050
+ await import_fs_extra11.default.writeFile(target, content);
4051
+ logger2.info(` + \u751F\u6210 BaseViewModel (\u9002\u914D ${adapter.name})`);
4052
+ }
4053
+ async enrichAppEntry(projectPath, adapter, context, logger2) {
4054
+ const possiblePaths = [
4055
+ (0, import_path23.join)(projectPath, "lib", "app.dart"),
4056
+ (0, import_path23.join)(projectPath, "lib", "main.dart")
4057
+ ];
4058
+ for (const path of possiblePaths) {
4059
+ if (await import_fs_extra11.default.pathExists(path)) {
4060
+ let content = await import_fs_extra11.default.readFile(path, "utf8");
4061
+ const patched = adapter.patchAppEntry(content, context);
4062
+ if (patched !== content) {
4063
+ await import_fs_extra11.default.writeFile(path, patched);
4064
+ logger2.info(` + \u5DF2\u589E\u5F3A App \u5165\u53E3: ${path.split("/").pop()}`);
4065
+ }
4066
+ }
4067
+ }
4068
+ }
4069
+ async enrichRoutes(projectPath, adapter, context, logger2) {
4070
+ if (!adapter.patchRoutes) return;
4071
+ const routesPath = (0, import_path23.join)(projectPath, "lib", "core", "router", "app_routes.dart");
4072
+ if (await import_fs_extra11.default.pathExists(routesPath)) {
4073
+ let content = await import_fs_extra11.default.readFile(routesPath, "utf8");
4074
+ const patched = adapter.patchRoutes(content, context);
4075
+ if (patched !== content) {
4076
+ await import_fs_extra11.default.writeFile(routesPath, patched);
4077
+ logger2.info(` + \u5DF2\u589E\u5F3A\u8DEF\u7531\u914D\u7F6E`);
4078
+ }
4079
+ }
4080
+ }
4081
+ };
4082
+
4083
+ // src/generators/tasks/project_config_task.ts
4084
+ var import_path24 = require("path");
4085
+ var import_fs_extra12 = __toESM(require("fs-extra"), 1);
4086
+ var ProjectConfigTask = class {
4087
+ name = "\u751F\u6210\u9879\u76EE\u914D\u7F6E";
4088
+ async run(context) {
4089
+ const { projectPath, templateType, options, logger: logger2 } = context;
4090
+ const templateSource = options.templateSource;
4091
+ logger2.info("\u2699\uFE0F \u6B63\u5728\u521D\u59CB\u5316\u9879\u76EE\u914D\u7F6E...");
4092
+ let baseConfig = {};
4093
+ if (templateSource) {
4094
+ const templatePaths = [
4095
+ (0, import_path24.join)(templateSource, ".flu-template.json"),
4096
+ (0, import_path24.join)(templateSource, ".flu-cli.json")
4097
+ ];
4098
+ for (const configPath of templatePaths) {
4099
+ if (await import_fs_extra12.default.pathExists(configPath)) {
4100
+ try {
4101
+ const content = await import_fs_extra12.default.readFile(configPath, "utf8");
4102
+ baseConfig = JSON.parse(content);
4103
+ logger2.info(` \u2713 \u4ECE\u6A21\u677F\u4E2D\u52A0\u8F7D\u4E86\u81EA\u63CF\u8FF0\u914D\u7F6E: ${configPath.split("/").pop()}`);
4104
+ break;
4105
+ } catch (e) {
4106
+ logger2.warn(` \u26A0\uFE0F \u8BFB\u53D6\u6A21\u677F\u914D\u7F6E\u5931\u8D25: ${configPath}`);
4107
+ }
4108
+ }
4109
+ }
4110
+ }
4111
+ if (!baseConfig.generators) {
4112
+ baseConfig = ProjectConfigManager.getDefaultConfigTemplate(templateType);
4113
+ logger2.info(` \u2713 \u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E\u65B9\u6848: ${templateType}`);
4114
+ }
4115
+ const finalConfig = {
4116
+ ...baseConfig,
4117
+ template: templateType,
4118
+ packageName: options.packageName || baseConfig.packageName
4119
+ };
4120
+ const targetConfigPath = (0, import_path24.join)(projectPath, ".flu-cli.json");
4121
+ await import_fs_extra12.default.writeJson(targetConfigPath, finalConfig, { spaces: 2 });
4122
+ logger2.info(" \u2713 \u9879\u76EE\u914D\u7F6E\u6587\u4EF6\u5DF2\u751F\u6210");
4123
+ }
4124
+ };
4125
+
4126
+ // src/generators/tasks/network_example_injection_task.ts
4127
+ var import_path25 = require("path");
4128
+ var import_fs_extra13 = __toESM(require("fs-extra"), 1);
4129
+ var NetworkExampleInjectionTask = class {
4130
+ name = "\u7F51\u7EDC\u793A\u4F8B\u4EE3\u7801\u6CE8\u5165";
4131
+ async run(context) {
4132
+ const { projectPath, templateType, includeNetworkLayer, logger: logger2 } = context;
4133
+ if (!includeNetworkLayer) {
4134
+ logger2.info(" \u23E9 \u8DF3\u8FC7\u7F51\u7EDC\u793A\u4F8B\u6CE8\u5165");
4135
+ return;
4136
+ }
4137
+ const templateTypeLower = (templateType || "lite").toLowerCase();
4138
+ try {
4139
+ await this.injectGalleryCard(projectPath, templateTypeLower, logger2);
4140
+ await this.injectRoutes(projectPath, templateTypeLower, logger2);
4141
+ logger2.info(" \u2713 \u7F51\u7EDC\u793A\u4F8B\u4EE3\u7801\u6CE8\u5165\u5B8C\u6210");
4142
+ } catch (error) {
4143
+ logger2.warn(` \u26A0 \u7F51\u7EDC\u793A\u4F8B\u6CE8\u5165\u5931\u8D25: ${error}`);
4144
+ }
4145
+ }
4146
+ /**
4147
+ * 注入图库卡片到 HomePage
4148
+ */
4149
+ async injectGalleryCard(projectPath, templateType, logger2) {
4150
+ const homePagePath = this.getHomePagePath(projectPath, templateType);
4151
+ if (!await import_fs_extra13.default.pathExists(homePagePath)) {
4152
+ return;
4153
+ }
4154
+ let content = await import_fs_extra13.default.readFile(homePagePath, "utf8");
4155
+ if (content.includes("_buildGalleryCard()")) {
4156
+ logger2.info(" \u2139 \u56FE\u5E93\u5361\u7247\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u6CE8\u5165");
4157
+ return;
4158
+ }
4159
+ const anchorRegex = /\/\/ 图库入口卡片 \(网络示例\) - 由 CLI 动态注入\s*/;
4160
+ if (!anchorRegex.test(content)) {
4161
+ logger2.warn(" \u26A0 \u672A\u627E\u5230\u56FE\u5E93\u5361\u7247\u6CE8\u5165\u951A\u70B9");
4162
+ return;
4163
+ }
4164
+ content = content.replace(
4165
+ anchorRegex,
4166
+ `_buildGalleryCard(),
4167
+
4168
+ `
4169
+ );
4170
+ const galleryCardMethod = `
4171
+ /// \u6784\u5EFA\u56FE\u5E93\u5165\u53E3\u5361\u7247
4172
+ Widget _buildGalleryCard() {
4173
+ return _buildEntryCard(
4174
+ title: '\u7CBE\u7F8E\u56FE\u5E93',
4175
+ subtitle: '\u5206\u9875\u52A0\u8F7D\u4E0E\u7F51\u7EDC\u8BF7\u6C42\u793A\u4F8B',
4176
+ icon: Icons.photo_library,
4177
+ color: const Color(0xFF667eea),
4178
+ onTap: () => NavigatorUtil.pushNamed(AppRoutes.egList),
4179
+ );
4180
+ }
4181
+ `;
4182
+ const lastBraceIndex = content.lastIndexOf("}");
4183
+ content = content.slice(0, lastBraceIndex) + galleryCardMethod + "\n" + content.slice(lastBraceIndex);
4184
+ await import_fs_extra13.default.writeFile(homePagePath, content, "utf8");
4185
+ logger2.info(" \u2713 \u56FE\u5E93\u5361\u7247\u5DF2\u6CE8\u5165\u5230 HomePage");
4186
+ }
4187
+ /**
4188
+ * 注入路由到 AppRoutes
4189
+ */
4190
+ async injectRoutes(projectPath, templateType, logger2) {
4191
+ const routesPath = (0, import_path25.join)(projectPath, "lib", "core", "router", "app_routes.dart");
4192
+ if (!await import_fs_extra13.default.pathExists(routesPath)) {
4193
+ return;
4194
+ }
4195
+ let content = await import_fs_extra13.default.readFile(routesPath, "utf8");
4196
+ if (content.includes("egList = '/eg-list'")) {
4197
+ logger2.info(" \u2139 \u7F51\u7EDC\u793A\u4F8B\u8DEF\u7531\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u6CE8\u5165");
4198
+ return;
4199
+ }
4200
+ const constantAnchor = /\/\/ 网络示例路由 - 由 CLI 动态注入\s*/;
4201
+ if (constantAnchor.test(content)) {
4202
+ content = content.replace(
4203
+ constantAnchor,
4204
+ "static const String egList = '/eg-list';\n "
4205
+ );
4206
+ }
4207
+ const mappingAnchor = /\/\/ 网络示例路由映射 - 由 CLI 动态注入\s*/;
4208
+ if (mappingAnchor.test(content)) {
4209
+ content = content.replace(
4210
+ mappingAnchor,
4211
+ "egList: (context) => const EgListPage(),\n "
4212
+ );
4213
+ }
4214
+ await import_fs_extra13.default.writeFile(routesPath, content, "utf8");
4215
+ logger2.info(" \u2713 \u7F51\u7EDC\u793A\u4F8B\u8DEF\u7531\u5DF2\u6CE8\u5165\u5230 AppRoutes");
4216
+ }
4217
+ /**
4218
+ * 获取 HomePage 路径(适配不同模板结构)
4219
+ */
4220
+ getHomePagePath(projectPath, templateType) {
4221
+ if (templateType.includes("lite")) {
4222
+ return (0, import_path25.join)(projectPath, "lib", "pages", "home_page.dart");
4223
+ } else if (templateType.includes("modular")) {
4224
+ return (0, import_path25.join)(projectPath, "lib", "features", "home", "pages", "home_page.dart");
4225
+ } else {
4226
+ return (0, import_path25.join)(projectPath, "lib", "features", "home", "presentation", "pages", "home_page.dart");
4227
+ }
4228
+ }
4229
+ };
4230
+
4231
+ // src/generators/project_generator.ts
4232
+ var ProjectGenerator = class {
4233
+ /**
4234
+ * 生成项目
4235
+ * @param {string} name 项目名称
4236
+ * @param {object} options 选项
4237
+ * @param {Logger} logger 日志工具
4238
+ */
4239
+ async generate(name, options = {}, logger2 = new ConsoleLogger()) {
4240
+ const templateType = options.templateType || options.template || "lite";
4241
+ const {
4242
+ outputDir = process.cwd()
4243
+ } = options;
4244
+ const projectPath = (0, import_path26.join)(outputDir, name);
4245
+ if (this.checkProjectExists(projectPath)) {
4246
+ logger2.error(`\u9879\u76EE\u5DF2\u5B58\u5728: ${projectPath}`);
4247
+ return false;
4248
+ }
4249
+ try {
4250
+ const templateManager = TemplateManager.getInstance();
4251
+ let templateName = typeof templateType === "string" ? templateType : "custom";
4252
+ if (templateName.includes("/") || templateName.includes("\\")) {
4253
+ const parts = templateName.split(/[/\\]/);
4254
+ const lastPart = parts[parts.length - 1];
4255
+ templateName = lastPart.replace(/^template_/, "");
4256
+ }
4257
+ const templateSource = await templateManager.prepareTemplate(
4258
+ templateName,
4259
+ logger2
4260
+ );
4261
+ const pipeline = new ProjectPipeline();
4262
+ const context = createDefaultContext(projectPath, name, options, logger2);
4263
+ context.options.templateSource = templateSource;
4264
+ pipeline.addTask(new FlutterInitTask()).addTask(new TemplateCopyTask()).addTask(new ProjectConfigTask()).addTask(new CoreEnrichTask()).addTask(new VariablesReplaceTask()).addTask(new StateManagerEnrichTask()).addTask(new RouteMappingTask()).addTask(new NetworkExampleInjectionTask()).addTask(new CleanupTask());
4265
+ const success = await pipeline.execute(context);
4266
+ if (success) {
4267
+ logger2.newLine();
4268
+ logger2.success(`\u2705 \u9879\u76EE ${name} \u5B9E\u4F8B\u5316\u6210\u529F\uFF01`);
4269
+ logger2.info(`\u4E0B\u4E00\u6B65:`);
4270
+ logger2.info(` cd ${name}`);
4271
+ logger2.info(` flutter pub get`);
4272
+ logger2.info(` flutter run`);
4273
+ }
4274
+ return success;
4275
+ } catch (error) {
4276
+ logger2.error(`\u274C \u521B\u5EFA\u9879\u76EE\u8FC7\u7A0B\u4E2D\u53D1\u751F\u81F4\u547D\u9519\u8BEF: ${error.message}`);
4277
+ return false;
4278
+ }
4279
+ }
4280
+ /**
4281
+ * 检查项目目录是否存在
4282
+ */
4283
+ checkProjectExists(projectPath) {
4284
+ return (0, import_fs14.existsSync)(projectPath);
4285
+ }
4286
+ };
4287
+
4288
+ // src/utils/app_assets_manager.ts
4289
+ var import_fs_extra14 = __toESM(require("fs-extra"), 1);
4290
+ var import_path27 = require("path");
4291
+ var import_fs15 = require("fs");
4292
+ var import_child_process2 = require("child_process");
4293
+ var import_util2 = require("util");
4294
+ var execPromise = (0, import_util2.promisify)(import_child_process2.exec);
4295
+ var AppAssetsManager = class {
4296
+ /**
4297
+ * 设置应用资源
4298
+ * @param projectPath 项目路径
4299
+ * @param assets 资源配置
4300
+ * @param logger 日志工具
4301
+ */
4302
+ async setupAppAssets(projectPath, assets, logger2) {
4303
+ try {
4304
+ const assetsDir = (0, import_path27.join)(projectPath, "assets");
4305
+ await import_fs_extra14.default.ensureDir(assetsDir);
4306
+ const devDeps = [];
4307
+ let pubspecAdditions = "";
4308
+ if (assets.appIcon) {
4309
+ logger2.info("\u{1F3A8} \u8BBE\u7F6E\u5E94\u7528\u56FE\u6807...");
4310
+ const iconDir = (0, import_path27.join)(assetsDir, "icon");
4311
+ await import_fs_extra14.default.ensureDir(iconDir);
4312
+ await this.copyIfDifferent(
4313
+ assets.appIcon,
4314
+ (0, import_path27.join)(iconDir, "app_icon.png")
4315
+ );
4316
+ devDeps.push("flutter_launcher_icons: ^0.14.4");
4317
+ pubspecAdditions += `
4318
+ flutter_launcher_icons:
4319
+ android: true
4320
+ ios: true
4321
+ image_path: "assets/icon/app_icon.png"
4322
+ min_sdk_android: 21
4323
+ web:
4324
+ generate: true
4325
+ image_path: "assets/icon/app_icon.png"
4326
+ windows:
4327
+ generate: true
4328
+ image_path: "assets/icon/app_icon.png"
4329
+ macos:
4330
+ generate: true
4331
+ image_path: "assets/icon/app_icon.png"
4332
+ `;
4333
+ }
4334
+ if (assets.splashLogo) {
4335
+ logger2.info("\u{1F680} \u8BBE\u7F6E\u542F\u52A8\u56FE...");
4336
+ const splashDir = (0, import_path27.join)(assetsDir, "splash");
4337
+ await import_fs_extra14.default.ensureDir(splashDir);
4338
+ await this.copyIfDifferent(
4339
+ assets.splashLogo,
4340
+ (0, import_path27.join)(splashDir, "logo.png")
4341
+ );
4342
+ let splashConfig = `
4343
+ flutter_native_splash:
4344
+ image: "assets/splash/logo.png"
4345
+ android: true
4346
+ ios: true
4347
+ web: true
4348
+ `;
4349
+ if (assets.splashBackgroundColor) {
4350
+ splashConfig += ` color: "${assets.splashBackgroundColor}"
4351
+ `;
4352
+ }
4353
+ if (assets.splashBackground) {
4354
+ await this.copyIfDifferent(
4355
+ assets.splashBackground,
4356
+ (0, import_path27.join)(splashDir, "background.png")
4357
+ );
4358
+ splashConfig += ` background_image: "assets/splash/background.png"
4359
+ `;
4360
+ }
4361
+ if (assets.enableDarkMode) {
4362
+ const darkColor = assets.splashBackgroundColorDark || "#000000";
4363
+ splashConfig += ` color_dark: "${darkColor}"
4364
+ `;
4365
+ if (assets.splashLogoDark) {
4366
+ await this.copyIfDifferent(
4367
+ assets.splashLogoDark,
4368
+ (0, import_path27.join)(splashDir, "logo_dark.png")
4369
+ );
4370
+ splashConfig += ` image_dark: "assets/splash/logo_dark.png"
4371
+ `;
4372
+ } else {
4373
+ splashConfig += ` image_dark: "assets/splash/logo.png"
4374
+ `;
4375
+ }
4376
+ if (assets.splashBackgroundDark) {
4377
+ await this.copyIfDifferent(
4378
+ assets.splashBackgroundDark,
4379
+ (0, import_path27.join)(splashDir, "background_dark.png")
4380
+ );
4381
+ splashConfig += ` background_image_dark: "assets/splash/background_dark.png"
4382
+ `;
4383
+ }
4384
+ splashConfig += `
4385
+ android_12:
4386
+ image: "assets/splash/logo.png"
4387
+ color: "${assets.splashBackgroundColor || "#FFFFFF"}"
4388
+ `;
4389
+ if (assets.splashBackground) {
4390
+ splashConfig += ` background_image: "assets/splash/background.png"
4391
+ `;
4392
+ }
4393
+ }
4394
+ devDeps.push("flutter_native_splash: ^2.3.10");
4395
+ pubspecAdditions += splashConfig;
4396
+ }
4397
+ if (devDeps.length > 0 || pubspecAdditions) {
4398
+ await this.updatePubspec(
4399
+ projectPath,
4400
+ devDeps,
4401
+ pubspecAdditions,
4402
+ logger2
4403
+ );
4404
+ }
4405
+ logger2.info("\u{1F4E6} \u8FD0\u884C flutter pub get...");
4406
+ await this.runCommand("flutter pub get", projectPath, logger2);
4407
+ if (assets.appIcon) {
4408
+ logger2.info("\u2699\uFE0F \u751F\u6210\u5E94\u7528\u56FE\u6807...");
4409
+ await this.runCommand(
4410
+ "dart run flutter_launcher_icons",
4411
+ projectPath,
4412
+ logger2
4413
+ );
4414
+ }
4415
+ if (assets.splashLogo) {
4416
+ logger2.info("\u2699\uFE0F \u751F\u6210\u542F\u52A8\u56FE...");
4417
+ await this.runCommand(
4418
+ "dart run flutter_native_splash:create",
4419
+ projectPath,
4420
+ logger2
4421
+ );
4422
+ }
4423
+ logger2.success("\u2705 \u5E94\u7528\u8D44\u6E90\u8BBE\u7F6E\u5B8C\u6210");
4424
+ return true;
4425
+ } catch (error) {
4426
+ logger2.error(`\u8BBE\u7F6E\u5E94\u7528\u8D44\u6E90\u5931\u8D25: ${error.message}`);
4427
+ return false;
4428
+ }
4429
+ }
4430
+ /**
4431
+ * 复制文件(当源路径与目标路径相同时跳过)
4432
+ */
4433
+ async copyIfDifferent(srcPath, destPath) {
4434
+ const src = (0, import_path27.resolve)(srcPath);
4435
+ const dest = (0, import_path27.resolve)(destPath);
4436
+ if (src === dest) {
4437
+ return;
4438
+ }
4439
+ await import_fs_extra14.default.copy(srcPath, destPath, { overwrite: true });
4440
+ }
4441
+ /**
4442
+ * 更新 pubspec.yaml
4443
+ */
4444
+ async updatePubspec(projectPath, devDeps, additions, logger2) {
4445
+ const pubspecPath = (0, import_path27.join)(projectPath, "pubspec.yaml");
4446
+ let content = (0, import_fs15.readFileSync)(pubspecPath, "utf-8");
4447
+ if (devDeps.length > 0) {
4448
+ if (!content.includes("dev_dependencies:")) {
4449
+ content += "\ndev_dependencies:\n";
4450
+ }
4451
+ for (const dep of devDeps) {
4452
+ const depName = dep.split(":")[0].trim();
4453
+ if (!content.includes(depName)) {
4454
+ const devDepsMatch = content.match(/dev_dependencies:/);
4455
+ if (devDepsMatch && devDepsMatch.index !== void 0) {
4456
+ const insertPos = devDepsMatch.index + devDepsMatch[0].length;
4457
+ content = content.slice(0, insertPos) + `
4458
+ ${dep}` + content.slice(insertPos);
4459
+ }
4460
+ }
4461
+ }
4462
+ }
4463
+ content = this.removeExistingConfigBlocks(content, additions);
4464
+ content += "\n" + additions;
4465
+ (0, import_fs15.writeFileSync)(pubspecPath, content, "utf-8");
4466
+ logger2.info("\u2713 pubspec.yaml \u5DF2\u66F4\u65B0");
4467
+ }
4468
+ /**
4469
+ * 移除已存在的配置块(如 flutter_launcher_icons:, flutter_native_splash:)
4470
+ */
4471
+ removeExistingConfigBlocks(content, additions) {
4472
+ const configKeys = (additions.match(/^[a-z_]+:/gm) || []).map(
4473
+ (k) => k.replace(":", "").trim()
4474
+ );
4475
+ if (configKeys.length === 0) return content;
4476
+ const lines = content.split("\n");
4477
+ const resultLines = [];
4478
+ let skipBlock = false;
4479
+ for (const line of lines) {
4480
+ const topLevelMatch = line.match(/^([a-z_]+):.*$/);
4481
+ if (topLevelMatch) {
4482
+ const keyName = topLevelMatch[1];
4483
+ if (configKeys.includes(keyName)) {
4484
+ skipBlock = true;
4485
+ continue;
4486
+ } else {
4487
+ skipBlock = false;
4488
+ }
4489
+ }
4490
+ if (skipBlock && (line.trim() === "" || /^\s+/.test(line))) {
4491
+ continue;
4492
+ }
4493
+ if (skipBlock && line.trim() !== "" && !/^\s+/.test(line)) {
4494
+ skipBlock = false;
4495
+ }
4496
+ if (!skipBlock) {
4497
+ resultLines.push(line);
4498
+ }
4499
+ }
4500
+ return resultLines.join("\n");
4501
+ }
4502
+ /**
4503
+ * 执行命令
4504
+ */
4505
+ async runCommand(cmd, cwd, logger2) {
4506
+ try {
4507
+ const { stdout, stderr } = await execPromise(cmd, {
4508
+ cwd,
4509
+ maxBuffer: 1024 * 1024 * 10
4510
+ });
4511
+ if (stdout) {
4512
+ const lines = stdout.split("\n").filter(
4513
+ (line) => line.includes("\u2713") || line.includes("success") || line.includes("completed")
4514
+ );
4515
+ if (lines.length > 0) {
4516
+ logger2.info(lines.join("\n"));
4517
+ }
4518
+ }
4519
+ if (stderr && !stderr.includes("Warning")) {
4520
+ logger2.warn(stderr);
4521
+ }
4522
+ } catch (error) {
4523
+ logger2.error(`\u547D\u4EE4\u6267\u884C\u5931\u8D25: ${cmd}`);
4524
+ if (error.stdout) logger2.info(error.stdout);
4525
+ if (error.stderr) logger2.error(error.stderr);
4526
+ throw error;
4527
+ }
4528
+ }
4529
+ };
4530
+
4531
+ // src/utils/i18n_manager.ts
4532
+ var import_fs_extra15 = __toESM(require("fs-extra"), 1);
4533
+ var import_path28 = require("path");
4534
+ var import_url3 = require("url");
4535
+ var import_meta3 = {};
4536
+ var I18nManager = class _I18nManager {
4537
+ static instance;
4538
+ locales = {};
4539
+ locale = "zh-CN";
4540
+ localesDir;
4541
+ constructor() {
4542
+ this.localesDir = this.resolveLocalesDir();
4543
+ this.loadLocales();
4544
+ const config = ConfigManager.getInstance();
4545
+ const envLang = typeof process !== "undefined" ? process.env.LANG || process.env.LANGUAGE || "" : "";
4546
+ this.locale = config.getLocale() || (envLang.startsWith("en") ? "en-US" : "zh-CN");
4547
+ }
4548
+ resolveLocalesDir() {
4549
+ const fallback = "locales";
4550
+ try {
4551
+ if (typeof import_meta3 !== "undefined" && import_meta3.url) {
4552
+ const currentDir = (0, import_path28.dirname)((0, import_url3.fileURLToPath)(import_meta3.url));
4553
+ const result = (0, import_path28.join)(currentDir, "..", "locales");
4554
+ if (result && typeof result === "string") {
4555
+ return result;
4556
+ }
4557
+ }
4558
+ } catch (e) {
4559
+ }
4560
+ try {
4561
+ if (typeof __dirname !== "undefined" && __dirname && typeof __dirname === "string") {
4562
+ const result = (0, import_path28.join)(__dirname, "..", "locales");
4563
+ if (result && typeof result === "string") {
4564
+ return result;
4565
+ }
4566
+ }
4567
+ } catch (e) {
4568
+ }
4569
+ return fallback;
4570
+ }
4571
+ static getInstance() {
4572
+ if (!_I18nManager.instance) {
4573
+ _I18nManager.instance = new _I18nManager();
4574
+ }
4575
+ return _I18nManager.instance;
4576
+ }
4577
+ loadLocales() {
4578
+ try {
4579
+ if (!this.localesDir || typeof this.localesDir !== "string") {
4580
+ return;
4581
+ }
4582
+ const zhPath = (0, import_path28.join)(this.localesDir, "zh-CN.json");
4583
+ const enPath = (0, import_path28.join)(this.localesDir, "en-US.json");
4584
+ if (zhPath && typeof zhPath === "string" && import_fs_extra15.default.pathExistsSync(zhPath)) {
4585
+ this.locales["zh-CN"] = import_fs_extra15.default.readJsonSync(zhPath);
4586
+ }
4587
+ if (enPath && typeof enPath === "string" && import_fs_extra15.default.pathExistsSync(enPath)) {
4588
+ this.locales["en-US"] = import_fs_extra15.default.readJsonSync(enPath);
4589
+ }
4590
+ } catch (error) {
4591
+ }
4592
+ }
4593
+ setLocale(locale) {
4594
+ if (this.locales[locale] || locale === "en-US" || locale === "zh-CN") {
4595
+ this.locale = locale;
4596
+ }
4597
+ }
4598
+ getLocale() {
4599
+ return this.locale;
4600
+ }
4601
+ t(key, params = {}) {
4602
+ let text = this.locales[this.locale]?.[key] || this.locales["en-US"]?.[key] || key;
4603
+ Object.keys(params).forEach((p) => {
4604
+ text = text.replace(new RegExp(`\\{${p}\\}`, "g"), String(params[p]));
4605
+ });
4606
+ return text;
4607
+ }
4608
+ };
4609
+ var t = (key, params) => I18nManager.getInstance().t(key, params);
4610
+ // Annotate the CommonJS export names for ESM import in node:
4611
+ 0 && (module.exports = {
4612
+ AppAssetsManager,
4613
+ BUILTIN_TEMPLATES,
4614
+ ConfigManager,
4615
+ ConsoleLogger,
4616
+ I18nManager,
4617
+ ProjectConfigManager,
4618
+ ProjectGenerator,
4619
+ TemplateGenerator,
4620
+ TemplateManager,
4621
+ checkFlutterInstalled,
4622
+ checkSnippetsVersion,
4623
+ cleanupTemplateFiles,
4624
+ copyCoreFiles,
4625
+ copyCustomTemplate,
4626
+ copyInfrastructure,
4627
+ copyNetworkFiles,
4628
+ copyTemplate,
4629
+ detectProjectTemplate,
4630
+ ensurePubspecName,
4631
+ generateComponent,
4632
+ generateIndexFile,
4633
+ generateModel,
4634
+ generateModule,
4635
+ generatePage,
4636
+ generateService,
4637
+ generateViewModel,
4638
+ generateWidget,
4639
+ getCoreFilesDir,
4640
+ getFlutterVersion,
4641
+ getModelPath,
4642
+ getNetworkDir,
4643
+ getPagePath,
4644
+ getRelativeImportPath,
4645
+ getServicePath,
4646
+ getSnippetContent,
4647
+ getStateManager,
4648
+ getTemplatesRootDir,
4649
+ getViewModelPath,
4650
+ getWidgetPath,
4651
+ injectNetworkExamples,
4652
+ isCustomTemplateProject,
4653
+ loadProjectSnippets,
4654
+ logger,
4655
+ removeTemplateSuffix,
4656
+ renderSnippet,
4657
+ replaceVariables,
4658
+ runFlutterCreate,
4659
+ runFlutterPubGet,
4660
+ t,
4661
+ toCamelCase,
4662
+ toKebabCase,
4663
+ toPascalCase,
4664
+ toSnakeCase,
4665
+ toTitleCase,
4666
+ updateIndexFile,
4667
+ upgradeSnippets
4668
+ });