diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index 00a7efa80188b4..3ba2383b332ff9 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -1,3 +1,9 @@ +## Master + +### New Features + +- The plugin now supports optional `combineAssets` options. When this flag is set to `true`, all information about assets is combined into a single `assets.(json|php)` file generated in the output directory ([#20330](https://github.com/WordPress/gutenberg/pull/20330)). + ## 2.0.0 (2019-09-16) ### Breaking Changes diff --git a/packages/dependency-extraction-webpack-plugin/README.md b/packages/dependency-extraction-webpack-plugin/README.md index e7ddc43786d7b5..58575c12b47dde 100644 --- a/packages/dependency-extraction-webpack-plugin/README.md +++ b/packages/dependency-extraction-webpack-plugin/README.md @@ -116,6 +116,13 @@ module.exports = { The output format for the generated asset file. There are two options available: 'php' or 'json'. +##### `combineAssets` + +- Type: boolean +- Default: `false` + +By default, one asset file is created for each entry point. When this flag is set to `true`, all information about assets is combined into a single `assets.(json|php)` file generated in the output directory. + ##### `useDefaults` - Type: boolean @@ -217,7 +224,7 @@ Enqueue your script as usual and read the script dependencies dynamically: $script_path = 'path/to/script.js'; $script_asset_path = 'path/to/script.asset.php'; $script_asset = file_exists( $script_asset_path ) - ? require( $script_asset_path ) + ? require( $script_asset_path ) : array( 'dependencies' => array(), 'version' => filemtime( $script_path ) ); $script_url = plugins_url( $script_path, __FILE__ ); wp_enqueue_script( 'script', $script_url, $script_asset['dependencies'], $script_asset['version'] ); diff --git a/packages/dependency-extraction-webpack-plugin/index.js b/packages/dependency-extraction-webpack-plugin/index.js index da909d7c5258f1..12e048d4fe2593 100644 --- a/packages/dependency-extraction-webpack-plugin/index.js +++ b/packages/dependency-extraction-webpack-plugin/index.js @@ -3,6 +3,7 @@ */ const { createHash } = require( 'crypto' ); const json2php = require( 'json2php' ); +const path = require( 'path' ); const { ExternalsPlugin } = require( 'webpack' ); const { RawSource } = require( 'webpack-sources' ); @@ -18,6 +19,7 @@ class DependencyExtractionWebpackPlugin { constructor( options ) { this.options = Object.assign( { + combineAssets: false, injectPolyfill: false, outputFormat: 'php', useDefaults: true, @@ -102,7 +104,12 @@ class DependencyExtractionWebpackPlugin { const { filename: outputFilename } = output; compiler.hooks.emit.tap( this.constructor.name, ( compilation ) => { - const { injectPolyfill, outputFormat } = this.options; + const { + combineAssets, + injectPolyfill, + outputFormat, + } = this.options; + const combinedAssetsData = {}; // Process each entry point independently. for ( const [ @@ -130,30 +137,37 @@ class DependencyExtractionWebpackPlugin { const runtimeChunk = entrypoint.getRuntimeChunk(); - // Get a stable, stringified representation of the WordPress script asset. - const assetString = this.stringify( { + const assetData = { + // Get a sorted array so we can produce a stable, stringified representation. dependencies: Array.from( entrypointExternalizedWpDeps ).sort(), version: runtimeChunk.hash, - } ); + }; + + const assetString = this.stringify( assetData ); // Determine a filename for the asset file. const [ filename, query ] = entrypointName.split( '?', 2 ); - const assetFilename = compilation - .getPath( outputFilename, { - chunk: runtimeChunk, - filename, - query, - basename: basename( filename ), - contentHash: createHash( 'md4' ) - .update( assetString ) - .digest( 'hex' ), - } ) - .replace( - /\.js$/i, - '.asset.' + ( outputFormat === 'php' ? 'php' : 'json' ) - ); + const buildFilename = compilation.getPath( outputFilename, { + chunk: runtimeChunk, + filename, + query, + basename: basename( filename ), + contentHash: createHash( 'md4' ) + .update( assetString ) + .digest( 'hex' ), + } ); + + if ( combineAssets ) { + combinedAssetsData[ buildFilename ] = assetData; + continue; + } + + const assetFilename = buildFilename.replace( + /\.js$/i, + '.asset.' + ( outputFormat === 'php' ? 'php' : 'json' ) + ); // Add source and file into compilation for webpack to output. compilation.assets[ assetFilename ] = new RawSource( @@ -161,6 +175,23 @@ class DependencyExtractionWebpackPlugin { ); runtimeChunk.files.push( assetFilename ); } + + if ( combineAssets ) { + const outputFolder = compiler.options.output.path; + const assetsFilePath = path.resolve( + outputFolder, + 'assets.' + ( outputFormat === 'php' ? 'php' : 'json' ) + ); + const assetsFilename = path.relative( + outputFolder, + assetsFilePath + ); + + // Add source into compilation for webpack to output. + compilation.assets[ assetsFilename ] = new RawSource( + this.stringify( combinedAssetsData ) + ); + } } ); } } diff --git a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap index 48ae403201a7e9..5746515136e13e 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -1,5 +1,39 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Webpack \`combine-assets\` should produce expected output: Asset file should match snapshot 1`] = `" array('dependencies' => array('lodash', 'wp-blob'), 'version' => '2ac177723ec97b400d9b7c46f5270974'), 'fileB.js' => array('dependencies' => array('wp-token-list'), 'version' => 'bddb08fb8608759738528b9de111454d'));"`; + +exports[`Webpack \`combine-assets\` should produce expected output: External modules should match snapshot 1`] = ` +Array [ + Object { + "externalType": "this", + "request": Object { + "this": Array [ + "wp", + "blob", + ], + }, + "userRequest": "@wordpress/blob", + }, + Object { + "externalType": "this", + "request": Object { + "this": Array [ + "wp", + "tokenList", + ], + }, + "userRequest": "@wordpress/token-list", + }, + Object { + "externalType": "this", + "request": Object { + "this": "lodash", + }, + "userRequest": "lodash", + }, +] +`; + exports[`Webpack \`dynamic-import\` should produce expected output: Asset file should match snapshot 1`] = `" array('lodash', 'wp-blob'), 'version' => '5d8e58fe98bc4c6277a76ece11fcb8b7');"`; exports[`Webpack \`dynamic-import\` should produce expected output: External modules should match snapshot 1`] = ` diff --git a/packages/dependency-extraction-webpack-plugin/test/build.js b/packages/dependency-extraction-webpack-plugin/test/build.js index 12216abc9687c7..d6df018b6f3296 100644 --- a/packages/dependency-extraction-webpack-plugin/test/build.js +++ b/packages/dependency-extraction-webpack-plugin/test/build.js @@ -47,12 +47,16 @@ describe.each( configFixtures )( 'Webpack `%s`', ( configCase ) => { expect( err ).toBeNull(); const assetFiles = glob( - `${ outputDirectory }/*.asset.@(json|php)` + `${ outputDirectory }/+(*.asset|assets).@(json|php)` ); - const expectedLength = + const hasCombinedAssets = ( options.plugins || [] ).some( + ( plugin ) => !! ( plugin.options || {} ).combineAssets + ); + const entrypointCount = typeof options.entry === 'object' ? Object.keys( options.entry ).length : 1; + const expectedLength = hasCombinedAssets ? 1 : entrypointCount; expect( assetFiles ).toHaveLength( expectedLength ); // Asset files should match. diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/combine-assets/file-a.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/combine-assets/file-a.js new file mode 100644 index 00000000000000..aa683b111a5ab2 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/combine-assets/file-a.js @@ -0,0 +1,11 @@ +/** + * External dependencies + */ +import { isEmpty } from 'lodash'; + +/** + * WordPress dependencies + */ +import { isBlobURL } from '@wordpress/blob'; + +isEmpty( isBlobURL( '' ) ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/combine-assets/file-b.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/combine-assets/file-b.js new file mode 100644 index 00000000000000..ff99551c7b1602 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/combine-assets/file-b.js @@ -0,0 +1,9 @@ +/** + * WordPress dependencies + */ +import TokenList from '@wordpress/token-list'; + +const tokens = new TokenList( 'abc def' ); +tokens.add( 'ghi' ); +tokens.remove( 'def' ); +tokens.replace( 'abc', 'xyz' ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/combine-assets/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/combine-assets/webpack.config.js new file mode 100644 index 00000000000000..1c0e557b6d1e6e --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/test/fixtures/combine-assets/webpack.config.js @@ -0,0 +1,13 @@ +const DependencyExtractionWebpackPlugin = require( '../../..' ); + +module.exports = { + entry: { + fileA: './file-a.js', + fileB: './file-b.js', + }, + plugins: [ + new DependencyExtractionWebpackPlugin( { + combineAssets: true, + } ), + ], +};