diff --git a/bin/ignore-extraneous-style-assets.js b/bin/ignore-extraneous-style-assets.js new file mode 100644 index 00000000000..a20328cbca9 --- /dev/null +++ b/bin/ignore-extraneous-style-assets.js @@ -0,0 +1,58 @@ +/** + * Webpack plugin to prevent extraneous assets from being emitted for stylesheets. + * + * JS files and PHP asset files generated by `DependencyExtractionWebpackPlugin` are currently ignored. + */ +class IgnoreExtraneousStyleAssets { + isCssFile( file ) { + return /^(?!!!).+\.(?:sc|sa|c)ss$/.test( file ); + } + + generateIgnoreRegex( entries ) { + const expression = entries + .reduce( ( acc, entry ) => [ ...acc, `${ entry }.js`, `${ entry }.asset.php` ], [] ) + .join( '|' ); + + return new RegExp( expression ); + } + + apply( compiler ) { + const cssEntries = []; + + compiler.hooks.compilation.tap( 'IgnoreExtraneousStyleAssets', ( compilation ) => { + // The `addEntry` compilation hook is undocumented. As for why, ¯\_(ツ)_/¯. + compilation.hooks.addEntry.tap( 'IgnoreExtraneousStyleAssets', ( entry, name ) => { + if ( entry.type === 'single entry' ) { + if ( this.isCssFile( entry.request ) ) { + cssEntries.push( name ); + } + } else if ( entry.type === 'multi entry' ) { + const filteredDependencies = entry.dependencies.filter( + ( singleDependency ) => this.isCssFile( singleDependency.request ), + ); + + // If all dependencies of the entry are CSS files, add it to the list of entries to ignore. + if ( entry.dependencies.length === filteredDependencies.length ) { + cssEntries.push( name ); + } + } + } ); + } ); + + compiler.hooks.emit.tap( 'IgnoreExtraneousStyleAssets', ( compilation ) => { + if ( cssEntries.length === 0 ) { + return; + } + + const ignoreRegex = this.generateIgnoreRegex( cssEntries ); + + Object.keys( compilation.assets ).forEach( ( assetName ) => { + if ( ignoreRegex.test( assetName ) ) { + delete compilation.assets[ assetName ]; + } + } ); + } ); + } +} + +module.exports = IgnoreExtraneousStyleAssets; diff --git a/package-lock.json b/package-lock.json index 5d9a82401a2..db7ed87b3fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,7 +61,6 @@ "grunt-contrib-copy": "1.0.0", "grunt-shell": "3.0.1", "grunt-wp-deploy": "2.1.2", - "ignore-emit-webpack-plugin": "2.0.6", "jest-silent-reporter": "0.5.0", "lint-staged": "10.5.4", "lodash": "4.17.21", @@ -76,7 +75,6 @@ "react-test-renderer": "16.14.0", "rtlcss-webpack-plugin": "4.0.6", "svgo": "2.3.0", - "terser-webpack-plugin": "4.2.3", "webpackbar": "4.0.0" }, "engines": { @@ -27019,106 +27017,6 @@ "node": ">=6.0.0" } }, - "node_modules/terser-webpack-plugin": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz", - "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==", - "dev": true, - "dependencies": { - "cacache": "^15.0.5", - "find-cache-dir": "^3.3.1", - "jest-worker": "^26.5.0", - "p-limit": "^3.0.2", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "source-map": "^0.6.1", - "terser": "^5.3.4", - "webpack-sources": "^1.4.3" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.6.1.tgz", - "integrity": "sha512-yv9YLFQQ+3ZqgWCUk+pvNJwgUTdlIxUk1WTN+RnaFJe2L7ipG2csPT0ra2XRm7Cs8cxN7QXmK1rFzEwYEQkzXw==", - "dev": true, - "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin/node_modules/terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/terser-webpack-plugin/node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, "node_modules/terser/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -52372,89 +52270,6 @@ } } }, - "terser-webpack-plugin": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz", - "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==", - "dev": true, - "requires": { - "cacache": "^15.0.5", - "find-cache-dir": "^3.3.1", - "jest-worker": "^26.5.0", - "p-limit": "^3.0.2", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "source-map": "^0.6.1", - "terser": "^5.3.4", - "webpack-sources": "^1.4.3" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "terser": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.6.1.tgz", - "integrity": "sha512-yv9YLFQQ+3ZqgWCUk+pvNJwgUTdlIxUk1WTN+RnaFJe2L7ipG2csPT0ra2XRm7Cs8cxN7QXmK1rFzEwYEQkzXw==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - } - } - }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", diff --git a/package.json b/package.json index 5ecbf5adb1e..e65ce431fa0 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,6 @@ "grunt-contrib-copy": "1.0.0", "grunt-shell": "3.0.1", "grunt-wp-deploy": "2.1.2", - "ignore-emit-webpack-plugin": "2.0.6", "jest-silent-reporter": "0.5.0", "lint-staged": "10.5.4", "lodash": "4.17.21", @@ -92,7 +91,6 @@ "react-test-renderer": "16.14.0", "rtlcss-webpack-plugin": "4.0.6", "svgo": "2.3.0", - "terser-webpack-plugin": "4.2.3", "webpackbar": "4.0.0" }, "scripts": { diff --git a/tests/php/bootstrap.php b/tests/php/bootstrap.php index 9e28ca15fe9..71067e2ff04 100644 --- a/tests/php/bootstrap.php +++ b/tests/php/bootstrap.php @@ -42,9 +42,6 @@ function amp_unit_test_load_plugin_file() { tests_add_filter( 'muplugins_loaded', 'amp_unit_test_load_plugin_file' ); -// Start up the WP testing environment. -require $_test_root . '/includes/bootstrap.php'; - /* * Load WP CLI. Its test bootstrap file can't be required as it will load * duplicate class names which are already in use. @@ -52,3 +49,9 @@ function amp_unit_test_load_plugin_file() { define( 'WP_CLI_ROOT', TESTS_PLUGIN_DIR . '/vendor/wp-cli/wp-cli' ); define( 'WP_CLI_VENDOR_DIR', TESTS_PLUGIN_DIR . '/vendor' ); require_once WP_CLI_ROOT . '/php/utils.php'; + +$logger = new WP_CLI\Loggers\Regular( true ); +WP_CLI::set_logger( $logger ); + +// Start up the WP testing environment. +require $_test_root . '/includes/bootstrap.php'; diff --git a/webpack.config.js b/webpack.config.js index f2925825ffb..9b3ab657249 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,12 +1,11 @@ /** * External dependencies */ +const fs = require( 'fs' ); const path = require( 'path' ); const CopyWebpackPlugin = require( 'copy-webpack-plugin' ); -const IgnoreEmitPlugin = require( 'ignore-emit-webpack-plugin' ); const OptimizeCSSAssetsPlugin = require( 'optimize-css-assets-webpack-plugin' ); const RtlCssPlugin = require( 'rtlcss-webpack-plugin' ); -const TerserPlugin = require( 'terser-webpack-plugin' ); const WebpackBar = require( 'webpackbar' ); /** @@ -16,6 +15,11 @@ const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' ); const { defaultRequestToExternal, defaultRequestToHandle, camelCaseDash } = require( '@wordpress/dependency-extraction-webpack-plugin/lib/util' ); +/** + * Internal dependencies + */ +const IgnoreExtraneousStyleAssets = require( './bin/ignore-extraneous-style-assets' ); + const sharedConfig = { ...defaultConfig, output: { @@ -54,22 +58,19 @@ const sharedConfig = { new RtlCssPlugin( { filename: '../css/[name]-rtl.css', } ), + new IgnoreExtraneousStyleAssets(), ], optimization: { - minimizer: [ - new TerserPlugin( { - parallel: true, - sourceMap: false, - cache: true, - terserOptions: { - output: { - comments: /translators:/i, - }, - }, - extractComments: false, - } ), - new OptimizeCSSAssetsPlugin( { } ), - ], + ...defaultConfig.optimization, + splitChunks: { + ...defaultConfig.optimization.splitChunks, + cacheGroups: { + ...defaultConfig.optimization.splitChunks.cacheGroups, + // Disable `style` cache group from default config. + style: false, + }, + }, + minimizer: defaultConfig.optimization.minimizer.concat( [ new OptimizeCSSAssetsPlugin( { } ) ] ), }, }; @@ -305,7 +306,7 @@ const settingsPage = { }, plugins: [ ...sharedConfig.plugins.filter( - ( plugin ) => plugin.constructor.name !== 'DependencyExtractionWebpackPlugin', + ( plugin ) => ! [ 'DependencyExtractionWebpackPlugin', 'IgnoreExtraneousStyleAssets' ].includes( plugin.constructor.name ), ), new DependencyExtractionWebpackPlugin( { useDefaults: false, @@ -333,6 +334,8 @@ const settingsPage = { } }, } ), + // Ensure assets generated by `DependencyExtractionWebpackPlugin` are also ignored for styles entries. + new IgnoreExtraneousStyleAssets(), new WebpackBar( { name: 'Settings page', color: '#67b255', @@ -342,19 +345,17 @@ const settingsPage = { const styles = { ...sharedConfig, - entry: { - 'admin-tables': './assets/css/src/admin-tables.css', - 'amp-block-editor': './assets/css/src/amp-block-editor.css', - 'amp-customizer': './assets/css/src/amp-customizer.css', - 'amp-customizer-legacy': './assets/css/src/amp-customizer-legacy.css', - 'amp-default': './assets/css/src/amp-default.css', - 'amp-icons': './assets/css/src/amp-icons.scss', - 'amp-mobile-version-switcher': './assets/css/src/amp-mobile-version-switcher.css', - 'amp-playlist-shortcode': './assets/css/src/amp-playlist-shortcode.css', - 'amp-post-meta-box': './assets/css/src/amp-post-meta-box.css', - 'amp-validation-error-taxonomy': './assets/css/src/amp-validation-error-taxonomy.scss', - 'amp-validation-single-error-url': './assets/css/src/amp-validation-single-error-url.css', - 'amp-validation-tooltips': './assets/css/src/amp-validation-tooltips.css', + entry: () => { + const entries = {}; + const dir = './assets/css/src'; + fs.readdirSync( dir ).forEach( ( fileName ) => { + const fullPath = `${ dir }/${ fileName }`; + if ( ! fs.lstatSync( fullPath ).isDirectory() ) { + entries[ fileName.replace( /\.[^/.]+$/, '' ) ] = fullPath; + } + } ); + + return entries; }, module: { ...sharedConfig.module, @@ -380,9 +381,7 @@ const styles = { ), }, plugins: [ - ...sharedConfig.plugins.filter( - ( plugin ) => plugin.constructor.name !== 'DependencyExtractionWebpackPlugin', - ), + ...sharedConfig.plugins, new WebpackBar( { name: 'Styles', color: '#b763ff', @@ -390,28 +389,6 @@ const styles = { ], }; -/** - * Get JS chunk names for a list of style entry points. - * - * @param {Array} entries List of style entry points. - * - * @return {Array} List of JS chunk names. - */ -const extractChunkNamesForStylesheets = ( entries ) => - entries - .map( ( entryPath ) => { - const match = entryPath.match( /([^\/]+)\.s?css$/ ); - return `${ match[ 1 ] }.js`; - } ); - -/** - * Do not generate empty JS chunks for stylesheets. - */ -styles.plugins = [ - ...styles.plugins, - new IgnoreEmitPlugin( extractChunkNamesForStylesheets( Object.values( styles.entry ) ) ), -]; - const mobileRedirection = { ...sharedConfig, entry: {