diff --git a/package.json b/package.json index ea1b1c2..1cb17fd 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "babel-loader": "^8.2.2", "browser-sync": "^2.26.7", "browserslist-config-webfactory": "^1.0.0", + "css-loader": "^7.1.2", "dotenv": "^10.0.0", "fancy-log": "^1.3.3", "gulp": "^4.0.0", @@ -22,10 +23,14 @@ "gulp-stylelint": "^13.0.0", "gulp-svgmin": "^4.1.0", "gulp-terser": "^2.0.1", + "mini-css-extract-plugin": "^2.9.4", "minimist": "^1.2.0", "postcss": "^8.0.9", + "postcss-loader": "^8.2.0", "postcss-preset-env": "^9.3.0", "postcss-url": "^10.1.3", + "resolve-url-loader": "^5.0.0", + "sass-loader": "^16.0.5", "stylelint": "^14.1.0", "stylelint-config-sass-guidelines": "^9.0.1", "stylelint-order": "^5.0.0", diff --git a/tasks/stylepack.js b/tasks/stylepack.js new file mode 100644 index 0000000..86789a8 --- /dev/null +++ b/tasks/stylepack.js @@ -0,0 +1,116 @@ +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const postcssPurgecss = require("@fullhuman/postcss-purgecss"); +const argv = require('minimist')(process.argv.slice(2)); +const webpackStream = require('webpack-stream'); +const mergeStream = require('merge-stream'); +const path = require('path'); + +// This custom extractor will also match selectors that contain +// special chars like "_", ".", ":", "\" and "@" +function utilityCssExtractor(content) { + return content.match(/[a-zA-Z0-9-_.:@\/]+/g) +} + +function stylepack(gulp, $, config) { + + const streams = config.styles.files.map((file) => { + // Set up PurgeCSS with external config + let purgeCssConfig = file.purgeCss ?? config.styles.purgeCss; + let purgeCssDisabled = argv.purgecss === false; // Check for CLI flags/args + let purgeCss = purgeCssConfig && !purgeCssDisabled; // Determine if PurgeCSS should run + + // Grab a PostCSS Preset Env config to use; + // always prefers a stylesheet-specific one over a global config for all CSS files + let postCssPresetEnvConfig = file.postCssPresetEnv || config.styles.postCssPresetEnv || ''; + + const webpackConfig = { + entry: `/${config.webdir}/${file.files}`, + output: { + // path: path.resolve(__dirname, 'webpack-tmp', file.destDir || ''), + filename: `webpack-tmp/${file.name}`, + }, + resolve: { + mainFields: ['browser', 'module', 'main'], + }, + module: { + rules: [ + { + test: /\.(png|jpe?g|gif|svg|webp|avif)$/i, + type: 'asset/resource', + generator: { + filename: 'img/[name].[hash][ext]' + } + }, + { + test: /\.s[ac]ss$/i, + use: [ + { + loader: MiniCssExtractPlugin.loader, + }, + { + loader: 'css-loader', + options: { + sourceMap: true, + } + }, + { + loader: 'resolve-url-loader', + }, + { + loader: "postcss-loader", + options: { + sourceMap: true, + postcssOptions: { + plugins: [ + ["postcss-preset-env", postCssPresetEnvConfig], + purgeCss ? postcssPurgecss({ + content: purgeCssConfig.content, + extractors: [ + { + extractor: utilityCssExtractor, + extensions: ['php', 'twig', 'js', 'svg'] + } + ], + safelist: purgeCssConfig.safelist, + }) : false, + ].filter(Boolean), + }, + }, + }, + { + loader: 'sass-loader', + options: { + implementation: 'sass-embedded', + api: 'modern', + sourceMap: true, + sassOptions: { + loadPaths: config.styles.includePaths ? config.styles.includePaths : [config.npmdir] + }, + }, + }, + ], + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: `${file.destDir}/${file.name}`, + }), + ], + mode: config.development && $.argv.prod !== true ? 'development' : 'production', + devtool: $.argv.debug === true ? 'source-map' : false, + stats: { + preset: 'normal', + timings: true + }, + }; + + return webpackStream(webpackConfig) + .pipe(gulp.dest(path.join(config.webdir))); + }); + + return mergeStream(...streams) + .pipe($.browserSync.reload({ stream: true })); +} + +exports.stylepack = stylepack;