@stryker-mutator/core
Version:
The extendable JavaScript mutation testing framework
121 lines • 5.99 kB
JavaScript
import path from 'path';
import { npmRunPathEnv } from 'npm-run-path';
import { normalizeWhitespaces } from '@stryker-mutator/util';
import { tokens, commonTokens } from '@stryker-mutator/api/plugin';
import { fileUtils } from '../utils/file-utils.js';
import { coreTokens } from '../di/index.js';
import { objectUtils } from '../utils/index.js';
export class Sandbox {
constructor(options, log, temporaryDirectory, project, execCommand, unexpectedExitHandler) {
this.options = options;
this.log = log;
this.temporaryDirectory = temporaryDirectory;
this.project = project;
this.execCommand = execCommand;
this.fileMap = new Map();
/**
* The backup directory when running in --inPlace mode
*/
this.backupDirectory = '';
if (options.inPlace) {
this.workingDirectory = process.cwd();
this.backupDirectory = temporaryDirectory.getRandomDirectory('backup');
this.tempDirectory = this.backupDirectory;
this.log.info('In place mode is enabled, Stryker will be overriding YOUR files. Find your backup at: %s', path.relative(process.cwd(), this.backupDirectory));
unexpectedExitHandler.registerHandler(this.dispose.bind(this, true));
}
else {
this.workingDirectory = temporaryDirectory.getRandomDirectory('sandbox');
this.tempDirectory = this.workingDirectory;
this.log.debug('Creating a sandbox for files in %s', this.workingDirectory);
}
}
async init() {
await this.temporaryDirectory.createDirectory(this.tempDirectory);
await this.fillSandbox();
await this.runBuildCommand();
await this.symlinkNodeModulesIfNeeded();
}
sandboxFileFor(fileName) {
const sandboxFileName = this.fileMap.get(fileName);
if (sandboxFileName === undefined) {
throw new Error(`Cannot find sandbox file for ${fileName}`);
}
return sandboxFileName;
}
originalFileFor(sandboxFileName) {
return path.resolve(sandboxFileName).replace(this.workingDirectory, process.cwd());
}
async fillSandbox() {
await Promise.all(objectUtils.map(this.project.files, (file, name) => this.sandboxFile(name, file)));
}
async runBuildCommand() {
if (this.options.buildCommand) {
const env = npmRunPathEnv();
this.log.info('Running build command "%s" in "%s".', this.options.buildCommand, this.workingDirectory);
this.log.debug('(using PATH: %s)', env.PATH);
await this.execCommand(this.options.buildCommand, { cwd: this.workingDirectory, env });
}
}
async symlinkNodeModulesIfNeeded() {
this.log.debug('Start symlink node_modules');
if (this.options.symlinkNodeModules && !this.options.inPlace) {
// TODO: Change with this.options.basePath when we have it
const basePath = process.cwd();
const nodeModulesList = await fileUtils.findNodeModulesList(basePath, this.options.tempDirName);
if (nodeModulesList.length > 0) {
for (const nodeModules of nodeModulesList) {
this.log.debug(`Create symlink from ${path.resolve(nodeModules)} to ${path.join(this.workingDirectory, nodeModules)}`);
await fileUtils
.symlinkJunction(path.resolve(nodeModules), path.join(this.workingDirectory, nodeModules))
.catch((error) => {
if (error.code === 'EEXIST') {
this.log.warn(normalizeWhitespaces(`Could not symlink "${nodeModules}" in sandbox directory,
it is already created in the sandbox. Please remove the node_modules from your sandbox files.
Alternatively, set \`symlinkNodeModules\` to \`false\` to disable this warning.`));
}
else {
this.log.warn(`Unexpected error while trying to symlink "${nodeModules}" in sandbox directory.`, error);
}
});
}
}
else {
this.log.debug(`Could not find a node_modules folder to symlink into the sandbox directory. Search "${basePath}" and its parent directories`);
}
}
}
/**
* Sandboxes a file (writes it to the sandbox). Either in-place, or an actual sandbox directory.
* @param name The name of the file
* @param file The file reference
*/
async sandboxFile(name, file) {
if (this.options.inPlace) {
if (file.hasChanges) {
// File is changed (either mutated or by a preprocessor), make a backup and replace in-place
const backupFileName = await file.backupTo(this.backupDirectory);
this.log.debug('Stored backup file at %s', backupFileName);
await file.writeInPlace();
}
this.fileMap.set(name, name);
}
else {
const targetFileName = await file.writeToSandbox(this.workingDirectory);
this.fileMap.set(name, targetFileName);
}
}
dispose(unexpected = false) {
if (this.backupDirectory) {
if (unexpected) {
console.error(`Detecting unexpected exit, recovering original files from ${path.relative(process.cwd(), this.backupDirectory)}`);
}
else {
this.log.info(`Resetting your original files from ${path.relative(process.cwd(), this.backupDirectory)}.`);
}
fileUtils.moveDirectoryRecursiveSync(this.backupDirectory, this.workingDirectory);
}
}
}
Sandbox.inject = tokens(commonTokens.options, commonTokens.logger, coreTokens.temporaryDirectory, coreTokens.project, coreTokens.execa, coreTokens.unexpectedExitRegistry);
//# sourceMappingURL=sandbox.js.map