108 lines
2.9 KiB
TypeScript
108 lines
2.9 KiB
TypeScript
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<string, string> = {};
|
|
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<void> {
|
|
// 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<void> {
|
|
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<void> {
|
|
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]) || 'latest';
|
|
const installedVersion = this.installed[pkgFullName];
|
|
if (installedVersion && installedVersion !== 'latest' && pkgVersion !== 'latest') {
|
|
const cardless = this.removeVersionWildcard(installedVersion) as string;
|
|
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<void> {
|
|
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 | undefined {
|
|
return str?.replace(/\^|>|=/, '');
|
|
}
|
|
}
|