chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import axios from 'axios';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { gte, major, minor, parse } from 'semver';
|
||||
|
||||
async function addMigrationPackageGroup(
|
||||
angularPackageMigrations: Record<string, any>,
|
||||
targetNxVersion: string,
|
||||
targetNxMigrationVersion: string,
|
||||
packageVersionMap: Map<string, string>,
|
||||
isPrerelease: boolean
|
||||
) {
|
||||
const existingEntry =
|
||||
angularPackageMigrations.packageJsonUpdates[targetNxVersion];
|
||||
angularPackageMigrations.packageJsonUpdates[targetNxVersion] = {
|
||||
version: `${targetNxMigrationVersion}`,
|
||||
};
|
||||
|
||||
const angularCoreRequirement = await getAngularCoreRequirement();
|
||||
if (angularCoreRequirement) {
|
||||
angularPackageMigrations.packageJsonUpdates[targetNxVersion].requires = {
|
||||
'@angular/core': angularCoreRequirement,
|
||||
};
|
||||
} else if (existingEntry?.requires) {
|
||||
// latest >= next: preserve the requires from the pre-release entry and
|
||||
// update the upper bound to the stable major.minor.0
|
||||
const angularCoreVersion = packageVersionMap.get('@angular/core');
|
||||
const { major: majorVersion, minor: minorVersion } =
|
||||
parse(angularCoreVersion)!;
|
||||
const existingReq = existingEntry.requires['@angular/core'] as string;
|
||||
const updatedReq = existingReq.replace(
|
||||
/<.*$/,
|
||||
`<${majorVersion}.${minorVersion}.0`
|
||||
);
|
||||
|
||||
angularPackageMigrations.packageJsonUpdates[targetNxVersion].requires = {
|
||||
'@angular/core': updatedReq,
|
||||
};
|
||||
console.log(
|
||||
'ℹ️ - The `@angular/core` latest version is greater than or equal to the next version. Preserving existing migration requirements.'
|
||||
);
|
||||
} else {
|
||||
console.warn(
|
||||
'❗️ - The `@angular/core` latest version is greater than or equal to the next version and no existing entry found.\n' +
|
||||
' Please manually add the requires field.'
|
||||
);
|
||||
}
|
||||
|
||||
angularPackageMigrations.packageJsonUpdates[targetNxVersion].packages = {};
|
||||
for (const [pkgName, version] of packageVersionMap) {
|
||||
if (
|
||||
pkgName.startsWith('@angular/') &&
|
||||
![
|
||||
'@angular/cli',
|
||||
'@angular/core',
|
||||
'@angular/material',
|
||||
'@angular/cdk',
|
||||
'@angular/google-maps',
|
||||
'@angular/ssr',
|
||||
'@angular/pwa',
|
||||
'@angular/build',
|
||||
].includes(pkgName)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const packageUpdate: any = {
|
||||
version: isPrerelease ? version : `~${version}`,
|
||||
alwaysAddToPackageJson: pkgName === '@angular/core',
|
||||
};
|
||||
if (pkgName === '@angular/cli') {
|
||||
packageUpdate.ignorePackageGroup = true;
|
||||
packageUpdate.ignoreMigrations = true;
|
||||
}
|
||||
|
||||
angularPackageMigrations.packageJsonUpdates[targetNxVersion].packages[
|
||||
pkgName
|
||||
] = packageUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
async function getAngularCoreRequirement(): Promise<string | null> {
|
||||
const angularCoreMetadata = await axios.get(
|
||||
'https://registry.npmjs.org/@angular/core'
|
||||
);
|
||||
const { latest, next } = angularCoreMetadata.data['dist-tags'];
|
||||
// When latest >= next (e.g. a stable GA also published to the `next` tag),
|
||||
// `latest` is the new version, not the previous one - so it can't seed the
|
||||
// requires lower bound. Bail and let the caller preserve the pre-release
|
||||
// entry's requires or warn for manual entry.
|
||||
if (gte(latest, next)) {
|
||||
return null;
|
||||
}
|
||||
return `>=${major(latest)}.${minor(latest)}.0 <${next}`;
|
||||
}
|
||||
|
||||
export async function buildMigrations(
|
||||
packageVersionMap: Map<string, string>,
|
||||
targetNxVersion: string,
|
||||
targetNxMigrationVersion: string,
|
||||
isPrerelease: boolean
|
||||
) {
|
||||
console.log('⏳ - Writing migrations...');
|
||||
const pathToMigrationsJsonFile = 'packages/angular/migrations.json';
|
||||
const angularPackageMigrations = JSON.parse(
|
||||
readFileSync(pathToMigrationsJsonFile, { encoding: 'utf-8' })
|
||||
);
|
||||
|
||||
await addMigrationPackageGroup(
|
||||
angularPackageMigrations,
|
||||
targetNxVersion,
|
||||
targetNxMigrationVersion,
|
||||
packageVersionMap,
|
||||
isPrerelease
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
pathToMigrationsJsonFile,
|
||||
JSON.stringify(angularPackageMigrations, null, 2)
|
||||
);
|
||||
|
||||
console.log('✅ - Wrote migrations');
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import axios from 'axios';
|
||||
import { coerce, gt, maxSatisfying, SemVer } from 'semver';
|
||||
|
||||
type PackageSpec = string | { main: string; children: string[] };
|
||||
const packagesToUpdate: PackageSpec[] = [
|
||||
{
|
||||
main: '@angular/cli',
|
||||
children: [
|
||||
'@angular-devkit/build-angular',
|
||||
'@angular-devkit/core',
|
||||
'@angular-devkit/schematics',
|
||||
'@angular/build',
|
||||
'@angular/pwa',
|
||||
'@angular/ssr',
|
||||
'@schematics/angular',
|
||||
],
|
||||
},
|
||||
{
|
||||
main: '@angular-devkit/architect',
|
||||
children: ['@angular-devkit/build-webpack'],
|
||||
},
|
||||
{
|
||||
main: '@angular/core',
|
||||
children: [
|
||||
'@angular/common',
|
||||
'@angular/compiler',
|
||||
'@angular/compiler-cli',
|
||||
'@angular/localize',
|
||||
'@angular/platform-browser',
|
||||
'@angular/platform-server',
|
||||
'@angular/router',
|
||||
],
|
||||
},
|
||||
{
|
||||
main: '@angular/material',
|
||||
children: ['@angular/cdk', '@angular/google-maps'],
|
||||
},
|
||||
'ng-packagr',
|
||||
];
|
||||
|
||||
// Resolved from `@angular/core`'s peerDependencies (not its own dist-tags) so it
|
||||
// stays aligned with whatever range Angular actually accepts. Only zone.js: rxjs
|
||||
// is intentionally excluded - Angular accepts a wide rxjs range (6 || 7), so we
|
||||
// don't pin/bump it from here (which would otherwise force it into the migration).
|
||||
const angularCorePeerResolvedPackages = ['zone.js'] as const;
|
||||
|
||||
export async function fetchVersionsFromRegistry(
|
||||
targetVersion: 'latest' | 'next'
|
||||
) {
|
||||
console.log('⏳ - Fetching versions from registry...');
|
||||
|
||||
const packageVersionMap = new Map<string, string>();
|
||||
await Promise.all(
|
||||
packagesToUpdate.map((pkgSpec) =>
|
||||
fetch(packageVersionMap, pkgSpec, targetVersion)
|
||||
)
|
||||
);
|
||||
|
||||
await resolveAngularCorePeerDependencies(packageVersionMap);
|
||||
|
||||
console.log('✅ - Finished fetching versions from registry');
|
||||
|
||||
return packageVersionMap;
|
||||
}
|
||||
|
||||
async function resolveAngularCorePeerDependencies(
|
||||
packageVersionMap: Map<string, string>
|
||||
) {
|
||||
const angularCoreVersion = packageVersionMap.get('@angular/core')!;
|
||||
const { data } = await axios.get(
|
||||
`https://registry.npmjs.org/@angular/core/${angularCoreVersion}`
|
||||
);
|
||||
const peerDependencies: Record<string, string> = data.peerDependencies ?? {};
|
||||
|
||||
await Promise.all(
|
||||
angularCorePeerResolvedPackages.map(async (pkgName) => {
|
||||
const peerRange = peerDependencies[pkgName];
|
||||
if (!peerRange) {
|
||||
console.warn(
|
||||
` ⚠️ @angular/core@${angularCoreVersion} declares no peerDependency for "${pkgName}", skipping`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = await axios.get(`https://registry.npmjs.org/${pkgName}`);
|
||||
const resolved = maxSatisfying(
|
||||
Object.keys(data.versions ?? {}),
|
||||
peerRange,
|
||||
{ includePrerelease: false }
|
||||
);
|
||||
if (!resolved) {
|
||||
console.warn(
|
||||
` ⚠️ Could not resolve a published version of "${pkgName}" matching peer range "${peerRange}"`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
packageVersionMap.set(pkgName, resolved);
|
||||
console.log(` ${pkgName}: ${resolved}`);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function fetch(
|
||||
packageVersionMap: Map<string, string>,
|
||||
pkgSpec: PackageSpec,
|
||||
targetVersion: 'latest' | 'next'
|
||||
) {
|
||||
function setPackageVersions(pkgSpec: PackageSpec, version: string) {
|
||||
if (typeof pkgSpec === 'string') {
|
||||
packageVersionMap.set(pkgSpec, version);
|
||||
return;
|
||||
}
|
||||
|
||||
packageVersionMap.set(pkgSpec.main, version);
|
||||
for (const child of pkgSpec.children) {
|
||||
packageVersionMap.set(child, version);
|
||||
}
|
||||
}
|
||||
|
||||
const pkgName = typeof pkgSpec === 'string' ? pkgSpec : pkgSpec.main;
|
||||
// set it to empty initially to keep the order of the specified packages
|
||||
setPackageVersions(pkgSpec, '');
|
||||
|
||||
const response = await axios.get(`https://registry.npmjs.org/${pkgName}`);
|
||||
const distTags = response.data['dist-tags'];
|
||||
const latestVersion = distTags['latest'];
|
||||
if (targetVersion === 'latest') {
|
||||
setPackageVersions(pkgSpec, latestVersion);
|
||||
console.log(` ${pkgName}: ${latestVersion}`);
|
||||
} else {
|
||||
const nextVersion = distTags['next'] ?? latestVersion;
|
||||
const coercedNextVersion = coerce(nextVersion) as SemVer;
|
||||
// check which is the greater version
|
||||
const versionToUse = gt(coercedNextVersion, latestVersion)
|
||||
? nextVersion
|
||||
: latestVersion;
|
||||
setPackageVersions(pkgSpec, versionToUse);
|
||||
console.log(` ${pkgName}: ${versionToUse}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* USAGE:
|
||||
*
|
||||
* Run the following command from the root of the workspace.
|
||||
* Replace the versions with the correct target versions
|
||||
*
|
||||
* pnpm ts-node scripts/angular-support-upgrades/init-upgrade.ts --angularVersion=next --targetNxVersion=15.5.0 --targetNxMigrationVersion=15.5.0-beta.0
|
||||
*
|
||||
*/
|
||||
import { execSync } from 'child_process';
|
||||
import { prerelease } from 'semver';
|
||||
import { buildMigrations } from './build-migrations';
|
||||
import { fetchVersionsFromRegistry } from './fetch-versions-from-registry';
|
||||
import { updatePackageDependencies } from './update-package-jsons';
|
||||
import { updateVersionUtils } from './update-version-utils';
|
||||
|
||||
const yargs = require('yargs');
|
||||
const { hideBin } = require('yargs/helpers');
|
||||
const argv = yargs(hideBin(process.argv)).argv;
|
||||
|
||||
if (
|
||||
!argv.angularVersion &&
|
||||
!argv.targetNxVersion &&
|
||||
!argv.targetNxMigrationVersion
|
||||
) {
|
||||
throw new Error(
|
||||
'You need to provide --angularVersion=(latest|next) and --targetNxVersion=versionString and --targetNxMigrationVersion=versionString'
|
||||
);
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const packageVersionMap = await fetchVersionsFromRegistry(
|
||||
argv.angularVersion
|
||||
);
|
||||
|
||||
const isPrerelease =
|
||||
prerelease(packageVersionMap.get('@angular/cli')!) !== null;
|
||||
await updatePackageDependencies(packageVersionMap, isPrerelease);
|
||||
await buildMigrations(
|
||||
packageVersionMap,
|
||||
argv.targetNxVersion,
|
||||
argv.targetNxMigrationVersion,
|
||||
isPrerelease
|
||||
);
|
||||
updateVersionUtils(packageVersionMap, isPrerelease);
|
||||
|
||||
console.log('⏳ - Installing packages...');
|
||||
execSync('pnpm install', {
|
||||
stdio: 'inherit',
|
||||
encoding: 'utf8',
|
||||
|
||||
windowsHide: false,
|
||||
});
|
||||
console.log('✅ - Finished installing packages!');
|
||||
|
||||
console.log('⏳ - Formatting files...');
|
||||
execSync('pnpm nx format', {
|
||||
stdio: 'inherit',
|
||||
encoding: 'utf8',
|
||||
windowsHide: false,
|
||||
});
|
||||
console.log('✅ - Finished creating migrations!');
|
||||
}
|
||||
|
||||
run();
|
||||
@@ -0,0 +1,36 @@
|
||||
import { dump, load } from '@zkochan/js-yaml';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
|
||||
function updatePnpmCatalogDefinitions(
|
||||
packageVersionMap: Map<string, string>,
|
||||
isPrerelease: boolean
|
||||
) {
|
||||
const pnpmWorkspaceYaml = readFileSync('pnpm-workspace.yaml', 'utf-8');
|
||||
const workspaceData = load(pnpmWorkspaceYaml)!;
|
||||
|
||||
for (const [pkgName, version] of packageVersionMap.entries()) {
|
||||
const versionToUse = isPrerelease ? version : `~${version}`;
|
||||
if (workspaceData.catalogs.angular[pkgName]) {
|
||||
workspaceData.catalogs.angular[pkgName] = versionToUse;
|
||||
}
|
||||
}
|
||||
|
||||
writeFileSync(
|
||||
'pnpm-workspace.yaml',
|
||||
dump(workspaceData, {
|
||||
indent: 2,
|
||||
quotingType: '"',
|
||||
forceQuotes: true,
|
||||
}),
|
||||
'utf-8'
|
||||
);
|
||||
}
|
||||
|
||||
export async function updatePackageDependencies(
|
||||
packageVersionMap: Map<string, string>,
|
||||
isPrerelease: boolean
|
||||
) {
|
||||
console.log('⏳ - Updating Pnpm Catalog definitions...');
|
||||
updatePnpmCatalogDefinitions(packageVersionMap, isPrerelease);
|
||||
console.log('✅ - Updated Pnpm Catalog definitions');
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
|
||||
function updateAngularVersionUtils(
|
||||
packageVersionMap: Map<string, string>,
|
||||
isPrerelease: boolean
|
||||
) {
|
||||
const pathToFile = 'packages/angular/src/utils/versions.ts';
|
||||
let versionUtilContents = readFileSync(pathToFile, { encoding: 'utf-8' });
|
||||
|
||||
const angularVersion = packageVersionMap.get('@angular/core')!;
|
||||
const angularDevkitVersion = packageVersionMap.get('@angular/cli')!;
|
||||
const ngPackagrVersion = packageVersionMap.get('ng-packagr')!;
|
||||
const zoneJsVersion = packageVersionMap.get('zone.js');
|
||||
const rxjsVersion = packageVersionMap.get('rxjs');
|
||||
|
||||
versionUtilContents = versionUtilContents.replace(
|
||||
/export const angularVersion = '.+';/,
|
||||
`export const angularVersion = '${
|
||||
isPrerelease ? angularVersion : `~${angularVersion}`
|
||||
}';`
|
||||
);
|
||||
versionUtilContents = versionUtilContents.replace(
|
||||
/export const angularDevkitVersion = '.+';/,
|
||||
`export const angularDevkitVersion = '${
|
||||
isPrerelease ? angularDevkitVersion : `~${angularDevkitVersion}`
|
||||
}';`
|
||||
);
|
||||
versionUtilContents = versionUtilContents.replace(
|
||||
/export const ngPackagrVersion = '.+';/,
|
||||
`export const ngPackagrVersion = '${
|
||||
isPrerelease ? ngPackagrVersion : `~${ngPackagrVersion}`
|
||||
}';`
|
||||
);
|
||||
if (zoneJsVersion) {
|
||||
versionUtilContents = versionUtilContents.replace(
|
||||
/export const zoneJsVersion = '.+';/,
|
||||
`export const zoneJsVersion = '${
|
||||
isPrerelease ? zoneJsVersion : `~${zoneJsVersion}`
|
||||
}';`
|
||||
);
|
||||
}
|
||||
if (rxjsVersion) {
|
||||
versionUtilContents = versionUtilContents.replace(
|
||||
/export const rxjsVersion = '.+';/,
|
||||
`export const rxjsVersion = '${
|
||||
isPrerelease ? rxjsVersion : `~${rxjsVersion}`
|
||||
}';`
|
||||
);
|
||||
}
|
||||
|
||||
writeFileSync(pathToFile, versionUtilContents);
|
||||
}
|
||||
|
||||
function updateWorkspaceAngularVersionUtils(
|
||||
packageVersionMap: Map<string, string>,
|
||||
isPrerelease: boolean
|
||||
) {
|
||||
const pathToFile = 'packages/workspace/src/utils/versions.ts';
|
||||
let versionUtilContents = readFileSync(pathToFile, { encoding: 'utf-8' });
|
||||
|
||||
const angularDevkitVersion = packageVersionMap.get('@angular/cli')!;
|
||||
|
||||
versionUtilContents = versionUtilContents.replace(
|
||||
/export const angularCliVersion = '.+';/,
|
||||
`export const angularCliVersion = '${
|
||||
isPrerelease ? angularDevkitVersion : `~${angularDevkitVersion}`
|
||||
}';`
|
||||
);
|
||||
|
||||
writeFileSync(pathToFile, versionUtilContents);
|
||||
}
|
||||
|
||||
export function updateVersionUtils(
|
||||
packageVersionMap: Map<string, string>,
|
||||
isPrerelease: boolean
|
||||
) {
|
||||
console.log('⏳ - Writing Util Files...');
|
||||
updateAngularVersionUtils(packageVersionMap, isPrerelease);
|
||||
updateWorkspaceAngularVersionUtils(packageVersionMap, isPrerelease);
|
||||
console.log('✅ - Wrote Util Files');
|
||||
}
|
||||
Reference in New Issue
Block a user