@jatinmourya/ng-init 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.
- package/CHANGELOG.md +129 -0
- package/CONTRIBUTING.md +210 -0
- package/IMPLEMENTATION_SUMMARY.md +411 -0
- package/INSTALLATION.md +375 -0
- package/LICENSE +21 -0
- package/PROJECT_DOCUMENTATION.md +384 -0
- package/QUICK_START.md +252 -0
- package/README.md +300 -0
- package/package.json +56 -0
- package/src/index.js +152 -0
- package/src/runner.js +574 -0
- package/src/templates/templates.js +403 -0
- package/src/utils/compatibility.js +333 -0
- package/src/utils/file-utils.js +232 -0
- package/src/utils/installer.js +247 -0
- package/src/utils/npm-search.js +354 -0
- package/src/utils/profile-manager.js +219 -0
- package/src/utils/prompt-handler.js +393 -0
- package/src/utils/version-checker.js +169 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check if current Node version is compatible with Angular version
|
|
6
|
+
*/
|
|
7
|
+
export function checkNodeCompatibility(currentNodeVersion, requiredNodeVersion) {
|
|
8
|
+
try {
|
|
9
|
+
const isCompatible = semver.satisfies(currentNodeVersion, requiredNodeVersion);
|
|
10
|
+
return {
|
|
11
|
+
compatible: isCompatible,
|
|
12
|
+
current: currentNodeVersion,
|
|
13
|
+
required: requiredNodeVersion
|
|
14
|
+
};
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return {
|
|
17
|
+
compatible: false,
|
|
18
|
+
current: currentNodeVersion,
|
|
19
|
+
required: requiredNodeVersion,
|
|
20
|
+
error: error.message
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Display compatibility status
|
|
27
|
+
*/
|
|
28
|
+
export function displayCompatibilityStatus(compatibility) {
|
|
29
|
+
console.log(chalk.bold.cyan('\nš Compatibility Check\n'));
|
|
30
|
+
console.log(chalk.gray('ā'.repeat(50)));
|
|
31
|
+
|
|
32
|
+
console.log(chalk.white('Current Node.js: ') + chalk.cyan(`v${compatibility.current}`));
|
|
33
|
+
console.log(chalk.white('Required Node.js: ') + chalk.cyan(compatibility.required));
|
|
34
|
+
|
|
35
|
+
if (compatibility.compatible) {
|
|
36
|
+
console.log(chalk.white('Status: ') + chalk.green('ā Compatible'));
|
|
37
|
+
} else {
|
|
38
|
+
console.log(chalk.white('Status: ') + chalk.red('ā Incompatible'));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log(chalk.gray('ā'.repeat(50)) + '\n');
|
|
42
|
+
|
|
43
|
+
return compatibility.compatible;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Find compatible Node versions from available versions
|
|
48
|
+
*/
|
|
49
|
+
export function findCompatibleVersions(availableVersions, requiredRange) {
|
|
50
|
+
try {
|
|
51
|
+
return availableVersions.filter(version => {
|
|
52
|
+
try {
|
|
53
|
+
return semver.satisfies(version, requiredRange);
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}).sort((a, b) => semver.rcompare(a, b)); // Sort descending (newest first)
|
|
58
|
+
} catch (error) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get recommended Node version from range
|
|
65
|
+
*/
|
|
66
|
+
export function getRecommendedNodeVersion(requiredRange) {
|
|
67
|
+
// Parse the range to get recommended version
|
|
68
|
+
try {
|
|
69
|
+
const ranges = requiredRange.split('||').map(r => r.trim());
|
|
70
|
+
|
|
71
|
+
// Try to extract version numbers
|
|
72
|
+
for (const range of ranges) {
|
|
73
|
+
const match = range.match(/(\d+)\./);
|
|
74
|
+
if (match) {
|
|
75
|
+
const major = parseInt(match[1]);
|
|
76
|
+
// Recommend LTS versions
|
|
77
|
+
if (major === 20) return '20.11.0';
|
|
78
|
+
if (major === 18) return '18.19.0';
|
|
79
|
+
if (major === 16) return '16.20.2';
|
|
80
|
+
if (major === 14) return '14.21.3';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return '18.19.0'; // Default to Node 18 LTS
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return '18.19.0';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Angular version to Node.js compatibility matrix
|
|
92
|
+
*/
|
|
93
|
+
export const ANGULAR_NODE_COMPATIBILITY = {
|
|
94
|
+
'18': '^18.19.1 || ^20.11.1 || ^22.0.0',
|
|
95
|
+
'17': '^18.13.0 || ^20.9.0',
|
|
96
|
+
'16': '^16.14.0 || ^18.10.0',
|
|
97
|
+
'15': '^14.20.0 || ^16.13.0 || ^18.10.0',
|
|
98
|
+
'14': '^14.15.0 || ^16.10.0',
|
|
99
|
+
'13': '^12.20.0 || ^14.15.0 || ^16.10.0',
|
|
100
|
+
'12': '^12.20.0 || ^14.15.0',
|
|
101
|
+
'11': '^10.13.0 || ^12.11.0',
|
|
102
|
+
'10': '^10.13.0 || ^12.11.0'
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get Node requirement from compatibility matrix
|
|
107
|
+
*/
|
|
108
|
+
export function getNodeRequirementFromMatrix(angularVersion) {
|
|
109
|
+
const majorVersion = angularVersion.split('.')[0];
|
|
110
|
+
return ANGULAR_NODE_COMPATIBILITY[majorVersion] || '^18.13.0 || ^20.9.0';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validate Angular version format
|
|
115
|
+
*/
|
|
116
|
+
export function isValidAngularVersion(version) {
|
|
117
|
+
return semver.valid(version) !== null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Check if Angular CLI is installed globally
|
|
122
|
+
*/
|
|
123
|
+
export function needsAngularCli(currentAngularCliVersion, targetAngularVersion) {
|
|
124
|
+
if (!currentAngularCliVersion) {
|
|
125
|
+
return {
|
|
126
|
+
needed: true,
|
|
127
|
+
reason: 'Angular CLI is not installed'
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const currentMajor = parseInt(currentAngularCliVersion.split('.')[0]);
|
|
132
|
+
const targetMajor = parseInt(targetAngularVersion.split('.')[0]);
|
|
133
|
+
|
|
134
|
+
if (currentMajor !== targetMajor) {
|
|
135
|
+
return {
|
|
136
|
+
needed: true,
|
|
137
|
+
reason: `Angular CLI version mismatch (current: ${currentMajor}, target: ${targetMajor})`,
|
|
138
|
+
suggestion: `Consider using npx @angular/cli@${targetAngularVersion} instead`
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
needed: false,
|
|
144
|
+
reason: 'Angular CLI version is compatible'
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Library compatibility matrix for Angular versions
|
|
150
|
+
* Maps popular libraries to compatible versions based on Angular major version
|
|
151
|
+
*/
|
|
152
|
+
export const LIBRARY_COMPATIBILITY_MATRIX = {
|
|
153
|
+
'@angular/material': {
|
|
154
|
+
'18': '^18.0.0',
|
|
155
|
+
'17': '^17.0.0',
|
|
156
|
+
'16': '^16.0.0',
|
|
157
|
+
'15': '^15.0.0',
|
|
158
|
+
'14': '^14.0.0',
|
|
159
|
+
'13': '^13.0.0',
|
|
160
|
+
'12': '^12.0.0'
|
|
161
|
+
},
|
|
162
|
+
'@angular/cdk': {
|
|
163
|
+
'18': '^18.0.0',
|
|
164
|
+
'17': '^17.0.0',
|
|
165
|
+
'16': '^16.0.0',
|
|
166
|
+
'15': '^15.0.0',
|
|
167
|
+
'14': '^14.0.0',
|
|
168
|
+
'13': '^13.0.0',
|
|
169
|
+
'12': '^12.0.0'
|
|
170
|
+
},
|
|
171
|
+
'@ng-bootstrap/ng-bootstrap': {
|
|
172
|
+
'18': '^17.0.0',
|
|
173
|
+
'17': '^16.0.0',
|
|
174
|
+
'16': '^14.0.0',
|
|
175
|
+
'15': '^13.0.0',
|
|
176
|
+
'14': '^12.0.0',
|
|
177
|
+
'13': '^10.0.0',
|
|
178
|
+
'12': '^10.0.0'
|
|
179
|
+
},
|
|
180
|
+
'@ngrx/store': {
|
|
181
|
+
'18': '^18.0.0',
|
|
182
|
+
'17': '^17.0.0',
|
|
183
|
+
'16': '^16.0.0',
|
|
184
|
+
'15': '^15.0.0',
|
|
185
|
+
'14': '^14.0.0',
|
|
186
|
+
'13': '^13.0.0',
|
|
187
|
+
'12': '^12.0.0'
|
|
188
|
+
},
|
|
189
|
+
'@ngrx/effects': {
|
|
190
|
+
'18': '^18.0.0',
|
|
191
|
+
'17': '^17.0.0',
|
|
192
|
+
'16': '^16.0.0',
|
|
193
|
+
'15': '^15.0.0',
|
|
194
|
+
'14': '^14.0.0',
|
|
195
|
+
'13': '^13.0.0',
|
|
196
|
+
'12': '^12.0.0'
|
|
197
|
+
},
|
|
198
|
+
'@ngrx/entity': {
|
|
199
|
+
'18': '^18.0.0',
|
|
200
|
+
'17': '^17.0.0',
|
|
201
|
+
'16': '^16.0.0',
|
|
202
|
+
'15': '^15.0.0',
|
|
203
|
+
'14': '^14.0.0',
|
|
204
|
+
'13': '^13.0.0',
|
|
205
|
+
'12': '^12.0.0'
|
|
206
|
+
},
|
|
207
|
+
'@ngrx/store-devtools': {
|
|
208
|
+
'18': '^18.0.0',
|
|
209
|
+
'17': '^17.0.0',
|
|
210
|
+
'16': '^16.0.0',
|
|
211
|
+
'15': '^15.0.0',
|
|
212
|
+
'14': '^14.0.0',
|
|
213
|
+
'13': '^13.0.0',
|
|
214
|
+
'12': '^12.0.0'
|
|
215
|
+
},
|
|
216
|
+
'@angular/pwa': {
|
|
217
|
+
'18': '^18.0.0',
|
|
218
|
+
'17': '^17.0.0',
|
|
219
|
+
'16': '^16.0.0',
|
|
220
|
+
'15': '^15.0.0',
|
|
221
|
+
'14': '^14.0.0',
|
|
222
|
+
'13': '^13.0.0',
|
|
223
|
+
'12': '^12.0.0'
|
|
224
|
+
},
|
|
225
|
+
'@angular/service-worker': {
|
|
226
|
+
'18': '^18.0.0',
|
|
227
|
+
'17': '^17.0.0',
|
|
228
|
+
'16': '^16.0.0',
|
|
229
|
+
'15': '^15.0.0',
|
|
230
|
+
'14': '^14.0.0',
|
|
231
|
+
'13': '^13.0.0',
|
|
232
|
+
'12': '^12.0.0'
|
|
233
|
+
},
|
|
234
|
+
'@angular/fire': {
|
|
235
|
+
'18': '^18.0.0',
|
|
236
|
+
'17': '^17.0.0',
|
|
237
|
+
'16': '^7.6.0',
|
|
238
|
+
'15': '^7.5.0',
|
|
239
|
+
'14': '^7.4.0',
|
|
240
|
+
'13': '^7.2.0',
|
|
241
|
+
'12': '^7.0.0'
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get compatible version for a library based on Angular version
|
|
247
|
+
*/
|
|
248
|
+
export function getCompatibleLibraryVersion(libraryName, angularVersion) {
|
|
249
|
+
const angularMajor = angularVersion.split('.')[0];
|
|
250
|
+
|
|
251
|
+
// Check if library is in compatibility matrix
|
|
252
|
+
if (LIBRARY_COMPATIBILITY_MATRIX[libraryName]) {
|
|
253
|
+
const compatibleVersion = LIBRARY_COMPATIBILITY_MATRIX[libraryName][angularMajor];
|
|
254
|
+
if (compatibleVersion) {
|
|
255
|
+
return compatibleVersion;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// For Angular-scoped packages not in matrix, try to match version
|
|
260
|
+
if (libraryName.startsWith('@angular/')) {
|
|
261
|
+
return `^${angularMajor}.0.0`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Default to latest for other packages
|
|
265
|
+
return 'latest';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Resolve library versions for compatibility with Angular
|
|
270
|
+
*/
|
|
271
|
+
export function resolveLibraryVersions(libraries, angularVersion) {
|
|
272
|
+
return libraries.map(lib => {
|
|
273
|
+
const requestedVersion = lib.version || 'latest';
|
|
274
|
+
|
|
275
|
+
// If version is 'latest', try to find compatible version
|
|
276
|
+
if (requestedVersion === 'latest') {
|
|
277
|
+
const compatibleVersion = getCompatibleLibraryVersion(lib.name, angularVersion);
|
|
278
|
+
return {
|
|
279
|
+
...lib,
|
|
280
|
+
version: compatibleVersion,
|
|
281
|
+
originalVersion: requestedVersion,
|
|
282
|
+
adjusted: compatibleVersion !== 'latest'
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// If a specific version is requested, keep it but flag potential incompatibility
|
|
287
|
+
return {
|
|
288
|
+
...lib,
|
|
289
|
+
adjusted: false
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Check if a library version is compatible with Angular version using semver
|
|
296
|
+
*/
|
|
297
|
+
export function checkLibraryCompatibility(peerDependency, angularVersion) {
|
|
298
|
+
if (!peerDependency || peerDependency === 'No Angular peer dependency') {
|
|
299
|
+
return { compatible: true, reason: 'No Angular peer dependency specified' };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
const angularMajor = angularVersion.split('.')[0];
|
|
304
|
+
|
|
305
|
+
// Check if the peer dependency includes the Angular major version
|
|
306
|
+
const patterns = [
|
|
307
|
+
`^${angularMajor}.`,
|
|
308
|
+
`~${angularMajor}.`,
|
|
309
|
+
`>=${angularMajor}.`,
|
|
310
|
+
`${angularMajor}.x`,
|
|
311
|
+
`${angularMajor}.0.0`
|
|
312
|
+
];
|
|
313
|
+
|
|
314
|
+
const isCompatible = patterns.some(pattern => peerDependency.includes(pattern));
|
|
315
|
+
|
|
316
|
+
if (isCompatible) {
|
|
317
|
+
return {
|
|
318
|
+
compatible: true,
|
|
319
|
+
reason: `Peer dependency '${peerDependency}' is compatible with Angular ${angularVersion}`
|
|
320
|
+
};
|
|
321
|
+
} else {
|
|
322
|
+
return {
|
|
323
|
+
compatible: false,
|
|
324
|
+
reason: `Peer dependency requires '${peerDependency}' but Angular ${angularVersion} is being used`
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
} catch (error) {
|
|
328
|
+
return {
|
|
329
|
+
compatible: false,
|
|
330
|
+
reason: `Error checking compatibility: ${error.message}`
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Initialize Git repository
|
|
9
|
+
*/
|
|
10
|
+
export async function initGitRepo(projectPath) {
|
|
11
|
+
const spinner = ora('Initializing Git repository...').start();
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
await execa('git', ['init'], { cwd: projectPath });
|
|
15
|
+
spinner.succeed('Git repository initialized');
|
|
16
|
+
return true;
|
|
17
|
+
} catch (error) {
|
|
18
|
+
spinner.fail('Failed to initialize Git repository');
|
|
19
|
+
console.error(chalk.red(error.message));
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create .gitignore file
|
|
26
|
+
*/
|
|
27
|
+
export async function createGitignore(projectPath, content) {
|
|
28
|
+
try {
|
|
29
|
+
const gitignorePath = path.join(projectPath, '.gitignore');
|
|
30
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
31
|
+
console.log(chalk.green('ā Created .gitignore'));
|
|
32
|
+
return true;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(chalk.red('Failed to create .gitignore:'), error.message);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create initial commit
|
|
41
|
+
*/
|
|
42
|
+
export async function createInitialCommit(projectPath, message) {
|
|
43
|
+
const spinner = ora('Creating initial commit...').start();
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
await execa('git', ['add', '.'], { cwd: projectPath });
|
|
47
|
+
await execa('git', ['commit', '-m', message], { cwd: projectPath });
|
|
48
|
+
spinner.succeed('Initial commit created');
|
|
49
|
+
return true;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
spinner.fail('Failed to create initial commit');
|
|
52
|
+
console.error(chalk.red(error.message));
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Create project folders
|
|
59
|
+
*/
|
|
60
|
+
export async function createProjectFolders(projectPath, folders) {
|
|
61
|
+
const spinner = ora('Creating project structure...').start();
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
for (const folder of folders) {
|
|
65
|
+
const folderPath = path.join(projectPath, folder);
|
|
66
|
+
await fs.mkdir(folderPath, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
spinner.succeed('Project structure created');
|
|
69
|
+
return true;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
spinner.fail('Failed to create project structure');
|
|
72
|
+
console.error(chalk.red(error.message));
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Create project files
|
|
79
|
+
*/
|
|
80
|
+
export async function createProjectFiles(projectPath, files) {
|
|
81
|
+
try {
|
|
82
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
83
|
+
const fullPath = path.join(projectPath, filePath);
|
|
84
|
+
const dir = path.dirname(fullPath);
|
|
85
|
+
|
|
86
|
+
// Ensure directory exists
|
|
87
|
+
await fs.mkdir(dir, { recursive: true });
|
|
88
|
+
|
|
89
|
+
// Write file
|
|
90
|
+
const fileContent = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
|
91
|
+
await fs.writeFile(fullPath, fileContent, 'utf-8');
|
|
92
|
+
}
|
|
93
|
+
console.log(chalk.green(`ā Created ${Object.keys(files).length} file(s)`));
|
|
94
|
+
return true;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error(chalk.red('Failed to create project files:'), error.message);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Create README.md
|
|
103
|
+
*/
|
|
104
|
+
export async function createReadme(projectPath, content) {
|
|
105
|
+
try {
|
|
106
|
+
const readmePath = path.join(projectPath, 'README.md');
|
|
107
|
+
await fs.writeFile(readmePath, content, 'utf-8');
|
|
108
|
+
console.log(chalk.green('ā Created README.md'));
|
|
109
|
+
return true;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(chalk.red('Failed to create README.md:'), error.message);
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Create CHANGELOG.md
|
|
118
|
+
*/
|
|
119
|
+
export async function createChangelog(projectPath, content) {
|
|
120
|
+
try {
|
|
121
|
+
const changelogPath = path.join(projectPath, 'CHANGELOG.md');
|
|
122
|
+
await fs.writeFile(changelogPath, content, 'utf-8');
|
|
123
|
+
console.log(chalk.green('ā Created CHANGELOG.md'));
|
|
124
|
+
return true;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error(chalk.red('Failed to create CHANGELOG.md:'), error.message);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Check if directory exists and is empty
|
|
133
|
+
*/
|
|
134
|
+
export async function isDirectoryEmpty(dirPath) {
|
|
135
|
+
try {
|
|
136
|
+
const files = await fs.readdir(dirPath);
|
|
137
|
+
return files.length === 0;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
// Directory doesn't exist
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Create directory if it doesn't exist
|
|
146
|
+
*/
|
|
147
|
+
export async function ensureDirectory(dirPath) {
|
|
148
|
+
try {
|
|
149
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
150
|
+
return true;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(chalk.red('Failed to create directory:'), error.message);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Validate directory name
|
|
159
|
+
*/
|
|
160
|
+
export function validateDirectoryName(name) {
|
|
161
|
+
// Check for invalid characters
|
|
162
|
+
const invalidChars = /[<>:"|?*\x00-\x1f]/;
|
|
163
|
+
if (invalidChars.test(name)) {
|
|
164
|
+
return 'Directory name contains invalid characters';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check for reserved names (Windows)
|
|
168
|
+
const reserved = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'];
|
|
169
|
+
if (reserved.includes(name.toUpperCase())) {
|
|
170
|
+
return 'Directory name is reserved';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Check for valid length
|
|
174
|
+
if (name.length === 0) {
|
|
175
|
+
return 'Directory name cannot be empty';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (name.length > 255) {
|
|
179
|
+
return 'Directory name is too long';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Update package.json scripts
|
|
187
|
+
*/
|
|
188
|
+
export async function updatePackageJsonScripts(projectPath, scripts) {
|
|
189
|
+
try {
|
|
190
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
191
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
192
|
+
|
|
193
|
+
packageJson.scripts = {
|
|
194
|
+
...packageJson.scripts,
|
|
195
|
+
...scripts
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8');
|
|
199
|
+
console.log(chalk.green('ā Updated package.json scripts'));
|
|
200
|
+
return true;
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(chalk.red('Failed to update package.json:'), error.message);
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Read package.json
|
|
209
|
+
*/
|
|
210
|
+
export async function readPackageJson(projectPath) {
|
|
211
|
+
try {
|
|
212
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
213
|
+
const content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
214
|
+
return JSON.parse(content);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Write package.json
|
|
222
|
+
*/
|
|
223
|
+
export async function writePackageJson(projectPath, content) {
|
|
224
|
+
try {
|
|
225
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
226
|
+
await fs.writeFile(packageJsonPath, JSON.stringify(content, null, 2), 'utf-8');
|
|
227
|
+
return true;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error(chalk.red('Failed to write package.json:'), error.message);
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|