Browse Source

refactor(cli): Create PackageJson class

Michael Bromley 1 year ago
parent
commit
15c602c12b

+ 4 - 4
packages/cli/src/commands/add/codegen/add-codegen.ts

@@ -1,11 +1,10 @@
 import { log, note, outro, spinner } from '@clack/prompts';
 import path from 'path';
-import pc from 'picocolors';
 import { ClassDeclaration, StructureKind, SyntaxKind } from 'ts-morph';
 
 import { selectMultiplePluginClasses } from '../../../shared/shared-prompts';
 import { createFile, getRelativeImportPath, getTsMorphProject } from '../../../utilities/ast-utils';
-import { addNpmScriptToPackageJson, installRequiredPackages } from '../../../utilities/package-utils';
+import { PackageJson } from '../../../utilities/package-utils';
 
 export async function addCodegen(providedPluginClass?: ClassDeclaration) {
     let pluginClasses = providedPluginClass ? [providedPluginClass] : [];
@@ -19,10 +18,11 @@ export async function addCodegen(providedPluginClass?: ClassDeclaration) {
         pluginClasses = await selectMultiplePluginClasses(project, 'Add codegen cancelled');
     }
 
+    const packageJson = new PackageJson(project);
     const installSpinner = spinner();
     installSpinner.start(`Installing dependencies...`);
     try {
-        await installRequiredPackages(project, [
+        await packageJson.installPackages([
             {
                 pkg: '@graphql-codegen/cli',
                 isDevDependency: true,
@@ -75,7 +75,7 @@ export async function addCodegen(providedPluginClass?: ClassDeclaration) {
     }
     codegenFile.move(path.join(rootDir.getPath(), 'codegen.ts'));
 
-    addNpmScriptToPackageJson(tempProject, 'codegen', 'graphql-codegen --config codegen.ts');
+    packageJson.addScript('codegen', 'graphql-codegen --config codegen.ts');
 
     configSpinner.stop('Configured codegen file');
 

+ 4 - 3
packages/cli/src/commands/add/ui-extensions/add-ui-extensions.ts

@@ -9,7 +9,7 @@ import {
     getTsMorphProject,
     getVendureConfig,
 } from '../../../utilities/ast-utils';
-import { determineVendureVersion, installRequiredPackages } from '../../../utilities/package-utils';
+import { PackageJson } from '../../../utilities/package-utils';
 
 import { addUiExtensionStaticProp } from './codemods/add-ui-extension-static-prop/add-ui-extension-static-prop';
 import { updateAdminUiPluginInit } from './codemods/update-admin-ui-plugin-init/update-admin-ui-plugin-init';
@@ -25,6 +25,7 @@ export async function addUiExtensions(providedPluginClass?: ClassDeclaration) {
         projectSpinner.stop('Project analyzed');
         pluginClass = await selectPluginClass(project, 'Add UI extensions cancelled');
     }
+    const packageJson = new PackageJson(project);
 
     if (pluginAlreadyHasUiExtensionProp(pluginClass)) {
         outro('This plugin already has UI extensions configured');
@@ -36,8 +37,8 @@ export async function addUiExtensions(providedPluginClass?: ClassDeclaration) {
     const installSpinner = spinner();
     installSpinner.start(`Installing dependencies...`);
     try {
-        const version = determineVendureVersion(project);
-        await installRequiredPackages(project, [
+        const version = packageJson.determineVendureVersion();
+        await packageJson.installPackages([
             {
                 pkg: '@vendure/ui-devkit',
                 isDevDependency: true,

+ 96 - 98
packages/cli/src/utilities/package-utils.ts

@@ -10,116 +10,114 @@ export interface PackageToInstall {
     isDevDependency?: boolean;
 }
 
-export function determineVendureVersion(project: Project): string | undefined {
-    const packageJson = getPackageJsonContent(project);
-    return packageJson.dependencies['@vendure/core'];
-}
-
-/**
- * @description
- * Installs the packages with the appropriate package manager if the package
- * is not already found in the package.json file.
- */
-export async function installRequiredPackages(project: Project, requiredPackages: PackageToInstall[]) {
-    const packageJson = getPackageJsonContent(project);
-    const packagesToInstall = requiredPackages.filter(({ pkg, version, isDevDependency }) => {
-        const hasDependency = isDevDependency
-            ? packageJson.devDependencies[pkg]
-            : packageJson.dependencies[pkg];
-        return !hasDependency;
-    });
+export class PackageJson {
+    constructor(private readonly project: Project) {}
 
-    const depsToInstall = packagesToInstall
-        .filter(p => !p.isDevDependency && packageJson.dependencies?.[p.pkg] === undefined)
-        .map(p => `${p.pkg}${p.version ? `@${p.version}` : ''}`);
-    const devDepsToInstall = packagesToInstall
-        .filter(p => p.isDevDependency && packageJson.devDependencies?.[p.pkg] === undefined)
-        .map(p => `${p.pkg}${p.version ? `@${p.version}` : ''}`);
-    if (depsToInstall.length) {
-        await installPackages(depsToInstall, false);
+    determineVendureVersion(): string | undefined {
+        const packageJson = this.getPackageJsonContent();
+        return packageJson.dependencies['@vendure/core'];
     }
-    if (devDepsToInstall.length) {
-        await installPackages(devDepsToInstall, true);
-    }
-}
 
-export async function installPackages(dependencies: string[], isDev: boolean) {
-    return new Promise<void>((resolve, reject) => {
-        const packageManager = determinePackageManagerBasedOnLockFile();
-        let command = '';
-        let args: string[] = [];
-        if (packageManager === 'yarn') {
-            command = 'yarnpkg';
-            args = ['add', '--exact', '--ignore-engines'];
-            if (isDev) {
-                args.push('--dev');
-            }
-
-            args = args.concat(dependencies);
-        } else {
-            command = 'npm';
-            args = ['install', '--save', '--save-exact', '--loglevel', 'error'].concat(dependencies);
-            if (isDev) {
-                args.push('--save-dev');
-            }
-        }
-        const child = spawn(command, args, { stdio: 'ignore' });
-        child.on('close', code => {
-            if (code !== 0) {
-                const message = 'An error occurred when installing dependencies.';
-                reject({
-                    message,
-                    command: `${command} ${args.join(' ')}`,
-                });
-                return;
-            }
-            resolve();
+    async installPackages(requiredPackages: PackageToInstall[]) {
+        const packageJson = this.getPackageJsonContent();
+        const packagesToInstall = requiredPackages.filter(({ pkg, version, isDevDependency }) => {
+            const hasDependency = isDevDependency
+                ? packageJson.devDependencies[pkg]
+                : packageJson.dependencies[pkg];
+            return !hasDependency;
         });
-    });
-}
 
-export function addNpmScriptToPackageJson(project: Project, scriptName: string, script: string) {
-    const packageJson = getPackageJsonContent(project);
-    if (!packageJson) {
-        return;
-    }
-    packageJson.scripts = packageJson.scripts || {};
-    packageJson.scripts[scriptName] = script;
-    const rootDir = project.getDirectory('.');
-    if (!rootDir) {
-        throw new Error('Could not find the root directory of the project');
+        const depsToInstall = packagesToInstall
+            .filter(p => !p.isDevDependency && packageJson.dependencies?.[p.pkg] === undefined)
+            .map(p => `${p.pkg}${p.version ? `@${p.version}` : ''}`);
+        const devDepsToInstall = packagesToInstall
+            .filter(p => p.isDevDependency && packageJson.devDependencies?.[p.pkg] === undefined)
+            .map(p => `${p.pkg}${p.version ? `@${p.version}` : ''}`);
+        if (depsToInstall.length) {
+            await this.runPackageManagerInstall(depsToInstall, false);
+        }
+        if (devDepsToInstall.length) {
+            await this.runPackageManagerInstall(devDepsToInstall, true);
+        }
     }
-    const packageJsonPath = path.join(rootDir.getPath(), 'package.json');
-    fs.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
-}
 
-function determinePackageManagerBasedOnLockFile(): 'yarn' | 'npm' | 'pnpm' {
-    const yarnLockPath = path.join(process.cwd(), 'yarn.lock');
-    const npmLockPath = path.join(process.cwd(), 'package-lock.json');
-    const pnpmLockPath = path.join(process.cwd(), 'pnpm-lock.yaml');
-    if (fs.existsSync(yarnLockPath)) {
-        return 'yarn';
+    getPackageJsonContent() {
+        const packageJsonPath = path.join(this.getPackageRootDir().getPath(), 'package.json');
+        if (!fs.existsSync(packageJsonPath)) {
+            note(
+                `Could not find a package.json in the current directory. Please run this command from the root of a Vendure project.`,
+            );
+            return false;
+        }
+        return fs.readJsonSync(packageJsonPath);
     }
-    if (fs.existsSync(npmLockPath)) {
+
+    determinePackageManager(): 'yarn' | 'npm' | 'pnpm' {
+        const rootDir = this.getPackageRootDir().getPath();
+        const yarnLockPath = path.join(rootDir, 'yarn.lock');
+        const npmLockPath = path.join(rootDir, 'package-lock.json');
+        const pnpmLockPath = path.join(rootDir, 'pnpm-lock.yaml');
+        if (fs.existsSync(yarnLockPath)) {
+            return 'yarn';
+        }
+        if (fs.existsSync(npmLockPath)) {
+            return 'npm';
+        }
+        if (fs.existsSync(pnpmLockPath)) {
+            return 'pnpm';
+        }
         return 'npm';
     }
-    if (fs.existsSync(pnpmLockPath)) {
-        return 'pnpm';
+
+    addScript(scriptName: string, script: string) {
+        const packageJson = this.getPackageJsonContent();
+        packageJson.scripts = packageJson.scripts || {};
+        packageJson.scripts[scriptName] = script;
+        const rootDir = this.getPackageRootDir();
+        const packageJsonPath = path.join(rootDir.getPath(), 'package.json');
+        fs.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
     }
-    return 'npm';
-}
 
-function getPackageJsonContent(project: Project) {
-    const rootDir = project.getDirectory('.');
-    if (!rootDir) {
-        throw new Error('Could not find the root directory of the project');
+    private getPackageRootDir() {
+        const rootDir = this.project.getDirectory('.');
+        if (!rootDir) {
+            throw new Error('Could not find the root directory of the project');
+        }
+        return rootDir;
     }
-    const packageJsonPath = path.join(rootDir.getPath(), 'package.json');
-    if (!fs.existsSync(packageJsonPath)) {
-        note(
-            `Could not find a package.json in the current directory. Please run this command from the root of a Vendure project.`,
-        );
-        return false;
+
+    private async runPackageManagerInstall(dependencies: string[], isDev: boolean) {
+        return new Promise<void>((resolve, reject) => {
+            const packageManager = this.determinePackageManager();
+            let command = '';
+            let args: string[] = [];
+            if (packageManager === 'yarn') {
+                command = 'yarnpkg';
+                args = ['add', '--exact', '--ignore-engines'];
+                if (isDev) {
+                    args.push('--dev');
+                }
+
+                args = args.concat(dependencies);
+            } else {
+                command = 'npm';
+                args = ['install', '--save', '--save-exact', '--loglevel', 'error'].concat(dependencies);
+                if (isDev) {
+                    args.push('--save-dev');
+                }
+            }
+            const child = spawn(command, args, { stdio: 'ignore' });
+            child.on('close', code => {
+                if (code !== 0) {
+                    const message = 'An error occurred when installing dependencies.';
+                    reject({
+                        message,
+                        command: `${command} ${args.join(' ')}`,
+                    });
+                    return;
+                }
+                resolve();
+            });
+        });
     }
-    return fs.readJsonSync(packageJsonPath);
 }