diff --git a/packages/qwik/src/cli/migrate-v2/replace-package.ts b/packages/qwik/src/cli/migrate-v2/replace-package.ts index 1f9b9d04ca7a..86826e527943 100644 --- a/packages/qwik/src/cli/migrate-v2/replace-package.ts +++ b/packages/qwik/src/cli/migrate-v2/replace-package.ts @@ -1,25 +1,28 @@ import { basename } from 'path'; import { isBinaryPath } from './tools/binary-extensions'; import { visitNotIgnoredFiles } from './tools/visit-not-ignored-files'; -import { readFileSync } from 'fs'; +import { readFileSync, writeFileSync } from 'fs'; import { log } from '@clack/prompts'; -function writeFileSync(path: string, content: string) { +function updateFileContent(path: string, content: string) { + writeFileSync(path, content); log.info(`"${path}" has been updated`); } export function replacePackage( oldPackageName: string, - newPackageName: string + newPackageName: string, + newPackageVersion: string ): void { - replacePackageInDependencies(oldPackageName, newPackageName); + replacePackageInDependencies(oldPackageName, newPackageName, newPackageVersion); replaceMentions(oldPackageName, newPackageName); } function replacePackageInDependencies( oldPackageName: string, - newPackageName: string + newPackageName: string, + newPackageVersion: string ) { visitNotIgnoredFiles('.', (path) => { if (basename(path) !== 'package.json') { @@ -35,24 +38,18 @@ function replacePackageInDependencies( packageJson.optionalDependencies ?? {}, ]) { if (oldPackageName in deps) { - deps[newPackageName] = deps[oldPackageName]; + deps[newPackageName] = newPackageVersion; delete deps[oldPackageName]; } } - writeFileSync(path, JSON.stringify(packageJson, null, 2)); + updateFileContent(path, JSON.stringify(packageJson, null, 2)); } catch (e) { - console.warn( - `Could not replace ${oldPackageName} with ${newPackageName} in ${path}.` - ); + console.warn(`Could not replace ${oldPackageName} with ${newPackageName} in ${path}.`); } }); } - -function replaceMentions( - oldPackageName: string, - newPackageName: string -) { +function replaceMentions(oldPackageName: string, newPackageName: string) { visitNotIgnoredFiles('.', (path) => { if (isBinaryPath(path)) { return; @@ -76,10 +73,7 @@ function replaceMentions( return; } - writeFileSync( - path, - contents.replace(new RegExp(oldPackageName, 'g'), newPackageName) - ); + updateFileContent(path, contents.replace(new RegExp(oldPackageName, 'g'), newPackageName)); } catch { // Its **probably** ok, contents can be null if the file is too large or // there was an access exception. diff --git a/packages/qwik/src/cli/migrate-v2/run-migration.ts b/packages/qwik/src/cli/migrate-v2/run-migration.ts index cecebd59383c..f1bbd736a385 100644 --- a/packages/qwik/src/cli/migrate-v2/run-migration.ts +++ b/packages/qwik/src/cli/migrate-v2/run-migration.ts @@ -1,15 +1,17 @@ -import { confirm, intro, isCancel } from '@clack/prompts'; +import { confirm, intro, isCancel, log } from '@clack/prompts'; import type { AppCommand } from '../utils/app-command'; -import { bgMagenta } from 'kleur/colors'; +import { bgMagenta, green } from 'kleur/colors'; import { bye } from '../utils/utils'; import { replacePackage } from './replace-package'; +import { updateDependencies } from './update-dependencies'; +import { versions } from './versions'; export async function runV2Migration(app: AppCommand) { intro( `✨ ${bgMagenta(' This command will migrate your Qwik application from v1 to v2 \n')}` + `This includes the following: \n` + - // TODO: package names - ` - "@builder.io/qwik", "@builder.io/qwik-city" packages will be rescoped to "@qwik.dev/core" and "@qwik.dev/qwik-city" \n` + + // TODO(migrate-v2): package names + ` - "@builder.io/qwik", "@builder.io/qwik-city" packages will be rescoped to "@qwik.dev/core" and "@qwik.dev/city" \n` + ` - related dependencies will be updated \n` ); const proceed = await confirm({ @@ -22,11 +24,12 @@ export async function runV2Migration(app: AppCommand) { } try { - replacePackage('@builder.io/qwik', '@qwik.dev/qwik'); - replacePackage('@builder.io/qwik-city', '@qwik.dev/city'); + replacePackage('@builder.io/qwik-city', '@qwik.dev/city', versions['@qwik.dev/city']); + replacePackage('@builder.io/qwik', '@qwik.dev/qwik', versions['@qwik.dev/qwik']); + await updateDependencies(); + log.success(`${green(`Your application has been successfully migrated to v2!`)}`); } catch (error) { - console.log(error); - throw error + console.error(error); + throw error; } - } diff --git a/packages/qwik/src/cli/migrate-v2/tools/visit-not-ignored-files.spec.ts b/packages/qwik/src/cli/migrate-v2/tools/visit-not-ignored-files.spec.ts deleted file mode 100644 index 8c9f85310c5f..000000000000 --- a/packages/qwik/src/cli/migrate-v2/tools/visit-not-ignored-files.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { createTree } from 'nx/src/generators/testing-utils/create-tree'; -import type { Tree } from 'nx/src/generators/tree'; -import { visitNotIgnoredFiles } from './visit-not-ignored-files'; - -describe('visitNotIgnoredFiles', () => { - let tree: Tree; - - beforeEach(() => { - tree = createTree(); - }); - - it('should visit files recursively in a directory', () => { - tree.write('dir/file1.ts', ''); - tree.write('dir/dir2/file2.ts', ''); - - const visitor = jest.fn(); - visitNotIgnoredFiles(tree, 'dir', visitor); - - expect(visitor).toHaveBeenCalledWith('dir/file1.ts'); - expect(visitor).toHaveBeenCalledWith('dir/dir2/file2.ts'); - }); - - it('should not visit ignored files in a directory', () => { - tree.write('.gitignore', 'node_modules'); - - tree.write('dir/file1.ts', ''); - tree.write('dir/node_modules/file1.ts', ''); - tree.write('dir/dir2/file2.ts', ''); - - const visitor = jest.fn(); - visitNotIgnoredFiles(tree, 'dir', visitor); - - expect(visitor).toHaveBeenCalledWith('dir/file1.ts'); - expect(visitor).toHaveBeenCalledWith('dir/dir2/file2.ts'); - expect(visitor).not.toHaveBeenCalledWith('dir/node_modules/file1.ts'); - }); - - it.each(['', '.', '/', './'])( - 'should be able to visit the root path "%s"', - (dirPath) => { - tree.write('.gitignore', 'node_modules'); - - tree.write('dir/file1.ts', ''); - tree.write('dir/node_modules/file1.ts', ''); - tree.write('dir/dir2/file2.ts', ''); - - const visitor = jest.fn(); - visitNotIgnoredFiles(tree, dirPath, visitor); - - expect(visitor).toHaveBeenCalledWith('.gitignore'); - expect(visitor).toHaveBeenCalledWith('dir/file1.ts'); - expect(visitor).toHaveBeenCalledWith('dir/dir2/file2.ts'); - expect(visitor).not.toHaveBeenCalledWith('dir/node_modules/file1.ts'); - } - ); -}); diff --git a/packages/qwik/src/cli/migrate-v2/tools/visit-not-ignored-files.ts b/packages/qwik/src/cli/migrate-v2/tools/visit-not-ignored-files.ts index eadcd32baa23..6312e8f1a1fd 100644 --- a/packages/qwik/src/cli/migrate-v2/tools/visit-not-ignored-files.ts +++ b/packages/qwik/src/cli/migrate-v2/tools/visit-not-ignored-files.ts @@ -2,25 +2,19 @@ import { existsSync, lstatSync, readFileSync, readdirSync } from 'fs'; import ignore from 'ignore'; import { join, relative } from 'path'; -/** - * Utility to act on all files in a tree that are not ignored by git. - */ -export function visitNotIgnoredFiles( - dirPath: string, - visitor: (path: string) => void -): void { +/** Utility to act on all files in a tree that are not ignored by git. */ +export function visitNotIgnoredFiles(dirPath: string, visitor: (path: string) => void): void { let ig: ReturnType | undefined; if (existsSync('.gitignore')) { ig = ignore(); ig.add('.git'); ig.add(readFileSync('.gitignore', 'utf-8')); } - // TODO: test how it works if invoked not from the workspace root dirPath = relative(process.cwd(), dirPath); if (dirPath !== '' && ig?.ignores(dirPath)) { return; } - for (const child of readdirSync(dirPath)) { + for (const child of readdirSync(join(process.cwd(), dirPath))) { const fullPath = join(dirPath, child); if (ig?.ignores(fullPath)) { continue; diff --git a/packages/qwik/src/cli/migrate-v2/update-dependencies.ts b/packages/qwik/src/cli/migrate-v2/update-dependencies.ts new file mode 100644 index 000000000000..3a7796cec9d3 --- /dev/null +++ b/packages/qwik/src/cli/migrate-v2/update-dependencies.ts @@ -0,0 +1,25 @@ +import { getPackageManager, readPackageJson, writePackageJson } from './../utils/utils'; +import { installDeps } from '../utils/install-deps'; +import { versions } from './versions'; + +export async function updateDependencies() { + // TODO(migrate-v2): rely on workspaceRoot instead? + const packageJson = await readPackageJson(process.cwd()); + const devDependencies = (packageJson.devDependencies ??= {}); + + // TODO: this logic should be enhanced to check in both dependencies and devDependencies + for (const key of Object.keys(devDependencies)) { + if (Object.prototype.hasOwnProperty.call(versions, key)) { + // for now only updating existing dependencies if they exist in root package.json + devDependencies[key] = versions[key as unknown as keyof typeof versions]; + } + } + + await writePackageJson(process.cwd(), packageJson); + // TODO(migrate-v2): not installing dependencies because we don't have correct versions set + // const { install } = installDeps(getPackageManager(), process.cwd()); + // const passed = await install; + // if (!passed) { + // throw new Error('Failed to install dependencies'); + // } +} diff --git a/packages/qwik/src/cli/migrate-v2/versions.ts b/packages/qwik/src/cli/migrate-v2/versions.ts new file mode 100644 index 000000000000..80d758c2c5c3 --- /dev/null +++ b/packages/qwik/src/cli/migrate-v2/versions.ts @@ -0,0 +1,5 @@ +export const versions = { + '@qwik.dev/qwik': '2.0.0', + '@qwik.dev/city': '2.0.0', + 'eslint-plugin-qwik': '2.0.0', +}; diff --git a/packages/qwik/src/cli/run.ts b/packages/qwik/src/cli/run.ts index 507aae9a2cdc..bdfa8127f070 100644 --- a/packages/qwik/src/cli/run.ts +++ b/packages/qwik/src/cli/run.ts @@ -49,10 +49,10 @@ const COMMANDS = [ { value: 'migrate-v2', label: 'migrate-v2', - // TODO: package names + // TODO(migrate-v2): package names hint: 'Rescopes the application from @builder.io/* namespace to @qwik.dev/*', run: (app: AppCommand) => runV2Migration(app), - showInHelp: true, + showInHelp: false, }, { value: 'help',