From d3cacafa8ebf774202d5d81c9d8780623ec51fc1 Mon Sep 17 00:00:00 2001 From: Dmitriy Stepanenko Date: Mon, 25 Nov 2024 22:56:13 +0200 Subject: [PATCH] feat: rely on tags to retrieve version of v2 app --- .../src/cli/migrate-v2/replace-package.ts | 17 ++--- .../qwik/src/cli/migrate-v2/run-migration.ts | 17 +++-- .../src/cli/migrate-v2/update-dependencies.ts | 66 ++++++++++++++++--- packages/qwik/src/cli/migrate-v2/versions.ts | 13 ++-- scripts/create-qwik-cli.ts | 2 +- scripts/submodule-cli.ts | 2 +- 6 files changed, 78 insertions(+), 39 deletions(-) diff --git a/packages/qwik/src/cli/migrate-v2/replace-package.ts b/packages/qwik/src/cli/migrate-v2/replace-package.ts index 86826e52794..46815cf6f2a 100644 --- a/packages/qwik/src/cli/migrate-v2/replace-package.ts +++ b/packages/qwik/src/cli/migrate-v2/replace-package.ts @@ -9,21 +9,13 @@ function updateFileContent(path: string, content: string) { log.info(`"${path}" has been updated`); } -export function replacePackage( - oldPackageName: string, - newPackageName: string, - newPackageVersion: string -): void { - replacePackageInDependencies(oldPackageName, newPackageName, newPackageVersion); +export function replacePackage(oldPackageName: string, newPackageName: string): void { + replacePackageInDependencies(oldPackageName, newPackageName); replaceMentions(oldPackageName, newPackageName); } -function replacePackageInDependencies( - oldPackageName: string, - newPackageName: string, - newPackageVersion: string -) { +function replacePackageInDependencies(oldPackageName: string, newPackageName: string) { visitNotIgnoredFiles('.', (path) => { if (basename(path) !== 'package.json') { return; @@ -38,7 +30,8 @@ function replacePackageInDependencies( packageJson.optionalDependencies ?? {}, ]) { if (oldPackageName in deps) { - deps[newPackageName] = newPackageVersion; + // We keep the old version intentionally. It will be updated later within another step of the migration. + deps[newPackageName] = deps[oldPackageName]; delete deps[oldPackageName]; } } diff --git a/packages/qwik/src/cli/migrate-v2/run-migration.ts b/packages/qwik/src/cli/migrate-v2/run-migration.ts index 7dcdb6c0591..73b0c66f240 100644 --- a/packages/qwik/src/cli/migrate-v2/run-migration.ts +++ b/packages/qwik/src/cli/migrate-v2/run-migration.ts @@ -1,6 +1,6 @@ import { confirm, intro, isCancel, log } from '@clack/prompts'; import type { AppCommand } from '../utils/app-command'; -import { bgMagenta, green } from 'kleur/colors'; +import { bgMagenta, bgRed, bold, green } from 'kleur/colors'; import { bye } from '../utils/utils'; import { replacePackage } from './replace-package'; import { @@ -8,15 +8,14 @@ import { removeTsMorphFromPackageJson, updateDependencies, } from './update-dependencies'; -import { versions } from './versions'; -import { replaceImportInFiles } from './rename-import'; export async function runV2Migration(app: AppCommand) { intro( - `✨ ${bgMagenta(' This command will migrate your Qwik application from v1 to v2 \n')}` + + `✨ ${bgMagenta(' This command will migrate your Qwik application from v1 to v2')}\n` + `This includes the following: \n` + ` - "@builder.io/qwik", "@builder.io/qwik-city" and "@builder.io/qwik-react" packages will be rescoped to "@qwik.dev/core", "@qwik.dev/router" and "@qwik.dev/react" respectively \n` + - ` - related dependencies will be updated \n` + ` - related dependencies will be updated \n\n` + + `${bold(bgRed('Warning: migration tool is experimental and will migrate your application to the "alpha" release of Qwik V2'))}` ); const proceed = await confirm({ message: 'Do you want to proceed?', @@ -29,7 +28,7 @@ export async function runV2Migration(app: AppCommand) { try { const installedTsMorph = await installTsMorph(); - + const { replaceImportInFiles } = await import('./rename-import'); replaceImportInFiles( [ ['QwikCityProvider', 'QwikRouterProvider'], @@ -42,10 +41,10 @@ export async function runV2Migration(app: AppCommand) { '@builder.io/qwik-city' ); - replacePackage('@builder.io/qwik-city', '@qwik.dev/router', versions['@qwik.dev/router']); - replacePackage('@builder.io/qwik-react', '@qwik.dev/react', versions['@qwik.dev/react']); + replacePackage('@builder.io/qwik-city', '@qwik.dev/router'); + replacePackage('@builder.io/qwik-react', '@qwik.dev/react'); // "@builder.io/qwik" should be the last one because it's name is a substring of the package names above - replacePackage('@builder.io/qwik', '@qwik.dev/core', versions['@qwik.dev/core']); + replacePackage('@builder.io/qwik', '@qwik.dev/core'); if (installedTsMorph) { await removeTsMorphFromPackageJson(); diff --git a/packages/qwik/src/cli/migrate-v2/update-dependencies.ts b/packages/qwik/src/cli/migrate-v2/update-dependencies.ts index 7ae96782127..6ab60ecd57d 100644 --- a/packages/qwik/src/cli/migrate-v2/update-dependencies.ts +++ b/packages/qwik/src/cli/migrate-v2/update-dependencies.ts @@ -1,6 +1,9 @@ +import { execSync } from 'node:child_process'; import { installDeps } from '../utils/install-deps'; import { getPackageManager, readPackageJson, writePackageJson } from './../utils/utils'; -import { versions } from './versions'; +import { packageNames, versionTagPriority } from './versions'; +import { major } from 'semver'; +import { log, spinner } from '@clack/prompts'; export async function updateDependencies() { // TODO(migrate-v2): rely on workspaceRoot instead? @@ -8,19 +11,58 @@ export async function updateDependencies() { const devDependencies = (packageJson.devDependencies ??= {}); const dependencies = (packageJson.dependencies ??= {}); - for (const key of Object.keys(devDependencies)) { - if (Object.prototype.hasOwnProperty.call(versions, key)) { - devDependencies[key] = versions[key as unknown as keyof typeof versions]; - } - } - for (const key of Object.keys(dependencies)) { - if (Object.prototype.hasOwnProperty.call(versions, key)) { - dependencies[key] = versions[key as unknown as keyof typeof versions]; + const version = getPackageTag(); + + for (const name of packageNames) { + if (dependencies[name] || devDependencies[name]) { + delete dependencies[name]; + devDependencies[name] = version; } } await writePackageJson(process.cwd(), packageJson); - runInstall(); + const loading = spinner(); + loading.start(`Updating dependencies...`); + await runInstall(); + loading.stop('Dependencies have been updated'); +} + +/** + * Resolve the list of available package tags for the "@qwik.dev/core" and get the best match of + * ^2.0.0 based on the "versionTagPriority" + */ +function getPackageTag() { + // we assume all migrated packages have the same set of tags + const tags: [tag: string, version: string][] = execSync('npm dist-tag @qwik.dev/core', { + encoding: 'utf-8', + }) + ?.split('\n') + .filter(Boolean) + .map((data) => + data + .split(':') + .map((v) => v?.trim()) + .filter(Boolean) + ) + .filter((v): v is [string, string] => v.length === 2) + .sort((a, b) => { + let aIndex = versionTagPriority.indexOf(a[0]); + let bIndex = versionTagPriority.indexOf(b[0]); + if (aIndex === -1) { + aIndex = Infinity; + } else if (bIndex === -1) { + bIndex = Infinity; + } + return aIndex - bIndex; + }); + + for (const [, version] of tags) { + if (major(version) === 2) { + return version; + } + } + log.warn('Failed to resolve the Qwik version tag, version "2.0.0" will be installed'); + return '2.0.0'; } export async function installTsMorph() { @@ -28,8 +70,12 @@ export async function installTsMorph() { if (packageJson.dependencies?.['ts-morph'] || packageJson.devDependencies?.['ts-morph']) { return false; } + const loading = spinner(); + loading.start('Fetching migration tools..'); (packageJson.devDependencies ??= {})['ts-morph'] = 'latest'; + await writePackageJson(process.cwd(), packageJson); await runInstall(); + loading.stop('Migration tools have been loaded'); return true; } diff --git a/packages/qwik/src/cli/migrate-v2/versions.ts b/packages/qwik/src/cli/migrate-v2/versions.ts index f9c095fbae6..27f96ee9c5f 100644 --- a/packages/qwik/src/cli/migrate-v2/versions.ts +++ b/packages/qwik/src/cli/migrate-v2/versions.ts @@ -1,6 +1,7 @@ -export const versions = { - '@qwik.dev/core': '2.0.0-alpha.1', - '@qwik.dev/router': '2.0.0-alpha.1', - '@qwik.dev/react': '2.0.0-alpha.1', - 'eslint-plugin-qwik': '2.0.0-alpha.1', -}; +export const versionTagPriority = ['latest', 'v2', 'rc', 'beta', 'alpha']; +export const packageNames = [ + '@qwik.dev/core', + '@qwik.dev/router', + '@qwik.dev/react', + 'eslint-plugin-qwik', +]; diff --git a/scripts/create-qwik-cli.ts b/scripts/create-qwik-cli.ts index b002a3c40c0..367a7dcce33 100644 --- a/scripts/create-qwik-cli.ts +++ b/scripts/create-qwik-cli.ts @@ -58,7 +58,7 @@ async function bundleCreateQwikCli(config: BuildConfig, srcCliDir: string, distC }, }, ], - external: ['prettier', 'typescript'], + external: ['prettier', 'typescript', 'ts-morph', 'semver', 'ignore'], define: { 'globalThis.CODE_MOD': 'false', 'globalThis.QWIK_VERSION': JSON.stringify(config.distVersion), diff --git a/scripts/submodule-cli.ts b/scripts/submodule-cli.ts index a4397ec3400..888c3134f0f 100644 --- a/scripts/submodule-cli.ts +++ b/scripts/submodule-cli.ts @@ -35,7 +35,7 @@ export async function submoduleCli(config: BuildConfig) { }, }, ], - external: ['prettier', 'typescript'], + external: ['prettier', 'typescript', 'ts-morph', 'semver', 'ignore'], define: { 'globalThis.CODE_MOD': 'true', 'globalThis.QWIK_VERSION': JSON.stringify(config.distVersion),