forked from salesforcecli/plugin-omnistudio-migration-tool
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprojectPathUtil.ts
More file actions
181 lines (161 loc) · 6.11 KB
/
Copy pathprojectPathUtil.ts
File metadata and controls
181 lines (161 loc) · 6.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import * as fs from 'fs';
import * as path from 'path';
import { Messages } from '@salesforce/core';
import { Logger } from './logger';
import { sfProject } from './sfcli/project/sfProject';
import { PromptUtil } from './promptUtil';
export const EXISTING_MODE = 'existing';
export const EMPTY_MODE = 'empty';
export const YES_SHORT = 'y';
export const NO_SHORT = 'n';
export const YES_LONG = 'yes';
export const NO_LONG = 'no';
// Helper to create SFDX project if needed
export function createSfdxProject(folderPath: string): void {
const projectName = path.basename(folderPath);
const parentDir = path.dirname(folderPath);
sfProject.create(projectName, parentDir);
}
export function isSfdxProject(folderPath: string): boolean {
const sfdxProjectJson = path.join(folderPath, 'sfdx-project.json');
return fs.existsSync(sfdxProjectJson);
}
export class ProjectPathUtil {
/**
* Gets project path with enhanced validation and user prompts
*
* @param messages - Messages object for internationalization
* @param enableRetrieval - Whether to enable the retrieval option for empty projects
* @returns Promise<string> - The validated project path
*/
public static async getProjectPath(messages: Messages<string>, enableRetrieval = true): Promise<string> {
const askWithTimeout = PromptUtil.askWithTimeOut(messages);
const mode = await ProjectPathUtil.promptForProjectType(messages, askWithTimeout);
if (mode === EMPTY_MODE && enableRetrieval) {
await ProjectPathUtil.promptForRetrieval(messages, askWithTimeout);
}
return ProjectPathUtil.promptForProjectPath(messages, askWithTimeout, mode);
}
/**
* Prompts user to choose between existing or empty project
*/
private static async promptForProjectType(
messages: Messages<string>,
askWithTimeout: (promptFn: (...args: unknown[]) => Promise<unknown>, ...args: unknown[]) => Promise<string>
): Promise<string> {
let validResponse = false;
let mode = EXISTING_MODE;
while (!validResponse) {
try {
const resp = await askWithTimeout(Logger.prompt.bind(Logger), messages.getMessage('existingApexPrompt'));
const response = typeof resp === 'string' ? resp.trim().toLowerCase() : '';
if (response === YES_SHORT || response === YES_LONG) {
mode = EXISTING_MODE;
validResponse = true;
} else if (response === NO_SHORT || response === NO_LONG) {
mode = EMPTY_MODE;
validResponse = true;
} else {
Logger.error(messages.getMessage('invalidYesNoResponse'));
}
} catch (err) {
Logger.error(messages.getMessage('requestTimedOut'));
process.exit(1);
}
}
return mode;
}
/**
* Prompts user to confirm if they want to retrieve APEX classes
*/
private static async promptForRetrieval(
messages: Messages<string>,
askWithTimeout: (promptFn: (...args: unknown[]) => Promise<unknown>, ...args: unknown[]) => Promise<string>
): Promise<void> {
let validResponse = false;
while (!validResponse) {
try {
const resp = await askWithTimeout(Logger.prompt.bind(Logger), messages.getMessage('retrieveApexPrompt'));
const response = typeof resp === 'string' ? resp.trim().toLowerCase() : '';
if (response === YES_SHORT || response === YES_LONG) {
validResponse = true;
} else if (response === NO_SHORT || response === NO_LONG) {
Logger.error(messages.getMessage('operationCancelled'));
process.exit(0);
} else {
Logger.error(messages.getMessage('invalidYesNoResponse'));
}
} catch (err) {
Logger.error(messages.getMessage('requestTimedOut'));
process.exit(1);
}
}
}
/**
* Prompts user for project path and validates it
*/
private static async promptForProjectPath(
messages: Messages<string>,
askWithTimeout: (promptFn: (...args: unknown[]) => Promise<unknown>, ...args: unknown[]) => Promise<string>,
mode: string
): Promise<string> {
let gotValidPath = false;
let folderPath = '';
while (!gotValidPath) {
folderPath = await ProjectPathUtil.getFolderPathFromUser(messages, askWithTimeout, mode);
if (ProjectPathUtil.isValidFolderPath(folderPath, mode, messages)) {
if (mode === EMPTY_MODE) {
createSfdxProject(folderPath);
}
gotValidPath = true;
}
}
return folderPath;
}
/**
* Gets folder path input from user
*/
private static async getFolderPathFromUser(
messages: Messages<string>,
askWithTimeout: (promptFn: (...args: unknown[]) => Promise<unknown>, ...args: unknown[]) => Promise<string>,
mode: string
): Promise<string> {
try {
const resp = await askWithTimeout(
Logger.prompt.bind(Logger),
mode === EXISTING_MODE
? messages.getMessage('enterExistingProjectPath')
: messages.getMessage('enterEmptyProjectPath')
);
return typeof resp === 'string' ? path.resolve(resp.trim()) : '';
} catch (err) {
Logger.error(messages.getMessage('requestTimedOut'));
process.exit(1);
}
}
/**
* Validates the folder path based on mode
*/
private static isValidFolderPath(folderPath: string, mode: string, messages: Messages<string>): boolean {
if (!fs.existsSync(folderPath) || !fs.lstatSync(folderPath).isDirectory()) {
Logger.error(messages.getMessage('invalidProjectFolderPath'));
return false;
}
// Check if folder path ends with restricted names (case insensitive)
const restrictedFolderNames = ['labels', 'messagechannels', 'lwc'];
const folderName = path.basename(folderPath);
if (restrictedFolderNames.includes(folderName.toLowerCase())) {
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
return false;
}
if (mode === EMPTY_MODE && fs.readdirSync(folderPath).length > 0) {
Logger.error(messages.getMessage('notEmptyProjectFolderPath'));
return false;
}
if (mode === EXISTING_MODE && !isSfdxProject(folderPath)) {
Logger.error(messages.getMessage('notSfdxProjectFolderPath'));
return false;
}
return true;
}
}