import fs from 'fs' import path from 'path' import sourceMapSupport from 'source-map-support' import MemoryFileSystem from 'memory-fs' import { Compiler, Stats } from 'webpack' import registerRequireHook from '../../util/registerRequireHook' import { ensureAbsolutePath } from '../../util/paths' import { MOCHAPACK_NAME } from '../../util/constants' type VoidFunction = () => void export default function registerInMemoryCompiler( compiler: Compiler ): VoidFunction { // register memory fs to webpack const memoryFs = new MemoryFileSystem() // the flag is used for integration tests that need to read compiled files if (!process.env.MOCHAPACK_WRITE_TO_DISK) { compiler.outputFileSystem = memoryFs // eslint-disable-line no-param-reassign } // build asset map to allow fast checks for file existence const assetMap = new Map() compiler.hooks.done.tap(MOCHAPACK_NAME, (stats: Stats) => { assetMap.clear() if (!stats.hasErrors()) { Object.keys(stats.compilation.assets).forEach(assetPath => assetMap.set( ensureAbsolutePath(assetPath, compiler.options.output.path), true ) ) } }) // provide file reader to read from memory fs let readFile = filePath => { if (assetMap.has(filePath)) { try { const code = memoryFs.readFileSync(filePath, 'utf8') return code } catch (e) { return null } } return null } // module resolver for require calls from memory fs const resolveFile = (filePath, requireCaller) => { // try to read file from memory-fs as it is let code = readFile(filePath) let resolvedPath = filePath if (code === null && requireCaller != null) { const { filename } = requireCaller if (filename != null) { // if that didn't work, resolve the file relative to it's parent resolvedPath = path.resolve(path.dirname(filename), filePath) code = readFile(resolvedPath) } } return { path: code !== null ? resolvedPath : null, source: code } } // install require hook to be able to require webpack bundles from memory const unmountHook = registerRequireHook('.js', resolveFile) // install source map support to read source map from memory sourceMapSupport.install({ emptyCacheBetweenOperations: true, handleUncaughtExceptions: false, environment: 'node', retrieveFile: f => readFile(f) // wrapper function to fake an unmount function }) return function unmount() { unmountHook() readFile = filePath => null // eslint-disable-line no-unused-vars } }