-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
migration.ts
190 lines (173 loc) · 6.31 KB
/
migration.ts
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
182
183
184
185
186
187
188
189
190
import { getPackageManagerCommand, output } from '@nrwl/devkit';
import { execSync } from 'child_process';
import { prompt } from 'enquirer';
import { readModulePackageJson } from 'nx/src/utils/package-json';
import { lt, lte, major, satisfies } from 'semver';
import { resolvePackageVersion } from './package-manager';
import { MigrationDefinition } from './types';
// up to this version the generator was in the @nrwl/workspace package
const latestWorkspaceRangeVersionWithMigration = '~13.9.3';
let latestWorkspaceVersionWithMigration: string;
// up to this version the preserveAngularCLILayout option was used
const latestVersionWithOldFlag = '13.8.3';
// map of Angular major versions to the versions of Nx that are compatible with them,
// key is the Angular major version, value is an object with the range of supported
// versions and the max version of the range if there's a bigger major version that
// is already supported
const nxAngularVersionMap: Record<number, { range: string; max?: string }> = {
13: { range: '>= 13.2.0 < 14.2.0', max: '~14.1.0' },
14: { range: '>= 14.2.0' },
15: { range: '>= 15.0.0' },
};
// latest major version of Angular that is compatible with Nx, based on the map above
const latestCompatibleAngularMajorVersion = Math.max(
...Object.keys(nxAngularVersionMap).map((x) => +x)
);
export async function determineMigration(
version: string | undefined
): Promise<MigrationDefinition> {
const angularVersion = getInstalledAngularVersion();
const majorAngularVersion = major(angularVersion);
latestWorkspaceVersionWithMigration = await resolvePackageVersion(
'@nrwl/angular',
latestWorkspaceRangeVersionWithMigration
);
if (version) {
const normalizedVersion = await normalizeVersion(version);
if (lte(normalizedVersion, latestWorkspaceVersionWithMigration)) {
// specified version should use @nrwl/workspace:ng-add
return { packageName: '@nrwl/workspace', version: normalizedVersion };
}
// if greater than the latest workspace version with the migration,
// check the versions map for compatibility with Angular
if (
nxAngularVersionMap[majorAngularVersion] &&
satisfies(
normalizedVersion,
nxAngularVersionMap[majorAngularVersion].range
)
) {
// there's a match, use @nrwl/angular:ng-add
return { packageName: '@nrwl/angular', version: normalizedVersion };
}
// it's not compatible with the currently installed Angular version,
// suggest the latest possible version to use
return await findAndSuggestVersionToUse(
angularVersion,
majorAngularVersion,
version
);
}
const latestNxCompatibleVersion =
await getNxVersionBasedOnInstalledAngularVersion(
angularVersion,
majorAngularVersion
);
// should use @nrwl/workspace:ng-add if the version is less than the
// latest workspace version that has the migration, otherwise use
// @nrwl/angular:ng-add
return {
packageName: lte(
latestNxCompatibleVersion,
latestWorkspaceVersionWithMigration
)
? '@nrwl/workspace'
: '@nrwl/angular',
version: latestNxCompatibleVersion,
};
}
export function migrateWorkspace(migration: MigrationDefinition): void {
const preserveAngularCliLayoutFlag = lte(
migration.version,
latestVersionWithOldFlag
)
? '--preserveAngularCLILayout'
: '--preserve-angular-cli-layout';
execSync(
`${getPackageManagerCommand().exec} nx g ${
migration.packageName
}:ng-add ${preserveAngularCliLayoutFlag}`,
{ stdio: [0, 1, 2] }
);
}
async function findAndSuggestVersionToUse(
angularVersion: string,
majorAngularVersion: number,
userSpecifiedVersion: string
): Promise<MigrationDefinition> {
const latestNxCompatibleVersion =
await getNxVersionBasedOnInstalledAngularVersion(
angularVersion,
majorAngularVersion
);
const useSuggestedVersion = await promptForVersion(latestNxCompatibleVersion);
if (useSuggestedVersion) {
// should use @nrwl/workspace:ng-add if the version is less than the
// latest workspace version that has the migration, otherwise use
// @nrwl/angular:ng-add
return {
packageName: lte(
latestNxCompatibleVersion,
latestWorkspaceVersionWithMigration
)
? '@nrwl/workspace'
: '@nrwl/angular',
version: latestNxCompatibleVersion,
};
}
output.error({
title: `❌ Cannot proceed with the migration.`,
bodyLines: [
`The specified Nx version "${userSpecifiedVersion}" is not compatible with the installed Angular version "${angularVersion}".`,
],
});
process.exit(1);
}
async function getNxVersionBasedOnInstalledAngularVersion(
angularVersion: string,
majorAngularVersion: number
): Promise<string> {
if (lt(angularVersion, '13.0.0')) {
// the @nrwl/angular:ng-add generator is only available for versions supporting
// Angular >= 13.0.0, fall back to @nrwl/workspace:ng-add
return latestWorkspaceVersionWithMigration;
}
if (nxAngularVersionMap[majorAngularVersion]?.max) {
// use the max of the range
return await resolvePackageVersion(
'@nrwl/angular',
nxAngularVersionMap[majorAngularVersion].max
);
}
if (majorAngularVersion > latestCompatibleAngularMajorVersion) {
// installed Angular version is not supported yet, we can't @nrwl/angular:ng-add,
// fall back to @nrwl/workspace:ng-add
return latestWorkspaceVersionWithMigration;
}
// use latest, only the last version in the map should not contain a max
return await resolvePackageVersion('@nrwl/angular', 'latest');
}
async function promptForVersion(version: string): Promise<boolean> {
const { useVersion } = await prompt<{ useVersion: boolean }>([
{
name: 'useVersion',
message: `The provided version of Nx is not compatible with the installed Angular CLI version. Would you like to use the recommended version "${version}" instead?`,
type: 'confirm',
initial: true,
},
]);
return useVersion;
}
function getInstalledAngularVersion(): string {
return readModulePackageJson('@angular/core').packageJson.version;
}
async function normalizeVersion(version: string): Promise<string> {
if (
version.startsWith('^') ||
version.startsWith('~') ||
version.split('.').length < 3
) {
return await resolvePackageVersion('@nrwl/angular', version);
}
return version;
}