¿Puedo usar el paquete web para generar CSS y JS por separado?

86

Yo tengo:

  1. Archivos JS que quiero agrupar.
  2. MENOS archivos que quiero compilar en CSS (resolviendo @imports en un solo paquete).

Esperaba especificarlos como dos entradas separadas y tener dos salidas separadas (probablemente a través de extract-text-webpack-plugin). Webpack tiene todos los complementos / cargadores adecuados para realizar la compilación, pero no parece gustarle la separación.

He visto ejemplos de personas que requieren sus archivos LESS directamente de JS, por ejemplo require('./app.less');, sin otro motivo que decirle a webpack que incluya esos archivos en el paquete. Esto le permite tener un solo punto de entrada, pero me parece realmente incorrecto: ¿por qué requeriría MENOS en mi JS cuando no tiene nada que ver con mi código JS?

Intenté usar múltiples puntos de entrada, entregando tanto el JS de entrada como el archivo LESS principal, pero cuando usé múltiples puntos de entrada, el paquete web genera un paquete que no ejecuta el JS en la carga; lo agrupa todo, pero no sabe lo que debe ejecutarse al inicio.

¿Estoy usando mal el paquete web? ¿Debo ejecutar instancias separadas de paquete web para estos módulos separados? ¿Debería usar el paquete web para activos que no sean JS si no los voy a mezclar en mi JS?

Brent Traut
fuente
Puedo recomendar el siguiente tutorial medium.freecodecamp.org/…
wilo087

Respuestas:

29

¿Debería usar el paquete web para activos que no sean JS si no los voy a mezclar en mi JS?

Tal vez no. Webpack está definitivamente centrado en js, con la suposición implícita de que lo que está creando es una aplicación js. Su implementación le require()permite tratar todo como un módulo (incluidos los parciales Sass / LESS, JSON, prácticamente cualquier cosa) y automáticamente realiza la administración de dependencias por usted (todo lo que requireestá empaquetado y nada más).

¿Por qué necesitaría MENOS en mi JS cuando no tiene nada que ver con mi código JS?

La gente hace esto porque están definiendo una parte de su aplicación (por ejemplo, un componente React, una vista Backbone) con js. Esa parte de la aplicación tiene CSS que la acompaña. Dependiendo de algún recurso CSS externo que se haya creado por separado y no se haga referencia directamente desde el módulo js, ​​es frágil, es más difícil trabajar con él y puede llevar a que los estilos se queden obsoletos, etc. (Sass, lo que sea) parcial que va con ese componente js, y el componente js require()es para aclarar la dependencia (para usted y para la herramienta de compilación, que nunca crea estilos que no necesita).

No sé si podría usar el paquete web para empaquetar CSS por sí solo (cuando los archivos CSS no están referenciados desde ningún js). Estoy seguro de que podría conectar algo con complementos, etc., pero no estoy seguro de que sea posible de inmediato. Si hace referencia a los archivos CSS de su js, puede agrupar fácilmente el CSS en un archivo separado con el complemento Extract Text, como dice.

Brendan Gannon
fuente
18

Se puede generar un paquete CSS separado sin usar require('main/less)en ninguno de sus JS, pero como señaló Brendan en la primera parte de su respuesta, Webpack no está diseñado para que un paquete CSS global vaya junto con JS modular, sin embargo, hay un par de opciones .

La primera es agregar un punto de entrada adicional para main.less, luego use el complemento Extract Text para crear el paquete CSS:

var webpack = require('webpack'),
    ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    entry: {
        home: [
            'js/common',
            'js/homepage'
        ],
        style: [
            'styles/main.less'
        ]
    },
    output: {
        path: 'dist',
        filename: "[name].min.js"
    },
    resolve: {
        extensions: ["", ".js"]
    },
    module: {
        loaders: [{
            test: /\.less$/,
            loader: ExtractTextPlugin.extract("style", "css", "less")
        }]
    },
    plugins: [
        new ExtractTextPlugin("[name].min.css", {
            allChunks: true
        })
    ]
};

El problema con este método es que también genera un archivo JS no deseado, así como el paquete, en este ejemplo: style.jsque es solo un módulo Webpack vacío.

Otra opción es agregar el archivo less principal a un punto de entrada de Webpack existente:

var webpack = require('webpack'),
    ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    entry: {
        home: [
            'js/common',
            'js/homepage',
            'styles/main.less'
        ],
    },
    output: {
        path: 'dist',
        filename: "[name].min.js"
    },
    resolve: {
        extensions: ["", ".js"]
    },
    module: {
        loaders: [{
            test: /\.less$/,
            loader: ExtractTextPlugin.extract("style", "css", "less")
        }]
    },
    plugins: [
        new ExtractTextPlugin("[name].min.css", {
            allChunks: true
        })
    ]
};

Esto es ideal si solo tiene 1 punto de entrada, pero si tiene más, entonces su configuración de Webpack se verá un poco extraña, ya que tendrá que elegir arbitrariamente a qué punto de entrada agregar el archivo menos principal.

bdmason
fuente
10

Para aclarar aún más la respuesta anterior de bdmason, parece que la configuración deseable sería crear un paquete JS y CSS para cada página, así:

 entry: {
        Home: ["./path/to/home.js", "./path/to/home.less"],
        About: ["./path/to/about.js", "./path/to/about.less"]
    }

Y luego usa el [name]interruptor:

output: {
        path: "path/to/generated/bundles",
        filename: "[name].js"
    },
plugins: new ExtractTextPlugin("[name].css")

Configuración completa, con algunas adiciones que no están relacionadas con la pregunta (en realidad estamos usando SASS en lugar de LESS):

var ExtractTextPlugin = require("extract-text-webpack-plugin");
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
require('babel-polyfill');

module.exports = [{
    devtool: debug ? "inline-sourcemap" : null,
    entry: {
        Home: ['babel-polyfill', "./home.js","path/to/HomeRootStyle.scss"],
        SearchResults: ['babel-polyfill', "./searchResults.js","path/to/SearchResultsRootStyle.scss"]
    },
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel-loader',
                query: {
                    presets: ['react', 'es2015'],
                    plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy']
                }
            },
            {
                test: /\.scss$/,
                loader: ExtractTextPlugin.extract("style-loader","css-raw-loader!sass-loader")
            }
        ]
    },
    output: {
        path: "./res/generated",
        filename: "[name].js"
    },
    plugins: debug ? [new ExtractTextPlugin("[name].css")] : [
        new ExtractTextPlugin("[name].css"),
        new webpack.DefinePlugin({
            'process.env':{
                'NODE_ENV': JSON.stringify('production')
            }
        }),
        new webpack.optimize.UglifyJsPlugin({
            compress:{
                warnings: true
            }
        })
    ]
}
];
Gilad Barner
fuente
9

solución webpack 4 con complemento mini-css-extract

el equipo del paquete web recomienda usar mini-css-extract sobre el complemento de texto de extracción

esta solución le permite crear un fragmento separado que contenga solo sus entradas css:

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

function recursiveIssuer(m) {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else if (m.name) {
    return m.name;
  } else {
    return false;
  }
}

module.exports = {
  entry: {
    foo: path.resolve(__dirname, 'src/foo'),
    bar: path.resolve(__dirname, 'src/bar'),
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        fooStyles: {
          name: 'foo',
          test: (m, c, entry = 'foo') =>
            m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
          chunks: 'all',
          enforce: true,
        },
        barStyles: {
          name: 'bar',
          test: (m, c, entry = 'bar') =>
            m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
          chunks: 'all',
          enforce: true,
        },
      },
    },
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};

Aquí hay un ejemplo más elaborado utilizando múltiples entradas de uno de mis proyectos personales:

const ManifestPlugin = require('webpack-manifest-plugin')
const webpack = require('webpack')
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const VENDOR = path.join(__dirname, 'node_modules')
const LOCAL_JS = path.join(__dirname, 'app/assets/js')
const LOCAL_SCSS = path.join(__dirname, 'app/assets/scss')
const BUILD_DIR = path.join(__dirname, 'public/dist')
const EXTERNAL = path.join(__dirname, 'public/external')

function recursiveIssuer(m) {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else if (m.name) {
    return m.name;
  } else {
    return false;
  }
}

module.exports = {
  entry: {
    vendor: [
      `${VENDOR}/jquery/dist/jquery.js`,
      `${VENDOR}/codemirror/lib/codemirror.js`,
      `${VENDOR}/codemirror/mode/javascript/javascript.js`,
      `${VENDOR}/codemirror/mode/yaml/yaml.js`,
      `${VENDOR}/zeroclipboard/dist/ZeroClipboard.js`,
    ],
    app: [
      `${LOCAL_JS}/utils.js`,
      `${LOCAL_JS}/editor.js`,
      `${LOCAL_JS}/clipboard.js`,
      `${LOCAL_JS}/fixtures.js`,
      `${LOCAL_JS}/ui.js`,
      `${LOCAL_JS}/data.js`,
      `${LOCAL_JS}/application.js`,
      `${LOCAL_JS}/google.js`
    ],
    'appStyles': [
      `${EXTERNAL}/montserrat.css`,
      `${EXTERNAL}/icons.css`,
      `${VENDOR}/purecss/pure-min.css`,
      `${VENDOR}/purecss/grids-core-min.css`,
      `${VENDOR}/purecss/grids-responsive-min.css`,
      `${VENDOR}/codemirror/lib/codemirror.css`,
      `${VENDOR}/codemirror/theme/monokai.css`,
    ]
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        appStyles: {
          name: 'appStyles',
          test: (m, c, entry = 'appStyles') =>
            m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
          chunks: 'all',
          enforce: true,
        },
      },
    },
  },
  module:  {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [ 'script-loader'],
      },
      {
        test: /\.(scss|css)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
    ],
  },
  mode: 'development',
  resolve: {
    extensions: ['.js', '.css', '.scss']
  },
  output: {
    path: BUILD_DIR,
    filename: "[name].[chunkhash].js",
  },
  plugins: [
    new ManifestPlugin(),
    new MiniCssExtractPlugin({
      filename: '[name].css'
    }),
  ]
};

Me doy cuenta de que este enfoque no es muy modular, pero debería darte una base para construir y es una excelente estrategia para adoptar webpack en proyectos en los que no deseas mezclar javascript y css.

La desventaja de este enfoque es que css-loader aún genera un archivo javascript adicional (ya sea que elija usarlo o no), esto supuestamente se solucionará en el paquete web 5 .

¿Debería usar el paquete web para activos que no sean JS si no los voy a mezclar en mi JS?

No veo nada malo en esto, pero en última instancia depende de su tolerancia para administrar múltiples sistemas de compilación. Para mí, esto se siente como una exageración, por lo que prefiero permanecer en el ecosistema de paquetes web.

Para obtener más información sobre las estrategias descritas anteriormente, consulte https://github.com/webpack-contrib/mini-css-extract-plugin#extracting-css-based-on-entry

lfender6445
fuente
esta debería ser la respuesta predeterminada hoy
Giona Granata
8

Sí, esto es posible, pero como otros dijeron, necesitará paquetes adicionales para hacerlo (consulte devDependencies en package.json). aquí está el código de muestra que utilicé para compilar mi bootstrap SCSS -> CSS y Bootstrap JS -> JS.

webpack.config.js:

module.exports = {
    mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
    entry: ['./src/app.js', './src/scss/app.scss'],
    output: {
    path: path.resolve(__dirname, 'lib/modules/theme/public'),
    filename: 'js/bootstrap.js'
    },
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'css/bootstrap.css',
                        }
                    },
                    {
                        loader: 'extract-loader'
                    },
                    {
                        loader: 'css-loader?-url'
                    },
                    {
                        loader: 'postcss-loader'
                    },
                    {
                        loader: 'sass-loader'
                    }
                ]
            }
        ]
    }
};

archivo postcss.config.js adicional:

module.exports = {
    plugins: {
        'autoprefixer': {}
    }
}

package.json:

{
  "main": "app.js",
  "scripts": {
    "build": "webpack",
    "start": "node app.js"
  },
  "author": "P'unk Avenue",
  "license": "MIT",
  "dependencies": {
    "bootstrap": "^4.1.3",
  },
  "devDependencies": {
    "autoprefixer": "^9.3.1",
    "css-loader": "^1.0.1",
    "exports-loader": "^0.7.0",
    "extract-loader": "^3.1.0",
    "file-loader": "^2.0.0",
    "node-sass": "^4.10.0",
    "popper.js": "^1.14.6",
    "postcss-cli": "^6.0.1",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "webpack": "^4.26.1",
    "webpack-cli": "^3.1.2"
  }
}

Vea el tutorial aquí: https://florianbrinkmann.com/en/4240/sass-webpack

geochanto
fuente
1

Como otros mencionaron, puede usar un complemento.

ExtractTextPlugin es obsoleto.

Puede utilizar el recomendado actualmente MiniCssExtractPluginen la configuración de su paquete web:

module.exports = {
     entry: {
        home: ['index.js', 'index.less']
     },
     plugins: [
            new MiniCssExtractPlugin({
                filename: '[name].css',
            }),
     ]
}
Repastificante
fuente
0

También puede poner sus declaraciones Less require en su archivo JS de entrada también:

en body.js

// CSS
require('css/_variable.scss')
require('css/_npm.scss')
require('css/_library.scss')
require('css/_lib.scss')

Luego en webpack

  entry: {
    body: [
      Path.join(__dirname, '/source/assets/javascripts/_body.js')
    ]
  },

const extractSass = new ExtractTextPlugin({
  filename: 'assets/stylesheets/all.bundle.css',
  disable: process.env.NODE_ENV === 'development',
  allChunks: true
})
Ian Warner
fuente