@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,247 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { platform } from 'os';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Install Node.js using winget (Windows)
|
|
8
|
+
*/
|
|
9
|
+
export async function installNodeWithWinget(version = 'LTS') {
|
|
10
|
+
const spinner = ora('Installing Node.js with winget...').start();
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const packageId = version === 'LTS' ? 'OpenJS.NodeJS.LTS' : 'OpenJS.NodeJS';
|
|
14
|
+
await execa('winget', ['install', packageId], { stdio: 'inherit' });
|
|
15
|
+
|
|
16
|
+
spinner.succeed('Node.js installed successfully');
|
|
17
|
+
return true;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
spinner.fail('Failed to install Node.js');
|
|
20
|
+
console.error(chalk.red(error.message));
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get nvm installation instructions based on OS
|
|
27
|
+
*/
|
|
28
|
+
export function getNvmInstallInstructions() {
|
|
29
|
+
const os = platform();
|
|
30
|
+
|
|
31
|
+
if (os === 'win32') {
|
|
32
|
+
return {
|
|
33
|
+
os: 'Windows',
|
|
34
|
+
method: 'Download and install',
|
|
35
|
+
url: 'https://github.com/coreybutler/nvm-windows/releases',
|
|
36
|
+
command: null,
|
|
37
|
+
description: 'Download the nvm-setup.zip file from the releases page and run the installer.'
|
|
38
|
+
};
|
|
39
|
+
} else if (os === 'darwin') {
|
|
40
|
+
return {
|
|
41
|
+
os: 'macOS',
|
|
42
|
+
method: 'Using curl',
|
|
43
|
+
url: 'https://github.com/nvm-sh/nvm',
|
|
44
|
+
command: 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash',
|
|
45
|
+
description: 'Run the curl command in your terminal to install nvm.'
|
|
46
|
+
};
|
|
47
|
+
} else {
|
|
48
|
+
return {
|
|
49
|
+
os: 'Linux',
|
|
50
|
+
method: 'Using curl or wget',
|
|
51
|
+
url: 'https://github.com/nvm-sh/nvm',
|
|
52
|
+
command: 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash',
|
|
53
|
+
alternativeCommand: 'wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash',
|
|
54
|
+
description: 'Run either the curl or wget command in your terminal to install nvm.'
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Display nvm installation guide
|
|
61
|
+
*/
|
|
62
|
+
export function displayNvmInstallGuide() {
|
|
63
|
+
const instructions = getNvmInstallInstructions();
|
|
64
|
+
|
|
65
|
+
console.log(chalk.bold.yellow('\nš nvm Installation Guide\n'));
|
|
66
|
+
console.log(chalk.gray('ā'.repeat(50)));
|
|
67
|
+
console.log(chalk.white('Operating System: ') + chalk.cyan(instructions.os));
|
|
68
|
+
console.log(chalk.white('Method: ') + chalk.cyan(instructions.method));
|
|
69
|
+
console.log(chalk.white('URL: ') + chalk.blue(instructions.url));
|
|
70
|
+
|
|
71
|
+
if (instructions.command) {
|
|
72
|
+
console.log(chalk.white('\nCommand:'));
|
|
73
|
+
console.log(chalk.green(` ${instructions.command}`));
|
|
74
|
+
|
|
75
|
+
if (instructions.alternativeCommand) {
|
|
76
|
+
console.log(chalk.white('\nAlternative:'));
|
|
77
|
+
console.log(chalk.green(` ${instructions.alternativeCommand}`));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(chalk.white('\nDescription:'));
|
|
82
|
+
console.log(chalk.gray(` ${instructions.description}`));
|
|
83
|
+
console.log(chalk.gray('ā'.repeat(50)) + '\n');
|
|
84
|
+
|
|
85
|
+
console.log(chalk.yellow('š” Benefits of using nvm:'));
|
|
86
|
+
console.log(chalk.gray(' ⢠Manage multiple Node.js versions'));
|
|
87
|
+
console.log(chalk.gray(' ⢠Switch between versions easily'));
|
|
88
|
+
console.log(chalk.gray(' ⢠No admin/sudo required for installations'));
|
|
89
|
+
console.log(chalk.gray(' ⢠Project-specific Node versions\n'));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Install npm package globally
|
|
94
|
+
*/
|
|
95
|
+
export async function installGlobalPackage(packageName, version = 'latest') {
|
|
96
|
+
const spinner = ora(`Installing ${packageName}@${version}...`).start();
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const packageSpec = version === 'latest' ? packageName : `${packageName}@${version}`;
|
|
100
|
+
await execa('npm', ['install', '-g', packageSpec], { stdio: 'inherit' });
|
|
101
|
+
|
|
102
|
+
spinner.succeed(`${packageName} installed successfully`);
|
|
103
|
+
return true;
|
|
104
|
+
} catch (error) {
|
|
105
|
+
spinner.fail(`Failed to install ${packageName}`);
|
|
106
|
+
console.error(chalk.red(error.message));
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Install npm packages in project
|
|
113
|
+
*/
|
|
114
|
+
export async function installPackages(packages, projectPath, dev = false) {
|
|
115
|
+
const spinner = ora(`Installing ${packages.length} package(s)...`).start();
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const args = ['install'];
|
|
119
|
+
if (dev) args.push('--save-dev');
|
|
120
|
+
args.push(...packages);
|
|
121
|
+
|
|
122
|
+
await execa('npm', args, {
|
|
123
|
+
cwd: projectPath,
|
|
124
|
+
stdio: 'inherit'
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
spinner.succeed('Packages installed successfully');
|
|
128
|
+
return true;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
spinner.warn('Failed with strict dependencies, retrying with --legacy-peer-deps...');
|
|
131
|
+
|
|
132
|
+
// Retry with --legacy-peer-deps flag
|
|
133
|
+
try {
|
|
134
|
+
const args = ['install', '--legacy-peer-deps'];
|
|
135
|
+
if (dev) args.push('--save-dev');
|
|
136
|
+
args.push(...packages);
|
|
137
|
+
|
|
138
|
+
await execa('npm', args, {
|
|
139
|
+
cwd: projectPath,
|
|
140
|
+
stdio: 'inherit'
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
spinner.succeed('Packages installed successfully with --legacy-peer-deps');
|
|
144
|
+
console.log(chalk.yellow('ā ļø Note: Installed with --legacy-peer-deps flag due to peer dependency conflicts'));
|
|
145
|
+
return true;
|
|
146
|
+
} catch (retryError) {
|
|
147
|
+
spinner.fail('Failed to install packages');
|
|
148
|
+
console.error(chalk.red(retryError.message));
|
|
149
|
+
console.log(chalk.yellow('\nš” Tip: You can try installing manually with:'));
|
|
150
|
+
console.log(chalk.cyan(` cd ${projectPath}`));
|
|
151
|
+
console.log(chalk.cyan(` npm install ${packages.join(' ')} --force`));
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Initialize npm project
|
|
159
|
+
*/
|
|
160
|
+
export async function initNpmProject(projectPath) {
|
|
161
|
+
try {
|
|
162
|
+
await execa('npm', ['init', '-y'], {
|
|
163
|
+
cwd: projectPath,
|
|
164
|
+
stdio: 'inherit'
|
|
165
|
+
});
|
|
166
|
+
return true;
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error(chalk.red('Failed to initialize npm project'));
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Run npm install in project
|
|
175
|
+
*/
|
|
176
|
+
export async function runNpmInstall(projectPath) {
|
|
177
|
+
const spinner = ora('Installing dependencies...').start();
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
await execa('npm', ['install'], {
|
|
181
|
+
cwd: projectPath,
|
|
182
|
+
stdio: 'inherit'
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
spinner.succeed('Dependencies installed successfully');
|
|
186
|
+
return true;
|
|
187
|
+
} catch (error) {
|
|
188
|
+
spinner.warn('Failed with strict dependencies, retrying with --legacy-peer-deps...');
|
|
189
|
+
|
|
190
|
+
// Retry with --legacy-peer-deps flag
|
|
191
|
+
try {
|
|
192
|
+
await execa('npm', ['install', '--legacy-peer-deps'], {
|
|
193
|
+
cwd: projectPath,
|
|
194
|
+
stdio: 'inherit'
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
spinner.succeed('Dependencies installed successfully with --legacy-peer-deps');
|
|
198
|
+
console.log(chalk.yellow('ā ļø Note: Installed with --legacy-peer-deps flag due to peer dependency conflicts'));
|
|
199
|
+
return true;
|
|
200
|
+
} catch (retryError) {
|
|
201
|
+
spinner.fail('Failed to install dependencies');
|
|
202
|
+
console.error(chalk.red(retryError.message));
|
|
203
|
+
console.log(chalk.yellow('\nš” Tip: You can try installing manually with:'));
|
|
204
|
+
console.log(chalk.cyan(` cd ${projectPath}`));
|
|
205
|
+
console.log(chalk.cyan(` npm install --force`));
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Install Angular CLI globally
|
|
213
|
+
*/
|
|
214
|
+
export async function installAngularCli(version = 'latest') {
|
|
215
|
+
return installGlobalPackage('@angular/cli', version);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Create Angular project using CLI
|
|
220
|
+
*/
|
|
221
|
+
export async function createAngularProject(projectName, angularVersion, options = {}) {
|
|
222
|
+
const spinner = ora(`Creating Angular project: ${projectName}...`).start();
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
const args = ['new', projectName];
|
|
226
|
+
|
|
227
|
+
// Add options
|
|
228
|
+
if (options.skipInstall) args.push('--skip-install');
|
|
229
|
+
if (options.routing !== undefined) args.push(`--routing=${options.routing}`);
|
|
230
|
+
if (options.style) args.push(`--style=${options.style}`);
|
|
231
|
+
if (options.strict !== undefined) args.push(`--strict=${options.strict}`);
|
|
232
|
+
if (options.standalone !== undefined) args.push(`--standalone=${options.standalone}`);
|
|
233
|
+
|
|
234
|
+
const cliCommand = angularVersion ? `@angular/cli@${angularVersion}` : '@angular/cli';
|
|
235
|
+
|
|
236
|
+
await execa('npx', [cliCommand, ...args], {
|
|
237
|
+
stdio: 'inherit'
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
spinner.succeed(`Angular project ${projectName} created successfully`);
|
|
241
|
+
return true;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
spinner.fail('Failed to create Angular project');
|
|
244
|
+
console.error(chalk.red(error.message));
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import debounce from 'lodash.debounce';
|
|
3
|
+
|
|
4
|
+
const NPM_REGISTRY_URL = 'https://registry.npmjs.org';
|
|
5
|
+
const NPM_SEARCH_URL = 'https://registry.npmjs.org/-/v1/search';
|
|
6
|
+
const NPM_DOWNLOADS_URL = 'https://api.npmjs.org/downloads/point/last-week';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Search npm packages
|
|
10
|
+
*/
|
|
11
|
+
export async function searchNpmPackages(query, size = 10) {
|
|
12
|
+
try {
|
|
13
|
+
const response = await axios.get(NPM_SEARCH_URL, {
|
|
14
|
+
params: {
|
|
15
|
+
text: query,
|
|
16
|
+
size: size
|
|
17
|
+
},
|
|
18
|
+
timeout: 5000
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return response.data.objects.map(obj => ({
|
|
22
|
+
name: obj.package.name,
|
|
23
|
+
version: obj.package.version,
|
|
24
|
+
description: obj.package.description || 'No description',
|
|
25
|
+
author: obj.package.publisher?.username || 'Unknown',
|
|
26
|
+
date: obj.package.date,
|
|
27
|
+
verified: obj.package.publisher?.verified || false
|
|
28
|
+
}));
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Error searching npm packages:', error.message);
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get package details from npm registry
|
|
37
|
+
*/
|
|
38
|
+
export async function getPackageDetails(packageName) {
|
|
39
|
+
try {
|
|
40
|
+
const response = await axios.get(`${NPM_REGISTRY_URL}/${packageName}`, {
|
|
41
|
+
timeout: 5000
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const latestVersion = response.data['dist-tags']?.latest;
|
|
45
|
+
const versions = Object.keys(response.data.versions || {});
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
name: response.data.name,
|
|
49
|
+
description: response.data.description || 'No description',
|
|
50
|
+
latestVersion: latestVersion,
|
|
51
|
+
versions: versions,
|
|
52
|
+
homepage: response.data.homepage,
|
|
53
|
+
repository: response.data.repository,
|
|
54
|
+
license: response.data.license,
|
|
55
|
+
keywords: response.data.keywords || []
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error.response?.status === 404) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get package download statistics
|
|
67
|
+
*/
|
|
68
|
+
export async function getPackageDownloads(packageName) {
|
|
69
|
+
try {
|
|
70
|
+
const response = await axios.get(`${NPM_DOWNLOADS_URL}/${packageName}`, {
|
|
71
|
+
timeout: 5000
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return response.data.downloads;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validate if a package exists on npm
|
|
82
|
+
*/
|
|
83
|
+
export async function validatePackage(packageName) {
|
|
84
|
+
const details = await getPackageDetails(packageName);
|
|
85
|
+
return details !== null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get enhanced package info (details + downloads)
|
|
90
|
+
*/
|
|
91
|
+
export async function getEnhancedPackageInfo(packageName) {
|
|
92
|
+
try {
|
|
93
|
+
const [details, downloads] = await Promise.all([
|
|
94
|
+
getPackageDetails(packageName),
|
|
95
|
+
getPackageDownloads(packageName)
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
if (!details) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
...details,
|
|
104
|
+
weeklyDownloads: downloads
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error(`Error getting package info for ${packageName}:`, error.message);
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Format download count for display
|
|
114
|
+
*/
|
|
115
|
+
export function formatDownloads(downloads) {
|
|
116
|
+
if (downloads >= 1000000) {
|
|
117
|
+
return `${(downloads / 1000000).toFixed(1)}M`;
|
|
118
|
+
} else if (downloads >= 1000) {
|
|
119
|
+
return `${(downloads / 1000).toFixed(1)}K`;
|
|
120
|
+
}
|
|
121
|
+
return downloads.toString();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Debounced search function for autocomplete
|
|
126
|
+
*/
|
|
127
|
+
export const debouncedSearch = debounce(async (query, callback) => {
|
|
128
|
+
if (!query || query.length < 2) {
|
|
129
|
+
callback([]);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const results = await searchNpmPackages(query, 10);
|
|
134
|
+
callback(results);
|
|
135
|
+
}, 300);
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get all versions of Angular CLI
|
|
139
|
+
*/
|
|
140
|
+
export async function getAngularVersions() {
|
|
141
|
+
try {
|
|
142
|
+
const response = await axios.get(`${NPM_REGISTRY_URL}/@angular/cli`, {
|
|
143
|
+
timeout: 10000
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const versions = Object.keys(response.data.versions || {})
|
|
147
|
+
.filter(v => !v.includes('rc') && !v.includes('beta') && !v.includes('next'))
|
|
148
|
+
.sort((a, b) => {
|
|
149
|
+
// Sort in descending order (newest first)
|
|
150
|
+
const aParts = a.split('.').map(Number);
|
|
151
|
+
const bParts = b.split('.').map(Number);
|
|
152
|
+
|
|
153
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
154
|
+
const aVal = aParts[i] || 0;
|
|
155
|
+
const bVal = bParts[i] || 0;
|
|
156
|
+
if (aVal !== bVal) return bVal - aVal;
|
|
157
|
+
}
|
|
158
|
+
return 0;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const distTags = response.data['dist-tags'] || {};
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
versions: versions,
|
|
165
|
+
latest: distTags.latest,
|
|
166
|
+
lts: distTags.lts
|
|
167
|
+
};
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error('Error fetching Angular versions:', error.message);
|
|
170
|
+
return { versions: [], latest: null, lts: null };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get unique major versions from all Angular versions
|
|
176
|
+
*/
|
|
177
|
+
export function getMajorVersions(versions) {
|
|
178
|
+
const majorVersions = new Set();
|
|
179
|
+
|
|
180
|
+
versions.forEach(version => {
|
|
181
|
+
const major = version.split('.')[0];
|
|
182
|
+
majorVersions.add(major);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return Array.from(majorVersions).sort((a, b) => Number(b) - Number(a));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get minor versions for a specific major version
|
|
190
|
+
*/
|
|
191
|
+
export function getMinorVersionsForMajor(versions, major) {
|
|
192
|
+
const minorVersions = new Set();
|
|
193
|
+
|
|
194
|
+
versions
|
|
195
|
+
.filter(v => v.startsWith(`${major}.`))
|
|
196
|
+
.forEach(version => {
|
|
197
|
+
const parts = version.split('.');
|
|
198
|
+
const minorVersion = `${parts[0]}.${parts[1]}`;
|
|
199
|
+
minorVersions.add(minorVersion);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return Array.from(minorVersions).sort((a, b) => {
|
|
203
|
+
const aParts = a.split('.').map(Number);
|
|
204
|
+
const bParts = b.split('.').map(Number);
|
|
205
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
206
|
+
const aVal = aParts[i] || 0;
|
|
207
|
+
const bVal = bParts[i] || 0;
|
|
208
|
+
if (aVal !== bVal) return bVal - aVal;
|
|
209
|
+
}
|
|
210
|
+
return 0;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get patch versions for a specific major.minor version
|
|
216
|
+
*/
|
|
217
|
+
export function getPatchVersionsForMinor(versions, majorMinor) {
|
|
218
|
+
return versions
|
|
219
|
+
.filter(v => v.startsWith(`${majorMinor}.`))
|
|
220
|
+
.sort((a, b) => {
|
|
221
|
+
const aParts = a.split('.').map(Number);
|
|
222
|
+
const bParts = b.split('.').map(Number);
|
|
223
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
224
|
+
const aVal = aParts[i] || 0;
|
|
225
|
+
const bVal = bParts[i] || 0;
|
|
226
|
+
if (aVal !== bVal) return bVal - aVal;
|
|
227
|
+
}
|
|
228
|
+
return 0;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Get all versions of a specific npm package
|
|
234
|
+
*/
|
|
235
|
+
export async function getPackageVersions(packageName) {
|
|
236
|
+
try {
|
|
237
|
+
const response = await axios.get(`${NPM_REGISTRY_URL}/${packageName}`, {
|
|
238
|
+
timeout: 10000
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const versions = Object.keys(response.data.versions || {})
|
|
242
|
+
.filter(v => !v.includes('rc') && !v.includes('beta') && !v.includes('next') && !v.includes('alpha'))
|
|
243
|
+
.sort((a, b) => {
|
|
244
|
+
// Sort in descending order (newest first)
|
|
245
|
+
const aParts = a.split('.').map(Number);
|
|
246
|
+
const bParts = b.split('.').map(Number);
|
|
247
|
+
|
|
248
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
249
|
+
const aVal = aParts[i] || 0;
|
|
250
|
+
const bVal = bParts[i] || 0;
|
|
251
|
+
if (aVal !== bVal) return bVal - aVal;
|
|
252
|
+
}
|
|
253
|
+
return 0;
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const distTags = response.data['dist-tags'] || {};
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
versions: versions,
|
|
260
|
+
latest: distTags.latest,
|
|
261
|
+
lts: distTags.lts
|
|
262
|
+
};
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error(`Error fetching versions for ${packageName}:`, error.message);
|
|
265
|
+
return { versions: [], latest: null, lts: null };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get Node.js version requirements for Angular version
|
|
271
|
+
*/
|
|
272
|
+
export async function getNodeRequirementsForAngular(angularVersion) {
|
|
273
|
+
try {
|
|
274
|
+
const response = await axios.get(`${NPM_REGISTRY_URL}/@angular/cli/${angularVersion}`, {
|
|
275
|
+
timeout: 5000
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const engines = response.data.engines || {};
|
|
279
|
+
const nodeRequirement = engines.node || '^18.13.0 || ^20.9.0';
|
|
280
|
+
|
|
281
|
+
return nodeRequirement;
|
|
282
|
+
} catch (error) {
|
|
283
|
+
// Default Node requirements if we can't fetch
|
|
284
|
+
const majorVersion = parseInt(angularVersion.split('.')[0]);
|
|
285
|
+
|
|
286
|
+
if (majorVersion >= 17) return '^18.13.0 || ^20.9.0';
|
|
287
|
+
if (majorVersion >= 16) return '^16.14.0 || ^18.10.0';
|
|
288
|
+
if (majorVersion >= 15) return '^14.20.0 || ^16.13.0 || ^18.10.0';
|
|
289
|
+
if (majorVersion >= 14) return '^14.15.0 || ^16.10.0';
|
|
290
|
+
|
|
291
|
+
return '^14.0.0 || ^16.0.0 || ^18.0.0';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Get peer dependencies for a specific package version
|
|
297
|
+
*/
|
|
298
|
+
export async function getPackagePeerDependencies(packageName, version) {
|
|
299
|
+
try {
|
|
300
|
+
const response = await axios.get(`${NPM_REGISTRY_URL}/${packageName}/${version}`, {
|
|
301
|
+
timeout: 5000
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
return response.data.peerDependencies || {};
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error(`Error fetching peer dependencies for ${packageName}@${version}:`, error.message);
|
|
307
|
+
return {};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Find compatible versions of a package for given Angular version
|
|
313
|
+
*/
|
|
314
|
+
export async function findCompatiblePackageVersions(packageName, angularVersion, maxResults = 5) {
|
|
315
|
+
try {
|
|
316
|
+
const packageData = await getPackageVersions(packageName);
|
|
317
|
+
const angularMajor = angularVersion.split('.')[0];
|
|
318
|
+
const compatibleVersions = [];
|
|
319
|
+
|
|
320
|
+
// Check versions from newest to oldest
|
|
321
|
+
for (const version of packageData.versions) {
|
|
322
|
+
if (compatibleVersions.length >= maxResults) break;
|
|
323
|
+
|
|
324
|
+
const peerDeps = await getPackagePeerDependencies(packageName, version);
|
|
325
|
+
|
|
326
|
+
// Check if this version is compatible with the Angular version
|
|
327
|
+
if (peerDeps['@angular/core'] || peerDeps['@angular/common']) {
|
|
328
|
+
const angularDep = peerDeps['@angular/core'] || peerDeps['@angular/common'];
|
|
329
|
+
|
|
330
|
+
// Simple check: see if the Angular major version is mentioned in the peer dependency
|
|
331
|
+
if (angularDep.includes(`^${angularMajor}.`) ||
|
|
332
|
+
angularDep.includes(`~${angularMajor}.`) ||
|
|
333
|
+
angularDep.includes(`>=${angularMajor}.`) ||
|
|
334
|
+
angularDep.includes(`${angularMajor}.x`)) {
|
|
335
|
+
compatibleVersions.push({
|
|
336
|
+
version: version,
|
|
337
|
+
peerDependency: angularDep
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
// Package doesn't have Angular peer dependencies, it's likely compatible
|
|
342
|
+
compatibleVersions.push({
|
|
343
|
+
version: version,
|
|
344
|
+
peerDependency: 'No Angular peer dependency'
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return compatibleVersions;
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.error(`Error finding compatible versions for ${packageName}:`, error.message);
|
|
352
|
+
return [];
|
|
353
|
+
}
|
|
354
|
+
}
|