optikit 1.1.1 → 1.2.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 (79) hide show
  1. package/CHANGELOG.md +23 -2
  2. package/CLAUDE.md +239 -0
  3. package/CODE_QUALITY.md +398 -0
  4. package/ENHANCEMENTS.md +310 -0
  5. package/FEATURE_ENHANCEMENTS.md +435 -0
  6. package/README.md +46 -13
  7. package/SAFETY_FEATURES.md +396 -0
  8. package/USAGE.md +225 -41
  9. package/VERSION_MANAGEMENT.md +438 -0
  10. package/dist/cli.js +116 -7
  11. package/dist/commands/build/releases.js +57 -0
  12. package/dist/commands/buildReleases.js +48 -74
  13. package/dist/commands/clean/flutter.js +51 -0
  14. package/dist/commands/clean/ios.js +109 -0
  15. package/dist/commands/cleanProject.js +34 -4
  16. package/dist/commands/cleanProjectIos.js +17 -7
  17. package/dist/commands/config/init.js +54 -0
  18. package/dist/commands/config/rollback.js +161 -0
  19. package/dist/commands/generateModule.js +39 -11
  20. package/dist/commands/init.js +54 -0
  21. package/dist/commands/openProject.js +17 -0
  22. package/dist/commands/project/devices.js +188 -0
  23. package/dist/commands/project/generate.js +143 -0
  24. package/dist/commands/project/open.js +63 -0
  25. package/dist/commands/project/setup.js +46 -0
  26. package/dist/commands/rollback.js +161 -0
  27. package/dist/commands/setupVSCode.js +27 -21
  28. package/dist/commands/updateVersions.js +13 -1
  29. package/dist/commands/version/bump.js +161 -0
  30. package/dist/commands/version/update.js +91 -0
  31. package/dist/commands/version.js +161 -0
  32. package/dist/constants.js +131 -0
  33. package/dist/utils/backupHelpers.js +88 -0
  34. package/dist/utils/buildHelpers.js +55 -0
  35. package/dist/utils/commandHelpers.js +51 -0
  36. package/dist/utils/configHelpers.js +80 -0
  37. package/dist/utils/dryRunHelpers.js +103 -0
  38. package/dist/utils/fileHelpers.js +2 -1
  39. package/dist/utils/helpers/build.js +55 -0
  40. package/dist/utils/helpers/dryRun.js +103 -0
  41. package/dist/utils/helpers/file.js +24 -0
  42. package/dist/utils/helpers/string.js +3 -0
  43. package/dist/utils/helpers/version.js +80 -0
  44. package/dist/utils/services/backup.js +88 -0
  45. package/dist/utils/services/command.js +51 -0
  46. package/dist/utils/services/config.js +80 -0
  47. package/dist/utils/services/exec.js +132 -0
  48. package/dist/utils/services/logger.js +15 -0
  49. package/dist/utils/validationHelpers.js +101 -0
  50. package/dist/utils/validators/validation.js +101 -0
  51. package/dist/utils/versionHelpers.js +80 -0
  52. package/package.json +1 -1
  53. package/src/cli.ts +165 -7
  54. package/src/commands/build/releases.ts +79 -0
  55. package/src/commands/clean/flutter.ts +58 -0
  56. package/src/commands/{cleanProjectIos.ts → clean/ios.ts} +19 -10
  57. package/src/commands/config/init.ts +63 -0
  58. package/src/commands/config/rollback.ts +200 -0
  59. package/src/commands/project/devices.ts +246 -0
  60. package/src/commands/{generateModule.ts → project/generate.ts} +47 -17
  61. package/src/commands/{openProject.ts → project/open.ts} +26 -5
  62. package/src/commands/project/setup.ts +50 -0
  63. package/src/commands/version/bump.ts +202 -0
  64. package/src/commands/{updateVersions.ts → version/update.ts} +22 -6
  65. package/src/constants.ts +144 -0
  66. package/src/utils/helpers/build.ts +80 -0
  67. package/src/utils/helpers/dryRun.ts +124 -0
  68. package/src/utils/{fileHelpers.ts → helpers/file.ts} +3 -2
  69. package/src/utils/helpers/version.ts +109 -0
  70. package/src/utils/services/backup.ts +109 -0
  71. package/src/utils/services/command.ts +76 -0
  72. package/src/utils/services/config.ts +106 -0
  73. package/src/utils/{execHelpers.ts → services/exec.ts} +1 -1
  74. package/src/utils/validators/validation.ts +122 -0
  75. package/src/commands/buildReleases.ts +0 -102
  76. package/src/commands/cleanProject.ts +0 -25
  77. package/src/commands/setupVSCode.ts +0 -44
  78. /package/src/utils/{stringHelpers.ts → helpers/string.ts} +0 -0
  79. /package/src/utils/{loggerHelpers.ts → services/logger.ts} +0 -0
@@ -0,0 +1,54 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { LoggerHelpers } from "../utils/loggerHelpers.js";
4
+ import { saveConfig } from "../utils/configHelpers.js";
5
+ export { initializeProject };
6
+ /**
7
+ * Initializes OptiKit in the current project
8
+ * Creates .optikitrc configuration file with defaults
9
+ */
10
+ async function initializeProject() {
11
+ try {
12
+ LoggerHelpers.info("Initializing OptiKit in this project...");
13
+ const configPath = path.join(process.cwd(), ".optikitrc.json");
14
+ // Check if config already exists
15
+ if (fs.existsSync(configPath)) {
16
+ LoggerHelpers.warning("OptiKit configuration already exists.");
17
+ LoggerHelpers.info("To reconfigure, delete .optikitrc.json and run init again.");
18
+ return;
19
+ }
20
+ // Create default configuration
21
+ const defaultConfig = {
22
+ backupRetentionCount: 5,
23
+ useFvmByDefault: false,
24
+ autoBackup: true,
25
+ verbose: false,
26
+ };
27
+ // Save configuration
28
+ const success = saveConfig(defaultConfig);
29
+ if (success) {
30
+ LoggerHelpers.success("OptiKit initialized successfully!");
31
+ console.log("\nDefault configuration:");
32
+ console.log(JSON.stringify(defaultConfig, null, 2));
33
+ console.log("\nYou can modify .optikitrc.json to customize these settings.\n");
34
+ // Create .gitignore entry for backups if .gitignore exists
35
+ const gitignorePath = path.join(process.cwd(), ".gitignore");
36
+ if (fs.existsSync(gitignorePath)) {
37
+ const gitignoreContent = fs.readFileSync(gitignorePath, "utf8");
38
+ if (!gitignoreContent.includes(".optikit-backup")) {
39
+ fs.appendFileSync(gitignorePath, "\n# OptiKit backup files\n.optikit-backup/\n");
40
+ LoggerHelpers.success("Added .optikit-backup/ to .gitignore");
41
+ }
42
+ }
43
+ }
44
+ }
45
+ catch (error) {
46
+ if (error instanceof Error) {
47
+ LoggerHelpers.error(`Error initializing project: ${error.message}`);
48
+ }
49
+ else {
50
+ LoggerHelpers.error(`Error initializing project: ${error}`);
51
+ }
52
+ process.exit(1);
53
+ }
54
+ }
@@ -1,9 +1,17 @@
1
1
  import { LoggerHelpers } from "../utils/loggerHelpers.js";
2
2
  import { execCommand } from "../utils/execHelpers.js";
3
3
  import { platform } from "os";
4
+ import { validateFlutterProject, validateIosProject, validateAndroidProject } from "../utils/validationHelpers.js";
4
5
  export { openIos, openAndroid };
5
6
  async function openIos() {
6
7
  LoggerHelpers.info("Opening the iOS project in Xcode...");
8
+ // Pre-flight validation
9
+ if (!validateFlutterProject()) {
10
+ process.exit(1);
11
+ }
12
+ if (!validateIosProject()) {
13
+ process.exit(1);
14
+ }
7
15
  const command = "open ios/Runner.xcworkspace";
8
16
  try {
9
17
  await execCommand(command);
@@ -16,10 +24,18 @@ async function openIos() {
16
24
  else {
17
25
  LoggerHelpers.error(`Error while opening Xcode: ${error}`);
18
26
  }
27
+ process.exit(1);
19
28
  }
20
29
  }
21
30
  async function openAndroid() {
22
31
  LoggerHelpers.info("Opening the Android project in Android Studio...");
32
+ // Pre-flight validation
33
+ if (!validateFlutterProject()) {
34
+ process.exit(1);
35
+ }
36
+ if (!validateAndroidProject()) {
37
+ process.exit(1);
38
+ }
23
39
  const osPlatform = platform();
24
40
  let command;
25
41
  if (osPlatform === "win32") {
@@ -42,5 +58,6 @@ async function openAndroid() {
42
58
  else {
43
59
  LoggerHelpers.error(`Error while opening Android Studio: ${error}`);
44
60
  }
61
+ process.exit(1);
45
62
  }
46
63
  }
@@ -0,0 +1,188 @@
1
+ import { exec } from "child_process";
2
+ import { promisify } from "util";
3
+ import { execCommand } from "../../utils/services/exec.js";
4
+ import { LoggerHelpers } from "../../utils/services/logger.js";
5
+ import { validateFlutterProject, validateFlutterSdk } from "../../utils/validators/validation.js";
6
+ import chalk from "chalk";
7
+ const execAsync = promisify(exec);
8
+ export { listDevices, runApp, getDevicesList, runAppInteractive };
9
+ ;
10
+ /**
11
+ * Gets a parsed list of devices
12
+ */
13
+ async function getDevicesList(useFvm = false) {
14
+ const flutterCommand = useFvm ? "fvm flutter" : "flutter";
15
+ try {
16
+ const { stdout } = await execAsync(`${flutterCommand} devices --machine`);
17
+ const devices = JSON.parse(stdout);
18
+ return devices.map(device => ({
19
+ id: device.id,
20
+ name: device.name,
21
+ platform: device.platform,
22
+ isEmulator: device.emulator
23
+ }));
24
+ }
25
+ catch (error) {
26
+ // Fallback to empty array if parsing fails
27
+ return [];
28
+ }
29
+ }
30
+ /**
31
+ * Lists all connected devices with numbered list
32
+ */
33
+ async function listDevices(useFvm = false) {
34
+ // Pre-flight validation
35
+ if (!validateFlutterProject()) {
36
+ process.exit(1);
37
+ }
38
+ if (!(await validateFlutterSdk(!useFvm))) {
39
+ process.exit(1);
40
+ }
41
+ try {
42
+ LoggerHelpers.info("Fetching connected devices...\n");
43
+ const devices = await getDevicesList(useFvm);
44
+ if (devices.length === 0) {
45
+ LoggerHelpers.warning("No devices found.");
46
+ console.log(chalk.gray("\nMake sure you have:"));
47
+ console.log(chalk.gray(" - A device connected via USB"));
48
+ console.log(chalk.gray(" - An emulator/simulator running"));
49
+ console.log(chalk.gray(" - Chrome browser for web development\n"));
50
+ return;
51
+ }
52
+ console.log(chalk.bold("\n📱 Connected Devices:\n"));
53
+ devices.forEach((device, index) => {
54
+ const number = chalk.cyan(`[${index + 1}]`);
55
+ const name = chalk.white.bold(device.name);
56
+ const platform = chalk.gray(`(${device.platform})`);
57
+ const type = device.isEmulator ? chalk.yellow(" [Emulator]") : chalk.green(" [Physical]");
58
+ const id = chalk.gray(`ID: ${device.id}`);
59
+ console.log(`${number} ${name} ${platform}${type}`);
60
+ console.log(` ${id}`);
61
+ console.log();
62
+ });
63
+ console.log(chalk.gray("═".repeat(60)));
64
+ console.log(chalk.gray("To run on a specific device:"));
65
+ console.log(chalk.white(" optikit run --device <device-id>"));
66
+ console.log(chalk.gray("\nOr use interactive selection:"));
67
+ console.log(chalk.white(" optikit run-select"));
68
+ console.log(chalk.gray("═".repeat(60) + "\n"));
69
+ }
70
+ catch (error) {
71
+ if (error instanceof Error) {
72
+ LoggerHelpers.error(`Error listing devices: ${error.message}`);
73
+ }
74
+ else {
75
+ LoggerHelpers.error(`Error listing devices: ${error}`);
76
+ }
77
+ process.exit(1);
78
+ }
79
+ }
80
+ /**
81
+ * Runs the Flutter app on a connected device
82
+ */
83
+ async function runApp(config) {
84
+ // Pre-flight validation
85
+ if (!validateFlutterProject()) {
86
+ process.exit(1);
87
+ }
88
+ if (!(await validateFlutterSdk(!config.useFvm))) {
89
+ process.exit(1);
90
+ }
91
+ try {
92
+ const flutterCommand = config.useFvm ? "fvm flutter" : "flutter";
93
+ // Build the run command
94
+ let command = `${flutterCommand} run`;
95
+ // Add device flag if specified
96
+ if (config.device) {
97
+ command += ` --device-id ${config.device}`;
98
+ LoggerHelpers.info(`Running on device: ${config.device}`);
99
+ }
100
+ else {
101
+ LoggerHelpers.info("Running on default device...");
102
+ }
103
+ // Add release flag if specified
104
+ if (config.release) {
105
+ command += " --release";
106
+ LoggerHelpers.info("Running in release mode");
107
+ }
108
+ // Add flavor flag if specified
109
+ if (config.flavor) {
110
+ command += ` --flavor ${config.flavor}`;
111
+ LoggerHelpers.info(`Running with flavor: ${config.flavor}`);
112
+ }
113
+ console.log(chalk.cyan("\nStarting Flutter app..."));
114
+ console.log(chalk.gray(`Command: ${command}\n`));
115
+ await execCommand(command);
116
+ }
117
+ catch (error) {
118
+ if (error instanceof Error) {
119
+ LoggerHelpers.error(`Error running app: ${error.message}`);
120
+ }
121
+ else {
122
+ LoggerHelpers.error(`Error running app: ${error}`);
123
+ }
124
+ process.exit(1);
125
+ }
126
+ }
127
+ /**
128
+ * Interactive device selection and run
129
+ */
130
+ async function runAppInteractive(config) {
131
+ // Pre-flight validation
132
+ if (!validateFlutterProject()) {
133
+ process.exit(1);
134
+ }
135
+ if (!(await validateFlutterSdk(!config.useFvm))) {
136
+ process.exit(1);
137
+ }
138
+ try {
139
+ LoggerHelpers.info("Fetching connected devices...\n");
140
+ const devices = await getDevicesList(config.useFvm);
141
+ if (devices.length === 0) {
142
+ LoggerHelpers.error("No devices found. Please connect a device or start an emulator.");
143
+ process.exit(1);
144
+ }
145
+ // Show devices
146
+ console.log(chalk.bold("\n📱 Connected Devices:\n"));
147
+ devices.forEach((device, index) => {
148
+ const number = chalk.cyan.bold(`[${index + 1}]`);
149
+ const name = chalk.white.bold(device.name);
150
+ const platform = chalk.gray(`(${device.platform})`);
151
+ const type = device.isEmulator ? chalk.yellow(" [Emulator]") : chalk.green(" [Physical]");
152
+ console.log(`${number} ${name} ${platform}${type}`);
153
+ });
154
+ console.log(chalk.gray("\n" + "═".repeat(60)));
155
+ console.log(chalk.cyan("Enter device number to run on:"));
156
+ console.log(chalk.gray("═".repeat(60) + "\n"));
157
+ // Use readline for user input
158
+ const readline = require('readline');
159
+ const rl = readline.createInterface({
160
+ input: process.stdin,
161
+ output: process.stdout
162
+ });
163
+ rl.question(chalk.yellow("Device number: "), async (answer) => {
164
+ rl.close();
165
+ const deviceIndex = parseInt(answer) - 1;
166
+ if (isNaN(deviceIndex) || deviceIndex < 0 || deviceIndex >= devices.length) {
167
+ LoggerHelpers.error(`Invalid device number. Please choose between 1 and ${devices.length}`);
168
+ process.exit(1);
169
+ }
170
+ const selectedDevice = devices[deviceIndex];
171
+ console.log(chalk.green(`\n✓ Selected: ${selectedDevice.name}\n`));
172
+ // Run on selected device
173
+ await runApp({
174
+ ...config,
175
+ device: selectedDevice.id
176
+ });
177
+ });
178
+ }
179
+ catch (error) {
180
+ if (error instanceof Error) {
181
+ LoggerHelpers.error(`Error: ${error.message}`);
182
+ }
183
+ else {
184
+ LoggerHelpers.error(`Error: ${error}`);
185
+ }
186
+ process.exit(1);
187
+ }
188
+ }
@@ -0,0 +1,143 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { createDirectories, writeFile, getClassName, } from "../../utils/helpers/file.js";
4
+ import { LoggerHelpers } from "../../utils/services/logger.js";
5
+ export { generateModule };
6
+ function generateModule(moduleName) {
7
+ try {
8
+ // Validate module name
9
+ if (!moduleName || moduleName.trim().length === 0) {
10
+ LoggerHelpers.error("Module name cannot be empty.");
11
+ process.exit(1);
12
+ }
13
+ // Validate module name format (only lowercase letters, numbers, and underscores)
14
+ const validNamePattern = /^[a-z0-9_]+$/;
15
+ if (!validNamePattern.test(moduleName)) {
16
+ LoggerHelpers.error("Module name must contain only lowercase letters, numbers, and underscores.");
17
+ process.exit(1);
18
+ }
19
+ const modulePath = path.join("lib", "module", moduleName);
20
+ const directories = ["bloc", "event", "state", "screen", "import", "factory"];
21
+ // Check if module already exists
22
+ if (fs.existsSync(modulePath)) {
23
+ LoggerHelpers.warning(`Module ${moduleName} already exists at ${modulePath}`);
24
+ LoggerHelpers.info("Files will be overwritten...");
25
+ }
26
+ createDirectories(modulePath, directories);
27
+ LoggerHelpers.info(`Creating module structure for ${moduleName}...`);
28
+ generateBloc(moduleName, path.join(modulePath, "bloc"));
29
+ generateEvent(moduleName, path.join(modulePath, "event"));
30
+ generateState(moduleName, path.join(modulePath, "state"));
31
+ generateScreen(moduleName, path.join(modulePath, "screen"));
32
+ generateImport(moduleName, path.join(modulePath, "import"));
33
+ generateStateFactory(moduleName, path.join(modulePath, "factory"));
34
+ LoggerHelpers.success(`Module ${moduleName} created with full structure.`);
35
+ }
36
+ catch (error) {
37
+ if (error instanceof Error) {
38
+ LoggerHelpers.error(`Error generating module: ${error.message}`);
39
+ }
40
+ else {
41
+ LoggerHelpers.error(`Error generating module: ${error}`);
42
+ }
43
+ process.exit(1);
44
+ }
45
+ }
46
+ function generateBloc(moduleName, blocPath) {
47
+ const blocFilePath = path.join(blocPath, `${moduleName}_bloc.dart`);
48
+ const className = getClassName(moduleName, "bloc");
49
+ const template = `part of '../import/${moduleName}_import.dart';
50
+
51
+ class ${className}Bloc extends BaseBloc {
52
+ ${className}Bloc() : super(
53
+ ${className}Factory(),
54
+ initialState: ${className}InitialState(),
55
+ ) {}
56
+
57
+ @override
58
+ void onDispose() {}
59
+ }
60
+ `;
61
+ writeFile(blocFilePath, template);
62
+ LoggerHelpers.success(`Bloc file ${moduleName}_bloc.dart created in ${blocPath}`);
63
+ }
64
+ function generateEvent(moduleName, eventPath) {
65
+ const eventFilePath = path.join(eventPath, `${moduleName}_event.dart`);
66
+ const className = getClassName(moduleName, "event");
67
+ const template = `part of '../import/${moduleName}_import.dart';
68
+
69
+ class ${className}InitialEvent extends BaseEvent {}
70
+ `;
71
+ writeFile(eventFilePath, template);
72
+ LoggerHelpers.success(`Event file ${moduleName}_event.dart created in ${eventPath}`);
73
+ }
74
+ function generateState(moduleName, statePath) {
75
+ const stateFilePath = path.join(statePath, `${moduleName}_state.dart`);
76
+ const className = getClassName(moduleName, "state");
77
+ const template = `part of '../import/${moduleName}_import.dart';
78
+
79
+ class ${className}InitialState extends RenderDataState {
80
+ ${className}InitialState() : super(null);
81
+ }
82
+ `;
83
+ writeFile(stateFilePath, template);
84
+ LoggerHelpers.success(`State file ${moduleName}_state.dart created in ${statePath}`);
85
+ }
86
+ function generateScreen(moduleName, screenPath) {
87
+ const screenFilePath = path.join(screenPath, `${moduleName}_screen.dart`);
88
+ const className = getClassName(moduleName, "screen");
89
+ const template = `part of '../import/${moduleName}_import.dart';
90
+
91
+ class ${className}Screen extends StatefulWidget {
92
+ final ${className}Bloc bloc;
93
+ const ${className}Screen({super.key, required this.bloc});
94
+
95
+ @override
96
+ _${className}ScreenState createState() => _${className}ScreenState(bloc);
97
+ }
98
+
99
+ class _${className}ScreenState extends BaseScreen<${className}Bloc, ${className}Screen, dynamic> {
100
+ _${className}ScreenState(super.bloc);
101
+
102
+ @override
103
+ Widget buildWidget(BuildContext context, RenderDataState state) {
104
+ return Container();
105
+ }
106
+
107
+ @override
108
+ void listenToState(BuildContext context, BaseState state) {}
109
+ }
110
+ `;
111
+ writeFile(screenFilePath, template);
112
+ LoggerHelpers.success(`Screen file ${moduleName}_screen.dart created in ${screenPath}`);
113
+ }
114
+ function generateImport(moduleName, importPath) {
115
+ const importFilePath = path.join(importPath, `${moduleName}_import.dart`);
116
+ const className = getClassName(moduleName, "import");
117
+ const template = `import 'package:flutter/material.dart';
118
+ import 'package:opticore/opticore.dart';
119
+
120
+ part '../bloc/${moduleName}_bloc.dart';
121
+ part '../event/${moduleName}_event.dart';
122
+ part '../screen/${moduleName}_screen.dart';
123
+ part '../state/${moduleName}_state.dart';
124
+ part '../factory/${moduleName}_factory.dart';
125
+ `;
126
+ writeFile(importFilePath, template);
127
+ LoggerHelpers.success(`Import file ${moduleName}_import.dart created in ${importPath}`);
128
+ }
129
+ function generateStateFactory(moduleName, factoryPath) {
130
+ const factoryFilePath = path.join(factoryPath, `${moduleName}_factory.dart`);
131
+ const className = getClassName(moduleName, "state_factory");
132
+ const template = `part of '../import/${moduleName}_import.dart';
133
+
134
+ class ${className}Factory extends BaseFactory {
135
+ @override
136
+ BaseState getState<M>(M data) {
137
+ return DefaultState();
138
+ }
139
+ }
140
+ `;
141
+ writeFile(factoryFilePath, template);
142
+ LoggerHelpers.success(`State Factory file ${moduleName}_factory.dart created in ${factoryPath}`);
143
+ }
@@ -0,0 +1,63 @@
1
+ import { LoggerHelpers } from "../../utils/services/logger.js";
2
+ import { execCommand } from "../../utils/services/exec.js";
3
+ import { platform } from "os";
4
+ import { validateFlutterProject, validateIosProject, validateAndroidProject } from "../../utils/validators/validation.js";
5
+ export { openIos, openAndroid };
6
+ async function openIos() {
7
+ LoggerHelpers.info("Opening the iOS project in Xcode...");
8
+ // Pre-flight validation
9
+ if (!validateFlutterProject()) {
10
+ process.exit(1);
11
+ }
12
+ if (!validateIosProject()) {
13
+ process.exit(1);
14
+ }
15
+ const command = "open ios/Runner.xcworkspace";
16
+ try {
17
+ await execCommand(command);
18
+ LoggerHelpers.success("Xcode opened successfully.");
19
+ }
20
+ catch (error) {
21
+ if (error instanceof Error) {
22
+ LoggerHelpers.error(`Error while opening Xcode: ${error.message}`);
23
+ }
24
+ else {
25
+ LoggerHelpers.error(`Error while opening Xcode: ${error}`);
26
+ }
27
+ process.exit(1);
28
+ }
29
+ }
30
+ async function openAndroid() {
31
+ LoggerHelpers.info("Opening the Android project in Android Studio...");
32
+ // Pre-flight validation
33
+ if (!validateFlutterProject()) {
34
+ process.exit(1);
35
+ }
36
+ if (!validateAndroidProject()) {
37
+ process.exit(1);
38
+ }
39
+ const osPlatform = platform();
40
+ let command;
41
+ if (osPlatform === "win32") {
42
+ command = "start android";
43
+ }
44
+ else if (osPlatform === "darwin") {
45
+ command = "open -a 'Android Studio' android";
46
+ }
47
+ else {
48
+ command = "xdg-open android";
49
+ }
50
+ try {
51
+ await execCommand(command);
52
+ LoggerHelpers.success("Android Studio opened successfully.");
53
+ }
54
+ catch (error) {
55
+ if (error instanceof Error) {
56
+ LoggerHelpers.error(`Error while opening Android Studio: ${error.message}`);
57
+ }
58
+ else {
59
+ LoggerHelpers.error(`Error while opening Android Studio: ${error}`);
60
+ }
61
+ process.exit(1);
62
+ }
63
+ }
@@ -0,0 +1,46 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { LoggerHelpers } from "../../utils/services/logger.js";
4
+ export { createVscodeSettings, };
5
+ /**
6
+ * Creates a .vscode directory (if it doesn't exist) and writes a settings.json file
7
+ * with recommended Flutter configuration. The Flutter SDK path is set to ".fvm/flutter_sdk".
8
+ */
9
+ async function createVscodeSettings() {
10
+ try {
11
+ const vscodeDir = path.join(process.cwd(), ".vscode");
12
+ const settingsPath = path.join(vscodeDir, "settings.json");
13
+ // Create the .vscode folder using Node.js fs (cross-platform)
14
+ if (!fs.existsSync(vscodeDir)) {
15
+ fs.mkdirSync(vscodeDir, { recursive: true });
16
+ LoggerHelpers.success("Created .vscode directory.");
17
+ }
18
+ else {
19
+ LoggerHelpers.info(".vscode directory already exists.");
20
+ }
21
+ // Define the settings object
22
+ const settings = {
23
+ "dart.flutterSdkPath": ".fvm/flutter_sdk",
24
+ "editor.formatOnSave": true,
25
+ "dart.previewFlutterUiGuides": true,
26
+ "files.exclude": {
27
+ "**/.git": true,
28
+ "**/.DS_Store": true,
29
+ "**/node_modules": true,
30
+ "**/build": true
31
+ }
32
+ };
33
+ // Write settings.json using Node.js fs
34
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf8");
35
+ LoggerHelpers.success("Created .vscode/settings.json with Flutter configuration.");
36
+ }
37
+ catch (error) {
38
+ if (error instanceof Error) {
39
+ LoggerHelpers.error(`Error while creating VSCode settings: ${error.message}`);
40
+ }
41
+ else {
42
+ LoggerHelpers.error(`Error while creating VSCode settings: ${error}`);
43
+ }
44
+ process.exit(1);
45
+ }
46
+ }