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.
- package/CHANGELOG.md +23 -2
- package/CLAUDE.md +239 -0
- package/CODE_QUALITY.md +398 -0
- package/ENHANCEMENTS.md +310 -0
- package/FEATURE_ENHANCEMENTS.md +435 -0
- package/README.md +46 -13
- package/SAFETY_FEATURES.md +396 -0
- package/USAGE.md +225 -41
- package/VERSION_MANAGEMENT.md +438 -0
- package/dist/cli.js +116 -7
- package/dist/commands/build/releases.js +57 -0
- package/dist/commands/buildReleases.js +48 -74
- package/dist/commands/clean/flutter.js +51 -0
- package/dist/commands/clean/ios.js +109 -0
- package/dist/commands/cleanProject.js +34 -4
- package/dist/commands/cleanProjectIos.js +17 -7
- package/dist/commands/config/init.js +54 -0
- package/dist/commands/config/rollback.js +161 -0
- package/dist/commands/generateModule.js +39 -11
- package/dist/commands/init.js +54 -0
- package/dist/commands/openProject.js +17 -0
- package/dist/commands/project/devices.js +188 -0
- package/dist/commands/project/generate.js +143 -0
- package/dist/commands/project/open.js +63 -0
- package/dist/commands/project/setup.js +46 -0
- package/dist/commands/rollback.js +161 -0
- package/dist/commands/setupVSCode.js +27 -21
- package/dist/commands/updateVersions.js +13 -1
- package/dist/commands/version/bump.js +161 -0
- package/dist/commands/version/update.js +91 -0
- package/dist/commands/version.js +161 -0
- package/dist/constants.js +131 -0
- package/dist/utils/backupHelpers.js +88 -0
- package/dist/utils/buildHelpers.js +55 -0
- package/dist/utils/commandHelpers.js +51 -0
- package/dist/utils/configHelpers.js +80 -0
- package/dist/utils/dryRunHelpers.js +103 -0
- package/dist/utils/fileHelpers.js +2 -1
- package/dist/utils/helpers/build.js +55 -0
- package/dist/utils/helpers/dryRun.js +103 -0
- package/dist/utils/helpers/file.js +24 -0
- package/dist/utils/helpers/string.js +3 -0
- package/dist/utils/helpers/version.js +80 -0
- package/dist/utils/services/backup.js +88 -0
- package/dist/utils/services/command.js +51 -0
- package/dist/utils/services/config.js +80 -0
- package/dist/utils/services/exec.js +132 -0
- package/dist/utils/services/logger.js +15 -0
- package/dist/utils/validationHelpers.js +101 -0
- package/dist/utils/validators/validation.js +101 -0
- package/dist/utils/versionHelpers.js +80 -0
- package/package.json +1 -1
- package/src/cli.ts +165 -7
- package/src/commands/build/releases.ts +79 -0
- package/src/commands/clean/flutter.ts +58 -0
- package/src/commands/{cleanProjectIos.ts → clean/ios.ts} +19 -10
- package/src/commands/config/init.ts +63 -0
- package/src/commands/config/rollback.ts +200 -0
- package/src/commands/project/devices.ts +246 -0
- package/src/commands/{generateModule.ts → project/generate.ts} +47 -17
- package/src/commands/{openProject.ts → project/open.ts} +26 -5
- package/src/commands/project/setup.ts +50 -0
- package/src/commands/version/bump.ts +202 -0
- package/src/commands/{updateVersions.ts → version/update.ts} +22 -6
- package/src/constants.ts +144 -0
- package/src/utils/helpers/build.ts +80 -0
- package/src/utils/helpers/dryRun.ts +124 -0
- package/src/utils/{fileHelpers.ts → helpers/file.ts} +3 -2
- package/src/utils/helpers/version.ts +109 -0
- package/src/utils/services/backup.ts +109 -0
- package/src/utils/services/command.ts +76 -0
- package/src/utils/services/config.ts +106 -0
- package/src/utils/{execHelpers.ts → services/exec.ts} +1 -1
- package/src/utils/validators/validation.ts +122 -0
- package/src/commands/buildReleases.ts +0 -102
- package/src/commands/cleanProject.ts +0 -25
- package/src/commands/setupVSCode.ts +0 -44
- /package/src/utils/{stringHelpers.ts → helpers/string.ts} +0 -0
- /package/src/utils/{loggerHelpers.ts → services/logger.ts} +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { LoggerHelpers } from "../services/logger.js";
|
|
4
|
+
|
|
5
|
+
export { parseVersion, incrementVersion, getCurrentVersion, VersionInfo };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Version information structure
|
|
9
|
+
*/
|
|
10
|
+
interface VersionInfo {
|
|
11
|
+
major: number;
|
|
12
|
+
minor: number;
|
|
13
|
+
patch: number;
|
|
14
|
+
buildNumber: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parses a version string in format "X.Y.Z+B"
|
|
19
|
+
* @param versionString - Version string (e.g., "1.2.3+45")
|
|
20
|
+
* @returns Parsed version info
|
|
21
|
+
*/
|
|
22
|
+
function parseVersion(versionString: string): VersionInfo {
|
|
23
|
+
const match = versionString.match(/^(\d+)\.(\d+)\.(\d+)\+(\d+)$/);
|
|
24
|
+
|
|
25
|
+
if (!match) {
|
|
26
|
+
throw new Error(`Invalid version format: ${versionString}. Expected format: X.Y.Z+B (e.g., 1.2.3+45)`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
major: parseInt(match[1], 10),
|
|
31
|
+
minor: parseInt(match[2], 10),
|
|
32
|
+
patch: parseInt(match[3], 10),
|
|
33
|
+
buildNumber: parseInt(match[4], 10),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Increments version based on type
|
|
39
|
+
* @param current - Current version info
|
|
40
|
+
* @param type - Type of increment (major, minor, patch)
|
|
41
|
+
* @param resetIosBuildNumber - Whether to reset iOS build number to 1
|
|
42
|
+
* @returns New version info
|
|
43
|
+
*/
|
|
44
|
+
function incrementVersion(
|
|
45
|
+
current: VersionInfo,
|
|
46
|
+
type: 'major' | 'minor' | 'patch',
|
|
47
|
+
resetIosBuildNumber: boolean = false
|
|
48
|
+
): VersionInfo {
|
|
49
|
+
const newVersion = { ...current };
|
|
50
|
+
|
|
51
|
+
switch (type) {
|
|
52
|
+
case 'major':
|
|
53
|
+
newVersion.major += 1;
|
|
54
|
+
newVersion.minor = 0;
|
|
55
|
+
newVersion.patch = 0;
|
|
56
|
+
newVersion.buildNumber += 1;
|
|
57
|
+
break;
|
|
58
|
+
case 'minor':
|
|
59
|
+
newVersion.minor += 1;
|
|
60
|
+
newVersion.patch = 0;
|
|
61
|
+
newVersion.buildNumber += 1;
|
|
62
|
+
break;
|
|
63
|
+
case 'patch':
|
|
64
|
+
newVersion.patch += 1;
|
|
65
|
+
newVersion.buildNumber += 1;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return newVersion;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Gets current version from pubspec.yaml
|
|
74
|
+
* @returns Current version info
|
|
75
|
+
*/
|
|
76
|
+
function getCurrentVersion(): VersionInfo {
|
|
77
|
+
const pubspecPath = path.join(process.cwd(), "pubspec.yaml");
|
|
78
|
+
|
|
79
|
+
if (!fs.existsSync(pubspecPath)) {
|
|
80
|
+
throw new Error("pubspec.yaml not found. Are you in a Flutter project?");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const pubspecContent = fs.readFileSync(pubspecPath, "utf8");
|
|
84
|
+
const versionMatch = pubspecContent.match(/version:\s*(\d+\.\d+\.\d+\+\d+)/);
|
|
85
|
+
|
|
86
|
+
if (!versionMatch) {
|
|
87
|
+
throw new Error("No version found in pubspec.yaml");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return parseVersion(versionMatch[1]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Formats version info to string
|
|
95
|
+
* @param version - Version info
|
|
96
|
+
* @returns Formatted version string (e.g., "1.2.3+45")
|
|
97
|
+
*/
|
|
98
|
+
export function formatVersion(version: VersionInfo): string {
|
|
99
|
+
return `${version.major}.${version.minor}.${version.patch}+${version.buildNumber}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Gets the next build number from current version
|
|
104
|
+
* @returns Next build number
|
|
105
|
+
*/
|
|
106
|
+
export function getNextBuildNumber(): number {
|
|
107
|
+
const current = getCurrentVersion();
|
|
108
|
+
return current.buildNumber + 1;
|
|
109
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { LoggerHelpers } from "./logger.js";
|
|
4
|
+
|
|
5
|
+
export { createBackup, restoreBackup, cleanupBackups, getBackupPath };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a backup of a file with timestamp
|
|
9
|
+
* Returns the backup path if successful, null otherwise
|
|
10
|
+
*/
|
|
11
|
+
function createBackup(filePath: string): string | null {
|
|
12
|
+
try {
|
|
13
|
+
if (!fs.existsSync(filePath)) {
|
|
14
|
+
LoggerHelpers.warning(`File does not exist, skipping backup: ${filePath}`);
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
19
|
+
const parsedPath = path.parse(filePath);
|
|
20
|
+
const backupPath = path.join(
|
|
21
|
+
parsedPath.dir,
|
|
22
|
+
`.optikit-backup`,
|
|
23
|
+
`${parsedPath.name}_${timestamp}${parsedPath.ext}`
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// Create backup directory if it doesn't exist
|
|
27
|
+
const backupDir = path.dirname(backupPath);
|
|
28
|
+
if (!fs.existsSync(backupDir)) {
|
|
29
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Copy file to backup location
|
|
33
|
+
fs.copyFileSync(filePath, backupPath);
|
|
34
|
+
LoggerHelpers.info(`Backup created: ${backupPath}`);
|
|
35
|
+
|
|
36
|
+
return backupPath;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
LoggerHelpers.error(
|
|
39
|
+
`Failed to create backup: ${error instanceof Error ? error.message : error}`
|
|
40
|
+
);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Restores a file from backup
|
|
47
|
+
*/
|
|
48
|
+
function restoreBackup(originalPath: string, backupPath: string): boolean {
|
|
49
|
+
try {
|
|
50
|
+
if (!fs.existsSync(backupPath)) {
|
|
51
|
+
LoggerHelpers.error(`Backup file not found: ${backupPath}`);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
fs.copyFileSync(backupPath, originalPath);
|
|
56
|
+
LoggerHelpers.success(`Restored from backup: ${originalPath}`);
|
|
57
|
+
return true;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
LoggerHelpers.error(
|
|
60
|
+
`Failed to restore backup: ${error instanceof Error ? error.message : error}`
|
|
61
|
+
);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Cleans up old backups (keeps only the most recent N backups)
|
|
68
|
+
*/
|
|
69
|
+
function cleanupBackups(directory: string, keepCount: number = 5): void {
|
|
70
|
+
try {
|
|
71
|
+
const backupDir = path.join(directory, ".optikit-backup");
|
|
72
|
+
|
|
73
|
+
if (!fs.existsSync(backupDir)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const files = fs.readdirSync(backupDir);
|
|
78
|
+
|
|
79
|
+
// Sort by modification time (newest first)
|
|
80
|
+
const sortedFiles = files
|
|
81
|
+
.map((file) => ({
|
|
82
|
+
name: file,
|
|
83
|
+
path: path.join(backupDir, file),
|
|
84
|
+
mtime: fs.statSync(path.join(backupDir, file)).mtime.getTime(),
|
|
85
|
+
}))
|
|
86
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
87
|
+
|
|
88
|
+
// Delete old backups beyond keepCount
|
|
89
|
+
if (sortedFiles.length > keepCount) {
|
|
90
|
+
const filesToDelete = sortedFiles.slice(keepCount);
|
|
91
|
+
filesToDelete.forEach((file) => {
|
|
92
|
+
fs.unlinkSync(file.path);
|
|
93
|
+
LoggerHelpers.info(`Cleaned up old backup: ${file.name}`);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
LoggerHelpers.warning(
|
|
98
|
+
`Failed to cleanup backups: ${error instanceof Error ? error.message : error}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Gets the backup directory path for a given file
|
|
105
|
+
*/
|
|
106
|
+
function getBackupPath(filePath: string): string {
|
|
107
|
+
const parsedPath = path.parse(filePath);
|
|
108
|
+
return path.join(parsedPath.dir, ".optikit-backup");
|
|
109
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { validateFlutterProject, validateFlutterSdk, validateIosProject, validateAndroidProject } from "../validators/validation.js";
|
|
2
|
+
|
|
3
|
+
export { validateBuildEnvironment, getFlutterCommand };
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validation options for build environment
|
|
7
|
+
*/
|
|
8
|
+
interface ValidationOptions {
|
|
9
|
+
requireFlutterProject?: boolean;
|
|
10
|
+
requireFlutterSdk?: boolean;
|
|
11
|
+
requireIosProject?: boolean;
|
|
12
|
+
requireAndroidProject?: boolean;
|
|
13
|
+
useFvm?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Performs common validation checks for commands
|
|
18
|
+
* Exits with code 1 if any validation fails
|
|
19
|
+
*
|
|
20
|
+
* @param options - Validation options
|
|
21
|
+
* @returns true if all validations pass (won't return false, exits instead)
|
|
22
|
+
*/
|
|
23
|
+
function validateBuildEnvironment(options: ValidationOptions): boolean {
|
|
24
|
+
const {
|
|
25
|
+
requireFlutterProject = true,
|
|
26
|
+
requireFlutterSdk = false,
|
|
27
|
+
requireIosProject = false,
|
|
28
|
+
requireAndroidProject = false,
|
|
29
|
+
useFvm = false,
|
|
30
|
+
} = options;
|
|
31
|
+
|
|
32
|
+
// Flutter project validation
|
|
33
|
+
if (requireFlutterProject && !validateFlutterProject()) {
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Flutter SDK validation (async)
|
|
38
|
+
if (requireFlutterSdk) {
|
|
39
|
+
validateFlutterSdk(useFvm).then((isValid) => {
|
|
40
|
+
if (!isValid) {
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// iOS project validation
|
|
47
|
+
if (requireIosProject && !validateIosProject()) {
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Android project validation
|
|
52
|
+
if (requireAndroidProject && !validateAndroidProject()) {
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Gets the appropriate Flutter command based on FVM usage
|
|
61
|
+
*
|
|
62
|
+
* @param baseCommand - The base Flutter command (e.g., "flutter clean")
|
|
63
|
+
* @param useFvm - Whether to use FVM
|
|
64
|
+
* @returns The command string with or without FVM prefix
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* getFlutterCommand("flutter clean", true) // "fvm flutter clean"
|
|
68
|
+
* getFlutterCommand("flutter pub get", false) // "flutter pub get"
|
|
69
|
+
*/
|
|
70
|
+
function getFlutterCommand(baseCommand: string, useFvm: boolean): string {
|
|
71
|
+
if (useFvm) {
|
|
72
|
+
// Replace "flutter" with "fvm flutter"
|
|
73
|
+
return baseCommand.replace(/^flutter\s/, "fvm flutter ");
|
|
74
|
+
}
|
|
75
|
+
return baseCommand;
|
|
76
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { LoggerHelpers } from "./logger.js";
|
|
4
|
+
|
|
5
|
+
export { loadConfig, saveConfig, getConfigPath, OptiKitConfig };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* OptiKit configuration interface
|
|
9
|
+
*/
|
|
10
|
+
interface OptiKitConfig {
|
|
11
|
+
/** Backup retention count */
|
|
12
|
+
backupRetentionCount?: number;
|
|
13
|
+
/** Default FVM usage */
|
|
14
|
+
useFvmByDefault?: boolean;
|
|
15
|
+
/** Auto-create backups before destructive operations */
|
|
16
|
+
autoBackup?: boolean;
|
|
17
|
+
/** Verbose logging */
|
|
18
|
+
verbose?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Default configuration
|
|
23
|
+
*/
|
|
24
|
+
const DEFAULT_CONFIG: OptiKitConfig = {
|
|
25
|
+
backupRetentionCount: 5,
|
|
26
|
+
useFvmByDefault: false,
|
|
27
|
+
autoBackup: true,
|
|
28
|
+
verbose: false,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Gets the config file path
|
|
33
|
+
* Checks multiple locations in priority order:
|
|
34
|
+
* 1. .optikitrc in current directory
|
|
35
|
+
* 2. .optikitrc in home directory
|
|
36
|
+
*
|
|
37
|
+
* @returns Config file path or null if not found
|
|
38
|
+
*/
|
|
39
|
+
function getConfigPath(): string | null {
|
|
40
|
+
const cwd = process.cwd();
|
|
41
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
42
|
+
|
|
43
|
+
const possiblePaths = [
|
|
44
|
+
path.join(cwd, ".optikitrc"),
|
|
45
|
+
path.join(cwd, ".optikitrc.json"),
|
|
46
|
+
path.join(home, ".optikitrc"),
|
|
47
|
+
path.join(home, ".optikitrc.json"),
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
for (const configPath of possiblePaths) {
|
|
51
|
+
if (fs.existsSync(configPath)) {
|
|
52
|
+
return configPath;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Loads configuration from .optikitrc file
|
|
61
|
+
* Falls back to default configuration if file doesn't exist
|
|
62
|
+
*
|
|
63
|
+
* @returns Merged configuration (defaults + user config)
|
|
64
|
+
*/
|
|
65
|
+
function loadConfig(): OptiKitConfig {
|
|
66
|
+
const configPath = getConfigPath();
|
|
67
|
+
|
|
68
|
+
if (!configPath) {
|
|
69
|
+
return { ...DEFAULT_CONFIG };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const fileContent = fs.readFileSync(configPath, "utf8");
|
|
74
|
+
const userConfig = JSON.parse(fileContent) as Partial<OptiKitConfig>;
|
|
75
|
+
|
|
76
|
+
// Merge with defaults
|
|
77
|
+
return {
|
|
78
|
+
...DEFAULT_CONFIG,
|
|
79
|
+
...userConfig,
|
|
80
|
+
};
|
|
81
|
+
} catch (error) {
|
|
82
|
+
LoggerHelpers.warning(`Failed to load config from ${configPath}, using defaults.`);
|
|
83
|
+
return { ...DEFAULT_CONFIG };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Saves configuration to .optikitrc file in current directory
|
|
89
|
+
*
|
|
90
|
+
* @param config - Configuration to save
|
|
91
|
+
* @returns true if successful, false otherwise
|
|
92
|
+
*/
|
|
93
|
+
function saveConfig(config: OptiKitConfig): boolean {
|
|
94
|
+
const configPath = path.join(process.cwd(), ".optikitrc.json");
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf8");
|
|
98
|
+
LoggerHelpers.success(`Configuration saved to ${configPath}`);
|
|
99
|
+
return true;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
LoggerHelpers.error(
|
|
102
|
+
`Failed to save config: ${error instanceof Error ? error.message : error}`
|
|
103
|
+
);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { exec, spawn } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import { LoggerHelpers } from "
|
|
4
|
+
import { LoggerHelpers } from "./logger.js";
|
|
5
5
|
|
|
6
6
|
export const iosDirectory = path.join(process.cwd(), "ios");
|
|
7
7
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { execCommand } from "../services/exec.js";
|
|
4
|
+
import { LoggerHelpers } from "../services/logger.js";
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
validateFlutterProject,
|
|
8
|
+
validateFlutterSdk,
|
|
9
|
+
validateIosProject,
|
|
10
|
+
validateAndroidProject,
|
|
11
|
+
checkFileExists,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Validates that the current directory is a Flutter project
|
|
16
|
+
* by checking for pubspec.yaml
|
|
17
|
+
*/
|
|
18
|
+
function validateFlutterProject(): boolean {
|
|
19
|
+
const pubspecPath = path.join(process.cwd(), "pubspec.yaml");
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(pubspecPath)) {
|
|
22
|
+
LoggerHelpers.error("Not a Flutter project: pubspec.yaml not found.");
|
|
23
|
+
LoggerHelpers.info("Please run this command from the root of a Flutter project.");
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check if pubspec.yaml contains Flutter SDK
|
|
28
|
+
const pubspecContent = fs.readFileSync(pubspecPath, "utf8");
|
|
29
|
+
if (!pubspecContent.includes("flutter:")) {
|
|
30
|
+
LoggerHelpers.error("Not a Flutter project: pubspec.yaml does not reference Flutter SDK.");
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Validates that Flutter SDK is available (either via FVM or globally)
|
|
39
|
+
*/
|
|
40
|
+
async function validateFlutterSdk(useFvm: boolean = false): Promise<boolean> {
|
|
41
|
+
try {
|
|
42
|
+
if (useFvm) {
|
|
43
|
+
// Check if FVM directory exists
|
|
44
|
+
const fvmPath = path.join(process.cwd(), ".fvm", "flutter_sdk");
|
|
45
|
+
if (!fs.existsSync(fvmPath)) {
|
|
46
|
+
LoggerHelpers.error("FVM Flutter SDK not found at .fvm/flutter_sdk");
|
|
47
|
+
LoggerHelpers.info("Run 'fvm install' or use --disable-fvm flag.");
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Check if fvm command is available
|
|
52
|
+
await execCommand("fvm --version");
|
|
53
|
+
return true;
|
|
54
|
+
} else {
|
|
55
|
+
// Check if global Flutter is available
|
|
56
|
+
await execCommand("flutter --version");
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (useFvm) {
|
|
61
|
+
LoggerHelpers.error("FVM not found. Please install FVM or use --disable-fvm flag.");
|
|
62
|
+
LoggerHelpers.info("Install FVM: https://fvm.app/docs/getting_started/installation");
|
|
63
|
+
} else {
|
|
64
|
+
LoggerHelpers.error("Flutter SDK not found.");
|
|
65
|
+
LoggerHelpers.info("Install Flutter: https://flutter.dev/docs/get-started/install");
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validates that the iOS project exists
|
|
73
|
+
*/
|
|
74
|
+
function validateIosProject(): boolean {
|
|
75
|
+
const iosPath = path.join(process.cwd(), "ios");
|
|
76
|
+
|
|
77
|
+
if (!fs.existsSync(iosPath)) {
|
|
78
|
+
LoggerHelpers.error("iOS project directory not found.");
|
|
79
|
+
LoggerHelpers.info("Run 'flutter create .' to add iOS support.");
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const xcodeProjPath = path.join(iosPath, "Runner.xcodeproj");
|
|
84
|
+
const xcworkspacePath = path.join(iosPath, "Runner.xcworkspace");
|
|
85
|
+
|
|
86
|
+
if (!fs.existsSync(xcodeProjPath) && !fs.existsSync(xcworkspacePath)) {
|
|
87
|
+
LoggerHelpers.error("No Xcode project or workspace found in ios/ directory.");
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Validates that the Android project exists
|
|
96
|
+
*/
|
|
97
|
+
function validateAndroidProject(): boolean {
|
|
98
|
+
const androidPath = path.join(process.cwd(), "android");
|
|
99
|
+
|
|
100
|
+
if (!fs.existsSync(androidPath)) {
|
|
101
|
+
LoggerHelpers.error("Android project directory not found.");
|
|
102
|
+
LoggerHelpers.info("Run 'flutter create .' to add Android support.");
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const buildGradlePath = path.join(androidPath, "build.gradle");
|
|
107
|
+
const buildGradleKtsPath = path.join(androidPath, "build.gradle.kts");
|
|
108
|
+
|
|
109
|
+
if (!fs.existsSync(buildGradlePath) && !fs.existsSync(buildGradleKtsPath)) {
|
|
110
|
+
LoggerHelpers.error("No build.gradle found in android/ directory.");
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Checks if a file exists at the given path
|
|
119
|
+
*/
|
|
120
|
+
function checkFileExists(filePath: string): boolean {
|
|
121
|
+
return fs.existsSync(filePath);
|
|
122
|
+
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import { execCommand } from "../utils/execHelpers.js";
|
|
3
|
-
import { LoggerHelpers } from "../utils/loggerHelpers.js";
|
|
4
|
-
|
|
5
|
-
export {
|
|
6
|
-
buildFlutterApk,
|
|
7
|
-
buildFlutterBundle,
|
|
8
|
-
buildFlutterIos,
|
|
9
|
-
buildFlutterIpa,
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
async function buildFlutterApk(noFvm: boolean) {
|
|
13
|
-
LoggerHelpers.info(
|
|
14
|
-
noFvm
|
|
15
|
-
? "Building Flutter APK without FVM..."
|
|
16
|
-
: "Building Flutter APK with FVM..."
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
const command = noFvm
|
|
20
|
-
? "flutter build apk --release --obfuscate --split-debug-info=build/app/outputs/symbols"
|
|
21
|
-
: "fvm flutter build apk --release --obfuscate --split-debug-info=build/app/outputs/symbols";
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
await execCommand(command);
|
|
25
|
-
LoggerHelpers.success("Flutter APK build successful.");
|
|
26
|
-
} catch (error) {
|
|
27
|
-
if (error instanceof Error) {
|
|
28
|
-
LoggerHelpers.error(`Error during APK build: ${error.message}`);
|
|
29
|
-
} else {
|
|
30
|
-
LoggerHelpers.error(`Error during APK build: ${error}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function buildFlutterBundle(noFvm: boolean) {
|
|
36
|
-
LoggerHelpers.info(
|
|
37
|
-
noFvm
|
|
38
|
-
? "Building Flutter Bundle without FVM..."
|
|
39
|
-
: "Building Flutter Bundle with FVM..."
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const command = noFvm
|
|
43
|
-
? "flutter build appbundle --release --obfuscate --split-debug-info=build/app/outputs/symbols"
|
|
44
|
-
: "fvm flutter build appbundle --release --obfuscate --split-debug-info=build/app/outputs/symbols";
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
await execCommand(command);
|
|
48
|
-
LoggerHelpers.success("Flutter Bundle build successful.");
|
|
49
|
-
} catch (error) {
|
|
50
|
-
if (error instanceof Error) {
|
|
51
|
-
LoggerHelpers.error(`Error during Bundle build: ${error.message}`);
|
|
52
|
-
} else {
|
|
53
|
-
LoggerHelpers.error(`Error during Bundle build: ${error}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function buildFlutterIos(noFvm: boolean) {
|
|
59
|
-
LoggerHelpers.info(
|
|
60
|
-
noFvm
|
|
61
|
-
? "Building Flutter iOS app without FVM..."
|
|
62
|
-
: "Building Flutter iOS app with FVM..."
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
const command = noFvm
|
|
66
|
-
? "flutter build ios --release"
|
|
67
|
-
: "fvm flutter build ios --release";
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
await execCommand(command);
|
|
71
|
-
LoggerHelpers.success("Flutter iOS app build successful.");
|
|
72
|
-
} catch (error) {
|
|
73
|
-
if (error instanceof Error) {
|
|
74
|
-
LoggerHelpers.error(`Error during iOS build: ${error.message}`);
|
|
75
|
-
} else {
|
|
76
|
-
LoggerHelpers.error(`Error during iOS build: ${error}`);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function buildFlutterIpa(noFvm: boolean) {
|
|
82
|
-
LoggerHelpers.info(
|
|
83
|
-
noFvm
|
|
84
|
-
? "Creating release IPA without FVM..."
|
|
85
|
-
: "Creating release IPA with FVM..."
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
const command = noFvm
|
|
89
|
-
? "flutter build ipa --release"
|
|
90
|
-
: "fvm flutter build ipa --release";
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
await execCommand(command);
|
|
94
|
-
LoggerHelpers.success("Release IPA creation successful.");
|
|
95
|
-
} catch (error) {
|
|
96
|
-
if (error instanceof Error) {
|
|
97
|
-
LoggerHelpers.error(`Error during IPA creation: ${error.message}`);
|
|
98
|
-
} else {
|
|
99
|
-
LoggerHelpers.error(`Error during IPA creation: ${error}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { execCommand } from "../utils/execHelpers.js";
|
|
2
|
-
import { LoggerHelpers } from "../utils/loggerHelpers.js";
|
|
3
|
-
|
|
4
|
-
export { cleanProject };
|
|
5
|
-
|
|
6
|
-
async function cleanProject(noFvm: boolean) {
|
|
7
|
-
LoggerHelpers.info(
|
|
8
|
-
noFvm ? "Running clean without FVM..." : "Running clean with FVM..."
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
const command = noFvm
|
|
12
|
-
? "flutter clean && rm -rf pubspec.lock && flutter pub get"
|
|
13
|
-
: "fvm flutter clean && rm -rf pubspec.lock && fvm flutter pub get";
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
await execCommand(command);
|
|
17
|
-
LoggerHelpers.success("Project cleaned successfully.");
|
|
18
|
-
} catch (error) {
|
|
19
|
-
if (error instanceof Error) {
|
|
20
|
-
LoggerHelpers.error(`Error during clean: ${error.message}`);
|
|
21
|
-
} else {
|
|
22
|
-
LoggerHelpers.error(`Error during clean: ${error}`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import { execCommand } from "../utils/execHelpers.js";
|
|
3
|
-
import { LoggerHelpers } from "../utils/loggerHelpers.js";
|
|
4
|
-
|
|
5
|
-
export {
|
|
6
|
-
createVscodeSettings,
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Creates a .vscode directory (if it doesn't exist) and writes a settings.json file
|
|
11
|
-
* with recommended Flutter configuration. The Flutter SDK path is set to ".fvm/flutter_sdk".
|
|
12
|
-
*/
|
|
13
|
-
async function createVscodeSettings(): Promise<void> {
|
|
14
|
-
try {
|
|
15
|
-
// Create the .vscode folder (using -p ensures it won't error if it already exists)
|
|
16
|
-
await execCommand('mkdir -p .vscode');
|
|
17
|
-
LoggerHelpers.success("Created .vscode directory (if not already present).");
|
|
18
|
-
|
|
19
|
-
// Use a heredoc to write the settings.json file
|
|
20
|
-
const command = `
|
|
21
|
-
cat << 'EOF' > .vscode/settings.json
|
|
22
|
-
{
|
|
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
|
-
EOF
|
|
34
|
-
`;
|
|
35
|
-
await execCommand(command);
|
|
36
|
-
LoggerHelpers.success("Created .vscode/settings.json with Flutter configuration.");
|
|
37
|
-
} catch (error) {
|
|
38
|
-
if (error instanceof Error) {
|
|
39
|
-
LoggerHelpers.error(`Error while creating VSCode settings: ${error.message}`);
|
|
40
|
-
} else {
|
|
41
|
-
LoggerHelpers.error(`Error while creating VSCode settings: ${error}`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|