Shopify Theme Base GitHub

Webpack: Empaquetador de JS

Webpack nos permite trabajar con una arquitectura modular, la cual conoceremos más adelante en la documentación.

Webpack es conocido como un paquete de módulos estáticos para aplicaciones de JavaScript modernas, sin embargo, en nuestras tiendas, esta herramienta nos permite empaquetar código, por secciones y servirlos por templates más fácilmente.

Cuando webpack procesa una aplicación, crea internamente un gráfico de dependencias a partir de uno o más puntos de entrada. Luego, combina todos los módulos que necesita su proyecto en uno o más paquetes, para que nuestros outputs estáticos se sirvan en donde se necesiten.

En esta documentación conoceremos la configuración establecida como estándar en el tema base:

  • Entries
  • Outputs
  • Loaders
  • Plugins
  • Modes
  • Compatibilidad en navegadores

Entries

Un entry, nos indica un punto de entrada qué un módulo debe usar en el bundle/paquete para comenzar a construir un gráfico de dependencias. Webpack entiende qué otros módulos y bibliotecas dependen de este punto de entrada (directa e indirectamente). Nuestra entrada principal es: ./src/index.js, pero también, nosotros especificamos diferentes entradas:

  • ./src/product.js
  • ./src/collection.js

Revise nuestra configuración inicial de entradas:

webpack.common.js

const entry = {
  theme: './src/templates/index.js'
  // ... agregue más entradas a su configuración
}

module.exports = {
  entry: entry,
}

Outputs

Los outputs, deben indicar dónde emitir los paquetes que se crean y cómo se deben nombrar estos archivos. Nuestro valor estándar es ./assets/theme.js para el archivo de salida principal y en la ./assets carpeta podrá encontrar otro archivo generado.

Puede configurar esta parte del proceso especificando un nuevo entry en su configuración:

webpack.common.js

const path = require('path');

const entry = {
  theme: './src/templates/index.js'
  // ... agregue más entradas a su configuración
}

const output = {
  filename: '[name].js',
  path: path.resolve(__dirname, 'assets')
};

module.exports = {
  entry: entry,
  output: output,
}

Nota: Nuestra configuración usa un módulo nativo de nodeJS para manipular el path de emisión del bundle/paquete

Loaders

Webpack solo transpila archivos .js y .json. Nuestros loaders, permiten que el webpack procese otros archivos, como .scss, .svg ó archivos de recursos. En nuestra configuración inicial, usamos solo loaders para procesar archivos de estilos(.scss). Conozca un poco las estructura de los loaders:

  • La propiedad test, identifica qué archivo o archivos deben transformarse.
  • La propiedad use, indica qué cargador debe usarse para realizar la transformación.

webpack.common.js

const path = require('path');

const JS_DIR = path.resolve(__dirname, './src');

const entry = {
  theme: './src/templates/index.js'
  // ... agregue más entradas a su configuración
}

const output = {
  filename: '[name].js',
  path: path.resolve(__dirname, 'assets')
};

// transpila archivos .js con babel
const rules = [
  {
    test: /\.js$/,
    include: [JS_DIR],
    exclude: /node_modules/,
    use: 'babel-loader'
  }
];

module.exports = {
  entry: entry,
  output: output,
  module: {
    rules: rules,
  },
}

webpack.prod.js

const prodConfig = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader'
        ]
      },
    ]
  }
}

webpack.dev.js

const devConfig = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
        ]
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          "style-loader",
          "css-loader",
          "sass-loader",
        ],
      },
    ]
  }
}

Plugins

Los plugins se pueden aprovechar para realizar una gama más amplia de tareas:

  • Optimización
  • Gestión de activos
  • Inyección de variables de entorno.
  • Para usar un plugin, debe usar require() y agregarlo al item plugins de la matriz (La mayoría de los plugins se pueden personalizar a través de opciones de configuración) creando una instancia de él llamándolo con el operador new.

webpack.common.js

const path = require('path');
const TerserPlugin = require("terser-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

const JS_DIR = path.resolve(__dirname, './src');

const entry = {
  theme: './src/templates/index.js'
}

const output = {
  filename: '[name].js',
  path: path.resolve(__dirname, 'assets')
};

const rules = [
  {
    test: /\.js$/,
    include: [JS_DIR],
    exclude: /node_modules/,
    use: 'babel-loader'
  }
];

module.exports = {

  entry: entry,

  output: output,

  module: {
    rules: rules,
  },

  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin(),
      new CssMinimizerPlugin(),
    ]
  },

}

webpack.prod.js

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

const prodConfig = {
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader'
        ]
      },
    ]
  }
}

webpack.dev.js

const shoulAnalyze = process.argv.includes('--analyze');
const plugins = [];

if (shoulAnalyze) {
  const { BundleAnalyzerPlugin } = module.require('webpack-bundle-analyzer');
  plugins.push(new BundleAnalyzerPlugin());
}

const devConfig = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
        ]
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          "style-loader",
          "css-loader",
          "sass-loader",
        ],
      },
    ]
  },
  plugins,
}

Mode

Al establecer el parámetro mode en development, production o none, puede habilitar las optimizaciones integradas de webpack que corresponden a cada entorno. En este usamos diferentes archivos de configuración para modularizar nuestras configuración en caso de extender nuestros archivos. Encuentra la configuración:

webpack.dev.js

const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
const shoulAnalyze = process.argv.includes('--analyze');

/** @type {import("webpack").Configuration}  */

const plugins = [];

if (shoulAnalyze) {
  const { BundleAnalyzerPlugin } = module.require('webpack-bundle-analyzer');
  plugins.push(new BundleAnalyzerPlugin());
}

const devConfig = {
  mode: "development",
  devtool: "eval-source-map",
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
        ]
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          "style-loader",
          "css-loader",
          "sass-loader",
        ],
      },
    ]
  },
  plugins,
}

module.exports = merge(common, devConfig);

webpack.prod.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { merge } = require("webpack-merge");
const common = require("./webpack.common");

/** @type {import("webpack").Configuration}  */

const prodConfig = {
  mode: "production",
  devtool: "source-map",
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader'
        ]
      },
    ]
  }
}

module.exports = merge(common, prodConfig);

Compatibilidad en navegadores

Para esto usaremos una biblioteca de estandarización de JavaScript que incluye los polyfills de ECMA hasta el 2021, llamada core-js.

Nota: Use esta librería con cuidado, en caso de no requerir, borre del punto ó los punto de entrada. Modularizar su uso siguiendo las recomendaciones de su LT.