wake-up-neo.com

Kompilieren des Webpacks im Arbeitsspeicher, jedoch Auflösung auf node_modules auf der Festplatte

Ich versuche, ein Web Pack zu verwenden, um eine Zeichenkette aus gültigem Javascript-Code zu kompilieren. Ich verwende Speicher-Fs wie hier beschrieben: https://webpack.github.io/docs/node.js-api.html#compile-to-memory

Also nehme ich eine Zeichenfolge mit rohem Javascript, schreibe diese in den Speicher fs, und dann löst das Web Pack diesen Einstiegspunkt auf. Die Kompilierung schlägt jedoch bei der ersten Anforderung fehl, wahrscheinlich, weil sie nicht in der Lage ist, die echten fs nach node_modules zu durchsuchen. 

Irgendwelche Ideen, wie ich das erreichen kann?

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

Verwendungszweck

var code = "var _ = require('underscore'); console.log(_);";
var bundle = yield compile(code); //should be a bundle containing the underscore source.

Der Fehler ist 

ModuleNotFoundError: Modul nicht gefunden: Fehler: Modul .__ kann nicht aufgelöst werden. Unterstrich in /

Diese Frage zeigt an, dass andere dasselbe versucht haben: https://github.com/webpack/webpack/issues/1562 . Es gibt einen Gist unter https://Gist.github.com/DatenMetzgerX/2a96ebf287b4311f4c18 , von dem ich glaube, dass er das tun sollte, was ich erhoffe, aber in seiner aktuellen Form sehe ich nicht wie. Es weist allen Resolvers eine Instanz von MemoryFs zu. Ich habe versucht, das fs-Modul des Knotens zuzuweisen, aber keine Würfel. 

Kurz gesagt, ich versuche, einen Einstiegspunkt für eine In-Memory-Zeichenfolge von rohem Javascript festzulegen, aber ich muss immer noch Anweisungen zum Importieren und zu importierten Anweisungen auf node_modules auf der Festplatte auflösen.

UPDATE

Ich habe das gewünschte Ergebnis erhalten können, aber es ist nicht schön. Im Grunde überschreibe ich die Implementierung von #stat und #readFile in MemoryFS, um das reale Dateisystem zu überprüfen, ob es eine Anforderung für eine Datei erhält, die nicht im Speicher vorhanden ist. Ich könnte dies ein wenig aufräumen, indem ich MemoryFS subclassiere, anstatt zur Laufzeit Methodimplementierungen auszutauschen, aber die Idee wäre immer noch die gleiche.

Arbeitslösung

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

Verwendungszweck

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 _.

Wenn es keinen besseren Weg gibt, dann werde ich dies definitiv in einer Unterklasse von MemoryFS einkapseln, aber ich hoffe, dass es einen vernünftigeren Weg gibt, dies mit der Webpack-API zu erreichen.

29
mike

Ich habe dieses Snippet ungeprüft erstellt. Ich denke, Sie möchten, dass das inputFS das echte ist und die Ausgabe das fs im Speicher ist. Andererseits möchten Sie, dass alle Abhängigkeiten von file.js separat erstellt werden. Dafür habe ich gedacht, dass das webpack.optimize.CommonsChunkPlugin-Plugin helfen kann. Ich erwarte, dass das Webpack alles in den Speicher schreibt. Ich hoffe es klappt.

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
qballer

Anstelle von memory-fs sollte die Kombination unionfs/memfs/linkfs helfen.

1
user2524758

Sie verwenden MemoryFS , eine JavaScript-Neuimplementierung einer Funktion, die normalerweise vom Betriebssystem verarbeitet wird. Ich frage mich, könnten Sie ein Verzeichnis mit tmpfs auf Betriebssystemebene einhängen und dieses dann verwenden? webpack würde dann nicht wissen oder kümmern, dass die Eingabedatei tatsächlich im Speicher gespeichert ist.

Angenommen, Sie haben ein speicherbasiertes Dateisystem unter / media/memory gemountet, könnte der Konfigurationscode webpack so einfach sein:

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

Dieser Ansatz hat auch einen versteckten Vorteil: Wenn Sie den Eingabecode debuggen möchten, mounten Sie einfach / media/memory mit einem nicht-RAM-basierten Dateisystem und sehen, was generiert wird.

0
John Anderson