diff --git a/.changeset/early-suns-cry.md b/.changeset/early-suns-cry.md new file mode 100644 index 000000000..13618ca2e --- /dev/null +++ b/.changeset/early-suns-cry.md @@ -0,0 +1,5 @@ +--- +'@skeletonlabs/skeleton-cli': patch +--- + +Feature: Better error handling is now in place. diff --git a/.changeset/itchy-knives-try.md b/.changeset/itchy-knives-try.md new file mode 100644 index 000000000..ab74dc56b --- /dev/null +++ b/.changeset/itchy-knives-try.md @@ -0,0 +1,5 @@ +--- +'@skeletonlabs/skeleton-cli': patch +--- + +Feature: `<packagemanager> install` is now ran at the end of the migration. diff --git a/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/index.ts b/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/index.ts index d47364355..128658e21 100644 --- a/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/index.ts +++ b/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/index.ts @@ -5,10 +5,11 @@ import type { MigrateOptions } from '../../index.js'; import { isCancel, multiselect, spinner } from '@clack/prompts'; import { cli } from '../../../../index.js'; import { extname } from 'node:path'; -import { transformSvelte } from './transformers/transform-svelte'; -import { transformModule } from './transformers/transform-module'; -import { transformApp } from './transformers/transform-app'; +import { transformSvelte } from './transformers/transform-svelte.js'; +import { transformModule } from './transformers/transform-module.js'; +import { transformApp } from './transformers/transform-app.js'; import { readFile, writeFile } from 'node:fs/promises'; +import { installDependencies } from '../../../../utility/install-dependencies.js'; export default async function (options: MigrateOptions) { const cwd = options.cwd ?? process.cwd(); @@ -35,7 +36,11 @@ export default async function (options: MigrateOptions) { } } - const availableSourceFolders = await fg('*', { cwd, onlyDirectories: true, ignore: ['node_modules'] }); + const availableSourceFolders = await fg('*', { + cwd: cwd, + onlyDirectories: true, + ignore: ['node_modules'] + }); const sourceFolders = await multiselect({ message: 'What folders contain usage of Skeleton? (classes, imports, etc.)', options: availableSourceFolders.map((folder) => ({ label: folder, value: folder })), @@ -43,51 +48,95 @@ export default async function (options: MigrateOptions) { }); if (isCancel(sourceFolders)) { - cli.error('Migration cancelled.'); + cli.error('Migration cancelled by user.'); return; } const packageSpinner = spinner(); packageSpinner.start(`Migrating ${pkg.matcher}...`); - const pkgCode = await readFile(pkg.paths[0], 'utf-8'); - const transformedPkg = await transformPackage(pkgCode); - await writeFile(pkg.paths[0], transformedPkg.code); - packageSpinner.stop(`Successfully migrated ${pkg.matcher}`); + try { + const pkgCode = await readFile(pkg.paths[0], 'utf-8'); + const transformedPkg = await transformPackage(pkgCode); + await writeFile(pkg.paths[0], transformedPkg.code); + packageSpinner.stop(`Successfully migrated ${pkg.matcher}`); + } catch (e) { + if (e instanceof Error) { + packageSpinner.stop(`Failed to migrate ${pkg.matcher}: ${e.message}`); + } + packageSpinner.stop(`Failed to migrate ${pkg.matcher}`); + } + + let themeName: string | null = null; const tailwindSpinner = spinner(); tailwindSpinner.start(`Migrating ${tailwindConfig.matcher}...`); - const tailwindCode = await readFile(tailwindConfig.paths[0], 'utf-8'); - const transformedTailwind = transformTailwindConfig(tailwindCode); - await writeFile(tailwindConfig.paths[0], transformedTailwind.code); - tailwindSpinner.stop(`Successfully migrated ${tailwindConfig.matcher}`); - - const theme = transformedTailwind.meta.themes.find((theme) => theme.type === 'preset'); + try { + const tailwindCode = await readFile(tailwindConfig.paths[0], 'utf-8'); + const transformedTailwind = transformTailwindConfig(tailwindCode); + themeName = transformedTailwind.meta.themes.find((theme) => theme.type === 'preset')?.name ?? null; + await writeFile(tailwindConfig.paths[0], transformedTailwind.code); + tailwindSpinner.stop(`Successfully migrated ${tailwindConfig.matcher}`); + } catch (e) { + if (e instanceof Error) { + tailwindSpinner.stop(`Failed to migrate ${tailwindConfig.matcher}: ${e.message}`); + } + tailwindSpinner.stop(`Failed to migrate ${tailwindConfig.matcher}`); + } const appSpinner = spinner(); appSpinner.start(`Migrating ${app.matcher}...`); - const appCode = await readFile(app.paths[0], 'utf-8'); - const transformedApp = transformApp(appCode, theme?.name ?? 'cerberus'); - await writeFile(app.paths[0], transformedApp.code); - appSpinner.stop(`Successfully migrated ${app.matcher}`); + try { + const appCode = await readFile(app.paths[0], 'utf-8'); + const transformedApp = transformApp(appCode, themeName ?? 'cerberus'); + await writeFile(app.paths[0], transformedApp.code); + appSpinner.stop(`Successfully migrated ${app.matcher}!`); + } catch (e) { + if (e instanceof Error) { + appSpinner.stop(`Failed to migrate ${app.matcher}: ${e.message}`); + } + appSpinner.stop(`Failed to migrate ${app.matcher}.`); + } const sourceFileMatcher = `{${sourceFolders.join(',')}}/**/*.{js,mjs,ts,mts,svelte}`; - const sourceFiles = await fg(sourceFileMatcher, { cwd, ignore: ['node_modules', 'dist', 'build', 'public'] }); + const sourceFiles = await fg(sourceFileMatcher, { + cwd: cwd, + ignore: ['node_modules'] + }); const sourceFilesSpinner = spinner(); sourceFilesSpinner.start(`Migrating source files...`); - for (const sourceFile of sourceFiles) { - sourceFilesSpinner.message(`Migrating ${sourceFile}...`); - const extension = extname(sourceFile); - if (extension === '.svelte') { - const svelteCode = await readFile(sourceFile, 'utf-8'); - const transformedSvelte = transformSvelte(svelteCode); - await writeFile(sourceFile, transformedSvelte.code); - } else { - const moduleCode = await readFile(sourceFile, 'utf-8'); - const transformedModule = transformModule(moduleCode); - await writeFile(sourceFile, transformedModule.code); + try { + for (const sourceFile of sourceFiles) { + sourceFilesSpinner.message(`Migrating ${sourceFile}...`); + const extension = extname(sourceFile); + if (extension === '.svelte') { + const svelteCode = await readFile(sourceFile, 'utf-8'); + const transformedSvelte = transformSvelte(svelteCode); + await writeFile(sourceFile, transformedSvelte.code); + } else { + const moduleCode = await readFile(sourceFile, 'utf-8'); + const transformedModule = transformModule(moduleCode); + await writeFile(sourceFile, transformedModule.code); + } + sourceFilesSpinner.message(`Successfully migrated ${sourceFile}!`); + } + sourceFilesSpinner.stop('Successfully migrated all source files!'); + } catch (error) { + if (error instanceof Error) { + sourceFilesSpinner.stop(`Failed to migrate source files: ${error.message}`); + } + sourceFilesSpinner.stop('Failed to migrate source files.'); + } + + const installDependenciesSpinner = spinner(); + installDependenciesSpinner.start('Installing dependencies...'); + try { + await installDependencies(cwd); + installDependenciesSpinner.stop('Successfully installed dependencies!'); + } catch (e) { + if (e instanceof Error) { + installDependenciesSpinner.stop(`Failed to install dependencies: ${e.message}`); } - sourceFilesSpinner.message(`Successfully migrated ${sourceFile}`); + installDependenciesSpinner.stop('Failed to install dependencies.'); } - sourceFilesSpinner.stop('Successfully migrated all source files'); } diff --git a/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/transformers/transform-app.test.ts b/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/transformers/transform-app.test.ts index 3e1198392..32e07ac24 100644 --- a/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/transformers/transform-app.test.ts +++ b/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/transformers/transform-app.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { transformApp } from './transform-app.js'; describe('transformsApp', () => { - it('edits the `data-them` attribute when already present', () => { + it('transforms the `data-them` attribute when present', () => { expect( transformApp( ` @@ -27,7 +27,7 @@ describe('transformsApp', () => { .replace(/\r\n|\r|\n/g, '\n') ); }); - it('adds the `data-theme` attribute ', () => { + it('adds the `data-theme` attribute', () => { expect( transformApp( ` diff --git a/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/transformers/transform-app.ts b/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/transformers/transform-app.ts index 1434467cc..ce7071ae0 100644 --- a/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/transformers/transform-app.ts +++ b/packages/skeleton-cli/src/commands/migrate/migrations/skeleton-3/transformers/transform-app.ts @@ -13,10 +13,11 @@ function transformApp(code: string, theme: string) { const dataThemeAttribute = node.attributes.find((attribute) => { return attribute.type === 'Attribute' && attribute.name === 'data-theme'; }); + const newDataThemeAttribute = `data-theme="${theme}"`; if (dataThemeAttribute) { - s.update(dataThemeAttribute.start, dataThemeAttribute.end, `data-theme="${theme}"`); + s.update(dataThemeAttribute.start, dataThemeAttribute.end, newDataThemeAttribute); } else { - s.appendRight(node.start + '<body'.length, ` data-theme="${theme}"`); + s.appendLeft(node.start + '<body'.length, ` ${newDataThemeAttribute}`); } } } diff --git a/packages/skeleton-cli/src/utility/install-dependencies.ts b/packages/skeleton-cli/src/utility/install-dependencies.ts new file mode 100644 index 000000000..a60b32fe7 --- /dev/null +++ b/packages/skeleton-cli/src/utility/install-dependencies.ts @@ -0,0 +1,18 @@ +import { detect, resolveCommand } from 'package-manager-detector'; +import { execSync } from 'node:child_process'; + +async function installDependencies(cwd = process.cwd()) { + const pm = await detect({ + cwd: cwd + }); + const resolvedCommand = resolveCommand(pm?.agent ?? 'npm', 'install', ['--force']); + if (!resolvedCommand) { + throw new Error('Could not resolve package manager command.'); + } + execSync(`${resolvedCommand.command} ${resolvedCommand.args.join(' ')}`, { + cwd: cwd, + stdio: 'ignore' + }); +} + +export { installDependencies }; diff --git a/packages/skeleton-cli/tsconfig.json b/packages/skeleton-cli/tsconfig.json index 0bbdd13f0..387421669 100644 --- a/packages/skeleton-cli/tsconfig.json +++ b/packages/skeleton-cli/tsconfig.json @@ -10,6 +10,7 @@ "noEmit": true, "esModuleInterop": true, "isolatedModules": true, + "verbatimModuleSyntax": true, "skipDefaultLibCheck": true, "skipLibCheck": true }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7578634fb..8507776c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,57 +6,6 @@ settings: catalogs: default: - '@zag-js/accordion': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/avatar': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/combobox': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/dialog': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/file-upload': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/pagination': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/popover': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/progress': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/radio-group': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/rating-group': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/react': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/slider': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/svelte': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/switch': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/tabs': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/tags-input': - specifier: ^0.78.3 - version: 0.78.3 - '@zag-js/tooltip': - specifier: ^0.78.3 - version: 0.78.3 typescript: specifier: ^5.6.3 version: 5.6.3