import * as fs from 'fs-extra'; import * as path from 'path'; import semver from 'semver'; import { IEnvironment } from '../types/environment'; import { spawnProcess } from '../util/run'; /** * Execute NPM commands */ export class NPMExecutor { private installed: Record = {}; private packageFile: string = path.join(this.environment.path, 'package.json'); constructor( private environment: IEnvironment, private coreModule: string ) {} /** * Create a package.json file and install the core. */ public async init(): Promise { // Initialize npm environment const c1 = await spawnProcess('npm', ['init', '-y'], this.environment); if (c1.code > 0) { throw new Error(c1.stderr.join('\n')); } // Install core module const c2 = await spawnProcess('npm', ['install', this.coreModule], this.environment); if (c2.code > 0) { throw new Error(c2.stderr.join('\n')); } } /** * Load a package.json file into memory */ public async loadPackageFile(): Promise { if (!await fs.pathExists(this.packageFile)) { await this.init(); } const jsonData = await fs.readJson(this.packageFile); if (!jsonData.dependencies) { return; } this.installed = jsonData.dependencies; } /** * Install a npm package (examples: `@squeebot/core`, `@squeebot/core@3.3.3`, `node-ical`) * @param pkg Package name */ public async installPackage(pkg: string): Promise { if (!await fs.pathExists(this.packageFile)) { await this.init(); } let spi = 0; if (pkg.indexOf('@') === 0) { spi = 1; } const pkgRefSplit = pkg.split('@'); const pkgFullName = (spi === 1 ? '@' : '') + pkgRefSplit[spi]; const pkgVersion = this.removeVersionWildcard(pkgRefSplit[spi + 1]); const installedVersion = this.installed[pkgFullName]; if (installedVersion && installedVersion !== 'latest' && pkgVersion !== 'latest') { const cardless = this.removeVersionWildcard(installedVersion); if (semver.lte(pkgVersion, cardless)) { return; } } const { code, stderr, stdout } = await spawnProcess('npm', ['install', pkg], this.environment); if (code > 0) { throw new Error(stderr.join('\n')); } await this.loadPackageFile(); } /** * Uninstall a npm package. * * See `installPackage` for more info. * @param pkg Package name */ public async uninstallPackage(pkg: string): Promise { if (!await fs.pathExists(this.packageFile)) { await this.init(); } const { code, stderr, stdout } = await spawnProcess('npm', ['remove', pkg], this.environment); if (code > 0) { throw new Error(stderr.join('\n')); } await this.loadPackageFile(); } private removeVersionWildcard(str: string): string { return str.replace(/\^|>|=/, ''); } }