UNPKG

5.99 kBJavaScriptView Raw
1import path from 'path';
2import { npmRunPathEnv } from 'npm-run-path';
3import { normalizeWhitespaces } from '@stryker-mutator/util';
4import { tokens, commonTokens } from '@stryker-mutator/api/plugin';
5import { fileUtils } from '../utils/file-utils.js';
6import { coreTokens } from '../di/index.js';
7import { objectUtils } from '../utils/index.js';
8export class Sandbox {
9 constructor(options, log, temporaryDirectory, project, execCommand, unexpectedExitHandler) {
10 this.options = options;
11 this.log = log;
12 this.temporaryDirectory = temporaryDirectory;
13 this.project = project;
14 this.execCommand = execCommand;
15 this.fileMap = new Map();
16 /**
17 * The backup directory when running in --inPlace mode
18 */
19 this.backupDirectory = '';
20 if (options.inPlace) {
21 this.workingDirectory = process.cwd();
22 this.backupDirectory = temporaryDirectory.getRandomDirectory('backup');
23 this.tempDirectory = this.backupDirectory;
24 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));
25 unexpectedExitHandler.registerHandler(this.dispose.bind(this, true));
26 }
27 else {
28 this.workingDirectory = temporaryDirectory.getRandomDirectory('sandbox');
29 this.tempDirectory = this.workingDirectory;
30 this.log.debug('Creating a sandbox for files in %s', this.workingDirectory);
31 }
32 }
33 async init() {
34 await this.temporaryDirectory.createDirectory(this.tempDirectory);
35 await this.fillSandbox();
36 await this.runBuildCommand();
37 await this.symlinkNodeModulesIfNeeded();
38 }
39 sandboxFileFor(fileName) {
40 const sandboxFileName = this.fileMap.get(fileName);
41 if (sandboxFileName === undefined) {
42 throw new Error(`Cannot find sandbox file for ${fileName}`);
43 }
44 return sandboxFileName;
45 }
46 originalFileFor(sandboxFileName) {
47 return path.resolve(sandboxFileName).replace(this.workingDirectory, process.cwd());
48 }
49 async fillSandbox() {
50 await Promise.all(objectUtils.map(this.project.files, (file, name) => this.sandboxFile(name, file)));
51 }
52 async runBuildCommand() {
53 if (this.options.buildCommand) {
54 const env = npmRunPathEnv();
55 this.log.info('Running build command "%s" in "%s".', this.options.buildCommand, this.workingDirectory);
56 this.log.debug('(using PATH: %s)', env.PATH);
57 await this.execCommand(this.options.buildCommand, { cwd: this.workingDirectory, env });
58 }
59 }
60 async symlinkNodeModulesIfNeeded() {
61 this.log.debug('Start symlink node_modules');
62 if (this.options.symlinkNodeModules && !this.options.inPlace) {
63 // TODO: Change with this.options.basePath when we have it
64 const basePath = process.cwd();
65 const nodeModulesList = await fileUtils.findNodeModulesList(basePath, this.options.tempDirName);
66 if (nodeModulesList.length > 0) {
67 for (const nodeModules of nodeModulesList) {
68 this.log.debug(`Create symlink from ${path.resolve(nodeModules)} to ${path.join(this.workingDirectory, nodeModules)}`);
69 await fileUtils
70 .symlinkJunction(path.resolve(nodeModules), path.join(this.workingDirectory, nodeModules))
71 .catch((error) => {
72 if (error.code === 'EEXIST') {
73 this.log.warn(normalizeWhitespaces(`Could not symlink "${nodeModules}" in sandbox directory,
74 it is already created in the sandbox. Please remove the node_modules from your sandbox files.
75 Alternatively, set \`symlinkNodeModules\` to \`false\` to disable this warning.`));
76 }
77 else {
78 this.log.warn(`Unexpected error while trying to symlink "${nodeModules}" in sandbox directory.`, error);
79 }
80 });
81 }
82 }
83 else {
84 this.log.debug(`Could not find a node_modules folder to symlink into the sandbox directory. Search "${basePath}" and its parent directories`);
85 }
86 }
87 }
88 /**
89 * Sandboxes a file (writes it to the sandbox). Either in-place, or an actual sandbox directory.
90 * @param name The name of the file
91 * @param file The file reference
92 */
93 async sandboxFile(name, file) {
94 if (this.options.inPlace) {
95 if (file.hasChanges) {
96 // File is changed (either mutated or by a preprocessor), make a backup and replace in-place
97 const backupFileName = await file.backupTo(this.backupDirectory);
98 this.log.debug('Stored backup file at %s', backupFileName);
99 await file.writeInPlace();
100 }
101 this.fileMap.set(name, name);
102 }
103 else {
104 const targetFileName = await file.writeToSandbox(this.workingDirectory);
105 this.fileMap.set(name, targetFileName);
106 }
107 }
108 dispose(unexpected = false) {
109 if (this.backupDirectory) {
110 if (unexpected) {
111 console.error(`Detecting unexpected exit, recovering original files from ${path.relative(process.cwd(), this.backupDirectory)}`);
112 }
113 else {
114 this.log.info(`Resetting your original files from ${path.relative(process.cwd(), this.backupDirectory)}.`);
115 }
116 fileUtils.moveDirectoryRecursiveSync(this.backupDirectory, this.workingDirectory);
117 }
118 }
119}
120Sandbox.inject = tokens(commonTokens.options, commonTokens.logger, coreTokens.temporaryDirectory, coreTokens.project, coreTokens.execa, coreTokens.unexpectedExitRegistry);
121//# sourceMappingURL=sandbox.js.map
\No newline at end of file