import { Builder } from '../Builder'; import { ConfigPlugin } from '../ConfigPlugin'; import Spin from '../Spin'; const postCssDefaultConfig = (builder: Builder) => { return { plugins: () => [ builder.require('autoprefixer')({ browsers: ['last 2 versions', 'ie >= 9'] }) ] }; }; export default class CssProcessorPlugin implements ConfigPlugin { public configure(builder: Builder, spin: Spin) { const stack = builder.stack; const dev = spin.dev; const loaderOptions = builder.sourceMap ? { sourceMap: true } : {}; if (stack.hasAll('webpack') && !stack.hasAny('dll')) { let createRule; const rules = []; const postCssLoader = builder.require.probe('postcss-loader') ? 'postcss-loader' : undefined; const useDefaultPostCss: boolean = builder.useDefaultPostCss || false; let plugin; if (stack.hasAny('server')) { createRule = (ext: string, nodeModules: boolean, ruleList: any[]) => ({ test: nodeModules ? new RegExp(`^.*\\/node_modules\\/.*\\.${ext}$`) : new RegExp(`^(?!.*\\/node_modules\\/).*\\.${ext}$`), use: ([ { loader: 'isomorphic-style-loader', options: spin.createConfig(builder, 'isomorphicStyle', {}) }, { loader: 'css-loader', options: spin.createConfig(builder, 'css', { ...loaderOptions }) } ] as any[]) .concat( postCssLoader && !nodeModules ? { loader: postCssLoader, options: spin.createConfig( builder, 'postCss', useDefaultPostCss ? { ...postCssDefaultConfig(builder), ...loaderOptions } : { ...loaderOptions } ) } : [] ) .concat(ruleList) }); } else if (stack.hasAny('web')) { const webpackVer = builder.require('webpack/package.json').version.split('.')[0]; if (webpackVer < 4) { let ExtractCSSPlugin; if (!dev) { ExtractCSSPlugin = builder.require('extract-text-webpack-plugin'); } createRule = (ext: string, nodeModules: boolean, ruleList: any[]) => { if (!dev && !plugin) { plugin = new ExtractCSSPlugin({ filename: `[name].[contenthash].css` }); } return { test: nodeModules ? new RegExp(`^.*\\/node_modules\\/.*\\.${ext}$`) : new RegExp(`^(?!.*\\/node_modules\\/).*\\.${ext}$`), use: dev ? ([ { loader: 'style-loader', options: spin.createConfig(builder, 'style', {}) }, { loader: 'css-loader', options: spin.createConfig(builder, 'css', { ...loaderOptions, importLoaders: 1 }) } ] as any[]) .concat( postCssLoader && !nodeModules ? { loader: postCssLoader, options: spin.createConfig( builder, 'postCss', useDefaultPostCss ? { ...postCssDefaultConfig(builder), ...loaderOptions } : { ...loaderOptions } ) } : [] ) .concat(ruleList) : plugin.extract({ fallback: 'style-loader', use: [ { loader: 'css-loader', options: spin.createConfig(builder, 'css', { importLoaders: postCssLoader && !nodeModules ? 1 : 0 }) } ] .concat( postCssLoader && !nodeModules ? ({ loader: postCssLoader, options: spin.createConfig( builder, 'postCss', useDefaultPostCss ? postCssDefaultConfig(builder) : {} ) } as any) : [] ) .concat( ruleList ? ruleList.map(rule => { const { sourceMap, ...options } = rule.options; return { loader: rule.loader, options }; }) : [] ) }) }; }; } else { let ExtractCSSPlugin; if (!dev) { ExtractCSSPlugin = builder.require('mini-css-extract-plugin'); } createRule = (ext: string, nodeModules: boolean, ruleList: any[]) => { if (!dev && !plugin) { plugin = new ExtractCSSPlugin({ chunkFilename: '[id].css', filename: `[name].[contenthash].css` }); } return { test: nodeModules ? new RegExp(`^.*\\/node_modules\\/.*\\.${ext}$`) : new RegExp(`^(?!.*\\/node_modules\\/).*\\.${ext}$`), use: dev ? ([ { loader: 'style-loader', options: spin.createConfig(builder, 'style', {}) }, { loader: 'css-loader', options: spin.createConfig(builder, 'css', { ...loaderOptions, importLoaders: 1 }) } ] as any[]) .concat( postCssLoader && !nodeModules ? { loader: postCssLoader, options: spin.createConfig( builder, 'postCss', useDefaultPostCss ? { ...postCssDefaultConfig(builder), ...loaderOptions } : { ...loaderOptions } ) } : [] ) .concat(ruleList) : [ { loader: ExtractCSSPlugin.loader, options: spin.createConfig(builder, 'mini-css-extract', {}) }, { loader: 'css-loader', options: spin.createConfig(builder, 'css', { importLoaders: postCssLoader && !nodeModules ? 1 : 0 }) } ] .concat( postCssLoader && !nodeModules ? ({ loader: postCssLoader, options: spin.createConfig( builder, 'postCss', useDefaultPostCss ? postCssDefaultConfig(builder) : {} ) } as any) : [] ) .concat( ruleList ? ruleList.map(rule => { const { sourceMap, ...options } = rule.options; return { loader: rule.loader, options }; }) : [] ) }; }; } } if (createRule && stack.hasAny('css')) { rules.push(createRule('css', false, []), createRule('css', true, [])); } if (createRule && stack.hasAny('sass')) { const sassRule = [{ loader: 'sass-loader', options: spin.createConfig(builder, 'sass', { ...loaderOptions }) }]; rules.push(createRule('scss', false, sassRule), createRule('scss', true, sassRule)); } if (createRule && stack.hasAny('less')) { const lessLoaderVer = builder.require('less-loader/package.json').version.split('.')[0]; const options = lessLoaderVer >= 4 ? { javascriptEnabled: true, ...loaderOptions } : { ...loaderOptions }; const lessRule = [{ loader: 'less-loader', options: spin.createConfig(builder, 'less', options) }]; rules.push(createRule('less', false, lessRule), createRule('less', true, lessRule)); } builder.config = spin.merge(builder.config, { module: { rules } }); if (plugin) { builder.config.plugins.push(plugin); } } } }