Компиляция Webpack в памяти, но преобразование в node_modules на диске

26

Я пытаюсь использовать веб-пакет для компиляции строки в строке действительного кода javascript. Я использую память fs, как описано здесь: https://webpack.github.io/docs/node.js-api.html#compile-to-memory.

Итак, я беру строку, содержащую необработанный javascript, записывая это в память fs, а затем веб-пакет разрешает эту точку входа. Но компиляция терпит неудачу в первом требовании оператора, по-видимому, потому, что она не может смотреть в реальном fs для node_modules.

Любые идеи о том, как я могу это сделать?

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, которая, как я полагаю, была предназначена для того, чтобы делать то, что я надеюсь сделать, но в этом текущем виде я не вижу, как это сделать. Он назначает экземпляр MemoryFs всем резольверам. Я попытался назначить модуль node fs, но не кубик.

Короче говоря, я пытаюсь установить точку входа в строку памяти необработанного javascript, но все еще есть запросы на импорт и импорт, разрешенные на node_modules на диске.

UPDATE

Мне удалось получить результат, который я ищу, но это не очень. Я в основном переопределяю реализацию #stat и #readFile в MemoryFS чтобы проверить реальную файловую систему, если он получает какой-либо запрос на файл, который не существует в памяти. Я мог бы немного почистить это путем подклассификации 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 api.

  • 2
    Можете ли вы показать более полный код, в том числе ваши require ?
  • 0
    @jonaz Я обновил оригинальный фрагмент, а также добавил новую версию, с которой я смог работать. Надеясь придумать лучший способ, хотя.
Показать ещё 2 комментария
Теги:
webpack

3 ответа

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;
}
0

Вместо памяти-fs должна помочь комбинация unionfs/memfs/linkfs.

  • 0
    Вероятно, это должен быть принятый ответ. Просто нужно немного подробнее.
  • 0
    Я пытаюсь это сделать, но в unionfs есть основные ошибки: сейчас он не будет правильно читать каталоги: github.com/streamich/unionfs/issues/240
Показать ещё 1 комментарий
0

Используется MemoryFS, который является переопределением JavaScript, обычно обрабатываемым операционной системой. Интересно, смогла ли вы установить каталог с помощью tmpfs на уровне операционной системы, а затем использовать это? webpack не знает и не заботится о том, что входной файл фактически хранится в памяти.

Предполагая, что вы установили файловую систему на основе памяти в /media/memory, код конфигурации webpack может быть таким же простым, как это:

resolve: {
  root: ['/media/memory', ...other paths...],
  },
  output: {
    path: '/wherever/you/want/the/output/files'
  }
}

Этот подход также имеет скрытое преимущество: если вы хотите отлаживать входной код, вы просто монтируете/носитель/память с файловой системой, отличной от RAM, и вы можете видеть, что генерируется.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню