我尝试使用web pack来编译有效javascript代码的内存字符串。我正在使用此处概述的内存fs:https://webpack.github.io/docs/node.js-api.html#compile-to-memory。
所以我采用包含原始javascript的字符串,将其写入内存fs,然后web pack解析为该入口点。但是编译在第一个require语句中失败了,大概是因为它无法查找node_modules的实际fs。
关于如何实现这一目标的任何想法?
import webpack from 'webpack';
import MemoryFS from 'memory-fs';
import thenify from 'thenify';
function* compile(code) {
const fs = new MemoryFS();
fs.writeFileSync('/file.js', code);
const compiler = webpack({
entry: { file: '/file.js' },
output: {
path: '/build',
filename: '[name].js'
},
module: {
loaders: [
{ test: /\.json$/, loader: 'json' }
],
}
});
compiler.run = thenify(compiler.run);
compiler.inputFileSystem = fs;
compiler.resolvers.normal.fileSystem = fs; //this is needed for memfs
compiler.outputFileSystem = fs;
const stats = yield compiler.run();
//retrieve the output of the compilation
const res = stats.compilation.assets['file.js'].source();
return res;
}
用法
var code = "var _ = require('underscore'); console.log(_);";
var bundle = yield compile(code); //should be a bundle containing the underscore source.
错误是
ModuleNotFoundError:找不到模块:错误:无法解析模块 / /
中的下划线
这个问题表明其他人尝试过同样的事情:https://github.com/webpack/webpack/issues/1562。我在https://gist.github.com/DatenMetzgerX/2a96ebf287b4311f4c18引用的一个要点是我打算做我希望完成的事情,但在目前的形式中,我不知道如何做。它为所有解析器分配一个MemoryF实例。我尝试过分配节点的fs模块,但没有骰子。
简而言之,我试图设置一个原始javascript的内存字符串的入口点,但仍然将require和import语句解析为磁盘上的node_modules。
更新
我已经能够得到我想要的结果,但它并不漂亮。我基本上重写了MemoryFS中#stat和#readFile的实现 检查真实文件系统是否有任何请求内存中不存在的文件。我可以通过继承MemoryFS而不是在运行时交换方法实现来清理这一点,但这个想法仍然是一样的。
工作解决方案
import webpack from 'webpack';
import JsonLoader from 'json-loader';
import MemoryFS from 'memory-fs';
import UglifyJS from "uglify-js";
import thenify from 'thenify';
import path from 'path';
import fs from 'fs';
import root from 'app-root-path';
/*
* Provide webpack with an instance of MemoryFS for
* in-memory compilation. We're currently overriding
* #stat and #readFile. Webpack will ask MemoryFS for the
* entry file, which it will find successfully. However,
* all dependencies are on the real filesystem, so any require
* or import statements will fail. When that happens, our wrapper
* functions will then check fs for the requested file.
*/
const memFs = new MemoryFS();
const statOrig = memFs.stat.bind(memFs);
const readFileOrig = memFs.readFile.bind(memFs);
memFs.stat = function (_path, cb) {
statOrig(_path, function(err, result) {
if (err) {
return fs.stat(_path, cb);
} else {
return cb(err, result);
}
});
};
memFs.readFile = function (path, cb) {
readFileOrig(path, function (err, result) {
if (err) {
return fs.readFile(path, cb);
} else {
return cb(err, result);
}
});
};
export default function* compile(code) {
// Setup webpack
//create a directory structure in MemoryFS that matches
//the real filesystem
const rootDir = root.toString();
//write code snippet to memoryfs
const outputName = `file.js`;
const entry = path.join(rootDir, outputName);
const rootExists = memFs.existsSync(rootDir);
if (!rootExists) {
memFs.mkdirpSync(rootDir);
}
memFs.writeFileSync(entry, code);
//point webpack to memoryfs for the entry file
const compiler = webpack({
entry: entry,
output: {
filename: outputName
},
module: {
loaders: [
{ test: /\.json$/, loader: 'json' }
]
}
});
compiler.run = thenify(compiler.run);
//direct webpack to use memoryfs for file input
compiler.inputFileSystem = memFs;
compiler.resolvers.normal.fileSystem = memFs;
//direct webpack to output to memoryfs rather than to disk
compiler.outputFileSystem = memFs;
const stats = yield compiler.run();
//remove entry from memory. we're done with it
memFs.unlinkSync(entry);
const errors = stats.compilation.errors;
if (errors && errors.length > 0) {
//if there are errors, throw the first one
throw errors[0];
}
//retrieve the output of the compilation
const res = stats.compilation.assets[outputName].source();
return res;
}
用法
var code = "var _ = require('underscore'); console.log(_);";
var bundle = yield compile(code); //is a valid js bundle containing the underscore source and a log statement logging _.
如果没有更好的方法,那么我绝对会把它封装到MemoryFS的子类中,但是我希望通过Webpack实现这一目标更加理智#&# 39; s api。
答案 0 :(得分:1)
我创建了这个未经测试的代码段。我想你想要inputFS是真实的,输出fs是内存中的。另一方面,您希望单独构造file.js的所有依赖项。为此,我认为webpack.optimize.CommonsChunkPlugin插件可以提供帮助。我希望webpack能够将所有内容写入内存。我希望它有效。
import webpack from 'webpack';
import MemoryFS from 'memory-fs';
import thenify from 'thenify';
import realFS from 'fs';
function* compile(code) {
const fs = new MemoryFS();
const compiler = webpack({
entry: {
file: '/file.js',
vendor: [
'underscore',
'other-package-name'
]
},
output: {
path: '/build',
filename: '[name].js'
},
module: {
loaders: [
{ test: /\.json$/, loader: 'json' }
],
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js')
]
});
compiler.run = thenify(compiler.run);
compiler.inputFileSystem = realFS;
compiler.resolvers.normal.fileSystem = fs; //this is needed for memfs
compiler.outputFileSystem = fs;
const stats = yield compiler.run();
//retrieve the output of the compilation
const res = stats.compilation.assets['file.js'].source();
return res;
}
答案 1 :(得分:1)
而不是memory-fs,unionfs / memfs / linkfs的组合应该有所帮助。
答案 2 :(得分:0)
您正在使用 MemoryFS ,这是通常由操作系统处理的功能的JavaScript重新实现。我想知道,您是否可以在操作系统级别使用 tmpfs 安装目录,然后使用它? webpack 然后不知道或不关心输入文件实际存储在内存中。
假设您已在 / media / memory 安装了基于内存的文件系统, webpack 配置代码可能就像这样简单:
resolve: {
root: ['/media/memory', ...other paths...],
},
output: {
path: '/wherever/you/want/the/output/files'
}
}
这种方法也有一个隐藏的好处:如果要调试输入代码,只需使用非基于RAM的文件系统挂载 / media / memory ,就可以看到正在生成的内容。