Crie pacotes individuais de SPAs com o Webpack

Como uso o Webpack para criar pacotes independentes de SPA que podem ou não ser carregados dinamicamente quando meu usuário navega no meu SPA?

Eu tenho um módulo de contatos e um módulo de tarefas. Ambos têm duas dependências. Quero que o WebPack crie pacotes para cada um que é carregado quando (e se) for necessário.

O código está abaixo. O problema parece ser que cada uma dessas entradas está sendo vista como pontos de entrada do aplicativo e, portanto, o código de inicialização do webpack é inserido nele.

Eu já vi vários exemplos comCommonsChunkPlugin mas não consigo encontrar uma referência / documentação da API e, pelo que posso supor, não é isso que eu quero.

Editar - encontrou esses documentosaquie adicionamos uma tentativa com esse plug-in abaixo em minha edição.

Configuração atual

module.exports = {
    entry: {
        contacts: './contacts',
        tasks: './tasks'
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name]-bundle.js'
    }
};

Contacts.js

define(['./ca', './cb'], function(ca, cb){
    var name = 'Contacts';
    alert(ca + ' ' + cb);
});

Tasks.js

define(['./ta', './tb'], function(ta, tb){
    var name = 'TASKS Main';
    alert(ta + ' ' + tb);
});

tasks-bundle.js

/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};

/******/    // The require function
/******/    function __webpack_require__(moduleId) {

/******/        // Check if module is in cache
/******/        if(installedModules[moduleId])
/******/            return installedModules[moduleId].exports;

/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = true;

/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }


/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;

/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";

/******/    // Load entry module and return exports
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(3), __webpack_require__(4)], __WEBPACK_AMD_DEFINE_RESULT__ = function(ta, tb){
        var name = 'TASKS Main';
        alert(ta + ' ' + tb);
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));

/***/ },
/* 1 */,
/* 2 */,
/* 3 */
/***/ function(module, exports, __webpack_require__) {

    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function(){
        var name = 'TASKS - A';
        alert('ta');
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));

/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {

    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function(){
        var name = 'TASKS - B';
        alert('tb');
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));

/***/ }
/******/ ]);

EDITAR

Aqui está minha tentativa número 2 com o CommonsChunkPlugin. Eu criei um app.js fictício

app.js

var module = window.location.hash.split('/')[0];
alert(module);

Em seguida, movi todos os meus arquivos de contatos e tarefas para uma pasta de componentes, mas os deixei em paz. Minha nova configuração:

module.exports = {
    entry: {
        app: './app'
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name]-bundle.js'
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: './components/contacts',
            filename: 'contacts-component-bundle.js'
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: './components/tasks',
            filename: 'tasks-component-bundle.js'
        })
    ]
};

Estranhamente, agoraapp-bundle.js parece não ter nenhum código de inicialização do Webpack

webpackJsonp([0,1,2],[
/* 0 */
/***/ function(module, exports) {

    var module = window.location.hash.split('/')[0];
    alert(module);

/***/ }
]);

contatos-componentes-pacote.js agora só tem isso

webpackJsonp([1,2],[]);

etask-components-bundle.js parece ter todo o meu código de inicialização do webpack

/******/ (function(modules) { // webpackBootstrap
/******/    // install a JSONP callback for chunk loading
/******/    var parentJsonpFunction = window["webpackJsonp"];
/******/    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {
/******/        // add "moreModules" to the modules object,
/******/        // then flag all "chunkIds" as loaded and fire callback
/******/        var moduleId, chunkId, i = 0, callbacks = [];
/******/        for(;i < chunkIds.length; i++) {
/******/            chunkId = chunkIds[i];
/******/            if(installedChunks[chunkId])
/******/                callbacks.push.apply(callbacks, installedChunks[chunkId]);
/******/            installedChunks[chunkId] = 0;
/******/        }
/******/        for(moduleId in moreModules) {
/******/            modules[moduleId] = moreModules[moduleId];
/******/        }
/******/        if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules);
/******/        while(callbacks.length)
/******/            callbacks.shift().call(null, __webpack_require__);
/******/        if(moreModules[0]) {
/******/            installedModules[0] = 0;
/******/            return __webpack_require__(0);
/******/        }
/******/    };

/******/    // The module cache
/******/    var installedModules = {};

/******/    // object to store loaded and loading chunks
/******/    // "0" means "already loaded"
/******/    // Array means "loading", array contains callbacks
/******/    var installedChunks = {
/******/        2:0,
/******/        1:0
/******/    };

/******/    // The require function
/******/    function __webpack_require__(moduleId) {

/******/        // Check if module is in cache
/******/        if(installedModules[moduleId])
/******/            return installedModules[moduleId].exports;

/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = true;

/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }

/******/    // This file contains only the entry chunk.
/******/    // The chunk loading function for additional chunks
/******/    __webpack_require__.e = function requireEnsure(chunkId, callback) {
/******/        // "0" is the signal for "already loaded"
/******/        if(installedChunks[chunkId] === 0)
/******/            return callback.call(null, __webpack_require__);

/******/        // an array means "currently loading".
/******/        if(installedChunks[chunkId] !== undefined) {
/******/            installedChunks[chunkId].push(callback);
/******/        } else {
/******/            // start chunk loading
/******/            installedChunks[chunkId] = [callback];
/******/            var head = document.getElementsByTagName('head')[0];
/******/            var script = document.createElement('script');
/******/            script.type = 'text/javascript';
/******/            script.charset = 'utf-8';
/******/            script.async = true;

/******/            script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"app","1":"./components/contacts"}[chunkId]||chunkId) + "-bundle.js";
/******/            head.appendChild(script);
/******/        }
/******/    };

/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;

/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/ })
/************************************************************************/
/******/ ([]);

Novamente, estou apenas tentando usar o Webpack para obter uma prova de conceito do SPA em funcionamento, com algum tipo de ponto de entrada raiz app.js e, em seguida, algum número arbitrário de módulos / componentes que são carregados sob demanda. Isso é trivialmente fácil com o requirejs, então eu tenho que imaginar que estou perdendo algo importante aqui, especialmente com todos os artigos que vi falando sobre o quão bom é o Webpack para SPAs.

EDIT 2

Pela resposta de bebraw abaixo, tentei o seguinte:

app.js

var mod = window.location.hash.split('/')[0];
alert(mod);

require.ensure([], function() {
    require('./components/' + mod).show();
});

webpack.config.js

var path = require('path');
var webpack = require('webpack');

module.exports = {
    entry: {
        app: './app'
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name]-bundle.js'
    }
};

E então, na minha pasta de compilação, fico com app-bundle.js, que possui todo o meu código de bootstrap e meu código app.js, e então 1.1-bundle.js, que possuitudo do meu código de tarefas e contatos.

Eu tenhoAlém disso tentei isso

module.exports = {
    entry: {
        app: './app'
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name]-bundle.js'
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: './components/contacts',
            filename: 'contacts-component-bundle.js',
            children: true
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: './components/tasks',
            filename: 'tasks-component-bundle.js',
            children: true
        })
    ]
};

Que produz o mesmo que o acima, mas agoraAlém disso possui task-component-bundle.js e contatos-component-bundle.js, ambos com algum código de inicialização do webpack; o código de tarefas e contatos ainda está no pacote 1.1.

Mais uma vez, eu simplesmente quero poder dizer ao Webpack, de uma forma ou de outra, para agrupar módulos individuais e suas dependências para subsequente carregamento lento e assíncrono, quando necessário.

A resposta final foi dada por Tobias - criador do Webpack - abaixo, que colocarei aqui para a posteridade.

Verdadeiramente dinâmico não é possível. O webpack (em construção para require.js) compila seu aplicativo antes de executá-lo e não tem acesso às informações de tempo de execução. A dinâmica requer mergulho na Webpack em todas as pastas possíveis, desde que sua expressão dinâmica não contenha ... Você deve ser capaz de configurá-la para usar mod + '/' + mod com o ContextReplacementPlugin e um pouco de magia RegExp (use as referências anteriores em o RegExp). Por padrão, incluiria muitos módulos.

questionAnswers(3)

yourAnswerToTheQuestion