Загрузить модуль node.js из строки в памяти
Как бы я потребовал () файл, если бы у меня было содержимое файла в виде строки в памяти, без записи его на диск? Вот пример:
// Load the file as a string
var strFileContents = fs.readFileSync( "./myUnalteredModule.js", 'utf8' );
// Do some stuff to the files contents
strFileContents[532] = '6';
// Load it as a node module (how would I do this?)
var loadedModule = require( doMagic(strFileContents) );
5 ответов
function requireFromString(src, filename) {
var Module = module.constructor;
var m = new Module();
m._compile(src, filename);
return m.exports;
}
console.log(requireFromString('module.exports = { test: 1}'));
посмотрите на _compile, _extensions и _load в module.js
Андрей уже ответил на вопрос, но я столкнулся с недостатком, который мне пришлось решить и который мог бы заинтересовать других.
Я хотел, чтобы модуль в запомненной строке мог загружать другие модули через require
, но путь модуля был нарушен с помощью вышеуказанного решения (например, игла не была найдена). Я попытался найти элегантное решение для поддержки путей, используя некоторую существующую функцию, но я закончил с жесткой разводкой путей:
function requireFromString(src, filename) {
var m = new module.constructor();
m.paths = module.paths;
m._compile(src, filename);
return m.exports;
}
var codeString = 'var needle = require(\'needle\');\n'
+ '[...]\n'
+ 'exports.myFunc = myFunc;';
var virtMod = requireFromString(codeString);
console.log('Available public functions: '+Object.keys(virtMod));
После этого я смог загрузить все существующие модули из строкового модуля. Любые комментарии или лучшие решения высоко ценятся!
require-from-string
пакет делает работу.
Использование:
var requireFromString = require('require-from-string');
requireFromString('module.exports = 1');
//=> 1
После анализа исходного кода таких решений, как pirates
а также require-from-string
Я пришел к выводу, что простой fs
а также Module
методы были бы не хуже с точки зрения поддержки. И с точки зрения функциональности это будет лучше, потому что он поддерживает @babel/register
, pirates
и другие модули, которые изменяют процесс загрузки модуля.
Вы можете попробовать этот модуль npm require-from-memory
import fs from 'fs'
import BuiltinModule from 'module'
const Module = module.constructor.length > 1 ? module.constructor : BuiltinModule
function requireFromString(code, filename) {
if (!filename) {
filename = ''
}
if (typeof filename !== 'string') {
throw new Error(`filename must be a string: ${filename}`)
}
let buffer
function getBuffer() {
if (!buffer) {
buffer = Buffer.from(code, 'utf8')
}
return buffer
}
const now = new Date()
const nowMs = now.getTime()
const size = Buffer.byteLength(code, 'utf8')
const fileStat = {
size,
blksize : 4096,
blocks : Math.ceil(size / 4096),
atimeMs : nowMs,
mtimeMs : nowMs,
ctimeMs : nowMs,
birthtimeMs: nowMs,
atime : now,
mtime : now,
ctime : now,
birthtime : now
}
const resolveFilename = Module._resolveFilename
const readFileSync = fs.readFileSync
const statSync = fs.statSync
try {
Module._resolveFilename = () => {
Module._resolveFilename = resolveFilename
return filename
}
fs.readFileSync = (fname, options, ...other) => {
if (fname === filename) {
console.log(code)
return typeof options === 'string'
? code
: getBuffer()
}
console.log(code)
return readFileSync.apply(fs, [fname, options, ...other])
}
fs.statSync = (fname, ...other) => {
if (fname === filename) {
return fileStat
}
return statSync.apply(fs, [fname, ...other])
}
return require(filename)
} finally {
Module._resolveFilename = resolveFilename
fs.readFileSync = readFileSync
fs.statSync = statSync
}
}
Основываясь на решениях Андрея Сидорова и Доминика, меня огорчил тот факт, что я не могу требовать строкового модуля, тогда я предлагаю эту версию *.
Код:
void function() {
'use strict';
const EXTENSIONS = ['.js', '.json', '.node'];
var Module,
path,
cache,
resolveFilename,
demethodize,
hasOwnProperty,
dirname,
parse,
resolve,
stringify,
virtual;
Module = require('module');
path = require('path');
cache = Module._cache;
resolveFilename = Module._resolveFilename;
dirname = path.dirname;
parse = path.parse;
resolve = path.resolve;
demethodize = Function.bind.bind(Function.call);
hasOwnProperty = demethodize(Object.prototype.hasOwnProperty);
Module._resolveFilename = function(request, parent) {
var filename;
// Pre-resolution
filename = resolve(parse(parent.filename).dir, request);
// Adding extension, if needed
if (EXTENSIONS.indexOf(parse(filename).ext) === -1) {
filename += '.js';
}
// If the module exists or is virtual, return the filename
if (virtual || hasOwnProperty(cache, filename)) {
return filename;
}
// Preserving the native behavior
return resolveFilename.apply(Module, arguments);
};
Module._register = function(request, parent, src) {
var filename,
module;
// Enabling virtual resolution
virtual = true;
filename = Module._resolveFilename(request, parent);
// Disabling virtual resolution
virtual = false;
// Conflicts management
if (hasOwnProperty(cache, filename)) {
error = new Error('Existing module "' + request + '"');
error.code = 'MODULE_EXISTS';
throw error;
}
// Module loading
cache[filename] = module = new Module(filename, parent);
module.filename = filename;
module.paths = Module._nodeModulePaths(dirname(filename));
module._compile(stringify(src), filename);
module.loaded = true;
return module;
};
stringify = function(src) {
// If src is a function, turning to IIFE src
return typeof src === 'function'
? 'void ' + src.toString() + '();'
: src;
};
}();
void function() {
var Module,
parentModule,
child;
Module = require('module');
// Creating a parent module from string
parentModule = Module._register('parent', process.mainModule, `
module.exports = {
name: module.filename,
getChild: function() {
return require('child');
}
};
`);
// Creating a child module from function
Module._register('child', parentModule, function() {
module.exports = {
name: module.filename,
getParent: function() {
return module.parent.exports;
}
};
});
child = require('child');
console.log(child === child.getParent().getChild());
}();
Использование:
void function() {
var Module,
parentModule,
child;
Module = require('module');
// Creating a parent module from string
parentModule = Module._register('parent', process.mainModule, `
module.exports = {
name: module.filename,
getChild: function() {
return require('child');
}
};
`);
// Creating a child module from function
Module._register('child', parentModule, function() {
module.exports = {
name: module.filename,
getParent: function() {
return module.parent.exports;
}
};
});
child = require('child');
console.log(child === child.getParent().getChild());
}();
* Как вы можете видеть, он содержит функцию formater, которая позволяет создавать некоторые модули из функций.
Я думаю, что лучший способ подойти к этому - иметь параметр, который вы могли бы установить позже...
например, внутри файла: myUnalteredModule.js
exports.setChanges = function( args )...
Тогда вы могли бы сделать:
var loadedModule = require( 'myUnalteredModule' );
loadedModule