UNPKG

@teambit/isolator

Version:
130 lines (112 loc) 4.04 kB
import type { AnyFS } from '@teambit/any-fs'; import { NodeFS } from '@teambit/any-fs'; import type { Container, ContainerFactoryOptions, ContainerStatus, Exec, ExecOptions } from '@teambit/capsule'; import execa from 'execa'; import fs from 'fs-extra'; import * as path from 'path'; import type { Stream } from 'stream'; import ContainerExec from './container-exec'; const debug = require('debug')('fs-container'); export interface BitExecOption extends ExecOptions { cwd: string; stdio?: 'pipe' | 'ipc' | 'ignore' | 'inherit' | Stream | number | undefined; } export interface BitContainerConfig extends ContainerFactoryOptions { other?: string; } export default class FsContainer implements Container<Exec, AnyFS> { id = 'FS Container'; fs: NodeFS = new NodeFS(this.wrkDir); constructor(readonly wrkDir: string) {} // TODO: do we need this? public getPath() { return this.wrkDir; } private composePath(pathToCompose) { return path.join(this.getPath(), pathToCompose); } outputFile(file, data, options) { const filePath = this.composePath(file); debug(`writing file on ${filePath}`); return fs.outputFile(filePath, data, options); } removePath(dir: string): Promise<any> { const pathToRemove = this.composePath(dir); return fs.remove(pathToRemove); } async symlink(src: string, dest: string): Promise<any> { const srcPath = this.composePath(src); const destPath = this.composePath(dest); await fs.ensureDir(path.dirname(destPath)); return fs.ensureSymlink(srcPath, destPath); } async exec(execOptions: BitExecOption, exec = new ContainerExec()): Promise<ContainerExec> { const cwd = execOptions.cwd ? this.composePath(execOptions.cwd) : this.getPath(); debug(`executing the following command: ${execOptions.command.join(' ')}, on cwd: ${cwd}`); const subprocessP = execa.command(execOptions.command.join(' '), { shell: true, cwd, stdio: ['ipc'], }); // @TODO: FIX! This probably causes errors ad the promise is not handled properly! // eslint-disable-next-line @typescript-eslint/no-floating-promises subprocessP.on('message', function (msg: any) { exec.emit('message', msg); }); /* eslint-disable @typescript-eslint/no-non-null-assertion */ subprocessP.stderr?.pipe(exec.stderr); subprocessP.stdout?.pipe(exec.stdout); ['close', 'exit'].forEach(function (eventName: string) { // @TODO: FIX! This probably causes errors ad the promise is not handled properly! // eslint-disable-next-line @typescript-eslint/no-floating-promises subprocessP.on(eventName, function (statusCode) { exec.setStatus(statusCode); }); }); return exec; } async execP(execOptions: BitExecOption): Promise<string> { let hasError = false; const exec = await this.exec(execOptions); return new Promise((resolve, reject) => { exec.stdout.on('error', () => { hasError = true; }); exec.on('close', () => { if (hasError) reject(exec.stderr.getContents!(exec.stderr.size).toString()); resolve(exec.stdout.getContents!(exec.stdout.size).toString()); }); }); } async terminal() { const cwd = this.getPath(); return execa.command(process.env.SHELL || '/bin/zsh', { cwd, stdio: 'inherit' }); } start(): Promise<void> { return fs.ensureDir(this.wrkDir); } // @ts-ignore async inspect(): Promise<ContainerStatus> { // todo: probably not needed for this container } async pause(): Promise<void> { // do nothing } async resume(): Promise<void> { // do nothing } // eslint-disable-next-line stop(ttl?: number | undefined): Promise<void> { return fs.remove(this.wrkDir); } async destroy(): Promise<void> { await this.stop(); } log(): Promise<Exec> { throw new Error('Method not implemented.'); } on(event: string, fn: (data: any) => void): void { return fn(event); // throw new Error('Method not implemented.'); } }