38 votes

Compilation de Webpack en mémoire mais résolution de node_modules sur le disque

J'essaie d'utiliser web pack pour compiler une chaîne en mémoire de code javascript valide. J'utilise memory fs comme indiqué ici : https://webpack.github.io/docs/node.js-api.html#compile-to-memory .

Donc, je prends une chaîne contenant du javascript brut, je l'écris dans le fs mémoire, et ensuite le pack web se résout à ce point d'entrée. Mais la compilation échoue à la première instruction require, sans doute parce qu'elle n'est pas capable de rechercher les node_modules dans le vrai fs.

Avez-vous une idée de la façon dont je peux y parvenir ?

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

Utilisation

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

L'erreur est

ModuleNotFoundError : Module non trouvé : Erreur : Impossible de résoudre le module underscore dans /

Cette question indique que d'autres ont essayé la même chose : https://github.com/webpack/webpack/issues/1562 . il y a un résumé référencé à https://gist.github.com/DatenMetzgerX/2a96ebf287b4311f4c18 qui, je crois, était destiné à faire ce que j'espère accomplir, mais dans sa forme actuelle, je ne vois pas comment. Il assigne une instance de MemoryFs à tous les résolveurs. J'ai essayé d'assigner le module fs du noeud, mais sans succès.

En résumé, j'essaie de définir un point d'entrée vers une chaîne en mémoire de javascript brut, tout en ayant les instructions require et import résolues vers les node_modules sur le disque.

UPDATE

J'ai réussi à obtenir le résultat que je recherche, mais ce n'est pas joli. Je suis en train de remplacer l'implémentation de #stat et #readFile dans MemoryFS pour vérifier le système de fichiers réel s'il reçoit une demande pour un fichier qui n'existe pas en mémoire. Je pourrais nettoyer un peu cela en sous-classant MemoryFS au lieu de permuter les implémentations des méthodes au moment de l'exécution, mais l'idée serait toujours la même.

Solution de travail

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

Utilisation

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

S'il n'y a pas de meilleur moyen, alors je vais certainement encapsuler ceci dans une sous-classe de MemoryFS, mais j'espère qu'il y a un moyen plus sain d'accomplir ceci avec l'api de Webpack.

2 votes

Pouvez-vous montrer un code plus complet, y compris votre require déclarations ?

0 votes

@jonaz J'ai mis à jour le snippet original et j'ai également ajouté une nouvelle version que j'ai réussi à faire fonctionner. J'espère trouver une meilleure solution.

0 votes

Pouvez-vous donner plus d'informations sur les exigences d'origine ? On dirait que vous voulez un point d'entrée dynamique ?

3voto

user2524758 Points 206

Au lieu de memory-fs, la combinaison unionfs/memfs/linkfs devrait aider.

0 votes

Cela devrait probablement être la réponse acceptée. Il faut juste un peu plus de détails.

0 votes

J'essaie de le faire mais unionfs a des erreurs de base, en ce moment il ne lit pas les répertoires correctement : github.com/streamich/unionfs/issues/240

0 votes

En outre, unionfs ne prend pas en charge mkdirp que webpack utilise

1voto

qballer Points 597

J'ai créé ce snippet sans le tester. Je pense que vous voulez que le FS d'entrée soit le FS réel et que le FS de sortie soit le FS en mémoire. D'autre part, vous voulez que toutes les dépendances de file.js soient construites séparément. Pour cela, j'ai pensé que le plugin webpack.optimize.CommonsChunkPlugin pourrait être utile. J'attends de webpack qu'il écrive tout dans la mémoire. J'espère que cela fonctionnera.

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

0voto

John Anderson Points 101

Vous utilisez MemoryFS qui est une réimplémentation en JavaScript d'une fonctionnalité normalement gérée par le système d'exploitation. Je me demande si vous pouvez monter un répertoire en utilisant tmpfs au niveau du système d'exploitation, puis l'utiliser ? webpack ne saurait donc pas ou ne se soucierait pas que le fichier d'entrée est en fait stocké en mémoire.

En supposant que vous avez monté un système de fichiers basé sur la mémoire à l'adresse /média/mémoire El webpack Le code de configuration pourrait être aussi simple que ceci :

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

Cette approche a également un avantage caché : si vous voulez déboguer le code d'entrée, il suffit de monter /média/mémoire avec un système de fichiers non basé sur la RAM et vous pouvez voir ce qui est généré.

0voto

German Meza Points 136

Je sais qu'il est tard, mais pour mémoire, voici un extrait de code.

import * as fs from 'fs';
import { resolve } from 'path';
import  { Volume } from 'memfs';
import { ufs } from 'unionfs';

const volume = Volume.fromJSON({
 [resolve(process.cwd(), 'test.js')]: 'this file is on memory not on disk'
});

ufs.use(fs).use(volume);

// Reads from memory
console.log(ufs.readFileSync(resolve(process.cwd(), 'test.js'), 'utf8'));
// Reads from disk
console.log(ufs.readFileSync(resolve(process.cwd(), 'package.json'), 'utf8'));

// Writing into memory
volume.writeFileSync(resolve(process.cwd(), 'test.memory'), 'This should be 
on memory');
console.log(ufs.readFileSync(resolve(process.cwd(), 'test.memory'), 'utf8'));

// Writing into disk
ufs.writeFileSync(resolve(process.cwd(), 'test.disk'), 'This should be on disk');
console.log(ufs.readFileSync(resolve(process.cwd(), 'test.disk'), 'utf8'));

Hers la sortie de la console :

user1@pc playground % node inMem.mjs
this file is on memory not on disk
{
  "name": "playground",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "memfs": "^3.3.0",
    "unionfs": "^4.4.0"
  }
}

This should be on memory
This should be on disk
user1@pc playground % ls .
inMem.mjs       node_modules    package.json            yarn.lock

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X