let webpack = require('webpack'); let ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = function () { let rules = []; let extractPlugins = []; // Babel Compilation. rules.push({ test: /\.jsx?$/, exclude: /(node_modules|bower_components)/, use: [ { loader: 'babel-loader', options: Config.babel() } ] }); // TypeScript Compilation. if (Mix.isUsing('typeScript')) { rules.push({ test: /\.tsx?$/, loader: 'ts-loader', exclude: /node_modules/, options: { appendTsSuffixTo: [/\.vue$/], } }); } // CSS Compilation. rules.push({ test: /\.css$/, exclude: Config.preprocessors.postCss ? Config.preprocessors.postCss.map(postCss => postCss.src.path()) : [], loaders: ['style-loader', 'css-loader'] }); // Recognize .scss Imports. rules.push({ test: /\.s[ac]ss$/, exclude: Config.preprocessors.sass ? Config.preprocessors.sass.map(sass => sass.src.path()) : [], loaders: ['style-loader', 'css-loader', 'sass-loader'] }); // Recognize .less Imports. rules.push({ test: /\.less$/, exclude: Config.preprocessors.less ? Config.preprocessors.less.map(less => less.src.path()) : [], loaders: ['style-loader', 'css-loader', 'less-loader'] }); // Add support for loading HTML files. rules.push({ test: /\.html$/, loaders: ['html-loader'] }); // Add support for loading images. rules.push({ test: /\.(png|jpe?g|gif)$/, loaders: [ { loader: 'file-loader', options: { name: path => { if (! /node_modules|bower_components/.test(path)) { return Config.fileLoaderDirs.images + '/[name].[ext]?[hash]'; } return Config.fileLoaderDirs.images + '/vendor/' + path .replace(/\\/g, '/') .replace( /((.*(node_modules|bower_components))|images|image|img|assets)\//g, '' ) + '?[hash]'; }, publicPath: Config.resourceRoot } }, { loader: 'img-loader', options: Config.imgLoaderOptions } ] }); // Add support for loading fonts. rules.push({ test: /\.(woff2?|ttf|eot|svg|otf)$/, loader: 'file-loader', options: { name: path => { if (! /node_modules|bower_components/.test(path)) { return Config.fileLoaderDirs.fonts + '/[name].[ext]?[hash]'; } return Config.fileLoaderDirs.fonts + '/vendor/' + path .replace(/\\/g, '/') .replace( /((.*(node_modules|bower_components))|fonts|font|assets)\//g, '' ) + '?[hash]'; }, publicPath: Config.resourceRoot } }); // Add support for loading cursor files. rules.push({ test: /\.(cur|ani)$/, loader: 'file-loader', options: { name: '[name].[ext]?[hash]', publicPath: Config.resourceRoot } }); // Here, we'll filter through all CSS preprocessors that the user has requested. // For each one, we'll add a new Webpack rule and then prepare the necessary // extract plugin to extract the CSS into its file. Object.keys(Config.preprocessors).forEach(type => { if (type === 'fastSass') return; Config.preprocessors[type].forEach(preprocessor => { let outputPath = preprocessor.output.filePath.replace(Config.publicPath + path.sep, path.sep); tap(new ExtractTextPlugin(outputPath), extractPlugin => { let loaders = [ { loader: 'css-loader', options: { url: Config.processCssUrls, sourceMap: Mix.isUsing('sourcemaps'), importLoaders: 1 } }, { loader: 'postcss-loader', options: { sourceMap: (type === 'sass' && Config.processCssUrls) ? true : Mix.isUsing('sourcemaps'), ident: 'postcss', plugins: (function () { let plugins = Config.postCss; if (preprocessor.postCssPlugins && preprocessor.postCssPlugins.length) { plugins = preprocessor.postCssPlugins; } if (Config.autoprefixer) { plugins.push(require('autoprefixer')); } return plugins; })() } }, ]; if (type === 'sass' && Config.processCssUrls) { loaders.push({ loader: 'resolve-url-loader', options: { sourceMap: true, root: Mix.paths.root('node_modules') } }); } if (type !== 'postCss') { loaders.push({ loader: `${type}-loader`, options: Object.assign( preprocessor.pluginOptions, { sourceMap: (type === 'sass' && Config.processCssUrls) ? true : Mix.isUsing('sourcemaps') } ) }); } rules.push({ test: preprocessor.src.path(), use: extractPlugin.extract({ fallback: 'style-loader', use: loaders }) }); extractPlugins.push(extractPlugin); }); }); }); // Vue Compilation. let vueExtractPlugin; if (Config.extractVueStyles) { let fileName = typeof(Config.extractVueStyles) === "string" ? Config.extractVueStyles : 'vue-styles.css'; let filePath = fileName.replace(Config.publicPath, '').replace(/^\//, ""); vueExtractPlugin = extractPlugins.length ? extractPlugins[0] : new ExtractTextPlugin(filePath); } rules.push({ test: /\.vue$/, loader: 'vue-loader', exclude: /bower_components/, options: { // extractCSS: Config.extractVueStyles, loaders: Config.extractVueStyles ? { js: { loader: 'babel-loader', options: Config.babel() }, scss: vueExtractPlugin.extract({ use: 'css-loader!sass-loader', fallback: 'vue-style-loader' }), sass: vueExtractPlugin.extract({ use: 'css-loader!sass-loader?indentedSyntax', fallback: 'vue-style-loader' }), css: vueExtractPlugin.extract({ use: 'css-loader', fallback: 'vue-style-loader' }), stylus: vueExtractPlugin.extract({ use: 'css-loader!stylus-loader?paths[]=node_modules', fallback: 'vue-style-loader' }), less: vueExtractPlugin.extract({ use: 'css-loader!less-loader', fallback: 'vue-style-loader' }), } : { js: { loader: 'babel-loader', options: Config.babel() } }, postcss: Config.postCss, preLoaders: Config.vue.preLoaders, postLoaders: Config.vue.postLoaders, esModule: Config.vue.esModule } }); // If there were no existing extract text plugins to add our // Vue styles extraction too, we'll push a new one in. if (Config.extractVueStyles && ! extractPlugins.length) { extractPlugins.push(vueExtractPlugin); } // If we want to import a global styles file in every component, // use sass resources loader if (Config.extractVueStyles && Config.globalVueStyles) { tap(rules[rules.length - 1].options.loaders, vueLoaders => { vueLoaders.scss.push({ loader: 'sass-resources-loader', options: { resources: Mix.paths.root(Config.globalVueStyles) } }); vueLoaders.sass.push({ loader: 'sass-resources-loader', options: { resources: Mix.paths.root(Config.globalVueStyles) } }); }); } return { rules, extractPlugins }; }