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/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # @flu-cli/core (V6.0)
2
+
3
+ 核心逻辑库,遵循 **Task Pipeline (任务流水线)** 架构,为 flu-cli 和 VSCode 扩展提供标准化的 Flutter 代码生成与增强能力。
4
+
5
+ ## 🏗 核心架构:Task Pipeline
6
+
7
+ 从 V6.0 开始,核心生成器由传统的命令式逻辑转变为 **原子化任务流水线** 架构。
8
+
9
+ ### 1. 乐高式搭建 (The Lego Model)
10
+ `ProjectGenerator` 不再直接操作文件,而是作为一个 **Orchestrator (指挥官)**。它通过 `ProjectPipeline` 编排一系列 `IProjectTask`。
11
+
12
+ ```mermaid
13
+ graph LR
14
+ A[ProjectGenerator] --> B(ProjectPipeline)
15
+ B --> T1[FlutterInitTask]
16
+ B --> T2[TemplateCopyTask]
17
+ B --> T3[VariablesReplaceTask]
18
+ B --> T4[NetworkEnrichTask]
19
+ B --> T5[RouteMappingTask]
20
+ B --> T6[HomePatchTask]
21
+ B --> T7[CleanupTask]
22
+ ```
23
+
24
+ ### 2. 骨架 + 增强 (Skeleton + Enrichment)
25
+ - **Skeleton (骨架)**: 从 Git 或本地加载的纯净 Flutter 代码模板。
26
+ - **Enrichment (增强)**: CLI 通过任务流水线动态注入的功能层(如:`NetworkEnrichTask` 注入 Dio 封装,`StateManagerEnrichTask` 注入状态管理适配器)。
27
+
28
+ ## 💡 为什么这么牛?
29
+
30
+ - **极简核心**: `ProjectGenerator.ts` 从 1500 行精简至 100 行,逻辑高度内聚于各个 Task。
31
+ - **高可扩展性**: 想要支持一个新的功能(如 Sentry 监控、WeChat SDK)?只需编写一个新的 `EnrichTask` 并加入流水线。
32
+ - **高度解耦**: 状态管理、路由注册、环境修复各司其职,互不干扰。
33
+ - **声明式配置**: 开发者可以清晰地看到项目生成的每一个步骤。
34
+
35
+ ## 🚀 核心组件
36
+
37
+ ### 生成器 (Generators)
38
+ - **ProjectGenerator**: 采用 Pipeline 模式的项目实例化引擎。
39
+ - **Page/ViewModel/Model Generator**: 针对不同架构模板的高级代码生成器。
40
+
41
+ ### 任务体系 (Tasks)
42
+ - `FlutterInitTask`: 确保原生 Flutter 环境就绪。
43
+ - `TemplateCopyTask`: 智能处理模板复制与冗余清理。
44
+ - `NetworkEnrichTask`: 声明式注入网络层基础设施。
45
+ - `RouteMappingTask`: 自动扫描业务路径并注册路由表。
46
+ - `HomePatchTask`: 自动化首页示例入口侵入。
47
+
48
+ ## 🛠 开发与扩展
49
+
50
+ ### 如何添加一个新任务?
51
+ 1. 在 `src/generators/tasks/` 创建实现 `IProjectTask` 的类。
52
+ 2. 在 `ProjectGenerator.generate` 中通过 `.addTask()` 链式调用。
53
+
54
+ ```typescript
55
+ pipeline.addTask(new YourAwesomeTask());
56
+ ```
57
+
58
+ ## 📜 许可证
59
+
60
+ MIT - Copyright © 2025 火叶工作室
@@ -0,0 +1,378 @@
1
+ // src/generators/tasks/adapters/provider_adapter.ts
2
+ var ProviderAdapter = class {
3
+ name;
4
+ constructor(name = "provider") {
5
+ this.name = name;
6
+ }
7
+ getDependencies() {
8
+ return this.name === "provider" ? ["provider: ^6.1.1"] : [];
9
+ }
10
+ getBaseViewModelTemplate(context) {
11
+ return null;
12
+ }
13
+ getBasePageParent(isListPage) {
14
+ return isListPage ? "BaseListPage" : "BasePage";
15
+ }
16
+ getBaseViewModelParent(isListPage) {
17
+ return isListPage ? "BaseListViewModel" : "BaseViewModel";
18
+ }
19
+ getImports(relativeCorePath) {
20
+ if (this.name === "provider") {
21
+ return [
22
+ `import 'package:provider/provider.dart';`,
23
+ `import '${relativeCorePath}';`
24
+ ];
25
+ }
26
+ return [`import '${relativeCorePath}';`];
27
+ }
28
+ patchAppEntry(content, context) {
29
+ return content;
30
+ }
31
+ getCommonInfo() {
32
+ return ` ViewState _state = ViewState.idle;
33
+ String? _errorMessage;
34
+ bool _isRefreshing = false;
35
+
36
+ // ==================== Getters ====================
37
+ // \u5F53\u524D\u89C6\u56FE\u72B6\u6001
38
+ ViewState get state => _state;
39
+ // \u9519\u8BEF\u4FE1\u606F
40
+ String? get errorMessage => _errorMessage;
41
+ // \u662F\u5426\u6B63\u5728\u52A0\u8F7D
42
+ bool get isLoading => _state == ViewState.loading;
43
+ // \u662F\u5426\u52A0\u8F7D\u5931\u8D25
44
+ bool get isError => _state == ViewState.error;
45
+ // \u662F\u5426\u52A0\u8F7D\u6210\u529F
46
+ bool get isSuccess => _state == ViewState.success;
47
+ // \u662F\u5426\u7A7A\u95F2\u72B6\u6001
48
+ bool get isIdle => _state == ViewState.idle;
49
+ // \u662F\u5426\u6B63\u5728\u5237\u65B0
50
+ bool get isRefreshing => _isRefreshing;
51
+
52
+ // ==================== \u72B6\u6001\u8BBE\u7F6E ====================
53
+
54
+ /// \u8BBE\u7F6E\u89C6\u56FE\u72B6\u6001
55
+ void setState(ViewState state, {String? error}) {
56
+ _state = state;
57
+ _errorMessage = error;
58
+ notifyListeners();
59
+ }
60
+
61
+ /// \u8BBE\u7F6E\u5237\u65B0\u72B6\u6001
62
+ void setRefreshing(bool refreshing) {
63
+ _isRefreshing = refreshing;
64
+ notifyListeners();
65
+ }
66
+ `;
67
+ }
68
+ };
69
+
70
+ // src/generators/tasks/adapters/getx_adapter.ts
71
+ var GetXAdapter = class {
72
+ name = "getx";
73
+ getDependencies() {
74
+ return ["get: ^4.6.6"];
75
+ }
76
+ getBaseViewModelTemplate(context) {
77
+ return `import 'package:get/get.dart';
78
+
79
+ enum ViewState { idle, loading, success, error }
80
+
81
+ class BaseViewModel extends GetxController {
82
+ ViewState _state = ViewState.idle;
83
+ String? _errorMessage;
84
+ bool _isRefreshing = false;
85
+ bool _disposed = false;
86
+
87
+ // ==================== Getters ====================
88
+ ViewState get state => _state;
89
+ String? get errorMessage => _errorMessage;
90
+ bool get isLoading => _state == ViewState.loading;
91
+ bool get isError => _state == ViewState.error;
92
+ bool get isSuccess => _state == ViewState.success;
93
+ bool get isIdle => _state == ViewState.idle;
94
+ bool get isRefreshing => _isRefreshing;
95
+ // GetX \u4F7F\u7528 isClosed \u5224\u65AD\u662F\u5426\u5DF2\u91CA\u653E
96
+ bool get isDisposed => _disposed || isClosed;
97
+
98
+ // ==================== \u72B6\u6001\u8BBE\u7F6E ====================
99
+ void setState(ViewState state, {String? error}) {
100
+ if (_disposed || isClosed) return;
101
+ _state = state;
102
+ _errorMessage = error;
103
+ update(); // GetX \u4F7F\u7528 update() \u800C\u975E notifyListeners()
104
+ }
105
+
106
+ void setRefreshing(bool refreshing) {
107
+ if (_disposed || isClosed) return;
108
+ _isRefreshing = refreshing;
109
+ update();
110
+ }
111
+
112
+ /// \u901A\u77E5\u6570\u636E\u53D8\u5316(\u4EC5\u7528\u4E8E\u5C40\u90E8\u5237\u65B0)
113
+ void notifyDataChange() {
114
+ if (_disposed || isClosed) return;
115
+ update();
116
+ }
117
+
118
+ // ==================== \u751F\u547D\u5468\u671F ====================
119
+ /// \u521D\u59CB\u5316\u94A9\u5B50
120
+ @override
121
+ Future<void> onInit() async {
122
+ super.onInit();
123
+ // \u5B50\u7C7B\u53EF\u4EE5\u91CD\u5199\u6B64\u65B9\u6CD5\u8FDB\u884C\u521D\u59CB\u5316
124
+ }
125
+
126
+ /// \u5237\u65B0\u6570\u636E(\u94A9\u5B50)
127
+ Future<void> refreshData() async {
128
+ // \u5B50\u7C7B\u53EF\u4EE5\u91CD\u5199\u6B64\u65B9\u6CD5\u5B9E\u73B0\u5237\u65B0\u903B\u8F91
129
+ }
130
+
131
+ @override
132
+ void onClose() {
133
+ _disposed = true;
134
+ super.onClose();
135
+ }
136
+ }
137
+ `;
138
+ }
139
+ getBasePageParent(isListPage) {
140
+ return isListPage ? "BaseListPage" : "BasePage";
141
+ }
142
+ getBaseViewModelParent(isListPage) {
143
+ return isListPage ? "BaseListViewModel" : "BaseViewModel";
144
+ }
145
+ getImports(relativeCorePath) {
146
+ return [
147
+ `import '${relativeCorePath}';`
148
+ ];
149
+ }
150
+ /**
151
+ * 修改 app.dart
152
+ */
153
+ patchAppEntry(content, context) {
154
+ let raw = content;
155
+ if (!raw.includes("GetMaterialApp(")) {
156
+ raw = raw.replace(/MaterialApp\s*\(/, "GetMaterialApp(");
157
+ }
158
+ if (raw.includes("GetMaterialApp(") && !raw.includes("import 'package:get/get.dart'")) {
159
+ if (raw.includes("import 'package:flutter/material.dart';")) {
160
+ raw = raw.replace(
161
+ "import 'package:flutter/material.dart';",
162
+ "import 'package:flutter/material.dart';\nimport 'package:get/get.dart';"
163
+ );
164
+ } else {
165
+ raw = `import 'package:get/get.dart';
166
+ ` + raw;
167
+ }
168
+ }
169
+ if (raw.includes("routes: AppRoutes.routes")) {
170
+ raw = raw.replace("routes: AppRoutes.routes", "getPages: AppRoutes.pages");
171
+ }
172
+ if (raw.includes("navigatorKey: NavigatorUtil.navigatorKey")) {
173
+ raw = raw.replace(/navigatorKey:\s*NavigatorUtil\.navigatorKey\s*,?/g, "");
174
+ }
175
+ if (raw.includes("scaffoldMessengerKey: NavigatorUtil.scaffoldMessengerKey")) {
176
+ raw = raw.replace(/scaffoldMessengerKey:\s*NavigatorUtil\.scaffoldMessengerKey\s*,?\s*\n?/g, "");
177
+ }
178
+ return raw;
179
+ }
180
+ /**
181
+ * 修改 app_routes.dart
182
+ */
183
+ patchRoutes(content, context) {
184
+ let raw = content;
185
+ if (raw.includes("static List<GetPage> get pages")) {
186
+ return raw;
187
+ }
188
+ if (!raw.includes("import 'package:get/get.dart'")) {
189
+ if (raw.includes("import 'package:flutter/material.dart';")) {
190
+ raw = raw.replace(
191
+ "import 'package:flutter/material.dart';",
192
+ "import 'package:get/get.dart';"
193
+ );
194
+ } else {
195
+ raw = `import 'package:get/get.dart';
196
+ ` + raw;
197
+ }
198
+ }
199
+ const startMarker = "// __ROUTE_CONFIG_START__";
200
+ const endMarker = "// __ROUTE_CONFIG_END__";
201
+ const pagesGetter = `
202
+ /// GetX \u8DEF\u7531\u6620\u5C04
203
+ /// \u5728 app.dart \u4E2D\u4F7F\u7528: GetMaterialApp(getPages: AppRoutes.pages, ...)
204
+ static List<GetPage> get pages {
205
+ return [
206
+ GetPage(name: home, page: () => const HomePage()),
207
+ GetPage(name: userList, page: () => const UserListPage()),
208
+ // --- \u5728\u6B64\u4E0B\u65B9\u6DFB\u52A0\u60A8\u7684\u81EA\u5B9A\u4E49\u8DEF\u7531 ---
209
+ ];
210
+ }
211
+ `;
212
+ const startIndex = raw.indexOf(startMarker);
213
+ const endIndex = raw.indexOf(endMarker);
214
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
215
+ const before = raw.substring(0, startIndex);
216
+ const after = raw.substring(endIndex + endMarker.length);
217
+ raw = before + pagesGetter.trim() + after;
218
+ } else {
219
+ 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*\}/;
220
+ if (standardRoutesPattern.test(raw)) {
221
+ raw = raw.replace(standardRoutesPattern, pagesGetter);
222
+ } else {
223
+ const lastBraceIndex = raw.lastIndexOf("}");
224
+ if (lastBraceIndex !== -1) {
225
+ raw = raw.slice(0, lastBraceIndex) + "\n" + pagesGetter + raw.slice(lastBraceIndex);
226
+ }
227
+ }
228
+ }
229
+ return raw;
230
+ }
231
+ };
232
+
233
+ // src/generators/tasks/adapters/riverpod_adapter.ts
234
+ import { join } from "path";
235
+ import fsx from "fs-extra";
236
+ var RiverpodAdapter = class {
237
+ name = "riverpod";
238
+ getDependencies() {
239
+ return ["flutter_riverpod: ^2.4.9"];
240
+ }
241
+ getBasePageParent(isListPage) {
242
+ return isListPage ? "BaseListPageRiverpod" : "BasePageRiverpod";
243
+ }
244
+ getBaseViewModelParent(isListPage) {
245
+ return isListPage ? "BaseListNotifier" : "BaseNotifier";
246
+ }
247
+ getImports(relativeCorePath) {
248
+ return [
249
+ `import 'package:flutter_riverpod/flutter_riverpod.dart';`,
250
+ `import '${relativeCorePath}';`
251
+ ];
252
+ }
253
+ getBaseViewModelTemplate(context) {
254
+ return null;
255
+ }
256
+ patchAppEntry(content, context) {
257
+ let raw = content;
258
+ if (raw.includes("runApp(") && !raw.includes("ProviderScope")) {
259
+ raw = raw.replace(/runApp\((.*?)\)/, "runApp(const ProviderScope(child: $1))");
260
+ if (!raw.includes("import 'package:flutter_riverpod/flutter_riverpod.dart'")) {
261
+ raw = `import 'package:flutter_riverpod/flutter_riverpod.dart';
262
+ ` + raw;
263
+ }
264
+ }
265
+ return raw;
266
+ }
267
+ async onEnrich(context) {
268
+ const { projectPath } = context;
269
+ const baseDir = join(projectPath, "lib", "core", "base");
270
+ await fsx.ensureDir(baseDir);
271
+ await fsx.writeFile(join(baseDir, "view_state.dart"), "enum ViewState { idle, loading, success, error }\n");
272
+ await fsx.writeFile(join(baseDir, "base_state.dart"), `
273
+ import 'view_state.dart';
274
+
275
+ class BaseState {
276
+ final ViewState state;
277
+ final bool isRefreshing;
278
+ final String? error;
279
+
280
+ const BaseState({
281
+ this.state = ViewState.idle,
282
+ this.isRefreshing = false,
283
+ this.error,
284
+ });
285
+
286
+ BaseState copyWith({ ViewState? state, bool? isRefreshing, String? error }) {
287
+ return BaseState(
288
+ state: state ?? this.state,
289
+ isRefreshing: isRefreshing ?? this.isRefreshing,
290
+ error: error ?? this.error,
291
+ );
292
+ }
293
+
294
+ bool get isLoading => state == ViewState.loading;
295
+ bool get isSuccess => state == ViewState.success;
296
+ bool get isError => state == ViewState.error;
297
+ bool get isIdle => state == ViewState.idle;
298
+ }
299
+ `);
300
+ await fsx.writeFile(join(baseDir, "base_notifier.dart"), `
301
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
302
+ import 'view_state.dart';
303
+ import 'base_state.dart';
304
+
305
+ abstract class BaseNotifier<T extends BaseState> extends Notifier<T> {
306
+ T createInitialState();
307
+ @override
308
+ T build() => createInitialState();
309
+
310
+ void setLoading() { state = (state.copyWith(state: ViewState.loading, error: null)) as T; }
311
+ void setSuccess() { state = (state.copyWith(state: ViewState.success)) as T; }
312
+ void setError(String message) { state = (state.copyWith(state: ViewState.error, error: message)) as T; }
313
+ void setRefreshing(bool refreshing) { state = (state.copyWith(isRefreshing: refreshing)) as T; }
314
+
315
+ Future<void> run(Future<void> Function() task) async {
316
+ setLoading();
317
+ try {
318
+ await task();
319
+ setSuccess();
320
+ } catch (e) {
321
+ setError(e.toString());
322
+ }
323
+ }
324
+ }
325
+ `);
326
+ await fsx.writeFile(join(baseDir, "base_page_riverpod.dart"), `
327
+ import 'package:flutter/material.dart';
328
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
329
+ import 'base_state.dart';
330
+
331
+ typedef ContentBuilder<TState extends BaseState, TNotifier extends Notifier<TState>> = Widget Function(BuildContext context, TState state, TNotifier vm);
332
+
333
+ class BasePageRiverpod<TState extends BaseState, TNotifier extends Notifier<TState>> extends ConsumerWidget {
334
+ final ProviderListenable<TState> provider;
335
+ final String title;
336
+ final ContentBuilder<TState, TNotifier> builder;
337
+
338
+ const BasePageRiverpod({ super.key, required this.provider, required this.title, required this.builder });
339
+
340
+ @override
341
+ Widget build(BuildContext context, WidgetRef ref) {
342
+ final state = ref.watch(provider);
343
+ final vm = ref.read(provider as dynamic).notifier as TNotifier;
344
+ return Scaffold(
345
+ appBar: AppBar(title: Text(title)),
346
+ body: Center(
347
+ child: state.isLoading
348
+ ? const CircularProgressIndicator()
349
+ : state.isError
350
+ ? Text('Error: ' + (state.error ?? ''))
351
+ : builder(context, state, vm),
352
+ ),
353
+ );
354
+ }
355
+ }
356
+ `);
357
+ }
358
+ };
359
+
360
+ // src/generators/tasks/adapters/factory.ts
361
+ var StateManagerAdapterFactory = class {
362
+ static getAdapter(name) {
363
+ switch (name.toLowerCase()) {
364
+ case "getx":
365
+ return new GetXAdapter();
366
+ case "provider":
367
+ return new ProviderAdapter("provider");
368
+ case "riverpod":
369
+ return new RiverpodAdapter();
370
+ default:
371
+ return new ProviderAdapter("default");
372
+ }
373
+ }
374
+ };
375
+
376
+ export {
377
+ StateManagerAdapterFactory
378
+ };
@@ -0,0 +1,112 @@
1
+ // src/commands/upgrade_snippets.ts
2
+ import { existsSync, mkdirSync, copyFileSync, readFileSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ // src/utils/logger.ts
7
+ import chalk from "chalk";
8
+ var ConsoleLogger = class {
9
+ info(message) {
10
+ console.log(chalk.blue("\u2139\uFE0F " + message));
11
+ }
12
+ success(message) {
13
+ console.log(chalk.green("\u2705 " + message));
14
+ }
15
+ warn(message) {
16
+ console.log(chalk.yellow("\u26A0\uFE0F " + message));
17
+ }
18
+ error(message) {
19
+ console.log(chalk.red("\u274C " + message));
20
+ }
21
+ title(message) {
22
+ console.log(chalk.bold.cyan("\n" + message + "\n"));
23
+ }
24
+ newLine() {
25
+ console.log("");
26
+ }
27
+ };
28
+ var logger = new ConsoleLogger();
29
+
30
+ // src/commands/upgrade_snippets.ts
31
+ var __dirname;
32
+ try {
33
+ if (typeof import.meta !== "undefined" && import.meta.url) {
34
+ const __filename = fileURLToPath(import.meta.url);
35
+ __dirname = dirname(__filename);
36
+ } else {
37
+ throw new Error("Using CJS fallback");
38
+ }
39
+ } catch {
40
+ __dirname = typeof global !== "undefined" && global.__dirname ? global.__dirname : process.cwd();
41
+ }
42
+ async function upgradeSnippets(options = {}, logger2 = new ConsoleLogger()) {
43
+ try {
44
+ const projectDir = options.projectDir || process.cwd();
45
+ const force = options.force || false;
46
+ logger2.info("\u{1F504} \u6B63\u5728\u5347\u7EA7\u9879\u76EE snippets...");
47
+ const pubspecPath = join(projectDir, "pubspec.yaml");
48
+ if (!existsSync(pubspecPath)) {
49
+ logger2.error("\u274C \u672A\u68C0\u6D4B\u5230 Flutter \u9879\u76EE (\u7F3A\u5C11 pubspec.yaml)");
50
+ return false;
51
+ }
52
+ const sourceSnippetsPath = join(
53
+ __dirname,
54
+ "..",
55
+ "..",
56
+ "templates",
57
+ "snippets",
58
+ "flu-cli.code-snippets"
59
+ );
60
+ if (!existsSync(sourceSnippetsPath)) {
61
+ logger2.error(`\u274C \u627E\u4E0D\u5230\u6E90 snippets \u6587\u4EF6: ${sourceSnippetsPath}`);
62
+ return false;
63
+ }
64
+ const targetDir = join(projectDir, ".vscode");
65
+ const targetPath = join(targetDir, "flu-cli.code-snippets");
66
+ if (existsSync(targetPath) && !force) {
67
+ logger2.warn("\u26A0\uFE0F \u9879\u76EE\u4E2D\u5DF2\u5B58\u5728 snippets \u6587\u4EF6");
68
+ logger2.info("\u63D0\u793A: \u4F7F\u7528 --force \u53C2\u6570\u5F3A\u5236\u8986\u76D6");
69
+ return false;
70
+ }
71
+ if (!existsSync(targetDir)) {
72
+ mkdirSync(targetDir, { recursive: true });
73
+ logger2.info("\u2713 \u521B\u5EFA .vscode \u76EE\u5F55");
74
+ }
75
+ copyFileSync(sourceSnippetsPath, targetPath);
76
+ logger2.success(`\u2705 Snippets \u66F4\u65B0\u6210\u529F!`);
77
+ logger2.info(` \u4F4D\u7F6E: ${targetPath}`);
78
+ logger2.newLine();
79
+ logger2.info("\u{1F4DD} \u66F4\u65B0\u5185\u5BB9:");
80
+ logger2.info(" \u2022 Core \u5BFC\u5165\u8DEF\u5F84\u6539\u4E3A\u4F7F\u7528\u53D8\u91CF ${relative_core_path}");
81
+ logger2.info(" \u2022 \u652F\u6301 package imports (\u81EA\u52A8\u751F\u6210)");
82
+ logger2.info(" \u2022 \u9002\u914D\u6240\u6709\u67B6\u6784 (Lite/Modular/Clean)");
83
+ return true;
84
+ } catch (error) {
85
+ const msg = error instanceof Error ? error.message : String(error);
86
+ logger2.error(`\u274C \u5347\u7EA7\u5931\u8D25: ${msg}`);
87
+ return false;
88
+ }
89
+ }
90
+ function checkSnippetsVersion(projectDir = process.cwd()) {
91
+ const snippetsPath = join(projectDir, ".vscode", "flu-cli.code-snippets");
92
+ if (!existsSync(snippetsPath)) {
93
+ return "missing";
94
+ }
95
+ try {
96
+ const content = readFileSync(snippetsPath, "utf8");
97
+ if (content.includes("${relative_core_path}")) {
98
+ return "up-to-date";
99
+ } else {
100
+ return "outdated";
101
+ }
102
+ } catch {
103
+ return "missing";
104
+ }
105
+ }
106
+
107
+ export {
108
+ ConsoleLogger,
109
+ logger,
110
+ upgradeSnippets,
111
+ checkSnippetsVersion
112
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ StateManagerAdapterFactory
3
+ } from "./chunk-FOMWV2YP.js";
4
+ export {
5
+ StateManagerAdapterFactory
6
+ };